diff --git a/pelican-plugins/.gitignore b/pelican-plugins/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..b7d252ae16d0aa18b9ca04b2ee4e94df341aeef5
--- /dev/null
+++ b/pelican-plugins/.gitignore
@@ -0,0 +1,13 @@
+*~
+._*
+.*.swp
+.*.swo
+*.pyc
+*.log
+.DS_Store
+.directory
+.idea
+.project
+.pydevproject
+__pycache__
+.vscode
diff --git a/pelican-plugins/.gitmodules b/pelican-plugins/.gitmodules
new file mode 100644
index 0000000000000000000000000000000000000000..5310e4e00b5940bb0bc019877dffb9f1b109b5a1
--- /dev/null
+++ b/pelican-plugins/.gitmodules
@@ -0,0 +1,144 @@
+[submodule "pelican-fontawesome"]
+	path = pelican-fontawesome
+	url = https://github.com/kura/pelican-fontawesome.git
+[submodule "pelican_youtube"]
+	path = pelican_youtube
+	url = https://github.com/kura/pelican_youtube.git
+[submodule "pelican_vimeo"]
+	path = pelican_vimeo
+	url = https://github.com/kura/pelican_vimeo.git
+[submodule "cjk-auto-spacing"]
+	path = cjk-auto-spacing
+	url = https://github.com/yuex/cjk-auto-spacing.git
+[submodule "pelican-gist"]
+	path = pelican-gist
+	url = https://github.com/streeter/pelican-gist.git
+[submodule "pelicanfly"]
+	path = pelicanfly
+	url = https://github.com/bmcorser/pelicanfly.git
+[submodule "pelican-flickr"]
+	path = pelican-flickr
+	url = https://github.com/La0/pelican-flickr.git
+[submodule "better_code_samples"]
+	path = better_code_samples
+	url = https://github.com/ChrislS/better_code_samples.git
+[submodule "pin_to_top"]
+	path = pin_to_top
+	url = https://github.com/Shaked/pin_to_top.git
+[submodule "pelican-githubprojects"]
+	path = pelican-githubprojects
+	url = https://github.com/kura/pelican-githubprojects.git
+[submodule "pelicanthemes-generator"]
+	path = pelicanthemes-generator
+	url = https://github.com/badele/pelicanthemes-generator
+[submodule "pelican-page-order"]
+	path = pelican-page-order
+	url = https://github.com/akhayyat/pelican-page-order.git
+[submodule "pelican-page-hierarchy"]
+	path = pelican-page-hierarchy
+	url = https://github.com/akhayyat/pelican-page-hierarchy.git
+[submodule "multi_neighbors"]
+	path = multi_neighbors
+	url = https://github.com/davidlesieur/multi_neighbors.git
+[submodule "pelican-langcategory"]
+	path = pelican-langcategory
+	url = https://github.com/CNBorn/pelican-langcategory.git
+[submodule "pandoc_reader"]
+	path = pandoc_reader
+	url = https://github.com/liob/pandoc_reader.git
+[submodule "bootstrapify"]
+	path = bootstrapify
+	url = https://github.com/ingwinlu/pelican-bootstrapify.git
+[submodule "pelican-jinja2content"]
+	path = pelican-jinja2content
+	url = https://github.com/joachimneu/pelican-jinja2content.git
+[submodule "panorama"]
+	path = panorama
+	url = https://github.com/romainx/panorama.git
+[submodule "pelican-genealogy"]
+	path = pelican-genealogy
+	url = https://github.com/zappala/pelican-genealogy
+[submodule "image_process"]
+	path = image_process
+	url = https://github.com/whiskyechobravo/image_process
+[submodule "pelican-open_graph"]
+	path = pelican-open_graph
+	url = https://github.com/whiskyechobravo/pelican-open_graph.git
+[submodule "replacer"]
+	path = replacer
+	url = https://github.com/narusemotoki/replacer
+[submodule "pelican-toc"]
+	path = pelican-toc
+	url = https://github.com/ingwinlu/pelican-toc
+[submodule "multimarkdown_reader"]
+	path = multimarkdown_reader
+	url = https://github.com/dames57/multimarkdown_reader.git
+[submodule "pelican_javascript"]
+	path = pelican_javascript
+	url = https://github.com/mortada/pelican_javascript.git
+[submodule "loadcsv"]
+	path = loadcsv
+	url = https://github.com/e9t/pelican-loadcsv
+[submodule "org_pandoc_reader"]
+	path = org_pandoc_reader
+	url = https://github.com/jo-tham/org_pandoc_reader.git
+[submodule "pdf-img"]
+	path = pdf-img
+	url = https://github.com/cmacmackin/pdf-img.git
+[submodule "pelican-cite"]
+	path = pelican-cite
+	url = https://github.com/cmacmackin/pelican-cite.git
+[submodule "figure-ref"]
+	path = figure-ref
+	url = https://github.com/cmacmackin/figure-ref
+[submodule "encrypt-content"]
+	path = encrypt-content
+	url = https://github.com/mindcruzer/pelican-encrypt-content
+[submodule "md-metayaml"]
+	path = md-metayaml
+	url = https://github.com/joachimneu/pelican-md-metayaml
+[submodule "backreftranslate"]
+	path = backreftranslate
+	url = https://github.com/daltonmatos/pelican-plugin-backref-translate
+[submodule "category_order"]
+	path = category_order
+	url = https://github.com/jhshi/pelican.plugins.category_order.git
+[submodule "ga_page_view"]
+	path = ga_page_view
+	url = https://github.com/jhshi/pelican.plugins.ga_page_view.git
+[submodule "post_revision"]
+	path = post_revision
+	url = https://github.com/jhshi/pelican.plugins.post_revision
+[submodule "linkclass"]
+	path = pelican-linkclass
+	url = https://github.com/rlaboiss/pelican-linkclass
+[submodule "just_table"]
+	path = just_table
+	url = https://github.com/burakkose/just_table
+[submodule "ace_editor"]
+	path = ace_editor
+	url = https://github.com/mothsART/ace_editor.git
+[submodule "mboxreader"]
+	path = pelican-mboxreader
+	url = https://github.com/TC01/pelican-mboxreader
+[submodule "pelican-version"]
+	path = pelican-version
+	url = https://github.com/Shaked/pelican-version
+[submodule "lightbox"]
+	path = lightbox
+	url = https://github.com/kura/lightbox
+[submodule "pelican-ipynb"]
+	path = pelican-ipynb
+	url = https://github.com/danielfrg/pelican-ipynb
+[submodule "deadlinks"]
+	path = deadlinks
+	url = https://github.com/silentlamb/pelican-deadlinks.git
+[submodule "pelican-ert"]
+	path = pelican-ert
+	url = https://github.com/nogaems/pelican-ert.git
+[submodule "pelican_meetup_info"]
+	path = pelican_meetup_info
+	url = https://github.com/tylerdave/pelican-meetup-info.git
+[submodule "similar_posts"]
+	path = similar_posts
+	url = https://github.com/davidlesieur/similar_posts.git
diff --git a/pelican-plugins/.travis.yml b/pelican-plugins/.travis.yml
new file mode 100644
index 0000000000000000000000000000000000000000..3b920286b4b667a8fb3412d113ad3540a3a2ada9
--- /dev/null
+++ b/pelican-plugins/.travis.yml
@@ -0,0 +1,33 @@
+language: python
+python:
+    - "3.7"
+cache: pip
+env:
+ - R_LIBS_USER=$TRAVIS_BUILD_DIR/R/Library
+cache:
+  directories:
+   - $R_LIBS_USER
+before_install:
+# By default R 3.2.3 is installed with xenial, but rpy2 requires R >= 3.3
+# Hence we add an extra deb package repo to get a more recent version:
+ - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys E298A3A825C0D65DFD57CBB651716619E084DAB9
+ - sudo add-apt-repository 'deb [arch=amd64,i386] https://cloud.r-project.org/bin/linux/ubuntu xenial/'
+ - sudo apt-get update -qq
+ - sudo apt-get install -qq --no-install-recommends asciidoctor pandoc plantuml r-base ruby-sass
+ - mkdir -p $R_LIBS_USER
+ - echo 'install.packages("knitr", repos="https://cran.rstudio.com")' | R --no-save
+install:
+ - pip install -U -r requirements.txt
+ # Installing pelican in editable mode makes its tests.support package available to us
+ - pip install -e git+https://github.com/getpelican/pelican.git#egg=pelican
+ # The following commands install all plugins dependencies.
+ # The `grep` & `sed commands` below are needed because some submodules have conflicting requirements.
+ # cf. https://github.com/getpelican/pelican-plugins/issues/1170
+ - pip install -U -r <(grep -hv pelican */requirements.txt | sed 's/==/>=/')
+# We need to exclude the multimarkdown_reader & pandoc_reader plugins,
+# otherwise their BaseReader subclasses will be loaded by pelican.readers.Readers,
+# and override the default .md one.
+# We also need to exclude the image_process plugin as long as they are not fixed, cf. https://github.com/getpelican/pelican-plugins/issues/1170
+script: nosetests --with-summary-report
+   --exclude-dir=multimarkdown_reader --exclude-dir=pandoc_reader
+   --exclude-dir=image_process
diff --git a/pelican-plugins/Contributing.rst b/pelican-plugins/Contributing.rst
new file mode 100644
index 0000000000000000000000000000000000000000..4647b3c374133d81f88eba032282c62a82942cc8
--- /dev/null
+++ b/pelican-plugins/Contributing.rst
@@ -0,0 +1,39 @@
+Contributing a plugin
+=====================
+
+Details regarding how to write a plugin are explained in the Pelican `docs`_.
+
+If you want to contribute, **please be sure** to read our general contributing
+`guidelines`_ first. Then you can fork this repository, create a new branch,
+make your changes, squash your commits, and issue your pull request from your
+new branch (i.e., **not** the ``master`` branch).
+
+Make sure that your plugin follows the structure below::
+
+    my_plugin
+       ├──  __init__.py
+       ├──  my_plugin.py
+       ├──  test_my_plugin.py
+       └──  ReadMe.rst / ReadMe.md
+
+``my_plugin.py`` is the actual plugin implementation. Include a brief
+explanation of what the plugin does as a module docstring. Put any further
+explanations and usage details into the ``ReadMe`` file.
+
+``__init__.py`` should contain a single line with ``from .my_plugin import *``.
+
+Place tests for your plugin in the same folder inside ``test_my_plugin.py``.
+If you need content or templates in your tests, you can use the main
+``test_data`` folder for that purpose.
+
+**Note:** Each plugin can contain a LICENSE file stating the license it's
+released under. If there is an absence of LICENSE then it defaults to the
+*GNU AFFERO GENERAL PUBLIC LICENSE Version 3*. Please refer to the ``LICENSE``
+file for the full text of the license.
+
+Before making your initial commit, please be sure to add an entry to the repo's
+top-level ``ReadMe`` file, adding your plugin to the list (in alphabetical
+order) and providing a brief description.
+
+.. _guidelines: http://docs.getpelican.com/en/latest/contribute.html#using-git-and-github
+.. _docs: http://docs.getpelican.com/en/latest/plugins.html#how-to-create-plugins
diff --git a/pelican-plugins/LICENSE b/pelican-plugins/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..ba67bd0dc0391a3c68dc9388c5d391f65f94e0df
--- /dev/null
+++ b/pelican-plugins/LICENSE
@@ -0,0 +1,665 @@
+Unless the folder itself contains a LICENSE stating otherwise, all the files
+distributed here are released under the GNU AFFERO GENERAL PUBLIC LICENSE.
+
+
+                    GNU AFFERO GENERAL PUBLIC LICENSE
+                       Version 3, 19 November 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU Affero General Public License is a free, copyleft license for
+software and other kinds of works, specifically designed to ensure
+cooperation with the community in the case of network server software.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+our General Public Licenses are intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  Developers that use our General Public Licenses protect your rights
+with two steps: (1) assert copyright on the software, and (2) offer
+you this License which gives you legal permission to copy, distribute
+and/or modify the software.
+
+  A secondary benefit of defending all users' freedom is that
+improvements made in alternate versions of the program, if they
+receive widespread use, become available for other developers to
+incorporate.  Many developers of free software are heartened and
+encouraged by the resulting cooperation.  However, in the case of
+software used on network servers, this result may fail to come about.
+The GNU General Public License permits making a modified version and
+letting the public access it on a server without ever releasing its
+source code to the public.
+
+  The GNU Affero General Public License is designed specifically to
+ensure that, in such cases, the modified source code becomes available
+to the community.  It requires the operator of a network server to
+provide the source code of the modified version running there to the
+users of that server.  Therefore, public use of a modified version, on
+a publicly accessible server, gives the public access to the source
+code of the modified version.
+
+  An older license, called the Affero General Public License and
+published by Affero, was designed to accomplish similar goals.  This is
+a different license, not a version of the Affero GPL, but Affero has
+released a new version of the Affero GPL which permits relicensing under
+this license.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU Affero General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Remote Network Interaction; Use with the GNU General Public License.
+
+  Notwithstanding any other provision of this License, if you modify the
+Program, your modified version must prominently offer all users
+interacting with it remotely through a computer network (if your version
+supports such interaction) an opportunity to receive the Corresponding
+Source of your version by providing access to the Corresponding Source
+from a network server at no charge, through some standard or customary
+means of facilitating copying of software.  This Corresponding Source
+shall include the Corresponding Source for any work covered by version 3
+of the GNU General Public License that is incorporated pursuant to the
+following paragraph.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the work with which it is combined will remain governed by version
+3 of the GNU General Public License.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU Affero General Public License from time to time.  Such new versions
+will be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU Affero General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU Affero General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU Affero General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If your software can interact with users remotely through a computer
+network, you should also make sure that it provides a way for users to
+get its source.  For example, if your program is a web application, its
+interface could display a "Source" link that leads users to an archive
+of the code.  There are many ways you could offer source, and different
+solutions will be better for different programs; see section 13 for the
+specific requirements.
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU AGPL, see
+<http://www.gnu.org/licenses/>.
diff --git a/pelican-plugins/README.md b/pelican-plugins/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..b960572ecc67cfb79f5dd66587123bceb8d8a0ab
--- /dev/null
+++ b/pelican-plugins/README.md
@@ -0,0 +1,81 @@
+# Pelican on GitLab Pages
+
+Example [Pelican] website using GitLab Pages. Check the resulting website here: <https://pages.gitlab.io/pelican>
+
+Learn more about [GitLab Pages](https://about.gitlab.com/stages-devops-lifecycle/pages/) and the [official
+documentation](https://docs.gitlab.com/ce/user/project/pages/), including
+[how to fork a project like this one to get started](https://docs.gitlab.com/ee/user/project/pages/getting_started_part_two.html#fork-a-project-to-get-started-from).
+
+---
+
+## GitLab CI
+
+This project's static Pages are built by [GitLab CI][ci], following the steps
+defined in the file [`.gitlab-ci.yml`](.gitlab-ci.yml).
+
+## Building locally
+
+To work locally with this project, you'll have to follow the steps below:
+
+1. Fork, clone or download this project
+1. [Install][] Pelican
+1. Regenerate and serve on port 8000: `make devserver`
+1. Add content
+
+Read more at Pelican's [documentation].
+
+## GitLab User or Group Pages
+
+To use this project as your user/group website, you will need to perform
+some additional steps:
+
+1. Rename your project to `namespace.gitlab.io`, where `namespace` is
+your `username` or `groupname`. This can be done by navigating to your
+project's **Settings > General (Advanced)**.
+
+2. Adjust Pelican's `SITEURL` configuration setting in `publishconf.py` to
+the new URL (e.g. `https://namespace.gitlab.io`)
+
+Read more about [GitLab Pages for projects and user/group websites][pagesdoc].
+
+## Use a custom theme
+
+To use a custom theme:
+
+1. Visit <https://github.com/getpelican/pelican-themes> and pick the name of
+   the theme you want to use.
+1. Uncomment the following lines from `.gitlab-ci.yml`, replacing `<theme_name>`
+   with the name of the theme you chose:
+
+   ```yaml
+   - svn export https://github.com/getpelican/pelican-themes/trunk/<theme-name> /tmp/<theme-name>
+   - pelican-themes --install /tmp/<theme-name>
+   ```
+
+1. Edit `pelicanconf.py` and add the theme:
+
+   ```plaintext
+   THEME = '/tmp/<theme-name>'
+   ```
+
+For more information, see the discussion in [issue #1](https://gitlab.com/pages/pelican/-/issues/1).
+
+## Did you fork this project?
+
+If you forked this project for your own use, please go to your project's
+**Settings** and remove the forking relationship, which won't be necessary
+unless you want to contribute back to the upstream project.
+
+## Troubleshooting
+
+1. CSS is missing! That means two things:
+
+    Either that you have wrongly set up the CSS URL in your templates, or
+    your static generator has a configuration option that needs to be explicitly
+    set in order to serve static assets under a relative URL.
+
+[ci]: https://about.gitlab.com/gitlab-ci/
+[pelican]: http://blog.getpelican.com/
+[install]: https://docs.getpelican.com/en/stable/install.html
+[documentation]: http://docs.getpelican.com/
+[pagesdoc]: https://docs.gitlab.com/ce/user/project/pages/getting_started_part_one.html
diff --git a/pelican-plugins/Readme.rst b/pelican-plugins/Readme.rst
new file mode 100644
index 0000000000000000000000000000000000000000..bb22b980890ee58eefca49e6c1171231eb2e857f
--- /dev/null
+++ b/pelican-plugins/Readme.rst
@@ -0,0 +1,348 @@
+Pelican Plugins
+###############
+
+**Important note:** We are in the process of migrating plugins from this monolithic repository to their own individual repositories under the new `Pelican Plugins`_ organization, a place for plugin authors to collaborate more broadly with Pelican maintainers and other members of the community. The intention is for all the plugins under the new organization to be in the new “namespace plugin” format, which means these plugins can easily be Pip-installed and recognized immediately by Pelican 4.5+ — without having to explicitly enable them.
+
+This transition process will take some time, so we appreciate your patience in the interim. If you would like to help speed up this transition, the following would be very helpful:
+
+* **If you find a plugin here that has not yet been migrated to the new organization**, create a new issue under this repository and communicate which plugin you would like to help migrate, after which a Pelican maintainer will guide you through the process.
+
+* **If you have come here to submit a pull request to add your plugin**, please consider instead moving your plugin under the `Pelican Plugins`_ organization. To get started, create a new issue under this repository with the details of your plugin, after which a Pelican maintainer will guide you through the process.
+
+Whether you are creating a new plugin or migrating an existing plugin, please use the provided `Cookiecutter template <https://github.com/getpelican/cookiecutter-pelican-plugin>`_ to generate a scaffolded namespace plugin that conforms to community conventions. Have a look at the `Simple Footnotes <https://github.com/pelican-plugins/simple-footnotes>`_ repository to see an example of a migrated plugin.
+
+The rest of the information below is relevant for legacy plugins but not for the new namespace plugins found at the `Pelican Plugins`_ organization.
+
+.. _Pelican Plugins: https://github.com/pelican-plugins
+
+How to use plugins
+==================
+
+The easiest way to install and use these plugins is to clone this repo::
+
+    git clone --recursive https://github.com/getpelican/pelican-plugins
+
+and activate the ones you want in your settings file::
+
+    PLUGIN_PATHS = ['path/to/pelican-plugins']
+    PLUGINS = ['assets', 'sitemap', 'gravatar']
+
+``PLUGIN_PATHS`` can be a path relative to your settings file or an absolute path.
+
+Alternatively, if plugins are in an importable path, you can omit ``PLUGIN_PATHS``
+and list them::
+
+    PLUGINS = ['assets', 'sitemap', 'gravatar']
+
+or you can ``import`` the plugin directly and give that::
+
+    import my_plugin
+    PLUGINS = [my_plugin, 'assets']
+
+Plugin descriptions
+===================
+
+Migration status:
+
+* (blank): Local hosted plugin is still waiting for migration work.
+* ⚠️ : Deprecated. Can be safely removed from this repository.
+* ❓: Externally maintained plugins that do not need explicit migration from the mono-repo. Migration work need to happen in the original owners' repo.
+* ✔ : Repository has been migrated to `Pelican Plugins`_ organization.
+
+================================================================  ========================================================================  ===========================================================
+Plugin                                                            ℹ️                                                                          Description
+================================================================  ========================================================================  ===========================================================
+Ace Editor                                                        `❓ <https://github.com/mothsART/ace_editor>`_                             Replace default **<code>** by an Ace__ code editor with settings configure on pelicanconf.py.
+
+`Always modified <./always_modified>`_                                                                                                       Copy created date metadata into modified date for easy "latest updates" indexes
+
+`AsciiDoc reader <./asciidoc_reader>`_                                                                                                       Use AsciiDoc to write your posts.
+
+Asset management                                                  `✔  <https://github.com/pelican-plugins/webassets>`_                       Use the Webassets module to manage assets such as CSS and JS files.
+
+`Author images <./author_images>`_                                                                                                           Adds support for author images and avatars.
+
+`Auto Pages <./autopages>`_                                                                                                                  Generate custom content for generated Author, Category, and Tag pages (e.g. author biography)
+
+Backref Translate                                                 `❓ <https://github.com/daltonmatos/pelican-plugin-backref-translate>`_    Add a new attribute (``is_translation_of``) to every article/page (which is a translation) pointing back to the original article/page which is being translated
+
+Better code samples                                               `❓ <https://github.com/classner/better_code_samples>`_                    Wraps ``table`` blocks with ``div > .hilitewrapper > .codehilitetable`` class attribute, allowing for scrollable code blocks.
+
+`Better code line numbers <./better_codeblock_line_numbering>`_                                                                              Allow code blocks with line numbers to wrap
+
+`Better figures/samples <./better_figures_and_images>`_                                                                                      Adds a ``style="width: ???px; height: auto;"`` attribute to any ``<img>`` tags in the content
+
+`Better tables <./better_tables>`_                                                                                                           Removes the excess attributes and elements in the HTML tables generated from reST.
+
+`bootstrap-rst <./bootstrap-rst>`_                                                                                                           Provides most (though not all) of Bootstrap's features as rst directives
+
+bootstrapify                                                      `❓ <https://github.com/ingwinlu/pelican-bootstrapify>`_                   Automatically add bootstraps default classes to your content
+
+`Category meta <./category_meta>`_                                                                                                           Read metadata for each category from an index file in that category's directory.
+
+Category Order                                                    `❓ <https://github.com/jhshi/pelican.plugins.category_order>`_            Order categories (and tags) by the number of articles in that category (or tag).
+
+CJK auto spacing                                                  `❓ <https://github.com/yuex/cjk-auto-spacing>`_                           Inserts spaces between Chinese/Japanese/Korean characters and English words
+
+`Clean summary <./clean_summary>`_                                                                                                           Cleans your summary of excess images
+
+`Code include <./code_include>`_                                                                                                             Includes Pygments highlighted code in reStructuredText
+
+`Collate content <./collate_content>`_                                                                                                       Makes categories of content available to the template as lists through a ``collations`` attribute
+
+`Creole reader <./creole_reader>`_                                                                                                           Allows you to write your posts using the wikicreole syntax
+
+`CSS HTML JS Minify <./css-html-js-minify>`_                                                                                                 Minifies all CSS, HTML and JavaScript files in the output path after site generation.
+
+`CTags generator <./ctags_generator>`_                                                                                                       Generates a "tags" file following the CTags in the "content/" directory, to provide autocompletion for code editors that support it.
+
+`Custom article URLs <./custom_article_urls>`_                                                                                               Adds support for defining different default URLs for different categories
+
+`Dateish <./dateish>`_                                                                                                                       Treat arbitrary metadata fields as datetime objects
+
+Dead Links                                                        `❓ <https://github.com/silentlamb/pelican-deadlinks>`_                    Manage dead links (website not available, errors such as 403, 404)
+
+`Disqus static comments <./disqus_static>`_                                                                                                  Adds a disqus_comments property to all articles. Comments are fetched at generation time using disqus API
+
+Encrypt content                                                   `❓ <https://github.com/mindcruzer/pelican-encrypt-content>`_              Password protect pages and articles
+
+`Events <./events>`_                                                                                                                         Add event start, duration, and location info to post metadata to generate an iCalendar file
+
+`Extract table of content <./extract_toc>`_                                                                                                  Extracts table of contents (ToC) from ``article.content``
+
+`Feed summary <./feed_summary>`_                                  ⚠️                                                                          Allows article summaries to be used in ATOM and RSS feeds instead of the entire article.
+
+Figure References                                                 `❓ <https://github.com/cmacmackin/figure-ref>`_                           Provides a system to number and references figures
+
+`Filetime from Git <./filetime_from_git>`_                                                                                                   Uses Git commit to determine page date
+
+`Filetime from Hg <./filetime_from_hg>`_                                                                                                     Uses Mercurial commit to determine page date
+
+`Footer Insert <./footer_insert>`_                                                                                                           Add standardized footer (e.g., author information) at end of every article
+
+GA Page View                                                      `❓ <https://github.com/jhshi/pelican.plugins.ga_page_view>`_              Display Google Analytics page views on individual articles and pages
+
+`Gallery <./gallery>`_                                                                                                                       Allows an article to contain an album of pictures
+
+`Gist directive <./gist_directive>`_                                                                                                         This plugin adds a ``gist`` reStructuredText directive.
+
+`GitHub wiki <./github-wiki>`_                                                                                                               Converts a flat github wiki into a structured read only wiki on your site
+
+`GitHub activity <./github_activity>`_                                                                                                       On the template side, you just have to iterate over the ``github_activity`` variable
+
+`Global license <./global_license>`_                                                                                                         Allows you to define a ``LICENSE`` setting and adds the contents of that license variable to the article's context
+
+`Glossary <./glossary>`_                                                                                                                     Adds a variable containing definitions extracted from definition lists in articles and pages. This variable is visible to all page templates.
+
+`Goodreads activity <./goodreads_activity>`_                                                                                                 Lists books from your Goodreads shelves
+
+`GooglePlus comments <./googleplus_comments>`_                                                                                               Adds GooglePlus comments to Pelican
+
+Gravatar                                                          `✔  <https://github.com/pelican-plugins/avatar>`_                          This plugin's functionality has been superseded by the newer Avatar plugin
+
+`Gzip cache <./gzip_cache>`_                                                                                                                 Enables certain web servers (e.g., Nginx) to use a static cache of gzip-compressed files to prevent the server from compressing files during an HTTP call
+
+`Headerid <./headerid>`_                                                                                                                     This plugin adds an anchor to each heading so you can deeplink to headers in reStructuredText articles.
+
+`HTML entities <./html_entity>`_                                                                                                             Allows you to enter HTML entities such as &copy;, &lt;, &#149; inline in a RST document
+
+`HTML tags for rST <./html_rst_directive>`_                                                                                                  Allows you to use HTML tags from within reST documents
+
+`I18N Sub-sites <./i18n_subsites>`_                                                                                                          Extends the translations functionality by creating internationalized sub-sites for the default site
+
+`ical <./ical>`_                                                                                                                             Looks for and parses an ``.ics`` file if it is defined in a given page's ``calendar`` metadata.
+
+Image Process                                                     `✔  <https://github.com/pelican-plugins/image-process>`_                   Automates the processing of images based on their class attributes
+
+`Interlinks <./interlinks>`_                                                                                                                 Lets you add frequently used URLs to your markup using short keywords
+
+Jinja2 Content                                                    `✔  <https://github.com/pelican-plugins/jinja2content>`_                   Allows the use of Jinja2 template code in articles, including ``include`` and ``import`` statements. Replacement for pelican-jinja2content.
+
+`JPEG Reader <./jpeg_reader>`_                                                                                                               Create image gallery pages based on content of JPEG metadata
+
+Just table                                                        `❓ <https://github.com/burakkose/just_table>`_                            Allows you to easily create and manage tables. You can embed the tables into posts with a simple way.
+
+Libravatar                                                        `✔  <https://github.com/pelican-plugins/avatar>`_                          Allows inclusion of user profile pictures from libravatar.org
+
+Lightbox                                                          `❓ <https://github.com/kura/lightbox>`_                                   A pure CSS lightbox for Pelican.
+
+`Linker <./linker>`_                                                                                                                         Allows the definition of custom linker commands in analogy to the builtin ``{filename}``, ``{attach}``, ``{category}``, ``{tag}``, ``{author}``, and ``{index}`` syntax
+
+Liquid-style tags                                                 `✔  <https://github.com/pelican-plugins/liquid-tags>`_                     Allows liquid-style tags to be inserted into markdown within Pelican documents
+
+Load CSV                                                          `❓ <https://github.com/e9t/pelican-loadcsv>`_                             Adds ``csv`` Jinja tag to display the contents of a CSV file as an HTML table
+
+Markdown-metaYAML                                                 `⚠️  <https://github.com/joachimneu/pelican-md-metayaml>`_                  Pelican reader to enable YAML-style metadata in markdown articles. See also: `YAML Metadata <https://github.com/pelican-plugins/yaml-metadata>`_
+
+`Markdown Inline Extension <./md_inline_extension>`_                                                                                         Enables you to add customize inline patterns to your markdown
+
+`Members <./members>`_                                                                                                                       Looks for a members metadata header containing key/value pairs and makes them available for use in templates.
+
+More Categories                                                   `✔  <https://github.com/pelican-plugins/more-categories>`_                 Multiple categories per article; nested categories (`foo/bar, foo/baz`)
+
+Multi Neighbors                                                   `❓ <https://github.com/davidlesieur/multi_neighbors>`_                    Adds a list of newer articles and a list of older articles to every article's context.
+
+`Multi parts posts <./multi_part>`_                               ⚠️                                                                          Allows you to write multi-part posts
+
+MultiMarkdown reader                                              `❓ <https://github.com/dames57/multimarkdown_reader>`_                    A MultiMarkdown reader.
+
+Neighbor articles                                                 `✔  <https://github.com/pelican-plugins/neighbors>`_                       Adds ``next_article`` (newer) and ``prev_article`` (older) variables to the article's context
+
+`Optimize images <./optimize_images>`_                                                                                                       Applies lossless compression on JPEG and PNG images
+
+Pandoc Org Reader                                                 `❓ <https://github.com/jo-tham/org_pandoc_reader>`_
+
+`Python Org Reader <./org_python_reader>`_
+
+`Org Reader <./org_reader>`_                                                                                                                 Create posts via Emacs Orgmode files
+
+Pandoc reader                                                     `✔  <https://github.com/pelican-plugins/pandoc-reader>`_
+
+Panorama                                                          `❓ <https://github.com/romainx/panorama>`_                                Creates charts from posts metadata
+
+PDF Images                                                        `❓ <https://github.com/cmacmackin/pdf-img>`_                              If an img tag contains a PDF, EPS or PS file as a source, this plugin generates a PNG preview which will then act as a link to the original file.
+
+PDF Generator                                                     `✔  <https://github.com/pelican-plugins/pdf>`_                             Automatically exports articles and pages as PDF files
+
+Pelican Cite                                                      `❓ <https://github.com/cmacmackin/pelican-cite>`_                         Produces inline citations and a bibliography in articles and pages, using a BibTeX file.
+
+pelican-ert                                                       `❓ <https://github.com/nogaems/pelican-ert>`_                             Allows you to add estimated reading time of an article
+
+Pelican-flickr                                                    `❓ <https://github.com/La0/pelican-flickr>`_                              Brings your Flickr photos & sets into your static website
+
+Pelican Genealogy                                                 `❓ <https://github.com/zappala/pelican-genealogy>`_                       Add surnames and people so metadata and context can be accessed from within a theme to provide surname and person pages
+
+Pelican Gist tag                                                  `❓ <https://github.com/streeter/pelican-gist>`_                           Easily embed GitHub Gists in your Pelican articles
+
+Pelican Github Projects                                           `❓ <https://github.com/kura/pelican-githubprojects>`_                     Embed a list of your public GitHub projects in your pages
+
+Jupyter Notebooks                                                 `❓ <https://github.com/danielfrg/pelican-jupyter>`_                       Provides two modes to use Jupyter notebooks in Pelican.
+
+Pelican Jinja2Content                                             `⚠️  <https://github.com/joachimneu/pelican-jinja2content>`_                Allows the use of Jinja2 template code in articles, including ``include`` and ``import`` statements
+
+Lang Category                                                     `❓ <https://github.com/CNBorn/pelican-langcategory>`_                     Make languages behave the same as categories (visitor can browse articles in certain language).
+
+Pelican Link Class                                                `✔  <https://github.com/pelican-plugins/linkclass>`_                       Set class attribute of ``<a>`` elements according to whether the link is external or internal
+
+Pelican Mbox Reader                                               `❓ <https://github.com/TC01/pelican-mboxreader>`_                         Generate articles automatically via email, given a path to a Unix mbox
+
+Pelican Open graph                                                `❓ <https://github.com/whiskyechobravo/pelican-open_graph>`_              Generates Open Graph tags for your articles
+
+Pelican Page Hierarchy                                            `❓ <https://github.com/akhayyat/pelican-page-hierarchy>`_                 Creates a URL hierarchy for pages that matches the filesystem hierarchy of their sources
+
+Pelican Page Order                                                `❓ <https://github.com/akhayyat/pelican-page-order>`_                     Adds a ``page_order`` attribute to all pages if one is not defined.
+
+`pelican-rdf <./pelican-rdf>`_                                                                                                               Allows the processing of .rdf vocabularies, and the generation of a lightweight documentation.
+
+pelican-toc                                                       `❓ <https://github.com/ingwinlu/pelican-toc>`_                            Generates a Table of Contents and make it available to the theme via article.toc
+
+Version Generator                                                 `❓ <https://github.com/Shaked/pelican-version>`_                          A simple version generator which generates an incremented version file.
+
+`Pelican Comment System <./pelican_comment_system>`_                                                                                         Allows you to add static comments to your articles
+
+pelican_javascript                                                `❓ <https://github.com/mortada/pelican_javascript>`_                      Allows you to embed Javascript and CSS files into individual articles
+
+Pelican Meetup Info                                               `❓ <https://github.com/tylerdave/pelican-meetup-info>`_                   Include your Meetup.com group and event information on generated pages and articles
+
+`Unity WebGL <./pelican_unity_webgl>`_                                                                                                       Easily embed Unity3d games into posts and pages
+
+Pelican Vimeo                                                     `❓ <https://github.com/kura/pelican_vimeo>`_                              Enables you to embed Vimeo videos in your pages and articles
+
+Pelican YouTube                                                   `❓ <https://github.com/kura/pelican_youtube>`_                            Enables you to embed YouTube videos in your pages and articles
+
+pelicanfly                                                        `❓ <https://github.com/bmcorser/pelicanfly>`_                             Lets you type things like ``i ♥ :fa-coffee:`` in your Markdown documents and have it come out as little Font Awesome icons in the browser
+
+Pelican Themes Generator                                          `❓ <https://github.com/badele/pelicanthemes-generator>`_                  Generates theme screenshots from the Pelican Themes repository
+
+`permalink <./permalinks>`_                                                                                                                  Enables a kind of permalink using html redirects.
+
+`Photos <./photos>`_                                               `✔  <https://github.com/pelican-plugins/photos>`                          Add a photo or a gallery of photos to an article, or include photos in the body text. Resize photos as needed.
+
+Pin to top                                                        `❓ <https://github.com/Shaked/pin_to_top>`_                               Pin Pelican's article(s) to top "Sticky article"
+
+`PlantUML <./plantuml>`_                                                                                                                     Allows you to define UML diagrams directly into rst documents using the great PlantUML tool
+
+Post Revision                                                     `❓ <https://github.com/jhshi/pelican.plugins.post_revision>`_             Extract article and page revision information from Git commit history
+
+`Post statistics <./post_stats>`_                                                                                                            Calculates various statistics about a post and store them in an article.stats dictionary
+
+`Random article <./random_article>`_                                                                                                         Generates a html file which redirect to a random article
+
+Read More link                                                    `✔  <https://github.com/pelican-plugins/read-more>`_                       Inserts an inline "read more" or "continue" link into the last html element of the object summary
+
+`Readtime <./readtime>`_                                                                                                                     Adds article estimated read time calculator to the site, in the form of '<n> minutes'.
+
+`Reddit poster <./reddit_poster>`_                                                                                                           You can use the 'subreddit' attribute in you articles to specify which subbreddit the article should be post in aside of your default sub.
+
+Related posts                                                     `✔  <https://github.com/pelican-plugins/related-posts>`_                   Adds the ``related_posts`` variable to the article's context
+
+Render Math                                                       `✔  <https://github.com/pelican-plugins/render-math>`_                     Render mathematics in content via the MathJax Javascript engine
+
+Replacer                                                          `❓ <https://github.com/narusemotoki/replacer>`_                           Replace a text of a generated HTML
+
+`Representative image <./representative_image>`_                                                                                             Extracts a representative image (i.e, featured image) from the article's summary or content
+
+`RMD Reader <./rmd_reader>`_                                                                                                                 Create posts via knitr RMarkdown files
+
+`Section number <./section_number>`_                                                                                                         Adds section numbers for article headers, in the form of ``2.3.3``
+
+Series                                                            `✔  <https://github.com/pelican-plugins/series>`_                          Groups related articles into a series
+
+`Shaarli poster <./shaarli_poster>`_                                                                                                         Upload newly redacted articles onto a specified `Shaarli <https://github.com/shaarli/Shaarli>`__ instance.
+
+Share post                                                        `✔  <https://github.com/pelican-plugins/share-post>`_                      Creates share URLs for the current article
+
+`Shortcodes <./shortcodes>`_                                                                                                                 Easy and explicit inline jinja2 macros
+
+Show Source                                                       `✔  <https://github.com/pelican-plugins/show-source>`_                     Place a link to the source text of your posts.
+
+Similar Posts                                                     `✔  <https://github.com/pelican-plugins/similar-posts>`_                   Adds a list of similar posts to every article's context.
+
+Simple footnotes                                                  `✔  <https://github.com/pelican-plugins/simple-footnotes>`_                Adds footnotes to blog posts
+
+Sitemap                                                           `✔  <https://github.com/pelican-plugins/sitemap>`_                         Generates plain-text or XML sitemaps
+
+`Slim <./slim>`_                                                                                                                             Render theme template files via Plim, a Python port of Slim, instead of Jinja
+
+`Static comments <./static_comments>`_                                                                                                       Allows you to add static comments to an article
+
+`Sub parts <./sub_parts>`_                                                                                                                   Break a very long article in parts, without polluting the timeline with lots of small articles.
+
+`Subcategory <./subcategory>`_                                                                                                               Adds support for subcategories
+
+`Summary <./summary>`_                                                                                                                       Allows easy, variable length summaries directly embedded into the body of your articles
+
+Tag Cloud                                                         `✔  <https://github.com/pelican-plugins/tag-cloud>`_                       Provides a tag cloud of frequently-used post tags
+
+`Textile Reader <./textile_reader>`_                                                                                                         Adds support for Textile markup
+
+Thumbnailer                                                       `✔  <https://github.com/pelican-plugins/thumbnailer>`_                     Creates thumbnails for all of the images found under a specific directory
+
+`Tipue Search <./tipue_search>`_                                                                                                             Serializes generated HTML to JSON that can be used by jQuery plugin - Tipue Search
+
+`Touch <./touch>`_                                                `✔  <https://github.com/pelican-plugins/touch>`_                           Does a touch on your generated files using the date metadata from the content
+
+`Twitter Bootstrap <./twitter_bootstrap_rst_directives>`_                                                                                    Defines some rst directive that enable a clean usage of the twitter bootstrap CSS and Javascript components
+
+`txt2tags_reader <./txt2tags_reader>`_                                                                                                       Reader that renders txt2tags markup in content
+
+`Video Privacy Enhancer <./video_privacy_enhancer>`_                                                                                         Increases user privacy by stopping YouTube, Google, et al from placing cookies via embedded video
+
+`W3C validate <./w3c_validate>`_                                                                                                             Submits generated HTML content to the W3C Markup Validation Service
+
+Webring                                                           `✔  <https://github.com/pelican-plugins/webring>`_                         Add a webring to your site from a list of web feeds (e.g. RSS/Atom)
+
+`Yuicompressor <./yuicompressor>`_                                                                                                           Minify CSS and JS files on building step
+================================================================  ========================================================================  ===========================================================
+
+__ https://ace.c9.io
+
+Please refer to the ``Readme`` file in a plugin's folder for detailed information about
+that plugin.
+
+Contributing a plugin
+=====================
+
+Please refer to the `Contributing`_ file.
+
+.. _Contributing: Contributing.rst
diff --git a/pelican-plugins/__init__.py b/pelican-plugins/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/pelican-plugins/always_modified/README.md b/pelican-plugins/always_modified/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..585e67b5b540950f1b76b8c85d2b4fe51c8c78fa
--- /dev/null
+++ b/pelican-plugins/always_modified/README.md
@@ -0,0 +1,14 @@
+# Always Modified
+
+Say you want to sort by modified date/time in a theme template, but not all
+your articles have modified date/timestamps explicitly defined in article
+metadata. This plugin facilitates that sorting by assuming the modified date
+(if undefined) is equal to the created date.
+
+## Usage
+
+1. Add `ALWAYS_MODIFIED = True` to your settings file.
+2. Now you can sort by modified date in your templates:
+
+    {% for article in articles|sort(reverse=True,attribute='modified') %}
+
diff --git a/pelican-plugins/always_modified/__init__.py b/pelican-plugins/always_modified/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..1fd5e14d71b1a61b389a19d05313d231027b33b3
--- /dev/null
+++ b/pelican-plugins/always_modified/__init__.py
@@ -0,0 +1 @@
+from .always_modified import *
diff --git a/pelican-plugins/always_modified/always_modified.py b/pelican-plugins/always_modified/always_modified.py
new file mode 100644
index 0000000000000000000000000000000000000000..0f2578b6675199962a5c910075dad27c0b02cb07
--- /dev/null
+++ b/pelican-plugins/always_modified/always_modified.py
@@ -0,0 +1,20 @@
+"""
+If "modified" date/time is not defined in article metadata, fall back to the "created" date.
+"""
+
+from pelican import signals
+from pelican.contents import Content, Article
+
+def add_modified(content):
+    if not isinstance(content, Article):
+        return
+    
+    if not content.settings.get('ALWAYS_MODIFIED', False):
+        return
+
+    if hasattr(content, 'date') and not hasattr(content, 'modified'):
+        content.modified = content.date
+        content.locale_modified = content.locale_date
+    
+def register():
+    signals.content_object_init.connect(add_modified)
diff --git a/pelican-plugins/asciidoc_reader/README.rst b/pelican-plugins/asciidoc_reader/README.rst
new file mode 100644
index 0000000000000000000000000000000000000000..1700c286c3bb065d5b4c14298f6f4a78064af2b7
--- /dev/null
+++ b/pelican-plugins/asciidoc_reader/README.rst
@@ -0,0 +1,49 @@
+AsciiDoc Reader
+###############
+
+This plugin allows you to use `AsciiDoc <http://www.methods.co.nz/asciidoc/>`_
+to write your posts. File extension should be ``.asc``, ``.adoc``,
+or ``.asciidoc``.
+
+Dependency
+----------
+
+There are two command line utilities commonly used to render AsciiDoc:
+``asciidoc`` and ``asciidoctor``. One of the two will need to be installed and
+on the PATH.
+
+**Note**: The ``asciidoctor`` utility is recommended since the original
+``asciidoc`` is no longer maintained.
+
+Settings
+--------
+
+========================================  =======================================================
+Setting name (followed by default value)  What does it do?
+========================================  =======================================================
+``ASCIIDOC_CMD = 'asciidoc'``             Selects which utility to use for rendering. Will
+                                          autodetect utility if not provided.
+``ASCIIDOC_OPTIONS = []``                 A list of options to pass to AsciiDoc. See the `manpage
+                                          <http://www.methods.co.nz/asciidoc/manpage.html>`_.
+========================================  =======================================================
+
+Example file header
+-------------------
+
+Following the `example <https://github.com/getpelican/pelican/blob/master/docs/content.rst#file-metadata>`_ in the main pelican documentation:
+
+.. code-block:: none
+
+  = My super title
+
+  :date: 2010-10-03 10:20
+  :modified: 2010-10-04 18:40
+  :tags: thats, awesome
+  :category: yeah
+  :slug: my-super-post
+  :authors: Alexis Metaireau, Conan Doyle
+  :summary: Short version for index and feeds
+
+  == title level 2
+
+  and so on...
diff --git a/pelican-plugins/asciidoc_reader/__init__.py b/pelican-plugins/asciidoc_reader/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..5752357d263a7989356123569cd349607b86daf2
--- /dev/null
+++ b/pelican-plugins/asciidoc_reader/__init__.py
@@ -0,0 +1 @@
+from .asciidoc_reader import *
diff --git a/pelican-plugins/asciidoc_reader/asciidoc_reader.py b/pelican-plugins/asciidoc_reader/asciidoc_reader.py
new file mode 100644
index 0000000000000000000000000000000000000000..d4f90d76b2bb7a6c7810b51f651875f797ee2614
--- /dev/null
+++ b/pelican-plugins/asciidoc_reader/asciidoc_reader.py
@@ -0,0 +1,115 @@
+# -*- coding: utf-8 -*-
+"""
+AsciiDoc Reader
+===============
+
+This plugin allows you to use AsciiDoc to write your posts.
+File extension should be ``.asc``, ``.adoc``, or ``asciidoc``.
+"""
+
+from pelican.readers import BaseReader
+from pelican import signals
+import os
+import re
+import subprocess
+import sys
+import tempfile
+
+import logging
+logger = logging.getLogger(__name__)
+
+
+def encoding():
+    """Return encoding used to decode shell output in call function"""
+    if os.name == 'nt':
+        from ctypes import cdll
+        return 'cp' + str(cdll.kernel32.GetOEMCP())
+    return 'utf-8'
+
+def call(cmd):
+    """Calls a CLI command and returns the stdout as string."""
+    logger.debug('AsciiDocReader: Running: %s', cmd)
+    stdoutdata, stderrdata = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True).communicate()
+    if stderrdata:
+        logger.warning('AsciiDocReader: strderr: %s', stderrdata)
+    return stdoutdata.decode(encoding())
+
+def default():
+    """Attempt to find the default AsciiDoc utility."""
+    for cmd in ALLOWED_CMDS:
+        if len(call(cmd + " --help")):
+            logger.debug('AsciiDocReader: Using cmd: %s', cmd)
+            return cmd
+
+ALLOWED_CMDS = ["asciidoc", "asciidoctor"]
+
+ENABLED = None != default()
+
+class AsciiDocReader(BaseReader):
+    """Reader for AsciiDoc files."""
+
+    enabled = ENABLED
+    file_extensions = ['asc', 'adoc', 'asciidoc']
+    default_options = ['--no-header-footer']
+
+    def read(self, source_path):
+        """Parse content and metadata of AsciiDoc files."""
+        cmd = self._get_cmd()
+        content = ""
+        if cmd:
+            logger.debug('AsciiDocReader: Reading: %s', source_path)
+            optlist = self.settings.get('ASCIIDOC_OPTIONS', []) + self.default_options
+            options = " ".join(optlist)
+            # Beware! # Don't use tempfile.NamedTemporaryFile under Windows: https://bugs.python.org/issue14243
+            # Also, use mkstemp correctly (Linux and Windows): https://www.logilab.org/blogentry/17873
+            fd, temp_name = tempfile.mkstemp()
+            content = call("%s %s -o %s \"%s\"" % (cmd, options, temp_name, source_path))
+            with open(temp_name, encoding='utf-8') as f:
+                content = f.read()
+            os.close(fd)
+            os.unlink(temp_name)
+        metadata = self._read_metadata(source_path)
+        logger.debug('AsciiDocReader: Got content (showing first 50 chars): %s', (content[:50] + '...') if len(content) > 50 else content)
+        return content, metadata
+
+    def _get_cmd(self):
+        """Returns the AsciiDoc utility command to use for rendering or None if
+        one cannot be found."""
+        if self.settings.get('ASCIIDOC_CMD') in ALLOWED_CMDS:
+            return self.settings.get('ASCIIDOC_CMD')
+        return default()
+
+    def _read_metadata(self, source_path):
+        """Parses the AsciiDoc file at the given `source_path` and returns found
+        metadata."""
+        metadata = {}
+        with open(source_path, encoding='utf-8') as fi:
+            prev = ""
+            for line in fi.readlines():
+                # Parse for doc title.
+                if 'title' not in metadata.keys():
+                    title = ""
+                    if line.startswith("= "):
+                        title = line[2:].strip()
+                    elif line.count("=") == len(prev.strip()):
+                        title = prev.strip()
+                    if title:
+                        metadata['title'] = self.process_metadata('title', title)
+
+                # Parse for other metadata.
+                regexp = re.compile(r"^:\w+:")
+                if regexp.search(line):
+                    toks = line.split(":", 2)
+                    key = toks[1].strip().lower()
+                    val = toks[2].strip()
+                    metadata[key] = self.process_metadata(key, val)
+                prev = line
+        logger.debug('AsciiDocReader: Found metadata: %s', metadata)
+        return metadata
+
+def add_reader(readers):
+    for ext in AsciiDocReader.file_extensions:
+        readers.reader_classes[ext] = AsciiDocReader
+
+def register():
+    signals.readers_init.connect(add_reader)
diff --git a/pelican-plugins/asciidoc_reader/test_asciidoc_reader.py b/pelican-plugins/asciidoc_reader/test_asciidoc_reader.py
new file mode 100644
index 0000000000000000000000000000000000000000..46a4dac30e3fb3c90a07c3fa9f405a5fd2477213
--- /dev/null
+++ b/pelican-plugins/asciidoc_reader/test_asciidoc_reader.py
@@ -0,0 +1,70 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+import datetime
+import os
+
+from pelican.readers import Readers
+from pelican.tests.support import unittest, get_settings
+
+from .asciidoc_reader import ENABLED
+
+CUR_DIR = os.path.dirname(__file__)
+CONTENT_PATH = os.path.join(CUR_DIR, 'test_data')
+
+@unittest.skipUnless(ENABLED, "asciidoc isn't installed")
+class AsciiDocReaderTest(unittest.TestCase):
+    def read_file(self, path, **kwargs):
+        # Isolate from future API changes to readers.read_file
+        r = Readers(settings=get_settings(**kwargs))
+        return r.read_file(base_path=CONTENT_PATH, path=path)
+
+    def test_article_with_asc_extension(self):
+        # Ensure the asc extension is being processed by the correct reader
+        page = self.read_file(
+            path='article_with_asc_extension.asc')
+        expected = ('<div class="sect1">'
+                    '<h2 id="_used_for_pelican_test">'
+                    'Used for pelican test</h2>'
+                    '<div class="sectionbody">'
+                    '<div class="paragraph">'
+                    '<p>The quick brown fox jumped over '
+                    'the lazy dog&#8217;s back.</p>'
+                    '</div></div></div>')
+        actual = "".join(page.content.splitlines())
+        expected = "".join(expected.splitlines())
+        self.assertEqual(actual, expected)
+        expected = {
+            'category': 'Blog',
+            'author': 'Author O. Article',
+            'title': 'Test AsciiDoc File Header',
+            'date': datetime.datetime(2011, 9, 15, 9, 5),
+            'tags': ['Linux', 'Python', 'Pelican'],
+        }
+        for key, value in expected.items():
+            self.assertEqual(value, page.metadata[key], (
+                'Metadata attribute \'%s\' does not match expected value.\n'
+                'Expected: %s\n'
+                'Actual: %s') % (key, value, page.metadata[key]))
+
+    def test_article_with_asc_options(self):
+        # test to ensure the ASCIIDOC_OPTIONS is being used
+        page = self.read_file(path='article_with_asc_options.asc',
+            ASCIIDOC_OPTIONS=["-a revision=1.0.42"])
+        expected = ('<div class="sect1">'
+                    '<h2 id="_used_for_pelican_test">'
+                    'Used for pelican test</h2>'
+                    '<div class="sectionbody">'
+                    '<div class="paragraph">'
+                    '<p>version 1.0.42</p></div>'
+                    '<div class="paragraph">'
+                    '<p>The quick brown fox jumped over '
+                    'the lazy dog&#8217;s back.</p>'
+                    '</div></div></div>')
+        actual = "".join(page.content.splitlines())
+        expected = "".join(expected.splitlines())
+        self.assertEqual(actual, expected)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/pelican-plugins/asciidoc_reader/test_data/article_with_asc_extension.asc b/pelican-plugins/asciidoc_reader/test_data/article_with_asc_extension.asc
new file mode 100644
index 0000000000000000000000000000000000000000..3204d8f8fe367ad318458f51a7804cc06821645f
--- /dev/null
+++ b/pelican-plugins/asciidoc_reader/test_data/article_with_asc_extension.asc
@@ -0,0 +1,11 @@
+Test AsciiDoc File Header
+=========================
+:Author: Author O. Article
+:Email: <author@nowhere.com>
+:Date: 2011-09-15 09:05
+:Category: Blog
+:Tags: Linux, Python, Pelican
+
+== Used for pelican test
+
+The quick brown fox jumped over the lazy dog's back.
diff --git a/pelican-plugins/asciidoc_reader/test_data/article_with_asc_options.asc b/pelican-plugins/asciidoc_reader/test_data/article_with_asc_options.asc
new file mode 100644
index 0000000000000000000000000000000000000000..620abbaaa80236f51b15a49ff7b53181fcd787ee
--- /dev/null
+++ b/pelican-plugins/asciidoc_reader/test_data/article_with_asc_options.asc
@@ -0,0 +1,7 @@
+= Test AsciiDoc File Header
+
+== Used for pelican test
+
+version {revision}
+
+The quick brown fox jumped over the lazy dog's back.
diff --git a/pelican-plugins/assets/Readme.rst b/pelican-plugins/assets/Readme.rst
new file mode 100644
index 0000000000000000000000000000000000000000..eecac8fde4ae8d431c4aaf8bc797d36de7025474
--- /dev/null
+++ b/pelican-plugins/assets/Readme.rst
@@ -0,0 +1,108 @@
+Asset management
+----------------
+
+**NOTE:** `This plugin has been moved to its own repository <https://github.com/pelican-plugins/webassets>`_. Please file any issues/PRs there. Once all plugins have been migrated to the `new Pelican Plugins organization <https://github.com/pelican-plugins>`_, this monolithic repository will be archived.
+
+This plugin allows you to use the `Webassets`_ module to manage assets such as
+CSS and JS files. The module must first be installed::
+
+    pip install webassets
+
+The Webassets module allows you to perform a number of useful asset management
+functions, including:
+
+* CSS minifier (``cssmin``, ``yui_css``, ...)
+* CSS compiler (``less``, ``sass``, ...)
+* JS minifier (``uglifyjs``, ``yui_js``, ``closure``, ...)
+
+Others filters include CSS URL rewriting, integration of images in CSS via data
+URIs, and more. Webassets can also append a version identifier to your asset
+URL to convince browsers to download new versions of your assets when you use
+far-future expires headers. Please refer to the `Webassets documentation`_ for
+more information.
+
+When used with Pelican, Webassets is configured to process assets in the
+``OUTPUT_PATH/theme`` directory. You can use Webassets in your templates by
+including one or more template tags. The Jinja variable ``{{ ASSET_URL }}`` can
+be used in templates and is relative to the ``theme/`` url. The
+``{{ ASSET_URL }}`` variable should be used in conjunction with the
+``{{ SITEURL }}`` variable in order to generate URLs properly. For example:
+
+.. code-block:: jinja
+
+    {% assets filters="cssmin", output="css/style.min.css", "css/inuit.css", "css/pygment-monokai.css", "css/main.css" %}
+        <link rel="stylesheet" href="{{ SITEURL }}/{{ ASSET_URL }}">
+    {% endassets %}
+
+... will produce a minified css file with a version identifier that looks like:
+
+.. code-block:: html
+
+    <link href="http://{SITEURL}/theme/css/style.min.css?b3a7c807" rel="stylesheet">
+
+These filters can be combined. Here is an example that uses the SASS compiler
+and minifies the output:
+
+.. code-block:: jinja
+
+    {% assets filters="sass,cssmin", output="css/style.min.css", "css/style.scss" %}
+        <link rel="stylesheet" href="{{ SITEURL }}/{{ ASSET_URL }}">
+    {% endassets %}
+
+Another example for Javascript:
+
+.. code-block:: jinja
+
+    {% assets filters="uglifyjs", output="js/packed.js", "js/jquery.js", "js/base.js", "js/widgets.js" %}
+        <script src="{{ SITEURL }}/{{ ASSET_URL }}"></script>
+    {% endassets %}
+
+The above will produce a minified JS file:
+
+.. code-block:: html
+
+    <script src="http://{SITEURL}/theme/js/packed.js?00703b9d"></script>
+
+Pelican's debug mode is propagated to Webassets to disable asset packaging
+and instead work with the uncompressed assets.
+
+If you need to create named bundles (for example, if you need to compile SASS
+files before minifying with other CSS files), you can use the ``ASSET_BUNDLES``
+variable in your settings file. This is an ordered sequence of 3-tuples, where
+the 3-tuple is defined as ``(name, args, kwargs)``. This tuple is passed to the
+`environment's register() method`_. The following will compile two SCSS files
+into a named bundle, using the ``pyscss`` filter:
+
+.. code-block:: python
+
+    ASSET_BUNDLES = (
+        ('scss', ['colors.scss', 'main.scss'], {'filters': 'pyscss'}),
+    )
+
+Many of Webasset's available compilers have additional configuration options
+(i.e. 'Less', 'Sass', 'Stylus', 'Closure_js').  You can pass these options to
+Webassets using the ``ASSET_CONFIG`` in your settings file.
+
+The following will handle Google Closure's compilation level and locate
+LessCSS's binary:
+
+.. code-block:: python
+
+    ASSET_CONFIG = (('closure_compressor_optimization', 'WHITESPACE_ONLY'),
+                    ('less_bin', 'lessc.cmd'), )
+
+If you wish to place your assets in locations other than the theme output
+directory, you can use ``ASSET_SOURCE_PATHS`` in your settings file to provide
+webassets with a list of additional directories to search, relative to the
+theme's top-level directory:
+
+.. code-block:: python
+
+   ASSET_SOURCE_PATHS = [
+       'vendor/css',
+       'scss',
+       ]
+
+.. _Webassets: https://github.com/miracle2k/webassets
+.. _Webassets documentation: http://webassets.readthedocs.org/en/latest/builtin_filters.html
+.. _environment's register() method: http://webassets.readthedocs.org/en/latest/environment.html#registering-bundles
diff --git a/pelican-plugins/assets/__init__.py b/pelican-plugins/assets/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..67b75dd5e0b3deb4eadb195e952ad1abd1f0a815
--- /dev/null
+++ b/pelican-plugins/assets/__init__.py
@@ -0,0 +1 @@
+from .assets import *
diff --git a/pelican-plugins/assets/assets.py b/pelican-plugins/assets/assets.py
new file mode 100644
index 0000000000000000000000000000000000000000..e204dd65d85841bd5ec5415c6a3f633194df2fce
--- /dev/null
+++ b/pelican-plugins/assets/assets.py
@@ -0,0 +1,75 @@
+# -*- coding: utf-8 -*-
+"""
+Asset management plugin for Pelican
+===================================
+
+This plugin allows you to use the `webassets`_ module to manage assets such as
+CSS and JS files.
+
+The ASSET_URL is set to a relative url to honor Pelican's RELATIVE_URLS
+setting. This requires the use of SITEURL in the templates::
+
+    <link rel="stylesheet" href="{{ SITEURL }}/{{ ASSET_URL }}">
+
+.. _webassets: https://webassets.readthedocs.org/
+
+"""
+from __future__ import unicode_literals
+
+import os
+import logging
+
+from pelican import signals
+logger = logging.getLogger(__name__)
+
+try:
+    import webassets
+    from webassets import Environment
+    from webassets.ext.jinja2 import AssetsExtension
+except ImportError:
+    webassets = None
+
+def add_jinja2_ext(pelican):
+    """Add Webassets to Jinja2 extensions in Pelican settings."""
+
+    if 'JINJA_ENVIRONMENT' in pelican.settings: # pelican 3.7+
+        pelican.settings['JINJA_ENVIRONMENT']['extensions'].append(AssetsExtension)
+    else:
+        pelican.settings['JINJA_EXTENSIONS'].append(AssetsExtension)
+
+
+def create_assets_env(generator):
+    """Define the assets environment and pass it to the generator."""
+
+    theme_static_dir = generator.settings['THEME_STATIC_DIR']
+    assets_destination = os.path.join(generator.output_path, theme_static_dir)
+    generator.env.assets_environment = Environment(
+        assets_destination, theme_static_dir)
+
+    if 'ASSET_CONFIG' in generator.settings:
+        for item in generator.settings['ASSET_CONFIG']:
+            generator.env.assets_environment.config[item[0]] = item[1]
+
+    if 'ASSET_BUNDLES' in generator.settings:
+        for name, args, kwargs in generator.settings['ASSET_BUNDLES']:
+            generator.env.assets_environment.register(name, *args, **kwargs)
+
+    if 'ASSET_DEBUG' in generator.settings:
+        generator.env.assets_environment.debug = generator.settings['ASSET_DEBUG']
+    elif logging.getLevelName(logger.getEffectiveLevel()) == "DEBUG":
+        generator.env.assets_environment.debug = True
+
+    for path in (generator.settings['THEME_STATIC_PATHS'] +
+                 generator.settings.get('ASSET_SOURCE_PATHS', [])):
+        full_path = os.path.join(generator.theme, path)
+        generator.env.assets_environment.append_path(full_path)
+
+
+def register():
+    """Plugin registration."""
+    if webassets:
+        signals.initialized.connect(add_jinja2_ext)
+        signals.generator_init.connect(create_assets_env)
+    else:
+        logger.warning('`assets` failed to load dependency `webassets`.'
+                       '`assets` plugin not loaded.')
diff --git a/pelican-plugins/assets/requirements.txt b/pelican-plugins/assets/requirements.txt
new file mode 100755
index 0000000000000000000000000000000000000000..7725d6ac14a2a632e0b05da8bb2ac6aafd125204
--- /dev/null
+++ b/pelican-plugins/assets/requirements.txt
@@ -0,0 +1,2 @@
+cssmin
+webassets
\ No newline at end of file
diff --git a/pelican-plugins/assets/test_assets.py b/pelican-plugins/assets/test_assets.py
new file mode 100644
index 0000000000000000000000000000000000000000..0aaebf07984cb3a32385352966d3fcfd4a0025bb
--- /dev/null
+++ b/pelican-plugins/assets/test_assets.py
@@ -0,0 +1,113 @@
+# -*- coding: utf-8 -*-
+# from __future__ import unicode_literals
+
+import hashlib
+import locale
+import os
+from codecs import open
+from tempfile import mkdtemp
+from shutil import rmtree
+import unittest
+import subprocess
+
+from pelican import Pelican
+from pelican.settings import read_settings
+from pelican.tests.support import mute, skipIfNoExecutable, module_exists
+
+CUR_DIR = os.path.dirname(__file__)
+THEME_DIR = os.path.join(CUR_DIR, 'test_data')
+CSS_REF = open(os.path.join(THEME_DIR, 'static', 'css',
+                            'style.min.css')).read()
+CSS_HASH = hashlib.md5(CSS_REF.encode()).hexdigest()[0:8]
+
+
+@unittest.skipUnless(module_exists('webassets'), "webassets isn't installed")
+@skipIfNoExecutable(['sass', '-v'])
+@skipIfNoExecutable(['cssmin', '--version'])
+class TestWebAssets(unittest.TestCase):
+    """Base class for testing webassets."""
+
+    def setUp(self, override=None):
+        import assets
+        self.temp_path = mkdtemp(prefix='pelicantests.')
+        settings = {
+            'ASSET_CONFIG': [('sass_bin', 'scss')],
+            'PATH': os.path.join(os.path.dirname(CUR_DIR), 'test_data', 'content'),
+            'OUTPUT_PATH': self.temp_path,
+            'PLUGINS': [assets],
+            'THEME': THEME_DIR,
+            'LOCALE': locale.normalize('en_US'),
+            'CACHE_CONTENT': False
+        }
+        if override:
+            settings.update(override)
+
+        self.settings = read_settings(override=settings)
+        pelican = Pelican(settings=self.settings)
+        mute(True)(pelican.run)()
+
+    def tearDown(self):
+        rmtree(self.temp_path)
+
+    def check_link_tag(self, css_file, html_file):
+        """Check the presence of `css_file` in `html_file`."""
+
+        link_tag = ('<link rel="stylesheet" href="{css_file}">'
+                    .format(css_file=css_file))
+        html = open(html_file).read()
+        self.assertRegexpMatches(html, link_tag)
+
+
+class TestWebAssetsRelativeURLS(TestWebAssets):
+    """Test pelican with relative urls."""
+
+
+    def setUp(self):
+        TestWebAssets.setUp(self, override={'RELATIVE_URLS': True})
+
+    def test_jinja2_ext(self):
+        # Test that the Jinja2 extension was correctly added.
+
+        from webassets.ext.jinja2 import AssetsExtension
+        self.assertIn(AssetsExtension, self.settings['JINJA_ENVIRONMENT']['extensions'])
+
+    def test_compilation(self):
+        # Compare the compiled css with the reference.
+
+        gen_file = os.path.join(self.temp_path, 'theme', 'gen',
+                                'style.{0}.min.css'.format(CSS_HASH))
+        self.assertTrue(os.path.isfile(gen_file))
+
+        css_new = open(gen_file).read()
+        self.assertEqual(css_new, CSS_REF)
+
+    def test_template(self):
+        # Look in the output files for the link tag.
+
+        css_file = './theme/gen/style.{0}.min.css'.format(CSS_HASH)
+        html_files = ['index.html', 'archives.html',
+                      'this-is-a-super-article.html']
+        for f in html_files:
+            self.check_link_tag(css_file, os.path.join(self.temp_path, f))
+
+        self.check_link_tag(
+            '../theme/gen/style.{0}.min.css'.format(CSS_HASH),
+            os.path.join(self.temp_path, 'category/yeah.html'))
+
+
+class TestWebAssetsAbsoluteURLS(TestWebAssets):
+    """Test pelican with absolute urls."""
+
+    def setUp(self):
+        TestWebAssets.setUp(self, override={'RELATIVE_URLS': False,
+                                            'SITEURL': 'http://localhost'})
+
+    def test_absolute_url(self):
+        # Look in the output files for the link tag with absolute url.
+
+        css_file = ('http://localhost/theme/gen/style.{0}.min.css'
+                    .format(CSS_HASH))
+        html_files = ['index.html', 'archives.html',
+                      'this-is-a-super-article.html']
+        for f in html_files:
+            self.check_link_tag(css_file, os.path.join(self.temp_path, f))
diff --git a/pelican-plugins/assets/test_data/static/css/style.min.css b/pelican-plugins/assets/test_data/static/css/style.min.css
new file mode 100644
index 0000000000000000000000000000000000000000..daf9c3cbd3e561d167b3950414156684690f8136
--- /dev/null
+++ b/pelican-plugins/assets/test_data/static/css/style.min.css
@@ -0,0 +1 @@
+body{font:14px/1.5 "Droid Sans",sans-serif;background-color:#e4e4e4;color:#242424}a{color:red}a:hover{color:orange}
\ No newline at end of file
diff --git a/pelican-plugins/assets/test_data/static/css/style.scss b/pelican-plugins/assets/test_data/static/css/style.scss
new file mode 100644
index 0000000000000000000000000000000000000000..10cd05be7d1a4fb823902f2cfdc5906c0ca344b0
--- /dev/null
+++ b/pelican-plugins/assets/test_data/static/css/style.scss
@@ -0,0 +1,19 @@
+/* -*- scss-compile-at-save: nil -*- */
+
+$baseFontFamily : "Droid Sans", sans-serif;
+$textColor      : #242424;
+$bodyBackground : #e4e4e4;
+
+body {
+  font: 14px/1.5 $baseFontFamily;
+  background-color: $bodyBackground;
+  color: $textColor;
+}
+
+a {
+  color: red;
+
+  &:hover {
+    color: orange;
+  }
+}
diff --git a/pelican-plugins/assets/test_data/templates/base.html b/pelican-plugins/assets/test_data/templates/base.html
new file mode 100644
index 0000000000000000000000000000000000000000..05a32d0690c8d87e09cbfbfb39396b6fcb08de99
--- /dev/null
+++ b/pelican-plugins/assets/test_data/templates/base.html
@@ -0,0 +1,7 @@
+{% extends "!simple/base.html" %}
+
+{% block head %}
+  {% assets filters="scss,cssmin", output="gen/style.%(version)s.min.css", "css/style.scss" %}
+    <link rel="stylesheet" href="{{ SITEURL }}/{{ ASSET_URL }}">
+  {% endassets %}
+{% endblock %}
diff --git a/pelican-plugins/author_images/README.md b/pelican-plugins/author_images/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..a976204abbd53edd99407edd69f63c716bbb6e8a
--- /dev/null
+++ b/pelican-plugins/author_images/README.md
@@ -0,0 +1,55 @@
+# author_images
+
+This Pelican plugin adds support for author images and avatars. You may choose
+to display one or the other.
+
+## Configuration
+
+Add the directory to the base plugins directory to `PLUGIN_PATHS` in
+`pelicanconf.py`, and then add `author_images` to the `PLUGINS` list. For example,
+
+    PLUGIN_PATHS = ["../git/pelican-plugins"]
+    PLUGINS = ['author_images']
+
+You can also configure the directory for the author images and avatars. Note
+that both of these directories should exist in your theme, inside the static
+directory. This feels like the best way to approach this.
+
+    AUTHOR_AVATARS = 'images/author_avatars'
+    AUTHOR_IMAGES = 'images/author_images'
+
+### Adding images
+
+Now you can place images and avatars into the correct places. The location for
+these is `THEME / THEME_STATIC_DIR / AUTHOR_AVATARS`. For instance,
+`strudel/static/images/author_avatars` for my particular setup. Note that in
+this case, `strudel` is my theme.
+
+### Naming images
+
+Images have to named correctly for the plugin to find them. Currently, this
+means you need to take a `sha256` of the authors name. The extension of
+the file can be one of `svg`, `jpg`, `jpeg` or `png`. For instance, my name is
+William Pettersson, so I can run
+
+    python -c 'import hashlib; print(hashlib.sha256("William Pettersson".encode("UTF-8")).hexdigest())'
+
+to get the hash sum of my name. Then I just rename my images or avatars to have
+that name, but with the appropriate extension. For simplicity, there is a
+`generate_hashsum.py` which can also be used as follows
+
+    python generate_hashsum.py "William Pettersson"
+
+which prints out
+`a40249517dfaf4e83264ced7d802c9fe9b811c8425b1ce1b3e8b9e236b52fa3e`. This means
+my files have to be named
+`a40249517dfaf4e83264ced7d802c9fe9b811c8425b1ce1b3e8b9e236b52fa3e.png`, or
+`a40249517dfaf4e83264ced7d802c9fe9b811c8425b1ce1b3e8b9e236b52fa3e.jpg` or
+similar.
+
+
+### Using in themes
+
+These images and avatars are made available to themes through the
+`author.avatar` and `author.image` variables.
+
diff --git a/pelican-plugins/author_images/__init__.py b/pelican-plugins/author_images/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..b3ebd91859ed39ccd8a25c5c44f5fe5c05070456
--- /dev/null
+++ b/pelican-plugins/author_images/__init__.py
@@ -0,0 +1 @@
+from .author_images import *
diff --git a/pelican-plugins/author_images/author_images.py b/pelican-plugins/author_images/author_images.py
new file mode 100644
index 0000000000000000000000000000000000000000..4a8f5d52e24516d2c10b91407eedb949c9ca0b6a
--- /dev/null
+++ b/pelican-plugins/author_images/author_images.py
@@ -0,0 +1,47 @@
+"""
+Author images plugin for Pelican
+===========================
+
+This plugin assigns the ``author.avatar`` and ``author.image`` variables to the
+avatar and image of the author in question. Authors are identified by email
+address, and avatars are images are stored in directories configured by
+AUTHOR_AVATARS and AUTHOR_IMAGES.
+"""
+
+from pelican import signals
+from hashlib import sha256
+from os.path import exists
+
+EXTENSIONS = ['jpg', 'png', 'svg']
+
+
+def add_author_image(author, generator):
+    hashsum = sha256(author.name.encode("UTF-8")).hexdigest()
+    static = generator.settings['THEME'] + '/static/'
+    if 'AUTHOR_AVATARS' in generator.settings.keys():
+        avatar = generator.settings['AUTHOR_AVATARS'] + '/' + hashsum
+        for ext in EXTENSIONS:
+            if exists('%s%s.%s' % (static, avatar, ext)):
+                author.avatar = '%s/%s.%s' % \
+                    (generator.settings['THEME_STATIC_DIR'], avatar, ext)
+                break
+
+    if 'AUTHOR_IMAGES' in generator.settings.keys():
+        image = generator.settings['AUTHOR_IMAGES'] + '/' + hashsum
+        for ext in EXTENSIONS:
+            if exists('%s%s.%s' % (static, image, ext)):
+                author.image = '%s/%s.%s' % \
+                    (generator.settings['THEME_STATIC_DIR'], image, ext)
+                break
+
+
+def add_author_images(generator):
+    for article in generator.articles:
+        for author in article.authors:
+            add_author_image(author, generator)
+    for author, _ in generator.authors:
+        add_author_image(author, generator)
+
+
+def register():
+    signals.article_generator_finalized.connect(add_author_images)
diff --git a/pelican-plugins/author_images/generate_hashsum.py b/pelican-plugins/author_images/generate_hashsum.py
new file mode 100755
index 0000000000000000000000000000000000000000..bc169cb9992c2449d65b4ce7bdf2f62299b5833e
--- /dev/null
+++ b/pelican-plugins/author_images/generate_hashsum.py
@@ -0,0 +1,6 @@
+#!/usr/bin/env python3
+
+import hashlib
+import sys
+
+print(hashlib.sha256(sys.argv[1].encode("UTF-8")).hexdigest())
diff --git a/pelican-plugins/autopages/LICENSE b/pelican-plugins/autopages/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..44364d35775bb5d4655e9ef1f121c494f3da2368
--- /dev/null
+++ b/pelican-plugins/autopages/LICENSE
@@ -0,0 +1,28 @@
+Copyright (c) 2015, Magnetic Media Online, Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+   this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its
+   contributors may be used to endorse or promote products derived from this
+   software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
diff --git a/pelican-plugins/autopages/README.md b/pelican-plugins/autopages/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..a21140dbb7844f5dc84b363729a1bdac980e3d77
--- /dev/null
+++ b/pelican-plugins/autopages/README.md
@@ -0,0 +1,21 @@
+# Auto Pages
+
+This plugin adds an attribute `page` to the author, category, and tag
+objects which can be used in templates by themes. The page is processed as
+an ordinary Pelican page, so it can be Markdown, reStructuredText, etc.
+
+## Configuration
+
+| Setting              | Default      | Notes                                                                                                                                                               |
+|----------------------|--------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `AUTHOR_PAGE_PATH`   | `authors`    | The location, relative to the project root where author pages can be found. The filename of the author page minus the extension must match the Author's slug.       |
+| `CATEGORY_PAGE_PATH` | `categories` | The location, relative to the project root where category pages can be found. The filename of the category page minus the extension must match the Category's slug. |
+| `TAG_PAGE_PATH`      | `tags`       | The location, relative to the project root where tag pages can be found. The filename of the tag page minus the extension must match the Tag's slug.                |
+
+## Template Variables
+
+| Variable        | Notes                                |
+|-----------------|--------------------------------------|
+| `author.page`   | `Page` object for the author page.   |
+| `category.page` | `Page` object for the category page. |
+| `tag.page`      | `Page` object for the tag page.      |
diff --git a/pelican-plugins/autopages/__init__.py b/pelican-plugins/autopages/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..b562ddcb042a0414d3b22bf5a95183a72a4b2223
--- /dev/null
+++ b/pelican-plugins/autopages/__init__.py
@@ -0,0 +1 @@
+from .autopages import *
diff --git a/pelican-plugins/autopages/autopages.py b/pelican-plugins/autopages/autopages.py
new file mode 100644
index 0000000000000000000000000000000000000000..ea8220da0fec97c40f3889b27d7e2219c8b46bda
--- /dev/null
+++ b/pelican-plugins/autopages/autopages.py
@@ -0,0 +1,64 @@
+import logging
+import os
+import os.path
+
+from pelican import signals
+from pelican.contents import Page
+
+
+logger = logging.getLogger("autopages")
+
+def yield_files(root):
+    root = os.path.realpath(os.path.abspath(root))
+    for dirpath, dirnames, filenames in os.walk(root):
+        for dirname in list(dirnames):
+            try:
+                if dirname.startswith("."):
+                    dirnames.remove(dirname)
+            except IndexError:
+                # duplicate already removed?
+                pass
+        for filename in filenames:
+            if filename.startswith("."):
+                continue
+            yield os.path.join(dirpath, filename)
+
+def make_page(readers, context, filename):
+    base_path, filename = os.path.split(filename)
+    page = readers.read_file(base_path, filename, Page, None, context)
+    slug, _ = os.path.splitext(filename)
+    return slug, page
+
+def make_pages(readers, context, path):
+    pages = {}
+    for filename in yield_files(path):
+        try:
+            slug, page = make_page(readers, context, filename)
+        except Exception:
+            logger.exception("Could not make autopage for %r", filename)
+            continue
+        pages[slug] = page
+    return pages
+
+def create_autopages(article_generator):
+    settings = article_generator.settings
+    readers = article_generator.readers
+    context = article_generator.context
+
+    authors_path = settings.get("AUTHOR_PAGE_PATH", "authors")
+    categories_path = settings.get("CATEGORY_PAGE_PATH", "categories")
+    tags_path = settings.get("TAG_PAGE_PATH", "tags")
+
+    author_pages = make_pages(readers, context, authors_path)
+    category_pages = make_pages(readers, context, categories_path)
+    tag_pages = make_pages(readers, context, tags_path)
+
+    for author, _ in article_generator.authors:
+        author.page = author_pages.get(author.slug, "")
+    for category, _ in article_generator.categories:
+        category.page = category_pages.get(category.slug, "")
+    for tag in article_generator.tags:
+        tag.page = tag_pages.get(tag.slug, "")
+
+def register():
+    signals.article_generator_finalized.connect(create_autopages)
diff --git a/pelican-plugins/better_codeblock_line_numbering/Readme.md b/pelican-plugins/better_codeblock_line_numbering/Readme.md
new file mode 100644
index 0000000000000000000000000000000000000000..cd9eeca2f0abfff2f34edb593772a8825a73af53
--- /dev/null
+++ b/pelican-plugins/better_codeblock_line_numbering/Readme.md
@@ -0,0 +1,125 @@
+# Better Code Line Numbering Plugin
+
+## Copyright, Contact, and Acknowledgements
+
+This plugin is copyright 2014 Jacob Levernier  
+It is released under the BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause). This basically means that you can do whatever you want with the code, provided that you include this copyright and license notice.
+
+Some of this code is modified from my YouTube Privacy Enhancer plugin for Pelican (https://github.com/getpelican/pelican-plugins/pull/183).
+
+
+### To contact the author:
+
+* jleverni at uoregon dot edu  
+* http://AdUnumDatum.org  
+* BitBucket: https://bitbucket.org/jlev_uo/  
+* Github: https://github.com/publicus  
+
+This is the second plugin that I've written for Pelican, and it was intended as a training project for learning Pelican as well as Python better. I would be very happy to hear constructive feedback on the plugin and for suggestions for making it more efficient and/or expandable. Also, I've heavily annotated all of the Python code in order to make it easier to understand for others looking to learn more, like I was when I wrote the plugin.
+
+### Acknowledgements
+
+I'm grateful to the authors of the plugins in the pelican-plugins repo; being able to look over other plugins' authors' code helped me immensely in learning more about how Pelican's [signals](http://docs.getpelican.com/en/3.3.0/plugins.html#how-to-create-plugins "Pelican documentation on creating plugins") system works.
+
+
+## Explanation and Rationale
+
+Pelican uses Python's built-in code highlighting extension when processing Markdown. This extension, called Code HiLite (https://pythonhosted.org/Markdown/extensions/code_hilite.html), can add line numbers to any code that is enclosed in triple backticks (and, by default, has a shebang as a first line), like this:
+
+```
+#!python
+
+code goes here
+
+more code goes here
+```
+
+This works well, except for one problem: If the lines of code are long (or if the page template is narrow), the code will run off of the page, requiring that the user scroll sideways to read it. This can be annoying in some circumstances (e.g., if the code block scrollbar is at the very bottom of a long block of code). The Code HiLite Python extension creates line numbers by making a table with all of the line numbers in one column (as a big line of text, not separated into different table rows) and the code in the second column, like this:
+
+Column 1  | Column 2  
+--------- | -------------  
+1         | Code line 1 goes here, and maybe is very very long.  
+2         | Code line 2 goes here.  
+
+It is possible to use CSS to get this code to wrap, but the line numbers can become mis-matched with the code to which they're supposed to refer, like this:
+
+Column 1  | Column 2  
+--------- | -------------  
+1         | Code line 1 goes here, and  
+2         | maybe is very very long.  
+          | Code line 2 goes here.
+
+This plugin enables the use of a CSS technique from http://bililite.com/blog/2012/08/05/line-numbering-in-pre-elements/ . All that this plugin does is wrap every individual line of a code block with <span class="code-line">...</span>. When you combine this with the CSS that's included in the setup instructions below, your code blocks will word-wrap and have nicely formatted line numbers. Since they're added with CSS, the line numbers will not be highlighted when a user wants to copy and paste the contents of the code block, making it easier for the user to benefit from what you've written.
+
+
+## Usage
+
+**After you set up the plugin (by following the steps below), any code written in triple backticks will have line numbers added to it.** Thus, to avoid line numbers, you can use a single backtick to include code `like this`, and can add line numbers by using three backticks
+
+```
+like this
+```
+
+That's all there is to it!
+
+Since this plugin builds on the Code HiLite plugin, you can change syntax highlighting by using one of two methods:
+
+```{python}
+This code will highlight as python
+```
+
+```
+#!python
+This code will also highlight as python
+```
+
+
+**In order for this plugin to work optimally, you need to do just a few things:**
+
+1. Enable the plugin in pelicanconf.py (see http://docs.getpelican.com/en/3.3.0/plugins.html for documentation):  
+    PLUGIN_PATH = "/pelican-plugins"  
+    PLUGINS = ["better_codeblock_line_numbering"]
+
+2. Add the following to your pelicanconf.py file:  
+```
+MD_EXTENSIONS = [
+    'codehilite(css_class=highlight,linenums=False)',
+    'extra'
+    ]
+```  
+This sets python's CodeHiLite Markdown extension (http://pythonhosted.org/Markdown/extensions/code_hilite.html) so that it never assigns line numbers (since we're taking care of those ourselves now), and to wrap code blocks in a div with class="highlight". As is the default for Pelican (see http://docs.getpelican.com/en/3.1.1/settings.html, under "MD_EXTENSIONS"), this also keeps the 'extra' extension (http://pythonhosted.org/Markdown/extensions/extra.html) active.
+
+3. Add the following code to your CSS file:  
+```
+/* For use with the code_line-number_word-wrap_switcher_jquery.js Pelican plugin */
+code {
+	overflow: auto;
+	/* This uses `white-space: pre-wrap` to get elements within <pre> tags to wrap. Python, for code chunks within three backticks (```), doesn't wordwrap code lines by default, because they're within <pre> tags, which don't wrap by default. See https://github.com/github/markup/issues/168 , which is specifically about this parsing issue, even though that link's discussion is talking about GitHub. */
+	white-space: pre-wrap;       /* css-3 */
+    white-space: -moz-pre-wrap;  /* Mozilla, since 1999 */
+    white-space: -pre-wrap;      /* Opera 4-6 */
+    white-space: -o-pre-wrap;    /* Opera 7 */
+    word-wrap: break-word;       /* Internet Explorer 5.5+ */
+}
+
+/* Following http://bililite.com/blog/2012/08/05/line-numbering-in-pre-elements/, use CSS to add line numbers to all spans that have the class 'code-line' */
+
+.highlight pre {
+    counter-reset: linecounter;
+    padding-left: 2em;
+}
+.highlight pre span.code-line {
+	counter-increment: linecounter;
+    padding-left: 1em;
+	text-indent: -1em;
+	display: inline-block;
+}
+.highlight pre span.code-line:before {
+    content: counter(linecounter);
+    padding-right: 1em;
+    display: inline-block;
+    color: grey;
+    text-align: right;
+}
+```
+
diff --git a/pelican-plugins/better_codeblock_line_numbering/__init__.py b/pelican-plugins/better_codeblock_line_numbering/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..fe59e18110094ea5bf6ad633224b1dddc5b4853f
--- /dev/null
+++ b/pelican-plugins/better_codeblock_line_numbering/__init__.py
@@ -0,0 +1 @@
+from .better_codeblock_line_numbering import *
diff --git a/pelican-plugins/better_codeblock_line_numbering/better_codeblock_line_numbering.py b/pelican-plugins/better_codeblock_line_numbering/better_codeblock_line_numbering.py
new file mode 100644
index 0000000000000000000000000000000000000000..2cfd7b0d7d0166c037692bd224d2c41604189ac3
--- /dev/null
+++ b/pelican-plugins/better_codeblock_line_numbering/better_codeblock_line_numbering.py
@@ -0,0 +1,47 @@
+"""
+Better Code-Block Line Numbering Plugin
+--------------------------
+
+Authored by Jacob Levernier, 2014
+Released under the BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause)
+
+For more information on this plugin, please see the attached Readme.md file.
+"""
+
+from pelican import signals # For making this plugin work with Pelican.
+
+import os.path # For checking whether files are present in the filesystem.
+
+import re # For using regular expressions.
+
+def add_line_wrappers(data_passed_from_pelican):
+    """A function to read through each page and post as it comes through from Pelican, find all instances of triple-backtick (```...```) code blocks, and add an HTML wrapper to each line of each of those code blocks"""
+
+    if data_passed_from_pelican._content: # If the item passed from Pelican has a "content" attribute (i.e., if it's not an image file or something else like that). NOTE: data_passed_from_pelican.content (without an underscore in front of 'content') seems to be read-only, whereas data_passed_from_pelican._content is able to be overwritten. This is somewhat explained in an IRC log from 2013-02-03 from user alexis to user webdesignhero_ at https://botbot.me/freenode/pelican/2013-02-01/?tz=America/Los_Angeles.
+        full_content_of_page_or_post = data_passed_from_pelican._content
+    else:
+        return # Exit the function, essentially passing over the (non-text) file.
+
+    all_instances_of_pre_elements = re.findall('<pre>.*?</pre>', full_content_of_page_or_post, re.DOTALL) # Use a regular expression to find every instance of '<pre>' followed by anything up to the first matching '</pre>'. re.DOTALL puts python's regular expression engine ('re') into a mode where a dot ('.') matches absolutely anything, including newline characters.
+    
+    if(len(all_instances_of_pre_elements) > 0): # If the article/page HAS any <pre>...</pre> elements, go on. Otherwise, don't (to do so would inadvertantly wipe out the output content for that article/page).
+        updated_full_content_of_page_or_post = full_content_of_page_or_post # This just gives this an initial value before going into the loop below.
+
+        # Go through each <pre> element instance that we found above, and parse it:
+        for pre_element_to_parse in all_instances_of_pre_elements:
+
+            # Wrap each line of the <pre>...</pre> section with <span class=code-line>...</span>, following http://bililite.com/blog/2012/08/05/line-numbering-in-pre-elements/. We'll use these to add line numbers using CSS later.
+            # Note that below, '^' is the beginning of a string, '$' is the end of a string, and '\n' is a newline.
+            replacement_text_with_beginning_of_each_line_wrapped_in_span = re.sub(r'(<pre.*?>|\n(?!</pre>))','\\1<span class="code-line">',pre_element_to_parse) # The (?!...) here is a Negative Lookahead (cf. http://www.regular-expressions.info/lookaround.html). This full regular expression says "Give me all code snippets that start with <pre ****> or start with a newline (\n), but NOT if the newline is followed immediately with '</pre>'. Take whatever you find, and replace it with what you found (\1) followed immediately by '<span class="code-lines">'.
+            # http://stackoverflow.com/a/14625628 explains why we need to escape the backslash in the capture group reference (the '\1'). In short, python will recognize it as "\x01" if it's not escaped.
+            replacement_text_with_full_line_wrapped_in_span = re.sub(r'((?<!</pre>)$|(?<!</pre>)\n)','</span>\\1',replacement_text_with_beginning_of_each_line_wrapped_in_span) # This regular expression says "Give me all code snippets that are the end of a string or a newline (but not preceeded by "</pre>" (this is a 'negative lookahead,' '(?<)'), and replace whatever you found with '</span'> followed by whatever you found (\1).
+            
+            updated_full_content_of_page_or_post = updated_full_content_of_page_or_post.replace(pre_element_to_parse,replacement_text_with_full_line_wrapped_in_span)
+
+        # Replace the content of the page or post with our now-updated content (having gone through all instances of <pre> elements and updated them all, exiting the loop above.
+        data_passed_from_pelican._content = updated_full_content_of_page_or_post
+
+
+# Make Pelican work (see http://docs.getpelican.com/en/3.3.0/plugins.html#how-to-create-plugins):
+def register():
+    signals.content_object_init.connect(add_line_wrappers)
diff --git a/pelican-plugins/better_figures_and_images/README.rst b/pelican-plugins/better_figures_and_images/README.rst
new file mode 100644
index 0000000000000000000000000000000000000000..905f009d203dedc69f9e2252b3ab19f2f5095eee
--- /dev/null
+++ b/pelican-plugins/better_figures_and_images/README.rst
@@ -0,0 +1,54 @@
+Requirements
+------------
+
+* pip install pillow beautifulsoup4 pysvg-py3
+
+Summary
+=======
+
+This plug-in:
+
+- Adds a ``style="width: ???px; height: auto;"`` attribute to any ``<img>`` tags in the content, by checking the dimensions of the image file and adding the appropriate ``style="width: ???px; height: auto;"`` to the ``<img>`` tag.
+
+- Also finds any ``div class="figures"`` tags in the content, that contain images and adds the same style to them too.
+
+- If ``RESPONSIVE_IMAGES`` setting is true, it adds ``style="width: ???px; max-width: 100%; height: auto;"`` instead.
+
+- Corrects Alt text: If an img ``alt`` attribute equals the image filename, it sets it to ""
+
+
+Assuming that the image is 250px wide, it turns this::
+
+  <div class="figure">
+    <img alt="/static/images/image.jpg" src="/static/images/image.jpg" />
+    <p class="caption">
+      This is the caption of the figure.
+    </p>
+    <div class="legend">
+      Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod 	        tempor incididunt ut labore et dolore magna aliqua.
+    </div>
+  </div>
+
+into this::
+
+  <div class="figure" style="width: 250px; height: auto;">
+    <img style="width: 250px; height: auto;" alt="" src="/static/images/image.jpg" />
+    <p class="caption">
+      This is the caption of the figure.
+    </p>
+    <div class="legend">
+      Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
+    </div>
+  </div>
+
+or this, if ``RESPONSIVE_IMAGES = True``::
+
+  <div class="figure" style="width: 250px; max-width: 100%; height: auto;">
+    <img style="width: 250px; max-width: 100%; height: auto;" alt="" src="/static/images/image.jpg" />
+    <p class="caption">
+      This is the caption of the figure.
+    </p>
+    <div class="legend">
+      Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
+    </div>
+  </div>
diff --git a/pelican-plugins/better_figures_and_images/__init__.py b/pelican-plugins/better_figures_and_images/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..b8fefc4d4b49f6906d858b6fe3840ab75b3c48d9
--- /dev/null
+++ b/pelican-plugins/better_figures_and_images/__init__.py
@@ -0,0 +1 @@
+from .better_figures_and_images import *
diff --git a/pelican-plugins/better_figures_and_images/better_figures_and_images.py b/pelican-plugins/better_figures_and_images/better_figures_and_images.py
new file mode 100644
index 0000000000000000000000000000000000000000..91e65c098b07f00c26d934dd84fce2516959afcd
--- /dev/null
+++ b/pelican-plugins/better_figures_and_images/better_figures_and_images.py
@@ -0,0 +1,140 @@
+"""
+Better Figures & Images
+------------------------
+
+This plugin:
+
+- Adds a style="width: ???px; height: auto;" to each image in the content
+- Also adds the width of the contained image to any parent div.figures.
+    - If RESPONSIVE_IMAGES == True, also adds style="max-width: 100%;"
+- Corrects alt text: if alt == image filename, set alt = ''
+
+TODO: Need to add a test.py for this plugin.
+
+"""
+
+from __future__ import unicode_literals
+from os import path, access, R_OK
+import os
+
+from pelican import signals
+
+from bs4 import BeautifulSoup
+from PIL import Image
+import pysvg.parser
+import cssutils
+
+import logging
+logger = logging.getLogger(__name__)
+
+def content_object_init(instance):
+
+    if instance._content is not None:
+        content = instance._content
+        soup = BeautifulSoup(content, 'html.parser')
+
+        for img in soup(['img', 'object']):
+            logger.debug('Better Fig. PATH: %s', instance.settings['PATH'])
+            if img.name == 'img':
+                logger.debug('Better Fig. img.src: %s', img['src'])
+                img_path, img_filename = path.split(img['src'])
+            else:
+                logger.debug('Better Fig. img.data: %s', img['data'])
+                img_path, img_filename = path.split(img['data'])
+            logger.debug('Better Fig. img_path: %s', img_path)
+            logger.debug('Better Fig. img_fname: %s', img_filename)
+
+            # If the image already has attributes... then we can skip it. Assuming it's already optimised
+            if 'style' in img.attrs:
+                sheet = cssutils.parseStyle(img['style'])
+                if len(sheet.width) > 0 or len(sheet.height) > 0:
+                    continue
+
+            # Pelican 3.5+ supports {attach} macro for auto copy, in this use case the content does not exist in output
+            # due to the fact it has not been copied, hence we take it from the source (same as current document)
+            src = None
+            if img_filename.startswith('{attach}'):
+                img_path = os.path.dirname(instance.source_path)
+                img_filename = img_filename[8:]
+                src = os.path.join(img_path, img_filename)
+            elif img_path.startswith(('{filename}', '|filename|')):
+                # Strip off {filename}, |filename| or /static
+                img_path = img_path[10:]
+            elif img_path.startswith('{static}'):
+                img_path = img_path[8:]
+            elif img_path.startswith('/static'):
+                img_path = img_path[7:]
+            elif img_path.startswith('data:image'):
+                # Image is encoded in-line (not a file).
+                continue
+            else:
+                # Check the location in the output as some plugins create them there.
+                output_path = path.dirname(instance.save_as)
+                image_output_location = path.join(instance.settings['OUTPUT_PATH'], output_path, img_filename)
+                if path.isfile(image_output_location):
+                    src = image_output_location
+                    logger.info('{src} located in output, missing from content.'.format(src=img_filename))
+                else:
+                    logger.warning('Better Fig. Error: img_path should start with either {attach}, {filename}, |filename|, {static} or /static')
+
+            if src is None:
+                # search src path list
+                # 1. Build the source image filename from PATH
+                # 2. Build the source image filename from STATIC_PATHS
+
+                # if img_path start with '/', remove it.
+                img_path = os.path.sep.join([el for el in img_path.split("/") if len(el) > 0])
+
+                # style: {filename}/static/foo/bar.png
+                src = os.path.join(instance.settings['PATH'], img_path, img_filename)
+                src_candidates = [src]
+
+                # style: {filename}../static/foo/bar.png
+                src_candidates += [os.path.join(instance.settings['PATH'], static_path, img_path, img_filename) for static_path in instance.settings['STATIC_PATHS']]
+
+                src_candidates = [f for f in src_candidates if path.isfile(f) and access(f, R_OK)]
+
+                if not src_candidates:
+                    logger.error('Better Fig. Error: image not found: %s', src)
+                    logger.debug('Better Fig. Skip src: %s', img_path + '/' + img_filename)
+                    continue
+
+                src = src_candidates[0]
+            logger.debug('Better Fig. src: %s', src)
+
+            # Open the source image and query dimensions; build style string
+            try:
+                if img.name == 'img':
+                    im = Image.open(src)
+                    extra_style = 'width: {}px; height: auto;'.format(im.size[0])
+                else:
+                    svg = pysvg.parser.parse(src)
+                    extra_style = 'width: {}px; height: auto;'.format(svg.get_width())
+            except IOError as e:
+                logger.debug('Better Fig. Failed to open: %s', src)
+                extra_style = 'width: 100%; height: auto;'
+
+            if 'RESPONSIVE_IMAGES' in instance.settings and instance.settings['RESPONSIVE_IMAGES']:
+                extra_style += ' max-width: 100%;'
+
+            if img.get('style'):
+                img['style'] += extra_style
+            else:
+                img['style'] = extra_style
+
+            if img.name == 'img':
+                if img['alt'] == img['src']:
+                    img['alt'] = ''
+
+            fig = img.find_parent('div', 'figure')
+            if fig:
+                if fig.get('style'):
+                    fig['style'] += extra_style
+                else:
+                    fig['style'] = extra_style
+
+        instance._content = soup.decode()
+
+
+def register():
+    signals.content_object_init.connect(content_object_init)
diff --git a/pelican-plugins/better_figures_and_images/requirements.txt b/pelican-plugins/better_figures_and_images/requirements.txt
new file mode 100644
index 0000000000000000000000000000000000000000..f0ca748822fe152ce470df18be30f396d609305f
--- /dev/null
+++ b/pelican-plugins/better_figures_and_images/requirements.txt
@@ -0,0 +1,4 @@
+beautifulsoup4
+cssutils
+pillow
+pysvg-py3
\ No newline at end of file
diff --git a/pelican-plugins/better_figures_and_images/test_data/dummy-200x200.png b/pelican-plugins/better_figures_and_images/test_data/dummy-200x200.png
new file mode 100644
index 0000000000000000000000000000000000000000..598d754f7f76e8605c48ceae31a8681f4165dc56
Binary files /dev/null and b/pelican-plugins/better_figures_and_images/test_data/dummy-200x200.png differ
diff --git a/pelican-plugins/better_figures_and_images/test_data/dummy-250x300.png b/pelican-plugins/better_figures_and_images/test_data/dummy-250x300.png
new file mode 100644
index 0000000000000000000000000000000000000000..f21336a86a999a855a34e16bb47afdf08400afc5
Binary files /dev/null and b/pelican-plugins/better_figures_and_images/test_data/dummy-250x300.png differ
diff --git a/pelican-plugins/better_figures_and_images/test_data/dummy-800x300.png b/pelican-plugins/better_figures_and_images/test_data/dummy-800x300.png
new file mode 100644
index 0000000000000000000000000000000000000000..4122c8818d08faab78b83ab8cf9c1d245339cd8f
Binary files /dev/null and b/pelican-plugins/better_figures_and_images/test_data/dummy-800x300.png differ
diff --git a/pelican-plugins/better_figures_and_images/test_data/images-test.html b/pelican-plugins/better_figures_and_images/test_data/images-test.html
new file mode 100644
index 0000000000000000000000000000000000000000..98a8e64c6982ab3fc7320fa13d1fc09f00dad2d3
--- /dev/null
+++ b/pelican-plugins/better_figures_and_images/test_data/images-test.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<html lang="en">
+    <head>
+        <meta charset="utf-8">
+        <title>Better Figures &amp; Images Test</title>
+    </head>
+    <body>
+        <div class="figure">
+            <img alt="map to buried treasure" src="/static/images/dummy-200x200.png">
+            <p class="caption">
+                This is the caption of the figure (a simple paragraph).
+            </p>
+            <div class="legend">
+                The legend consists of all elements after the caption. In this case, the legend consists of this paragraph.
+            </div>
+        </div>
+        <p>
+            Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
+        </p>
+        <div class="figure align-right">
+            <img alt="map to buried treasure 2" src="/static/images/dummy-250x300.png">
+            <p class="caption">
+                This is the second image caption.
+            </p>
+            <div class="legend">
+                Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
+            </div>
+        </div>
+        <p>
+            Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
+        </p>
+        <img alt="/static/images/dummy-200x200.png" src="/static/images/dummy-200x200.png">
+        <p>
+            Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
+        </p>
+        <p>
+            Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
+        </p>
+        <img alt="/static/images/dummy-250x300.png" class="align-right" src="/static/images/dummy-250x300.png">
+        <p>
+            Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
+        </p>
+    </body>
+</html>
\ No newline at end of file
diff --git a/pelican-plugins/better_figures_and_images/test_data/images-test.rst b/pelican-plugins/better_figures_and_images/test_data/images-test.rst
new file mode 100644
index 0000000000000000000000000000000000000000..b1104ba97f8b5943e27fb5d1ab72e15f417d2ab4
--- /dev/null
+++ b/pelican-plugins/better_figures_and_images/test_data/images-test.rst
@@ -0,0 +1,75 @@
+:title: Images Test
+:slug: images-test
+:date: 2013-04-23 15:59:39
+:tags: images, test
+
+.. figure:: /static/images/dummy-800x300.png
+
+	This is the caption of the figure (a simple paragraph).
+
+	The legend consists of all elements after the caption.  In this
+	case, the legend consists of this paragraph.
+
+Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
+tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
+quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
+consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
+cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
+proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
+
+.. figure:: /static/images/dummy-200x200.png
+	:alt: map to buried treasure
+
+	This is the caption of the figure (a simple paragraph).
+
+	The legend consists of all elements after the caption.  In this
+	case, the legend consists of this paragraph.
+
+Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
+tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
+quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
+consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
+cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
+proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
+
+.. figure:: /static/images/dummy-250x300.png
+	:alt: map to buried treasure 2
+	:align: right
+
+	This is the second image caption.
+
+	Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
+	tempor incididunt ut labore et dolore magna aliqua.
+
+Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
+tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
+quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
+consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
+cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
+proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
+
+.. image:: /static/images/dummy-200x200.png
+
+Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
+tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
+quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
+consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
+cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
+proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
+
+Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
+tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
+quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
+consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
+cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
+proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
+
+.. image:: /static/images/dummy-250x300.png
+	:align: right
+
+Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
+tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
+quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
+consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
+cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
+proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
\ No newline at end of file
diff --git a/pelican-plugins/better_tables/LICENSE b/pelican-plugins/better_tables/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..e41460ac25789bcf688b13e56608ad77a74fc597
--- /dev/null
+++ b/pelican-plugins/better_tables/LICENSE
@@ -0,0 +1,18 @@
+Copyright (c) 2015 Alex Waite
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/pelican-plugins/better_tables/README.md b/pelican-plugins/better_tables/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..7c456f410a94b8c9e809837368e568dfd4acc8e8
--- /dev/null
+++ b/pelican-plugins/better_tables/README.md
@@ -0,0 +1,38 @@
+# Better Tables
+
+This Pelican plugin removes the excess attributes and elements in the HTML
+tables generated from reST. Trimming this fat allows them to pass HTML5
+validation. Hopefully `rst2html5` will be merged into Pelican at some point, but
+until then, this hacky approach is needed.
+
+This approach has the advantage of restoring sanity to tables, and allows their
+column width to flow normally. All styling is default and must be styled by CSS
+rather than in HTML attributes.
+
+I make no claim that **all** HTML table crimes generated are corrected — merely
+the ones that I have stumbled across.
+
+## Requirements
+
+* Beautiful Soup 4
+
+## What does it do?
+
+At the moment, the following is stripped from tables (though when in doubt,
+check the source as it may be updated and out-of-sync with this document).
+
+* `<colgroup>` element (and its evil `<col>` children)
+* `table` > `border` attribute
+* `<tbody>` and `<thead>` > `valign` attribute
+
+## Usage
+
+Enable the plugin in your pelicanconf.py:
+
+    PLUGINS = [
+        # ...
+        'better_tables',
+        # ...
+    ]
+
+And that's it. Life's simple like that sometimes.
diff --git a/pelican-plugins/better_tables/__init__.py b/pelican-plugins/better_tables/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..f752acf642764fb0cce0183aa4742b306ee49ec4
--- /dev/null
+++ b/pelican-plugins/better_tables/__init__.py
@@ -0,0 +1,31 @@
+# Copyright (c) 2015 Alex Waite
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+# -*- coding: utf-8 -*-
+__title__ = 'better-tables'
+__version__ = '0.1.0'
+__author__ = 'Alex Waite'
+__credits__ = ["Alex Waite"]
+__maintainer__ = "Alex Waite"
+__email__ = "Alexqw85@gmail.com"
+__status__ = "Stable"
+__license__ = 'MIT'
+__copyright__ = 'Copyright 2015'
+
+from .better_tables import *
diff --git a/pelican-plugins/better_tables/better_tables.py b/pelican-plugins/better_tables/better_tables.py
new file mode 100644
index 0000000000000000000000000000000000000000..16112496deb3672f603a8870cccc40e92899b5af
--- /dev/null
+++ b/pelican-plugins/better_tables/better_tables.py
@@ -0,0 +1,71 @@
+# Copyright (c) 2015 Alex Waite
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+''' Better Tables: Restore sanity to rst->html tables
+
+This pelican plugin removes the excess attributes and elements in the HTML
+tables generated from RST. Trimming this fat allows them to pass HTML5
+validation. Hopefully rst2html5 will be merged into pelican at some point, but
+until then, this hacky approach is needed.
+
+This approach has the advantage of restoring sanity to tables, and allows their
+column with to flow normally. All styling is default and must be styled by CSS
+rather than in HTML attributes.
+
+I make no claim that /all/ HTML table crimes generated are corrected, merely
+the ones which I have stumbled across.
+
+Usage:
+    Enable the plugin in your pelicanconf.py
+
+    PLUGINS = [
+        # ...
+        'better_tables',
+        # ...
+    ]
+
+    And that's it. Life's simple like that sometimes.
+'''
+
+from pelican import signals, contents
+from bs4 import BeautifulSoup
+
+def better_tables(content):
+    if isinstance(content, contents.Static):
+        return
+
+    soup = BeautifulSoup(content._content, 'html.parser')
+
+    for table in soup.findAll('table'):
+        # table's "border" is so 1996
+        del(table['border'])
+
+        # col widths. not only /infuriating/ it's also not in HTML5
+        for tag in table.findAll('colgroup'):
+            tag.extract()
+
+        # tbody and thead's valign
+        for tag in table.findAll(['tbody', 'thead']):
+            del(tag['valign'])
+
+    soup.renderContents()
+    content._content = soup.decode()
+
+def register():
+    signals.content_object_init.connect(better_tables)
diff --git a/pelican-plugins/bootstrap-rst/140x140.png b/pelican-plugins/bootstrap-rst/140x140.png
new file mode 100644
index 0000000000000000000000000000000000000000..79e899298ae81a30376dfafc678f9ccb8cb8f7be
Binary files /dev/null and b/pelican-plugins/bootstrap-rst/140x140.png differ
diff --git a/pelican-plugins/bootstrap-rst/171x180.png b/pelican-plugins/bootstrap-rst/171x180.png
new file mode 100644
index 0000000000000000000000000000000000000000..9067b5cd8ac2bedecd86a0fcdc24227b767a2c4b
Binary files /dev/null and b/pelican-plugins/bootstrap-rst/171x180.png differ
diff --git a/pelican-plugins/bootstrap-rst/300x200.png b/pelican-plugins/bootstrap-rst/300x200.png
new file mode 100644
index 0000000000000000000000000000000000000000..866c67436947b2cde67986ca5e7628d9d547df28
Binary files /dev/null and b/pelican-plugins/bootstrap-rst/300x200.png differ
diff --git a/pelican-plugins/bootstrap-rst/LICENSE b/pelican-plugins/bootstrap-rst/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..4fa4e7b3cbfcce47e44fef26f753633d96ac0e13
--- /dev/null
+++ b/pelican-plugins/bootstrap-rst/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 Nicolas P. Rougier
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/pelican-plugins/bootstrap-rst/Makefile b/pelican-plugins/bootstrap-rst/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..5cc5deab0a15cabf949f9f0c0a9ea61b17f16402
--- /dev/null
+++ b/pelican-plugins/bootstrap-rst/Makefile
@@ -0,0 +1,32 @@
+MAKE             = /usr/bin/make
+RST2HTML         = ./bootstrap.py
+STYLESHEET       =
+RST2HTML_OPTIONS = --strip-comments             \
+                   --report=3                   \
+                   --no-doc-title               \
+                   --traceback                  \
+                   --compact-lists              \
+                   --no-toc-backlinks           \
+                   --syntax-highlight=short     \
+                   --template=page.tmpl         \
+                   --cloak-email-addresses      \
+                   --stylesheet=$(STYLESHEET)   \
+                   --link-stylesheet
+
+SOURCES = $(wildcard doc/*.rst)
+TMP = $(subst .rst,.html, $(SOURCES))
+OBJECTS = $(subst doc/,, $(TMP))
+
+all:$(OBJECTS)
+
+%.html: doc/%.rst
+	@echo "  - $@"
+	@$(RST2HTML) $(RST2HTML_OPTIONS) $< $@
+
+clean:
+	@-rm -f $(OBJECTS)
+
+distclean: clean
+	@-rm -f `find . -name "*~"`
+
+.PHONY: all clean distclean
diff --git a/pelican-plugins/bootstrap-rst/README.rst b/pelican-plugins/bootstrap-rst/README.rst
new file mode 100644
index 0000000000000000000000000000000000000000..bb34b12d5f5c6c6fb89d28a3232f2d13ab1a772c
--- /dev/null
+++ b/pelican-plugins/bootstrap-rst/README.rst
@@ -0,0 +1,44 @@
+Bootstrap RST for Pelican
+=========================
+
+This plugin merely adds what little glue is needed to make bootstrap-rst play
+nicely with Pelican. All credit goes to **Nicolas P. Rougier**.
+
+It is much more featureful than the 'twitter_bootstrap_rst_directives' plugin.
+
+Usage
+-----
+Enable the plugin in your `pelicanconf.py`
+
+    PLUGINS = [
+        'bootstrap-rst'
+    ]
+
+And then use any of the many fine directives made available. Peruse the `doc`
+folder for more information.
+
+Bootstrap RST
+=============
+
+Bootstrap RST offers an easy access to the `bootstrap <http://getbootstrap.com>`_
+framework using the `reStructuredText <http://docutils.sourceforge.net/rst.html>`_
+markup language. Bootstrap RST provides a set of new directives and roles (row,
+column, button, list, etc.) that allow seamless integration of the bootstrap
+framework.  Have a look at the source of this page for example and see below for
+what is available so far.
+
+See demo at: `http://rougier.github.io/bootstrap-rst/ <http://rougier.github.io/bootstrap-rst/>`_.
+
+Creators
+========
+
+**Nicolas P. Rougier** :
+
+* `http://www.labri.fr/perso/nrougier/ <http://www.labri.fr/perso/nrougier/>`_
+* `https://github.com/rougier <https://github.com/rougier>`_
+
+Copyright and License
+=====================
+
+Code and documentation copyright 2014 Nicolas P. Rougier.
+Code released under the MIT license. Docs released under CC BY 3.0.
diff --git a/pelican-plugins/bootstrap-rst/__init__.py b/pelican-plugins/bootstrap-rst/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..ac086b75ba784bc48b231c3debd3da70d4f1bf8c
--- /dev/null
+++ b/pelican-plugins/bootstrap-rst/__init__.py
@@ -0,0 +1,12 @@
+# -*- coding: utf-8 -*-
+__title__ = 'bootstrap-rst'
+__version__ = '0.1.1'
+__author__ = 'Nicolas P. Rougier'
+__credits__ = ['Nicolas P. Rougier', 'Alex Waite', 'Trevor Morgan']
+__maintainer__ = "Alex Waite"
+__email__ = "alex@waite.eu"
+__status__ = "Development"
+__license__ = 'MIT'
+__copyright__ = 'Copyright 2014'
+
+from .bootstrap import *
diff --git a/pelican-plugins/bootstrap-rst/bootstrap.py b/pelican-plugins/bootstrap-rst/bootstrap.py
new file mode 100755
index 0000000000000000000000000000000000000000..c1bf8f10ff4eec19ee58f9d9fdce89cde5c51408
--- /dev/null
+++ b/pelican-plugins/bootstrap-rst/bootstrap.py
@@ -0,0 +1,310 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# -----------------------------------------------------------------------------
+# Bootstrap RST
+# Copyright (c) 2014, Nicolas P. Rougier
+# Distributed under the (new) BSD License. See LICENSE.txt for more info.
+# -----------------------------------------------------------------------------
+import sys, os, re
+from docutils import nodes, utils
+from docutils.parsers.rst.directives import images
+from docutils.transforms import TransformError, Transform, parts
+from docutils.parsers.rst import Directive, directives, states, roles
+from docutils.nodes import fully_normalize_name, whitespace_normalize_name
+from docutils.parsers.rst.roles import set_classes
+
+from docutils.io import StringOutput
+from docutils.core import Publisher
+
+from pelican import signals
+from pelican.readers import RstReader, PelicanHTMLTranslator
+
+from .roles import *
+from .directives import *
+
+
+class HTMLTranslator(PelicanHTMLTranslator):
+    """
+    This is a translator class for the docutils system.
+    """
+
+    def visit_h1(self, node):
+        self.body.append('<h1>%s</h1>' % node.children[0])
+        raise nodes.SkipNode
+
+    def visit_h2(self, node):
+        self.body.append('<h2>%s</h2>' % node.children[0])
+        raise nodes.SkipNode
+
+    def visit_h3(self, node):
+        self.body.append('<h3>%s</h3>' % node.children[0])
+        raise nodes.SkipNode
+
+    def visit_h4(self, node):
+        self.body.append('<h4>%s</h4>' % node.children[0])
+        raise nodes.SkipNode
+
+    def visit_h5(self, node):
+        self.body.append('<h5>%s</h5>' % node.children[0])
+        raise nodes.SkipNode
+
+    def visit_h6(self, node):
+        self.body.append('<h6>%s</h6>' % node.children[0])
+        raise nodes.SkipNode
+
+    def visit_label_default(self, node):
+        self.body.append(
+            '<span class="label label-default">%s</span>' % node.children[0])
+        raise nodes.SkipNode
+
+    def visit_label_primary(self, node):
+        self.body.append(
+            '<span class="label label-primary">%s</span>' % node.children[0])
+        raise nodes.SkipNode
+
+    def visit_label_success(self, node):
+        self.body.append(
+            '<span class="label label-success">%s</span>' % node.children[0])
+        raise nodes.SkipNode
+
+    def visit_label_info(self, node):
+        self.body.append(
+            '<span class="label label-info">%s</span>' % node.children[0])
+        raise nodes.SkipNode
+
+    def visit_label_warning(self, node):
+        self.body.append(
+            '<span class="label label-warning">%s</span>' % node.children[0])
+        raise nodes.SkipNode
+
+    def visit_label_danger(self, node):
+        self.body.append(
+            '<span class="label label-danger">%s</span>' % node.children[0])
+        raise nodes.SkipNode
+
+    def visit_page_row(self, node):
+        self.body.append(self.starttag(node,'div'))
+
+    def depart_page_row(self, node):
+        self.body.append('</div>\n')
+
+    def visit_page_column(self, node):
+        self.body.append(self.starttag(node,'div'))
+
+    def depart_page_column(self, node):
+        self.body.append('</div>\n')
+
+
+    def visit_button(self, node):
+        btn_classes = { 'primary' : 'btn-primary', 'success' : 'btn-success',
+                        'info'    : 'btn-info',    'warning' : 'btn-warning',
+                        'danger'  : 'btn-danger',  'link'    : 'btn-link',
+                        'outline' : 'btn-outline', 'tiny'    : 'btn-xs',
+                        'small'   : 'btn-sm',      'large'   : 'btn-lg',
+                        'block'   : 'btn-block',   'active'  : 'btn-active' }
+
+        classes = 'btn '
+        flag = False
+        for node_class in node['classes']:
+            if node_class in ['primary', 'success', 'warning'
+                              'info', 'link', 'danger', 'outline']:
+                flag = True
+            btn_class = btn_classes.get(node_class, None)
+            if btn_class:
+                classes += btn_class + ' '
+        if flag == False:
+            classes += 'btn-default'
+
+        target = node['target']
+        properties = ''
+
+        # Disabled
+        if 'disabled' in node['classes']:
+            if target:
+                properties += ' disabled="disabled"'
+            else:
+                classes += ' disabled'
+
+        # Data toggle
+        if 'toggle' in node['classes']:
+            classes += ' dropdown-toggle '
+            properties += ' data-toggle="dropdown"'
+        if target:
+            properties += ' role="button"'
+            anchor = '<a href="%s" class="%s" %s>' % (target,classes,properties)
+            self.body.append(anchor)
+        else:
+            properties += ' type="button"'
+            button = '<button class="%s" %s>' % (classes,properties)
+            self.body.append(button)
+
+    def depart_button(self, node):
+        if node['target']:
+            self.body.append('</a>\n')
+        else:
+            self.body.append('</button>\n')
+
+
+    def visit_progress(self, node):
+        prg_classes = { 'success' : 'progress-bar-success',
+                        'info'    : 'progress-bar-info',
+                        'warning' : 'progress-bar-warning',
+                        'danger'  : 'progress-bar-danger' }
+
+        label = node['label']
+        classes = 'progress-bar'
+        flag = False
+        for nodeclass in node['classes']:
+            flag = True
+            classes += ' ' + prg_classes.get(nodeclass, '')
+        if flag == False:
+            classes += ' progress-bar-default'
+        properties = 'role="progress-bar"'
+        properties += ' aria-valuenow="%d"' % int(node['value'])
+        properties += ' aria-valuemin="%d"' % int(node['value_min'])
+        properties += ' aria-valuemax="%d"' % int(node['value_max'])
+        properties += ' style="width: %d%%";' % int(node['value'])
+        if 'active' in node['classes']:
+            self.body.append('<div class="progress progress-striped active">')
+        elif 'striped' in node['classes']:
+            self.body.append('<div class="progress progress-striped">')
+        else:
+            self.body.append('<div class="progress">')
+        self.body.append(
+            '<div class="%s" %s>%s</div>' % (classes,properties,label))
+        self.body.append('</div>')
+        raise nodes.SkipNode
+
+    def visit_alert(self, node):
+        self.body.append(self.starttag(node, 'div', CLASS='alert'))
+        if node.dismissable:
+            self.body.append(
+                u"""<button type="button" class="close" data-dismiss="alert" """
+                u"""aria-hidden="true">×</button>""")
+
+    def depart_alert(self, node):
+        self.body.append('</div>\n')
+
+    def visit_callout(self, node):
+        self.body.append(self.starttag(node, 'div', CLASS='bs-callout'))
+
+    def depart_callout(self, node):
+        self.body.append('</div>\n')
+
+
+
+    # overwritten
+    def visit_definition_list(self, node):
+        list_class = node.parent.get('list-class', [])
+        list_class.append('docutils')
+        list_class = ' '.join(list_class)
+        self.body.append(self.starttag(node, 'dl', CLASS=list_class))
+
+    # overwritten
+    def visit_sidebar(self, node):
+        self.body.append(self.starttag(node, 'div', CLASS='col-md-3 col-md-push-9'))
+        self.body.append(self.starttag(node, 'div', CLASS='bs-docs-sidebar hidden-print affix-top'))
+        self.body.append(self.starttag(node, 'div', CLASS='sidebar'))
+        self.set_first_last(node)
+        self.in_sidebar = True
+
+    # overwritten
+    def depart_sidebar(self, node):
+        self.body.append('</div>\n')
+        self.body.append('</div>\n')
+        self.body.append('</div>\n')
+        #  Opening tag for body
+        self.body.append(self.starttag(node, 'div', CLASS='col-md-9 col-md-pull-3'))
+        self.in_sidebar = False
+
+    # overwritten : removed compact paragraph
+    # def visit_paragraph(self, node):
+    #     if self.should_be_compact_paragraph(node):
+    #         self.context.append('')
+    #     else:
+    #         self.body.append(self.starttag(node, 'p', ''))
+    #     self.context.append('</p>\n')
+
+    # overwritten: remove border=1, replace docutils/table class
+    def visit_table(self, node):
+        self.context.append(self.compact_p)
+        self.compact_p = True
+        #classes = ' '.join(['docutils', self.settings.table_style]).strip()
+        classes = ' '.join(['table', self.settings.table_style]).strip()
+        self.body.append(self.starttag(node, 'table', CLASS=classes))
+
+    # overwritten : removed 'container' class
+    def visit_container(self, node):
+        self.body.append(self.starttag(node, 'div', CLASS=''))
+
+    # overwritten: get rid of <hr> tag
+    def depart_header(self, node):
+        start = self.context.pop()
+        header = [self.starttag(node, 'div', CLASS='header')]
+        header.extend(self.body[start:])
+        header.append('\n</div>\n')
+        self.body_prefix.extend(header)
+        self.header.extend(header)
+        del self.body[start:]
+
+    # overwritten: get rid of <hr> tag
+    def depart_footer(self, node):
+        start = self.context.pop()
+        footer = [self.starttag(node, 'div', CLASS='footer')]
+        footer.extend(self.body[start:])
+        footer.append('\n</div>\n')
+        self.footer.extend(footer)
+        self.body_suffix[:0] = footer
+        del self.body[start:]
+
+    # overwritten
+    def depart_document(self, node):
+        self.head_prefix.extend([self.doctype,
+                                 self.head_prefix_template %
+                                 {'lang': self.settings.language_code}])
+        self.html_prolog.append(self.doctype)
+        self.meta.insert(0, self.content_type % self.settings.output_encoding)
+        self.head.insert(0, self.content_type % self.settings.output_encoding)
+        if self.math_header:
+            self.head.append(self.math_header)
+        # skip content-type meta tag with interpolated charset value:
+        self.html_head.extend(self.head[1:])
+        # self.body_prefix.append(self.starttag(node, 'div', CLASS='document'))
+        self.body_prefix.append(self.starttag(node, 'div', CLASS='container'))
+        # self.body_suffix.insert(0, '</div>\n')
+        self.fragment.extend(self.body) # self.fragment is the "naked" body
+        self.html_body.extend(self.body_prefix[1:] + self.body_pre_docinfo
+                              + self.docinfo + self.body
+                              + self.body_suffix[:-1])
+        assert not self.context, 'len(context) = %s' % len(self.context)
+
+
+# -----------------------------------------------------------------------------
+class RSTReader(RstReader):
+    """
+        A custom RST reader that behaves exactly like its parent class RstReader
+        with the difference that it uses our HTMLTranslator
+    """
+
+    def _get_publisher(self, source_path):
+        extra_params = {'initial_header_level': '2',
+                        'syntax_highlight': 'short',
+                        'input_encoding': 'utf-8'}
+        user_params = self.settings.get('DOCUTILS_SETTINGS')
+        if user_params:
+            extra_params.update(user_params)
+
+        pub = Publisher(destination_class=StringOutput)
+        pub.set_components('standalone', 'restructuredtext', 'html')
+        pub.writer.translator_class = HTMLTranslator
+        pub.process_programmatic_settings(None, extra_params, None)
+        pub.set_source(source_path=source_path)
+        pub.publish()
+        return pub
+
+
+def add_reader(readers):
+    readers.reader_classes['rst'] = RSTReader
+
+def register():
+    signals.readers_init.connect(add_reader)
diff --git a/pelican-plugins/bootstrap-rst/bootstrap/css/bootstrap-theme.css b/pelican-plugins/bootstrap-rst/bootstrap/css/bootstrap-theme.css
new file mode 100644
index 0000000000000000000000000000000000000000..a4069929bceb661eacbd4b1eb21306cfa5a1c8f9
--- /dev/null
+++ b/pelican-plugins/bootstrap-rst/bootstrap/css/bootstrap-theme.css
@@ -0,0 +1,347 @@
+/*!
+ * Bootstrap v3.1.1 (http://getbootstrap.com)
+ * Copyright 2011-2014 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ */
+
+.btn-default,
+.btn-primary,
+.btn-success,
+.btn-info,
+.btn-warning,
+.btn-danger {
+  text-shadow: 0 -1px 0 rgba(0, 0, 0, .2);
+  -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075);
+          box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075);
+}
+.btn-default:active,
+.btn-primary:active,
+.btn-success:active,
+.btn-info:active,
+.btn-warning:active,
+.btn-danger:active,
+.btn-default.active,
+.btn-primary.active,
+.btn-success.active,
+.btn-info.active,
+.btn-warning.active,
+.btn-danger.active {
+  -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
+          box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
+}
+.btn:active,
+.btn.active {
+  background-image: none;
+}
+.btn-default {
+  text-shadow: 0 1px 0 #fff;
+  background-image: -webkit-linear-gradient(top, #fff 0%, #e0e0e0 100%);
+  background-image:         linear-gradient(to bottom, #fff 0%, #e0e0e0 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);
+  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
+  background-repeat: repeat-x;
+  border-color: #dbdbdb;
+  border-color: #ccc;
+}
+.btn-default:hover,
+.btn-default:focus {
+  background-color: #e0e0e0;
+  background-position: 0 -15px;
+}
+.btn-default:active,
+.btn-default.active {
+  background-color: #e0e0e0;
+  border-color: #dbdbdb;
+}
+.btn-primary {
+  background-image: -webkit-linear-gradient(top, #428bca 0%, #2d6ca2 100%);
+  background-image:         linear-gradient(to bottom, #428bca 0%, #2d6ca2 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff2d6ca2', GradientType=0);
+  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
+  background-repeat: repeat-x;
+  border-color: #2b669a;
+}
+.btn-primary:hover,
+.btn-primary:focus {
+  background-color: #2d6ca2;
+  background-position: 0 -15px;
+}
+.btn-primary:active,
+.btn-primary.active {
+  background-color: #2d6ca2;
+  border-color: #2b669a;
+}
+.btn-success {
+  background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%);
+  background-image:         linear-gradient(to bottom, #5cb85c 0%, #419641 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);
+  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
+  background-repeat: repeat-x;
+  border-color: #3e8f3e;
+}
+.btn-success:hover,
+.btn-success:focus {
+  background-color: #419641;
+  background-position: 0 -15px;
+}
+.btn-success:active,
+.btn-success.active {
+  background-color: #419641;
+  border-color: #3e8f3e;
+}
+.btn-info {
+  background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);
+  background-image:         linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);
+  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
+  background-repeat: repeat-x;
+  border-color: #28a4c9;
+}
+.btn-info:hover,
+.btn-info:focus {
+  background-color: #2aabd2;
+  background-position: 0 -15px;
+}
+.btn-info:active,
+.btn-info.active {
+  background-color: #2aabd2;
+  border-color: #28a4c9;
+}
+.btn-warning {
+  background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);
+  background-image:         linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);
+  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
+  background-repeat: repeat-x;
+  border-color: #e38d13;
+}
+.btn-warning:hover,
+.btn-warning:focus {
+  background-color: #eb9316;
+  background-position: 0 -15px;
+}
+.btn-warning:active,
+.btn-warning.active {
+  background-color: #eb9316;
+  border-color: #e38d13;
+}
+.btn-danger {
+  background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%);
+  background-image:         linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);
+  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
+  background-repeat: repeat-x;
+  border-color: #b92c28;
+}
+.btn-danger:hover,
+.btn-danger:focus {
+  background-color: #c12e2a;
+  background-position: 0 -15px;
+}
+.btn-danger:active,
+.btn-danger.active {
+  background-color: #c12e2a;
+  border-color: #b92c28;
+}
+.thumbnail,
+.img-thumbnail {
+  -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
+          box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
+}
+.dropdown-menu > li > a:hover,
+.dropdown-menu > li > a:focus {
+  background-color: #e8e8e8;
+  background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
+  background-image:         linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
+  background-repeat: repeat-x;
+}
+.dropdown-menu > .active > a,
+.dropdown-menu > .active > a:hover,
+.dropdown-menu > .active > a:focus {
+  background-color: #357ebd;
+  background-image: -webkit-linear-gradient(top, #428bca 0%, #357ebd 100%);
+  background-image:         linear-gradient(to bottom, #428bca 0%, #357ebd 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0);
+  background-repeat: repeat-x;
+}
+.navbar-default {
+  background-image: -webkit-linear-gradient(top, #fff 0%, #f8f8f8 100%);
+  background-image:         linear-gradient(to bottom, #fff 0%, #f8f8f8 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);
+  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
+  background-repeat: repeat-x;
+  border-radius: 4px;
+  -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075);
+          box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075);
+}
+.navbar-default .navbar-nav > .active > a {
+  background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f3f3f3 100%);
+  background-image:         linear-gradient(to bottom, #ebebeb 0%, #f3f3f3 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff3f3f3', GradientType=0);
+  background-repeat: repeat-x;
+  -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075);
+          box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075);
+}
+.navbar-brand,
+.navbar-nav > li > a {
+  text-shadow: 0 1px 0 rgba(255, 255, 255, .25);
+}
+.navbar-inverse {
+  background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222 100%);
+  background-image:         linear-gradient(to bottom, #3c3c3c 0%, #222 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);
+  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
+  background-repeat: repeat-x;
+}
+.navbar-inverse .navbar-nav > .active > a {
+  background-image: -webkit-linear-gradient(top, #222 0%, #282828 100%);
+  background-image:         linear-gradient(to bottom, #222 0%, #282828 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222', endColorstr='#ff282828', GradientType=0);
+  background-repeat: repeat-x;
+  -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25);
+          box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25);
+}
+.navbar-inverse .navbar-brand,
+.navbar-inverse .navbar-nav > li > a {
+  text-shadow: 0 -1px 0 rgba(0, 0, 0, .25);
+}
+.navbar-static-top,
+.navbar-fixed-top,
+.navbar-fixed-bottom {
+  border-radius: 0;
+}
+.alert {
+  text-shadow: 0 1px 0 rgba(255, 255, 255, .2);
+  -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05);
+          box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05);
+}
+.alert-success {
+  background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);
+  background-image:         linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);
+  background-repeat: repeat-x;
+  border-color: #b2dba1;
+}
+.alert-info {
+  background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%);
+  background-image:         linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);
+  background-repeat: repeat-x;
+  border-color: #9acfea;
+}
+.alert-warning {
+  background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);
+  background-image:         linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);
+  background-repeat: repeat-x;
+  border-color: #f5e79e;
+}
+.alert-danger {
+  background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);
+  background-image:         linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);
+  background-repeat: repeat-x;
+  border-color: #dca7a7;
+}
+.progress {
+  background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);
+  background-image:         linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);
+  background-repeat: repeat-x;
+}
+.progress-bar {
+  background-image: -webkit-linear-gradient(top, #428bca 0%, #3071a9 100%);
+  background-image:         linear-gradient(to bottom, #428bca 0%, #3071a9 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3071a9', GradientType=0);
+  background-repeat: repeat-x;
+}
+.progress-bar-success {
+  background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%);
+  background-image:         linear-gradient(to bottom, #5cb85c 0%, #449d44 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);
+  background-repeat: repeat-x;
+}
+.progress-bar-info {
+  background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);
+  background-image:         linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);
+  background-repeat: repeat-x;
+}
+.progress-bar-warning {
+  background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);
+  background-image:         linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);
+  background-repeat: repeat-x;
+}
+.progress-bar-danger {
+  background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%);
+  background-image:         linear-gradient(to bottom, #d9534f 0%, #c9302c 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);
+  background-repeat: repeat-x;
+}
+.list-group {
+  border-radius: 4px;
+  -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
+          box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
+}
+.list-group-item.active,
+.list-group-item.active:hover,
+.list-group-item.active:focus {
+  text-shadow: 0 -1px 0 #3071a9;
+  background-image: -webkit-linear-gradient(top, #428bca 0%, #3278b3 100%);
+  background-image:         linear-gradient(to bottom, #428bca 0%, #3278b3 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3278b3', GradientType=0);
+  background-repeat: repeat-x;
+  border-color: #3278b3;
+}
+.panel {
+  -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .05);
+          box-shadow: 0 1px 2px rgba(0, 0, 0, .05);
+}
+.panel-default > .panel-heading {
+  background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
+  background-image:         linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
+  background-repeat: repeat-x;
+}
+.panel-primary > .panel-heading {
+  background-image: -webkit-linear-gradient(top, #428bca 0%, #357ebd 100%);
+  background-image:         linear-gradient(to bottom, #428bca 0%, #357ebd 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0);
+  background-repeat: repeat-x;
+}
+.panel-success > .panel-heading {
+  background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);
+  background-image:         linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);
+  background-repeat: repeat-x;
+}
+.panel-info > .panel-heading {
+  background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);
+  background-image:         linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);
+  background-repeat: repeat-x;
+}
+.panel-warning > .panel-heading {
+  background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);
+  background-image:         linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);
+  background-repeat: repeat-x;
+}
+.panel-danger > .panel-heading {
+  background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%);
+  background-image:         linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);
+  background-repeat: repeat-x;
+}
+.well {
+  background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);
+  background-image:         linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);
+  background-repeat: repeat-x;
+  border-color: #dcdcdc;
+  -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1);
+          box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1);
+}
+/*# sourceMappingURL=bootstrap-theme.css.map */
diff --git a/pelican-plugins/bootstrap-rst/bootstrap/css/bootstrap-theme.css.map b/pelican-plugins/bootstrap-rst/bootstrap/css/bootstrap-theme.css.map
new file mode 100644
index 0000000000000000000000000000000000000000..b36fc9a4970e41d7a3bfdb67780e93ab18a68faf
--- /dev/null
+++ b/pelican-plugins/bootstrap-rst/bootstrap/css/bootstrap-theme.css.map
@@ -0,0 +1 @@
+{"version":3,"sources":["less/theme.less","less/mixins.less"],"names":[],"mappings":"AAeA;AACA;AACA;AACA;AACA;AACA;EACE,wCAAA;ECoGA,2FAAA;EACQ,mFAAA;;ADhGR,YAAC;AAAD,YAAC;AAAD,YAAC;AAAD,SAAC;AAAD,YAAC;AAAD,WAAC;AACD,YAAC;AAAD,YAAC;AAAD,YAAC;AAAD,SAAC;AAAD,YAAC;AAAD,WAAC;EC8FD,wDAAA;EACQ,gDAAA;;ADnER,IAAC;AACD,IAAC;EACC,sBAAA;;AAKJ;EC4PI,kBAAkB,sDAAlB;EACA,kBAAkB,oDAAlB;EAEA,sHAAA;EAoCF,mEAAA;ED7TA,2BAAA;EACA,qBAAA;EAyB2C,yBAAA;EAA2B,kBAAA;;AAvBtE,YAAC;AACD,YAAC;EACC,yBAAA;EACA,4BAAA;;AAGF,YAAC;AACD,YAAC;EACC,yBAAA;EACA,qBAAA;;AAeJ;EC2PI,kBAAkB,sDAAlB;EACA,kBAAkB,oDAAlB;EAEA,sHAAA;EAoCF,mEAAA;ED7TA,2BAAA;EACA,qBAAA;;AAEA,YAAC;AACD,YAAC;EACC,yBAAA;EACA,4BAAA;;AAGF,YAAC;AACD,YAAC;EACC,yBAAA;EACA,qBAAA;;AAgBJ;EC0PI,kBAAkB,sDAAlB;EACA,kBAAkB,oDAAlB;EAEA,sHAAA;EAoCF,mEAAA;ED7TA,2BAAA;EACA,qBAAA;;AAEA,YAAC;AACD,YAAC;EACC,yBAAA;EACA,4BAAA;;AAGF,YAAC;AACD,YAAC;EACC,yBAAA;EACA,qBAAA;;AAiBJ;ECyPI,kBAAkB,sDAAlB;EACA,kBAAkB,oDAAlB;EAEA,sHAAA;EAoCF,mEAAA;ED7TA,2BAAA;EACA,qBAAA;;AAEA,SAAC;AACD,SAAC;EACC,yBAAA;EACA,4BAAA;;AAGF,SAAC;AACD,SAAC;EACC,yBAAA;EACA,qBAAA;;AAkBJ;ECwPI,kBAAkB,sDAAlB;EACA,kBAAkB,oDAAlB;EAEA,sHAAA;EAoCF,mEAAA;ED7TA,2BAAA;EACA,qBAAA;;AAEA,YAAC;AACD,YAAC;EACC,yBAAA;EACA,4BAAA;;AAGF,YAAC;AACD,YAAC;EACC,yBAAA;EACA,qBAAA;;AAmBJ;ECuPI,kBAAkB,sDAAlB;EACA,kBAAkB,oDAAlB;EAEA,sHAAA;EAoCF,mEAAA;ED7TA,2BAAA;EACA,qBAAA;;AAEA,WAAC;AACD,WAAC;EACC,yBAAA;EACA,4BAAA;;AAGF,WAAC;AACD,WAAC;EACC,yBAAA;EACA,qBAAA;;AA2BJ;AACA;EC6CE,kDAAA;EACQ,0CAAA;;ADpCV,cAAe,KAAK,IAAG;AACvB,cAAe,KAAK,IAAG;ECmOnB,kBAAkB,sDAAlB;EACA,kBAAkB,oDAAlB;EACA,2BAAA;EACA,sHAAA;EDpOF,yBAAA;;AAEF,cAAe,UAAU;AACzB,cAAe,UAAU,IAAG;AAC5B,cAAe,UAAU,IAAG;EC6NxB,kBAAkB,sDAAlB;EACA,kBAAkB,oDAAlB;EACA,2BAAA;EACA,sHAAA;ED9NF,yBAAA;;AAUF;ECiNI,kBAAkB,sDAAlB;EACA,kBAAkB,oDAAlB;EACA,2BAAA;EACA,sHAAA;EAoCF,mEAAA;EDrPA,kBAAA;ECaA,2FAAA;EACQ,mFAAA;;ADjBV,eAOE,YAAY,UAAU;EC0MpB,kBAAkB,sDAAlB;EACA,kBAAkB,oDAAlB;EACA,2BAAA;EACA,sHAAA;EApMF,wDAAA;EACQ,gDAAA;;ADLV;AACA,WAAY,KAAK;EACf,8CAAA;;AAIF;EC+LI,kBAAkB,sDAAlB;EACA,kBAAkB,oDAAlB;EACA,2BAAA;EACA,sHAAA;EAoCF,mEAAA;;ADtOF,eAIE,YAAY,UAAU;EC2LpB,kBAAkB,sDAAlB;EACA,kBAAkB,oDAAlB;EACA,2BAAA;EACA,sHAAA;EApMF,uDAAA;EACQ,+CAAA;;ADCV,eASE;AATF,eAUE,YAAY,KAAK;EACf,yCAAA;;AAKJ;AACA;AACA;EACE,gBAAA;;AAUF;EACE,6CAAA;EChCA,0FAAA;EACQ,kFAAA;;AD2CV;ECqJI,kBAAkB,sDAAlB;EACA,kBAAkB,oDAAlB;EACA,2BAAA;EACA,sHAAA;ED5JF,qBAAA;;AAKF;ECoJI,kBAAkB,sDAAlB;EACA,kBAAkB,oDAAlB;EACA,2BAAA;EACA,sHAAA;ED5JF,qBAAA;;AAMF;ECmJI,kBAAkB,sDAAlB;EACA,kBAAkB,oDAAlB;EACA,2BAAA;EACA,sHAAA;ED5JF,qBAAA;;AAOF;ECkJI,kBAAkB,sDAAlB;EACA,kBAAkB,oDAAlB;EACA,2BAAA;EACA,sHAAA;ED5JF,qBAAA;;AAgBF;ECyII,kBAAkB,sDAAlB;EACA,kBAAkB,oDAAlB;EACA,2BAAA;EACA,sHAAA;;ADlIJ;EC+HI,kBAAkB,sDAAlB;EACA,kBAAkB,oDAAlB;EACA,2BAAA;EACA,sHAAA;;ADjIJ;EC8HI,kBAAkB,sDAAlB;EACA,kBAAkB,oDAAlB;EACA,2BAAA;EACA,sHAAA;;ADhIJ;EC6HI,kBAAkB,sDAAlB;EACA,kBAAkB,oDAAlB;EACA,2BAAA;EACA,sHAAA;;AD/HJ;EC4HI,kBAAkB,sDAAlB;EACA,kBAAkB,oDAAlB;EACA,2BAAA;EACA,sHAAA;;AD9HJ;EC2HI,kBAAkB,sDAAlB;EACA,kBAAkB,oDAAlB;EACA,2BAAA;EACA,sHAAA;;ADtHJ;EACE,kBAAA;EC/EA,kDAAA;EACQ,0CAAA;;ADiFV,gBAAgB;AAChB,gBAAgB,OAAO;AACvB,gBAAgB,OAAO;EACrB,6BAAA;EC4GE,kBAAkB,sDAAlB;EACA,kBAAkB,oDAAlB;EACA,2BAAA;EACA,sHAAA;ED7GF,qBAAA;;AAUF;ECjGE,iDAAA;EACQ,yCAAA;;AD0GV,cAAe;ECsFX,kBAAkB,sDAAlB;EACA,kBAAkB,oDAAlB;EACA,2BAAA;EACA,sHAAA;;ADxFJ,cAAe;ECqFX,kBAAkB,sDAAlB;EACA,kBAAkB,oDAAlB;EACA,2BAAA;EACA,sHAAA;;ADvFJ,cAAe;ECoFX,kBAAkB,sDAAlB;EACA,kBAAkB,oDAAlB;EACA,2BAAA;EACA,sHAAA;;ADtFJ,WAAY;ECmFR,kBAAkB,sDAAlB;EACA,kBAAkB,oDAAlB;EACA,2BAAA;EACA,sHAAA;;ADrFJ,cAAe;ECkFX,kBAAkB,sDAAlB;EACA,kBAAkB,oDAAlB;EACA,2BAAA;EACA,sHAAA;;ADpFJ,aAAc;ECiFV,kBAAkB,sDAAlB;EACA,kBAAkB,oDAAlB;EACA,2BAAA;EACA,sHAAA;;AD5EJ;ECyEI,kBAAkB,sDAAlB;EACA,kBAAkB,oDAAlB;EACA,2BAAA;EACA,sHAAA;ED1EF,qBAAA;EC1HA,yFAAA;EACQ,iFAAA","sourcesContent":["\n//\n// Load core variables and mixins\n// --------------------------------------------------\n\n@import \"variables.less\";\n@import \"mixins.less\";\n\n\n\n//\n// Buttons\n// --------------------------------------------------\n\n// Common styles\n.btn-default,\n.btn-primary,\n.btn-success,\n.btn-info,\n.btn-warning,\n.btn-danger {\n  text-shadow: 0 -1px 0 rgba(0,0,0,.2);\n  @shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 1px rgba(0,0,0,.075);\n  .box-shadow(@shadow);\n\n  // Reset the shadow\n  &:active,\n  &.active {\n    .box-shadow(inset 0 3px 5px rgba(0,0,0,.125));\n  }\n}\n\n// Mixin for generating new styles\n.btn-styles(@btn-color: #555) {\n  #gradient > .vertical(@start-color: @btn-color; @end-color: darken(@btn-color, 12%));\n  .reset-filter(); // Disable gradients for IE9 because filter bleeds through rounded corners\n  background-repeat: repeat-x;\n  border-color: darken(@btn-color, 14%);\n\n  &:hover,\n  &:focus  {\n    background-color: darken(@btn-color, 12%);\n    background-position: 0 -15px;\n  }\n\n  &:active,\n  &.active {\n    background-color: darken(@btn-color, 12%);\n    border-color: darken(@btn-color, 14%);\n  }\n}\n\n// Common styles\n.btn {\n  // Remove the gradient for the pressed/active state\n  &:active,\n  &.active {\n    background-image: none;\n  }\n}\n\n// Apply the mixin to the buttons\n.btn-default { .btn-styles(@btn-default-bg); text-shadow: 0 1px 0 #fff; border-color: #ccc; }\n.btn-primary { .btn-styles(@btn-primary-bg); }\n.btn-success { .btn-styles(@btn-success-bg); }\n.btn-info    { .btn-styles(@btn-info-bg); }\n.btn-warning { .btn-styles(@btn-warning-bg); }\n.btn-danger  { .btn-styles(@btn-danger-bg); }\n\n\n\n//\n// Images\n// --------------------------------------------------\n\n.thumbnail,\n.img-thumbnail {\n  .box-shadow(0 1px 2px rgba(0,0,0,.075));\n}\n\n\n\n//\n// Dropdowns\n// --------------------------------------------------\n\n.dropdown-menu > li > a:hover,\n.dropdown-menu > li > a:focus {\n  #gradient > .vertical(@start-color: @dropdown-link-hover-bg; @end-color: darken(@dropdown-link-hover-bg, 5%));\n  background-color: darken(@dropdown-link-hover-bg, 5%);\n}\n.dropdown-menu > .active > a,\n.dropdown-menu > .active > a:hover,\n.dropdown-menu > .active > a:focus {\n  #gradient > .vertical(@start-color: @dropdown-link-active-bg; @end-color: darken(@dropdown-link-active-bg, 5%));\n  background-color: darken(@dropdown-link-active-bg, 5%);\n}\n\n\n\n//\n// Navbar\n// --------------------------------------------------\n\n// Default navbar\n.navbar-default {\n  #gradient > .vertical(@start-color: lighten(@navbar-default-bg, 10%); @end-color: @navbar-default-bg);\n  .reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered\n  border-radius: @navbar-border-radius;\n  @shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 5px rgba(0,0,0,.075);\n  .box-shadow(@shadow);\n\n  .navbar-nav > .active > a {\n    #gradient > .vertical(@start-color: darken(@navbar-default-bg, 5%); @end-color: darken(@navbar-default-bg, 2%));\n    .box-shadow(inset 0 3px 9px rgba(0,0,0,.075));\n  }\n}\n.navbar-brand,\n.navbar-nav > li > a {\n  text-shadow: 0 1px 0 rgba(255,255,255,.25);\n}\n\n// Inverted navbar\n.navbar-inverse {\n  #gradient > .vertical(@start-color: lighten(@navbar-inverse-bg, 10%); @end-color: @navbar-inverse-bg);\n  .reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered\n\n  .navbar-nav > .active > a {\n    #gradient > .vertical(@start-color: @navbar-inverse-bg; @end-color: lighten(@navbar-inverse-bg, 2.5%));\n    .box-shadow(inset 0 3px 9px rgba(0,0,0,.25));\n  }\n\n  .navbar-brand,\n  .navbar-nav > li > a {\n    text-shadow: 0 -1px 0 rgba(0,0,0,.25);\n  }\n}\n\n// Undo rounded corners in static and fixed navbars\n.navbar-static-top,\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n  border-radius: 0;\n}\n\n\n\n//\n// Alerts\n// --------------------------------------------------\n\n// Common styles\n.alert {\n  text-shadow: 0 1px 0 rgba(255,255,255,.2);\n  @shadow: inset 0 1px 0 rgba(255,255,255,.25), 0 1px 2px rgba(0,0,0,.05);\n  .box-shadow(@shadow);\n}\n\n// Mixin for generating new styles\n.alert-styles(@color) {\n  #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 7.5%));\n  border-color: darken(@color, 15%);\n}\n\n// Apply the mixin to the alerts\n.alert-success    { .alert-styles(@alert-success-bg); }\n.alert-info       { .alert-styles(@alert-info-bg); }\n.alert-warning    { .alert-styles(@alert-warning-bg); }\n.alert-danger     { .alert-styles(@alert-danger-bg); }\n\n\n\n//\n// Progress bars\n// --------------------------------------------------\n\n// Give the progress background some depth\n.progress {\n  #gradient > .vertical(@start-color: darken(@progress-bg, 4%); @end-color: @progress-bg)\n}\n\n// Mixin for generating new styles\n.progress-bar-styles(@color) {\n  #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 10%));\n}\n\n// Apply the mixin to the progress bars\n.progress-bar            { .progress-bar-styles(@progress-bar-bg); }\n.progress-bar-success    { .progress-bar-styles(@progress-bar-success-bg); }\n.progress-bar-info       { .progress-bar-styles(@progress-bar-info-bg); }\n.progress-bar-warning    { .progress-bar-styles(@progress-bar-warning-bg); }\n.progress-bar-danger     { .progress-bar-styles(@progress-bar-danger-bg); }\n\n\n\n//\n// List groups\n// --------------------------------------------------\n\n.list-group {\n  border-radius: @border-radius-base;\n  .box-shadow(0 1px 2px rgba(0,0,0,.075));\n}\n.list-group-item.active,\n.list-group-item.active:hover,\n.list-group-item.active:focus {\n  text-shadow: 0 -1px 0 darken(@list-group-active-bg, 10%);\n  #gradient > .vertical(@start-color: @list-group-active-bg; @end-color: darken(@list-group-active-bg, 7.5%));\n  border-color: darken(@list-group-active-border, 7.5%);\n}\n\n\n\n//\n// Panels\n// --------------------------------------------------\n\n// Common styles\n.panel {\n  .box-shadow(0 1px 2px rgba(0,0,0,.05));\n}\n\n// Mixin for generating new styles\n.panel-heading-styles(@color) {\n  #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 5%));\n}\n\n// Apply the mixin to the panel headings only\n.panel-default > .panel-heading   { .panel-heading-styles(@panel-default-heading-bg); }\n.panel-primary > .panel-heading   { .panel-heading-styles(@panel-primary-heading-bg); }\n.panel-success > .panel-heading   { .panel-heading-styles(@panel-success-heading-bg); }\n.panel-info > .panel-heading      { .panel-heading-styles(@panel-info-heading-bg); }\n.panel-warning > .panel-heading   { .panel-heading-styles(@panel-warning-heading-bg); }\n.panel-danger > .panel-heading    { .panel-heading-styles(@panel-danger-heading-bg); }\n\n\n\n//\n// Wells\n// --------------------------------------------------\n\n.well {\n  #gradient > .vertical(@start-color: darken(@well-bg, 5%); @end-color: @well-bg);\n  border-color: darken(@well-bg, 10%);\n  @shadow: inset 0 1px 3px rgba(0,0,0,.05), 0 1px 0 rgba(255,255,255,.1);\n  .box-shadow(@shadow);\n}\n","//\n// Mixins\n// --------------------------------------------------\n\n\n// Utilities\n// -------------------------\n\n// Clearfix\n// Source: http://nicolasgallagher.com/micro-clearfix-hack/\n//\n// For modern browsers\n// 1. The space content is one way to avoid an Opera bug when the\n//    contenteditable attribute is included anywhere else in the document.\n//    Otherwise it causes space to appear at the top and bottom of elements\n//    that are clearfixed.\n// 2. The use of `table` rather than `block` is only necessary if using\n//    `:before` to contain the top-margins of child elements.\n.clearfix() {\n  &:before,\n  &:after {\n    content: \" \"; // 1\n    display: table; // 2\n  }\n  &:after {\n    clear: both;\n  }\n}\n\n// WebKit-style focus\n.tab-focus() {\n  // Default\n  outline: thin dotted;\n  // WebKit\n  outline: 5px auto -webkit-focus-ring-color;\n  outline-offset: -2px;\n}\n\n// Center-align a block level element\n.center-block() {\n  display: block;\n  margin-left: auto;\n  margin-right: auto;\n}\n\n// Sizing shortcuts\n.size(@width; @height) {\n  width: @width;\n  height: @height;\n}\n.square(@size) {\n  .size(@size; @size);\n}\n\n// Placeholder text\n.placeholder(@color: @input-color-placeholder) {\n  &::-moz-placeholder           { color: @color;   // Firefox\n                                  opacity: 1; } // See https://github.com/twbs/bootstrap/pull/11526\n  &:-ms-input-placeholder       { color: @color; } // Internet Explorer 10+\n  &::-webkit-input-placeholder  { color: @color; } // Safari and Chrome\n}\n\n// Text overflow\n// Requires inline-block or block for proper styling\n.text-overflow() {\n  overflow: hidden;\n  text-overflow: ellipsis;\n  white-space: nowrap;\n}\n\n// CSS image replacement\n//\n// Heads up! v3 launched with with only `.hide-text()`, but per our pattern for\n// mixins being reused as classes with the same name, this doesn't hold up. As\n// of v3.0.1 we have added `.text-hide()` and deprecated `.hide-text()`. Note\n// that we cannot chain the mixins together in Less, so they are repeated.\n//\n// Source: https://github.com/h5bp/html5-boilerplate/commit/aa0396eae757\n\n// Deprecated as of v3.0.1 (will be removed in v4)\n.hide-text() {\n  font: ~\"0/0\" a;\n  color: transparent;\n  text-shadow: none;\n  background-color: transparent;\n  border: 0;\n}\n// New mixin to use as of v3.0.1\n.text-hide() {\n  .hide-text();\n}\n\n\n\n// CSS3 PROPERTIES\n// --------------------------------------------------\n\n// Single side border-radius\n.border-top-radius(@radius) {\n  border-top-right-radius: @radius;\n   border-top-left-radius: @radius;\n}\n.border-right-radius(@radius) {\n  border-bottom-right-radius: @radius;\n     border-top-right-radius: @radius;\n}\n.border-bottom-radius(@radius) {\n  border-bottom-right-radius: @radius;\n   border-bottom-left-radius: @radius;\n}\n.border-left-radius(@radius) {\n  border-bottom-left-radius: @radius;\n     border-top-left-radius: @radius;\n}\n\n// Drop shadows\n//\n// Note: Deprecated `.box-shadow()` as of v3.1.0 since all of Bootstrap's\n//   supported browsers that have box shadow capabilities now support the\n//   standard `box-shadow` property.\n.box-shadow(@shadow) {\n  -webkit-box-shadow: @shadow; // iOS <4.3 & Android <4.1\n          box-shadow: @shadow;\n}\n\n// Transitions\n.transition(@transition) {\n  -webkit-transition: @transition;\n          transition: @transition;\n}\n.transition-property(@transition-property) {\n  -webkit-transition-property: @transition-property;\n          transition-property: @transition-property;\n}\n.transition-delay(@transition-delay) {\n  -webkit-transition-delay: @transition-delay;\n          transition-delay: @transition-delay;\n}\n.transition-duration(@transition-duration) {\n  -webkit-transition-duration: @transition-duration;\n          transition-duration: @transition-duration;\n}\n.transition-transform(@transition) {\n  -webkit-transition: -webkit-transform @transition;\n     -moz-transition: -moz-transform @transition;\n       -o-transition: -o-transform @transition;\n          transition: transform @transition;\n}\n\n// Transformations\n.rotate(@degrees) {\n  -webkit-transform: rotate(@degrees);\n      -ms-transform: rotate(@degrees); // IE9 only\n          transform: rotate(@degrees);\n}\n.scale(@ratio; @ratio-y...) {\n  -webkit-transform: scale(@ratio, @ratio-y);\n      -ms-transform: scale(@ratio, @ratio-y); // IE9 only\n          transform: scale(@ratio, @ratio-y);\n}\n.translate(@x; @y) {\n  -webkit-transform: translate(@x, @y);\n      -ms-transform: translate(@x, @y); // IE9 only\n          transform: translate(@x, @y);\n}\n.skew(@x; @y) {\n  -webkit-transform: skew(@x, @y);\n      -ms-transform: skewX(@x) skewY(@y); // See https://github.com/twbs/bootstrap/issues/4885; IE9+\n          transform: skew(@x, @y);\n}\n.translate3d(@x; @y; @z) {\n  -webkit-transform: translate3d(@x, @y, @z);\n          transform: translate3d(@x, @y, @z);\n}\n\n.rotateX(@degrees) {\n  -webkit-transform: rotateX(@degrees);\n      -ms-transform: rotateX(@degrees); // IE9 only\n          transform: rotateX(@degrees);\n}\n.rotateY(@degrees) {\n  -webkit-transform: rotateY(@degrees);\n      -ms-transform: rotateY(@degrees); // IE9 only\n          transform: rotateY(@degrees);\n}\n.perspective(@perspective) {\n  -webkit-perspective: @perspective;\n     -moz-perspective: @perspective;\n          perspective: @perspective;\n}\n.perspective-origin(@perspective) {\n  -webkit-perspective-origin: @perspective;\n     -moz-perspective-origin: @perspective;\n          perspective-origin: @perspective;\n}\n.transform-origin(@origin) {\n  -webkit-transform-origin: @origin;\n     -moz-transform-origin: @origin;\n      -ms-transform-origin: @origin; // IE9 only\n          transform-origin: @origin;\n}\n\n// Animations\n.animation(@animation) {\n  -webkit-animation: @animation;\n          animation: @animation;\n}\n.animation-name(@name) {\n  -webkit-animation-name: @name;\n          animation-name: @name;\n}\n.animation-duration(@duration) {\n  -webkit-animation-duration: @duration;\n          animation-duration: @duration;\n}\n.animation-timing-function(@timing-function) {\n  -webkit-animation-timing-function: @timing-function;\n          animation-timing-function: @timing-function;\n}\n.animation-delay(@delay) {\n  -webkit-animation-delay: @delay;\n          animation-delay: @delay;\n}\n.animation-iteration-count(@iteration-count) {\n  -webkit-animation-iteration-count: @iteration-count;\n          animation-iteration-count: @iteration-count;\n}\n.animation-direction(@direction) {\n  -webkit-animation-direction: @direction;\n          animation-direction: @direction;\n}\n\n// Backface visibility\n// Prevent browsers from flickering when using CSS 3D transforms.\n// Default value is `visible`, but can be changed to `hidden`\n.backface-visibility(@visibility){\n  -webkit-backface-visibility: @visibility;\n     -moz-backface-visibility: @visibility;\n          backface-visibility: @visibility;\n}\n\n// Box sizing\n.box-sizing(@boxmodel) {\n  -webkit-box-sizing: @boxmodel;\n     -moz-box-sizing: @boxmodel;\n          box-sizing: @boxmodel;\n}\n\n// User select\n// For selecting text on the page\n.user-select(@select) {\n  -webkit-user-select: @select;\n     -moz-user-select: @select;\n      -ms-user-select: @select; // IE10+\n          user-select: @select;\n}\n\n// Resize anything\n.resizable(@direction) {\n  resize: @direction; // Options: horizontal, vertical, both\n  overflow: auto; // Safari fix\n}\n\n// CSS3 Content Columns\n.content-columns(@column-count; @column-gap: @grid-gutter-width) {\n  -webkit-column-count: @column-count;\n     -moz-column-count: @column-count;\n          column-count: @column-count;\n  -webkit-column-gap: @column-gap;\n     -moz-column-gap: @column-gap;\n          column-gap: @column-gap;\n}\n\n// Optional hyphenation\n.hyphens(@mode: auto) {\n  word-wrap: break-word;\n  -webkit-hyphens: @mode;\n     -moz-hyphens: @mode;\n      -ms-hyphens: @mode; // IE10+\n       -o-hyphens: @mode;\n          hyphens: @mode;\n}\n\n// Opacity\n.opacity(@opacity) {\n  opacity: @opacity;\n  // IE8 filter\n  @opacity-ie: (@opacity * 100);\n  filter: ~\"alpha(opacity=@{opacity-ie})\";\n}\n\n\n\n// GRADIENTS\n// --------------------------------------------------\n\n#gradient {\n\n  // Horizontal gradient, from left to right\n  //\n  // Creates two color stops, start and end, by specifying a color and position for each color stop.\n  // Color stops are not available in IE9 and below.\n  .horizontal(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n    background-image: -webkit-linear-gradient(left, color-stop(@start-color @start-percent), color-stop(@end-color @end-percent)); // Safari 5.1-6, Chrome 10+\n    background-image:  linear-gradient(to right, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n    background-repeat: repeat-x;\n    filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down\n  }\n\n  // Vertical gradient, from top to bottom\n  //\n  // Creates two color stops, start and end, by specifying a color and position for each color stop.\n  // Color stops are not available in IE9 and below.\n  .vertical(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n    background-image: -webkit-linear-gradient(top, @start-color @start-percent, @end-color @end-percent);  // Safari 5.1-6, Chrome 10+\n    background-image: linear-gradient(to bottom, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n    background-repeat: repeat-x;\n    filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down\n  }\n\n  .directional(@start-color: #555; @end-color: #333; @deg: 45deg) {\n    background-repeat: repeat-x;\n    background-image: -webkit-linear-gradient(@deg, @start-color, @end-color); // Safari 5.1-6, Chrome 10+\n    background-image: linear-gradient(@deg, @start-color, @end-color); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n  }\n  .horizontal-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n    background-image: -webkit-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n    background-image: linear-gradient(to right, @start-color, @mid-color @color-stop, @end-color);\n    background-repeat: no-repeat;\n    filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n  }\n  .vertical-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n    background-image: -webkit-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n    background-image: linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n    background-repeat: no-repeat;\n    filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n  }\n  .radial(@inner-color: #555; @outer-color: #333) {\n    background-image: -webkit-radial-gradient(circle, @inner-color, @outer-color);\n    background-image: radial-gradient(circle, @inner-color, @outer-color);\n    background-repeat: no-repeat;\n  }\n  .striped(@color: rgba(255,255,255,.15); @angle: 45deg) {\n    background-image: -webkit-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n    background-image: linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n  }\n}\n\n// Reset filters for IE\n//\n// When you need to remove a gradient background, do not forget to use this to reset\n// the IE filter for IE9 and below.\n.reset-filter() {\n  filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(enabled = false)\"));\n}\n\n\n\n// Retina images\n//\n// Short retina mixin for setting background-image and -size\n\n.img-retina(@file-1x; @file-2x; @width-1x; @height-1x) {\n  background-image: url(\"@{file-1x}\");\n\n  @media\n  only screen and (-webkit-min-device-pixel-ratio: 2),\n  only screen and (   min--moz-device-pixel-ratio: 2),\n  only screen and (     -o-min-device-pixel-ratio: 2/1),\n  only screen and (        min-device-pixel-ratio: 2),\n  only screen and (                min-resolution: 192dpi),\n  only screen and (                min-resolution: 2dppx) {\n    background-image: url(\"@{file-2x}\");\n    background-size: @width-1x @height-1x;\n  }\n}\n\n\n// Responsive image\n//\n// Keep images from scaling beyond the width of their parents.\n\n.img-responsive(@display: block) {\n  display: @display;\n  max-width: 100%; // Part 1: Set a maximum relative to the parent\n  height: auto; // Part 2: Scale the height according to the width, otherwise you get stretching\n}\n\n\n// COMPONENT MIXINS\n// --------------------------------------------------\n\n// Horizontal dividers\n// -------------------------\n// Dividers (basically an hr) within dropdowns and nav lists\n.nav-divider(@color: #e5e5e5) {\n  height: 1px;\n  margin: ((@line-height-computed / 2) - 1) 0;\n  overflow: hidden;\n  background-color: @color;\n}\n\n// Panels\n// -------------------------\n.panel-variant(@border; @heading-text-color; @heading-bg-color; @heading-border) {\n  border-color: @border;\n\n  & > .panel-heading {\n    color: @heading-text-color;\n    background-color: @heading-bg-color;\n    border-color: @heading-border;\n\n    + .panel-collapse .panel-body {\n      border-top-color: @border;\n    }\n  }\n  & > .panel-footer {\n    + .panel-collapse .panel-body {\n      border-bottom-color: @border;\n    }\n  }\n}\n\n// Alerts\n// -------------------------\n.alert-variant(@background; @border; @text-color) {\n  background-color: @background;\n  border-color: @border;\n  color: @text-color;\n\n  hr {\n    border-top-color: darken(@border, 5%);\n  }\n  .alert-link {\n    color: darken(@text-color, 10%);\n  }\n}\n\n// Tables\n// -------------------------\n.table-row-variant(@state; @background) {\n  // Exact selectors below required to override `.table-striped` and prevent\n  // inheritance to nested tables.\n  .table > thead > tr,\n  .table > tbody > tr,\n  .table > tfoot > tr {\n    > td.@{state},\n    > th.@{state},\n    &.@{state} > td,\n    &.@{state} > th {\n      background-color: @background;\n    }\n  }\n\n  // Hover states for `.table-hover`\n  // Note: this is not available for cells or rows within `thead` or `tfoot`.\n  .table-hover > tbody > tr {\n    > td.@{state}:hover,\n    > th.@{state}:hover,\n    &.@{state}:hover > td,\n    &.@{state}:hover > th {\n      background-color: darken(@background, 5%);\n    }\n  }\n}\n\n// List Groups\n// -------------------------\n.list-group-item-variant(@state; @background; @color) {\n  .list-group-item-@{state} {\n    color: @color;\n    background-color: @background;\n\n    a& {\n      color: @color;\n\n      .list-group-item-heading { color: inherit; }\n\n      &:hover,\n      &:focus {\n        color: @color;\n        background-color: darken(@background, 5%);\n      }\n      &.active,\n      &.active:hover,\n      &.active:focus {\n        color: #fff;\n        background-color: @color;\n        border-color: @color;\n      }\n    }\n  }\n}\n\n// Button variants\n// -------------------------\n// Easily pump out default styles, as well as :hover, :focus, :active,\n// and disabled options for all buttons\n.button-variant(@color; @background; @border) {\n  color: @color;\n  background-color: @background;\n  border-color: @border;\n\n  &:hover,\n  &:focus,\n  &:active,\n  &.active,\n  .open .dropdown-toggle& {\n    color: @color;\n    background-color: darken(@background, 8%);\n        border-color: darken(@border, 12%);\n  }\n  &:active,\n  &.active,\n  .open .dropdown-toggle& {\n    background-image: none;\n  }\n  &.disabled,\n  &[disabled],\n  fieldset[disabled] & {\n    &,\n    &:hover,\n    &:focus,\n    &:active,\n    &.active {\n      background-color: @background;\n          border-color: @border;\n    }\n  }\n\n  .badge {\n    color: @background;\n    background-color: @color;\n  }\n}\n\n// Button sizes\n// -------------------------\n.button-size(@padding-vertical; @padding-horizontal; @font-size; @line-height; @border-radius) {\n  padding: @padding-vertical @padding-horizontal;\n  font-size: @font-size;\n  line-height: @line-height;\n  border-radius: @border-radius;\n}\n\n// Pagination\n// -------------------------\n.pagination-size(@padding-vertical; @padding-horizontal; @font-size; @border-radius) {\n  > li {\n    > a,\n    > span {\n      padding: @padding-vertical @padding-horizontal;\n      font-size: @font-size;\n    }\n    &:first-child {\n      > a,\n      > span {\n        .border-left-radius(@border-radius);\n      }\n    }\n    &:last-child {\n      > a,\n      > span {\n        .border-right-radius(@border-radius);\n      }\n    }\n  }\n}\n\n// Labels\n// -------------------------\n.label-variant(@color) {\n  background-color: @color;\n  &[href] {\n    &:hover,\n    &:focus {\n      background-color: darken(@color, 10%);\n    }\n  }\n}\n\n// Contextual backgrounds\n// -------------------------\n.bg-variant(@color) {\n  background-color: @color;\n  a&:hover {\n    background-color: darken(@color, 10%);\n  }\n}\n\n// Typography\n// -------------------------\n.text-emphasis-variant(@color) {\n  color: @color;\n  a&:hover {\n    color: darken(@color, 10%);\n  }\n}\n\n// Navbar vertical align\n// -------------------------\n// Vertically center elements in the navbar.\n// Example: an element has a height of 30px, so write out `.navbar-vertical-align(30px);` to calculate the appropriate top margin.\n.navbar-vertical-align(@element-height) {\n  margin-top: ((@navbar-height - @element-height) / 2);\n  margin-bottom: ((@navbar-height - @element-height) / 2);\n}\n\n// Progress bars\n// -------------------------\n.progress-bar-variant(@color) {\n  background-color: @color;\n  .progress-striped & {\n    #gradient > .striped();\n  }\n}\n\n// Responsive utilities\n// -------------------------\n// More easily include all the states for responsive-utilities.less.\n.responsive-visibility() {\n  display: block !important;\n  table&  { display: table; }\n  tr&     { display: table-row !important; }\n  th&,\n  td&     { display: table-cell !important; }\n}\n\n.responsive-invisibility() {\n  display: none !important;\n}\n\n\n// Grid System\n// -----------\n\n// Centered container element\n.container-fixed() {\n  margin-right: auto;\n  margin-left: auto;\n  padding-left:  (@grid-gutter-width / 2);\n  padding-right: (@grid-gutter-width / 2);\n  &:extend(.clearfix all);\n}\n\n// Creates a wrapper for a series of columns\n.make-row(@gutter: @grid-gutter-width) {\n  margin-left:  (@gutter / -2);\n  margin-right: (@gutter / -2);\n  &:extend(.clearfix all);\n}\n\n// Generate the extra small columns\n.make-xs-column(@columns; @gutter: @grid-gutter-width) {\n  position: relative;\n  float: left;\n  width: percentage((@columns / @grid-columns));\n  min-height: 1px;\n  padding-left:  (@gutter / 2);\n  padding-right: (@gutter / 2);\n}\n.make-xs-column-offset(@columns) {\n  @media (min-width: @screen-xs-min) {\n    margin-left: percentage((@columns / @grid-columns));\n  }\n}\n.make-xs-column-push(@columns) {\n  @media (min-width: @screen-xs-min) {\n    left: percentage((@columns / @grid-columns));\n  }\n}\n.make-xs-column-pull(@columns) {\n  @media (min-width: @screen-xs-min) {\n    right: percentage((@columns / @grid-columns));\n  }\n}\n\n\n// Generate the small columns\n.make-sm-column(@columns; @gutter: @grid-gutter-width) {\n  position: relative;\n  min-height: 1px;\n  padding-left:  (@gutter / 2);\n  padding-right: (@gutter / 2);\n\n  @media (min-width: @screen-sm-min) {\n    float: left;\n    width: percentage((@columns / @grid-columns));\n  }\n}\n.make-sm-column-offset(@columns) {\n  @media (min-width: @screen-sm-min) {\n    margin-left: percentage((@columns / @grid-columns));\n  }\n}\n.make-sm-column-push(@columns) {\n  @media (min-width: @screen-sm-min) {\n    left: percentage((@columns / @grid-columns));\n  }\n}\n.make-sm-column-pull(@columns) {\n  @media (min-width: @screen-sm-min) {\n    right: percentage((@columns / @grid-columns));\n  }\n}\n\n\n// Generate the medium columns\n.make-md-column(@columns; @gutter: @grid-gutter-width) {\n  position: relative;\n  min-height: 1px;\n  padding-left:  (@gutter / 2);\n  padding-right: (@gutter / 2);\n\n  @media (min-width: @screen-md-min) {\n    float: left;\n    width: percentage((@columns / @grid-columns));\n  }\n}\n.make-md-column-offset(@columns) {\n  @media (min-width: @screen-md-min) {\n    margin-left: percentage((@columns / @grid-columns));\n  }\n}\n.make-md-column-push(@columns) {\n  @media (min-width: @screen-md-min) {\n    left: percentage((@columns / @grid-columns));\n  }\n}\n.make-md-column-pull(@columns) {\n  @media (min-width: @screen-md-min) {\n    right: percentage((@columns / @grid-columns));\n  }\n}\n\n\n// Generate the large columns\n.make-lg-column(@columns; @gutter: @grid-gutter-width) {\n  position: relative;\n  min-height: 1px;\n  padding-left:  (@gutter / 2);\n  padding-right: (@gutter / 2);\n\n  @media (min-width: @screen-lg-min) {\n    float: left;\n    width: percentage((@columns / @grid-columns));\n  }\n}\n.make-lg-column-offset(@columns) {\n  @media (min-width: @screen-lg-min) {\n    margin-left: percentage((@columns / @grid-columns));\n  }\n}\n.make-lg-column-push(@columns) {\n  @media (min-width: @screen-lg-min) {\n    left: percentage((@columns / @grid-columns));\n  }\n}\n.make-lg-column-pull(@columns) {\n  @media (min-width: @screen-lg-min) {\n    right: percentage((@columns / @grid-columns));\n  }\n}\n\n\n// Framework grid generation\n//\n// Used only by Bootstrap to generate the correct number of grid classes given\n// any value of `@grid-columns`.\n\n.make-grid-columns() {\n  // Common styles for all sizes of grid columns, widths 1-12\n  .col(@index) when (@index = 1) { // initial\n    @item: ~\".col-xs-@{index}, .col-sm-@{index}, .col-md-@{index}, .col-lg-@{index}\";\n    .col((@index + 1), @item);\n  }\n  .col(@index, @list) when (@index =< @grid-columns) { // general; \"=<\" isn't a typo\n    @item: ~\".col-xs-@{index}, .col-sm-@{index}, .col-md-@{index}, .col-lg-@{index}\";\n    .col((@index + 1), ~\"@{list}, @{item}\");\n  }\n  .col(@index, @list) when (@index > @grid-columns) { // terminal\n    @{list} {\n      position: relative;\n      // Prevent columns from collapsing when empty\n      min-height: 1px;\n      // Inner gutter via padding\n      padding-left:  (@grid-gutter-width / 2);\n      padding-right: (@grid-gutter-width / 2);\n    }\n  }\n  .col(1); // kickstart it\n}\n\n.float-grid-columns(@class) {\n  .col(@index) when (@index = 1) { // initial\n    @item: ~\".col-@{class}-@{index}\";\n    .col((@index + 1), @item);\n  }\n  .col(@index, @list) when (@index =< @grid-columns) { // general\n    @item: ~\".col-@{class}-@{index}\";\n    .col((@index + 1), ~\"@{list}, @{item}\");\n  }\n  .col(@index, @list) when (@index > @grid-columns) { // terminal\n    @{list} {\n      float: left;\n    }\n  }\n  .col(1); // kickstart it\n}\n\n.calc-grid-column(@index, @class, @type) when (@type = width) and (@index > 0) {\n  .col-@{class}-@{index} {\n    width: percentage((@index / @grid-columns));\n  }\n}\n.calc-grid-column(@index, @class, @type) when (@type = push) {\n  .col-@{class}-push-@{index} {\n    left: percentage((@index / @grid-columns));\n  }\n}\n.calc-grid-column(@index, @class, @type) when (@type = pull) {\n  .col-@{class}-pull-@{index} {\n    right: percentage((@index / @grid-columns));\n  }\n}\n.calc-grid-column(@index, @class, @type) when (@type = offset) {\n  .col-@{class}-offset-@{index} {\n    margin-left: percentage((@index / @grid-columns));\n  }\n}\n\n// Basic looping in LESS\n.loop-grid-columns(@index, @class, @type) when (@index >= 0) {\n  .calc-grid-column(@index, @class, @type);\n  // next iteration\n  .loop-grid-columns((@index - 1), @class, @type);\n}\n\n// Create grid for specific class\n.make-grid(@class) {\n  .float-grid-columns(@class);\n  .loop-grid-columns(@grid-columns, @class, width);\n  .loop-grid-columns(@grid-columns, @class, pull);\n  .loop-grid-columns(@grid-columns, @class, push);\n  .loop-grid-columns(@grid-columns, @class, offset);\n}\n\n// Form validation states\n//\n// Used in forms.less to generate the form validation CSS for warnings, errors,\n// and successes.\n\n.form-control-validation(@text-color: #555; @border-color: #ccc; @background-color: #f5f5f5) {\n  // Color the label and help text\n  .help-block,\n  .control-label,\n  .radio,\n  .checkbox,\n  .radio-inline,\n  .checkbox-inline  {\n    color: @text-color;\n  }\n  // Set the border and box shadow on specific inputs to match\n  .form-control {\n    border-color: @border-color;\n    .box-shadow(inset 0 1px 1px rgba(0,0,0,.075)); // Redeclare so transitions work\n    &:focus {\n      border-color: darken(@border-color, 10%);\n      @shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 6px lighten(@border-color, 20%);\n      .box-shadow(@shadow);\n    }\n  }\n  // Set validation states also for addons\n  .input-group-addon {\n    color: @text-color;\n    border-color: @border-color;\n    background-color: @background-color;\n  }\n  // Optional feedback icon\n  .form-control-feedback {\n    color: @text-color;\n  }\n}\n\n// Form control focus state\n//\n// Generate a customized focus state and for any input with the specified color,\n// which defaults to the `@input-focus-border` variable.\n//\n// We highly encourage you to not customize the default value, but instead use\n// this to tweak colors on an as-needed basis. This aesthetic change is based on\n// WebKit's default styles, but applicable to a wider range of browsers. Its\n// usability and accessibility should be taken into account with any change.\n//\n// Example usage: change the default blue border and shadow to white for better\n// contrast against a dark gray background.\n\n.form-control-focus(@color: @input-border-focus) {\n  @color-rgba: rgba(red(@color), green(@color), blue(@color), .6);\n  &:focus {\n    border-color: @color;\n    outline: 0;\n    .box-shadow(~\"inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px @{color-rgba}\");\n  }\n}\n\n// Form control sizing\n//\n// Relative text size, padding, and border-radii changes for form controls. For\n// horizontal sizing, wrap controls in the predefined grid classes. `<select>`\n// element gets special love because it's special, and that's a fact!\n\n.input-size(@input-height; @padding-vertical; @padding-horizontal; @font-size; @line-height; @border-radius) {\n  height: @input-height;\n  padding: @padding-vertical @padding-horizontal;\n  font-size: @font-size;\n  line-height: @line-height;\n  border-radius: @border-radius;\n\n  select& {\n    height: @input-height;\n    line-height: @input-height;\n  }\n\n  textarea&,\n  select[multiple]& {\n    height: auto;\n  }\n}\n"]}
\ No newline at end of file
diff --git a/pelican-plugins/bootstrap-rst/bootstrap/css/bootstrap-theme.min.css b/pelican-plugins/bootstrap-rst/bootstrap/css/bootstrap-theme.min.css
new file mode 100644
index 0000000000000000000000000000000000000000..ba4bd28ae51616917024b5d4a8d2d20b969a9e31
--- /dev/null
+++ b/pelican-plugins/bootstrap-rst/bootstrap/css/bootstrap-theme.min.css
@@ -0,0 +1,7 @@
+/*!
+ * Bootstrap v3.1.1 (http://getbootstrap.com)
+ * Copyright 2011-2014 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ */
+
+.btn-default,.btn-primary,.btn-success,.btn-info,.btn-warning,.btn-danger{text-shadow:0 -1px 0 rgba(0,0,0,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075)}.btn-default:active,.btn-primary:active,.btn-success:active,.btn-info:active,.btn-warning:active,.btn-danger:active,.btn-default.active,.btn-primary.active,.btn-success.active,.btn-info.active,.btn-warning.active,.btn-danger.active{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn:active,.btn.active{background-image:none}.btn-default{background-image:-webkit-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:linear-gradient(to bottom,#fff 0,#e0e0e0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#dbdbdb;text-shadow:0 1px 0 #fff;border-color:#ccc}.btn-default:hover,.btn-default:focus{background-color:#e0e0e0;background-position:0 -15px}.btn-default:active,.btn-default.active{background-color:#e0e0e0;border-color:#dbdbdb}.btn-primary{background-image:-webkit-linear-gradient(top,#428bca 0,#2d6ca2 100%);background-image:linear-gradient(to bottom,#428bca 0,#2d6ca2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff2d6ca2', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#2b669a}.btn-primary:hover,.btn-primary:focus{background-color:#2d6ca2;background-position:0 -15px}.btn-primary:active,.btn-primary.active{background-color:#2d6ca2;border-color:#2b669a}.btn-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:linear-gradient(to bottom,#5cb85c 0,#419641 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#3e8f3e}.btn-success:hover,.btn-success:focus{background-color:#419641;background-position:0 -15px}.btn-success:active,.btn-success.active{background-color:#419641;border-color:#3e8f3e}.btn-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:linear-gradient(to bottom,#5bc0de 0,#2aabd2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#28a4c9}.btn-info:hover,.btn-info:focus{background-color:#2aabd2;background-position:0 -15px}.btn-info:active,.btn-info.active{background-color:#2aabd2;border-color:#28a4c9}.btn-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:linear-gradient(to bottom,#f0ad4e 0,#eb9316 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#e38d13}.btn-warning:hover,.btn-warning:focus{background-color:#eb9316;background-position:0 -15px}.btn-warning:active,.btn-warning.active{background-color:#eb9316;border-color:#e38d13}.btn-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:linear-gradient(to bottom,#d9534f 0,#c12e2a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#b92c28}.btn-danger:hover,.btn-danger:focus{background-color:#c12e2a;background-position:0 -15px}.btn-danger:active,.btn-danger.active{background-color:#c12e2a;border-color:#b92c28}.thumbnail,.img-thumbnail{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-color:#e8e8e8}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{background-image:-webkit-linear-gradient(top,#428bca 0,#357ebd 100%);background-image:linear-gradient(to bottom,#428bca 0,#357ebd 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0);background-color:#357ebd}.navbar-default{background-image:-webkit-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:linear-gradient(to bottom,#fff 0,#f8f8f8 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075)}.navbar-default .navbar-nav>.active>a{background-image:-webkit-linear-gradient(top,#ebebeb 0,#f3f3f3 100%);background-image:linear-gradient(to bottom,#ebebeb 0,#f3f3f3 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff3f3f3', GradientType=0);-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.075);box-shadow:inset 0 3px 9px rgba(0,0,0,.075)}.navbar-brand,.navbar-nav>li>a{text-shadow:0 1px 0 rgba(255,255,255,.25)}.navbar-inverse{background-image:-webkit-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:linear-gradient(to bottom,#3c3c3c 0,#222 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.navbar-inverse .navbar-nav>.active>a{background-image:-webkit-linear-gradient(top,#222 0,#282828 100%);background-image:linear-gradient(to bottom,#222 0,#282828 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222', endColorstr='#ff282828', GradientType=0);-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.25);box-shadow:inset 0 3px 9px rgba(0,0,0,.25)}.navbar-inverse .navbar-brand,.navbar-inverse .navbar-nav>li>a{text-shadow:0 -1px 0 rgba(0,0,0,.25)}.navbar-static-top,.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}.alert{text-shadow:0 1px 0 rgba(255,255,255,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05)}.alert-success{background-image:-webkit-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:linear-gradient(to bottom,#dff0d8 0,#c8e5bc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);border-color:#b2dba1}.alert-info{background-image:-webkit-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:linear-gradient(to bottom,#d9edf7 0,#b9def0 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);border-color:#9acfea}.alert-warning{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:linear-gradient(to bottom,#fcf8e3 0,#f8efc0 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);border-color:#f5e79e}.alert-danger{background-image:-webkit-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:linear-gradient(to bottom,#f2dede 0,#e7c3c3 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);border-color:#dca7a7}.progress{background-image:-webkit-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:linear-gradient(to bottom,#ebebeb 0,#f5f5f5 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0)}.progress-bar{background-image:-webkit-linear-gradient(top,#428bca 0,#3071a9 100%);background-image:linear-gradient(to bottom,#428bca 0,#3071a9 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3071a9', GradientType=0)}.progress-bar-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:linear-gradient(to bottom,#5cb85c 0,#449d44 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0)}.progress-bar-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:linear-gradient(to bottom,#5bc0de 0,#31b0d5 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0)}.progress-bar-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:linear-gradient(to bottom,#f0ad4e 0,#ec971f 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0)}.progress-bar-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:linear-gradient(to bottom,#d9534f 0,#c9302c 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0)}.list-group{border-radius:4px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.list-group-item.active,.list-group-item.active:hover,.list-group-item.active:focus{text-shadow:0 -1px 0 #3071a9;background-image:-webkit-linear-gradient(top,#428bca 0,#3278b3 100%);background-image:linear-gradient(to bottom,#428bca 0,#3278b3 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3278b3', GradientType=0);border-color:#3278b3}.panel{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.05);box-shadow:0 1px 2px rgba(0,0,0,.05)}.panel-default>.panel-heading{background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0)}.panel-primary>.panel-heading{background-image:-webkit-linear-gradient(top,#428bca 0,#357ebd 100%);background-image:linear-gradient(to bottom,#428bca 0,#357ebd 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0)}.panel-success>.panel-heading{background-image:-webkit-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:linear-gradient(to bottom,#dff0d8 0,#d0e9c6 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0)}.panel-info>.panel-heading{background-image:-webkit-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:linear-gradient(to bottom,#d9edf7 0,#c4e3f3 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0)}.panel-warning>.panel-heading{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:linear-gradient(to bottom,#fcf8e3 0,#faf2cc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0)}.panel-danger>.panel-heading{background-image:-webkit-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:linear-gradient(to bottom,#f2dede 0,#ebcccc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0)}.well{background-image:-webkit-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:linear-gradient(to bottom,#e8e8e8 0,#f5f5f5 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);border-color:#dcdcdc;-webkit-box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1)}
\ No newline at end of file
diff --git a/pelican-plugins/bootstrap-rst/bootstrap/css/bootstrap.css b/pelican-plugins/bootstrap-rst/bootstrap/css/bootstrap.css
new file mode 100644
index 0000000000000000000000000000000000000000..7f36651961ed5bc42a712042c6db5493b4ce99e9
--- /dev/null
+++ b/pelican-plugins/bootstrap-rst/bootstrap/css/bootstrap.css
@@ -0,0 +1,5785 @@
+/*!
+ * Bootstrap v3.1.1 (http://getbootstrap.com)
+ * Copyright 2011-2014 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ */
+
+/*! normalize.css v3.0.0 | MIT License | git.io/normalize */
+html {
+  font-family: sans-serif;
+  -webkit-text-size-adjust: 100%;
+      -ms-text-size-adjust: 100%;
+}
+body {
+  margin: 0;
+}
+article,
+aside,
+details,
+figcaption,
+figure,
+footer,
+header,
+hgroup,
+main,
+nav,
+section,
+summary {
+  display: block;
+}
+audio,
+canvas,
+progress,
+video {
+  display: inline-block;
+  vertical-align: baseline;
+}
+audio:not([controls]) {
+  display: none;
+  height: 0;
+}
+[hidden],
+template {
+  display: none;
+}
+a {
+  background: transparent;
+}
+a:active,
+a:hover {
+  outline: 0;
+}
+abbr[title] {
+  border-bottom: 1px dotted;
+}
+b,
+strong {
+  font-weight: bold;
+}
+dfn {
+  font-style: italic;
+}
+h1 {
+  margin: .67em 0;
+  font-size: 2em;
+}
+mark {
+  color: #000;
+  background: #ff0;
+}
+small {
+  font-size: 80%;
+}
+sub,
+sup {
+  position: relative;
+  font-size: 75%;
+  line-height: 0;
+  vertical-align: baseline;
+}
+sup {
+  top: -.5em;
+}
+sub {
+  bottom: -.25em;
+}
+img {
+  border: 0;
+}
+svg:not(:root) {
+  overflow: hidden;
+}
+figure {
+  margin: 1em 40px;
+}
+hr {
+  height: 0;
+  -moz-box-sizing: content-box;
+       box-sizing: content-box;
+}
+pre {
+  overflow: auto;
+}
+code,
+kbd,
+pre,
+samp {
+  font-family: monospace, monospace;
+  font-size: 1em;
+}
+button,
+input,
+optgroup,
+select,
+textarea {
+  margin: 0;
+  font: inherit;
+  color: inherit;
+}
+button {
+  overflow: visible;
+}
+button,
+select {
+  text-transform: none;
+}
+button,
+html input[type="button"],
+input[type="reset"],
+input[type="submit"] {
+  -webkit-appearance: button;
+  cursor: pointer;
+}
+button[disabled],
+html input[disabled] {
+  cursor: default;
+}
+button::-moz-focus-inner,
+input::-moz-focus-inner {
+  padding: 0;
+  border: 0;
+}
+input {
+  line-height: normal;
+}
+input[type="checkbox"],
+input[type="radio"] {
+  box-sizing: border-box;
+  padding: 0;
+}
+input[type="number"]::-webkit-inner-spin-button,
+input[type="number"]::-webkit-outer-spin-button {
+  height: auto;
+}
+input[type="search"] {
+  -webkit-box-sizing: content-box;
+     -moz-box-sizing: content-box;
+          box-sizing: content-box;
+  -webkit-appearance: textfield;
+}
+input[type="search"]::-webkit-search-cancel-button,
+input[type="search"]::-webkit-search-decoration {
+  -webkit-appearance: none;
+}
+fieldset {
+  padding: .35em .625em .75em;
+  margin: 0 2px;
+  border: 1px solid #c0c0c0;
+}
+legend {
+  padding: 0;
+  border: 0;
+}
+textarea {
+  overflow: auto;
+}
+optgroup {
+  font-weight: bold;
+}
+table {
+  border-spacing: 0;
+  border-collapse: collapse;
+}
+td,
+th {
+  padding: 0;
+}
+@media print {
+  * {
+    color: #000 !important;
+    text-shadow: none !important;
+    background: transparent !important;
+    box-shadow: none !important;
+  }
+  a,
+  a:visited {
+    text-decoration: underline;
+  }
+  a[href]:after {
+    content: " (" attr(href) ")";
+  }
+  abbr[title]:after {
+    content: " (" attr(title) ")";
+  }
+  a[href^="javascript:"]:after,
+  a[href^="#"]:after {
+    content: "";
+  }
+  pre,
+  blockquote {
+    border: 1px solid #999;
+
+    page-break-inside: avoid;
+  }
+  thead {
+    display: table-header-group;
+  }
+  tr,
+  img {
+    page-break-inside: avoid;
+  }
+  img {
+    max-width: 100% !important;
+  }
+  p,
+  h2,
+  h3 {
+    orphans: 3;
+    widows: 3;
+  }
+  h2,
+  h3 {
+    page-break-after: avoid;
+  }
+  select {
+    background: #fff !important;
+  }
+  .navbar {
+    display: none;
+  }
+  .table td,
+  .table th {
+    background-color: #fff !important;
+  }
+  .btn > .caret,
+  .dropup > .btn > .caret {
+    border-top-color: #000 !important;
+  }
+  .label {
+    border: 1px solid #000;
+  }
+  .table {
+    border-collapse: collapse !important;
+  }
+  .table-bordered th,
+  .table-bordered td {
+    border: 1px solid #ddd !important;
+  }
+}
+* {
+  -webkit-box-sizing: border-box;
+     -moz-box-sizing: border-box;
+          box-sizing: border-box;
+}
+*:before,
+*:after {
+  -webkit-box-sizing: border-box;
+     -moz-box-sizing: border-box;
+          box-sizing: border-box;
+}
+html {
+  font-size: 62.5%;
+
+  -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
+}
+body {
+  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+  font-size: 14px;
+  line-height: 1.42857143;
+  color: #333;
+  background-color: #fff;
+}
+input,
+button,
+select,
+textarea {
+  font-family: inherit;
+  font-size: inherit;
+  line-height: inherit;
+}
+a {
+  color: #428bca;
+  text-decoration: none;
+}
+a:hover,
+a:focus {
+  color: #2a6496;
+  text-decoration: underline;
+}
+a:focus {
+  outline: thin dotted;
+  outline: 5px auto -webkit-focus-ring-color;
+  outline-offset: -2px;
+}
+figure {
+  margin: 0;
+}
+img {
+  vertical-align: middle;
+}
+.img-responsive,
+.thumbnail > img,
+.thumbnail a > img,
+.carousel-inner > .item > img,
+.carousel-inner > .item > a > img {
+  display: block;
+  max-width: 100%;
+  height: auto;
+}
+.img-rounded {
+  border-radius: 6px;
+}
+.img-thumbnail {
+  display: inline-block;
+  max-width: 100%;
+  height: auto;
+  padding: 4px;
+  line-height: 1.42857143;
+  background-color: #fff;
+  border: 1px solid #ddd;
+  border-radius: 4px;
+  -webkit-transition: all .2s ease-in-out;
+          transition: all .2s ease-in-out;
+}
+.img-circle {
+  border-radius: 50%;
+}
+hr {
+  margin-top: 20px;
+  margin-bottom: 20px;
+  border: 0;
+  border-top: 1px solid #eee;
+}
+.sr-only {
+  position: absolute;
+  width: 1px;
+  height: 1px;
+  padding: 0;
+  margin: -1px;
+  overflow: hidden;
+  clip: rect(0, 0, 0, 0);
+  border: 0;
+}
+h1,
+h2,
+h3,
+h4,
+h5,
+h6,
+.h1,
+.h2,
+.h3,
+.h4,
+.h5,
+.h6 {
+  font-family: inherit;
+  font-weight: 500;
+  line-height: 1.1;
+  color: inherit;
+}
+h1 small,
+h2 small,
+h3 small,
+h4 small,
+h5 small,
+h6 small,
+.h1 small,
+.h2 small,
+.h3 small,
+.h4 small,
+.h5 small,
+.h6 small,
+h1 .small,
+h2 .small,
+h3 .small,
+h4 .small,
+h5 .small,
+h6 .small,
+.h1 .small,
+.h2 .small,
+.h3 .small,
+.h4 .small,
+.h5 .small,
+.h6 .small {
+  font-weight: normal;
+  line-height: 1;
+  color: #999;
+}
+h1,
+.h1,
+h2,
+.h2,
+h3,
+.h3 {
+  margin-top: 20px;
+  margin-bottom: 10px;
+}
+h1 small,
+.h1 small,
+h2 small,
+.h2 small,
+h3 small,
+.h3 small,
+h1 .small,
+.h1 .small,
+h2 .small,
+.h2 .small,
+h3 .small,
+.h3 .small {
+  font-size: 65%;
+}
+h4,
+.h4,
+h5,
+.h5,
+h6,
+.h6 {
+  margin-top: 10px;
+  margin-bottom: 10px;
+}
+h4 small,
+.h4 small,
+h5 small,
+.h5 small,
+h6 small,
+.h6 small,
+h4 .small,
+.h4 .small,
+h5 .small,
+.h5 .small,
+h6 .small,
+.h6 .small {
+  font-size: 75%;
+}
+h1,
+.h1 {
+  font-size: 36px;
+}
+h2,
+.h2 {
+  font-size: 30px;
+}
+h3,
+.h3 {
+  font-size: 24px;
+}
+h4,
+.h4 {
+  font-size: 18px;
+}
+h5,
+.h5 {
+  font-size: 14px;
+}
+h6,
+.h6 {
+  font-size: 12px;
+}
+p {
+  margin: 0 0 10px;
+}
+.lead {
+  margin-bottom: 20px;
+  font-size: 16px;
+  font-weight: 200;
+  line-height: 1.4;
+}
+@media (min-width: 768px) {
+  .lead {
+    font-size: 21px;
+  }
+}
+small,
+.small {
+  font-size: 85%;
+}
+cite {
+  font-style: normal;
+}
+.text-left {
+  text-align: left;
+}
+.text-right {
+  text-align: right;
+}
+.text-center {
+  text-align: center;
+}
+.text-justify {
+  text-align: justify;
+}
+.text-muted {
+  color: #999;
+}
+.text-primary {
+  color: #428bca;
+}
+a.text-primary:hover {
+  color: #3071a9;
+}
+.text-success {
+  color: #3c763d;
+}
+a.text-success:hover {
+  color: #2b542c;
+}
+.text-info {
+  color: #31708f;
+}
+a.text-info:hover {
+  color: #245269;
+}
+.text-warning {
+  color: #8a6d3b;
+}
+a.text-warning:hover {
+  color: #66512c;
+}
+.text-danger {
+  color: #a94442;
+}
+a.text-danger:hover {
+  color: #843534;
+}
+.bg-primary {
+  color: #fff;
+  background-color: #428bca;
+}
+a.bg-primary:hover {
+  background-color: #3071a9;
+}
+.bg-success {
+  background-color: #dff0d8;
+}
+a.bg-success:hover {
+  background-color: #c1e2b3;
+}
+.bg-info {
+  background-color: #d9edf7;
+}
+a.bg-info:hover {
+  background-color: #afd9ee;
+}
+.bg-warning {
+  background-color: #fcf8e3;
+}
+a.bg-warning:hover {
+  background-color: #f7ecb5;
+}
+.bg-danger {
+  background-color: #f2dede;
+}
+a.bg-danger:hover {
+  background-color: #e4b9b9;
+}
+.page-header {
+  padding-bottom: 9px;
+  margin: 40px 0 20px;
+  border-bottom: 1px solid #eee;
+}
+ul,
+ol {
+  margin-top: 0;
+  margin-bottom: 10px;
+}
+ul ul,
+ol ul,
+ul ol,
+ol ol {
+  margin-bottom: 0;
+}
+.list-unstyled {
+  padding-left: 0;
+  list-style: none;
+}
+.list-inline {
+  padding-left: 0;
+  margin-left: -5px;
+  list-style: none;
+}
+.list-inline > li {
+  display: inline-block;
+  padding-right: 5px;
+  padding-left: 5px;
+}
+dl {
+  margin-top: 0;
+  margin-bottom: 20px;
+}
+dt,
+dd {
+  line-height: 1.42857143;
+}
+dt {
+  font-weight: bold;
+}
+dd {
+  margin-left: 0;
+}
+@media (min-width: 768px) {
+  .dl-horizontal dt {
+    float: left;
+    width: 160px;
+    overflow: hidden;
+    clear: left;
+    text-align: right;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+  }
+  .dl-horizontal dd {
+    margin-left: 180px;
+  }
+}
+abbr[title],
+abbr[data-original-title] {
+  cursor: help;
+  border-bottom: 1px dotted #999;
+}
+.initialism {
+  font-size: 90%;
+  text-transform: uppercase;
+}
+blockquote {
+  padding: 10px 20px;
+  margin: 0 0 20px;
+  font-size: 17.5px;
+  border-left: 5px solid #eee;
+}
+blockquote p:last-child,
+blockquote ul:last-child,
+blockquote ol:last-child {
+  margin-bottom: 0;
+}
+blockquote footer,
+blockquote small,
+blockquote .small {
+  display: block;
+  font-size: 80%;
+  line-height: 1.42857143;
+  color: #999;
+}
+blockquote footer:before,
+blockquote small:before,
+blockquote .small:before {
+  content: '\2014 \00A0';
+}
+.blockquote-reverse,
+blockquote.pull-right {
+  padding-right: 15px;
+  padding-left: 0;
+  text-align: right;
+  border-right: 5px solid #eee;
+  border-left: 0;
+}
+.blockquote-reverse footer:before,
+blockquote.pull-right footer:before,
+.blockquote-reverse small:before,
+blockquote.pull-right small:before,
+.blockquote-reverse .small:before,
+blockquote.pull-right .small:before {
+  content: '';
+}
+.blockquote-reverse footer:after,
+blockquote.pull-right footer:after,
+.blockquote-reverse small:after,
+blockquote.pull-right small:after,
+.blockquote-reverse .small:after,
+blockquote.pull-right .small:after {
+  content: '\00A0 \2014';
+}
+blockquote:before,
+blockquote:after {
+  content: "";
+}
+address {
+  margin-bottom: 20px;
+  font-style: normal;
+  line-height: 1.42857143;
+}
+code,
+kbd,
+pre,
+samp {
+  font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
+}
+code {
+  padding: 2px 4px;
+  font-size: 90%;
+  color: #c7254e;
+  white-space: nowrap;
+  background-color: #f9f2f4;
+  border-radius: 4px;
+}
+kbd {
+  padding: 2px 4px;
+  font-size: 90%;
+  color: #fff;
+  background-color: #333;
+  border-radius: 3px;
+  box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25);
+}
+pre {
+  display: block;
+  padding: 9.5px;
+  margin: 0 0 10px;
+  font-size: 13px;
+  line-height: 1.42857143;
+  color: #333;
+  word-break: break-all;
+  word-wrap: break-word;
+  background-color: #f5f5f5;
+  border: 1px solid #ccc;
+  border-radius: 4px;
+}
+pre code {
+  padding: 0;
+  font-size: inherit;
+  color: inherit;
+  white-space: pre-wrap;
+  background-color: transparent;
+  border-radius: 0;
+}
+.pre-scrollable {
+  max-height: 340px;
+  overflow-y: scroll;
+}
+.container {
+  padding-right: 15px;
+  padding-left: 15px;
+  margin-right: auto;
+  margin-left: auto;
+}
+@media (min-width: 768px) {
+  .container {
+    width: 750px;
+  }
+}
+@media (min-width: 992px) {
+  .container {
+    width: 970px;
+  }
+}
+@media (min-width: 1200px) {
+  .container {
+    width: 1170px;
+  }
+}
+.container-fluid {
+  padding-right: 15px;
+  padding-left: 15px;
+  margin-right: auto;
+  margin-left: auto;
+}
+.row {
+  margin-right: -15px;
+  margin-left: -15px;
+}
+.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12 {
+  position: relative;
+  min-height: 1px;
+  padding-right: 15px;
+  padding-left: 15px;
+}
+.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12 {
+  float: left;
+}
+.col-xs-12 {
+  width: 100%;
+}
+.col-xs-11 {
+  width: 91.66666667%;
+}
+.col-xs-10 {
+  width: 83.33333333%;
+}
+.col-xs-9 {
+  width: 75%;
+}
+.col-xs-8 {
+  width: 66.66666667%;
+}
+.col-xs-7 {
+  width: 58.33333333%;
+}
+.col-xs-6 {
+  width: 50%;
+}
+.col-xs-5 {
+  width: 41.66666667%;
+}
+.col-xs-4 {
+  width: 33.33333333%;
+}
+.col-xs-3 {
+  width: 25%;
+}
+.col-xs-2 {
+  width: 16.66666667%;
+}
+.col-xs-1 {
+  width: 8.33333333%;
+}
+.col-xs-pull-12 {
+  right: 100%;
+}
+.col-xs-pull-11 {
+  right: 91.66666667%;
+}
+.col-xs-pull-10 {
+  right: 83.33333333%;
+}
+.col-xs-pull-9 {
+  right: 75%;
+}
+.col-xs-pull-8 {
+  right: 66.66666667%;
+}
+.col-xs-pull-7 {
+  right: 58.33333333%;
+}
+.col-xs-pull-6 {
+  right: 50%;
+}
+.col-xs-pull-5 {
+  right: 41.66666667%;
+}
+.col-xs-pull-4 {
+  right: 33.33333333%;
+}
+.col-xs-pull-3 {
+  right: 25%;
+}
+.col-xs-pull-2 {
+  right: 16.66666667%;
+}
+.col-xs-pull-1 {
+  right: 8.33333333%;
+}
+.col-xs-pull-0 {
+  right: 0;
+}
+.col-xs-push-12 {
+  left: 100%;
+}
+.col-xs-push-11 {
+  left: 91.66666667%;
+}
+.col-xs-push-10 {
+  left: 83.33333333%;
+}
+.col-xs-push-9 {
+  left: 75%;
+}
+.col-xs-push-8 {
+  left: 66.66666667%;
+}
+.col-xs-push-7 {
+  left: 58.33333333%;
+}
+.col-xs-push-6 {
+  left: 50%;
+}
+.col-xs-push-5 {
+  left: 41.66666667%;
+}
+.col-xs-push-4 {
+  left: 33.33333333%;
+}
+.col-xs-push-3 {
+  left: 25%;
+}
+.col-xs-push-2 {
+  left: 16.66666667%;
+}
+.col-xs-push-1 {
+  left: 8.33333333%;
+}
+.col-xs-push-0 {
+  left: 0;
+}
+.col-xs-offset-12 {
+  margin-left: 100%;
+}
+.col-xs-offset-11 {
+  margin-left: 91.66666667%;
+}
+.col-xs-offset-10 {
+  margin-left: 83.33333333%;
+}
+.col-xs-offset-9 {
+  margin-left: 75%;
+}
+.col-xs-offset-8 {
+  margin-left: 66.66666667%;
+}
+.col-xs-offset-7 {
+  margin-left: 58.33333333%;
+}
+.col-xs-offset-6 {
+  margin-left: 50%;
+}
+.col-xs-offset-5 {
+  margin-left: 41.66666667%;
+}
+.col-xs-offset-4 {
+  margin-left: 33.33333333%;
+}
+.col-xs-offset-3 {
+  margin-left: 25%;
+}
+.col-xs-offset-2 {
+  margin-left: 16.66666667%;
+}
+.col-xs-offset-1 {
+  margin-left: 8.33333333%;
+}
+.col-xs-offset-0 {
+  margin-left: 0;
+}
+@media (min-width: 768px) {
+  .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12 {
+    float: left;
+  }
+  .col-sm-12 {
+    width: 100%;
+  }
+  .col-sm-11 {
+    width: 91.66666667%;
+  }
+  .col-sm-10 {
+    width: 83.33333333%;
+  }
+  .col-sm-9 {
+    width: 75%;
+  }
+  .col-sm-8 {
+    width: 66.66666667%;
+  }
+  .col-sm-7 {
+    width: 58.33333333%;
+  }
+  .col-sm-6 {
+    width: 50%;
+  }
+  .col-sm-5 {
+    width: 41.66666667%;
+  }
+  .col-sm-4 {
+    width: 33.33333333%;
+  }
+  .col-sm-3 {
+    width: 25%;
+  }
+  .col-sm-2 {
+    width: 16.66666667%;
+  }
+  .col-sm-1 {
+    width: 8.33333333%;
+  }
+  .col-sm-pull-12 {
+    right: 100%;
+  }
+  .col-sm-pull-11 {
+    right: 91.66666667%;
+  }
+  .col-sm-pull-10 {
+    right: 83.33333333%;
+  }
+  .col-sm-pull-9 {
+    right: 75%;
+  }
+  .col-sm-pull-8 {
+    right: 66.66666667%;
+  }
+  .col-sm-pull-7 {
+    right: 58.33333333%;
+  }
+  .col-sm-pull-6 {
+    right: 50%;
+  }
+  .col-sm-pull-5 {
+    right: 41.66666667%;
+  }
+  .col-sm-pull-4 {
+    right: 33.33333333%;
+  }
+  .col-sm-pull-3 {
+    right: 25%;
+  }
+  .col-sm-pull-2 {
+    right: 16.66666667%;
+  }
+  .col-sm-pull-1 {
+    right: 8.33333333%;
+  }
+  .col-sm-pull-0 {
+    right: 0;
+  }
+  .col-sm-push-12 {
+    left: 100%;
+  }
+  .col-sm-push-11 {
+    left: 91.66666667%;
+  }
+  .col-sm-push-10 {
+    left: 83.33333333%;
+  }
+  .col-sm-push-9 {
+    left: 75%;
+  }
+  .col-sm-push-8 {
+    left: 66.66666667%;
+  }
+  .col-sm-push-7 {
+    left: 58.33333333%;
+  }
+  .col-sm-push-6 {
+    left: 50%;
+  }
+  .col-sm-push-5 {
+    left: 41.66666667%;
+  }
+  .col-sm-push-4 {
+    left: 33.33333333%;
+  }
+  .col-sm-push-3 {
+    left: 25%;
+  }
+  .col-sm-push-2 {
+    left: 16.66666667%;
+  }
+  .col-sm-push-1 {
+    left: 8.33333333%;
+  }
+  .col-sm-push-0 {
+    left: 0;
+  }
+  .col-sm-offset-12 {
+    margin-left: 100%;
+  }
+  .col-sm-offset-11 {
+    margin-left: 91.66666667%;
+  }
+  .col-sm-offset-10 {
+    margin-left: 83.33333333%;
+  }
+  .col-sm-offset-9 {
+    margin-left: 75%;
+  }
+  .col-sm-offset-8 {
+    margin-left: 66.66666667%;
+  }
+  .col-sm-offset-7 {
+    margin-left: 58.33333333%;
+  }
+  .col-sm-offset-6 {
+    margin-left: 50%;
+  }
+  .col-sm-offset-5 {
+    margin-left: 41.66666667%;
+  }
+  .col-sm-offset-4 {
+    margin-left: 33.33333333%;
+  }
+  .col-sm-offset-3 {
+    margin-left: 25%;
+  }
+  .col-sm-offset-2 {
+    margin-left: 16.66666667%;
+  }
+  .col-sm-offset-1 {
+    margin-left: 8.33333333%;
+  }
+  .col-sm-offset-0 {
+    margin-left: 0;
+  }
+}
+@media (min-width: 992px) {
+  .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12 {
+    float: left;
+  }
+  .col-md-12 {
+    width: 100%;
+  }
+  .col-md-11 {
+    width: 91.66666667%;
+  }
+  .col-md-10 {
+    width: 83.33333333%;
+  }
+  .col-md-9 {
+    width: 75%;
+  }
+  .col-md-8 {
+    width: 66.66666667%;
+  }
+  .col-md-7 {
+    width: 58.33333333%;
+  }
+  .col-md-6 {
+    width: 50%;
+  }
+  .col-md-5 {
+    width: 41.66666667%;
+  }
+  .col-md-4 {
+    width: 33.33333333%;
+  }
+  .col-md-3 {
+    width: 25%;
+  }
+  .col-md-2 {
+    width: 16.66666667%;
+  }
+  .col-md-1 {
+    width: 8.33333333%;
+  }
+  .col-md-pull-12 {
+    right: 100%;
+  }
+  .col-md-pull-11 {
+    right: 91.66666667%;
+  }
+  .col-md-pull-10 {
+    right: 83.33333333%;
+  }
+  .col-md-pull-9 {
+    right: 75%;
+  }
+  .col-md-pull-8 {
+    right: 66.66666667%;
+  }
+  .col-md-pull-7 {
+    right: 58.33333333%;
+  }
+  .col-md-pull-6 {
+    right: 50%;
+  }
+  .col-md-pull-5 {
+    right: 41.66666667%;
+  }
+  .col-md-pull-4 {
+    right: 33.33333333%;
+  }
+  .col-md-pull-3 {
+    right: 25%;
+  }
+  .col-md-pull-2 {
+    right: 16.66666667%;
+  }
+  .col-md-pull-1 {
+    right: 8.33333333%;
+  }
+  .col-md-pull-0 {
+    right: 0;
+  }
+  .col-md-push-12 {
+    left: 100%;
+  }
+  .col-md-push-11 {
+    left: 91.66666667%;
+  }
+  .col-md-push-10 {
+    left: 83.33333333%;
+  }
+  .col-md-push-9 {
+    left: 75%;
+  }
+  .col-md-push-8 {
+    left: 66.66666667%;
+  }
+  .col-md-push-7 {
+    left: 58.33333333%;
+  }
+  .col-md-push-6 {
+    left: 50%;
+  }
+  .col-md-push-5 {
+    left: 41.66666667%;
+  }
+  .col-md-push-4 {
+    left: 33.33333333%;
+  }
+  .col-md-push-3 {
+    left: 25%;
+  }
+  .col-md-push-2 {
+    left: 16.66666667%;
+  }
+  .col-md-push-1 {
+    left: 8.33333333%;
+  }
+  .col-md-push-0 {
+    left: 0;
+  }
+  .col-md-offset-12 {
+    margin-left: 100%;
+  }
+  .col-md-offset-11 {
+    margin-left: 91.66666667%;
+  }
+  .col-md-offset-10 {
+    margin-left: 83.33333333%;
+  }
+  .col-md-offset-9 {
+    margin-left: 75%;
+  }
+  .col-md-offset-8 {
+    margin-left: 66.66666667%;
+  }
+  .col-md-offset-7 {
+    margin-left: 58.33333333%;
+  }
+  .col-md-offset-6 {
+    margin-left: 50%;
+  }
+  .col-md-offset-5 {
+    margin-left: 41.66666667%;
+  }
+  .col-md-offset-4 {
+    margin-left: 33.33333333%;
+  }
+  .col-md-offset-3 {
+    margin-left: 25%;
+  }
+  .col-md-offset-2 {
+    margin-left: 16.66666667%;
+  }
+  .col-md-offset-1 {
+    margin-left: 8.33333333%;
+  }
+  .col-md-offset-0 {
+    margin-left: 0;
+  }
+}
+@media (min-width: 1200px) {
+  .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12 {
+    float: left;
+  }
+  .col-lg-12 {
+    width: 100%;
+  }
+  .col-lg-11 {
+    width: 91.66666667%;
+  }
+  .col-lg-10 {
+    width: 83.33333333%;
+  }
+  .col-lg-9 {
+    width: 75%;
+  }
+  .col-lg-8 {
+    width: 66.66666667%;
+  }
+  .col-lg-7 {
+    width: 58.33333333%;
+  }
+  .col-lg-6 {
+    width: 50%;
+  }
+  .col-lg-5 {
+    width: 41.66666667%;
+  }
+  .col-lg-4 {
+    width: 33.33333333%;
+  }
+  .col-lg-3 {
+    width: 25%;
+  }
+  .col-lg-2 {
+    width: 16.66666667%;
+  }
+  .col-lg-1 {
+    width: 8.33333333%;
+  }
+  .col-lg-pull-12 {
+    right: 100%;
+  }
+  .col-lg-pull-11 {
+    right: 91.66666667%;
+  }
+  .col-lg-pull-10 {
+    right: 83.33333333%;
+  }
+  .col-lg-pull-9 {
+    right: 75%;
+  }
+  .col-lg-pull-8 {
+    right: 66.66666667%;
+  }
+  .col-lg-pull-7 {
+    right: 58.33333333%;
+  }
+  .col-lg-pull-6 {
+    right: 50%;
+  }
+  .col-lg-pull-5 {
+    right: 41.66666667%;
+  }
+  .col-lg-pull-4 {
+    right: 33.33333333%;
+  }
+  .col-lg-pull-3 {
+    right: 25%;
+  }
+  .col-lg-pull-2 {
+    right: 16.66666667%;
+  }
+  .col-lg-pull-1 {
+    right: 8.33333333%;
+  }
+  .col-lg-pull-0 {
+    right: 0;
+  }
+  .col-lg-push-12 {
+    left: 100%;
+  }
+  .col-lg-push-11 {
+    left: 91.66666667%;
+  }
+  .col-lg-push-10 {
+    left: 83.33333333%;
+  }
+  .col-lg-push-9 {
+    left: 75%;
+  }
+  .col-lg-push-8 {
+    left: 66.66666667%;
+  }
+  .col-lg-push-7 {
+    left: 58.33333333%;
+  }
+  .col-lg-push-6 {
+    left: 50%;
+  }
+  .col-lg-push-5 {
+    left: 41.66666667%;
+  }
+  .col-lg-push-4 {
+    left: 33.33333333%;
+  }
+  .col-lg-push-3 {
+    left: 25%;
+  }
+  .col-lg-push-2 {
+    left: 16.66666667%;
+  }
+  .col-lg-push-1 {
+    left: 8.33333333%;
+  }
+  .col-lg-push-0 {
+    left: 0;
+  }
+  .col-lg-offset-12 {
+    margin-left: 100%;
+  }
+  .col-lg-offset-11 {
+    margin-left: 91.66666667%;
+  }
+  .col-lg-offset-10 {
+    margin-left: 83.33333333%;
+  }
+  .col-lg-offset-9 {
+    margin-left: 75%;
+  }
+  .col-lg-offset-8 {
+    margin-left: 66.66666667%;
+  }
+  .col-lg-offset-7 {
+    margin-left: 58.33333333%;
+  }
+  .col-lg-offset-6 {
+    margin-left: 50%;
+  }
+  .col-lg-offset-5 {
+    margin-left: 41.66666667%;
+  }
+  .col-lg-offset-4 {
+    margin-left: 33.33333333%;
+  }
+  .col-lg-offset-3 {
+    margin-left: 25%;
+  }
+  .col-lg-offset-2 {
+    margin-left: 16.66666667%;
+  }
+  .col-lg-offset-1 {
+    margin-left: 8.33333333%;
+  }
+  .col-lg-offset-0 {
+    margin-left: 0;
+  }
+}
+table {
+  max-width: 100%;
+  background-color: transparent;
+}
+th {
+  text-align: left;
+}
+.table {
+  width: 100%;
+  margin-bottom: 20px;
+}
+.table > thead > tr > th,
+.table > tbody > tr > th,
+.table > tfoot > tr > th,
+.table > thead > tr > td,
+.table > tbody > tr > td,
+.table > tfoot > tr > td {
+  padding: 8px;
+  line-height: 1.42857143;
+  vertical-align: top;
+  border-top: 1px solid #ddd;
+}
+.table > thead > tr > th {
+  vertical-align: bottom;
+  border-bottom: 2px solid #ddd;
+}
+.table > caption + thead > tr:first-child > th,
+.table > colgroup + thead > tr:first-child > th,
+.table > thead:first-child > tr:first-child > th,
+.table > caption + thead > tr:first-child > td,
+.table > colgroup + thead > tr:first-child > td,
+.table > thead:first-child > tr:first-child > td {
+  border-top: 0;
+}
+.table > tbody + tbody {
+  border-top: 2px solid #ddd;
+}
+.table .table {
+  background-color: #fff;
+}
+.table-condensed > thead > tr > th,
+.table-condensed > tbody > tr > th,
+.table-condensed > tfoot > tr > th,
+.table-condensed > thead > tr > td,
+.table-condensed > tbody > tr > td,
+.table-condensed > tfoot > tr > td {
+  padding: 5px;
+}
+.table-bordered {
+  border: 1px solid #ddd;
+}
+.table-bordered > thead > tr > th,
+.table-bordered > tbody > tr > th,
+.table-bordered > tfoot > tr > th,
+.table-bordered > thead > tr > td,
+.table-bordered > tbody > tr > td,
+.table-bordered > tfoot > tr > td {
+  border: 1px solid #ddd;
+}
+.table-bordered > thead > tr > th,
+.table-bordered > thead > tr > td {
+  border-bottom-width: 2px;
+}
+.table-striped > tbody > tr:nth-child(odd) > td,
+.table-striped > tbody > tr:nth-child(odd) > th {
+  background-color: #f9f9f9;
+}
+.table-hover > tbody > tr:hover > td,
+.table-hover > tbody > tr:hover > th {
+  background-color: #f5f5f5;
+}
+table col[class*="col-"] {
+  position: static;
+  display: table-column;
+  float: none;
+}
+table td[class*="col-"],
+table th[class*="col-"] {
+  position: static;
+  display: table-cell;
+  float: none;
+}
+.table > thead > tr > td.active,
+.table > tbody > tr > td.active,
+.table > tfoot > tr > td.active,
+.table > thead > tr > th.active,
+.table > tbody > tr > th.active,
+.table > tfoot > tr > th.active,
+.table > thead > tr.active > td,
+.table > tbody > tr.active > td,
+.table > tfoot > tr.active > td,
+.table > thead > tr.active > th,
+.table > tbody > tr.active > th,
+.table > tfoot > tr.active > th {
+  background-color: #f5f5f5;
+}
+.table-hover > tbody > tr > td.active:hover,
+.table-hover > tbody > tr > th.active:hover,
+.table-hover > tbody > tr.active:hover > td,
+.table-hover > tbody > tr.active:hover > th {
+  background-color: #e8e8e8;
+}
+.table > thead > tr > td.success,
+.table > tbody > tr > td.success,
+.table > tfoot > tr > td.success,
+.table > thead > tr > th.success,
+.table > tbody > tr > th.success,
+.table > tfoot > tr > th.success,
+.table > thead > tr.success > td,
+.table > tbody > tr.success > td,
+.table > tfoot > tr.success > td,
+.table > thead > tr.success > th,
+.table > tbody > tr.success > th,
+.table > tfoot > tr.success > th {
+  background-color: #dff0d8;
+}
+.table-hover > tbody > tr > td.success:hover,
+.table-hover > tbody > tr > th.success:hover,
+.table-hover > tbody > tr.success:hover > td,
+.table-hover > tbody > tr.success:hover > th {
+  background-color: #d0e9c6;
+}
+.table > thead > tr > td.info,
+.table > tbody > tr > td.info,
+.table > tfoot > tr > td.info,
+.table > thead > tr > th.info,
+.table > tbody > tr > th.info,
+.table > tfoot > tr > th.info,
+.table > thead > tr.info > td,
+.table > tbody > tr.info > td,
+.table > tfoot > tr.info > td,
+.table > thead > tr.info > th,
+.table > tbody > tr.info > th,
+.table > tfoot > tr.info > th {
+  background-color: #d9edf7;
+}
+.table-hover > tbody > tr > td.info:hover,
+.table-hover > tbody > tr > th.info:hover,
+.table-hover > tbody > tr.info:hover > td,
+.table-hover > tbody > tr.info:hover > th {
+  background-color: #c4e3f3;
+}
+.table > thead > tr > td.warning,
+.table > tbody > tr > td.warning,
+.table > tfoot > tr > td.warning,
+.table > thead > tr > th.warning,
+.table > tbody > tr > th.warning,
+.table > tfoot > tr > th.warning,
+.table > thead > tr.warning > td,
+.table > tbody > tr.warning > td,
+.table > tfoot > tr.warning > td,
+.table > thead > tr.warning > th,
+.table > tbody > tr.warning > th,
+.table > tfoot > tr.warning > th {
+  background-color: #fcf8e3;
+}
+.table-hover > tbody > tr > td.warning:hover,
+.table-hover > tbody > tr > th.warning:hover,
+.table-hover > tbody > tr.warning:hover > td,
+.table-hover > tbody > tr.warning:hover > th {
+  background-color: #faf2cc;
+}
+.table > thead > tr > td.danger,
+.table > tbody > tr > td.danger,
+.table > tfoot > tr > td.danger,
+.table > thead > tr > th.danger,
+.table > tbody > tr > th.danger,
+.table > tfoot > tr > th.danger,
+.table > thead > tr.danger > td,
+.table > tbody > tr.danger > td,
+.table > tfoot > tr.danger > td,
+.table > thead > tr.danger > th,
+.table > tbody > tr.danger > th,
+.table > tfoot > tr.danger > th {
+  background-color: #f2dede;
+}
+.table-hover > tbody > tr > td.danger:hover,
+.table-hover > tbody > tr > th.danger:hover,
+.table-hover > tbody > tr.danger:hover > td,
+.table-hover > tbody > tr.danger:hover > th {
+  background-color: #ebcccc;
+}
+@media (max-width: 767px) {
+  .table-responsive {
+    width: 100%;
+    margin-bottom: 15px;
+    overflow-x: scroll;
+    overflow-y: hidden;
+    -webkit-overflow-scrolling: touch;
+    -ms-overflow-style: -ms-autohiding-scrollbar;
+    border: 1px solid #ddd;
+  }
+  .table-responsive > .table {
+    margin-bottom: 0;
+  }
+  .table-responsive > .table > thead > tr > th,
+  .table-responsive > .table > tbody > tr > th,
+  .table-responsive > .table > tfoot > tr > th,
+  .table-responsive > .table > thead > tr > td,
+  .table-responsive > .table > tbody > tr > td,
+  .table-responsive > .table > tfoot > tr > td {
+    white-space: nowrap;
+  }
+  .table-responsive > .table-bordered {
+    border: 0;
+  }
+  .table-responsive > .table-bordered > thead > tr > th:first-child,
+  .table-responsive > .table-bordered > tbody > tr > th:first-child,
+  .table-responsive > .table-bordered > tfoot > tr > th:first-child,
+  .table-responsive > .table-bordered > thead > tr > td:first-child,
+  .table-responsive > .table-bordered > tbody > tr > td:first-child,
+  .table-responsive > .table-bordered > tfoot > tr > td:first-child {
+    border-left: 0;
+  }
+  .table-responsive > .table-bordered > thead > tr > th:last-child,
+  .table-responsive > .table-bordered > tbody > tr > th:last-child,
+  .table-responsive > .table-bordered > tfoot > tr > th:last-child,
+  .table-responsive > .table-bordered > thead > tr > td:last-child,
+  .table-responsive > .table-bordered > tbody > tr > td:last-child,
+  .table-responsive > .table-bordered > tfoot > tr > td:last-child {
+    border-right: 0;
+  }
+  .table-responsive > .table-bordered > tbody > tr:last-child > th,
+  .table-responsive > .table-bordered > tfoot > tr:last-child > th,
+  .table-responsive > .table-bordered > tbody > tr:last-child > td,
+  .table-responsive > .table-bordered > tfoot > tr:last-child > td {
+    border-bottom: 0;
+  }
+}
+fieldset {
+  min-width: 0;
+  padding: 0;
+  margin: 0;
+  border: 0;
+}
+legend {
+  display: block;
+  width: 100%;
+  padding: 0;
+  margin-bottom: 20px;
+  font-size: 21px;
+  line-height: inherit;
+  color: #333;
+  border: 0;
+  border-bottom: 1px solid #e5e5e5;
+}
+label {
+  display: inline-block;
+  margin-bottom: 5px;
+  font-weight: bold;
+}
+input[type="search"] {
+  -webkit-box-sizing: border-box;
+     -moz-box-sizing: border-box;
+          box-sizing: border-box;
+}
+input[type="radio"],
+input[type="checkbox"] {
+  margin: 4px 0 0;
+  margin-top: 1px \9;
+  /* IE8-9 */
+  line-height: normal;
+}
+input[type="file"] {
+  display: block;
+}
+input[type="range"] {
+  display: block;
+  width: 100%;
+}
+select[multiple],
+select[size] {
+  height: auto;
+}
+input[type="file"]:focus,
+input[type="radio"]:focus,
+input[type="checkbox"]:focus {
+  outline: thin dotted;
+  outline: 5px auto -webkit-focus-ring-color;
+  outline-offset: -2px;
+}
+output {
+  display: block;
+  padding-top: 7px;
+  font-size: 14px;
+  line-height: 1.42857143;
+  color: #555;
+}
+.form-control {
+  display: block;
+  width: 100%;
+  height: 34px;
+  padding: 6px 12px;
+  font-size: 14px;
+  line-height: 1.42857143;
+  color: #555;
+  background-color: #fff;
+  background-image: none;
+  border: 1px solid #ccc;
+  border-radius: 4px;
+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
+          box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
+  -webkit-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
+          transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
+}
+.form-control:focus {
+  border-color: #66afe9;
+  outline: 0;
+  -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6);
+          box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6);
+}
+.form-control::-moz-placeholder {
+  color: #999;
+  opacity: 1;
+}
+.form-control:-ms-input-placeholder {
+  color: #999;
+}
+.form-control::-webkit-input-placeholder {
+  color: #999;
+}
+.form-control[disabled],
+.form-control[readonly],
+fieldset[disabled] .form-control {
+  cursor: not-allowed;
+  background-color: #eee;
+  opacity: 1;
+}
+textarea.form-control {
+  height: auto;
+}
+input[type="search"] {
+  -webkit-appearance: none;
+}
+input[type="date"] {
+  line-height: 34px;
+}
+.form-group {
+  margin-bottom: 15px;
+}
+.radio,
+.checkbox {
+  display: block;
+  min-height: 20px;
+  padding-left: 20px;
+  margin-top: 10px;
+  margin-bottom: 10px;
+}
+.radio label,
+.checkbox label {
+  display: inline;
+  font-weight: normal;
+  cursor: pointer;
+}
+.radio input[type="radio"],
+.radio-inline input[type="radio"],
+.checkbox input[type="checkbox"],
+.checkbox-inline input[type="checkbox"] {
+  float: left;
+  margin-left: -20px;
+}
+.radio + .radio,
+.checkbox + .checkbox {
+  margin-top: -5px;
+}
+.radio-inline,
+.checkbox-inline {
+  display: inline-block;
+  padding-left: 20px;
+  margin-bottom: 0;
+  font-weight: normal;
+  vertical-align: middle;
+  cursor: pointer;
+}
+.radio-inline + .radio-inline,
+.checkbox-inline + .checkbox-inline {
+  margin-top: 0;
+  margin-left: 10px;
+}
+input[type="radio"][disabled],
+input[type="checkbox"][disabled],
+.radio[disabled],
+.radio-inline[disabled],
+.checkbox[disabled],
+.checkbox-inline[disabled],
+fieldset[disabled] input[type="radio"],
+fieldset[disabled] input[type="checkbox"],
+fieldset[disabled] .radio,
+fieldset[disabled] .radio-inline,
+fieldset[disabled] .checkbox,
+fieldset[disabled] .checkbox-inline {
+  cursor: not-allowed;
+}
+.input-sm {
+  height: 30px;
+  padding: 5px 10px;
+  font-size: 12px;
+  line-height: 1.5;
+  border-radius: 3px;
+}
+select.input-sm {
+  height: 30px;
+  line-height: 30px;
+}
+textarea.input-sm,
+select[multiple].input-sm {
+  height: auto;
+}
+.input-lg {
+  height: 46px;
+  padding: 10px 16px;
+  font-size: 18px;
+  line-height: 1.33;
+  border-radius: 6px;
+}
+select.input-lg {
+  height: 46px;
+  line-height: 46px;
+}
+textarea.input-lg,
+select[multiple].input-lg {
+  height: auto;
+}
+.has-feedback {
+  position: relative;
+}
+.has-feedback .form-control {
+  padding-right: 42.5px;
+}
+.has-feedback .form-control-feedback {
+  position: absolute;
+  top: 25px;
+  right: 0;
+  display: block;
+  width: 34px;
+  height: 34px;
+  line-height: 34px;
+  text-align: center;
+}
+.has-success .help-block,
+.has-success .control-label,
+.has-success .radio,
+.has-success .checkbox,
+.has-success .radio-inline,
+.has-success .checkbox-inline {
+  color: #3c763d;
+}
+.has-success .form-control {
+  border-color: #3c763d;
+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
+          box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
+}
+.has-success .form-control:focus {
+  border-color: #2b542c;
+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168;
+          box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168;
+}
+.has-success .input-group-addon {
+  color: #3c763d;
+  background-color: #dff0d8;
+  border-color: #3c763d;
+}
+.has-success .form-control-feedback {
+  color: #3c763d;
+}
+.has-warning .help-block,
+.has-warning .control-label,
+.has-warning .radio,
+.has-warning .checkbox,
+.has-warning .radio-inline,
+.has-warning .checkbox-inline {
+  color: #8a6d3b;
+}
+.has-warning .form-control {
+  border-color: #8a6d3b;
+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
+          box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
+}
+.has-warning .form-control:focus {
+  border-color: #66512c;
+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b;
+          box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b;
+}
+.has-warning .input-group-addon {
+  color: #8a6d3b;
+  background-color: #fcf8e3;
+  border-color: #8a6d3b;
+}
+.has-warning .form-control-feedback {
+  color: #8a6d3b;
+}
+.has-error .help-block,
+.has-error .control-label,
+.has-error .radio,
+.has-error .checkbox,
+.has-error .radio-inline,
+.has-error .checkbox-inline {
+  color: #a94442;
+}
+.has-error .form-control {
+  border-color: #a94442;
+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
+          box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
+}
+.has-error .form-control:focus {
+  border-color: #843534;
+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483;
+          box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483;
+}
+.has-error .input-group-addon {
+  color: #a94442;
+  background-color: #f2dede;
+  border-color: #a94442;
+}
+.has-error .form-control-feedback {
+  color: #a94442;
+}
+.form-control-static {
+  margin-bottom: 0;
+}
+.help-block {
+  display: block;
+  margin-top: 5px;
+  margin-bottom: 10px;
+  color: #737373;
+}
+@media (min-width: 768px) {
+  .form-inline .form-group {
+    display: inline-block;
+    margin-bottom: 0;
+    vertical-align: middle;
+  }
+  .form-inline .form-control {
+    display: inline-block;
+    width: auto;
+    vertical-align: middle;
+  }
+  .form-inline .input-group > .form-control {
+    width: 100%;
+  }
+  .form-inline .control-label {
+    margin-bottom: 0;
+    vertical-align: middle;
+  }
+  .form-inline .radio,
+  .form-inline .checkbox {
+    display: inline-block;
+    padding-left: 0;
+    margin-top: 0;
+    margin-bottom: 0;
+    vertical-align: middle;
+  }
+  .form-inline .radio input[type="radio"],
+  .form-inline .checkbox input[type="checkbox"] {
+    float: none;
+    margin-left: 0;
+  }
+  .form-inline .has-feedback .form-control-feedback {
+    top: 0;
+  }
+}
+.form-horizontal .control-label,
+.form-horizontal .radio,
+.form-horizontal .checkbox,
+.form-horizontal .radio-inline,
+.form-horizontal .checkbox-inline {
+  padding-top: 7px;
+  margin-top: 0;
+  margin-bottom: 0;
+}
+.form-horizontal .radio,
+.form-horizontal .checkbox {
+  min-height: 27px;
+}
+.form-horizontal .form-group {
+  margin-right: -15px;
+  margin-left: -15px;
+}
+.form-horizontal .form-control-static {
+  padding-top: 7px;
+}
+@media (min-width: 768px) {
+  .form-horizontal .control-label {
+    text-align: right;
+  }
+}
+.form-horizontal .has-feedback .form-control-feedback {
+  top: 0;
+  right: 15px;
+}
+.btn {
+  display: inline-block;
+  padding: 6px 12px;
+  margin-bottom: 0;
+  font-size: 14px;
+  font-weight: normal;
+  line-height: 1.42857143;
+  text-align: center;
+  white-space: nowrap;
+  vertical-align: middle;
+  cursor: pointer;
+  -webkit-user-select: none;
+     -moz-user-select: none;
+      -ms-user-select: none;
+          user-select: none;
+  background-image: none;
+  border: 1px solid transparent;
+  border-radius: 4px;
+}
+.btn:focus,
+.btn:active:focus,
+.btn.active:focus {
+  outline: thin dotted;
+  outline: 5px auto -webkit-focus-ring-color;
+  outline-offset: -2px;
+}
+.btn:hover,
+.btn:focus {
+  color: #333;
+  text-decoration: none;
+}
+.btn:active,
+.btn.active {
+  background-image: none;
+  outline: 0;
+  -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
+          box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
+}
+.btn.disabled,
+.btn[disabled],
+fieldset[disabled] .btn {
+  pointer-events: none;
+  cursor: not-allowed;
+  filter: alpha(opacity=65);
+  -webkit-box-shadow: none;
+          box-shadow: none;
+  opacity: .65;
+}
+.btn-default {
+  color: #333;
+  background-color: #fff;
+  border-color: #ccc;
+}
+.btn-default:hover,
+.btn-default:focus,
+.btn-default:active,
+.btn-default.active,
+.open .dropdown-toggle.btn-default {
+  color: #333;
+  background-color: #ebebeb;
+  border-color: #adadad;
+}
+.btn-default:active,
+.btn-default.active,
+.open .dropdown-toggle.btn-default {
+  background-image: none;
+}
+.btn-default.disabled,
+.btn-default[disabled],
+fieldset[disabled] .btn-default,
+.btn-default.disabled:hover,
+.btn-default[disabled]:hover,
+fieldset[disabled] .btn-default:hover,
+.btn-default.disabled:focus,
+.btn-default[disabled]:focus,
+fieldset[disabled] .btn-default:focus,
+.btn-default.disabled:active,
+.btn-default[disabled]:active,
+fieldset[disabled] .btn-default:active,
+.btn-default.disabled.active,
+.btn-default[disabled].active,
+fieldset[disabled] .btn-default.active {
+  background-color: #fff;
+  border-color: #ccc;
+}
+.btn-default .badge {
+  color: #fff;
+  background-color: #333;
+}
+.btn-primary {
+  color: #fff;
+  background-color: #428bca;
+  border-color: #357ebd;
+}
+.btn-primary:hover,
+.btn-primary:focus,
+.btn-primary:active,
+.btn-primary.active,
+.open .dropdown-toggle.btn-primary {
+  color: #fff;
+  background-color: #3276b1;
+  border-color: #285e8e;
+}
+.btn-primary:active,
+.btn-primary.active,
+.open .dropdown-toggle.btn-primary {
+  background-image: none;
+}
+.btn-primary.disabled,
+.btn-primary[disabled],
+fieldset[disabled] .btn-primary,
+.btn-primary.disabled:hover,
+.btn-primary[disabled]:hover,
+fieldset[disabled] .btn-primary:hover,
+.btn-primary.disabled:focus,
+.btn-primary[disabled]:focus,
+fieldset[disabled] .btn-primary:focus,
+.btn-primary.disabled:active,
+.btn-primary[disabled]:active,
+fieldset[disabled] .btn-primary:active,
+.btn-primary.disabled.active,
+.btn-primary[disabled].active,
+fieldset[disabled] .btn-primary.active {
+  background-color: #428bca;
+  border-color: #357ebd;
+}
+.btn-primary .badge {
+  color: #428bca;
+  background-color: #fff;
+}
+.btn-success {
+  color: #fff;
+  background-color: #5cb85c;
+  border-color: #4cae4c;
+}
+.btn-success:hover,
+.btn-success:focus,
+.btn-success:active,
+.btn-success.active,
+.open .dropdown-toggle.btn-success {
+  color: #fff;
+  background-color: #47a447;
+  border-color: #398439;
+}
+.btn-success:active,
+.btn-success.active,
+.open .dropdown-toggle.btn-success {
+  background-image: none;
+}
+.btn-success.disabled,
+.btn-success[disabled],
+fieldset[disabled] .btn-success,
+.btn-success.disabled:hover,
+.btn-success[disabled]:hover,
+fieldset[disabled] .btn-success:hover,
+.btn-success.disabled:focus,
+.btn-success[disabled]:focus,
+fieldset[disabled] .btn-success:focus,
+.btn-success.disabled:active,
+.btn-success[disabled]:active,
+fieldset[disabled] .btn-success:active,
+.btn-success.disabled.active,
+.btn-success[disabled].active,
+fieldset[disabled] .btn-success.active {
+  background-color: #5cb85c;
+  border-color: #4cae4c;
+}
+.btn-success .badge {
+  color: #5cb85c;
+  background-color: #fff;
+}
+.btn-info {
+  color: #fff;
+  background-color: #5bc0de;
+  border-color: #46b8da;
+}
+.btn-info:hover,
+.btn-info:focus,
+.btn-info:active,
+.btn-info.active,
+.open .dropdown-toggle.btn-info {
+  color: #fff;
+  background-color: #39b3d7;
+  border-color: #269abc;
+}
+.btn-info:active,
+.btn-info.active,
+.open .dropdown-toggle.btn-info {
+  background-image: none;
+}
+.btn-info.disabled,
+.btn-info[disabled],
+fieldset[disabled] .btn-info,
+.btn-info.disabled:hover,
+.btn-info[disabled]:hover,
+fieldset[disabled] .btn-info:hover,
+.btn-info.disabled:focus,
+.btn-info[disabled]:focus,
+fieldset[disabled] .btn-info:focus,
+.btn-info.disabled:active,
+.btn-info[disabled]:active,
+fieldset[disabled] .btn-info:active,
+.btn-info.disabled.active,
+.btn-info[disabled].active,
+fieldset[disabled] .btn-info.active {
+  background-color: #5bc0de;
+  border-color: #46b8da;
+}
+.btn-info .badge {
+  color: #5bc0de;
+  background-color: #fff;
+}
+.btn-warning {
+  color: #fff;
+  background-color: #f0ad4e;
+  border-color: #eea236;
+}
+.btn-warning:hover,
+.btn-warning:focus,
+.btn-warning:active,
+.btn-warning.active,
+.open .dropdown-toggle.btn-warning {
+  color: #fff;
+  background-color: #ed9c28;
+  border-color: #d58512;
+}
+.btn-warning:active,
+.btn-warning.active,
+.open .dropdown-toggle.btn-warning {
+  background-image: none;
+}
+.btn-warning.disabled,
+.btn-warning[disabled],
+fieldset[disabled] .btn-warning,
+.btn-warning.disabled:hover,
+.btn-warning[disabled]:hover,
+fieldset[disabled] .btn-warning:hover,
+.btn-warning.disabled:focus,
+.btn-warning[disabled]:focus,
+fieldset[disabled] .btn-warning:focus,
+.btn-warning.disabled:active,
+.btn-warning[disabled]:active,
+fieldset[disabled] .btn-warning:active,
+.btn-warning.disabled.active,
+.btn-warning[disabled].active,
+fieldset[disabled] .btn-warning.active {
+  background-color: #f0ad4e;
+  border-color: #eea236;
+}
+.btn-warning .badge {
+  color: #f0ad4e;
+  background-color: #fff;
+}
+.btn-danger {
+  color: #fff;
+  background-color: #d9534f;
+  border-color: #d43f3a;
+}
+.btn-danger:hover,
+.btn-danger:focus,
+.btn-danger:active,
+.btn-danger.active,
+.open .dropdown-toggle.btn-danger {
+  color: #fff;
+  background-color: #d2322d;
+  border-color: #ac2925;
+}
+.btn-danger:active,
+.btn-danger.active,
+.open .dropdown-toggle.btn-danger {
+  background-image: none;
+}
+.btn-danger.disabled,
+.btn-danger[disabled],
+fieldset[disabled] .btn-danger,
+.btn-danger.disabled:hover,
+.btn-danger[disabled]:hover,
+fieldset[disabled] .btn-danger:hover,
+.btn-danger.disabled:focus,
+.btn-danger[disabled]:focus,
+fieldset[disabled] .btn-danger:focus,
+.btn-danger.disabled:active,
+.btn-danger[disabled]:active,
+fieldset[disabled] .btn-danger:active,
+.btn-danger.disabled.active,
+.btn-danger[disabled].active,
+fieldset[disabled] .btn-danger.active {
+  background-color: #d9534f;
+  border-color: #d43f3a;
+}
+.btn-danger .badge {
+  color: #d9534f;
+  background-color: #fff;
+}
+.btn-link {
+  font-weight: normal;
+  color: #428bca;
+  cursor: pointer;
+  border-radius: 0;
+}
+.btn-link,
+.btn-link:active,
+.btn-link[disabled],
+fieldset[disabled] .btn-link {
+  background-color: transparent;
+  -webkit-box-shadow: none;
+          box-shadow: none;
+}
+.btn-link,
+.btn-link:hover,
+.btn-link:focus,
+.btn-link:active {
+  border-color: transparent;
+}
+.btn-link:hover,
+.btn-link:focus {
+  color: #2a6496;
+  text-decoration: underline;
+  background-color: transparent;
+}
+.btn-link[disabled]:hover,
+fieldset[disabled] .btn-link:hover,
+.btn-link[disabled]:focus,
+fieldset[disabled] .btn-link:focus {
+  color: #999;
+  text-decoration: none;
+}
+.btn-lg,
+.btn-group-lg > .btn {
+  padding: 10px 16px;
+  font-size: 18px;
+  line-height: 1.33;
+  border-radius: 6px;
+}
+.btn-sm,
+.btn-group-sm > .btn {
+  padding: 5px 10px;
+  font-size: 12px;
+  line-height: 1.5;
+  border-radius: 3px;
+}
+.btn-xs,
+.btn-group-xs > .btn {
+  padding: 1px 5px;
+  font-size: 12px;
+  line-height: 1.5;
+  border-radius: 3px;
+}
+.btn-block {
+  display: block;
+  width: 100%;
+  padding-right: 0;
+  padding-left: 0;
+}
+.btn-block + .btn-block {
+  margin-top: 5px;
+}
+input[type="submit"].btn-block,
+input[type="reset"].btn-block,
+input[type="button"].btn-block {
+  width: 100%;
+}
+.fade {
+  opacity: 0;
+  -webkit-transition: opacity .15s linear;
+          transition: opacity .15s linear;
+}
+.fade.in {
+  opacity: 1;
+}
+.collapse {
+  display: none;
+}
+.collapse.in {
+  display: block;
+}
+.collapsing {
+  position: relative;
+  height: 0;
+  overflow: hidden;
+  -webkit-transition: height .35s ease;
+          transition: height .35s ease;
+}
+@font-face {
+  font-family: 'Glyphicons Halflings';
+
+  src: url('../fonts/glyphicons-halflings-regular.eot');
+  src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons-halflings-regular.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg');
+}
+.glyphicon {
+  position: relative;
+  top: 1px;
+  display: inline-block;
+  font-family: 'Glyphicons Halflings';
+  font-style: normal;
+  font-weight: normal;
+  line-height: 1;
+
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+.glyphicon-asterisk:before {
+  content: "\2a";
+}
+.glyphicon-plus:before {
+  content: "\2b";
+}
+.glyphicon-euro:before {
+  content: "\20ac";
+}
+.glyphicon-minus:before {
+  content: "\2212";
+}
+.glyphicon-cloud:before {
+  content: "\2601";
+}
+.glyphicon-envelope:before {
+  content: "\2709";
+}
+.glyphicon-pencil:before {
+  content: "\270f";
+}
+.glyphicon-glass:before {
+  content: "\e001";
+}
+.glyphicon-music:before {
+  content: "\e002";
+}
+.glyphicon-search:before {
+  content: "\e003";
+}
+.glyphicon-heart:before {
+  content: "\e005";
+}
+.glyphicon-star:before {
+  content: "\e006";
+}
+.glyphicon-star-empty:before {
+  content: "\e007";
+}
+.glyphicon-user:before {
+  content: "\e008";
+}
+.glyphicon-film:before {
+  content: "\e009";
+}
+.glyphicon-th-large:before {
+  content: "\e010";
+}
+.glyphicon-th:before {
+  content: "\e011";
+}
+.glyphicon-th-list:before {
+  content: "\e012";
+}
+.glyphicon-ok:before {
+  content: "\e013";
+}
+.glyphicon-remove:before {
+  content: "\e014";
+}
+.glyphicon-zoom-in:before {
+  content: "\e015";
+}
+.glyphicon-zoom-out:before {
+  content: "\e016";
+}
+.glyphicon-off:before {
+  content: "\e017";
+}
+.glyphicon-signal:before {
+  content: "\e018";
+}
+.glyphicon-cog:before {
+  content: "\e019";
+}
+.glyphicon-trash:before {
+  content: "\e020";
+}
+.glyphicon-home:before {
+  content: "\e021";
+}
+.glyphicon-file:before {
+  content: "\e022";
+}
+.glyphicon-time:before {
+  content: "\e023";
+}
+.glyphicon-road:before {
+  content: "\e024";
+}
+.glyphicon-download-alt:before {
+  content: "\e025";
+}
+.glyphicon-download:before {
+  content: "\e026";
+}
+.glyphicon-upload:before {
+  content: "\e027";
+}
+.glyphicon-inbox:before {
+  content: "\e028";
+}
+.glyphicon-play-circle:before {
+  content: "\e029";
+}
+.glyphicon-repeat:before {
+  content: "\e030";
+}
+.glyphicon-refresh:before {
+  content: "\e031";
+}
+.glyphicon-list-alt:before {
+  content: "\e032";
+}
+.glyphicon-lock:before {
+  content: "\e033";
+}
+.glyphicon-flag:before {
+  content: "\e034";
+}
+.glyphicon-headphones:before {
+  content: "\e035";
+}
+.glyphicon-volume-off:before {
+  content: "\e036";
+}
+.glyphicon-volume-down:before {
+  content: "\e037";
+}
+.glyphicon-volume-up:before {
+  content: "\e038";
+}
+.glyphicon-qrcode:before {
+  content: "\e039";
+}
+.glyphicon-barcode:before {
+  content: "\e040";
+}
+.glyphicon-tag:before {
+  content: "\e041";
+}
+.glyphicon-tags:before {
+  content: "\e042";
+}
+.glyphicon-book:before {
+  content: "\e043";
+}
+.glyphicon-bookmark:before {
+  content: "\e044";
+}
+.glyphicon-print:before {
+  content: "\e045";
+}
+.glyphicon-camera:before {
+  content: "\e046";
+}
+.glyphicon-font:before {
+  content: "\e047";
+}
+.glyphicon-bold:before {
+  content: "\e048";
+}
+.glyphicon-italic:before {
+  content: "\e049";
+}
+.glyphicon-text-height:before {
+  content: "\e050";
+}
+.glyphicon-text-width:before {
+  content: "\e051";
+}
+.glyphicon-align-left:before {
+  content: "\e052";
+}
+.glyphicon-align-center:before {
+  content: "\e053";
+}
+.glyphicon-align-right:before {
+  content: "\e054";
+}
+.glyphicon-align-justify:before {
+  content: "\e055";
+}
+.glyphicon-list:before {
+  content: "\e056";
+}
+.glyphicon-indent-left:before {
+  content: "\e057";
+}
+.glyphicon-indent-right:before {
+  content: "\e058";
+}
+.glyphicon-facetime-video:before {
+  content: "\e059";
+}
+.glyphicon-picture:before {
+  content: "\e060";
+}
+.glyphicon-map-marker:before {
+  content: "\e062";
+}
+.glyphicon-adjust:before {
+  content: "\e063";
+}
+.glyphicon-tint:before {
+  content: "\e064";
+}
+.glyphicon-edit:before {
+  content: "\e065";
+}
+.glyphicon-share:before {
+  content: "\e066";
+}
+.glyphicon-check:before {
+  content: "\e067";
+}
+.glyphicon-move:before {
+  content: "\e068";
+}
+.glyphicon-step-backward:before {
+  content: "\e069";
+}
+.glyphicon-fast-backward:before {
+  content: "\e070";
+}
+.glyphicon-backward:before {
+  content: "\e071";
+}
+.glyphicon-play:before {
+  content: "\e072";
+}
+.glyphicon-pause:before {
+  content: "\e073";
+}
+.glyphicon-stop:before {
+  content: "\e074";
+}
+.glyphicon-forward:before {
+  content: "\e075";
+}
+.glyphicon-fast-forward:before {
+  content: "\e076";
+}
+.glyphicon-step-forward:before {
+  content: "\e077";
+}
+.glyphicon-eject:before {
+  content: "\e078";
+}
+.glyphicon-chevron-left:before {
+  content: "\e079";
+}
+.glyphicon-chevron-right:before {
+  content: "\e080";
+}
+.glyphicon-plus-sign:before {
+  content: "\e081";
+}
+.glyphicon-minus-sign:before {
+  content: "\e082";
+}
+.glyphicon-remove-sign:before {
+  content: "\e083";
+}
+.glyphicon-ok-sign:before {
+  content: "\e084";
+}
+.glyphicon-question-sign:before {
+  content: "\e085";
+}
+.glyphicon-info-sign:before {
+  content: "\e086";
+}
+.glyphicon-screenshot:before {
+  content: "\e087";
+}
+.glyphicon-remove-circle:before {
+  content: "\e088";
+}
+.glyphicon-ok-circle:before {
+  content: "\e089";
+}
+.glyphicon-ban-circle:before {
+  content: "\e090";
+}
+.glyphicon-arrow-left:before {
+  content: "\e091";
+}
+.glyphicon-arrow-right:before {
+  content: "\e092";
+}
+.glyphicon-arrow-up:before {
+  content: "\e093";
+}
+.glyphicon-arrow-down:before {
+  content: "\e094";
+}
+.glyphicon-share-alt:before {
+  content: "\e095";
+}
+.glyphicon-resize-full:before {
+  content: "\e096";
+}
+.glyphicon-resize-small:before {
+  content: "\e097";
+}
+.glyphicon-exclamation-sign:before {
+  content: "\e101";
+}
+.glyphicon-gift:before {
+  content: "\e102";
+}
+.glyphicon-leaf:before {
+  content: "\e103";
+}
+.glyphicon-fire:before {
+  content: "\e104";
+}
+.glyphicon-eye-open:before {
+  content: "\e105";
+}
+.glyphicon-eye-close:before {
+  content: "\e106";
+}
+.glyphicon-warning-sign:before {
+  content: "\e107";
+}
+.glyphicon-plane:before {
+  content: "\e108";
+}
+.glyphicon-calendar:before {
+  content: "\e109";
+}
+.glyphicon-random:before {
+  content: "\e110";
+}
+.glyphicon-comment:before {
+  content: "\e111";
+}
+.glyphicon-magnet:before {
+  content: "\e112";
+}
+.glyphicon-chevron-up:before {
+  content: "\e113";
+}
+.glyphicon-chevron-down:before {
+  content: "\e114";
+}
+.glyphicon-retweet:before {
+  content: "\e115";
+}
+.glyphicon-shopping-cart:before {
+  content: "\e116";
+}
+.glyphicon-folder-close:before {
+  content: "\e117";
+}
+.glyphicon-folder-open:before {
+  content: "\e118";
+}
+.glyphicon-resize-vertical:before {
+  content: "\e119";
+}
+.glyphicon-resize-horizontal:before {
+  content: "\e120";
+}
+.glyphicon-hdd:before {
+  content: "\e121";
+}
+.glyphicon-bullhorn:before {
+  content: "\e122";
+}
+.glyphicon-bell:before {
+  content: "\e123";
+}
+.glyphicon-certificate:before {
+  content: "\e124";
+}
+.glyphicon-thumbs-up:before {
+  content: "\e125";
+}
+.glyphicon-thumbs-down:before {
+  content: "\e126";
+}
+.glyphicon-hand-right:before {
+  content: "\e127";
+}
+.glyphicon-hand-left:before {
+  content: "\e128";
+}
+.glyphicon-hand-up:before {
+  content: "\e129";
+}
+.glyphicon-hand-down:before {
+  content: "\e130";
+}
+.glyphicon-circle-arrow-right:before {
+  content: "\e131";
+}
+.glyphicon-circle-arrow-left:before {
+  content: "\e132";
+}
+.glyphicon-circle-arrow-up:before {
+  content: "\e133";
+}
+.glyphicon-circle-arrow-down:before {
+  content: "\e134";
+}
+.glyphicon-globe:before {
+  content: "\e135";
+}
+.glyphicon-wrench:before {
+  content: "\e136";
+}
+.glyphicon-tasks:before {
+  content: "\e137";
+}
+.glyphicon-filter:before {
+  content: "\e138";
+}
+.glyphicon-briefcase:before {
+  content: "\e139";
+}
+.glyphicon-fullscreen:before {
+  content: "\e140";
+}
+.glyphicon-dashboard:before {
+  content: "\e141";
+}
+.glyphicon-paperclip:before {
+  content: "\e142";
+}
+.glyphicon-heart-empty:before {
+  content: "\e143";
+}
+.glyphicon-link:before {
+  content: "\e144";
+}
+.glyphicon-phone:before {
+  content: "\e145";
+}
+.glyphicon-pushpin:before {
+  content: "\e146";
+}
+.glyphicon-usd:before {
+  content: "\e148";
+}
+.glyphicon-gbp:before {
+  content: "\e149";
+}
+.glyphicon-sort:before {
+  content: "\e150";
+}
+.glyphicon-sort-by-alphabet:before {
+  content: "\e151";
+}
+.glyphicon-sort-by-alphabet-alt:before {
+  content: "\e152";
+}
+.glyphicon-sort-by-order:before {
+  content: "\e153";
+}
+.glyphicon-sort-by-order-alt:before {
+  content: "\e154";
+}
+.glyphicon-sort-by-attributes:before {
+  content: "\e155";
+}
+.glyphicon-sort-by-attributes-alt:before {
+  content: "\e156";
+}
+.glyphicon-unchecked:before {
+  content: "\e157";
+}
+.glyphicon-expand:before {
+  content: "\e158";
+}
+.glyphicon-collapse-down:before {
+  content: "\e159";
+}
+.glyphicon-collapse-up:before {
+  content: "\e160";
+}
+.glyphicon-log-in:before {
+  content: "\e161";
+}
+.glyphicon-flash:before {
+  content: "\e162";
+}
+.glyphicon-log-out:before {
+  content: "\e163";
+}
+.glyphicon-new-window:before {
+  content: "\e164";
+}
+.glyphicon-record:before {
+  content: "\e165";
+}
+.glyphicon-save:before {
+  content: "\e166";
+}
+.glyphicon-open:before {
+  content: "\e167";
+}
+.glyphicon-saved:before {
+  content: "\e168";
+}
+.glyphicon-import:before {
+  content: "\e169";
+}
+.glyphicon-export:before {
+  content: "\e170";
+}
+.glyphicon-send:before {
+  content: "\e171";
+}
+.glyphicon-floppy-disk:before {
+  content: "\e172";
+}
+.glyphicon-floppy-saved:before {
+  content: "\e173";
+}
+.glyphicon-floppy-remove:before {
+  content: "\e174";
+}
+.glyphicon-floppy-save:before {
+  content: "\e175";
+}
+.glyphicon-floppy-open:before {
+  content: "\e176";
+}
+.glyphicon-credit-card:before {
+  content: "\e177";
+}
+.glyphicon-transfer:before {
+  content: "\e178";
+}
+.glyphicon-cutlery:before {
+  content: "\e179";
+}
+.glyphicon-header:before {
+  content: "\e180";
+}
+.glyphicon-compressed:before {
+  content: "\e181";
+}
+.glyphicon-earphone:before {
+  content: "\e182";
+}
+.glyphicon-phone-alt:before {
+  content: "\e183";
+}
+.glyphicon-tower:before {
+  content: "\e184";
+}
+.glyphicon-stats:before {
+  content: "\e185";
+}
+.glyphicon-sd-video:before {
+  content: "\e186";
+}
+.glyphicon-hd-video:before {
+  content: "\e187";
+}
+.glyphicon-subtitles:before {
+  content: "\e188";
+}
+.glyphicon-sound-stereo:before {
+  content: "\e189";
+}
+.glyphicon-sound-dolby:before {
+  content: "\e190";
+}
+.glyphicon-sound-5-1:before {
+  content: "\e191";
+}
+.glyphicon-sound-6-1:before {
+  content: "\e192";
+}
+.glyphicon-sound-7-1:before {
+  content: "\e193";
+}
+.glyphicon-copyright-mark:before {
+  content: "\e194";
+}
+.glyphicon-registration-mark:before {
+  content: "\e195";
+}
+.glyphicon-cloud-download:before {
+  content: "\e197";
+}
+.glyphicon-cloud-upload:before {
+  content: "\e198";
+}
+.glyphicon-tree-conifer:before {
+  content: "\e199";
+}
+.glyphicon-tree-deciduous:before {
+  content: "\e200";
+}
+.caret {
+  display: inline-block;
+  width: 0;
+  height: 0;
+  margin-left: 2px;
+  vertical-align: middle;
+  border-top: 4px solid;
+  border-right: 4px solid transparent;
+  border-left: 4px solid transparent;
+}
+.dropdown {
+  position: relative;
+}
+.dropdown-toggle:focus {
+  outline: 0;
+}
+.dropdown-menu {
+  position: absolute;
+  top: 100%;
+  left: 0;
+  z-index: 1000;
+  display: none;
+  float: left;
+  min-width: 160px;
+  padding: 5px 0;
+  margin: 2px 0 0;
+  font-size: 14px;
+  list-style: none;
+  background-color: #fff;
+  background-clip: padding-box;
+  border: 1px solid #ccc;
+  border: 1px solid rgba(0, 0, 0, .15);
+  border-radius: 4px;
+  -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, .175);
+          box-shadow: 0 6px 12px rgba(0, 0, 0, .175);
+}
+.dropdown-menu.pull-right {
+  right: 0;
+  left: auto;
+}
+.dropdown-menu .divider {
+  height: 1px;
+  margin: 9px 0;
+  overflow: hidden;
+  background-color: #e5e5e5;
+}
+.dropdown-menu > li > a {
+  display: block;
+  padding: 3px 20px;
+  clear: both;
+  font-weight: normal;
+  line-height: 1.42857143;
+  color: #333;
+  white-space: nowrap;
+}
+.dropdown-menu > li > a:hover,
+.dropdown-menu > li > a:focus {
+  color: #262626;
+  text-decoration: none;
+  background-color: #f5f5f5;
+}
+.dropdown-menu > .active > a,
+.dropdown-menu > .active > a:hover,
+.dropdown-menu > .active > a:focus {
+  color: #fff;
+  text-decoration: none;
+  background-color: #428bca;
+  outline: 0;
+}
+.dropdown-menu > .disabled > a,
+.dropdown-menu > .disabled > a:hover,
+.dropdown-menu > .disabled > a:focus {
+  color: #999;
+}
+.dropdown-menu > .disabled > a:hover,
+.dropdown-menu > .disabled > a:focus {
+  text-decoration: none;
+  cursor: not-allowed;
+  background-color: transparent;
+  background-image: none;
+  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
+}
+.open > .dropdown-menu {
+  display: block;
+}
+.open > a {
+  outline: 0;
+}
+.dropdown-menu-right {
+  right: 0;
+  left: auto;
+}
+.dropdown-menu-left {
+  right: auto;
+  left: 0;
+}
+.dropdown-header {
+  display: block;
+  padding: 3px 20px;
+  font-size: 12px;
+  line-height: 1.42857143;
+  color: #999;
+}
+.dropdown-backdrop {
+  position: fixed;
+  top: 0;
+  right: 0;
+  bottom: 0;
+  left: 0;
+  z-index: 990;
+}
+.pull-right > .dropdown-menu {
+  right: 0;
+  left: auto;
+}
+.dropup .caret,
+.navbar-fixed-bottom .dropdown .caret {
+  content: "";
+  border-top: 0;
+  border-bottom: 4px solid;
+}
+.dropup .dropdown-menu,
+.navbar-fixed-bottom .dropdown .dropdown-menu {
+  top: auto;
+  bottom: 100%;
+  margin-bottom: 1px;
+}
+@media (min-width: 768px) {
+  .navbar-right .dropdown-menu {
+    right: 0;
+    left: auto;
+  }
+  .navbar-right .dropdown-menu-left {
+    right: auto;
+    left: 0;
+  }
+}
+.btn-group,
+.btn-group-vertical {
+  position: relative;
+  display: inline-block;
+  vertical-align: middle;
+}
+.btn-group > .btn,
+.btn-group-vertical > .btn {
+  position: relative;
+  float: left;
+}
+.btn-group > .btn:hover,
+.btn-group-vertical > .btn:hover,
+.btn-group > .btn:focus,
+.btn-group-vertical > .btn:focus,
+.btn-group > .btn:active,
+.btn-group-vertical > .btn:active,
+.btn-group > .btn.active,
+.btn-group-vertical > .btn.active {
+  z-index: 2;
+}
+.btn-group > .btn:focus,
+.btn-group-vertical > .btn:focus {
+  outline: none;
+}
+.btn-group .btn + .btn,
+.btn-group .btn + .btn-group,
+.btn-group .btn-group + .btn,
+.btn-group .btn-group + .btn-group {
+  margin-left: -1px;
+}
+.btn-toolbar {
+  margin-left: -5px;
+}
+.btn-toolbar .btn-group,
+.btn-toolbar .input-group {
+  float: left;
+}
+.btn-toolbar > .btn,
+.btn-toolbar > .btn-group,
+.btn-toolbar > .input-group {
+  margin-left: 5px;
+}
+.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) {
+  border-radius: 0;
+}
+.btn-group > .btn:first-child {
+  margin-left: 0;
+}
+.btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) {
+  border-top-right-radius: 0;
+  border-bottom-right-radius: 0;
+}
+.btn-group > .btn:last-child:not(:first-child),
+.btn-group > .dropdown-toggle:not(:first-child) {
+  border-top-left-radius: 0;
+  border-bottom-left-radius: 0;
+}
+.btn-group > .btn-group {
+  float: left;
+}
+.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn {
+  border-radius: 0;
+}
+.btn-group > .btn-group:first-child > .btn:last-child,
+.btn-group > .btn-group:first-child > .dropdown-toggle {
+  border-top-right-radius: 0;
+  border-bottom-right-radius: 0;
+}
+.btn-group > .btn-group:last-child > .btn:first-child {
+  border-top-left-radius: 0;
+  border-bottom-left-radius: 0;
+}
+.btn-group .dropdown-toggle:active,
+.btn-group.open .dropdown-toggle {
+  outline: 0;
+}
+.btn-group > .btn + .dropdown-toggle {
+  padding-right: 8px;
+  padding-left: 8px;
+}
+.btn-group > .btn-lg + .dropdown-toggle {
+  padding-right: 12px;
+  padding-left: 12px;
+}
+.btn-group.open .dropdown-toggle {
+  -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
+          box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
+}
+.btn-group.open .dropdown-toggle.btn-link {
+  -webkit-box-shadow: none;
+          box-shadow: none;
+}
+.btn .caret {
+  margin-left: 0;
+}
+.btn-lg .caret {
+  border-width: 5px 5px 0;
+  border-bottom-width: 0;
+}
+.dropup .btn-lg .caret {
+  border-width: 0 5px 5px;
+}
+.btn-group-vertical > .btn,
+.btn-group-vertical > .btn-group,
+.btn-group-vertical > .btn-group > .btn {
+  display: block;
+  float: none;
+  width: 100%;
+  max-width: 100%;
+}
+.btn-group-vertical > .btn-group > .btn {
+  float: none;
+}
+.btn-group-vertical > .btn + .btn,
+.btn-group-vertical > .btn + .btn-group,
+.btn-group-vertical > .btn-group + .btn,
+.btn-group-vertical > .btn-group + .btn-group {
+  margin-top: -1px;
+  margin-left: 0;
+}
+.btn-group-vertical > .btn:not(:first-child):not(:last-child) {
+  border-radius: 0;
+}
+.btn-group-vertical > .btn:first-child:not(:last-child) {
+  border-top-right-radius: 4px;
+  border-bottom-right-radius: 0;
+  border-bottom-left-radius: 0;
+}
+.btn-group-vertical > .btn:last-child:not(:first-child) {
+  border-top-left-radius: 0;
+  border-top-right-radius: 0;
+  border-bottom-left-radius: 4px;
+}
+.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn {
+  border-radius: 0;
+}
+.btn-group-vertical > .btn-group:first-child:not(:last-child) > .btn:last-child,
+.btn-group-vertical > .btn-group:first-child:not(:last-child) > .dropdown-toggle {
+  border-bottom-right-radius: 0;
+  border-bottom-left-radius: 0;
+}
+.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child {
+  border-top-left-radius: 0;
+  border-top-right-radius: 0;
+}
+.btn-group-justified {
+  display: table;
+  width: 100%;
+  table-layout: fixed;
+  border-collapse: separate;
+}
+.btn-group-justified > .btn,
+.btn-group-justified > .btn-group {
+  display: table-cell;
+  float: none;
+  width: 1%;
+}
+.btn-group-justified > .btn-group .btn {
+  width: 100%;
+}
+[data-toggle="buttons"] > .btn > input[type="radio"],
+[data-toggle="buttons"] > .btn > input[type="checkbox"] {
+  display: none;
+}
+.input-group {
+  position: relative;
+  display: table;
+  border-collapse: separate;
+}
+.input-group[class*="col-"] {
+  float: none;
+  padding-right: 0;
+  padding-left: 0;
+}
+.input-group .form-control {
+  position: relative;
+  z-index: 2;
+  float: left;
+  width: 100%;
+  margin-bottom: 0;
+}
+.input-group-lg > .form-control,
+.input-group-lg > .input-group-addon,
+.input-group-lg > .input-group-btn > .btn {
+  height: 46px;
+  padding: 10px 16px;
+  font-size: 18px;
+  line-height: 1.33;
+  border-radius: 6px;
+}
+select.input-group-lg > .form-control,
+select.input-group-lg > .input-group-addon,
+select.input-group-lg > .input-group-btn > .btn {
+  height: 46px;
+  line-height: 46px;
+}
+textarea.input-group-lg > .form-control,
+textarea.input-group-lg > .input-group-addon,
+textarea.input-group-lg > .input-group-btn > .btn,
+select[multiple].input-group-lg > .form-control,
+select[multiple].input-group-lg > .input-group-addon,
+select[multiple].input-group-lg > .input-group-btn > .btn {
+  height: auto;
+}
+.input-group-sm > .form-control,
+.input-group-sm > .input-group-addon,
+.input-group-sm > .input-group-btn > .btn {
+  height: 30px;
+  padding: 5px 10px;
+  font-size: 12px;
+  line-height: 1.5;
+  border-radius: 3px;
+}
+select.input-group-sm > .form-control,
+select.input-group-sm > .input-group-addon,
+select.input-group-sm > .input-group-btn > .btn {
+  height: 30px;
+  line-height: 30px;
+}
+textarea.input-group-sm > .form-control,
+textarea.input-group-sm > .input-group-addon,
+textarea.input-group-sm > .input-group-btn > .btn,
+select[multiple].input-group-sm > .form-control,
+select[multiple].input-group-sm > .input-group-addon,
+select[multiple].input-group-sm > .input-group-btn > .btn {
+  height: auto;
+}
+.input-group-addon,
+.input-group-btn,
+.input-group .form-control {
+  display: table-cell;
+}
+.input-group-addon:not(:first-child):not(:last-child),
+.input-group-btn:not(:first-child):not(:last-child),
+.input-group .form-control:not(:first-child):not(:last-child) {
+  border-radius: 0;
+}
+.input-group-addon,
+.input-group-btn {
+  width: 1%;
+  white-space: nowrap;
+  vertical-align: middle;
+}
+.input-group-addon {
+  padding: 6px 12px;
+  font-size: 14px;
+  font-weight: normal;
+  line-height: 1;
+  color: #555;
+  text-align: center;
+  background-color: #eee;
+  border: 1px solid #ccc;
+  border-radius: 4px;
+}
+.input-group-addon.input-sm {
+  padding: 5px 10px;
+  font-size: 12px;
+  border-radius: 3px;
+}
+.input-group-addon.input-lg {
+  padding: 10px 16px;
+  font-size: 18px;
+  border-radius: 6px;
+}
+.input-group-addon input[type="radio"],
+.input-group-addon input[type="checkbox"] {
+  margin-top: 0;
+}
+.input-group .form-control:first-child,
+.input-group-addon:first-child,
+.input-group-btn:first-child > .btn,
+.input-group-btn:first-child > .btn-group > .btn,
+.input-group-btn:first-child > .dropdown-toggle,
+.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle),
+.input-group-btn:last-child > .btn-group:not(:last-child) > .btn {
+  border-top-right-radius: 0;
+  border-bottom-right-radius: 0;
+}
+.input-group-addon:first-child {
+  border-right: 0;
+}
+.input-group .form-control:last-child,
+.input-group-addon:last-child,
+.input-group-btn:last-child > .btn,
+.input-group-btn:last-child > .btn-group > .btn,
+.input-group-btn:last-child > .dropdown-toggle,
+.input-group-btn:first-child > .btn:not(:first-child),
+.input-group-btn:first-child > .btn-group:not(:first-child) > .btn {
+  border-top-left-radius: 0;
+  border-bottom-left-radius: 0;
+}
+.input-group-addon:last-child {
+  border-left: 0;
+}
+.input-group-btn {
+  position: relative;
+  font-size: 0;
+  white-space: nowrap;
+}
+.input-group-btn > .btn {
+  position: relative;
+}
+.input-group-btn > .btn + .btn {
+  margin-left: -1px;
+}
+.input-group-btn > .btn:hover,
+.input-group-btn > .btn:focus,
+.input-group-btn > .btn:active {
+  z-index: 2;
+}
+.input-group-btn:first-child > .btn,
+.input-group-btn:first-child > .btn-group {
+  margin-right: -1px;
+}
+.input-group-btn:last-child > .btn,
+.input-group-btn:last-child > .btn-group {
+  margin-left: -1px;
+}
+.nav {
+  padding-left: 0;
+  margin-bottom: 0;
+  list-style: none;
+}
+.nav > li {
+  position: relative;
+  display: block;
+}
+.nav > li > a {
+  position: relative;
+  display: block;
+  padding: 10px 15px;
+}
+.nav > li > a:hover,
+.nav > li > a:focus {
+  text-decoration: none;
+  background-color: #eee;
+}
+.nav > li.disabled > a {
+  color: #999;
+}
+.nav > li.disabled > a:hover,
+.nav > li.disabled > a:focus {
+  color: #999;
+  text-decoration: none;
+  cursor: not-allowed;
+  background-color: transparent;
+}
+.nav .open > a,
+.nav .open > a:hover,
+.nav .open > a:focus {
+  background-color: #eee;
+  border-color: #428bca;
+}
+.nav .nav-divider {
+  height: 1px;
+  margin: 9px 0;
+  overflow: hidden;
+  background-color: #e5e5e5;
+}
+.nav > li > a > img {
+  max-width: none;
+}
+.nav-tabs {
+  border-bottom: 1px solid #ddd;
+}
+.nav-tabs > li {
+  float: left;
+  margin-bottom: -1px;
+}
+.nav-tabs > li > a {
+  margin-right: 2px;
+  line-height: 1.42857143;
+  border: 1px solid transparent;
+  border-radius: 4px 4px 0 0;
+}
+.nav-tabs > li > a:hover {
+  border-color: #eee #eee #ddd;
+}
+.nav-tabs > li.active > a,
+.nav-tabs > li.active > a:hover,
+.nav-tabs > li.active > a:focus {
+  color: #555;
+  cursor: default;
+  background-color: #fff;
+  border: 1px solid #ddd;
+  border-bottom-color: transparent;
+}
+.nav-tabs.nav-justified {
+  width: 100%;
+  border-bottom: 0;
+}
+.nav-tabs.nav-justified > li {
+  float: none;
+}
+.nav-tabs.nav-justified > li > a {
+  margin-bottom: 5px;
+  text-align: center;
+}
+.nav-tabs.nav-justified > .dropdown .dropdown-menu {
+  top: auto;
+  left: auto;
+}
+@media (min-width: 768px) {
+  .nav-tabs.nav-justified > li {
+    display: table-cell;
+    width: 1%;
+  }
+  .nav-tabs.nav-justified > li > a {
+    margin-bottom: 0;
+  }
+}
+.nav-tabs.nav-justified > li > a {
+  margin-right: 0;
+  border-radius: 4px;
+}
+.nav-tabs.nav-justified > .active > a,
+.nav-tabs.nav-justified > .active > a:hover,
+.nav-tabs.nav-justified > .active > a:focus {
+  border: 1px solid #ddd;
+}
+@media (min-width: 768px) {
+  .nav-tabs.nav-justified > li > a {
+    border-bottom: 1px solid #ddd;
+    border-radius: 4px 4px 0 0;
+  }
+  .nav-tabs.nav-justified > .active > a,
+  .nav-tabs.nav-justified > .active > a:hover,
+  .nav-tabs.nav-justified > .active > a:focus {
+    border-bottom-color: #fff;
+  }
+}
+.nav-pills > li {
+  float: left;
+}
+.nav-pills > li > a {
+  border-radius: 4px;
+}
+.nav-pills > li + li {
+  margin-left: 2px;
+}
+.nav-pills > li.active > a,
+.nav-pills > li.active > a:hover,
+.nav-pills > li.active > a:focus {
+  color: #fff;
+  background-color: #428bca;
+}
+.nav-stacked > li {
+  float: none;
+}
+.nav-stacked > li + li {
+  margin-top: 2px;
+  margin-left: 0;
+}
+.nav-justified {
+  width: 100%;
+}
+.nav-justified > li {
+  float: none;
+}
+.nav-justified > li > a {
+  margin-bottom: 5px;
+  text-align: center;
+}
+.nav-justified > .dropdown .dropdown-menu {
+  top: auto;
+  left: auto;
+}
+@media (min-width: 768px) {
+  .nav-justified > li {
+    display: table-cell;
+    width: 1%;
+  }
+  .nav-justified > li > a {
+    margin-bottom: 0;
+  }
+}
+.nav-tabs-justified {
+  border-bottom: 0;
+}
+.nav-tabs-justified > li > a {
+  margin-right: 0;
+  border-radius: 4px;
+}
+.nav-tabs-justified > .active > a,
+.nav-tabs-justified > .active > a:hover,
+.nav-tabs-justified > .active > a:focus {
+  border: 1px solid #ddd;
+}
+@media (min-width: 768px) {
+  .nav-tabs-justified > li > a {
+    border-bottom: 1px solid #ddd;
+    border-radius: 4px 4px 0 0;
+  }
+  .nav-tabs-justified > .active > a,
+  .nav-tabs-justified > .active > a:hover,
+  .nav-tabs-justified > .active > a:focus {
+    border-bottom-color: #fff;
+  }
+}
+.tab-content > .tab-pane {
+  display: none;
+}
+.tab-content > .active {
+  display: block;
+}
+.nav-tabs .dropdown-menu {
+  margin-top: -1px;
+  border-top-left-radius: 0;
+  border-top-right-radius: 0;
+}
+.navbar {
+  position: relative;
+  min-height: 50px;
+  margin-bottom: 20px;
+  border: 1px solid transparent;
+}
+@media (min-width: 768px) {
+  .navbar {
+    border-radius: 4px;
+  }
+}
+@media (min-width: 768px) {
+  .navbar-header {
+    float: left;
+  }
+}
+.navbar-collapse {
+  max-height: 340px;
+  padding-right: 15px;
+  padding-left: 15px;
+  overflow-x: visible;
+  -webkit-overflow-scrolling: touch;
+  border-top: 1px solid transparent;
+  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1);
+}
+.navbar-collapse.in {
+  overflow-y: auto;
+}
+@media (min-width: 768px) {
+  .navbar-collapse {
+    width: auto;
+    border-top: 0;
+    box-shadow: none;
+  }
+  .navbar-collapse.collapse {
+    display: block !important;
+    height: auto !important;
+    padding-bottom: 0;
+    overflow: visible !important;
+  }
+  .navbar-collapse.in {
+    overflow-y: visible;
+  }
+  .navbar-fixed-top .navbar-collapse,
+  .navbar-static-top .navbar-collapse,
+  .navbar-fixed-bottom .navbar-collapse {
+    padding-right: 0;
+    padding-left: 0;
+  }
+}
+.container > .navbar-header,
+.container-fluid > .navbar-header,
+.container > .navbar-collapse,
+.container-fluid > .navbar-collapse {
+  margin-right: -15px;
+  margin-left: -15px;
+}
+@media (min-width: 768px) {
+  .container > .navbar-header,
+  .container-fluid > .navbar-header,
+  .container > .navbar-collapse,
+  .container-fluid > .navbar-collapse {
+    margin-right: 0;
+    margin-left: 0;
+  }
+}
+.navbar-static-top {
+  z-index: 1000;
+  border-width: 0 0 1px;
+}
+@media (min-width: 768px) {
+  .navbar-static-top {
+    border-radius: 0;
+  }
+}
+.navbar-fixed-top,
+.navbar-fixed-bottom {
+  position: fixed;
+  right: 0;
+  left: 0;
+  z-index: 1030;
+}
+@media (min-width: 768px) {
+  .navbar-fixed-top,
+  .navbar-fixed-bottom {
+    border-radius: 0;
+  }
+}
+.navbar-fixed-top {
+  top: 0;
+  border-width: 0 0 1px;
+}
+.navbar-fixed-bottom {
+  bottom: 0;
+  margin-bottom: 0;
+  border-width: 1px 0 0;
+}
+.navbar-brand {
+  float: left;
+  height: 50px;
+  padding: 15px 15px;
+  font-size: 18px;
+  line-height: 20px;
+}
+.navbar-brand:hover,
+.navbar-brand:focus {
+  text-decoration: none;
+}
+@media (min-width: 768px) {
+  .navbar > .container .navbar-brand,
+  .navbar > .container-fluid .navbar-brand {
+    margin-left: -15px;
+  }
+}
+.navbar-toggle {
+  position: relative;
+  float: right;
+  padding: 9px 10px;
+  margin-top: 8px;
+  margin-right: 15px;
+  margin-bottom: 8px;
+  background-color: transparent;
+  background-image: none;
+  border: 1px solid transparent;
+  border-radius: 4px;
+}
+.navbar-toggle:focus {
+  outline: none;
+}
+.navbar-toggle .icon-bar {
+  display: block;
+  width: 22px;
+  height: 2px;
+  border-radius: 1px;
+}
+.navbar-toggle .icon-bar + .icon-bar {
+  margin-top: 4px;
+}
+@media (min-width: 768px) {
+  .navbar-toggle {
+    display: none;
+  }
+}
+.navbar-nav {
+  margin: 7.5px -15px;
+}
+.navbar-nav > li > a {
+  padding-top: 10px;
+  padding-bottom: 10px;
+  line-height: 20px;
+}
+@media (max-width: 767px) {
+  .navbar-nav .open .dropdown-menu {
+    position: static;
+    float: none;
+    width: auto;
+    margin-top: 0;
+    background-color: transparent;
+    border: 0;
+    box-shadow: none;
+  }
+  .navbar-nav .open .dropdown-menu > li > a,
+  .navbar-nav .open .dropdown-menu .dropdown-header {
+    padding: 5px 15px 5px 25px;
+  }
+  .navbar-nav .open .dropdown-menu > li > a {
+    line-height: 20px;
+  }
+  .navbar-nav .open .dropdown-menu > li > a:hover,
+  .navbar-nav .open .dropdown-menu > li > a:focus {
+    background-image: none;
+  }
+}
+@media (min-width: 768px) {
+  .navbar-nav {
+    float: left;
+    margin: 0;
+  }
+  .navbar-nav > li {
+    float: left;
+  }
+  .navbar-nav > li > a {
+    padding-top: 15px;
+    padding-bottom: 15px;
+  }
+  .navbar-nav.navbar-right:last-child {
+    margin-right: -15px;
+  }
+}
+@media (min-width: 768px) {
+  .navbar-left {
+    float: left !important;
+  }
+  .navbar-right {
+    float: right !important;
+  }
+}
+.navbar-form {
+  padding: 10px 15px;
+  margin-top: 8px;
+  margin-right: -15px;
+  margin-bottom: 8px;
+  margin-left: -15px;
+  border-top: 1px solid transparent;
+  border-bottom: 1px solid transparent;
+  -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1);
+          box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1);
+}
+@media (min-width: 768px) {
+  .navbar-form .form-group {
+    display: inline-block;
+    margin-bottom: 0;
+    vertical-align: middle;
+  }
+  .navbar-form .form-control {
+    display: inline-block;
+    width: auto;
+    vertical-align: middle;
+  }
+  .navbar-form .input-group > .form-control {
+    width: 100%;
+  }
+  .navbar-form .control-label {
+    margin-bottom: 0;
+    vertical-align: middle;
+  }
+  .navbar-form .radio,
+  .navbar-form .checkbox {
+    display: inline-block;
+    padding-left: 0;
+    margin-top: 0;
+    margin-bottom: 0;
+    vertical-align: middle;
+  }
+  .navbar-form .radio input[type="radio"],
+  .navbar-form .checkbox input[type="checkbox"] {
+    float: none;
+    margin-left: 0;
+  }
+  .navbar-form .has-feedback .form-control-feedback {
+    top: 0;
+  }
+}
+@media (max-width: 767px) {
+  .navbar-form .form-group {
+    margin-bottom: 5px;
+  }
+}
+@media (min-width: 768px) {
+  .navbar-form {
+    width: auto;
+    padding-top: 0;
+    padding-bottom: 0;
+    margin-right: 0;
+    margin-left: 0;
+    border: 0;
+    -webkit-box-shadow: none;
+            box-shadow: none;
+  }
+  .navbar-form.navbar-right:last-child {
+    margin-right: -15px;
+  }
+}
+.navbar-nav > li > .dropdown-menu {
+  margin-top: 0;
+  border-top-left-radius: 0;
+  border-top-right-radius: 0;
+}
+.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu {
+  border-bottom-right-radius: 0;
+  border-bottom-left-radius: 0;
+}
+.navbar-btn {
+  margin-top: 8px;
+  margin-bottom: 8px;
+}
+.navbar-btn.btn-sm {
+  margin-top: 10px;
+  margin-bottom: 10px;
+}
+.navbar-btn.btn-xs {
+  margin-top: 14px;
+  margin-bottom: 14px;
+}
+.navbar-text {
+  margin-top: 15px;
+  margin-bottom: 15px;
+}
+@media (min-width: 768px) {
+  .navbar-text {
+    float: left;
+    margin-right: 15px;
+    margin-left: 15px;
+  }
+  .navbar-text.navbar-right:last-child {
+    margin-right: 0;
+  }
+}
+.navbar-default {
+  background-color: #f8f8f8;
+  border-color: #e7e7e7;
+}
+.navbar-default .navbar-brand {
+  color: #777;
+}
+.navbar-default .navbar-brand:hover,
+.navbar-default .navbar-brand:focus {
+  color: #5e5e5e;
+  background-color: transparent;
+}
+.navbar-default .navbar-text {
+  color: #777;
+}
+.navbar-default .navbar-nav > li > a {
+  color: #777;
+}
+.navbar-default .navbar-nav > li > a:hover,
+.navbar-default .navbar-nav > li > a:focus {
+  color: #333;
+  background-color: transparent;
+}
+.navbar-default .navbar-nav > .active > a,
+.navbar-default .navbar-nav > .active > a:hover,
+.navbar-default .navbar-nav > .active > a:focus {
+  color: #555;
+  background-color: #e7e7e7;
+}
+.navbar-default .navbar-nav > .disabled > a,
+.navbar-default .navbar-nav > .disabled > a:hover,
+.navbar-default .navbar-nav > .disabled > a:focus {
+  color: #ccc;
+  background-color: transparent;
+}
+.navbar-default .navbar-toggle {
+  border-color: #ddd;
+}
+.navbar-default .navbar-toggle:hover,
+.navbar-default .navbar-toggle:focus {
+  background-color: #ddd;
+}
+.navbar-default .navbar-toggle .icon-bar {
+  background-color: #888;
+}
+.navbar-default .navbar-collapse,
+.navbar-default .navbar-form {
+  border-color: #e7e7e7;
+}
+.navbar-default .navbar-nav > .open > a,
+.navbar-default .navbar-nav > .open > a:hover,
+.navbar-default .navbar-nav > .open > a:focus {
+  color: #555;
+  background-color: #e7e7e7;
+}
+@media (max-width: 767px) {
+  .navbar-default .navbar-nav .open .dropdown-menu > li > a {
+    color: #777;
+  }
+  .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover,
+  .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus {
+    color: #333;
+    background-color: transparent;
+  }
+  .navbar-default .navbar-nav .open .dropdown-menu > .active > a,
+  .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover,
+  .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus {
+    color: #555;
+    background-color: #e7e7e7;
+  }
+  .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a,
+  .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover,
+  .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus {
+    color: #ccc;
+    background-color: transparent;
+  }
+}
+.navbar-default .navbar-link {
+  color: #777;
+}
+.navbar-default .navbar-link:hover {
+  color: #333;
+}
+.navbar-inverse {
+  background-color: #222;
+  border-color: #080808;
+}
+.navbar-inverse .navbar-brand {
+  color: #999;
+}
+.navbar-inverse .navbar-brand:hover,
+.navbar-inverse .navbar-brand:focus {
+  color: #fff;
+  background-color: transparent;
+}
+.navbar-inverse .navbar-text {
+  color: #999;
+}
+.navbar-inverse .navbar-nav > li > a {
+  color: #999;
+}
+.navbar-inverse .navbar-nav > li > a:hover,
+.navbar-inverse .navbar-nav > li > a:focus {
+  color: #fff;
+  background-color: transparent;
+}
+.navbar-inverse .navbar-nav > .active > a,
+.navbar-inverse .navbar-nav > .active > a:hover,
+.navbar-inverse .navbar-nav > .active > a:focus {
+  color: #fff;
+  background-color: #080808;
+}
+.navbar-inverse .navbar-nav > .disabled > a,
+.navbar-inverse .navbar-nav > .disabled > a:hover,
+.navbar-inverse .navbar-nav > .disabled > a:focus {
+  color: #444;
+  background-color: transparent;
+}
+.navbar-inverse .navbar-toggle {
+  border-color: #333;
+}
+.navbar-inverse .navbar-toggle:hover,
+.navbar-inverse .navbar-toggle:focus {
+  background-color: #333;
+}
+.navbar-inverse .navbar-toggle .icon-bar {
+  background-color: #fff;
+}
+.navbar-inverse .navbar-collapse,
+.navbar-inverse .navbar-form {
+  border-color: #101010;
+}
+.navbar-inverse .navbar-nav > .open > a,
+.navbar-inverse .navbar-nav > .open > a:hover,
+.navbar-inverse .navbar-nav > .open > a:focus {
+  color: #fff;
+  background-color: #080808;
+}
+@media (max-width: 767px) {
+  .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header {
+    border-color: #080808;
+  }
+  .navbar-inverse .navbar-nav .open .dropdown-menu .divider {
+    background-color: #080808;
+  }
+  .navbar-inverse .navbar-nav .open .dropdown-menu > li > a {
+    color: #999;
+  }
+  .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover,
+  .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus {
+    color: #fff;
+    background-color: transparent;
+  }
+  .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a,
+  .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover,
+  .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus {
+    color: #fff;
+    background-color: #080808;
+  }
+  .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a,
+  .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover,
+  .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus {
+    color: #444;
+    background-color: transparent;
+  }
+}
+.navbar-inverse .navbar-link {
+  color: #999;
+}
+.navbar-inverse .navbar-link:hover {
+  color: #fff;
+}
+.breadcrumb {
+  padding: 8px 15px;
+  margin-bottom: 20px;
+  list-style: none;
+  background-color: #f5f5f5;
+  border-radius: 4px;
+}
+.breadcrumb > li {
+  display: inline-block;
+}
+.breadcrumb > li + li:before {
+  padding: 0 5px;
+  color: #ccc;
+  content: "/\00a0";
+}
+.breadcrumb > .active {
+  color: #999;
+}
+.pagination {
+  display: inline-block;
+  padding-left: 0;
+  margin: 20px 0;
+  border-radius: 4px;
+}
+.pagination > li {
+  display: inline;
+}
+.pagination > li > a,
+.pagination > li > span {
+  position: relative;
+  float: left;
+  padding: 6px 12px;
+  margin-left: -1px;
+  line-height: 1.42857143;
+  color: #428bca;
+  text-decoration: none;
+  background-color: #fff;
+  border: 1px solid #ddd;
+}
+.pagination > li:first-child > a,
+.pagination > li:first-child > span {
+  margin-left: 0;
+  border-top-left-radius: 4px;
+  border-bottom-left-radius: 4px;
+}
+.pagination > li:last-child > a,
+.pagination > li:last-child > span {
+  border-top-right-radius: 4px;
+  border-bottom-right-radius: 4px;
+}
+.pagination > li > a:hover,
+.pagination > li > span:hover,
+.pagination > li > a:focus,
+.pagination > li > span:focus {
+  color: #2a6496;
+  background-color: #eee;
+  border-color: #ddd;
+}
+.pagination > .active > a,
+.pagination > .active > span,
+.pagination > .active > a:hover,
+.pagination > .active > span:hover,
+.pagination > .active > a:focus,
+.pagination > .active > span:focus {
+  z-index: 2;
+  color: #fff;
+  cursor: default;
+  background-color: #428bca;
+  border-color: #428bca;
+}
+.pagination > .disabled > span,
+.pagination > .disabled > span:hover,
+.pagination > .disabled > span:focus,
+.pagination > .disabled > a,
+.pagination > .disabled > a:hover,
+.pagination > .disabled > a:focus {
+  color: #999;
+  cursor: not-allowed;
+  background-color: #fff;
+  border-color: #ddd;
+}
+.pagination-lg > li > a,
+.pagination-lg > li > span {
+  padding: 10px 16px;
+  font-size: 18px;
+}
+.pagination-lg > li:first-child > a,
+.pagination-lg > li:first-child > span {
+  border-top-left-radius: 6px;
+  border-bottom-left-radius: 6px;
+}
+.pagination-lg > li:last-child > a,
+.pagination-lg > li:last-child > span {
+  border-top-right-radius: 6px;
+  border-bottom-right-radius: 6px;
+}
+.pagination-sm > li > a,
+.pagination-sm > li > span {
+  padding: 5px 10px;
+  font-size: 12px;
+}
+.pagination-sm > li:first-child > a,
+.pagination-sm > li:first-child > span {
+  border-top-left-radius: 3px;
+  border-bottom-left-radius: 3px;
+}
+.pagination-sm > li:last-child > a,
+.pagination-sm > li:last-child > span {
+  border-top-right-radius: 3px;
+  border-bottom-right-radius: 3px;
+}
+.pager {
+  padding-left: 0;
+  margin: 20px 0;
+  text-align: center;
+  list-style: none;
+}
+.pager li {
+  display: inline;
+}
+.pager li > a,
+.pager li > span {
+  display: inline-block;
+  padding: 5px 14px;
+  background-color: #fff;
+  border: 1px solid #ddd;
+  border-radius: 15px;
+}
+.pager li > a:hover,
+.pager li > a:focus {
+  text-decoration: none;
+  background-color: #eee;
+}
+.pager .next > a,
+.pager .next > span {
+  float: right;
+}
+.pager .previous > a,
+.pager .previous > span {
+  float: left;
+}
+.pager .disabled > a,
+.pager .disabled > a:hover,
+.pager .disabled > a:focus,
+.pager .disabled > span {
+  color: #999;
+  cursor: not-allowed;
+  background-color: #fff;
+}
+.label {
+  display: inline;
+  padding: .2em .6em .3em;
+  font-size: 75%;
+  font-weight: bold;
+  line-height: 1;
+  color: #fff;
+  text-align: center;
+  white-space: nowrap;
+  vertical-align: baseline;
+  border-radius: .25em;
+}
+.label[href]:hover,
+.label[href]:focus {
+  color: #fff;
+  text-decoration: none;
+  cursor: pointer;
+}
+.label:empty {
+  display: none;
+}
+.btn .label {
+  position: relative;
+  top: -1px;
+}
+.label-default {
+  background-color: #999;
+}
+.label-default[href]:hover,
+.label-default[href]:focus {
+  background-color: #808080;
+}
+.label-primary {
+  background-color: #428bca;
+}
+.label-primary[href]:hover,
+.label-primary[href]:focus {
+  background-color: #3071a9;
+}
+.label-success {
+  background-color: #5cb85c;
+}
+.label-success[href]:hover,
+.label-success[href]:focus {
+  background-color: #449d44;
+}
+.label-info {
+  background-color: #5bc0de;
+}
+.label-info[href]:hover,
+.label-info[href]:focus {
+  background-color: #31b0d5;
+}
+.label-warning {
+  background-color: #f0ad4e;
+}
+.label-warning[href]:hover,
+.label-warning[href]:focus {
+  background-color: #ec971f;
+}
+.label-danger {
+  background-color: #d9534f;
+}
+.label-danger[href]:hover,
+.label-danger[href]:focus {
+  background-color: #c9302c;
+}
+.badge {
+  display: inline-block;
+  min-width: 10px;
+  padding: 3px 7px;
+  font-size: 12px;
+  font-weight: bold;
+  line-height: 1;
+  color: #fff;
+  text-align: center;
+  white-space: nowrap;
+  vertical-align: baseline;
+  background-color: #999;
+  border-radius: 10px;
+}
+.badge:empty {
+  display: none;
+}
+.btn .badge {
+  position: relative;
+  top: -1px;
+}
+.btn-xs .badge {
+  top: 0;
+  padding: 1px 5px;
+}
+a.badge:hover,
+a.badge:focus {
+  color: #fff;
+  text-decoration: none;
+  cursor: pointer;
+}
+a.list-group-item.active > .badge,
+.nav-pills > .active > a > .badge {
+  color: #428bca;
+  background-color: #fff;
+}
+.nav-pills > li > a > .badge {
+  margin-left: 3px;
+}
+.jumbotron {
+  padding: 30px;
+  margin-bottom: 30px;
+  color: inherit;
+  background-color: #eee;
+}
+.jumbotron h1,
+.jumbotron .h1 {
+  color: inherit;
+}
+.jumbotron p {
+  margin-bottom: 15px;
+  font-size: 21px;
+  font-weight: 200;
+}
+.container .jumbotron {
+  border-radius: 6px;
+}
+.jumbotron .container {
+  max-width: 100%;
+}
+@media screen and (min-width: 768px) {
+  .jumbotron {
+    padding-top: 48px;
+    padding-bottom: 48px;
+  }
+  .container .jumbotron {
+    padding-right: 60px;
+    padding-left: 60px;
+  }
+  .jumbotron h1,
+  .jumbotron .h1 {
+    font-size: 63px;
+  }
+}
+.thumbnail {
+  display: block;
+  padding: 4px;
+  margin-bottom: 20px;
+  line-height: 1.42857143;
+  background-color: #fff;
+  border: 1px solid #ddd;
+  border-radius: 4px;
+  -webkit-transition: all .2s ease-in-out;
+          transition: all .2s ease-in-out;
+}
+.thumbnail > img,
+.thumbnail a > img {
+  margin-right: auto;
+  margin-left: auto;
+}
+a.thumbnail:hover,
+a.thumbnail:focus,
+a.thumbnail.active {
+  border-color: #428bca;
+}
+.thumbnail .caption {
+  padding: 9px;
+  color: #333;
+}
+.alert {
+  padding: 15px;
+  margin-bottom: 20px;
+  border: 1px solid transparent;
+  border-radius: 4px;
+}
+.alert h4 {
+  margin-top: 0;
+  color: inherit;
+}
+.alert .alert-link {
+  font-weight: bold;
+}
+.alert > p,
+.alert > ul {
+  margin-bottom: 0;
+}
+.alert > p + p {
+  margin-top: 5px;
+}
+.alert-dismissable {
+  padding-right: 35px;
+}
+.alert-dismissable .close {
+  position: relative;
+  top: -2px;
+  right: -21px;
+  color: inherit;
+}
+.alert-success {
+  color: #3c763d;
+  background-color: #dff0d8;
+  border-color: #d6e9c6;
+}
+.alert-success hr {
+  border-top-color: #c9e2b3;
+}
+.alert-success .alert-link {
+  color: #2b542c;
+}
+.alert-info {
+  color: #31708f;
+  background-color: #d9edf7;
+  border-color: #bce8f1;
+}
+.alert-info hr {
+  border-top-color: #a6e1ec;
+}
+.alert-info .alert-link {
+  color: #245269;
+}
+.alert-warning {
+  color: #8a6d3b;
+  background-color: #fcf8e3;
+  border-color: #faebcc;
+}
+.alert-warning hr {
+  border-top-color: #f7e1b5;
+}
+.alert-warning .alert-link {
+  color: #66512c;
+}
+.alert-danger {
+  color: #a94442;
+  background-color: #f2dede;
+  border-color: #ebccd1;
+}
+.alert-danger hr {
+  border-top-color: #e4b9c0;
+}
+.alert-danger .alert-link {
+  color: #843534;
+}
+@-webkit-keyframes progress-bar-stripes {
+  from {
+    background-position: 40px 0;
+  }
+  to {
+    background-position: 0 0;
+  }
+}
+@keyframes progress-bar-stripes {
+  from {
+    background-position: 40px 0;
+  }
+  to {
+    background-position: 0 0;
+  }
+}
+.progress {
+  height: 20px;
+  margin-bottom: 20px;
+  overflow: hidden;
+  background-color: #f5f5f5;
+  border-radius: 4px;
+  -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1);
+          box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1);
+}
+.progress-bar {
+  float: left;
+  width: 0;
+  height: 100%;
+  font-size: 12px;
+  line-height: 20px;
+  color: #fff;
+  text-align: center;
+  background-color: #428bca;
+  -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15);
+          box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15);
+  -webkit-transition: width .6s ease;
+          transition: width .6s ease;
+}
+.progress-striped .progress-bar {
+  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+  background-image:         linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+  background-size: 40px 40px;
+}
+.progress.active .progress-bar {
+  -webkit-animation: progress-bar-stripes 2s linear infinite;
+          animation: progress-bar-stripes 2s linear infinite;
+}
+.progress-bar-success {
+  background-color: #5cb85c;
+}
+.progress-striped .progress-bar-success {
+  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+  background-image:         linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+}
+.progress-bar-info {
+  background-color: #5bc0de;
+}
+.progress-striped .progress-bar-info {
+  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+  background-image:         linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+}
+.progress-bar-warning {
+  background-color: #f0ad4e;
+}
+.progress-striped .progress-bar-warning {
+  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+  background-image:         linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+}
+.progress-bar-danger {
+  background-color: #d9534f;
+}
+.progress-striped .progress-bar-danger {
+  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+  background-image:         linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+}
+.media,
+.media-body {
+  overflow: hidden;
+  zoom: 1;
+}
+.media,
+.media .media {
+  margin-top: 15px;
+}
+.media:first-child {
+  margin-top: 0;
+}
+.media-object {
+  display: block;
+}
+.media-heading {
+  margin: 0 0 5px;
+}
+.media > .pull-left {
+  margin-right: 10px;
+}
+.media > .pull-right {
+  margin-left: 10px;
+}
+.media-list {
+  padding-left: 0;
+  list-style: none;
+}
+.list-group {
+  padding-left: 0;
+  margin-bottom: 20px;
+}
+.list-group-item {
+  position: relative;
+  display: block;
+  padding: 10px 15px;
+  margin-bottom: -1px;
+  background-color: #fff;
+  border: 1px solid #ddd;
+}
+.list-group-item:first-child {
+  border-top-left-radius: 4px;
+  border-top-right-radius: 4px;
+}
+.list-group-item:last-child {
+  margin-bottom: 0;
+  border-bottom-right-radius: 4px;
+  border-bottom-left-radius: 4px;
+}
+.list-group-item > .badge {
+  float: right;
+}
+.list-group-item > .badge + .badge {
+  margin-right: 5px;
+}
+a.list-group-item {
+  color: #555;
+}
+a.list-group-item .list-group-item-heading {
+  color: #333;
+}
+a.list-group-item:hover,
+a.list-group-item:focus {
+  text-decoration: none;
+  background-color: #f5f5f5;
+}
+a.list-group-item.active,
+a.list-group-item.active:hover,
+a.list-group-item.active:focus {
+  z-index: 2;
+  color: #fff;
+  background-color: #428bca;
+  border-color: #428bca;
+}
+a.list-group-item.active .list-group-item-heading,
+a.list-group-item.active:hover .list-group-item-heading,
+a.list-group-item.active:focus .list-group-item-heading {
+  color: inherit;
+}
+a.list-group-item.active .list-group-item-text,
+a.list-group-item.active:hover .list-group-item-text,
+a.list-group-item.active:focus .list-group-item-text {
+  color: #e1edf7;
+}
+.list-group-item-success {
+  color: #3c763d;
+  background-color: #dff0d8;
+}
+a.list-group-item-success {
+  color: #3c763d;
+}
+a.list-group-item-success .list-group-item-heading {
+  color: inherit;
+}
+a.list-group-item-success:hover,
+a.list-group-item-success:focus {
+  color: #3c763d;
+  background-color: #d0e9c6;
+}
+a.list-group-item-success.active,
+a.list-group-item-success.active:hover,
+a.list-group-item-success.active:focus {
+  color: #fff;
+  background-color: #3c763d;
+  border-color: #3c763d;
+}
+.list-group-item-info {
+  color: #31708f;
+  background-color: #d9edf7;
+}
+a.list-group-item-info {
+  color: #31708f;
+}
+a.list-group-item-info .list-group-item-heading {
+  color: inherit;
+}
+a.list-group-item-info:hover,
+a.list-group-item-info:focus {
+  color: #31708f;
+  background-color: #c4e3f3;
+}
+a.list-group-item-info.active,
+a.list-group-item-info.active:hover,
+a.list-group-item-info.active:focus {
+  color: #fff;
+  background-color: #31708f;
+  border-color: #31708f;
+}
+.list-group-item-warning {
+  color: #8a6d3b;
+  background-color: #fcf8e3;
+}
+a.list-group-item-warning {
+  color: #8a6d3b;
+}
+a.list-group-item-warning .list-group-item-heading {
+  color: inherit;
+}
+a.list-group-item-warning:hover,
+a.list-group-item-warning:focus {
+  color: #8a6d3b;
+  background-color: #faf2cc;
+}
+a.list-group-item-warning.active,
+a.list-group-item-warning.active:hover,
+a.list-group-item-warning.active:focus {
+  color: #fff;
+  background-color: #8a6d3b;
+  border-color: #8a6d3b;
+}
+.list-group-item-danger {
+  color: #a94442;
+  background-color: #f2dede;
+}
+a.list-group-item-danger {
+  color: #a94442;
+}
+a.list-group-item-danger .list-group-item-heading {
+  color: inherit;
+}
+a.list-group-item-danger:hover,
+a.list-group-item-danger:focus {
+  color: #a94442;
+  background-color: #ebcccc;
+}
+a.list-group-item-danger.active,
+a.list-group-item-danger.active:hover,
+a.list-group-item-danger.active:focus {
+  color: #fff;
+  background-color: #a94442;
+  border-color: #a94442;
+}
+.list-group-item-heading {
+  margin-top: 0;
+  margin-bottom: 5px;
+}
+.list-group-item-text {
+  margin-bottom: 0;
+  line-height: 1.3;
+}
+.panel {
+  margin-bottom: 20px;
+  background-color: #fff;
+  border: 1px solid transparent;
+  border-radius: 4px;
+  -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, .05);
+          box-shadow: 0 1px 1px rgba(0, 0, 0, .05);
+}
+.panel-body {
+  padding: 15px;
+}
+.panel-heading {
+  padding: 10px 15px;
+  border-bottom: 1px solid transparent;
+  border-top-left-radius: 3px;
+  border-top-right-radius: 3px;
+}
+.panel-heading > .dropdown .dropdown-toggle {
+  color: inherit;
+}
+.panel-title {
+  margin-top: 0;
+  margin-bottom: 0;
+  font-size: 16px;
+  color: inherit;
+}
+.panel-title > a {
+  color: inherit;
+}
+.panel-footer {
+  padding: 10px 15px;
+  background-color: #f5f5f5;
+  border-top: 1px solid #ddd;
+  border-bottom-right-radius: 3px;
+  border-bottom-left-radius: 3px;
+}
+.panel > .list-group {
+  margin-bottom: 0;
+}
+.panel > .list-group .list-group-item {
+  border-width: 1px 0;
+  border-radius: 0;
+}
+.panel > .list-group:first-child .list-group-item:first-child {
+  border-top: 0;
+  border-top-left-radius: 3px;
+  border-top-right-radius: 3px;
+}
+.panel > .list-group:last-child .list-group-item:last-child {
+  border-bottom: 0;
+  border-bottom-right-radius: 3px;
+  border-bottom-left-radius: 3px;
+}
+.panel-heading + .list-group .list-group-item:first-child {
+  border-top-width: 0;
+}
+.panel > .table,
+.panel > .table-responsive > .table {
+  margin-bottom: 0;
+}
+.panel > .table:first-child,
+.panel > .table-responsive:first-child > .table:first-child {
+  border-top-left-radius: 3px;
+  border-top-right-radius: 3px;
+}
+.panel > .table:first-child > thead:first-child > tr:first-child td:first-child,
+.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:first-child,
+.panel > .table:first-child > tbody:first-child > tr:first-child td:first-child,
+.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:first-child,
+.panel > .table:first-child > thead:first-child > tr:first-child th:first-child,
+.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:first-child,
+.panel > .table:first-child > tbody:first-child > tr:first-child th:first-child,
+.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:first-child {
+  border-top-left-radius: 3px;
+}
+.panel > .table:first-child > thead:first-child > tr:first-child td:last-child,
+.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:last-child,
+.panel > .table:first-child > tbody:first-child > tr:first-child td:last-child,
+.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:last-child,
+.panel > .table:first-child > thead:first-child > tr:first-child th:last-child,
+.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:last-child,
+.panel > .table:first-child > tbody:first-child > tr:first-child th:last-child,
+.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:last-child {
+  border-top-right-radius: 3px;
+}
+.panel > .table:last-child,
+.panel > .table-responsive:last-child > .table:last-child {
+  border-bottom-right-radius: 3px;
+  border-bottom-left-radius: 3px;
+}
+.panel > .table:last-child > tbody:last-child > tr:last-child td:first-child,
+.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:first-child,
+.panel > .table:last-child > tfoot:last-child > tr:last-child td:first-child,
+.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:first-child,
+.panel > .table:last-child > tbody:last-child > tr:last-child th:first-child,
+.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:first-child,
+.panel > .table:last-child > tfoot:last-child > tr:last-child th:first-child,
+.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:first-child {
+  border-bottom-left-radius: 3px;
+}
+.panel > .table:last-child > tbody:last-child > tr:last-child td:last-child,
+.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:last-child,
+.panel > .table:last-child > tfoot:last-child > tr:last-child td:last-child,
+.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:last-child,
+.panel > .table:last-child > tbody:last-child > tr:last-child th:last-child,
+.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:last-child,
+.panel > .table:last-child > tfoot:last-child > tr:last-child th:last-child,
+.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:last-child {
+  border-bottom-right-radius: 3px;
+}
+.panel > .panel-body + .table,
+.panel > .panel-body + .table-responsive {
+  border-top: 1px solid #ddd;
+}
+.panel > .table > tbody:first-child > tr:first-child th,
+.panel > .table > tbody:first-child > tr:first-child td {
+  border-top: 0;
+}
+.panel > .table-bordered,
+.panel > .table-responsive > .table-bordered {
+  border: 0;
+}
+.panel > .table-bordered > thead > tr > th:first-child,
+.panel > .table-responsive > .table-bordered > thead > tr > th:first-child,
+.panel > .table-bordered > tbody > tr > th:first-child,
+.panel > .table-responsive > .table-bordered > tbody > tr > th:first-child,
+.panel > .table-bordered > tfoot > tr > th:first-child,
+.panel > .table-responsive > .table-bordered > tfoot > tr > th:first-child,
+.panel > .table-bordered > thead > tr > td:first-child,
+.panel > .table-responsive > .table-bordered > thead > tr > td:first-child,
+.panel > .table-bordered > tbody > tr > td:first-child,
+.panel > .table-responsive > .table-bordered > tbody > tr > td:first-child,
+.panel > .table-bordered > tfoot > tr > td:first-child,
+.panel > .table-responsive > .table-bordered > tfoot > tr > td:first-child {
+  border-left: 0;
+}
+.panel > .table-bordered > thead > tr > th:last-child,
+.panel > .table-responsive > .table-bordered > thead > tr > th:last-child,
+.panel > .table-bordered > tbody > tr > th:last-child,
+.panel > .table-responsive > .table-bordered > tbody > tr > th:last-child,
+.panel > .table-bordered > tfoot > tr > th:last-child,
+.panel > .table-responsive > .table-bordered > tfoot > tr > th:last-child,
+.panel > .table-bordered > thead > tr > td:last-child,
+.panel > .table-responsive > .table-bordered > thead > tr > td:last-child,
+.panel > .table-bordered > tbody > tr > td:last-child,
+.panel > .table-responsive > .table-bordered > tbody > tr > td:last-child,
+.panel > .table-bordered > tfoot > tr > td:last-child,
+.panel > .table-responsive > .table-bordered > tfoot > tr > td:last-child {
+  border-right: 0;
+}
+.panel > .table-bordered > thead > tr:first-child > td,
+.panel > .table-responsive > .table-bordered > thead > tr:first-child > td,
+.panel > .table-bordered > tbody > tr:first-child > td,
+.panel > .table-responsive > .table-bordered > tbody > tr:first-child > td,
+.panel > .table-bordered > thead > tr:first-child > th,
+.panel > .table-responsive > .table-bordered > thead > tr:first-child > th,
+.panel > .table-bordered > tbody > tr:first-child > th,
+.panel > .table-responsive > .table-bordered > tbody > tr:first-child > th {
+  border-bottom: 0;
+}
+.panel > .table-bordered > tbody > tr:last-child > td,
+.panel > .table-responsive > .table-bordered > tbody > tr:last-child > td,
+.panel > .table-bordered > tfoot > tr:last-child > td,
+.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > td,
+.panel > .table-bordered > tbody > tr:last-child > th,
+.panel > .table-responsive > .table-bordered > tbody > tr:last-child > th,
+.panel > .table-bordered > tfoot > tr:last-child > th,
+.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > th {
+  border-bottom: 0;
+}
+.panel > .table-responsive {
+  margin-bottom: 0;
+  border: 0;
+}
+.panel-group {
+  margin-bottom: 20px;
+}
+.panel-group .panel {
+  margin-bottom: 0;
+  overflow: hidden;
+  border-radius: 4px;
+}
+.panel-group .panel + .panel {
+  margin-top: 5px;
+}
+.panel-group .panel-heading {
+  border-bottom: 0;
+}
+.panel-group .panel-heading + .panel-collapse .panel-body {
+  border-top: 1px solid #ddd;
+}
+.panel-group .panel-footer {
+  border-top: 0;
+}
+.panel-group .panel-footer + .panel-collapse .panel-body {
+  border-bottom: 1px solid #ddd;
+}
+.panel-default {
+  border-color: #ddd;
+}
+.panel-default > .panel-heading {
+  color: #333;
+  background-color: #f5f5f5;
+  border-color: #ddd;
+}
+.panel-default > .panel-heading + .panel-collapse .panel-body {
+  border-top-color: #ddd;
+}
+.panel-default > .panel-footer + .panel-collapse .panel-body {
+  border-bottom-color: #ddd;
+}
+.panel-primary {
+  border-color: #428bca;
+}
+.panel-primary > .panel-heading {
+  color: #fff;
+  background-color: #428bca;
+  border-color: #428bca;
+}
+.panel-primary > .panel-heading + .panel-collapse .panel-body {
+  border-top-color: #428bca;
+}
+.panel-primary > .panel-footer + .panel-collapse .panel-body {
+  border-bottom-color: #428bca;
+}
+.panel-success {
+  border-color: #d6e9c6;
+}
+.panel-success > .panel-heading {
+  color: #3c763d;
+  background-color: #dff0d8;
+  border-color: #d6e9c6;
+}
+.panel-success > .panel-heading + .panel-collapse .panel-body {
+  border-top-color: #d6e9c6;
+}
+.panel-success > .panel-footer + .panel-collapse .panel-body {
+  border-bottom-color: #d6e9c6;
+}
+.panel-info {
+  border-color: #bce8f1;
+}
+.panel-info > .panel-heading {
+  color: #31708f;
+  background-color: #d9edf7;
+  border-color: #bce8f1;
+}
+.panel-info > .panel-heading + .panel-collapse .panel-body {
+  border-top-color: #bce8f1;
+}
+.panel-info > .panel-footer + .panel-collapse .panel-body {
+  border-bottom-color: #bce8f1;
+}
+.panel-warning {
+  border-color: #faebcc;
+}
+.panel-warning > .panel-heading {
+  color: #8a6d3b;
+  background-color: #fcf8e3;
+  border-color: #faebcc;
+}
+.panel-warning > .panel-heading + .panel-collapse .panel-body {
+  border-top-color: #faebcc;
+}
+.panel-warning > .panel-footer + .panel-collapse .panel-body {
+  border-bottom-color: #faebcc;
+}
+.panel-danger {
+  border-color: #ebccd1;
+}
+.panel-danger > .panel-heading {
+  color: #a94442;
+  background-color: #f2dede;
+  border-color: #ebccd1;
+}
+.panel-danger > .panel-heading + .panel-collapse .panel-body {
+  border-top-color: #ebccd1;
+}
+.panel-danger > .panel-footer + .panel-collapse .panel-body {
+  border-bottom-color: #ebccd1;
+}
+.well {
+  min-height: 20px;
+  padding: 19px;
+  margin-bottom: 20px;
+  background-color: #f5f5f5;
+  border: 1px solid #e3e3e3;
+  border-radius: 4px;
+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05);
+          box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05);
+}
+.well blockquote {
+  border-color: #ddd;
+  border-color: rgba(0, 0, 0, .15);
+}
+.well-lg {
+  padding: 24px;
+  border-radius: 6px;
+}
+.well-sm {
+  padding: 9px;
+  border-radius: 3px;
+}
+.close {
+  float: right;
+  font-size: 21px;
+  font-weight: bold;
+  line-height: 1;
+  color: #000;
+  text-shadow: 0 1px 0 #fff;
+  filter: alpha(opacity=20);
+  opacity: .2;
+}
+.close:hover,
+.close:focus {
+  color: #000;
+  text-decoration: none;
+  cursor: pointer;
+  filter: alpha(opacity=50);
+  opacity: .5;
+}
+button.close {
+  -webkit-appearance: none;
+  padding: 0;
+  cursor: pointer;
+  background: transparent;
+  border: 0;
+}
+.modal-open {
+  overflow: hidden;
+}
+.modal {
+  position: fixed;
+  top: 0;
+  right: 0;
+  bottom: 0;
+  left: 0;
+  z-index: 1050;
+  display: none;
+  overflow: auto;
+  overflow-y: scroll;
+  -webkit-overflow-scrolling: touch;
+  outline: 0;
+}
+.modal.fade .modal-dialog {
+  -webkit-transition: -webkit-transform .3s ease-out;
+     -moz-transition:    -moz-transform .3s ease-out;
+       -o-transition:      -o-transform .3s ease-out;
+          transition:         transform .3s ease-out;
+  -webkit-transform: translate(0, -25%);
+      -ms-transform: translate(0, -25%);
+          transform: translate(0, -25%);
+}
+.modal.in .modal-dialog {
+  -webkit-transform: translate(0, 0);
+      -ms-transform: translate(0, 0);
+          transform: translate(0, 0);
+}
+.modal-dialog {
+  position: relative;
+  width: auto;
+  margin: 10px;
+}
+.modal-content {
+  position: relative;
+  background-color: #fff;
+  background-clip: padding-box;
+  border: 1px solid #999;
+  border: 1px solid rgba(0, 0, 0, .2);
+  border-radius: 6px;
+  outline: none;
+  -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, .5);
+          box-shadow: 0 3px 9px rgba(0, 0, 0, .5);
+}
+.modal-backdrop {
+  position: fixed;
+  top: 0;
+  right: 0;
+  bottom: 0;
+  left: 0;
+  z-index: 1040;
+  background-color: #000;
+}
+.modal-backdrop.fade {
+  filter: alpha(opacity=0);
+  opacity: 0;
+}
+.modal-backdrop.in {
+  filter: alpha(opacity=50);
+  opacity: .5;
+}
+.modal-header {
+  min-height: 16.42857143px;
+  padding: 15px;
+  border-bottom: 1px solid #e5e5e5;
+}
+.modal-header .close {
+  margin-top: -2px;
+}
+.modal-title {
+  margin: 0;
+  line-height: 1.42857143;
+}
+.modal-body {
+  position: relative;
+  padding: 20px;
+}
+.modal-footer {
+  padding: 19px 20px 20px;
+  margin-top: 15px;
+  text-align: right;
+  border-top: 1px solid #e5e5e5;
+}
+.modal-footer .btn + .btn {
+  margin-bottom: 0;
+  margin-left: 5px;
+}
+.modal-footer .btn-group .btn + .btn {
+  margin-left: -1px;
+}
+.modal-footer .btn-block + .btn-block {
+  margin-left: 0;
+}
+@media (min-width: 768px) {
+  .modal-dialog {
+    width: 600px;
+    margin: 30px auto;
+  }
+  .modal-content {
+    -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, .5);
+            box-shadow: 0 5px 15px rgba(0, 0, 0, .5);
+  }
+  .modal-sm {
+    width: 300px;
+  }
+}
+@media (min-width: 992px) {
+  .modal-lg {
+    width: 900px;
+  }
+}
+.tooltip {
+  position: absolute;
+  z-index: 1030;
+  display: block;
+  font-size: 12px;
+  line-height: 1.4;
+  visibility: visible;
+  filter: alpha(opacity=0);
+  opacity: 0;
+}
+.tooltip.in {
+  filter: alpha(opacity=90);
+  opacity: .9;
+}
+.tooltip.top {
+  padding: 5px 0;
+  margin-top: -3px;
+}
+.tooltip.right {
+  padding: 0 5px;
+  margin-left: 3px;
+}
+.tooltip.bottom {
+  padding: 5px 0;
+  margin-top: 3px;
+}
+.tooltip.left {
+  padding: 0 5px;
+  margin-left: -3px;
+}
+.tooltip-inner {
+  max-width: 200px;
+  padding: 3px 8px;
+  color: #fff;
+  text-align: center;
+  text-decoration: none;
+  background-color: #000;
+  border-radius: 4px;
+}
+.tooltip-arrow {
+  position: absolute;
+  width: 0;
+  height: 0;
+  border-color: transparent;
+  border-style: solid;
+}
+.tooltip.top .tooltip-arrow {
+  bottom: 0;
+  left: 50%;
+  margin-left: -5px;
+  border-width: 5px 5px 0;
+  border-top-color: #000;
+}
+.tooltip.top-left .tooltip-arrow {
+  bottom: 0;
+  left: 5px;
+  border-width: 5px 5px 0;
+  border-top-color: #000;
+}
+.tooltip.top-right .tooltip-arrow {
+  right: 5px;
+  bottom: 0;
+  border-width: 5px 5px 0;
+  border-top-color: #000;
+}
+.tooltip.right .tooltip-arrow {
+  top: 50%;
+  left: 0;
+  margin-top: -5px;
+  border-width: 5px 5px 5px 0;
+  border-right-color: #000;
+}
+.tooltip.left .tooltip-arrow {
+  top: 50%;
+  right: 0;
+  margin-top: -5px;
+  border-width: 5px 0 5px 5px;
+  border-left-color: #000;
+}
+.tooltip.bottom .tooltip-arrow {
+  top: 0;
+  left: 50%;
+  margin-left: -5px;
+  border-width: 0 5px 5px;
+  border-bottom-color: #000;
+}
+.tooltip.bottom-left .tooltip-arrow {
+  top: 0;
+  left: 5px;
+  border-width: 0 5px 5px;
+  border-bottom-color: #000;
+}
+.tooltip.bottom-right .tooltip-arrow {
+  top: 0;
+  right: 5px;
+  border-width: 0 5px 5px;
+  border-bottom-color: #000;
+}
+.popover {
+  position: absolute;
+  top: 0;
+  left: 0;
+  z-index: 1010;
+  display: none;
+  max-width: 276px;
+  padding: 1px;
+  text-align: left;
+  white-space: normal;
+  background-color: #fff;
+  background-clip: padding-box;
+  border: 1px solid #ccc;
+  border: 1px solid rgba(0, 0, 0, .2);
+  border-radius: 6px;
+  -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, .2);
+          box-shadow: 0 5px 10px rgba(0, 0, 0, .2);
+}
+.popover.top {
+  margin-top: -10px;
+}
+.popover.right {
+  margin-left: 10px;
+}
+.popover.bottom {
+  margin-top: 10px;
+}
+.popover.left {
+  margin-left: -10px;
+}
+.popover-title {
+  padding: 8px 14px;
+  margin: 0;
+  font-size: 14px;
+  font-weight: normal;
+  line-height: 18px;
+  background-color: #f7f7f7;
+  border-bottom: 1px solid #ebebeb;
+  border-radius: 5px 5px 0 0;
+}
+.popover-content {
+  padding: 9px 14px;
+}
+.popover > .arrow,
+.popover > .arrow:after {
+  position: absolute;
+  display: block;
+  width: 0;
+  height: 0;
+  border-color: transparent;
+  border-style: solid;
+}
+.popover > .arrow {
+  border-width: 11px;
+}
+.popover > .arrow:after {
+  content: "";
+  border-width: 10px;
+}
+.popover.top > .arrow {
+  bottom: -11px;
+  left: 50%;
+  margin-left: -11px;
+  border-top-color: #999;
+  border-top-color: rgba(0, 0, 0, .25);
+  border-bottom-width: 0;
+}
+.popover.top > .arrow:after {
+  bottom: 1px;
+  margin-left: -10px;
+  content: " ";
+  border-top-color: #fff;
+  border-bottom-width: 0;
+}
+.popover.right > .arrow {
+  top: 50%;
+  left: -11px;
+  margin-top: -11px;
+  border-right-color: #999;
+  border-right-color: rgba(0, 0, 0, .25);
+  border-left-width: 0;
+}
+.popover.right > .arrow:after {
+  bottom: -10px;
+  left: 1px;
+  content: " ";
+  border-right-color: #fff;
+  border-left-width: 0;
+}
+.popover.bottom > .arrow {
+  top: -11px;
+  left: 50%;
+  margin-left: -11px;
+  border-top-width: 0;
+  border-bottom-color: #999;
+  border-bottom-color: rgba(0, 0, 0, .25);
+}
+.popover.bottom > .arrow:after {
+  top: 1px;
+  margin-left: -10px;
+  content: " ";
+  border-top-width: 0;
+  border-bottom-color: #fff;
+}
+.popover.left > .arrow {
+  top: 50%;
+  right: -11px;
+  margin-top: -11px;
+  border-right-width: 0;
+  border-left-color: #999;
+  border-left-color: rgba(0, 0, 0, .25);
+}
+.popover.left > .arrow:after {
+  right: 1px;
+  bottom: -10px;
+  content: " ";
+  border-right-width: 0;
+  border-left-color: #fff;
+}
+.carousel {
+  position: relative;
+}
+.carousel-inner {
+  position: relative;
+  width: 100%;
+  overflow: hidden;
+}
+.carousel-inner > .item {
+  position: relative;
+  display: none;
+  -webkit-transition: .6s ease-in-out left;
+          transition: .6s ease-in-out left;
+}
+.carousel-inner > .item > img,
+.carousel-inner > .item > a > img {
+  line-height: 1;
+}
+.carousel-inner > .active,
+.carousel-inner > .next,
+.carousel-inner > .prev {
+  display: block;
+}
+.carousel-inner > .active {
+  left: 0;
+}
+.carousel-inner > .next,
+.carousel-inner > .prev {
+  position: absolute;
+  top: 0;
+  width: 100%;
+}
+.carousel-inner > .next {
+  left: 100%;
+}
+.carousel-inner > .prev {
+  left: -100%;
+}
+.carousel-inner > .next.left,
+.carousel-inner > .prev.right {
+  left: 0;
+}
+.carousel-inner > .active.left {
+  left: -100%;
+}
+.carousel-inner > .active.right {
+  left: 100%;
+}
+.carousel-control {
+  position: absolute;
+  top: 0;
+  bottom: 0;
+  left: 0;
+  width: 15%;
+  font-size: 20px;
+  color: #fff;
+  text-align: center;
+  text-shadow: 0 1px 2px rgba(0, 0, 0, .6);
+  filter: alpha(opacity=50);
+  opacity: .5;
+}
+.carousel-control.left {
+  background-image: -webkit-linear-gradient(left, color-stop(rgba(0, 0, 0, .5) 0%), color-stop(rgba(0, 0, 0, .0001) 100%));
+  background-image:         linear-gradient(to right, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);
+  background-repeat: repeat-x;
+}
+.carousel-control.right {
+  right: 0;
+  left: auto;
+  background-image: -webkit-linear-gradient(left, color-stop(rgba(0, 0, 0, .0001) 0%), color-stop(rgba(0, 0, 0, .5) 100%));
+  background-image:         linear-gradient(to right, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);
+  background-repeat: repeat-x;
+}
+.carousel-control:hover,
+.carousel-control:focus {
+  color: #fff;
+  text-decoration: none;
+  filter: alpha(opacity=90);
+  outline: none;
+  opacity: .9;
+}
+.carousel-control .icon-prev,
+.carousel-control .icon-next,
+.carousel-control .glyphicon-chevron-left,
+.carousel-control .glyphicon-chevron-right {
+  position: absolute;
+  top: 50%;
+  z-index: 5;
+  display: inline-block;
+}
+.carousel-control .icon-prev,
+.carousel-control .glyphicon-chevron-left {
+  left: 50%;
+}
+.carousel-control .icon-next,
+.carousel-control .glyphicon-chevron-right {
+  right: 50%;
+}
+.carousel-control .icon-prev,
+.carousel-control .icon-next {
+  width: 20px;
+  height: 20px;
+  margin-top: -10px;
+  margin-left: -10px;
+  font-family: serif;
+}
+.carousel-control .icon-prev:before {
+  content: '\2039';
+}
+.carousel-control .icon-next:before {
+  content: '\203a';
+}
+.carousel-indicators {
+  position: absolute;
+  bottom: 10px;
+  left: 50%;
+  z-index: 15;
+  width: 60%;
+  padding-left: 0;
+  margin-left: -30%;
+  text-align: center;
+  list-style: none;
+}
+.carousel-indicators li {
+  display: inline-block;
+  width: 10px;
+  height: 10px;
+  margin: 1px;
+  text-indent: -999px;
+  cursor: pointer;
+  background-color: #000 \9;
+  background-color: rgba(0, 0, 0, 0);
+  border: 1px solid #fff;
+  border-radius: 10px;
+}
+.carousel-indicators .active {
+  width: 12px;
+  height: 12px;
+  margin: 0;
+  background-color: #fff;
+}
+.carousel-caption {
+  position: absolute;
+  right: 15%;
+  bottom: 20px;
+  left: 15%;
+  z-index: 10;
+  padding-top: 20px;
+  padding-bottom: 20px;
+  color: #fff;
+  text-align: center;
+  text-shadow: 0 1px 2px rgba(0, 0, 0, .6);
+}
+.carousel-caption .btn {
+  text-shadow: none;
+}
+@media screen and (min-width: 768px) {
+  .carousel-control .glyphicon-chevron-left,
+  .carousel-control .glyphicon-chevron-right,
+  .carousel-control .icon-prev,
+  .carousel-control .icon-next {
+    width: 30px;
+    height: 30px;
+    margin-top: -15px;
+    margin-left: -15px;
+    font-size: 30px;
+  }
+  .carousel-caption {
+    right: 20%;
+    left: 20%;
+    padding-bottom: 30px;
+  }
+  .carousel-indicators {
+    bottom: 20px;
+  }
+}
+.clearfix:before,
+.clearfix:after,
+.container:before,
+.container:after,
+.container-fluid:before,
+.container-fluid:after,
+.row:before,
+.row:after,
+.form-horizontal .form-group:before,
+.form-horizontal .form-group:after,
+.btn-toolbar:before,
+.btn-toolbar:after,
+.btn-group-vertical > .btn-group:before,
+.btn-group-vertical > .btn-group:after,
+.nav:before,
+.nav:after,
+.navbar:before,
+.navbar:after,
+.navbar-header:before,
+.navbar-header:after,
+.navbar-collapse:before,
+.navbar-collapse:after,
+.pager:before,
+.pager:after,
+.panel-body:before,
+.panel-body:after,
+.modal-footer:before,
+.modal-footer:after {
+  display: table;
+  content: " ";
+}
+.clearfix:after,
+.container:after,
+.container-fluid:after,
+.row:after,
+.form-horizontal .form-group:after,
+.btn-toolbar:after,
+.btn-group-vertical > .btn-group:after,
+.nav:after,
+.navbar:after,
+.navbar-header:after,
+.navbar-collapse:after,
+.pager:after,
+.panel-body:after,
+.modal-footer:after {
+  clear: both;
+}
+.center-block {
+  display: block;
+  margin-right: auto;
+  margin-left: auto;
+}
+.pull-right {
+  float: right !important;
+}
+.pull-left {
+  float: left !important;
+}
+.hide {
+  display: none !important;
+}
+.show {
+  display: block !important;
+}
+.invisible {
+  visibility: hidden;
+}
+.text-hide {
+  font: 0/0 a;
+  color: transparent;
+  text-shadow: none;
+  background-color: transparent;
+  border: 0;
+}
+.hidden {
+  display: none !important;
+  visibility: hidden !important;
+}
+.affix {
+  position: fixed;
+}
+@-ms-viewport {
+  width: device-width;
+}
+.visible-xs,
+.visible-sm,
+.visible-md,
+.visible-lg {
+  display: none !important;
+}
+@media (max-width: 767px) {
+  .visible-xs {
+    display: block !important;
+  }
+  table.visible-xs {
+    display: table;
+  }
+  tr.visible-xs {
+    display: table-row !important;
+  }
+  th.visible-xs,
+  td.visible-xs {
+    display: table-cell !important;
+  }
+}
+@media (min-width: 768px) and (max-width: 991px) {
+  .visible-sm {
+    display: block !important;
+  }
+  table.visible-sm {
+    display: table;
+  }
+  tr.visible-sm {
+    display: table-row !important;
+  }
+  th.visible-sm,
+  td.visible-sm {
+    display: table-cell !important;
+  }
+}
+@media (min-width: 992px) and (max-width: 1199px) {
+  .visible-md {
+    display: block !important;
+  }
+  table.visible-md {
+    display: table;
+  }
+  tr.visible-md {
+    display: table-row !important;
+  }
+  th.visible-md,
+  td.visible-md {
+    display: table-cell !important;
+  }
+}
+@media (min-width: 1200px) {
+  .visible-lg {
+    display: block !important;
+  }
+  table.visible-lg {
+    display: table;
+  }
+  tr.visible-lg {
+    display: table-row !important;
+  }
+  th.visible-lg,
+  td.visible-lg {
+    display: table-cell !important;
+  }
+}
+@media (max-width: 767px) {
+  .hidden-xs {
+    display: none !important;
+  }
+}
+@media (min-width: 768px) and (max-width: 991px) {
+  .hidden-sm {
+    display: none !important;
+  }
+}
+@media (min-width: 992px) and (max-width: 1199px) {
+  .hidden-md {
+    display: none !important;
+  }
+}
+@media (min-width: 1200px) {
+  .hidden-lg {
+    display: none !important;
+  }
+}
+.visible-print {
+  display: none !important;
+}
+@media print {
+  .visible-print {
+    display: block !important;
+  }
+  table.visible-print {
+    display: table;
+  }
+  tr.visible-print {
+    display: table-row !important;
+  }
+  th.visible-print,
+  td.visible-print {
+    display: table-cell !important;
+  }
+}
+@media print {
+  .hidden-print {
+    display: none !important;
+  }
+}
+/*# sourceMappingURL=bootstrap.css.map */
diff --git a/pelican-plugins/bootstrap-rst/bootstrap/css/bootstrap.css.map b/pelican-plugins/bootstrap-rst/bootstrap/css/bootstrap.css.map
new file mode 100644
index 0000000000000000000000000000000000000000..6bc5a2dc75413860670f9e657d62b64cf15fd195
--- /dev/null
+++ b/pelican-plugins/bootstrap-rst/bootstrap/css/bootstrap.css.map
@@ -0,0 +1 @@
+{"version":3,"sources":["less/normalize.less","less/print.less","less/scaffolding.less","less/mixins.less","less/variables.less","less/thumbnails.less","less/carousel.less","less/type.less","less/code.less","less/grid.less","less/tables.less","less/forms.less","less/buttons.less","less/button-groups.less","less/component-animations.less","less/glyphicons.less","less/dropdowns.less","less/input-groups.less","less/navs.less","less/navbar.less","less/utilities.less","less/breadcrumbs.less","less/pagination.less","less/pager.less","less/labels.less","less/badges.less","less/jumbotron.less","less/alerts.less","less/progress-bars.less","less/media.less","less/list-group.less","less/panels.less","less/wells.less","less/close.less","less/modals.less","less/tooltip.less","less/popovers.less","less/responsive-utilities.less"],"names":[],"mappings":";AAQA;EACE,uBAAA;EACA,0BAAA;EACA,8BAAA;;AAOF;EACE,SAAA;;AAUF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;EACE,cAAA;;AAQF;AACA;AACA;AACA;EACE,qBAAA;EACA,wBAAA;;AAQF,KAAK,IAAI;EACP,aAAA;EACA,SAAA;;AAQF;AACA;EACE,aAAA;;AAUF;EACE,uBAAA;;AAOF,CAAC;AACD,CAAC;EACC,UAAA;;AAUF,IAAI;EACF,yBAAA;;AAOF;AACA;EACE,iBAAA;;AAOF;EACE,kBAAA;;AAQF;EACE,cAAA;EACA,gBAAA;;AAOF;EACE,gBAAA;EACA,WAAA;;AAOF;EACE,cAAA;;AAOF;AACA;EACE,cAAA;EACA,cAAA;EACA,kBAAA;EACA,wBAAA;;AAGF;EACE,WAAA;;AAGF;EACE,eAAA;;AAUF;EACE,SAAA;;AAOF,GAAG,IAAI;EACL,gBAAA;;AAUF;EACE,gBAAA;;AAOF;EACE,4BAAA;EACA,uBAAA;EACA,SAAA;;AAOF;EACE,cAAA;;AAOF;AACA;AACA;AACA;EACE,iCAAA;EACA,cAAA;;AAkBF;AACA;AACA;AACA;AACA;EACE,cAAA;EACA,aAAA;EACA,SAAA;;AAOF;EACE,iBAAA;;AAUF;AACA;EACE,oBAAA;;AAWF;AACA,IAAK,MAAK;AACV,KAAK;AACL,KAAK;EACH,0BAAA;EACA,eAAA;;AAOF,MAAM;AACN,IAAK,MAAK;EACR,eAAA;;AAOF,MAAM;AACN,KAAK;EACH,SAAA;EACA,UAAA;;AAQF;EACE,mBAAA;;AAWF,KAAK;AACL,KAAK;EACH,sBAAA;EACA,UAAA;;AASF,KAAK,eAAe;AACpB,KAAK,eAAe;EAClB,YAAA;;AASF,KAAK;EACH,6BAAA;EACA,4BAAA;EACA,+BAAA;EACA,uBAAA;;AASF,KAAK,eAAe;AACpB,KAAK,eAAe;EAClB,wBAAA;;AAOF;EACE,yBAAA;EACA,aAAA;EACA,8BAAA;;AAQF;EACE,SAAA;EACA,UAAA;;AAOF;EACE,cAAA;;AAQF;EACE,iBAAA;;AAUF;EACE,yBAAA;EACA,iBAAA;;AAGF;AACA;EACE,UAAA;;AChUF;EA9FE;IACE,4BAAA;IACA,sBAAA;IACA,kCAAA;IACA,2BAAA;;EAGF;EACA,CAAC;IACC,0BAAA;;EAGF,CAAC,MAAM;IACL,SAAS,KAAK,WAAW,GAAzB;;EAGF,IAAI,OAAO;IACT,SAAS,KAAK,YAAY,GAA1B;;EAIF,CAAC,qBAAqB;EACtB,CAAC,WAAW;IACV,SAAS,EAAT;;EAGF;EACA;IACE,sBAAA;IACA,wBAAA;;EAGF;IACE,2BAAA;;EAGF;EACA;IACE,wBAAA;;EAGF;IACE,0BAAA;;EAGF;EACA;EACA;IACE,UAAA;IACA,SAAA;;EAGF;EACA;IACE,uBAAA;;EAKF;IACE,2BAAA;;EAIF;IACE,aAAA;;EAEF,MACE;EADF,MAEE;IACE,iCAAA;;EAGJ,IAEE;EADF,OAAQ,OACN;IACE,iCAAA;;EAGJ;IACE,sBAAA;;EAGF;IACE,oCAAA;;EAEF,eACE;EADF,eAEE;IACE,iCAAA;;;ACtFN;ECyOE,8BAAA;EACG,2BAAA;EACK,sBAAA;;ADxOV,CAAC;AACD,CAAC;ECqOC,8BAAA;EACG,2BAAA;EACK,sBAAA;;ADhOV;EACE,gBAAA;EACA,6CAAA;;AAGF;EACE,aEcwB,8CFdxB;EACA,eAAA;EACA,uBAAA;EACA,cAAA;EACA,yBAAA;;AAIF;AACA;AACA;AACA;EACE,oBAAA;EACA,kBAAA;EACA,oBAAA;;AAMF;EACE,cAAA;EACA,qBAAA;;AAEA,CAAC;AACD,CAAC;EACC,cAAA;EACA,0BAAA;;AAGF,CAAC;ECzBD,oBAAA;EAEA,0CAAA;EACA,oBAAA;;ADiCF;EACE,SAAA;;AAMF;EACE,sBAAA;;AAIF;AG1EA,UAUE;AAVF,UAWE,EAAE;ACPJ,eAKE,QAME;AAXJ,eAKE,QAOE,IAAI;EHyWN,cAAA;EACA,eAAA;EACA,YAAA;;AD5SF;EACE,kBAAA;;AAMF;EACE,YAAA;EACA,uBAAA;EACA,yBAAA;EACA,yBAAA;EACA,kBAAA;EC8BA,wCAAA;EACQ,gCAAA;EA+PR,qBAAA;EACA,eAAA;EACA,YAAA;;ADxRF;EACE,kBAAA;;AAMF;EACE,gBAAA;EACA,mBAAA;EACA,SAAA;EACA,6BAAA;;AAQF;EACE,kBAAA;EACA,UAAA;EACA,WAAA;EACA,YAAA;EACA,UAAA;EACA,gBAAA;EACA,MAAM,gBAAN;EACA,SAAA;;AK5HF;AAAI;AAAI;AAAI;AAAI;AAAI;AACpB;AAAK;AAAK;AAAK;AAAK;AAAK;EACvB,oBAAA;EACA,gBAAA;EACA,gBAAA;EACA,cAAA;;AALF,EAOE;AAPE,EAOF;AAPM,EAON;AAPU,EAOV;AAPc,EAOd;AAPkB,EAOlB;AANF,GAME;AANG,GAMH;AANQ,GAMR;AANa,GAMb;AANkB,GAMlB;AANuB,GAMvB;AAPF,EAQE;AARE,EAQF;AARM,EAQN;AARU,EAQV;AARc,EAQd;AARkB,EAQlB;AAPF,GAOE;AAPG,GAOH;AAPQ,GAOR;AAPa,GAOb;AAPkB,GAOlB;AAPuB,GAOvB;EACE,mBAAA;EACA,cAAA;EACA,cAAA;;AAIJ;AAAI;AACJ;AAAI;AACJ;AAAI;EACF,gBAAA;EACA,mBAAA;;AAJF,EAME;AANE,GAMF;AALF,EAKE;AALE,GAKF;AAJF,EAIE;AAJE,GAIF;AANF,EAOE;AAPE,GAOF;AANF,EAME;AANE,GAMF;AALF,EAKE;AALE,GAKF;EACE,cAAA;;AAGJ;AAAI;AACJ;AAAI;AACJ;AAAI;EACF,gBAAA;EACA,mBAAA;;AAJF,EAME;AANE,GAMF;AALF,EAKE;AALE,GAKF;AAJF,EAIE;AAJE,GAIF;AANF,EAOE;AAPE,GAOF;AANF,EAME;AANE,GAMF;AALF,EAKE;AALE,GAKF;EACE,cAAA;;AAIJ;AAAI;EAAM,eAAA;;AACV;AAAI;EAAM,eAAA;;AACV;AAAI;EAAM,eAAA;;AACV;AAAI;EAAM,eAAA;;AACV;AAAI;EAAM,eAAA;;AACV;AAAI;EAAM,eAAA;;AAMV;EACE,gBAAA;;AAGF;EACE,mBAAA;EACA,eAAA;EACA,gBAAA;EACA,gBAAA;;AAKF,QAHqC;EAGrC;IAFI,eAAA;;;AASJ;AACA;EAAU,cAAA;;AAGV;EAAU,kBAAA;;AAGV;EAAuB,gBAAA;;AACvB;EAAuB,iBAAA;;AACvB;EAAuB,kBAAA;;AACvB;EAAuB,mBAAA;;AAGvB;EACE,cAAA;;AAEF;EJofE,cAAA;;AACA,CAAC,aAAC;EACA,cAAA;;AInfJ;EJifE,cAAA;;AACA,CAAC,aAAC;EACA,cAAA;;AIhfJ;EJ8eE,cAAA;;AACA,CAAC,UAAC;EACA,cAAA;;AI7eJ;EJ2eE,cAAA;;AACA,CAAC,aAAC;EACA,cAAA;;AI1eJ;EJweE,cAAA;;AACA,CAAC,YAAC;EACA,cAAA;;AIneJ;EAGE,WAAA;EJqdA,yBAAA;;AACA,CAAC,WAAC;EACA,yBAAA;;AIpdJ;EJkdE,yBAAA;;AACA,CAAC,WAAC;EACA,yBAAA;;AIjdJ;EJ+cE,yBAAA;;AACA,CAAC,QAAC;EACA,yBAAA;;AI9cJ;EJ4cE,yBAAA;;AACA,CAAC,WAAC;EACA,yBAAA;;AI3cJ;EJycE,yBAAA;;AACA,CAAC,UAAC;EACA,yBAAA;;AIncJ;EACE,mBAAA;EACA,mBAAA;EACA,gCAAA;;AAQF;AACA;EACE,aAAA;EACA,mBAAA;;AAHF,EAIE;AAHF,EAGE;AAJF,EAKE;AAJF,EAIE;EACE,gBAAA;;AAOJ;EACE,eAAA;EACA,gBAAA;;AAIF;EALE,eAAA;EACA,gBAAA;EAMA,iBAAA;;AAFF,YAIE;EACE,qBAAA;EACA,iBAAA;EACA,kBAAA;;AAKJ;EACE,aAAA;EACA,mBAAA;;AAEF;AACA;EACE,uBAAA;;AAEF;EACE,iBAAA;;AAEF;EACE,cAAA;;AAwBF,QAhB2C;EACzC,cACE;IACE,WAAA;IACA,YAAA;IACA,WAAA;IACA,iBAAA;IJ1IJ,gBAAA;IACA,uBAAA;IACA,mBAAA;;EImIA,cAQE;IACE,kBAAA;;;AAUN,IAAI;AAEJ,IAAI;EACF,YAAA;EACA,iCAAA;;AAEF;EACE,cAAA;EACA,yBAAA;;AAIF;EACE,kBAAA;EACA,gBAAA;EACA,iBAAA;EACA,8BAAA;;AAKE,UAHF,EAGG;AAAD,UAFF,GAEG;AAAD,UADF,GACG;EACC,gBAAA;;AAVN,UAgBE;AAhBF,UAiBE;AAjBF,UAkBE;EACE,cAAA;EACA,cAAA;EACA,uBAAA;EACA,cAAA;;AAEA,UARF,OAQG;AAAD,UAPF,MAOG;AAAD,UANF,OAMG;EACC,SAAS,aAAT;;AAQN;AACA,UAAU;EACR,mBAAA;EACA,eAAA;EACA,+BAAA;EACA,cAAA;EACA,iBAAA;;AAME,mBAHF,OAGG;AAAD,UAXM,WAQR,OAGG;AAAD,mBAFF,MAEG;AAAD,UAXM,WASR,MAEG;AAAD,mBADF,OACG;AAAD,UAXM,WAUR,OACG;EAAU,SAAS,EAAT;;AACX,mBAJF,OAIG;AAAD,UAZM,WAQR,OAIG;AAAD,mBAHF,MAGG;AAAD,UAZM,WASR,MAGG;AAAD,mBAFF,OAEG;AAAD,UAZM,WAUR,OAEG;EACC,SAAS,aAAT;;AAMN,UAAU;AACV,UAAU;EACR,SAAS,EAAT;;AAIF;EACE,mBAAA;EACA,kBAAA;EACA,uBAAA;;AC7RF;AACA;AACA;AACA;EACE,sCJkCiD,wBIlCjD;;AAIF;EACE,gBAAA;EACA,cAAA;EACA,cAAA;EACA,yBAAA;EACA,mBAAA;EACA,kBAAA;;AAIF;EACE,gBAAA;EACA,cAAA;EACA,cAAA;EACA,yBAAA;EACA,kBAAA;EACA,8CAAA;;AAIF;EACE,cAAA;EACA,cAAA;EACA,gBAAA;EACA,eAAA;EACA,uBAAA;EACA,qBAAA;EACA,qBAAA;EACA,cAAA;EACA,yBAAA;EACA,yBAAA;EACA,kBAAA;;AAXF,GAcE;EACE,UAAA;EACA,kBAAA;EACA,cAAA;EACA,qBAAA;EACA,6BAAA;EACA,gBAAA;;AAKJ;EACE,iBAAA;EACA,kBAAA;;ACpDF;ENqnBE,kBAAA;EACA,iBAAA;EACA,kBAAA;EACA,mBAAA;;AMlnBA,QAHmC;EAGnC;IAFE,YAAA;;;AAKF,QAHmC;EAGnC;IAFE,YAAA;;;AAKJ,QAHqC;EAGrC;IAFI,aAAA;;;AAUJ;ENimBE,kBAAA;EACA,iBAAA;EACA,kBAAA;EACA,mBAAA;;AM3lBF;ENimBE,kBAAA;EACA,mBAAA;;AAqIE;EACE,kBAAA;EAEA,eAAA;EAEA,kBAAA;EACA,mBAAA;;AAgBF;EACE,WAAA;;AAOJ,KAAK,EAAQ,CAAC;EACZ,WAAA;;AADF,KAAK,EAAQ,CAAC;EACZ,mBAAA;;AADF,KAAK,EAAQ,CAAC;EACZ,mBAAA;;AADF,KAAK,EAAQ,CAAC;EACZ,UAAA;;AADF,KAAK,EAAQ,CAAC;EACZ,mBAAA;;AADF,KAAK,EAAQ,CAAC;EACZ,mBAAA;;AADF,KAAK,EAAQ,CAAC;EACZ,UAAA;;AADF,KAAK,EAAQ,CAAC;EACZ,mBAAA;;AADF,KAAK,EAAQ,CAAC;EACZ,mBAAA;;AADF,KAAK,EAAQ,CAAC;EACZ,UAAA;;AADF,KAAK,EAAQ,CAAC;EACZ,mBAAA;;AADF,KAAK,EAAQ,CAAC;EACZ,kBAAA;;AASF,KAAK,EAAQ,MAAM;EACjB,WAAA;;AADF,KAAK,EAAQ,MAAM;EACjB,mBAAA;;AADF,KAAK,EAAQ,MAAM;EACjB,mBAAA;;AADF,KAAK,EAAQ,MAAM;EACjB,UAAA;;AADF,KAAK,EAAQ,MAAM;EACjB,mBAAA;;AADF,KAAK,EAAQ,MAAM;EACjB,mBAAA;;AADF,KAAK,EAAQ,MAAM;EACjB,UAAA;;AADF,KAAK,EAAQ,MAAM;EACjB,mBAAA;;AADF,KAAK,EAAQ,MAAM;EACjB,mBAAA;;AADF,KAAK,EAAQ,MAAM;EACjB,UAAA;;AADF,KAAK,EAAQ,MAAM;EACjB,mBAAA;;AADF,KAAK,EAAQ,MAAM;EACjB,kBAAA;;AADF,KAAK,EAAQ,MAAM;EACjB,SAAA;;AANF,KAAK,EAAQ,MAAM;EACjB,UAAA;;AADF,KAAK,EAAQ,MAAM;EACjB,kBAAA;;AADF,KAAK,EAAQ,MAAM;EACjB,kBAAA;;AADF,KAAK,EAAQ,MAAM;EACjB,SAAA;;AADF,KAAK,EAAQ,MAAM;EACjB,kBAAA;;AADF,KAAK,EAAQ,MAAM;EACjB,kBAAA;;AADF,KAAK,EAAQ,MAAM;EACjB,SAAA;;AADF,KAAK,EAAQ,MAAM;EACjB,kBAAA;;AADF,KAAK,EAAQ,MAAM;EACjB,kBAAA;;AADF,KAAK,EAAQ,MAAM;EACjB,SAAA;;AADF,KAAK,EAAQ,MAAM;EACjB,kBAAA;;AADF,KAAK,EAAQ,MAAM;EACjB,iBAAA;;AADF,KAAK,EAAQ,MAAM;EACjB,QAAA;;AASF,KAAK,EAAQ,QAAQ;EACnB,iBAAA;;AADF,KAAK,EAAQ,QAAQ;EACnB,yBAAA;;AADF,KAAK,EAAQ,QAAQ;EACnB,yBAAA;;AADF,KAAK,EAAQ,QAAQ;EACnB,gBAAA;;AADF,KAAK,EAAQ,QAAQ;EACnB,yBAAA;;AADF,KAAK,EAAQ,QAAQ;EACnB,yBAAA;;AADF,KAAK,EAAQ,QAAQ;EACnB,gBAAA;;AADF,KAAK,EAAQ,QAAQ;EACnB,yBAAA;;AADF,KAAK,EAAQ,QAAQ;EACnB,yBAAA;;AADF,KAAK,EAAQ,QAAQ;EACnB,gBAAA;;AADF,KAAK,EAAQ,QAAQ;EACnB,yBAAA;;AADF,KAAK,EAAQ,QAAQ;EACnB,wBAAA;;AADF,KAAK,EAAQ,QAAQ;EACnB,eAAA;;AMvvBJ,QALmC;ENouB/B;IACE,WAAA;;EAOJ,KAAK,EAAQ,CAAC;IACZ,WAAA;;EADF,KAAK,EAAQ,CAAC;IACZ,mBAAA;;EADF,KAAK,EAAQ,CAAC;IACZ,mBAAA;;EADF,KAAK,EAAQ,CAAC;IACZ,UAAA;;EADF,KAAK,EAAQ,CAAC;IACZ,mBAAA;;EADF,KAAK,EAAQ,CAAC;IACZ,mBAAA;;EADF,KAAK,EAAQ,CAAC;IACZ,UAAA;;EADF,KAAK,EAAQ,CAAC;IACZ,mBAAA;;EADF,KAAK,EAAQ,CAAC;IACZ,mBAAA;;EADF,KAAK,EAAQ,CAAC;IACZ,UAAA;;EADF,KAAK,EAAQ,CAAC;IACZ,mBAAA;;EADF,KAAK,EAAQ,CAAC;IACZ,kBAAA;;EASF,KAAK,EAAQ,MAAM;IACjB,WAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,mBAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,mBAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,UAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,mBAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,mBAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,UAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,mBAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,mBAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,UAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,mBAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,kBAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,SAAA;;EANF,KAAK,EAAQ,MAAM;IACjB,UAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,kBAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,kBAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,SAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,kBAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,kBAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,SAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,kBAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,kBAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,SAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,kBAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,iBAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,QAAA;;EASF,KAAK,EAAQ,QAAQ;IACnB,iBAAA;;EADF,KAAK,EAAQ,QAAQ;IACnB,yBAAA;;EADF,KAAK,EAAQ,QAAQ;IACnB,yBAAA;;EADF,KAAK,EAAQ,QAAQ;IACnB,gBAAA;;EADF,KAAK,EAAQ,QAAQ;IACnB,yBAAA;;EADF,KAAK,EAAQ,QAAQ;IACnB,yBAAA;;EADF,KAAK,EAAQ,QAAQ;IACnB,gBAAA;;EADF,KAAK,EAAQ,QAAQ;IACnB,yBAAA;;EADF,KAAK,EAAQ,QAAQ;IACnB,yBAAA;;EADF,KAAK,EAAQ,QAAQ;IACnB,gBAAA;;EADF,KAAK,EAAQ,QAAQ;IACnB,yBAAA;;EADF,KAAK,EAAQ,QAAQ;IACnB,wBAAA;;EADF,KAAK,EAAQ,QAAQ;IACnB,eAAA;;;AM9uBJ,QALmC;EN2tB/B;IACE,WAAA;;EAOJ,KAAK,EAAQ,CAAC;IACZ,WAAA;;EADF,KAAK,EAAQ,CAAC;IACZ,mBAAA;;EADF,KAAK,EAAQ,CAAC;IACZ,mBAAA;;EADF,KAAK,EAAQ,CAAC;IACZ,UAAA;;EADF,KAAK,EAAQ,CAAC;IACZ,mBAAA;;EADF,KAAK,EAAQ,CAAC;IACZ,mBAAA;;EADF,KAAK,EAAQ,CAAC;IACZ,UAAA;;EADF,KAAK,EAAQ,CAAC;IACZ,mBAAA;;EADF,KAAK,EAAQ,CAAC;IACZ,mBAAA;;EADF,KAAK,EAAQ,CAAC;IACZ,UAAA;;EADF,KAAK,EAAQ,CAAC;IACZ,mBAAA;;EADF,KAAK,EAAQ,CAAC;IACZ,kBAAA;;EASF,KAAK,EAAQ,MAAM;IACjB,WAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,mBAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,mBAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,UAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,mBAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,mBAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,UAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,mBAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,mBAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,UAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,mBAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,kBAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,SAAA;;EANF,KAAK,EAAQ,MAAM;IACjB,UAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,kBAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,kBAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,SAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,kBAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,kBAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,SAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,kBAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,kBAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,SAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,kBAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,iBAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,QAAA;;EASF,KAAK,EAAQ,QAAQ;IACnB,iBAAA;;EADF,KAAK,EAAQ,QAAQ;IACnB,yBAAA;;EADF,KAAK,EAAQ,QAAQ;IACnB,yBAAA;;EADF,KAAK,EAAQ,QAAQ;IACnB,gBAAA;;EADF,KAAK,EAAQ,QAAQ;IACnB,yBAAA;;EADF,KAAK,EAAQ,QAAQ;IACnB,yBAAA;;EADF,KAAK,EAAQ,QAAQ;IACnB,gBAAA;;EADF,KAAK,EAAQ,QAAQ;IACnB,yBAAA;;EADF,KAAK,EAAQ,QAAQ;IACnB,yBAAA;;EADF,KAAK,EAAQ,QAAQ;IACnB,gBAAA;;EADF,KAAK,EAAQ,QAAQ;IACnB,yBAAA;;EADF,KAAK,EAAQ,QAAQ;IACnB,wBAAA;;EADF,KAAK,EAAQ,QAAQ;IACnB,eAAA;;;AMvuBJ,QAHmC;ENktB/B;IACE,WAAA;;EAOJ,KAAK,EAAQ,CAAC;IACZ,WAAA;;EADF,KAAK,EAAQ,CAAC;IACZ,mBAAA;;EADF,KAAK,EAAQ,CAAC;IACZ,mBAAA;;EADF,KAAK,EAAQ,CAAC;IACZ,UAAA;;EADF,KAAK,EAAQ,CAAC;IACZ,mBAAA;;EADF,KAAK,EAAQ,CAAC;IACZ,mBAAA;;EADF,KAAK,EAAQ,CAAC;IACZ,UAAA;;EADF,KAAK,EAAQ,CAAC;IACZ,mBAAA;;EADF,KAAK,EAAQ,CAAC;IACZ,mBAAA;;EADF,KAAK,EAAQ,CAAC;IACZ,UAAA;;EADF,KAAK,EAAQ,CAAC;IACZ,mBAAA;;EADF,KAAK,EAAQ,CAAC;IACZ,kBAAA;;EASF,KAAK,EAAQ,MAAM;IACjB,WAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,mBAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,mBAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,UAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,mBAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,mBAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,UAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,mBAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,mBAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,UAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,mBAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,kBAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,SAAA;;EANF,KAAK,EAAQ,MAAM;IACjB,UAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,kBAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,kBAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,SAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,kBAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,kBAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,SAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,kBAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,kBAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,SAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,kBAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,iBAAA;;EADF,KAAK,EAAQ,MAAM;IACjB,QAAA;;EASF,KAAK,EAAQ,QAAQ;IACnB,iBAAA;;EADF,KAAK,EAAQ,QAAQ;IACnB,yBAAA;;EADF,KAAK,EAAQ,QAAQ;IACnB,yBAAA;;EADF,KAAK,EAAQ,QAAQ;IACnB,gBAAA;;EADF,KAAK,EAAQ,QAAQ;IACnB,yBAAA;;EADF,KAAK,EAAQ,QAAQ;IACnB,yBAAA;;EADF,KAAK,EAAQ,QAAQ;IACnB,gBAAA;;EADF,KAAK,EAAQ,QAAQ;IACnB,yBAAA;;EADF,KAAK,EAAQ,QAAQ;IACnB,yBAAA;;EADF,KAAK,EAAQ,QAAQ;IACnB,gBAAA;;EADF,KAAK,EAAQ,QAAQ;IACnB,yBAAA;;EADF,KAAK,EAAQ,QAAQ;IACnB,wBAAA;;EADF,KAAK,EAAQ,QAAQ;IACnB,eAAA;;;AOtzBJ;EACE,eAAA;EACA,6BAAA;;AAEF;EACE,gBAAA;;AAMF;EACE,WAAA;EACA,mBAAA;;AAFF,MAIE,QAGE,KACE;AARN,MAKE,QAEE,KACE;AARN,MAME,QACE,KACE;AARN,MAIE,QAGE,KAEE;AATN,MAKE,QAEE,KAEE;AATN,MAME,QACE,KAEE;EACE,YAAA;EACA,uBAAA;EACA,mBAAA;EACA,6BAAA;;AAbR,MAkBE,QAAQ,KAAK;EACX,sBAAA;EACA,gCAAA;;AApBJ,MAuBE,UAAU,QAGR,KAAI,YACF;AA3BN,MAwBE,WAAW,QAET,KAAI,YACF;AA3BN,MAyBE,QAAO,YACL,KAAI,YACF;AA3BN,MAuBE,UAAU,QAGR,KAAI,YAEF;AA5BN,MAwBE,WAAW,QAET,KAAI,YAEF;AA5BN,MAyBE,QAAO,YACL,KAAI,YAEF;EACE,aAAA;;AA7BR,MAkCE,QAAQ;EACN,6BAAA;;AAnCJ,MAuCE;EACE,yBAAA;;AAOJ,gBACE,QAGE,KACE;AALN,gBAEE,QAEE,KACE;AALN,gBAGE,QACE,KACE;AALN,gBACE,QAGE,KAEE;AANN,gBAEE,QAEE,KAEE;AANN,gBAGE,QACE,KAEE;EACE,YAAA;;AAWR;EACE,yBAAA;;AADF,eAEE,QAGE,KACE;AANN,eAGE,QAEE,KACE;AANN,eAIE,QACE,KACE;AANN,eAEE,QAGE,KAEE;AAPN,eAGE,QAEE,KAEE;AAPN,eAIE,QACE,KAEE;EACE,yBAAA;;AARR,eAYE,QAAQ,KACN;AAbJ,eAYE,QAAQ,KAEN;EACE,wBAAA;;AAUN,cACE,QAAQ,KAAI,UAAU,KACpB;AAFJ,cACE,QAAQ,KAAI,UAAU,KAEpB;EACE,yBAAA;;AAUN,YACE,QAAQ,KAAI,MACV;AAFJ,YACE,QAAQ,KAAI,MAEV;EACE,yBAAA;;AAUN,KAAM,IAAG;EACP,gBAAA;EACA,WAAA;EACA,qBAAA;;AAKE,KAFF,GAEG;AAAD,KADF,GACG;EACC,gBAAA;EACA,WAAA;EACA,mBAAA;;AP0SJ,MAAO,QAAQ,KAGb,KAAI,CAAC;AAFP,MAAO,QAAQ,KAEb,KAAI,CAAC;AADP,MAAO,QAAQ,KACb,KAAI,CAAC;AAHP,MAAO,QAAQ,KAIb,KAAI,CAAC;AAHP,MAAO,QAAQ,KAGb,KAAI,CAAC;AAFP,MAAO,QAAQ,KAEb,KAAI,CAAC;AACL,MALK,QAAQ,KAKZ,CAAC,MAAS;AAAX,MAJK,QAAQ,KAIZ,CAAC,MAAS;AAAX,MAHK,QAAQ,KAGZ,CAAC,MAAS;AACX,MANK,QAAQ,KAMZ,CAAC,MAAS;AAAX,MALK,QAAQ,KAKZ,CAAC,MAAS;AAAX,MAJK,QAAQ,KAIZ,CAAC,MAAS;EACT,yBAAA;;AAMJ,YAAa,QAAQ,KACnB,KAAI,CAAC,MAAQ;AADf,YAAa,QAAQ,KAEnB,KAAI,CAAC,MAAQ;AACb,YAHW,QAAQ,KAGlB,CAAC,MAAQ,MAAO;AACjB,YAJW,QAAQ,KAIlB,CAAC,MAAQ,MAAO;EACf,yBAAA;;AAlBJ,MAAO,QAAQ,KAGb,KAAI,CAAC;AAFP,MAAO,QAAQ,KAEb,KAAI,CAAC;AADP,MAAO,QAAQ,KACb,KAAI,CAAC;AAHP,MAAO,QAAQ,KAIb,KAAI,CAAC;AAHP,MAAO,QAAQ,KAGb,KAAI,CAAC;AAFP,MAAO,QAAQ,KAEb,KAAI,CAAC;AACL,MALK,QAAQ,KAKZ,CAAC,OAAS;AAAX,MAJK,QAAQ,KAIZ,CAAC,OAAS;AAAX,MAHK,QAAQ,KAGZ,CAAC,OAAS;AACX,MANK,QAAQ,KAMZ,CAAC,OAAS;AAAX,MALK,QAAQ,KAKZ,CAAC,OAAS;AAAX,MAJK,QAAQ,KAIZ,CAAC,OAAS;EACT,yBAAA;;AAMJ,YAAa,QAAQ,KACnB,KAAI,CAAC,OAAQ;AADf,YAAa,QAAQ,KAEnB,KAAI,CAAC,OAAQ;AACb,YAHW,QAAQ,KAGlB,CAAC,OAAQ,MAAO;AACjB,YAJW,QAAQ,KAIlB,CAAC,OAAQ,MAAO;EACf,yBAAA;;AAlBJ,MAAO,QAAQ,KAGb,KAAI,CAAC;AAFP,MAAO,QAAQ,KAEb,KAAI,CAAC;AADP,MAAO,QAAQ,KACb,KAAI,CAAC;AAHP,MAAO,QAAQ,KAIb,KAAI,CAAC;AAHP,MAAO,QAAQ,KAGb,KAAI,CAAC;AAFP,MAAO,QAAQ,KAEb,KAAI,CAAC;AACL,MALK,QAAQ,KAKZ,CAAC,IAAS;AAAX,MAJK,QAAQ,KAIZ,CAAC,IAAS;AAAX,MAHK,QAAQ,KAGZ,CAAC,IAAS;AACX,MANK,QAAQ,KAMZ,CAAC,IAAS;AAAX,MALK,QAAQ,KAKZ,CAAC,IAAS;AAAX,MAJK,QAAQ,KAIZ,CAAC,IAAS;EACT,yBAAA;;AAMJ,YAAa,QAAQ,KACnB,KAAI,CAAC,IAAQ;AADf,YAAa,QAAQ,KAEnB,KAAI,CAAC,IAAQ;AACb,YAHW,QAAQ,KAGlB,CAAC,IAAQ,MAAO;AACjB,YAJW,QAAQ,KAIlB,CAAC,IAAQ,MAAO;EACf,yBAAA;;AAlBJ,MAAO,QAAQ,KAGb,KAAI,CAAC;AAFP,MAAO,QAAQ,KAEb,KAAI,CAAC;AADP,MAAO,QAAQ,KACb,KAAI,CAAC;AAHP,MAAO,QAAQ,KAIb,KAAI,CAAC;AAHP,MAAO,QAAQ,KAGb,KAAI,CAAC;AAFP,MAAO,QAAQ,KAEb,KAAI,CAAC;AACL,MALK,QAAQ,KAKZ,CAAC,OAAS;AAAX,MAJK,QAAQ,KAIZ,CAAC,OAAS;AAAX,MAHK,QAAQ,KAGZ,CAAC,OAAS;AACX,MANK,QAAQ,KAMZ,CAAC,OAAS;AAAX,MALK,QAAQ,KAKZ,CAAC,OAAS;AAAX,MAJK,QAAQ,KAIZ,CAAC,OAAS;EACT,yBAAA;;AAMJ,YAAa,QAAQ,KACnB,KAAI,CAAC,OAAQ;AADf,YAAa,QAAQ,KAEnB,KAAI,CAAC,OAAQ;AACb,YAHW,QAAQ,KAGlB,CAAC,OAAQ,MAAO;AACjB,YAJW,QAAQ,KAIlB,CAAC,OAAQ,MAAO;EACf,yBAAA;;AAlBJ,MAAO,QAAQ,KAGb,KAAI,CAAC;AAFP,MAAO,QAAQ,KAEb,KAAI,CAAC;AADP,MAAO,QAAQ,KACb,KAAI,CAAC;AAHP,MAAO,QAAQ,KAIb,KAAI,CAAC;AAHP,MAAO,QAAQ,KAGb,KAAI,CAAC;AAFP,MAAO,QAAQ,KAEb,KAAI,CAAC;AACL,MALK,QAAQ,KAKZ,CAAC,MAAS;AAAX,MAJK,QAAQ,KAIZ,CAAC,MAAS;AAAX,MAHK,QAAQ,KAGZ,CAAC,MAAS;AACX,MANK,QAAQ,KAMZ,CAAC,MAAS;AAAX,MALK,QAAQ,KAKZ,CAAC,MAAS;AAAX,MAJK,QAAQ,KAIZ,CAAC,MAAS;EACT,yBAAA;;AAMJ,YAAa,QAAQ,KACnB,KAAI,CAAC,MAAQ;AADf,YAAa,QAAQ,KAEnB,KAAI,CAAC,MAAQ;AACb,YAHW,QAAQ,KAGlB,CAAC,MAAQ,MAAO;AACjB,YAJW,QAAQ,KAIlB,CAAC,MAAQ,MAAO;EACf,yBAAA;;AOpON,QA/DmC;EACjC;IACE,WAAA;IACA,mBAAA;IACA,kBAAA;IACA,kBAAA;IACA,4CAAA;IACA,yBAAA;IACA,iCAAA;;EAPF,iBAUE;IACE,gBAAA;;EAXJ,iBAUE,SAIE,QAGE,KACE;EAlBR,iBAUE,SAKE,QAEE,KACE;EAlBR,iBAUE,SAME,QACE,KACE;EAlBR,iBAUE,SAIE,QAGE,KAEE;EAnBR,iBAUE,SAKE,QAEE,KAEE;EAnBR,iBAUE,SAME,QACE,KAEE;IACE,mBAAA;;EApBV,iBA2BE;IACE,SAAA;;EA5BJ,iBA2BE,kBAIE,QAGE,KACE,KAAI;EAnCZ,iBA2BE,kBAKE,QAEE,KACE,KAAI;EAnCZ,iBA2BE,kBAME,QACE,KACE,KAAI;EAnCZ,iBA2BE,kBAIE,QAGE,KAEE,KAAI;EApCZ,iBA2BE,kBAKE,QAEE,KAEE,KAAI;EApCZ,iBA2BE,kBAME,QACE,KAEE,KAAI;IACF,cAAA;;EArCV,iBA2BE,kBAIE,QAGE,KAKE,KAAI;EAvCZ,iBA2BE,kBAKE,QAEE,KAKE,KAAI;EAvCZ,iBA2BE,kBAME,QACE,KAKE,KAAI;EAvCZ,iBA2BE,kBAIE,QAGE,KAME,KAAI;EAxCZ,iBA2BE,kBAKE,QAEE,KAME,KAAI;EAxCZ,iBA2BE,kBAME,QACE,KAME,KAAI;IACF,eAAA;;EAzCV,iBA2BE,kBAsBE,QAEE,KAAI,WACF;EApDR,iBA2BE,kBAuBE,QACE,KAAI,WACF;EApDR,iBA2BE,kBAsBE,QAEE,KAAI,WAEF;EArDR,iBA2BE,kBAuBE,QACE,KAAI,WAEF;IACE,gBAAA;;;ACxNZ;EACE,UAAA;EACA,SAAA;EACA,SAAA;EAIA,YAAA;;AAGF;EACE,cAAA;EACA,WAAA;EACA,UAAA;EACA,mBAAA;EACA,eAAA;EACA,oBAAA;EACA,cAAA;EACA,SAAA;EACA,gCAAA;;AAGF;EACE,qBAAA;EACA,kBAAA;EACA,iBAAA;;AAWF,KAAK;ERsMH,8BAAA;EACG,2BAAA;EACK,sBAAA;;AQnMV,KAAK;AACL,KAAK;EACH,eAAA;EACA,kBAAA;;EACA,mBAAA;;AAIF,KAAK;EACH,cAAA;;AAIF,KAAK;EACH,cAAA;EACA,WAAA;;AAIF,MAAM;AACN,MAAM;EACJ,YAAA;;AAIF,KAAK,aAAa;AAClB,KAAK,cAAc;AACnB,KAAK,iBAAiB;ER7CpB,oBAAA;EAEA,0CAAA;EACA,oBAAA;;AQ+CF;EACE,cAAA;EACA,gBAAA;EACA,eAAA;EACA,uBAAA;EACA,cAAA;;AA0BF;EACE,cAAA;EACA,WAAA;EACA,YAAA;EACA,iBAAA;EACA,eAAA;EACA,uBAAA;EACA,cAAA;EACA,yBAAA;EACA,sBAAA;EACA,yBAAA;EACA,kBAAA;ERHA,wDAAA;EACQ,gDAAA;EAKR,8EAAA;EACQ,sEAAA;;AAmwBR,aAAC;EACC,qBAAA;EACA,UAAA;EA5wBF,sFAAA;EACQ,8EAAA;;AAlER,aAAC;EAA+B,cAAA;EACA,UAAA;;AAChC,aAAC;EAA+B,cAAA;;AAChC,aAAC;EAA+B,cAAA;;AQgFhC,aAAC;AACD,aAAC;AACD,QAAQ,UAAW;EACjB,mBAAA;EACA,yBAAA;EACA,UAAA;;AAIF,QAAQ;EACN,YAAA;;AAYJ,KAAK;EACH,wBAAA;;AASF,KAAK;EACH,iBAAA;;AASF;EACE,mBAAA;;AAQF;AACA;EACE,cAAA;EACA,gBAAA;EACA,gBAAA;EACA,mBAAA;EACA,kBAAA;;AANF,MAOE;AANF,SAME;EACE,eAAA;EACA,mBAAA;EACA,eAAA;;AAGJ,MAAO,MAAK;AACZ,aAAc,MAAK;AACnB,SAAU,MAAK;AACf,gBAAiB,MAAK;EACpB,WAAA;EACA,kBAAA;;AAEF,MAAO;AACP,SAAU;EACR,gBAAA;;AAIF;AACA;EACE,qBAAA;EACA,kBAAA;EACA,gBAAA;EACA,sBAAA;EACA,mBAAA;EACA,eAAA;;AAEF,aAAc;AACd,gBAAiB;EACf,aAAA;EACA,iBAAA;;AAYA,KANG,cAMF;AAAD,KALG,iBAKF;AAAD,MAAC;AAAD,aAAC;AAAD,SAAC;AAAD,gBAAC;AACD,QAAQ,UAAW,MAPhB;AAOH,QAAQ,UAAW,MANhB;AAMH,QAAQ,UAAW;AAAnB,QAAQ,UAAW;AAAnB,QAAQ,UAAW;AAAnB,QAAQ,UAAW;EACjB,mBAAA;;AAUJ;ERqpBE,YAAA;EACA,iBAAA;EACA,eAAA;EACA,gBAAA;EACA,kBAAA;;AAEA,MAAM;EACJ,YAAA;EACA,iBAAA;;AAGF,QAAQ;AACR,MAAM,UAAU;EACd,YAAA;;AQ9pBJ;ERipBE,YAAA;EACA,kBAAA;EACA,eAAA;EACA,iBAAA;EACA,kBAAA;;AAEA,MAAM;EACJ,YAAA;EACA,iBAAA;;AAGF,QAAQ;AACR,MAAM,UAAU;EACd,YAAA;;AQrpBJ;EAEE,kBAAA;;AAFF,aAKE;EACE,qBAAA;;AANJ,aAUE;EACE,kBAAA;EACA,SAAA;EACA,QAAA;EACA,cAAA;EACA,WAAA;EACA,YAAA;EACA,iBAAA;EACA,kBAAA;;AAKJ,YRsjBE;AQtjBF,YRujBE;AQvjBF,YRwjBE;AQxjBF,YRyjBE;AQzjBF,YR0jBE;AQ1jBF,YR2jBE;EACE,cAAA;;AQ5jBJ,YR+jBE;EACE,qBAAA;EAvuBF,wDAAA;EACQ,gDAAA;;AAwuBN,YAHF,cAGG;EACC,qBAAA;EA1uBJ,yEAAA;EACQ,iEAAA;;AQsKV,YRykBE;EACE,cAAA;EACA,qBAAA;EACA,yBAAA;;AQ5kBJ,YR+kBE;EACE,cAAA;;AQ7kBJ,YRmjBE;AQnjBF,YRojBE;AQpjBF,YRqjBE;AQrjBF,YRsjBE;AQtjBF,YRujBE;AQvjBF,YRwjBE;EACE,cAAA;;AQzjBJ,YR4jBE;EACE,qBAAA;EAvuBF,wDAAA;EACQ,gDAAA;;AAwuBN,YAHF,cAGG;EACC,qBAAA;EA1uBJ,yEAAA;EACQ,iEAAA;;AQyKV,YRskBE;EACE,cAAA;EACA,qBAAA;EACA,yBAAA;;AQzkBJ,YR4kBE;EACE,cAAA;;AQ1kBJ,URgjBE;AQhjBF,URijBE;AQjjBF,URkjBE;AQljBF,URmjBE;AQnjBF,URojBE;AQpjBF,URqjBE;EACE,cAAA;;AQtjBJ,URyjBE;EACE,qBAAA;EAvuBF,wDAAA;EACQ,gDAAA;;AAwuBN,UAHF,cAGG;EACC,qBAAA;EA1uBJ,yEAAA;EACQ,iEAAA;;AQ4KV,URmkBE;EACE,cAAA;EACA,qBAAA;EACA,yBAAA;;AQtkBJ,URykBE;EACE,cAAA;;AQhkBJ;EACE,gBAAA;;AASF;EACE,cAAA;EACA,eAAA;EACA,mBAAA;EACA,cAAA;;AAoEF,QAjDqC;EAiDrC,YA/CI;IACE,qBAAA;IACA,gBAAA;IACA,sBAAA;;EA4CN,YAxCI;IACE,qBAAA;IACA,WAAA;IACA,sBAAA;;EAqCN,YAlCI,aAAa;IACX,WAAA;;EAiCN,YA9BI;IACE,gBAAA;IACA,sBAAA;;EA4BN,YAtBI;EAsBJ,YArBI;IACE,qBAAA;IACA,aAAA;IACA,gBAAA;IACA,eAAA;IACA,sBAAA;;EAgBN,YAdI,OAAO,MAAK;EAchB,YAbI,UAAU,MAAK;IACb,WAAA;IACA,cAAA;;EAWN,YAJI,cAAc;IACZ,MAAA;;;AAWN,gBAGE;AAHF,gBAIE;AAJF,gBAKE;AALF,gBAME;AANF,gBAOE;EACE,aAAA;EACA,gBAAA;EACA,gBAAA;;AAVJ,gBAcE;AAdF,gBAeE;EACE,gBAAA;;AAhBJ,gBAoBE;ERyOA,kBAAA;EACA,mBAAA;;AQ9PF,gBAwBE;EACE,gBAAA;;AAUF,QANmC;EAMnC,gBALE;IACE,iBAAA;;;AA/BN,gBAuCE,cAAc;EACZ,MAAA;EACA,WAAA;;AC3aJ;EACE,qBAAA;EACA,gBAAA;EACA,mBAAA;EACA,kBAAA;EACA,sBAAA;EACA,eAAA;EACA,sBAAA;EACA,6BAAA;EACA,mBAAA;ET0gBA,iBAAA;EACA,eAAA;EACA,uBAAA;EACA,kBAAA;EAnSA,yBAAA;EACG,sBAAA;EACC,qBAAA;EACI,iBAAA;;AStON,IAAC;AAAD,IAFD,OAEE;AAAD,IADD,OACE;ETQH,oBAAA;EAEA,0CAAA;EACA,oBAAA;;ASNA,IAAC;AACD,IAAC;EACC,cAAA;EACA,qBAAA;;AAGF,IAAC;AACD,IAAC;EACC,UAAA;EACA,sBAAA;ETmFF,wDAAA;EACQ,gDAAA;;AShFR,IAAC;AACD,IAAC;AACD,QAAQ,UAAW;EACjB,mBAAA;EACA,oBAAA;ET+OF,aAAA;EAGA,yBAAA;EAvKA,wBAAA;EACQ,gBAAA;;ASlEV;ET2bE,cAAA;EACA,yBAAA;EACA,qBAAA;;AAEA,YAAC;AACD,YAAC;AACD,YAAC;AACD,YAAC;AACD,KAAM,iBAAgB;EACpB,cAAA;EACA,yBAAA;EACI,qBAAA;;AAEN,YAAC;AACD,YAAC;AACD,KAAM,iBAAgB;EACpB,sBAAA;;AAKA,YAHD;AAGC,YAFD;AAEC,QADM,UAAW;AAEjB,YAJD,SAIE;AAAD,YAHD,UAGE;AAAD,QAFM,UAAW,aAEhB;AACD,YALD,SAKE;AAAD,YAJD,UAIE;AAAD,QAHM,UAAW,aAGhB;AACD,YAND,SAME;AAAD,YALD,UAKE;AAAD,QAJM,UAAW,aAIhB;AACD,YAPD,SAOE;AAAD,YAND,UAME;AAAD,QALM,UAAW,aAKhB;EACC,yBAAA;EACI,qBAAA;;AStdV,YT0dE;EACE,cAAA;EACA,yBAAA;;ASzdJ;ETwbE,cAAA;EACA,yBAAA;EACA,qBAAA;;AAEA,YAAC;AACD,YAAC;AACD,YAAC;AACD,YAAC;AACD,KAAM,iBAAgB;EACpB,cAAA;EACA,yBAAA;EACI,qBAAA;;AAEN,YAAC;AACD,YAAC;AACD,KAAM,iBAAgB;EACpB,sBAAA;;AAKA,YAHD;AAGC,YAFD;AAEC,QADM,UAAW;AAEjB,YAJD,SAIE;AAAD,YAHD,UAGE;AAAD,QAFM,UAAW,aAEhB;AACD,YALD,SAKE;AAAD,YAJD,UAIE;AAAD,QAHM,UAAW,aAGhB;AACD,YAND,SAME;AAAD,YALD,UAKE;AAAD,QAJM,UAAW,aAIhB;AACD,YAPD,SAOE;AAAD,YAND,UAME;AAAD,QALM,UAAW,aAKhB;EACC,yBAAA;EACI,qBAAA;;ASndV,YTudE;EACE,cAAA;EACA,yBAAA;;ASrdJ;ETobE,cAAA;EACA,yBAAA;EACA,qBAAA;;AAEA,YAAC;AACD,YAAC;AACD,YAAC;AACD,YAAC;AACD,KAAM,iBAAgB;EACpB,cAAA;EACA,yBAAA;EACI,qBAAA;;AAEN,YAAC;AACD,YAAC;AACD,KAAM,iBAAgB;EACpB,sBAAA;;AAKA,YAHD;AAGC,YAFD;AAEC,QADM,UAAW;AAEjB,YAJD,SAIE;AAAD,YAHD,UAGE;AAAD,QAFM,UAAW,aAEhB;AACD,YALD,SAKE;AAAD,YAJD,UAIE;AAAD,QAHM,UAAW,aAGhB;AACD,YAND,SAME;AAAD,YALD,UAKE;AAAD,QAJM,UAAW,aAIhB;AACD,YAPD,SAOE;AAAD,YAND,UAME;AAAD,QALM,UAAW,aAKhB;EACC,yBAAA;EACI,qBAAA;;AS/cV,YTmdE;EACE,cAAA;EACA,yBAAA;;ASjdJ;ETgbE,cAAA;EACA,yBAAA;EACA,qBAAA;;AAEA,SAAC;AACD,SAAC;AACD,SAAC;AACD,SAAC;AACD,KAAM,iBAAgB;EACpB,cAAA;EACA,yBAAA;EACI,qBAAA;;AAEN,SAAC;AACD,SAAC;AACD,KAAM,iBAAgB;EACpB,sBAAA;;AAKA,SAHD;AAGC,SAFD;AAEC,QADM,UAAW;AAEjB,SAJD,SAIE;AAAD,SAHD,UAGE;AAAD,QAFM,UAAW,UAEhB;AACD,SALD,SAKE;AAAD,SAJD,UAIE;AAAD,QAHM,UAAW,UAGhB;AACD,SAND,SAME;AAAD,SALD,UAKE;AAAD,QAJM,UAAW,UAIhB;AACD,SAPD,SAOE;AAAD,SAND,UAME;AAAD,QALM,UAAW,UAKhB;EACC,yBAAA;EACI,qBAAA;;AS3cV,ST+cE;EACE,cAAA;EACA,yBAAA;;AS7cJ;ET4aE,cAAA;EACA,yBAAA;EACA,qBAAA;;AAEA,YAAC;AACD,YAAC;AACD,YAAC;AACD,YAAC;AACD,KAAM,iBAAgB;EACpB,cAAA;EACA,yBAAA;EACI,qBAAA;;AAEN,YAAC;AACD,YAAC;AACD,KAAM,iBAAgB;EACpB,sBAAA;;AAKA,YAHD;AAGC,YAFD;AAEC,QADM,UAAW;AAEjB,YAJD,SAIE;AAAD,YAHD,UAGE;AAAD,QAFM,UAAW,aAEhB;AACD,YALD,SAKE;AAAD,YAJD,UAIE;AAAD,QAHM,UAAW,aAGhB;AACD,YAND,SAME;AAAD,YALD,UAKE;AAAD,QAJM,UAAW,aAIhB;AACD,YAPD,SAOE;AAAD,YAND,UAME;AAAD,QALM,UAAW,aAKhB;EACC,yBAAA;EACI,qBAAA;;ASvcV,YT2cE;EACE,cAAA;EACA,yBAAA;;ASzcJ;ETwaE,cAAA;EACA,yBAAA;EACA,qBAAA;;AAEA,WAAC;AACD,WAAC;AACD,WAAC;AACD,WAAC;AACD,KAAM,iBAAgB;EACpB,cAAA;EACA,yBAAA;EACI,qBAAA;;AAEN,WAAC;AACD,WAAC;AACD,KAAM,iBAAgB;EACpB,sBAAA;;AAKA,WAHD;AAGC,WAFD;AAEC,QADM,UAAW;AAEjB,WAJD,SAIE;AAAD,WAHD,UAGE;AAAD,QAFM,UAAW,YAEhB;AACD,WALD,SAKE;AAAD,WAJD,UAIE;AAAD,QAHM,UAAW,YAGhB;AACD,WAND,SAME;AAAD,WALD,UAKE;AAAD,QAJM,UAAW,YAIhB;AACD,WAPD,SAOE;AAAD,WAND,UAME;AAAD,QALM,UAAW,YAKhB;EACC,yBAAA;EACI,qBAAA;;ASncV,WTucE;EACE,cAAA;EACA,yBAAA;;AShcJ;EACE,cAAA;EACA,mBAAA;EACA,eAAA;EACA,gBAAA;;AAEA;AACA,SAAC;AACD,SAAC;AACD,QAAQ,UAAW;EACjB,6BAAA;ET2BF,wBAAA;EACQ,gBAAA;;ASzBR;AACA,SAAC;AACD,SAAC;AACD,SAAC;EACC,yBAAA;;AAEF,SAAC;AACD,SAAC;EACC,cAAA;EACA,0BAAA;EACA,6BAAA;;AAIA,SAFD,UAEE;AAAD,QADM,UAAW,UAChB;AACD,SAHD,UAGE;AAAD,QAFM,UAAW,UAEhB;EACC,cAAA;EACA,qBAAA;;AASN;ACvBA,aAAc;EVubZ,kBAAA;EACA,eAAA;EACA,iBAAA;EACA,kBAAA;;AS/ZF;AC5BA,aAAc;EVwbZ,iBAAA;EACA,eAAA;EACA,gBAAA;EACA,kBAAA;;AS3ZF;ACjCA,aAAc;EVybZ,gBAAA;EACA,eAAA;EACA,gBAAA;EACA,kBAAA;;ASnZF;EACE,cAAA;EACA,WAAA;EACA,eAAA;EACA,gBAAA;;AAIF,UAAW;EACT,eAAA;;AAOA,KAHG,eAGF;AAAD,KAFG,cAEF;AAAD,KADG,eACF;EACC,WAAA;;AEnJJ;EACE,UAAA;EXqHA,wCAAA;EACQ,gCAAA;;AWpHR,KAAC;EACC,UAAA;;AAIJ;EACE,aAAA;;AACA,SAAC;EACC,cAAA;;AAGJ;EACE,kBAAA;EACA,SAAA;EACA,gBAAA;EXqGA,qCAAA;EACQ,6BAAA;;AYtHV;EACE,aAAa,sBAAb;EACA,qDAAA;EACA,2TAAA;;AAOF;EACE,kBAAA;EACA,QAAA;EACA,qBAAA;EACA,aAAa,sBAAb;EACA,kBAAA;EACA,mBAAA;EACA,cAAA;EACA,mCAAA;EACA,kCAAA;;AAIkC,mBAAC;EAAU,SAAS,KAAT;;AACX,eAAC;EAAU,SAAS,KAAT;;AACX,eAAC;EAAU,SAAS,OAAT;;AACX,gBAAC;EAAU,SAAS,OAAT;;AACX,gBAAC;EAAU,SAAS,OAAT;;AACX,mBAAC;EAAU,SAAS,OAAT;;AACX,iBAAC;EAAU,SAAS,OAAT;;AACX,gBAAC;EAAU,SAAS,OAAT;;AACX,gBAAC;EAAU,SAAS,OAAT;;AACX,iBAAC;EAAU,SAAS,OAAT;;AACX,gBAAC;EAAU,SAAS,OAAT;;AACX,eAAC;EAAU,SAAS,OAAT;;AACX,qBAAC;EAAU,SAAS,OAAT;;AACX,eAAC;EAAU,SAAS,OAAT;;AACX,eAAC;EAAU,SAAS,OAAT;;AACX,mBAAC;EAAU,SAAS,OAAT;;AACX,aAAC;EAAU,SAAS,OAAT;;AACX,kBAAC;EAAU,SAAS,OAAT;;AACX,aAAC;EAAU,SAAS,OAAT;;AACX,iBAAC;EAAU,SAAS,OAAT;;AACX,kBAAC;EAAU,SAAS,OAAT;;AACX,mBAAC;EAAU,SAAS,OAAT;;AACX,cAAC;EAAU,SAAS,OAAT;;AACX,iBAAC;EAAU,SAAS,OAAT;;AACX,cAAC;EAAU,SAAS,OAAT;;AACX,gBAAC;EAAU,SAAS,OAAT;;AACX,eAAC;EAAU,SAAS,OAAT;;AACX,eAAC;EAAU,SAAS,OAAT;;AACX,eAAC;EAAU,SAAS,OAAT;;AACX,eAAC;EAAU,SAAS,OAAT;;AACX,uBAAC;EAAU,SAAS,OAAT;;AACX,mBAAC;EAAU,SAAS,OAAT;;AACX,iBAAC;EAAU,SAAS,OAAT;;AACX,gBAAC;EAAU,SAAS,OAAT;;AACX,sBAAC;EAAU,SAAS,OAAT;;AACX,iBAAC;EAAU,SAAS,OAAT;;AACX,kBAAC;EAAU,SAAS,OAAT;;AACX,mBAAC;EAAU,SAAS,OAAT;;AACX,eAAC;EAAU,SAAS,OAAT;;AACX,eAAC;EAAU,SAAS,OAAT;;AACX,qBAAC;EAAU,SAAS,OAAT;;AACX,qBAAC;EAAU,SAAS,OAAT;;AACX,sBAAC;EAAU,SAAS,OAAT;;AACX,oBAAC;EAAU,SAAS,OAAT;;AACX,iBAAC;EAAU,SAAS,OAAT;;AACX,kBAAC;EAAU,SAAS,OAAT;;AACX,cAAC;EAAU,SAAS,OAAT;;AACX,eAAC;EAAU,SAAS,OAAT;;AACX,eAAC;EAAU,SAAS,OAAT;;AACX,mBAAC;EAAU,SAAS,OAAT;;AACX,gBAAC;EAAU,SAAS,OAAT;;AACX,iBAAC;EAAU,SAAS,OAAT;;AACX,eAAC;EAAU,SAAS,OAAT;;AACX,eAAC;EAAU,SAAS,OAAT;;AACX,iBAAC;EAAU,SAAS,OAAT;;AACX,sBAAC;EAAU,SAAS,OAAT;;AACX,qBAAC;EAAU,SAAS,OAAT;;AACX,qBAAC;EAAU,SAAS,OAAT;;AACX,uBAAC;EAAU,SAAS,OAAT;;AACX,sBAAC;EAAU,SAAS,OAAT;;AACX,wBAAC;EAAU,SAAS,OAAT;;AACX,eAAC;EAAU,SAAS,OAAT;;AACX,sBAAC;EAAU,SAAS,OAAT;;AACX,uBAAC;EAAU,SAAS,OAAT;;AACX,yBAAC;EAAU,SAAS,OAAT;;AACX,kBAAC;EAAU,SAAS,OAAT;;AACX,qBAAC;EAAU,SAAS,OAAT;;AACX,iBAAC;EAAU,SAAS,OAAT;;AACX,eAAC;EAAU,SAAS,OAAT;;AACX,eAAC;EAAU,SAAS,OAAT;;AACX,gBAAC;EAAU,SAAS,OAAT;;AACX,gBAAC;EAAU,SAAS,OAAT;;AACX,eAAC;EAAU,SAAS,OAAT;;AACX,wBAAC;EAAU,SAAS,OAAT;;AACX,wBAAC;EAAU,SAAS,OAAT;;AACX,mBAAC;EAAU,SAAS,OAAT;;AACX,eAAC;EAAU,SAAS,OAAT;;AACX,gBAAC;EAAU,SAAS,OAAT;;AACX,eAAC;EAAU,SAAS,OAAT;;AACX,kBAAC;EAAU,SAAS,OAAT;;AACX,uBAAC;EAAU,SAAS,OAAT;;AACX,uBAAC;EAAU,SAAS,OAAT;;AACX,gBAAC;EAAU,SAAS,OAAT;;AACX,uBAAC;EAAU,SAAS,OAAT;;AACX,wBAAC;EAAU,SAAS,OAAT;;AACX,oBAAC;EAAU,SAAS,OAAT;;AACX,qBAAC;EAAU,SAAS,OAAT;;AACX,sBAAC;EAAU,SAAS,OAAT;;AACX,kBAAC;EAAU,SAAS,OAAT;;AACX,wBAAC;EAAU,SAAS,OAAT;;AACX,oBAAC;EAAU,SAAS,OAAT;;AACX,qBAAC;EAAU,SAAS,OAAT;;AACX,wBAAC;EAAU,SAAS,OAAT;;AACX,oBAAC;EAAU,SAAS,OAAT;;AACX,qBAAC;EAAU,SAAS,OAAT;;AACX,qBAAC;EAAU,SAAS,OAAT;;AACX,sBAAC;EAAU,SAAS,OAAT;;AACX,mBAAC;EAAU,SAAS,OAAT;;AACX,qBAAC;EAAU,SAAS,OAAT;;AACX,oBAAC;EAAU,SAAS,OAAT;;AACX,sBAAC;EAAU,SAAS,OAAT;;AACX,uBAAC;EAAU,SAAS,OAAT;;AACX,2BAAC;EAAU,SAAS,OAAT;;AACX,eAAC;EAAU,SAAS,OAAT;;AACX,eAAC;EAAU,SAAS,OAAT;;AACX,eAAC;EAAU,SAAS,OAAT;;AACX,mBAAC;EAAU,SAAS,OAAT;;AACX,oBAAC;EAAU,SAAS,OAAT;;AACX,uBAAC;EAAU,SAAS,OAAT;;AACX,gBAAC;EAAU,SAAS,OAAT;;AACX,mBAAC;EAAU,SAAS,OAAT;;AACX,iBAAC;EAAU,SAAS,OAAT;;AACX,kBAAC;EAAU,SAAS,OAAT;;AACX,iBAAC;EAAU,SAAS,OAAT;;AACX,qBAAC;EAAU,SAAS,OAAT;;AACX,uBAAC;EAAU,SAAS,OAAT;;AACX,kBAAC;EAAU,SAAS,OAAT;;AACX,wBAAC;EAAU,SAAS,OAAT;;AACX,uBAAC;EAAU,SAAS,OAAT;;AACX,sBAAC;EAAU,SAAS,OAAT;;AACX,0BAAC;EAAU,SAAS,OAAT;;AACX,4BAAC;EAAU,SAAS,OAAT;;AACX,cAAC;EAAU,SAAS,OAAT;;AACX,mBAAC;EAAU,SAAS,OAAT;;AACX,eAAC;EAAU,SAAS,OAAT;;AACX,sBAAC;EAAU,SAAS,OAAT;;AACX,oBAAC;EAAU,SAAS,OAAT;;AACX,sBAAC;EAAU,SAAS,OAAT;;AACX,qBAAC;EAAU,SAAS,OAAT;;AACX,oBAAC;EAAU,SAAS,OAAT;;AACX,kBAAC;EAAU,SAAS,OAAT;;AACX,oBAAC;EAAU,SAAS,OAAT;;AACX,6BAAC;EAAU,SAAS,OAAT;;AACX,4BAAC;EAAU,SAAS,OAAT;;AACX,0BAAC;EAAU,SAAS,OAAT;;AACX,4BAAC;EAAU,SAAS,OAAT;;AACX,gBAAC;EAAU,SAAS,OAAT;;AACX,iBAAC;EAAU,SAAS,OAAT;;AACX,gBAAC;EAAU,SAAS,OAAT;;AACX,iBAAC;EAAU,SAAS,OAAT;;AACX,oBAAC;EAAU,SAAS,OAAT;;AACX,qBAAC;EAAU,SAAS,OAAT;;AACX,oBAAC;EAAU,SAAS,OAAT;;AACX,oBAAC;EAAU,SAAS,OAAT;;AACX,sBAAC;EAAU,SAAS,OAAT;;AACX,eAAC;EAAU,SAAS,OAAT;;AACX,gBAAC;EAAU,SAAS,OAAT;;AACX,kBAAC;EAAU,SAAS,OAAT;;AACX,cAAC;EAAU,SAAS,OAAT;;AACX,cAAC;EAAU,SAAS,OAAT;;AACX,eAAC;EAAU,SAAS,OAAT;;AACX,2BAAC;EAAU,SAAS,OAAT;;AACX,+BAAC;EAAU,SAAS,OAAT;;AACX,wBAAC;EAAU,SAAS,OAAT;;AACX,4BAAC;EAAU,SAAS,OAAT;;AACX,6BAAC;EAAU,SAAS,OAAT;;AACX,iCAAC;EAAU,SAAS,OAAT;;AACX,oBAAC;EAAU,SAAS,OAAT;;AACX,iBAAC;EAAU,SAAS,OAAT;;AACX,wBAAC;EAAU,SAAS,OAAT;;AACX,sBAAC;EAAU,SAAS,OAAT;;AACX,iBAAC;EAAU,SAAS,OAAT;;AACX,gBAAC;EAAU,SAAS,OAAT;;AACX,kBAAC;EAAU,SAAS,OAAT;;AACX,qBAAC;EAAU,SAAS,OAAT;;AACX,iBAAC;EAAU,SAAS,OAAT;;AACX,eAAC;EAAU,SAAS,OAAT;;AACX,eAAC;EAAU,SAAS,OAAT;;AACX,gBAAC;EAAU,SAAS,OAAT;;AACX,iBAAC;EAAU,SAAS,OAAT;;AACX,iBAAC;EAAU,SAAS,OAAT;;AACX,eAAC;EAAU,SAAS,OAAT;;AACX,sBAAC;EAAU,SAAS,OAAT;;AACX,uBAAC;EAAU,SAAS,OAAT;;AACX,wBAAC;EAAU,SAAS,OAAT;;AACX,sBAAC;EAAU,SAAS,OAAT;;AACX,sBAAC;EAAU,SAAS,OAAT;;AACX,sBAAC;EAAU,SAAS,OAAT;;AACX,mBAAC;EAAU,SAAS,OAAT;;AACX,kBAAC;EAAU,SAAS,OAAT;;AACX,iBAAC;EAAU,SAAS,OAAT;;AACX,qBAAC;EAAU,SAAS,OAAT;;AACX,mBAAC;EAAU,SAAS,OAAT;;AACX,oBAAC;EAAU,SAAS,OAAT;;AACX,gBAAC;EAAU,SAAS,OAAT;;AACX,gBAAC;EAAU,SAAS,OAAT;;AACX,mBAAC;EAAU,SAAS,OAAT;;AACX,mBAAC;EAAU,SAAS,OAAT;;AACX,oBAAC;EAAU,SAAS,OAAT;;AACX,uBAAC;EAAU,SAAS,OAAT;;AACX,sBAAC;EAAU,SAAS,OAAT;;AACX,oBAAC;EAAU,SAAS,OAAT;;AACX,oBAAC;EAAU,SAAS,OAAT;;AACX,oBAAC;EAAU,SAAS,OAAT;;AACX,yBAAC;EAAU,SAAS,OAAT;;AACX,4BAAC;EAAU,SAAS,OAAT;;AACX,yBAAC;EAAU,SAAS,OAAT;;AACX,uBAAC;EAAU,SAAS,OAAT;;AACX,uBAAC;EAAU,SAAS,OAAT;;AACX,yBAAC;EAAU,SAAS,OAAT;;AClO/C;EACE,qBAAA;EACA,QAAA;EACA,SAAA;EACA,gBAAA;EACA,sBAAA;EACA,qBAAA;EACA,mCAAA;EACA,kCAAA;;AAIF;EACE,kBAAA;;AAIF,gBAAgB;EACd,UAAA;;AAIF;EACE,kBAAA;EACA,SAAA;EACA,OAAA;EACA,aAAA;EACA,aAAA;EACA,WAAA;EACA,gBAAA;EACA,cAAA;EACA,eAAA;EACA,gBAAA;EACA,eAAA;EACA,yBAAA;EACA,yBAAA;EACA,qCAAA;EACA,kBAAA;Eb8EA,mDAAA;EACQ,2CAAA;Ea7ER,4BAAA;;AAKA,cAAC;EACC,QAAA;EACA,UAAA;;AAxBJ,cA4BE;EboVA,WAAA;EACA,aAAA;EACA,gBAAA;EACA,yBAAA;;AanXF,cAiCE,KAAK;EACH,cAAA;EACA,iBAAA;EACA,WAAA;EACA,mBAAA;EACA,uBAAA;EACA,cAAA;EACA,mBAAA;;AAMF,cADa,KAAK,IACjB;AACD,cAFa,KAAK,IAEjB;EACC,qBAAA;EACA,cAAA;EACA,yBAAA;;AAMF,cADa,UAAU;AAEvB,cAFa,UAAU,IAEtB;AACD,cAHa,UAAU,IAGtB;EACC,cAAA;EACA,qBAAA;EACA,UAAA;EACA,yBAAA;;AASF,cADa,YAAY;AAEzB,cAFa,YAAY,IAExB;AACD,cAHa,YAAY,IAGxB;EACC,cAAA;;AAKF,cADa,YAAY,IACxB;AACD,cAFa,YAAY,IAExB;EACC,qBAAA;EACA,6BAAA;EACA,sBAAA;EbkPF,mEAAA;EahPE,mBAAA;;AAKJ,KAEE;EACE,cAAA;;AAHJ,KAOE;EACE,UAAA;;AAQJ;EACE,UAAA;EACA,QAAA;;AAQF;EACE,OAAA;EACA,WAAA;;AAIF;EACE,cAAA;EACA,iBAAA;EACA,eAAA;EACA,uBAAA;EACA,cAAA;;AAIF;EACE,eAAA;EACA,OAAA;EACA,QAAA;EACA,SAAA;EACA,MAAA;EACA,YAAA;;AAIF,WAAY;EACV,QAAA;EACA,UAAA;;AAQF,OAGE;AAFF,oBAAqB,UAEnB;EACE,aAAA;EACA,wBAAA;EACA,SAAS,EAAT;;AANJ,OASE;AARF,oBAAqB,UAQnB;EACE,SAAA;EACA,YAAA;EACA,kBAAA;;AAsBJ,QAb2C;EACzC,aACE;IAnEF,UAAA;IACA,QAAA;;EAiEA,aAME;IA9DF,OAAA;IACA,WAAA;;;AH7IF;AACA;EACE,kBAAA;EACA,qBAAA;EACA,sBAAA;;AAJF,UAKE;AAJF,mBAIE;EACE,kBAAA;EACA,WAAA;;AAEA,UAJF,OAIG;AAAD,mBAJF,OAIG;AACD,UALF,OAKG;AAAD,mBALF,OAKG;AACD,UANF,OAMG;AAAD,mBANF,OAMG;AACD,UAPF,OAOG;AAAD,mBAPF,OAOG;EACC,UAAA;;AAEF,UAVF,OAUG;AAAD,mBAVF,OAUG;EAEC,aAAA;;AAMN,UACE,KAAK;AADP,UAEE,KAAK;AAFP,UAGE,WAAW;AAHb,UAIE,WAAW;EACT,iBAAA;;AAKJ;EACE,iBAAA;;AADF,YAIE;AAJF,YAKE;EACE,WAAA;;AANJ,YAQE;AARF,YASE;AATF,YAUE;EACE,gBAAA;;AAIJ,UAAW,OAAM,IAAI,cAAc,IAAI,aAAa,IAAI;EACtD,gBAAA;;AAIF,UAAW,OAAM;EACf,cAAA;;AACA,UAFS,OAAM,YAEd,IAAI,aAAa,IAAI;EV2CtB,6BAAA;EACG,0BAAA;;AUvCL,UAAW,OAAM,WAAW,IAAI;AAChC,UAAW,mBAAkB,IAAI;EV6C/B,4BAAA;EACG,yBAAA;;AUzCL,UAAW;EACT,WAAA;;AAEF,UAAW,aAAY,IAAI,cAAc,IAAI,aAAc;EACzD,gBAAA;;AAEF,UAAW,aAAY,YACrB,OAAM;AADR,UAAW,aAAY,YAErB;EVwBA,6BAAA;EACG,0BAAA;;AUrBL,UAAW,aAAY,WAAY,OAAM;EV4BvC,4BAAA;EACG,yBAAA;;AUxBL,UAAW,iBAAgB;AAC3B,UAAU,KAAM;EACd,UAAA;;AAiBF,UAAW,OAAO;EAChB,iBAAA;EACA,kBAAA;;AAEF,UAAW,UAAU;EACnB,kBAAA;EACA,mBAAA;;AAKF,UAAU,KAAM;EVGd,wDAAA;EACQ,gDAAA;;AUAR,UAJQ,KAAM,iBAIb;EVDD,wBAAA;EACQ,gBAAA;;AUOV,IAAK;EACH,cAAA;;AAGF,OAAQ;EACN,uBAAA;EACA,sBAAA;;AAGF,OAAQ,QAAQ;EACd,uBAAA;;AAOF,mBACE;AADF,mBAEE;AAFF,mBAGE,aAAa;EACX,cAAA;EACA,WAAA;EACA,WAAA;EACA,eAAA;;AAPJ,mBAWE,aAEE;EACE,WAAA;;AAdN,mBAkBE,OAAO;AAlBT,mBAmBE,OAAO;AAnBT,mBAoBE,aAAa;AApBf,mBAqBE,aAAa;EACX,gBAAA;EACA,cAAA;;AAKF,mBADkB,OACjB,IAAI,cAAc,IAAI;EACrB,gBAAA;;AAEF,mBAJkB,OAIjB,YAAY,IAAI;EACf,4BAAA;EVvEF,6BAAA;EACC,4BAAA;;AUyED,mBARkB,OAQjB,WAAW,IAAI;EACd,8BAAA;EVnFF,0BAAA;EACC,yBAAA;;AUsFH,mBAAoB,aAAY,IAAI,cAAc,IAAI,aAAc;EAClE,gBAAA;;AAEF,mBAAoB,aAAY,YAAY,IAAI,aAC9C,OAAM;AADR,mBAAoB,aAAY,YAAY,IAAI,aAE9C;EVpFA,6BAAA;EACC,4BAAA;;AUuFH,mBAAoB,aAAY,WAAW,IAAI,cAAe,OAAM;EVhGlE,0BAAA;EACC,yBAAA;;AUwGH;EACE,cAAA;EACA,WAAA;EACA,mBAAA;EACA,yBAAA;;AAJF,oBAKE;AALF,oBAME;EACE,WAAA;EACA,mBAAA;EACA,SAAA;;AATJ,oBAWE,aAAa;EACX,WAAA;;AAMJ,uBAAwB,OAAO,QAAO;AACtC,uBAAwB,OAAO,QAAO;EACpC,aAAA;;AI1NF;EACE,kBAAA;EACA,cAAA;EACA,yBAAA;;AAGA,YAAC;EACC,WAAA;EACA,eAAA;EACA,gBAAA;;AATJ,YAYE;EAGE,kBAAA;EACA,UAAA;EAKA,WAAA;EAEA,WAAA;EACA,gBAAA;;AASJ,eAAgB;AAChB,eAAgB;AAChB,eAAgB,mBAAmB;Edw2BjC,YAAA;EACA,kBAAA;EACA,eAAA;EACA,iBAAA;EACA,kBAAA;;AAEA,MAAM,ech3BQ;Adg3Bd,MAAM,ec/2BQ;Ad+2Bd,MAAM,ec92BQ,mBAAmB;Ed+2B/B,YAAA;EACA,iBAAA;;AAGF,QAAQ,ecr3BM;Adq3Bd,QAAQ,ecp3BM;Ado3Bd,QAAQ,ecn3BM,mBAAmB;Ado3BjC,MAAM,UAAU,ect3BF;Ads3Bd,MAAM,UAAU,ecr3BF;Adq3Bd,MAAM,UAAU,ecp3BF,mBAAmB;Edq3B/B,YAAA;;Acp3BJ,eAAgB;AAChB,eAAgB;AAChB,eAAgB,mBAAmB;Edq2BjC,YAAA;EACA,iBAAA;EACA,eAAA;EACA,gBAAA;EACA,kBAAA;;AAEA,MAAM,ec72BQ;Ad62Bd,MAAM,ec52BQ;Ad42Bd,MAAM,ec32BQ,mBAAmB;Ed42B/B,YAAA;EACA,iBAAA;;AAGF,QAAQ,ecl3BM;Adk3Bd,QAAQ,ecj3BM;Adi3Bd,QAAQ,ech3BM,mBAAmB;Adi3BjC,MAAM,UAAU,ecn3BF;Adm3Bd,MAAM,UAAU,ecl3BF;Adk3Bd,MAAM,UAAU,ecj3BF,mBAAmB;Edk3B/B,YAAA;;Ac72BJ;AACA;AACA,YAAa;EACX,mBAAA;;AAEA,kBAAC,IAAI,cAAc,IAAI;AAAvB,gBAAC,IAAI,cAAc,IAAI;AAAvB,YAHW,cAGV,IAAI,cAAc,IAAI;EACrB,gBAAA;;AAIJ;AACA;EACE,SAAA;EACA,mBAAA;EACA,sBAAA;;AAKF;EACE,iBAAA;EACA,eAAA;EACA,mBAAA;EACA,cAAA;EACA,cAAA;EACA,kBAAA;EACA,yBAAA;EACA,yBAAA;EACA,kBAAA;;AAGA,kBAAC;EACC,iBAAA;EACA,eAAA;EACA,kBAAA;;AAEF,kBAAC;EACC,kBAAA;EACA,eAAA;EACA,kBAAA;;AApBJ,kBAwBE,MAAK;AAxBP,kBAyBE,MAAK;EACH,aAAA;;AAKJ,YAAa,cAAa;AAC1B,kBAAkB;AAClB,gBAAgB,YAAa;AAC7B,gBAAgB,YAAa,aAAa;AAC1C,gBAAgB,YAAa;AAC7B,gBAAgB,WAAY,OAAM,IAAI,aAAa,IAAI;AACvD,gBAAgB,WAAY,aAAY,IAAI,aAAc;EdFxD,6BAAA;EACG,0BAAA;;AcIL,kBAAkB;EAChB,eAAA;;AAEF,YAAa,cAAa;AAC1B,kBAAkB;AAClB,gBAAgB,WAAY;AAC5B,gBAAgB,WAAY,aAAa;AACzC,gBAAgB,WAAY;AAC5B,gBAAgB,YAAa,OAAM,IAAI;AACvC,gBAAgB,YAAa,aAAY,IAAI,cAAe;EdN1D,4BAAA;EACG,yBAAA;;AcQL,kBAAkB;EAChB,cAAA;;AAKF;EACE,kBAAA;EAGA,YAAA;EACA,mBAAA;;AALF,gBASE;EACE,kBAAA;;AAVJ,gBASE,OAEE;EACE,iBAAA;;AAGF,gBANF,OAMG;AACD,gBAPF,OAOG;AACD,gBARF,OAQG;EACC,UAAA;;AAKJ,gBAAC,YACC;AADF,gBAAC,YAEC;EACE,kBAAA;;AAGJ,gBAAC,WACC;AADF,gBAAC,WAEC;EACE,iBAAA;;ACtJN;EACE,gBAAA;EACA,eAAA;EACA,gBAAA;;AAHF,IAME;EACE,kBAAA;EACA,cAAA;;AARJ,IAME,KAIE;EACE,kBAAA;EACA,cAAA;EACA,kBAAA;;AACA,IARJ,KAIE,IAIG;AACD,IATJ,KAIE,IAKG;EACC,qBAAA;EACA,yBAAA;;AAKJ,IAhBF,KAgBG,SAAU;EACT,cAAA;;AAEA,IAnBJ,KAgBG,SAAU,IAGR;AACD,IApBJ,KAgBG,SAAU,IAIR;EACC,cAAA;EACA,qBAAA;EACA,6BAAA;EACA,mBAAA;;AAOJ,IADF,MAAM;AAEJ,IAFF,MAAM,IAEH;AACD,IAHF,MAAM,IAGH;EACC,yBAAA;EACA,qBAAA;;AAzCN,IAkDE;EfkVA,WAAA;EACA,aAAA;EACA,gBAAA;EACA,yBAAA;;AevYF,IAyDE,KAAK,IAAI;EACP,eAAA;;AASJ;EACE,gCAAA;;AADF,SAEE;EACE,WAAA;EAEA,mBAAA;;AALJ,SAEE,KAME;EACE,iBAAA;EACA,uBAAA;EACA,6BAAA;EACA,0BAAA;;AACA,SAXJ,KAME,IAKG;EACC,qCAAA;;AAMF,SAlBJ,KAiBG,OAAQ;AAEP,SAnBJ,KAiBG,OAAQ,IAEN;AACD,SApBJ,KAiBG,OAAQ,IAGN;EACC,cAAA;EACA,yBAAA;EACA,yBAAA;EACA,gCAAA;EACA,eAAA;;AAKN,SAAC;EAqDD,WAAA;EA8BA,gBAAA;;AAnFA,SAAC,cAuDD;EACE,WAAA;;AAxDF,SAAC,cAuDD,KAEG;EACC,kBAAA;EACA,kBAAA;;AA3DJ,SAAC,cA+DD,YAAY;EACV,SAAA;EACA,UAAA;;AAYJ,QATqC;EASrC,SA7EG,cAqEC;IACE,mBAAA;IACA,SAAA;;EAMN,SA7EG,cAqEC,KAGE;IACE,gBAAA;;;AAzEN,SAAC,cAqFD,KAAK;EAEH,eAAA;EACA,kBAAA;;AAxFF,SAAC,cA2FD,UAAU;AA3FV,SAAC,cA4FD,UAAU,IAAG;AA5Fb,SAAC,cA6FD,UAAU,IAAG;EACX,yBAAA;;AAcJ,QAXqC;EAWrC,SA5GG,cAkGC,KAAK;IACH,gCAAA;IACA,0BAAA;;EAQN,SA5GG,cAsGC,UAAU;EAMd,SA5GG,cAuGC,UAAU,IAAG;EAKjB,SA5GG,cAwGC,UAAU,IAAG;IACX,4BAAA;;;AAhGN,UACE;EACE,WAAA;;AAFJ,UACE,KAIE;EACE,kBAAA;;AANN,UACE,KAOE;EACE,gBAAA;;AAKA,UAbJ,KAYG,OAAQ;AAEP,UAdJ,KAYG,OAAQ,IAEN;AACD,UAfJ,KAYG,OAAQ,IAGN;EACC,cAAA;EACA,yBAAA;;AAQR,YACE;EACE,WAAA;;AAFJ,YACE,KAEE;EACE,eAAA;EACA,cAAA;;AAYN;EACE,WAAA;;AADF,cAGE;EACE,WAAA;;AAJJ,cAGE,KAEG;EACC,kBAAA;EACA,kBAAA;;AAPN,cAWE,YAAY;EACV,SAAA;EACA,UAAA;;AAYJ,QATqC;EASrC,cARI;IACE,mBAAA;IACA,SAAA;;EAMN,cARI,KAGE;IACE,gBAAA;;;AASR;EACE,gBAAA;;AADF,mBAGE,KAAK;EAEH,eAAA;EACA,kBAAA;;AANJ,mBASE,UAAU;AATZ,mBAUE,UAAU,IAAG;AAVf,mBAWE,UAAU,IAAG;EACX,yBAAA;;AAcJ,QAXqC;EAWrC,mBAVI,KAAK;IACH,gCAAA;IACA,0BAAA;;EAQN,mBANI,UAAU;EAMd,mBALI,UAAU,IAAG;EAKjB,mBAJI,UAAU,IAAG;IACX,4BAAA;;;AAUN,YACE;EACE,aAAA;;AAFJ,YAIE;EACE,cAAA;;AASJ,SAAU;EAER,gBAAA;Ef3IA,0BAAA;EACC,yBAAA;;AgB1FH;EACE,kBAAA;EACA,gBAAA;EACA,mBAAA;EACA,6BAAA;;AAQF,QAH6C;EAG7C;IAFI,kBAAA;;;AAgBJ,QAH6C;EAG7C;IAFI,WAAA;;;AAeJ;EACE,iBAAA;EACA,mBAAA;EACA,mBAAA;EACA,kBAAA;EACA,iCAAA;EACA,kDAAA;EAEA,iCAAA;;AAEA,gBAAC;EACC,gBAAA;;AA4BJ,QAzB6C;EAyB7C;IAxBI,WAAA;IACA,aAAA;IACA,gBAAA;;EAEA,gBAAC;IACC,yBAAA;IACA,uBAAA;IACA,iBAAA;IACA,4BAAA;;EAGF,gBAAC;IACC,mBAAA;;EAKF,iBAAkB;EAClB,kBAAmB;EACnB,oBAAqB;IACnB,eAAA;IACA,gBAAA;;;AAUN,UAEE;AADF,gBACE;AAFF,UAGE;AAFF,gBAEE;EACE,mBAAA;EACA,kBAAA;;AAMF,QAJ6C;EAI7C,UATA;EASA,gBATA;EASA,UARA;EAQA,gBARA;IAKI,eAAA;IACA,cAAA;;;AAaN;EACE,aAAA;EACA,qBAAA;;AAKF,QAH6C;EAG7C;IAFI,gBAAA;;;AAKJ;AACA;EACE,eAAA;EACA,QAAA;EACA,OAAA;EACA,aAAA;;AAMF,QAH6C;EAG7C;EAAA;IAFI,gBAAA;;;AAGJ;EACE,MAAA;EACA,qBAAA;;AAEF;EACE,SAAA;EACA,gBAAA;EACA,qBAAA;;AAMF;EACE,WAAA;EACA,kBAAA;EACA,eAAA;EACA,iBAAA;EACA,YAAA;;AAEA,aAAC;AACD,aAAC;EACC,qBAAA;;AASJ,QAN6C;EACzC,OAAQ,aAAa;EACrB,OAAQ,mBAAmB;IACzB,kBAAA;;;AAWN;EACE,kBAAA;EACA,YAAA;EACA,kBAAA;EACA,iBAAA;EhBsaA,eAAA;EACA,kBAAA;EgBraA,6BAAA;EACA,sBAAA;EACA,6BAAA;EACA,kBAAA;;AAIA,cAAC;EACC,aAAA;;AAdJ,cAkBE;EACE,cAAA;EACA,WAAA;EACA,WAAA;EACA,kBAAA;;AAtBJ,cAwBE,UAAU;EACR,eAAA;;AAMJ,QAH6C;EAG7C;IAFI,aAAA;;;AAUJ;EACE,mBAAA;;AADF,WAGE,KAAK;EACH,iBAAA;EACA,oBAAA;EACA,iBAAA;;AA2BF,QAxB+C;EAwB/C,WAtBE,MAAM;IACJ,gBAAA;IACA,WAAA;IACA,WAAA;IACA,aAAA;IACA,6BAAA;IACA,SAAA;IACA,gBAAA;;EAeJ,WAtBE,MAAM,eAQJ,KAAK;EAcT,WAtBE,MAAM,eASJ;IACE,0BAAA;;EAYN,WAtBE,MAAM,eAYJ,KAAK;IACH,iBAAA;;EACA,WAdJ,MAAM,eAYJ,KAAK,IAEF;EACD,WAfJ,MAAM,eAYJ,KAAK,IAGF;IACC,sBAAA;;;AAuBV,QAhB6C;EAgB7C;IAfI,WAAA;IACA,SAAA;;EAcJ,WAZI;IACE,WAAA;;EAWN,WAZI,KAEE;IACE,iBAAA;IACA,oBAAA;;EAIJ,WAAC,aAAa;IACZ,mBAAA;;;AAkBN,QAN2C;EACzC;ICnQA,sBAAA;;EDoQA;ICvQA,uBAAA;;;ADgRF;EACE,kBAAA;EACA,mBAAA;EACA,kBAAA;EACA,iCAAA;EACA,oCAAA;EhB3KA,4FAAA;EACQ,oFAAA;EAkeR,eAAA;EACA,kBAAA;;AQ3NF,QAjDqC;EAiDrC,YA/CI;IACE,qBAAA;IACA,gBAAA;IACA,sBAAA;;EA4CN,YAxCI;IACE,qBAAA;IACA,WAAA;IACA,sBAAA;;EAqCN,YAlCI,aAAa;IACX,WAAA;;EAiCN,YA9BI;IACE,gBAAA;IACA,sBAAA;;EA4BN,YAtBI;EAsBJ,YArBI;IACE,qBAAA;IACA,aAAA;IACA,gBAAA;IACA,eAAA;IACA,sBAAA;;EAgBN,YAdI,OAAO,MAAK;EAchB,YAbI,UAAU,MAAK;IACb,WAAA;IACA,cAAA;;EAWN,YAJI,cAAc;IACZ,MAAA;;;AQhFJ,QAHiD;EAGjD,YAJA;IAEI,kBAAA;;;AAsBN,QAd6C;EAc7C;IAbI,WAAA;IACA,SAAA;IACA,cAAA;IACA,eAAA;IACA,cAAA;IACA,iBAAA;IhBlMF,wBAAA;IACQ,gBAAA;;EgBqMN,YAAC,aAAa;IACZ,mBAAA;;;AASN,WAAY,KAAK;EACf,aAAA;EhBvOA,0BAAA;EACC,yBAAA;;AgB0OH,oBAAqB,YAAY,KAAK;EhBnOpC,6BAAA;EACC,4BAAA;;AgB2OH;EhBqQE,eAAA;EACA,kBAAA;;AgBnQA,WAAC;EhBkQD,gBAAA;EACA,mBAAA;;AgBhQA,WAAC;EhB+PD,gBAAA;EACA,mBAAA;;AgBtPF;EhBqPE,gBAAA;EACA,mBAAA;;AgBzOF,QAV6C;EAU7C;IATI,WAAA;IACA,iBAAA;IACA,kBAAA;;EAGA,YAAC,aAAa;IACZ,eAAA;;;AASN;EACE,yBAAA;EACA,qBAAA;;AAFF,eAIE;EACE,cAAA;;AACA,eAFF,cAEG;AACD,eAHF,cAGG;EACC,cAAA;EACA,6BAAA;;AATN,eAaE;EACE,cAAA;;AAdJ,eAiBE,YACE,KAAK;EACH,cAAA;;AAEA,eAJJ,YACE,KAAK,IAGF;AACD,eALJ,YACE,KAAK,IAIF;EACC,cAAA;EACA,6BAAA;;AAIF,eAXJ,YAUE,UAAU;AAER,eAZJ,YAUE,UAAU,IAEP;AACD,eAbJ,YAUE,UAAU,IAGP;EACC,cAAA;EACA,yBAAA;;AAIF,eAnBJ,YAkBE,YAAY;AAEV,eApBJ,YAkBE,YAAY,IAET;AACD,eArBJ,YAkBE,YAAY,IAGT;EACC,cAAA;EACA,6BAAA;;AAxCR,eA6CE;EACE,qBAAA;;AACA,eAFF,eAEG;AACD,eAHF,eAGG;EACC,yBAAA;;AAjDN,eA6CE,eAME;EACE,yBAAA;;AApDN,eAwDE;AAxDF,eAyDE;EACE,qBAAA;;AAOE,eAHJ,YAEE,QAAQ;AAEN,eAJJ,YAEE,QAAQ,IAEL;AACD,eALJ,YAEE,QAAQ,IAGL;EACC,yBAAA;EACA,cAAA;;AAiCN,QA7BiD;EA6BjD,eAxCA,YAaI,MAAM,eACJ,KAAK;IACH,cAAA;;EACA,eAhBR,YAaI,MAAM,eACJ,KAAK,IAEF;EACD,eAjBR,YAaI,MAAM,eACJ,KAAK,IAGF;IACC,cAAA;IACA,6BAAA;;EAIF,eAvBR,YAaI,MAAM,eASJ,UAAU;EAER,eAxBR,YAaI,MAAM,eASJ,UAAU,IAEP;EACD,eAzBR,YAaI,MAAM,eASJ,UAAU,IAGP;IACC,cAAA;IACA,yBAAA;;EAIF,eA/BR,YAaI,MAAM,eAiBJ,YAAY;EAEV,eAhCR,YAaI,MAAM,eAiBJ,YAAY,IAET;EACD,eAjCR,YAaI,MAAM,eAiBJ,YAAY,IAGT;IACC,cAAA;IACA,6BAAA;;;AAjGZ,eA6GE;EACE,cAAA;;AACA,eAFF,aAEG;EACC,cAAA;;AAQN;EACE,yBAAA;EACA,qBAAA;;AAFF,eAIE;EACE,cAAA;;AACA,eAFF,cAEG;AACD,eAHF,cAGG;EACC,cAAA;EACA,6BAAA;;AATN,eAaE;EACE,cAAA;;AAdJ,eAiBE,YACE,KAAK;EACH,cAAA;;AAEA,eAJJ,YACE,KAAK,IAGF;AACD,eALJ,YACE,KAAK,IAIF;EACC,cAAA;EACA,6BAAA;;AAIF,eAXJ,YAUE,UAAU;AAER,eAZJ,YAUE,UAAU,IAEP;AACD,eAbJ,YAUE,UAAU,IAGP;EACC,cAAA;EACA,yBAAA;;AAIF,eAnBJ,YAkBE,YAAY;AAEV,eApBJ,YAkBE,YAAY,IAET;AACD,eArBJ,YAkBE,YAAY,IAGT;EACC,cAAA;EACA,6BAAA;;AAxCR,eA8CE;EACE,qBAAA;;AACA,eAFF,eAEG;AACD,eAHF,eAGG;EACC,yBAAA;;AAlDN,eA8CE,eAME;EACE,yBAAA;;AArDN,eAyDE;AAzDF,eA0DE;EACE,qBAAA;;AAME,eAFJ,YACE,QAAQ;AAEN,eAHJ,YACE,QAAQ,IAEL;AACD,eAJJ,YACE,QAAQ,IAGL;EACC,yBAAA;EACA,cAAA;;AAuCN,QAnCiD;EAmCjD,eA7CA,YAYI,MAAM,eACJ;IACE,qBAAA;;EA+BR,eA7CA,YAYI,MAAM,eAIJ;IACE,yBAAA;;EA4BR,eA7CA,YAYI,MAAM,eAOJ,KAAK;IACH,cAAA;;EACA,eArBR,YAYI,MAAM,eAOJ,KAAK,IAEF;EACD,eAtBR,YAYI,MAAM,eAOJ,KAAK,IAGF;IACC,cAAA;IACA,6BAAA;;EAIF,eA5BR,YAYI,MAAM,eAeJ,UAAU;EAER,eA7BR,YAYI,MAAM,eAeJ,UAAU,IAEP;EACD,eA9BR,YAYI,MAAM,eAeJ,UAAU,IAGP;IACC,cAAA;IACA,yBAAA;;EAIF,eApCR,YAYI,MAAM,eAuBJ,YAAY;EAEV,eArCR,YAYI,MAAM,eAuBJ,YAAY,IAET;EACD,eAtCR,YAYI,MAAM,eAuBJ,YAAY,IAGT;IACC,cAAA;IACA,6BAAA;;;AAvGZ,eA8GE;EACE,cAAA;;AACA,eAFF,aAEG;EACC,cAAA;;AE9lBN;EACE,iBAAA;EACA,mBAAA;EACA,gBAAA;EACA,yBAAA;EACA,kBAAA;;AALF,WAOE;EACE,qBAAA;;AARJ,WAOE,KAGE,KAAI;EACF,SAAS,QAAT;EACA,cAAA;EACA,cAAA;;AAbN,WAiBE;EACE,cAAA;;ACpBJ;EACE,qBAAA;EACA,eAAA;EACA,cAAA;EACA,kBAAA;;AAJF,WAME;EACE,eAAA;;AAPJ,WAME,KAEE;AARJ,WAME,KAGE;EACE,kBAAA;EACA,WAAA;EACA,iBAAA;EACA,uBAAA;EACA,qBAAA;EACA,cAAA;EACA,yBAAA;EACA,yBAAA;EACA,iBAAA;;AAEF,WAdF,KAcG,YACC;AADF,WAdF,KAcG,YAEC;EACE,cAAA;EnBqFN,8BAAA;EACG,2BAAA;;AmBlFD,WArBF,KAqBG,WACC;AADF,WArBF,KAqBG,WAEC;EnBuEJ,+BAAA;EACG,4BAAA;;AmBhED,WAFF,KAAK,IAEF;AAAD,WADF,KAAK,OACF;AACD,WAHF,KAAK,IAGF;AAAD,WAFF,KAAK,OAEF;EACC,cAAA;EACA,yBAAA;EACA,qBAAA;;AAMF,WAFF,UAAU;AAER,WADF,UAAU;AAER,WAHF,UAAU,IAGP;AAAD,WAFF,UAAU,OAEP;AACD,WAJF,UAAU,IAIP;AAAD,WAHF,UAAU,OAGP;EACC,UAAA;EACA,cAAA;EACA,yBAAA;EACA,qBAAA;EACA,eAAA;;AAtDN,WA0DE,YACE;AA3DJ,WA0DE,YAEE,OAAM;AA5DV,WA0DE,YAGE,OAAM;AA7DV,WA0DE,YAIE;AA9DJ,WA0DE,YAKE,IAAG;AA/DP,WA0DE,YAME,IAAG;EACD,cAAA;EACA,yBAAA;EACA,qBAAA;EACA,mBAAA;;AASN,cnBodE,KACE;AmBrdJ,cnBodE,KAEE;EACE,kBAAA;EACA,eAAA;;AAEF,cANF,KAMG,YACC;AADF,cANF,KAMG,YAEC;EA7bJ,8BAAA;EACG,2BAAA;;AAgcD,cAZF,KAYG,WACC;AADF,cAZF,KAYG,WAEC;EA3cJ,+BAAA;EACG,4BAAA;;AmBnBL,cnB+cE,KACE;AmBhdJ,cnB+cE,KAEE;EACE,iBAAA;EACA,eAAA;;AAEF,cANF,KAMG,YACC;AADF,cANF,KAMG,YAEC;EA7bJ,8BAAA;EACG,2BAAA;;AAgcD,cAZF,KAYG,WACC;AADF,cAZF,KAYG,WAEC;EA3cJ,+BAAA;EACG,4BAAA;;AoBnGL;EACE,eAAA;EACA,cAAA;EACA,gBAAA;EACA,kBAAA;;AAJF,MAME;EACE,eAAA;;AAPJ,MAME,GAEE;AARJ,MAME,GAGE;EACE,qBAAA;EACA,iBAAA;EACA,yBAAA;EACA,yBAAA;EACA,mBAAA;;AAdN,MAME,GAWE,IAAG;AAjBP,MAME,GAYE,IAAG;EACD,qBAAA;EACA,yBAAA;;AApBN,MAwBE,MACE;AAzBJ,MAwBE,MAEE;EACE,YAAA;;AA3BN,MA+BE,UACE;AAhCJ,MA+BE,UAEE;EACE,WAAA;;AAlCN,MAsCE,UACE;AAvCJ,MAsCE,UAEE,IAAG;AAxCP,MAsCE,UAGE,IAAG;AAzCP,MAsCE,UAIE;EACE,cAAA;EACA,yBAAA;EACA,mBAAA;;AC9CN;EACE,eAAA;EACA,uBAAA;EACA,cAAA;EACA,iBAAA;EACA,cAAA;EACA,cAAA;EACA,kBAAA;EACA,mBAAA;EACA,wBAAA;EACA,oBAAA;;AAIE,MADD,MACE;AACD,MAFD,MAEE;EACC,cAAA;EACA,qBAAA;EACA,eAAA;;AAKJ,MAAC;EACC,aAAA;;AAIF,IAAK;EACH,kBAAA;EACA,SAAA;;AAOJ;ErBmhBE,yBAAA;;AAEE,cADD,MACE;AACD,cAFD,MAEE;EACC,yBAAA;;AqBnhBN;ErB+gBE,yBAAA;;AAEE,cADD,MACE;AACD,cAFD,MAEE;EACC,yBAAA;;AqB/gBN;ErB2gBE,yBAAA;;AAEE,cADD,MACE;AACD,cAFD,MAEE;EACC,yBAAA;;AqB3gBN;ErBugBE,yBAAA;;AAEE,WADD,MACE;AACD,WAFD,MAEE;EACC,yBAAA;;AqBvgBN;ErBmgBE,yBAAA;;AAEE,cADD,MACE;AACD,cAFD,MAEE;EACC,yBAAA;;AqBngBN;ErB+fE,yBAAA;;AAEE,aADD,MACE;AACD,aAFD,MAEE;EACC,yBAAA;;AsB1jBN;EACE,qBAAA;EACA,eAAA;EACA,gBAAA;EACA,eAAA;EACA,iBAAA;EACA,cAAA;EACA,cAAA;EACA,wBAAA;EACA,mBAAA;EACA,kBAAA;EACA,yBAAA;EACA,mBAAA;;AAGA,MAAC;EACC,aAAA;;AAIF,IAAK;EACH,kBAAA;EACA,SAAA;;AAEF,OAAQ;EACN,MAAA;EACA,gBAAA;;AAMF,CADD,MACE;AACD,CAFD,MAEE;EACC,cAAA;EACA,qBAAA;EACA,eAAA;;AAKJ,CAAC,gBAAgB,OAAQ;AACzB,UAAW,UAAU,IAAI;EACvB,cAAA;EACA,yBAAA;;AAEF,UAAW,KAAK,IAAI;EAClB,gBAAA;;AChDF;EACE,aAAA;EACA,mBAAA;EACA,cAAA;EACA,yBAAA;;AAJF,UAME;AANF,UAOE;EACE,cAAA;;AARJ,UAUE;EACE,mBAAA;EACA,eAAA;EACA,gBAAA;;AAGF,UAAW;EACT,kBAAA;;AAjBJ,UAoBE;EACE,eAAA;;AAiBJ,mBAdgD;EAchD;IAbI,iBAAA;IACA,oBAAA;;EAEA,UAAW;IACT,kBAAA;IACA,mBAAA;;EAQN,UALI;EAKJ,UAJI;IACE,eAAA;;;ArBlCN;EACE,cAAA;EACA,YAAA;EACA,mBAAA;EACA,uBAAA;EACA,yBAAA;EACA,yBAAA;EACA,kBAAA;EFkHA,wCAAA;EACQ,gCAAA;;AE1HV,UAUE;AAVF,UAWE,EAAE;EAEA,iBAAA;EACA,kBAAA;;AAIF,CAAC,UAAC;AACF,CAAC,UAAC;AACF,CAAC,UAAC;EACA,qBAAA;;AArBJ,UAyBE;EACE,YAAA;EACA,cAAA;;AsBzBJ;EACE,aAAA;EACA,mBAAA;EACA,6BAAA;EACA,kBAAA;;AAJF,MAOE;EACE,aAAA;EAEA,cAAA;;AAVJ,MAaE;EACE,iBAAA;;AAdJ,MAkBE;AAlBF,MAmBE;EACE,gBAAA;;AApBJ,MAsBE,IAAI;EACF,eAAA;;AAQJ;EACC,mBAAA;;AADD,kBAIE;EACE,kBAAA;EACA,SAAA;EACA,YAAA;EACA,cAAA;;AAQJ;ExBmXE,yBAAA;EACA,qBAAA;EACA,cAAA;;AwBrXF,cxBuXE;EACE,yBAAA;;AwBxXJ,cxB0XE;EACE,cAAA;;AwBxXJ;ExBgXE,yBAAA;EACA,qBAAA;EACA,cAAA;;AwBlXF,WxBoXE;EACE,yBAAA;;AwBrXJ,WxBuXE;EACE,cAAA;;AwBrXJ;ExB6WE,yBAAA;EACA,qBAAA;EACA,cAAA;;AwB/WF,cxBiXE;EACE,yBAAA;;AwBlXJ,cxBoXE;EACE,cAAA;;AwBlXJ;ExB0WE,yBAAA;EACA,qBAAA;EACA,cAAA;;AwB5WF,axB8WE;EACE,yBAAA;;AwB/WJ,axBiXE;EACE,cAAA;;AyBzaJ;EACE;IAAQ,2BAAA;;EACR;IAAQ,wBAAA;;;AAIV;EACE;IAAQ,2BAAA;;EACR;IAAQ,wBAAA;;;AASV;EACE,gBAAA;EACA,YAAA;EACA,mBAAA;EACA,yBAAA;EACA,kBAAA;EzB0FA,sDAAA;EACQ,8CAAA;;AyBtFV;EACE,WAAA;EACA,SAAA;EACA,YAAA;EACA,eAAA;EACA,iBAAA;EACA,cAAA;EACA,kBAAA;EACA,yBAAA;EzB6EA,sDAAA;EACQ,8CAAA;EAKR,mCAAA;EACQ,2BAAA;;AyB9EV,iBAAkB;EzBqSd,kBAAkB,2LAAlB;EACA,kBAAkB,mLAAlB;EyBpSF,0BAAA;;AAIF,SAAS,OAAQ;EzBoJf,0DAAA;EACQ,kDAAA;;AyB5IV;EzBkiBE,yBAAA;;AACA,iBAAkB;EA7QhB,kBAAkB,2LAAlB;EACA,kBAAkB,mLAAlB;;AyBnRJ;EzB8hBE,yBAAA;;AACA,iBAAkB;EA7QhB,kBAAkB,2LAAlB;EACA,kBAAkB,mLAAlB;;AyB/QJ;EzB0hBE,yBAAA;;AACA,iBAAkB;EA7QhB,kBAAkB,2LAAlB;EACA,kBAAkB,mLAAlB;;AyB3QJ;EzBshBE,yBAAA;;AACA,iBAAkB;EA7QhB,kBAAkB,2LAAlB;EACA,kBAAkB,mLAAlB;;A0B/UJ;AACA;EACE,gBAAA;EACA,OAAA;;AAIF;AACA,MAAO;EACL,gBAAA;;AAEF,MAAM;EACJ,aAAA;;AAIF;EACE,cAAA;;AAIF;EACE,eAAA;;AAOF,MACE;EACE,kBAAA;;AAFJ,MAIE;EACE,iBAAA;;AASJ;EACE,eAAA;EACA,gBAAA;;AC7CF;EAEE,mBAAA;EACA,eAAA;;AAQF;EACE,kBAAA;EACA,cAAA;EACA,kBAAA;EAEA,mBAAA;EACA,yBAAA;EACA,yBAAA;;AAGA,gBAAC;E3BqED,4BAAA;EACC,2BAAA;;A2BnED,gBAAC;EACC,gBAAA;E3ByEF,+BAAA;EACC,8BAAA;;A2BxFH,gBAmBE;EACE,YAAA;;AApBJ,gBAsBE,SAAS;EACP,iBAAA;;AAUJ,CAAC;EACC,cAAA;;AADF,CAAC,gBAGC;EACE,cAAA;;AAIF,CARD,gBAQE;AACD,CATD,gBASE;EACC,qBAAA;EACA,yBAAA;;AAIF,CAfD,gBAeE;AACD,CAhBD,gBAgBE,OAAO;AACR,CAjBD,gBAiBE,OAAO;EACN,UAAA;EACA,cAAA;EACA,yBAAA;EACA,qBAAA;;AANF,CAfD,gBAeE,OASC;AARF,CAhBD,gBAgBE,OAAO,MAQN;AAPF,CAjBD,gBAiBE,OAAO,MAON;EACE,cAAA;;AAVJ,CAfD,gBAeE,OAYC;AAXF,CAhBD,gBAgBE,OAAO,MAWN;AAVF,CAjBD,gBAiBE,OAAO,MAUN;EACE,cAAA;;A3BoYJ,iBAAiB;EACf,cAAA;EACA,yBAAA;;AAEA,CAAC,iBAJc;EAKb,cAAA;;AADF,CAAC,iBAJc,OAOb;EAA2B,cAAA;;AAE3B,CALD,iBAJc,OASZ;AACD,CAND,iBAJc,OAUZ;EACC,cAAA;EACA,yBAAA;;AAEF,CAVD,iBAJc,OAcZ;AACD,CAXD,iBAJc,OAeZ,OAAO;AACR,CAZD,iBAJc,OAgBZ,OAAO;EACN,WAAA;EACA,yBAAA;EACA,qBAAA;;AAnBN,iBAAiB;EACf,cAAA;EACA,yBAAA;;AAEA,CAAC,iBAJc;EAKb,cAAA;;AADF,CAAC,iBAJc,IAOb;EAA2B,cAAA;;AAE3B,CALD,iBAJc,IASZ;AACD,CAND,iBAJc,IAUZ;EACC,cAAA;EACA,yBAAA;;AAEF,CAVD,iBAJc,IAcZ;AACD,CAXD,iBAJc,IAeZ,OAAO;AACR,CAZD,iBAJc,IAgBZ,OAAO;EACN,WAAA;EACA,yBAAA;EACA,qBAAA;;AAnBN,iBAAiB;EACf,cAAA;EACA,yBAAA;;AAEA,CAAC,iBAJc;EAKb,cAAA;;AADF,CAAC,iBAJc,OAOb;EAA2B,cAAA;;AAE3B,CALD,iBAJc,OASZ;AACD,CAND,iBAJc,OAUZ;EACC,cAAA;EACA,yBAAA;;AAEF,CAVD,iBAJc,OAcZ;AACD,CAXD,iBAJc,OAeZ,OAAO;AACR,CAZD,iBAJc,OAgBZ,OAAO;EACN,WAAA;EACA,yBAAA;EACA,qBAAA;;AAnBN,iBAAiB;EACf,cAAA;EACA,yBAAA;;AAEA,CAAC,iBAJc;EAKb,cAAA;;AADF,CAAC,iBAJc,MAOb;EAA2B,cAAA;;AAE3B,CALD,iBAJc,MASZ;AACD,CAND,iBAJc,MAUZ;EACC,cAAA;EACA,yBAAA;;AAEF,CAVD,iBAJc,MAcZ;AACD,CAXD,iBAJc,MAeZ,OAAO;AACR,CAZD,iBAJc,MAgBZ,OAAO;EACN,WAAA;EACA,yBAAA;EACA,qBAAA;;A2BlYR;EACE,aAAA;EACA,kBAAA;;AAEF;EACE,gBAAA;EACA,gBAAA;;ACtGF;EACE,mBAAA;EACA,yBAAA;EACA,6BAAA;EACA,kBAAA;E5B+GA,iDAAA;EACQ,yCAAA;;A4B3GV;EACE,aAAA;;AAKF;EACE,kBAAA;EACA,oCAAA;E5B4EA,4BAAA;EACC,2BAAA;;A4B/EH,cAKE,YAAY;EACV,cAAA;;AAKJ;EACE,aAAA;EACA,gBAAA;EACA,eAAA;EACA,cAAA;;AAJF,YAME;EACE,cAAA;;AAKJ;EACE,kBAAA;EACA,yBAAA;EACA,6BAAA;E5B4DA,+BAAA;EACC,8BAAA;;A4BnDH,MACE;EACE,gBAAA;;AAFJ,MACE,cAGE;EACE,mBAAA;EACA,gBAAA;;AAIF,MATF,cASG,YACC,iBAAgB;EACd,aAAA;E5B8BN,4BAAA;EACC,2BAAA;;A4B1BC,MAhBF,cAgBG,WACC,iBAAgB;EACd,gBAAA;E5B+BN,+BAAA;EACC,8BAAA;;A4BzBH,cAAe,cACb,iBAAgB;EACd,mBAAA;;AAUJ,MACE;AADF,MAEE,oBAAoB;EAClB,gBAAA;;AAHJ,MAME,SAAQ;AANV,MAOE,oBAAmB,YAAa,SAAQ;E5BHxC,4BAAA;EACC,2BAAA;;A4BLH,MAME,SAAQ,YAIN,QAAO,YAEL,KAAI,YACF,GAAE;AAbV,MAOE,oBAAmB,YAAa,SAAQ,YAGtC,QAAO,YAEL,KAAI,YACF,GAAE;AAbV,MAME,SAAQ,YAKN,QAAO,YACL,KAAI,YACF,GAAE;AAbV,MAOE,oBAAmB,YAAa,SAAQ,YAItC,QAAO,YACL,KAAI,YACF,GAAE;AAbV,MAME,SAAQ,YAIN,QAAO,YAEL,KAAI,YAEF,GAAE;AAdV,MAOE,oBAAmB,YAAa,SAAQ,YAGtC,QAAO,YAEL,KAAI,YAEF,GAAE;AAdV,MAME,SAAQ,YAKN,QAAO,YACL,KAAI,YAEF,GAAE;AAdV,MAOE,oBAAmB,YAAa,SAAQ,YAItC,QAAO,YACL,KAAI,YAEF,GAAE;EACA,2BAAA;;AAfV,MAME,SAAQ,YAIN,QAAO,YAEL,KAAI,YAKF,GAAE;AAjBV,MAOE,oBAAmB,YAAa,SAAQ,YAGtC,QAAO,YAEL,KAAI,YAKF,GAAE;AAjBV,MAME,SAAQ,YAKN,QAAO,YACL,KAAI,YAKF,GAAE;AAjBV,MAOE,oBAAmB,YAAa,SAAQ,YAItC,QAAO,YACL,KAAI,YAKF,GAAE;AAjBV,MAME,SAAQ,YAIN,QAAO,YAEL,KAAI,YAMF,GAAE;AAlBV,MAOE,oBAAmB,YAAa,SAAQ,YAGtC,QAAO,YAEL,KAAI,YAMF,GAAE;AAlBV,MAME,SAAQ,YAKN,QAAO,YACL,KAAI,YAMF,GAAE;AAlBV,MAOE,oBAAmB,YAAa,SAAQ,YAItC,QAAO,YACL,KAAI,YAMF,GAAE;EACA,4BAAA;;AAnBV,MAyBE,SAAQ;AAzBV,MA0BE,oBAAmB,WAAY,SAAQ;E5BdvC,+BAAA;EACC,8BAAA;;A4BbH,MAyBE,SAAQ,WAIN,QAAO,WAEL,KAAI,WACF,GAAE;AAhCV,MA0BE,oBAAmB,WAAY,SAAQ,WAGrC,QAAO,WAEL,KAAI,WACF,GAAE;AAhCV,MAyBE,SAAQ,WAKN,QAAO,WACL,KAAI,WACF,GAAE;AAhCV,MA0BE,oBAAmB,WAAY,SAAQ,WAIrC,QAAO,WACL,KAAI,WACF,GAAE;AAhCV,MAyBE,SAAQ,WAIN,QAAO,WAEL,KAAI,WAEF,GAAE;AAjCV,MA0BE,oBAAmB,WAAY,SAAQ,WAGrC,QAAO,WAEL,KAAI,WAEF,GAAE;AAjCV,MAyBE,SAAQ,WAKN,QAAO,WACL,KAAI,WAEF,GAAE;AAjCV,MA0BE,oBAAmB,WAAY,SAAQ,WAIrC,QAAO,WACL,KAAI,WAEF,GAAE;EACA,8BAAA;;AAlCV,MAyBE,SAAQ,WAIN,QAAO,WAEL,KAAI,WAKF,GAAE;AApCV,MA0BE,oBAAmB,WAAY,SAAQ,WAGrC,QAAO,WAEL,KAAI,WAKF,GAAE;AApCV,MAyBE,SAAQ,WAKN,QAAO,WACL,KAAI,WAKF,GAAE;AApCV,MA0BE,oBAAmB,WAAY,SAAQ,WAIrC,QAAO,WACL,KAAI,WAKF,GAAE;AApCV,MAyBE,SAAQ,WAIN,QAAO,WAEL,KAAI,WAMF,GAAE;AArCV,MA0BE,oBAAmB,WAAY,SAAQ,WAGrC,QAAO,WAEL,KAAI,WAMF,GAAE;AArCV,MAyBE,SAAQ,WAKN,QAAO,WACL,KAAI,WAMF,GAAE;AArCV,MA0BE,oBAAmB,WAAY,SAAQ,WAIrC,QAAO,WACL,KAAI,WAMF,GAAE;EACA,+BAAA;;AAtCV,MA2CE,cAAc;AA3ChB,MA4CE,cAAc;EACZ,6BAAA;;AA7CJ,MA+CE,SAAS,QAAO,YAAa,KAAI,YAAa;AA/ChD,MAgDE,SAAS,QAAO,YAAa,KAAI,YAAa;EAC5C,aAAA;;AAjDJ,MAmDE;AAnDF,MAoDE,oBAAoB;EAClB,SAAA;;AArDJ,MAmDE,kBAGE,QAGE,KACE,KAAI;AA1DZ,MAoDE,oBAAoB,kBAElB,QAGE,KACE,KAAI;AA1DZ,MAmDE,kBAIE,QAEE,KACE,KAAI;AA1DZ,MAoDE,oBAAoB,kBAGlB,QAEE,KACE,KAAI;AA1DZ,MAmDE,kBAKE,QACE,KACE,KAAI;AA1DZ,MAoDE,oBAAoB,kBAIlB,QACE,KACE,KAAI;AA1DZ,MAmDE,kBAGE,QAGE,KAEE,KAAI;AA3DZ,MAoDE,oBAAoB,kBAElB,QAGE,KAEE,KAAI;AA3DZ,MAmDE,kBAIE,QAEE,KAEE,KAAI;AA3DZ,MAoDE,oBAAoB,kBAGlB,QAEE,KAEE,KAAI;AA3DZ,MAmDE,kBAKE,QACE,KAEE,KAAI;AA3DZ,MAoDE,oBAAoB,kBAIlB,QACE,KAEE,KAAI;EACF,cAAA;;AA5DV,MAmDE,kBAGE,QAGE,KAKE,KAAI;AA9DZ,MAoDE,oBAAoB,kBAElB,QAGE,KAKE,KAAI;AA9DZ,MAmDE,kBAIE,QAEE,KAKE,KAAI;AA9DZ,MAoDE,oBAAoB,kBAGlB,QAEE,KAKE,KAAI;AA9DZ,MAmDE,kBAKE,QACE,KAKE,KAAI;AA9DZ,MAoDE,oBAAoB,kBAIlB,QACE,KAKE,KAAI;AA9DZ,MAmDE,kBAGE,QAGE,KAME,KAAI;AA/DZ,MAoDE,oBAAoB,kBAElB,QAGE,KAME,KAAI;AA/DZ,MAmDE,kBAIE,QAEE,KAME,KAAI;AA/DZ,MAoDE,oBAAoB,kBAGlB,QAEE,KAME,KAAI;AA/DZ,MAmDE,kBAKE,QACE,KAME,KAAI;AA/DZ,MAoDE,oBAAoB,kBAIlB,QACE,KAME,KAAI;EACF,eAAA;;AAhEV,MAmDE,kBAiBE,QAEE,KAAI,YACF;AAvER,MAoDE,oBAAoB,kBAgBlB,QAEE,KAAI,YACF;AAvER,MAmDE,kBAkBE,QACE,KAAI,YACF;AAvER,MAoDE,oBAAoB,kBAiBlB,QACE,KAAI,YACF;AAvER,MAmDE,kBAiBE,QAEE,KAAI,YAEF;AAxER,MAoDE,oBAAoB,kBAgBlB,QAEE,KAAI,YAEF;AAxER,MAmDE,kBAkBE,QACE,KAAI,YAEF;AAxER,MAoDE,oBAAoB,kBAiBlB,QACE,KAAI,YAEF;EACE,gBAAA;;AAzEV,MAmDE,kBA0BE,QAEE,KAAI,WACF;AAhFR,MAoDE,oBAAoB,kBAyBlB,QAEE,KAAI,WACF;AAhFR,MAmDE,kBA2BE,QACE,KAAI,WACF;AAhFR,MAoDE,oBAAoB,kBA0BlB,QACE,KAAI,WACF;AAhFR,MAmDE,kBA0BE,QAEE,KAAI,WAEF;AAjFR,MAoDE,oBAAoB,kBAyBlB,QAEE,KAAI,WAEF;AAjFR,MAmDE,kBA2BE,QACE,KAAI,WAEF;AAjFR,MAoDE,oBAAoB,kBA0BlB,QACE,KAAI,WAEF;EACE,gBAAA;;AAlFV,MAuFE;EACE,SAAA;EACA,gBAAA;;AAUJ;EACE,mBAAA;;AADF,YAIE;EACE,gBAAA;EACA,kBAAA;EACA,gBAAA;;AAPJ,YAIE,OAIE;EACE,eAAA;;AATN,YAaE;EACE,gBAAA;;AAdJ,YAaE,eAEE,kBAAkB;EAChB,6BAAA;;AAhBN,YAmBE;EACE,aAAA;;AApBJ,YAmBE,cAEE,kBAAkB;EAChB,gCAAA;;AAON;E5BsLE,qBAAA;;AAEA,cAAE;EACA,cAAA;EACA,yBAAA;EACA,qBAAA;;AAHF,cAAE,iBAKA,kBAAkB;EAChB,yBAAA;;AAGJ,cAAE,gBACA,kBAAkB;EAChB,4BAAA;;A4BhMN;E5BmLE,qBAAA;;AAEA,cAAE;EACA,cAAA;EACA,yBAAA;EACA,qBAAA;;AAHF,cAAE,iBAKA,kBAAkB;EAChB,yBAAA;;AAGJ,cAAE,gBACA,kBAAkB;EAChB,4BAAA;;A4B7LN;E5BgLE,qBAAA;;AAEA,cAAE;EACA,cAAA;EACA,yBAAA;EACA,qBAAA;;AAHF,cAAE,iBAKA,kBAAkB;EAChB,yBAAA;;AAGJ,cAAE,gBACA,kBAAkB;EAChB,4BAAA;;A4B1LN;E5B6KE,qBAAA;;AAEA,WAAE;EACA,cAAA;EACA,yBAAA;EACA,qBAAA;;AAHF,WAAE,iBAKA,kBAAkB;EAChB,yBAAA;;AAGJ,WAAE,gBACA,kBAAkB;EAChB,4BAAA;;A4BvLN;E5B0KE,qBAAA;;AAEA,cAAE;EACA,cAAA;EACA,yBAAA;EACA,qBAAA;;AAHF,cAAE,iBAKA,kBAAkB;EAChB,yBAAA;;AAGJ,cAAE,gBACA,kBAAkB;EAChB,4BAAA;;A4BpLN;E5BuKE,qBAAA;;AAEA,aAAE;EACA,cAAA;EACA,yBAAA;EACA,qBAAA;;AAHF,aAAE,iBAKA,kBAAkB;EAChB,yBAAA;;AAGJ,aAAE,gBACA,kBAAkB;EAChB,4BAAA;;A6B5ZN;EACE,gBAAA;EACA,aAAA;EACA,mBAAA;EACA,yBAAA;EACA,yBAAA;EACA,kBAAA;E7B6GA,uDAAA;EACQ,+CAAA;;A6BpHV,KAQE;EACE,kBAAA;EACA,iCAAA;;AAKJ;EACE,aAAA;EACA,kBAAA;;AAEF;EACE,YAAA;EACA,kBAAA;;ACtBF;EACE,YAAA;EACA,eAAA;EACA,iBAAA;EACA,cAAA;EACA,cAAA;EACA,4BAAA;E9BkRA,YAAA;EAGA,yBAAA;;A8BlRA,MAAC;AACD,MAAC;EACC,cAAA;EACA,qBAAA;EACA,eAAA;E9B2QF,YAAA;EAGA,yBAAA;;A8BvQA,MAAM;EACJ,UAAA;EACA,eAAA;EACA,uBAAA;EACA,SAAA;EACA,wBAAA;;ACpBJ;EACE,gBAAA;;AAIF;EACE,aAAA;EACA,cAAA;EACA,kBAAA;EACA,eAAA;EACA,MAAA;EACA,QAAA;EACA,SAAA;EACA,OAAA;EACA,aAAA;EACA,iCAAA;EAIA,UAAA;;AAGA,MAAC,KAAM;E/BiIP,mBAAmB,kBAAnB;EACI,eAAe,kBAAf;EACI,WAAW,kBAAX;EApBR,mDAAA;EACG,6CAAA;EACE,yCAAA;EACG,mCAAA;;A+B9GR,MAAC,GAAI;E/B6HL,mBAAmB,eAAnB;EACI,eAAe,eAAf;EACI,WAAW,eAAX;;A+B3HV;EACE,kBAAA;EACA,WAAA;EACA,YAAA;;AAIF;EACE,kBAAA;EACA,yBAAA;EACA,yBAAA;EACA,oCAAA;EACA,kBAAA;E/BqEA,gDAAA;EACQ,wCAAA;E+BpER,4BAAA;EAEA,aAAA;;AAIF;EACE,eAAA;EACA,MAAA;EACA,QAAA;EACA,SAAA;EACA,OAAA;EACA,aAAA;EACA,yBAAA;;AAEA,eAAC;E/BwND,UAAA;EAGA,wBAAA;;A+B1NA,eAAC;E/BuND,YAAA;EAGA,yBAAA;;A+BrNF;EACE,aAAA;EACA,gCAAA;EACA,yBAAA;;AAGF,aAAc;EACZ,gBAAA;;AAIF;EACE,SAAA;EACA,uBAAA;;AAKF;EACE,kBAAA;EACA,aAAA;;AAIF;EACE,gBAAA;EACA,uBAAA;EACA,iBAAA;EACA,6BAAA;;AAJF,aAQE,KAAK;EACH,gBAAA;EACA,gBAAA;;AAVJ,aAaE,WAAW,KAAK;EACd,iBAAA;;AAdJ,aAiBE,WAAW;EACT,cAAA;;AAmBJ,QAdmC;EAEjC;IACE,YAAA;IACA,iBAAA;;EAEF;I/BPA,iDAAA;IACQ,yCAAA;;E+BWR;IAAY,YAAA;;;AAMd,QAHmC;EACjC;IAAY,YAAA;;;ACnId;EACE,kBAAA;EACA,aAAA;EACA,cAAA;EACA,mBAAA;EACA,eAAA;EACA,gBAAA;EhCiRA,UAAA;EAGA,wBAAA;;AgCjRA,QAAC;EhC8QD,YAAA;EAGA,yBAAA;;AgChRA,QAAC;EAAU,gBAAA;EAAmB,cAAA;;AAC9B,QAAC;EAAU,gBAAA;EAAmB,cAAA;;AAC9B,QAAC;EAAU,eAAA;EAAmB,cAAA;;AAC9B,QAAC;EAAU,iBAAA;EAAmB,cAAA;;AAIhC;EACE,gBAAA;EACA,gBAAA;EACA,cAAA;EACA,kBAAA;EACA,qBAAA;EACA,yBAAA;EACA,kBAAA;;AAIF;EACE,kBAAA;EACA,QAAA;EACA,SAAA;EACA,yBAAA;EACA,mBAAA;;AAGA,QAAC,IAAK;EACJ,SAAA;EACA,SAAA;EACA,iBAAA;EACA,uBAAA;EACA,yBAAA;;AAEF,QAAC,SAAU;EACT,SAAA;EACA,SAAA;EACA,uBAAA;EACA,yBAAA;;AAEF,QAAC,UAAW;EACV,SAAA;EACA,UAAA;EACA,uBAAA;EACA,yBAAA;;AAEF,QAAC,MAAO;EACN,QAAA;EACA,OAAA;EACA,gBAAA;EACA,2BAAA;EACA,2BAAA;;AAEF,QAAC,KAAM;EACL,QAAA;EACA,QAAA;EACA,gBAAA;EACA,2BAAA;EACA,0BAAA;;AAEF,QAAC,OAAQ;EACP,MAAA;EACA,SAAA;EACA,iBAAA;EACA,uBAAA;EACA,4BAAA;;AAEF,QAAC,YAAa;EACZ,MAAA;EACA,SAAA;EACA,uBAAA;EACA,4BAAA;;AAEF,QAAC,aAAc;EACb,MAAA;EACA,UAAA;EACA,uBAAA;EACA,4BAAA;;ACvFJ;EACE,kBAAA;EACA,MAAA;EACA,OAAA;EACA,aAAA;EACA,aAAA;EACA,gBAAA;EACA,YAAA;EACA,gBAAA;EACA,yBAAA;EACA,4BAAA;EACA,yBAAA;EACA,oCAAA;EACA,kBAAA;EjCuGA,iDAAA;EACQ,yCAAA;EiCpGR,mBAAA;;AAGA,QAAC;EAAW,iBAAA;;AACZ,QAAC;EAAW,iBAAA;;AACZ,QAAC;EAAW,gBAAA;;AACZ,QAAC;EAAW,kBAAA;;AAGd;EACE,SAAA;EACA,iBAAA;EACA,eAAA;EACA,mBAAA;EACA,iBAAA;EACA,yBAAA;EACA,gCAAA;EACA,0BAAA;;AAGF;EACE,iBAAA;;AAQA,QADO;AAEP,QAFO,SAEN;EACC,kBAAA;EACA,cAAA;EACA,QAAA;EACA,SAAA;EACA,yBAAA;EACA,mBAAA;;AAGJ,QAAS;EACP,kBAAA;;AAEF,QAAS,SAAQ;EACf,kBAAA;EACA,SAAS,EAAT;;AAIA,QAAC,IAAK;EACJ,SAAA;EACA,kBAAA;EACA,sBAAA;EACA,yBAAA;EACA,qCAAA;EACA,aAAA;;AACA,QAPD,IAAK,SAOH;EACC,SAAS,GAAT;EACA,WAAA;EACA,kBAAA;EACA,sBAAA;EACA,yBAAA;;AAGJ,QAAC,MAAO;EACN,QAAA;EACA,WAAA;EACA,iBAAA;EACA,oBAAA;EACA,2BAAA;EACA,uCAAA;;AACA,QAPD,MAAO,SAOL;EACC,SAAS,GAAT;EACA,SAAA;EACA,aAAA;EACA,oBAAA;EACA,2BAAA;;AAGJ,QAAC,OAAQ;EACP,SAAA;EACA,kBAAA;EACA,mBAAA;EACA,4BAAA;EACA,wCAAA;EACA,UAAA;;AACA,QAPD,OAAQ,SAON;EACC,SAAS,GAAT;EACA,QAAA;EACA,kBAAA;EACA,mBAAA;EACA,4BAAA;;AAIJ,QAAC,KAAM;EACL,QAAA;EACA,YAAA;EACA,iBAAA;EACA,qBAAA;EACA,0BAAA;EACA,sCAAA;;AACA,QAPD,KAAM,SAOJ;EACC,SAAS,GAAT;EACA,UAAA;EACA,qBAAA;EACA,0BAAA;EACA,aAAA;;A9B1HN;EACE,kBAAA;;AAGF;EACE,kBAAA;EACA,gBAAA;EACA,WAAA;;AAHF,eAKE;EACE,aAAA;EACA,kBAAA;EH8GF,yCAAA;EACQ,iCAAA;;AGtHV,eAKE,QAME;AAXJ,eAKE,QAOE,IAAI;EAEF,cAAA;;AAdN,eAkBE;AAlBF,eAmBE;AAnBF,eAoBE;EAAU,cAAA;;AApBZ,eAsBE;EACE,OAAA;;AAvBJ,eA0BE;AA1BF,eA2BE;EACE,kBAAA;EACA,MAAA;EACA,WAAA;;AA9BJ,eAiCE;EACE,UAAA;;AAlCJ,eAoCE;EACE,WAAA;;AArCJ,eAuCE,QAAO;AAvCT,eAwCE,QAAO;EACL,OAAA;;AAzCJ,eA4CE,UAAS;EACP,WAAA;;AA7CJ,eA+CE,UAAS;EACP,UAAA;;AAQJ;EACE,kBAAA;EACA,MAAA;EACA,OAAA;EACA,SAAA;EACA,UAAA;EHsNA,YAAA;EAGA,yBAAA;EGvNA,eAAA;EACA,cAAA;EACA,kBAAA;EACA,yCAAA;;AAKA,iBAAC;EH8NC,kBAAkB,8BAA8B,mCAAyC,uCAAzF;EACA,kBAAmB,4EAAnB;EACA,2BAAA;EACA,sHAAA;;AG9NF,iBAAC;EACC,UAAA;EACA,QAAA;EHyNA,kBAAkB,8BAA8B,sCAAyC,oCAAzF;EACA,kBAAmB,4EAAnB;EACA,2BAAA;EACA,sHAAA;;AGvNF,iBAAC;AACD,iBAAC;EACC,aAAA;EACA,cAAA;EACA,qBAAA;EH8LF,YAAA;EAGA,yBAAA;;AG9NF,iBAkCE;AAlCF,iBAmCE;AAnCF,iBAoCE;AApCF,iBAqCE;EACE,kBAAA;EACA,QAAA;EACA,UAAA;EACA,qBAAA;;AAzCJ,iBA2CE;AA3CF,iBA4CE;EACE,SAAA;;AA7CJ,iBA+CE;AA/CF,iBAgDE;EACE,UAAA;;AAjDJ,iBAmDE;AAnDF,iBAoDE;EACE,WAAA;EACA,YAAA;EACA,iBAAA;EACA,kBAAA;EACA,kBAAA;;AAIA,iBADF,WACG;EACC,SAAS,OAAT;;AAIF,iBADF,WACG;EACC,SAAS,OAAT;;AAUN;EACE,kBAAA;EACA,YAAA;EACA,SAAA;EACA,WAAA;EACA,UAAA;EACA,iBAAA;EACA,eAAA;EACA,gBAAA;EACA,kBAAA;;AATF,oBAWE;EACE,qBAAA;EACA,WAAA;EACA,YAAA;EACA,WAAA;EACA,mBAAA;EACA,yBAAA;EACA,mBAAA;EACA,eAAA;EAUA,yBAAA;EACA,kCAAA;;AA9BJ,oBAgCE;EACE,SAAA;EACA,WAAA;EACA,YAAA;EACA,yBAAA;;AAOJ;EACE,kBAAA;EACA,SAAA;EACA,UAAA;EACA,YAAA;EACA,WAAA;EACA,iBAAA;EACA,oBAAA;EACA,cAAA;EACA,kBAAA;EACA,yCAAA;;AACA,iBAAE;EACA,iBAAA;;AAkCJ,mBA5B8C;EAG5C,iBACE;EADF,iBAEE;EAFF,iBAGE;EAHF,iBAIE;IACE,WAAA;IACA,YAAA;IACA,iBAAA;IACA,kBAAA;IACA,eAAA;;EAKJ;IACE,SAAA;IACA,UAAA;IACA,oBAAA;;EAIF;IACE,YAAA;;;AHlNF,SAAC;AACD,SAAC;AMXH,UNUG;AMVH,UNWG;AMSH,gBNVG;AMUH,gBNTG;AMkBH,INnBG;AMmBH,INlBG;AQsXH,gBAoBE,YR3YC;AQuXH,gBAoBE,YR1YC;AUkBH,YVnBG;AUmBH,YVlBG;AU8HH,mBAWE,aV1IC;AU+HH,mBAWE,aVzIC;AeZH,IfWG;AeXH,IfYG;AgBVH,OhBSG;AgBTH,OhBUG;AgBUH,chBXG;AgBWH,chBVG;AgB6BH,gBhB9BG;AgB8BH,gBhB7BG;AoBfH,MpBcG;AoBdH,MpBeG;A4BLH,W5BIG;A4BJH,W5BKG;A+B+EH,a/BhFG;A+BgFH,a/B/EG;EACC,SAAS,GAAT;EACA,cAAA;;AAEF,SAAC;AMfH,UNeG;AMKH,gBNLG;AMcH,INdG;AQkXH,gBAoBE,YRtYC;AUcH,YVdG;AU0HH,mBAWE,aVrIC;AehBH,IfgBG;AgBdH,OhBcG;AgBMH,chBNG;AgByBH,gBhBzBG;AoBnBH,MpBmBG;A4BTH,W5BSG;A+B2EH,a/B3EG;EACC,WAAA;;AiBdJ;EjB6BE,cAAA;EACA,iBAAA;EACA,kBAAA;;AiB5BF;EACE,uBAAA;;AAEF;EACE,sBAAA;;AAQF;EACE,wBAAA;;AAEF;EACE,yBAAA;;AAEF;EACE,kBAAA;;AAEF;EjB8CE,WAAA;EACA,kBAAA;EACA,iBAAA;EACA,6BAAA;EACA,SAAA;;AiBzCF;EACE,wBAAA;EACA,6BAAA;;AAOF;EACE,eAAA;;AiBnCF;EACE,mBAAA;;AAKF;AACA;AACA;AACA;ElCylBE,wBAAA;;AkCjlBF,QAHqC;EAGrC;IlCykBE,yBAAA;;EACA,KAAK;IAAK,cAAA;;EACV,EAAE;IAAQ,kBAAA;;EACV,EAAE;EACF,EAAE;IAAQ,mBAAA;;;AkCxkBZ,QAHqC,uBAAgC;EAGrE;IlCokBE,yBAAA;;EACA,KAAK;IAAK,cAAA;;EACV,EAAE;IAAQ,kBAAA;;EACV,EAAE;EACF,EAAE;IAAQ,mBAAA;;;AkCnkBZ,QAHqC,uBAAgC;EAGrE;IlC+jBE,yBAAA;;EACA,KAAK;IAAK,cAAA;;EACV,EAAE;IAAQ,kBAAA;;EACV,EAAE;EACF,EAAE;IAAQ,mBAAA;;;AkC9jBZ,QAHqC;EAGrC;IlC0jBE,yBAAA;;EACA,KAAK;IAAK,cAAA;;EACV,EAAE;IAAQ,kBAAA;;EACV,EAAE;EACF,EAAE;IAAQ,mBAAA;;;AkCxjBZ,QAHqC;EAGrC;IlC4jBE,wBAAA;;;AkCvjBF,QAHqC,uBAAgC;EAGrE;IlCujBE,wBAAA;;;AkCljBF,QAHqC,uBAAgC;EAGrE;IlCkjBE,wBAAA;;;AkC7iBF,QAHqC;EAGrC;IlC6iBE,wBAAA;;;AkCtiBF;ElCsiBE,wBAAA;;AkChiBF;EAAA;IlCwhBE,yBAAA;;EACA,KAAK;IAAK,cAAA;;EACV,EAAE;IAAQ,kBAAA;;EACV,EAAE;EACF,EAAE;IAAQ,mBAAA;;;AkCthBZ;EAAA;IlC0hBE,wBAAA","sourcesContent":["/*! normalize.css v3.0.0 | MIT License | git.io/normalize */\n\n//\n// 1. Set default font family to sans-serif.\n// 2. Prevent iOS text size adjust after orientation change, without disabling\n//    user zoom.\n//\n\nhtml {\n  font-family: sans-serif; // 1\n  -ms-text-size-adjust: 100%; // 2\n  -webkit-text-size-adjust: 100%; // 2\n}\n\n//\n// Remove default margin.\n//\n\nbody {\n  margin: 0;\n}\n\n// HTML5 display definitions\n// ==========================================================================\n\n//\n// Correct `block` display not defined in IE 8/9.\n//\n\narticle,\naside,\ndetails,\nfigcaption,\nfigure,\nfooter,\nheader,\nhgroup,\nmain,\nnav,\nsection,\nsummary {\n  display: block;\n}\n\n//\n// 1. Correct `inline-block` display not defined in IE 8/9.\n// 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera.\n//\n\naudio,\ncanvas,\nprogress,\nvideo {\n  display: inline-block; // 1\n  vertical-align: baseline; // 2\n}\n\n//\n// Prevent modern browsers from displaying `audio` without controls.\n// Remove excess height in iOS 5 devices.\n//\n\naudio:not([controls]) {\n  display: none;\n  height: 0;\n}\n\n//\n// Address `[hidden]` styling not present in IE 8/9.\n// Hide the `template` element in IE, Safari, and Firefox < 22.\n//\n\n[hidden],\ntemplate {\n  display: none;\n}\n\n// Links\n// ==========================================================================\n\n//\n// Remove the gray background color from active links in IE 10.\n//\n\na {\n  background: transparent;\n}\n\n//\n// Improve readability when focused and also mouse hovered in all browsers.\n//\n\na:active,\na:hover {\n  outline: 0;\n}\n\n// Text-level semantics\n// ==========================================================================\n\n//\n// Address styling not present in IE 8/9, Safari 5, and Chrome.\n//\n\nabbr[title] {\n  border-bottom: 1px dotted;\n}\n\n//\n// Address style set to `bolder` in Firefox 4+, Safari 5, and Chrome.\n//\n\nb,\nstrong {\n  font-weight: bold;\n}\n\n//\n// Address styling not present in Safari 5 and Chrome.\n//\n\ndfn {\n  font-style: italic;\n}\n\n//\n// Address variable `h1` font-size and margin within `section` and `article`\n// contexts in Firefox 4+, Safari 5, and Chrome.\n//\n\nh1 {\n  font-size: 2em;\n  margin: 0.67em 0;\n}\n\n//\n// Address styling not present in IE 8/9.\n//\n\nmark {\n  background: #ff0;\n  color: #000;\n}\n\n//\n// Address inconsistent and variable font size in all browsers.\n//\n\nsmall {\n  font-size: 80%;\n}\n\n//\n// Prevent `sub` and `sup` affecting `line-height` in all browsers.\n//\n\nsub,\nsup {\n  font-size: 75%;\n  line-height: 0;\n  position: relative;\n  vertical-align: baseline;\n}\n\nsup {\n  top: -0.5em;\n}\n\nsub {\n  bottom: -0.25em;\n}\n\n// Embedded content\n// ==========================================================================\n\n//\n// Remove border when inside `a` element in IE 8/9.\n//\n\nimg {\n  border: 0;\n}\n\n//\n// Correct overflow displayed oddly in IE 9.\n//\n\nsvg:not(:root) {\n  overflow: hidden;\n}\n\n// Grouping content\n// ==========================================================================\n\n//\n// Address margin not present in IE 8/9 and Safari 5.\n//\n\nfigure {\n  margin: 1em 40px;\n}\n\n//\n// Address differences between Firefox and other browsers.\n//\n\nhr {\n  -moz-box-sizing: content-box;\n  box-sizing: content-box;\n  height: 0;\n}\n\n//\n// Contain overflow in all browsers.\n//\n\npre {\n  overflow: auto;\n}\n\n//\n// Address odd `em`-unit font size rendering in all browsers.\n//\n\ncode,\nkbd,\npre,\nsamp {\n  font-family: monospace, monospace;\n  font-size: 1em;\n}\n\n// Forms\n// ==========================================================================\n\n//\n// Known limitation: by default, Chrome and Safari on OS X allow very limited\n// styling of `select`, unless a `border` property is set.\n//\n\n//\n// 1. Correct color not being inherited.\n//    Known issue: affects color of disabled elements.\n// 2. Correct font properties not being inherited.\n// 3. Address margins set differently in Firefox 4+, Safari 5, and Chrome.\n//\n\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n  color: inherit; // 1\n  font: inherit; // 2\n  margin: 0; // 3\n}\n\n//\n// Address `overflow` set to `hidden` in IE 8/9/10.\n//\n\nbutton {\n  overflow: visible;\n}\n\n//\n// Address inconsistent `text-transform` inheritance for `button` and `select`.\n// All other form control elements do not inherit `text-transform` values.\n// Correct `button` style inheritance in Firefox, IE 8+, and Opera\n// Correct `select` style inheritance in Firefox.\n//\n\nbutton,\nselect {\n  text-transform: none;\n}\n\n//\n// 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`\n//    and `video` controls.\n// 2. Correct inability to style clickable `input` types in iOS.\n// 3. Improve usability and consistency of cursor style between image-type\n//    `input` and others.\n//\n\nbutton,\nhtml input[type=\"button\"], // 1\ninput[type=\"reset\"],\ninput[type=\"submit\"] {\n  -webkit-appearance: button; // 2\n  cursor: pointer; // 3\n}\n\n//\n// Re-set default cursor for disabled elements.\n//\n\nbutton[disabled],\nhtml input[disabled] {\n  cursor: default;\n}\n\n//\n// Remove inner padding and border in Firefox 4+.\n//\n\nbutton::-moz-focus-inner,\ninput::-moz-focus-inner {\n  border: 0;\n  padding: 0;\n}\n\n//\n// Address Firefox 4+ setting `line-height` on `input` using `!important` in\n// the UA stylesheet.\n//\n\ninput {\n  line-height: normal;\n}\n\n//\n// It's recommended that you don't attempt to style these elements.\n// Firefox's implementation doesn't respect box-sizing, padding, or width.\n//\n// 1. Address box sizing set to `content-box` in IE 8/9/10.\n// 2. Remove excess padding in IE 8/9/10.\n//\n\ninput[type=\"checkbox\"],\ninput[type=\"radio\"] {\n  box-sizing: border-box; // 1\n  padding: 0; // 2\n}\n\n//\n// Fix the cursor style for Chrome's increment/decrement buttons. For certain\n// `font-size` values of the `input`, it causes the cursor style of the\n// decrement button to change from `default` to `text`.\n//\n\ninput[type=\"number\"]::-webkit-inner-spin-button,\ninput[type=\"number\"]::-webkit-outer-spin-button {\n  height: auto;\n}\n\n//\n// 1. Address `appearance` set to `searchfield` in Safari 5 and Chrome.\n// 2. Address `box-sizing` set to `border-box` in Safari 5 and Chrome\n//    (include `-moz` to future-proof).\n//\n\ninput[type=\"search\"] {\n  -webkit-appearance: textfield; // 1\n  -moz-box-sizing: content-box;\n  -webkit-box-sizing: content-box; // 2\n  box-sizing: content-box;\n}\n\n//\n// Remove inner padding and search cancel button in Safari and Chrome on OS X.\n// Safari (but not Chrome) clips the cancel button when the search input has\n// padding (and `textfield` appearance).\n//\n\ninput[type=\"search\"]::-webkit-search-cancel-button,\ninput[type=\"search\"]::-webkit-search-decoration {\n  -webkit-appearance: none;\n}\n\n//\n// Define consistent border, margin, and padding.\n//\n\nfieldset {\n  border: 1px solid #c0c0c0;\n  margin: 0 2px;\n  padding: 0.35em 0.625em 0.75em;\n}\n\n//\n// 1. Correct `color` not being inherited in IE 8/9.\n// 2. Remove padding so people aren't caught out if they zero out fieldsets.\n//\n\nlegend {\n  border: 0; // 1\n  padding: 0; // 2\n}\n\n//\n// Remove default vertical scrollbar in IE 8/9.\n//\n\ntextarea {\n  overflow: auto;\n}\n\n//\n// Don't inherit the `font-weight` (applied by a rule above).\n// NOTE: the default cannot safely be changed in Chrome and Safari on OS X.\n//\n\noptgroup {\n  font-weight: bold;\n}\n\n// Tables\n// ==========================================================================\n\n//\n// Remove most spacing between table cells.\n//\n\ntable {\n  border-collapse: collapse;\n  border-spacing: 0;\n}\n\ntd,\nth {\n  padding: 0;\n}","//\n// Basic print styles\n// --------------------------------------------------\n// Source: https://github.com/h5bp/html5-boilerplate/blob/master/css/main.css\n\n@media print {\n\n  * {\n    text-shadow: none !important;\n    color: #000 !important; // Black prints faster: h5bp.com/s\n    background: transparent !important;\n    box-shadow: none !important;\n  }\n\n  a,\n  a:visited {\n    text-decoration: underline;\n  }\n\n  a[href]:after {\n    content: \" (\" attr(href) \")\";\n  }\n\n  abbr[title]:after {\n    content: \" (\" attr(title) \")\";\n  }\n\n  // Don't show links for images, or javascript/internal links\n  a[href^=\"javascript:\"]:after,\n  a[href^=\"#\"]:after {\n    content: \"\";\n  }\n\n  pre,\n  blockquote {\n    border: 1px solid #999;\n    page-break-inside: avoid;\n  }\n\n  thead {\n    display: table-header-group; // h5bp.com/t\n  }\n\n  tr,\n  img {\n    page-break-inside: avoid;\n  }\n\n  img {\n    max-width: 100% !important;\n  }\n\n  p,\n  h2,\n  h3 {\n    orphans: 3;\n    widows: 3;\n  }\n\n  h2,\n  h3 {\n    page-break-after: avoid;\n  }\n\n  // Chrome (OSX) fix for https://github.com/twbs/bootstrap/issues/11245\n  // Once fixed, we can just straight up remove this.\n  select {\n    background: #fff !important;\n  }\n\n  // Bootstrap components\n  .navbar {\n    display: none;\n  }\n  .table {\n    td,\n    th {\n      background-color: #fff !important;\n    }\n  }\n  .btn,\n  .dropup > .btn {\n    > .caret {\n      border-top-color: #000 !important;\n    }\n  }\n  .label {\n    border: 1px solid #000;\n  }\n\n  .table {\n    border-collapse: collapse !important;\n  }\n  .table-bordered {\n    th,\n    td {\n      border: 1px solid #ddd !important;\n    }\n  }\n\n}\n","//\n// Scaffolding\n// --------------------------------------------------\n\n\n// Reset the box-sizing\n//\n// Heads up! This reset may cause conflicts with some third-party widgets.\n// For recommendations on resolving such conflicts, see\n// http://getbootstrap.com/getting-started/#third-box-sizing\n* {\n  .box-sizing(border-box);\n}\n*:before,\n*:after {\n  .box-sizing(border-box);\n}\n\n\n// Body reset\n\nhtml {\n  font-size: 62.5%;\n  -webkit-tap-highlight-color: rgba(0,0,0,0);\n}\n\nbody {\n  font-family: @font-family-base;\n  font-size: @font-size-base;\n  line-height: @line-height-base;\n  color: @text-color;\n  background-color: @body-bg;\n}\n\n// Reset fonts for relevant elements\ninput,\nbutton,\nselect,\ntextarea {\n  font-family: inherit;\n  font-size: inherit;\n  line-height: inherit;\n}\n\n\n// Links\n\na {\n  color: @link-color;\n  text-decoration: none;\n\n  &:hover,\n  &:focus {\n    color: @link-hover-color;\n    text-decoration: underline;\n  }\n\n  &:focus {\n    .tab-focus();\n  }\n}\n\n\n// Figures\n//\n// We reset this here because previously Normalize had no `figure` margins. This\n// ensures we don't break anyone's use of the element.\n\nfigure {\n  margin: 0;\n}\n\n\n// Images\n\nimg {\n  vertical-align: middle;\n}\n\n// Responsive images (ensure images don't scale beyond their parents)\n.img-responsive {\n  .img-responsive();\n}\n\n// Rounded corners\n.img-rounded {\n  border-radius: @border-radius-large;\n}\n\n// Image thumbnails\n//\n// Heads up! This is mixin-ed into thumbnails.less for `.thumbnail`.\n.img-thumbnail {\n  padding: @thumbnail-padding;\n  line-height: @line-height-base;\n  background-color: @thumbnail-bg;\n  border: 1px solid @thumbnail-border;\n  border-radius: @thumbnail-border-radius;\n  .transition(all .2s ease-in-out);\n\n  // Keep them at most 100% wide\n  .img-responsive(inline-block);\n}\n\n// Perfect circle\n.img-circle {\n  border-radius: 50%; // set radius in percents\n}\n\n\n// Horizontal rules\n\nhr {\n  margin-top:    @line-height-computed;\n  margin-bottom: @line-height-computed;\n  border: 0;\n  border-top: 1px solid @hr-border;\n}\n\n\n// Only display content to screen readers\n//\n// See: http://a11yproject.com/posts/how-to-hide-content/\n\n.sr-only {\n  position: absolute;\n  width: 1px;\n  height: 1px;\n  margin: -1px;\n  padding: 0;\n  overflow: hidden;\n  clip: rect(0,0,0,0);\n  border: 0;\n}\n","//\n// Mixins\n// --------------------------------------------------\n\n\n// Utilities\n// -------------------------\n\n// Clearfix\n// Source: http://nicolasgallagher.com/micro-clearfix-hack/\n//\n// For modern browsers\n// 1. The space content is one way to avoid an Opera bug when the\n//    contenteditable attribute is included anywhere else in the document.\n//    Otherwise it causes space to appear at the top and bottom of elements\n//    that are clearfixed.\n// 2. The use of `table` rather than `block` is only necessary if using\n//    `:before` to contain the top-margins of child elements.\n.clearfix() {\n  &:before,\n  &:after {\n    content: \" \"; // 1\n    display: table; // 2\n  }\n  &:after {\n    clear: both;\n  }\n}\n\n// WebKit-style focus\n.tab-focus() {\n  // Default\n  outline: thin dotted;\n  // WebKit\n  outline: 5px auto -webkit-focus-ring-color;\n  outline-offset: -2px;\n}\n\n// Center-align a block level element\n.center-block() {\n  display: block;\n  margin-left: auto;\n  margin-right: auto;\n}\n\n// Sizing shortcuts\n.size(@width; @height) {\n  width: @width;\n  height: @height;\n}\n.square(@size) {\n  .size(@size; @size);\n}\n\n// Placeholder text\n.placeholder(@color: @input-color-placeholder) {\n  &::-moz-placeholder           { color: @color;   // Firefox\n                                  opacity: 1; } // See https://github.com/twbs/bootstrap/pull/11526\n  &:-ms-input-placeholder       { color: @color; } // Internet Explorer 10+\n  &::-webkit-input-placeholder  { color: @color; } // Safari and Chrome\n}\n\n// Text overflow\n// Requires inline-block or block for proper styling\n.text-overflow() {\n  overflow: hidden;\n  text-overflow: ellipsis;\n  white-space: nowrap;\n}\n\n// CSS image replacement\n//\n// Heads up! v3 launched with with only `.hide-text()`, but per our pattern for\n// mixins being reused as classes with the same name, this doesn't hold up. As\n// of v3.0.1 we have added `.text-hide()` and deprecated `.hide-text()`. Note\n// that we cannot chain the mixins together in Less, so they are repeated.\n//\n// Source: https://github.com/h5bp/html5-boilerplate/commit/aa0396eae757\n\n// Deprecated as of v3.0.1 (will be removed in v4)\n.hide-text() {\n  font: ~\"0/0\" a;\n  color: transparent;\n  text-shadow: none;\n  background-color: transparent;\n  border: 0;\n}\n// New mixin to use as of v3.0.1\n.text-hide() {\n  .hide-text();\n}\n\n\n\n// CSS3 PROPERTIES\n// --------------------------------------------------\n\n// Single side border-radius\n.border-top-radius(@radius) {\n  border-top-right-radius: @radius;\n   border-top-left-radius: @radius;\n}\n.border-right-radius(@radius) {\n  border-bottom-right-radius: @radius;\n     border-top-right-radius: @radius;\n}\n.border-bottom-radius(@radius) {\n  border-bottom-right-radius: @radius;\n   border-bottom-left-radius: @radius;\n}\n.border-left-radius(@radius) {\n  border-bottom-left-radius: @radius;\n     border-top-left-radius: @radius;\n}\n\n// Drop shadows\n//\n// Note: Deprecated `.box-shadow()` as of v3.1.0 since all of Bootstrap's\n//   supported browsers that have box shadow capabilities now support the\n//   standard `box-shadow` property.\n.box-shadow(@shadow) {\n  -webkit-box-shadow: @shadow; // iOS <4.3 & Android <4.1\n          box-shadow: @shadow;\n}\n\n// Transitions\n.transition(@transition) {\n  -webkit-transition: @transition;\n          transition: @transition;\n}\n.transition-property(@transition-property) {\n  -webkit-transition-property: @transition-property;\n          transition-property: @transition-property;\n}\n.transition-delay(@transition-delay) {\n  -webkit-transition-delay: @transition-delay;\n          transition-delay: @transition-delay;\n}\n.transition-duration(@transition-duration) {\n  -webkit-transition-duration: @transition-duration;\n          transition-duration: @transition-duration;\n}\n.transition-transform(@transition) {\n  -webkit-transition: -webkit-transform @transition;\n     -moz-transition: -moz-transform @transition;\n       -o-transition: -o-transform @transition;\n          transition: transform @transition;\n}\n\n// Transformations\n.rotate(@degrees) {\n  -webkit-transform: rotate(@degrees);\n      -ms-transform: rotate(@degrees); // IE9 only\n          transform: rotate(@degrees);\n}\n.scale(@ratio; @ratio-y...) {\n  -webkit-transform: scale(@ratio, @ratio-y);\n      -ms-transform: scale(@ratio, @ratio-y); // IE9 only\n          transform: scale(@ratio, @ratio-y);\n}\n.translate(@x; @y) {\n  -webkit-transform: translate(@x, @y);\n      -ms-transform: translate(@x, @y); // IE9 only\n          transform: translate(@x, @y);\n}\n.skew(@x; @y) {\n  -webkit-transform: skew(@x, @y);\n      -ms-transform: skewX(@x) skewY(@y); // See https://github.com/twbs/bootstrap/issues/4885; IE9+\n          transform: skew(@x, @y);\n}\n.translate3d(@x; @y; @z) {\n  -webkit-transform: translate3d(@x, @y, @z);\n          transform: translate3d(@x, @y, @z);\n}\n\n.rotateX(@degrees) {\n  -webkit-transform: rotateX(@degrees);\n      -ms-transform: rotateX(@degrees); // IE9 only\n          transform: rotateX(@degrees);\n}\n.rotateY(@degrees) {\n  -webkit-transform: rotateY(@degrees);\n      -ms-transform: rotateY(@degrees); // IE9 only\n          transform: rotateY(@degrees);\n}\n.perspective(@perspective) {\n  -webkit-perspective: @perspective;\n     -moz-perspective: @perspective;\n          perspective: @perspective;\n}\n.perspective-origin(@perspective) {\n  -webkit-perspective-origin: @perspective;\n     -moz-perspective-origin: @perspective;\n          perspective-origin: @perspective;\n}\n.transform-origin(@origin) {\n  -webkit-transform-origin: @origin;\n     -moz-transform-origin: @origin;\n      -ms-transform-origin: @origin; // IE9 only\n          transform-origin: @origin;\n}\n\n// Animations\n.animation(@animation) {\n  -webkit-animation: @animation;\n          animation: @animation;\n}\n.animation-name(@name) {\n  -webkit-animation-name: @name;\n          animation-name: @name;\n}\n.animation-duration(@duration) {\n  -webkit-animation-duration: @duration;\n          animation-duration: @duration;\n}\n.animation-timing-function(@timing-function) {\n  -webkit-animation-timing-function: @timing-function;\n          animation-timing-function: @timing-function;\n}\n.animation-delay(@delay) {\n  -webkit-animation-delay: @delay;\n          animation-delay: @delay;\n}\n.animation-iteration-count(@iteration-count) {\n  -webkit-animation-iteration-count: @iteration-count;\n          animation-iteration-count: @iteration-count;\n}\n.animation-direction(@direction) {\n  -webkit-animation-direction: @direction;\n          animation-direction: @direction;\n}\n\n// Backface visibility\n// Prevent browsers from flickering when using CSS 3D transforms.\n// Default value is `visible`, but can be changed to `hidden`\n.backface-visibility(@visibility){\n  -webkit-backface-visibility: @visibility;\n     -moz-backface-visibility: @visibility;\n          backface-visibility: @visibility;\n}\n\n// Box sizing\n.box-sizing(@boxmodel) {\n  -webkit-box-sizing: @boxmodel;\n     -moz-box-sizing: @boxmodel;\n          box-sizing: @boxmodel;\n}\n\n// User select\n// For selecting text on the page\n.user-select(@select) {\n  -webkit-user-select: @select;\n     -moz-user-select: @select;\n      -ms-user-select: @select; // IE10+\n          user-select: @select;\n}\n\n// Resize anything\n.resizable(@direction) {\n  resize: @direction; // Options: horizontal, vertical, both\n  overflow: auto; // Safari fix\n}\n\n// CSS3 Content Columns\n.content-columns(@column-count; @column-gap: @grid-gutter-width) {\n  -webkit-column-count: @column-count;\n     -moz-column-count: @column-count;\n          column-count: @column-count;\n  -webkit-column-gap: @column-gap;\n     -moz-column-gap: @column-gap;\n          column-gap: @column-gap;\n}\n\n// Optional hyphenation\n.hyphens(@mode: auto) {\n  word-wrap: break-word;\n  -webkit-hyphens: @mode;\n     -moz-hyphens: @mode;\n      -ms-hyphens: @mode; // IE10+\n       -o-hyphens: @mode;\n          hyphens: @mode;\n}\n\n// Opacity\n.opacity(@opacity) {\n  opacity: @opacity;\n  // IE8 filter\n  @opacity-ie: (@opacity * 100);\n  filter: ~\"alpha(opacity=@{opacity-ie})\";\n}\n\n\n\n// GRADIENTS\n// --------------------------------------------------\n\n#gradient {\n\n  // Horizontal gradient, from left to right\n  //\n  // Creates two color stops, start and end, by specifying a color and position for each color stop.\n  // Color stops are not available in IE9 and below.\n  .horizontal(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n    background-image: -webkit-linear-gradient(left, color-stop(@start-color @start-percent), color-stop(@end-color @end-percent)); // Safari 5.1-6, Chrome 10+\n    background-image:  linear-gradient(to right, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n    background-repeat: repeat-x;\n    filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down\n  }\n\n  // Vertical gradient, from top to bottom\n  //\n  // Creates two color stops, start and end, by specifying a color and position for each color stop.\n  // Color stops are not available in IE9 and below.\n  .vertical(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n    background-image: -webkit-linear-gradient(top, @start-color @start-percent, @end-color @end-percent);  // Safari 5.1-6, Chrome 10+\n    background-image: linear-gradient(to bottom, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n    background-repeat: repeat-x;\n    filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down\n  }\n\n  .directional(@start-color: #555; @end-color: #333; @deg: 45deg) {\n    background-repeat: repeat-x;\n    background-image: -webkit-linear-gradient(@deg, @start-color, @end-color); // Safari 5.1-6, Chrome 10+\n    background-image: linear-gradient(@deg, @start-color, @end-color); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n  }\n  .horizontal-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n    background-image: -webkit-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n    background-image: linear-gradient(to right, @start-color, @mid-color @color-stop, @end-color);\n    background-repeat: no-repeat;\n    filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n  }\n  .vertical-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n    background-image: -webkit-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n    background-image: linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n    background-repeat: no-repeat;\n    filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n  }\n  .radial(@inner-color: #555; @outer-color: #333) {\n    background-image: -webkit-radial-gradient(circle, @inner-color, @outer-color);\n    background-image: radial-gradient(circle, @inner-color, @outer-color);\n    background-repeat: no-repeat;\n  }\n  .striped(@color: rgba(255,255,255,.15); @angle: 45deg) {\n    background-image: -webkit-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n    background-image: linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n  }\n}\n\n// Reset filters for IE\n//\n// When you need to remove a gradient background, do not forget to use this to reset\n// the IE filter for IE9 and below.\n.reset-filter() {\n  filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(enabled = false)\"));\n}\n\n\n\n// Retina images\n//\n// Short retina mixin for setting background-image and -size\n\n.img-retina(@file-1x; @file-2x; @width-1x; @height-1x) {\n  background-image: url(\"@{file-1x}\");\n\n  @media\n  only screen and (-webkit-min-device-pixel-ratio: 2),\n  only screen and (   min--moz-device-pixel-ratio: 2),\n  only screen and (     -o-min-device-pixel-ratio: 2/1),\n  only screen and (        min-device-pixel-ratio: 2),\n  only screen and (                min-resolution: 192dpi),\n  only screen and (                min-resolution: 2dppx) {\n    background-image: url(\"@{file-2x}\");\n    background-size: @width-1x @height-1x;\n  }\n}\n\n\n// Responsive image\n//\n// Keep images from scaling beyond the width of their parents.\n\n.img-responsive(@display: block) {\n  display: @display;\n  max-width: 100%; // Part 1: Set a maximum relative to the parent\n  height: auto; // Part 2: Scale the height according to the width, otherwise you get stretching\n}\n\n\n// COMPONENT MIXINS\n// --------------------------------------------------\n\n// Horizontal dividers\n// -------------------------\n// Dividers (basically an hr) within dropdowns and nav lists\n.nav-divider(@color: #e5e5e5) {\n  height: 1px;\n  margin: ((@line-height-computed / 2) - 1) 0;\n  overflow: hidden;\n  background-color: @color;\n}\n\n// Panels\n// -------------------------\n.panel-variant(@border; @heading-text-color; @heading-bg-color; @heading-border) {\n  border-color: @border;\n\n  & > .panel-heading {\n    color: @heading-text-color;\n    background-color: @heading-bg-color;\n    border-color: @heading-border;\n\n    + .panel-collapse .panel-body {\n      border-top-color: @border;\n    }\n  }\n  & > .panel-footer {\n    + .panel-collapse .panel-body {\n      border-bottom-color: @border;\n    }\n  }\n}\n\n// Alerts\n// -------------------------\n.alert-variant(@background; @border; @text-color) {\n  background-color: @background;\n  border-color: @border;\n  color: @text-color;\n\n  hr {\n    border-top-color: darken(@border, 5%);\n  }\n  .alert-link {\n    color: darken(@text-color, 10%);\n  }\n}\n\n// Tables\n// -------------------------\n.table-row-variant(@state; @background) {\n  // Exact selectors below required to override `.table-striped` and prevent\n  // inheritance to nested tables.\n  .table > thead > tr,\n  .table > tbody > tr,\n  .table > tfoot > tr {\n    > td.@{state},\n    > th.@{state},\n    &.@{state} > td,\n    &.@{state} > th {\n      background-color: @background;\n    }\n  }\n\n  // Hover states for `.table-hover`\n  // Note: this is not available for cells or rows within `thead` or `tfoot`.\n  .table-hover > tbody > tr {\n    > td.@{state}:hover,\n    > th.@{state}:hover,\n    &.@{state}:hover > td,\n    &.@{state}:hover > th {\n      background-color: darken(@background, 5%);\n    }\n  }\n}\n\n// List Groups\n// -------------------------\n.list-group-item-variant(@state; @background; @color) {\n  .list-group-item-@{state} {\n    color: @color;\n    background-color: @background;\n\n    a& {\n      color: @color;\n\n      .list-group-item-heading { color: inherit; }\n\n      &:hover,\n      &:focus {\n        color: @color;\n        background-color: darken(@background, 5%);\n      }\n      &.active,\n      &.active:hover,\n      &.active:focus {\n        color: #fff;\n        background-color: @color;\n        border-color: @color;\n      }\n    }\n  }\n}\n\n// Button variants\n// -------------------------\n// Easily pump out default styles, as well as :hover, :focus, :active,\n// and disabled options for all buttons\n.button-variant(@color; @background; @border) {\n  color: @color;\n  background-color: @background;\n  border-color: @border;\n\n  &:hover,\n  &:focus,\n  &:active,\n  &.active,\n  .open .dropdown-toggle& {\n    color: @color;\n    background-color: darken(@background, 8%);\n        border-color: darken(@border, 12%);\n  }\n  &:active,\n  &.active,\n  .open .dropdown-toggle& {\n    background-image: none;\n  }\n  &.disabled,\n  &[disabled],\n  fieldset[disabled] & {\n    &,\n    &:hover,\n    &:focus,\n    &:active,\n    &.active {\n      background-color: @background;\n          border-color: @border;\n    }\n  }\n\n  .badge {\n    color: @background;\n    background-color: @color;\n  }\n}\n\n// Button sizes\n// -------------------------\n.button-size(@padding-vertical; @padding-horizontal; @font-size; @line-height; @border-radius) {\n  padding: @padding-vertical @padding-horizontal;\n  font-size: @font-size;\n  line-height: @line-height;\n  border-radius: @border-radius;\n}\n\n// Pagination\n// -------------------------\n.pagination-size(@padding-vertical; @padding-horizontal; @font-size; @border-radius) {\n  > li {\n    > a,\n    > span {\n      padding: @padding-vertical @padding-horizontal;\n      font-size: @font-size;\n    }\n    &:first-child {\n      > a,\n      > span {\n        .border-left-radius(@border-radius);\n      }\n    }\n    &:last-child {\n      > a,\n      > span {\n        .border-right-radius(@border-radius);\n      }\n    }\n  }\n}\n\n// Labels\n// -------------------------\n.label-variant(@color) {\n  background-color: @color;\n  &[href] {\n    &:hover,\n    &:focus {\n      background-color: darken(@color, 10%);\n    }\n  }\n}\n\n// Contextual backgrounds\n// -------------------------\n.bg-variant(@color) {\n  background-color: @color;\n  a&:hover {\n    background-color: darken(@color, 10%);\n  }\n}\n\n// Typography\n// -------------------------\n.text-emphasis-variant(@color) {\n  color: @color;\n  a&:hover {\n    color: darken(@color, 10%);\n  }\n}\n\n// Navbar vertical align\n// -------------------------\n// Vertically center elements in the navbar.\n// Example: an element has a height of 30px, so write out `.navbar-vertical-align(30px);` to calculate the appropriate top margin.\n.navbar-vertical-align(@element-height) {\n  margin-top: ((@navbar-height - @element-height) / 2);\n  margin-bottom: ((@navbar-height - @element-height) / 2);\n}\n\n// Progress bars\n// -------------------------\n.progress-bar-variant(@color) {\n  background-color: @color;\n  .progress-striped & {\n    #gradient > .striped();\n  }\n}\n\n// Responsive utilities\n// -------------------------\n// More easily include all the states for responsive-utilities.less.\n.responsive-visibility() {\n  display: block !important;\n  table&  { display: table; }\n  tr&     { display: table-row !important; }\n  th&,\n  td&     { display: table-cell !important; }\n}\n\n.responsive-invisibility() {\n  display: none !important;\n}\n\n\n// Grid System\n// -----------\n\n// Centered container element\n.container-fixed() {\n  margin-right: auto;\n  margin-left: auto;\n  padding-left:  (@grid-gutter-width / 2);\n  padding-right: (@grid-gutter-width / 2);\n  &:extend(.clearfix all);\n}\n\n// Creates a wrapper for a series of columns\n.make-row(@gutter: @grid-gutter-width) {\n  margin-left:  (@gutter / -2);\n  margin-right: (@gutter / -2);\n  &:extend(.clearfix all);\n}\n\n// Generate the extra small columns\n.make-xs-column(@columns; @gutter: @grid-gutter-width) {\n  position: relative;\n  float: left;\n  width: percentage((@columns / @grid-columns));\n  min-height: 1px;\n  padding-left:  (@gutter / 2);\n  padding-right: (@gutter / 2);\n}\n.make-xs-column-offset(@columns) {\n  @media (min-width: @screen-xs-min) {\n    margin-left: percentage((@columns / @grid-columns));\n  }\n}\n.make-xs-column-push(@columns) {\n  @media (min-width: @screen-xs-min) {\n    left: percentage((@columns / @grid-columns));\n  }\n}\n.make-xs-column-pull(@columns) {\n  @media (min-width: @screen-xs-min) {\n    right: percentage((@columns / @grid-columns));\n  }\n}\n\n\n// Generate the small columns\n.make-sm-column(@columns; @gutter: @grid-gutter-width) {\n  position: relative;\n  min-height: 1px;\n  padding-left:  (@gutter / 2);\n  padding-right: (@gutter / 2);\n\n  @media (min-width: @screen-sm-min) {\n    float: left;\n    width: percentage((@columns / @grid-columns));\n  }\n}\n.make-sm-column-offset(@columns) {\n  @media (min-width: @screen-sm-min) {\n    margin-left: percentage((@columns / @grid-columns));\n  }\n}\n.make-sm-column-push(@columns) {\n  @media (min-width: @screen-sm-min) {\n    left: percentage((@columns / @grid-columns));\n  }\n}\n.make-sm-column-pull(@columns) {\n  @media (min-width: @screen-sm-min) {\n    right: percentage((@columns / @grid-columns));\n  }\n}\n\n\n// Generate the medium columns\n.make-md-column(@columns; @gutter: @grid-gutter-width) {\n  position: relative;\n  min-height: 1px;\n  padding-left:  (@gutter / 2);\n  padding-right: (@gutter / 2);\n\n  @media (min-width: @screen-md-min) {\n    float: left;\n    width: percentage((@columns / @grid-columns));\n  }\n}\n.make-md-column-offset(@columns) {\n  @media (min-width: @screen-md-min) {\n    margin-left: percentage((@columns / @grid-columns));\n  }\n}\n.make-md-column-push(@columns) {\n  @media (min-width: @screen-md-min) {\n    left: percentage((@columns / @grid-columns));\n  }\n}\n.make-md-column-pull(@columns) {\n  @media (min-width: @screen-md-min) {\n    right: percentage((@columns / @grid-columns));\n  }\n}\n\n\n// Generate the large columns\n.make-lg-column(@columns; @gutter: @grid-gutter-width) {\n  position: relative;\n  min-height: 1px;\n  padding-left:  (@gutter / 2);\n  padding-right: (@gutter / 2);\n\n  @media (min-width: @screen-lg-min) {\n    float: left;\n    width: percentage((@columns / @grid-columns));\n  }\n}\n.make-lg-column-offset(@columns) {\n  @media (min-width: @screen-lg-min) {\n    margin-left: percentage((@columns / @grid-columns));\n  }\n}\n.make-lg-column-push(@columns) {\n  @media (min-width: @screen-lg-min) {\n    left: percentage((@columns / @grid-columns));\n  }\n}\n.make-lg-column-pull(@columns) {\n  @media (min-width: @screen-lg-min) {\n    right: percentage((@columns / @grid-columns));\n  }\n}\n\n\n// Framework grid generation\n//\n// Used only by Bootstrap to generate the correct number of grid classes given\n// any value of `@grid-columns`.\n\n.make-grid-columns() {\n  // Common styles for all sizes of grid columns, widths 1-12\n  .col(@index) when (@index = 1) { // initial\n    @item: ~\".col-xs-@{index}, .col-sm-@{index}, .col-md-@{index}, .col-lg-@{index}\";\n    .col((@index + 1), @item);\n  }\n  .col(@index, @list) when (@index =< @grid-columns) { // general; \"=<\" isn't a typo\n    @item: ~\".col-xs-@{index}, .col-sm-@{index}, .col-md-@{index}, .col-lg-@{index}\";\n    .col((@index + 1), ~\"@{list}, @{item}\");\n  }\n  .col(@index, @list) when (@index > @grid-columns) { // terminal\n    @{list} {\n      position: relative;\n      // Prevent columns from collapsing when empty\n      min-height: 1px;\n      // Inner gutter via padding\n      padding-left:  (@grid-gutter-width / 2);\n      padding-right: (@grid-gutter-width / 2);\n    }\n  }\n  .col(1); // kickstart it\n}\n\n.float-grid-columns(@class) {\n  .col(@index) when (@index = 1) { // initial\n    @item: ~\".col-@{class}-@{index}\";\n    .col((@index + 1), @item);\n  }\n  .col(@index, @list) when (@index =< @grid-columns) { // general\n    @item: ~\".col-@{class}-@{index}\";\n    .col((@index + 1), ~\"@{list}, @{item}\");\n  }\n  .col(@index, @list) when (@index > @grid-columns) { // terminal\n    @{list} {\n      float: left;\n    }\n  }\n  .col(1); // kickstart it\n}\n\n.calc-grid-column(@index, @class, @type) when (@type = width) and (@index > 0) {\n  .col-@{class}-@{index} {\n    width: percentage((@index / @grid-columns));\n  }\n}\n.calc-grid-column(@index, @class, @type) when (@type = push) {\n  .col-@{class}-push-@{index} {\n    left: percentage((@index / @grid-columns));\n  }\n}\n.calc-grid-column(@index, @class, @type) when (@type = pull) {\n  .col-@{class}-pull-@{index} {\n    right: percentage((@index / @grid-columns));\n  }\n}\n.calc-grid-column(@index, @class, @type) when (@type = offset) {\n  .col-@{class}-offset-@{index} {\n    margin-left: percentage((@index / @grid-columns));\n  }\n}\n\n// Basic looping in LESS\n.loop-grid-columns(@index, @class, @type) when (@index >= 0) {\n  .calc-grid-column(@index, @class, @type);\n  // next iteration\n  .loop-grid-columns((@index - 1), @class, @type);\n}\n\n// Create grid for specific class\n.make-grid(@class) {\n  .float-grid-columns(@class);\n  .loop-grid-columns(@grid-columns, @class, width);\n  .loop-grid-columns(@grid-columns, @class, pull);\n  .loop-grid-columns(@grid-columns, @class, push);\n  .loop-grid-columns(@grid-columns, @class, offset);\n}\n\n// Form validation states\n//\n// Used in forms.less to generate the form validation CSS for warnings, errors,\n// and successes.\n\n.form-control-validation(@text-color: #555; @border-color: #ccc; @background-color: #f5f5f5) {\n  // Color the label and help text\n  .help-block,\n  .control-label,\n  .radio,\n  .checkbox,\n  .radio-inline,\n  .checkbox-inline  {\n    color: @text-color;\n  }\n  // Set the border and box shadow on specific inputs to match\n  .form-control {\n    border-color: @border-color;\n    .box-shadow(inset 0 1px 1px rgba(0,0,0,.075)); // Redeclare so transitions work\n    &:focus {\n      border-color: darken(@border-color, 10%);\n      @shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 6px lighten(@border-color, 20%);\n      .box-shadow(@shadow);\n    }\n  }\n  // Set validation states also for addons\n  .input-group-addon {\n    color: @text-color;\n    border-color: @border-color;\n    background-color: @background-color;\n  }\n  // Optional feedback icon\n  .form-control-feedback {\n    color: @text-color;\n  }\n}\n\n// Form control focus state\n//\n// Generate a customized focus state and for any input with the specified color,\n// which defaults to the `@input-focus-border` variable.\n//\n// We highly encourage you to not customize the default value, but instead use\n// this to tweak colors on an as-needed basis. This aesthetic change is based on\n// WebKit's default styles, but applicable to a wider range of browsers. Its\n// usability and accessibility should be taken into account with any change.\n//\n// Example usage: change the default blue border and shadow to white for better\n// contrast against a dark gray background.\n\n.form-control-focus(@color: @input-border-focus) {\n  @color-rgba: rgba(red(@color), green(@color), blue(@color), .6);\n  &:focus {\n    border-color: @color;\n    outline: 0;\n    .box-shadow(~\"inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px @{color-rgba}\");\n  }\n}\n\n// Form control sizing\n//\n// Relative text size, padding, and border-radii changes for form controls. For\n// horizontal sizing, wrap controls in the predefined grid classes. `<select>`\n// element gets special love because it's special, and that's a fact!\n\n.input-size(@input-height; @padding-vertical; @padding-horizontal; @font-size; @line-height; @border-radius) {\n  height: @input-height;\n  padding: @padding-vertical @padding-horizontal;\n  font-size: @font-size;\n  line-height: @line-height;\n  border-radius: @border-radius;\n\n  select& {\n    height: @input-height;\n    line-height: @input-height;\n  }\n\n  textarea&,\n  select[multiple]& {\n    height: auto;\n  }\n}\n","//\n// Variables\n// --------------------------------------------------\n\n\n//== Colors\n//\n//## Gray and brand colors for use across Bootstrap.\n\n@gray-darker:            lighten(#000, 13.5%); // #222\n@gray-dark:              lighten(#000, 20%);   // #333\n@gray:                   lighten(#000, 33.5%); // #555\n@gray-light:             lighten(#000, 60%);   // #999\n@gray-lighter:           lighten(#000, 93.5%); // #eee\n\n@brand-primary:         #428bca;\n@brand-success:         #5cb85c;\n@brand-info:            #5bc0de;\n@brand-warning:         #f0ad4e;\n@brand-danger:          #d9534f;\n\n\n//== Scaffolding\n//\n// ## Settings for some of the most global styles.\n\n//** Background color for `<body>`.\n@body-bg:               #fff;\n//** Global text color on `<body>`.\n@text-color:            @gray-dark;\n\n//** Global textual link color.\n@link-color:            @brand-primary;\n//** Link hover color set via `darken()` function.\n@link-hover-color:      darken(@link-color, 15%);\n\n\n//== Typography\n//\n//## Font, line-height, and color for body text, headings, and more.\n\n@font-family-sans-serif:  \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n@font-family-serif:       Georgia, \"Times New Roman\", Times, serif;\n//** Default monospace fonts for `<code>`, `<kbd>`, and `<pre>`.\n@font-family-monospace:   Menlo, Monaco, Consolas, \"Courier New\", monospace;\n@font-family-base:        @font-family-sans-serif;\n\n@font-size-base:          14px;\n@font-size-large:         ceil((@font-size-base * 1.25)); // ~18px\n@font-size-small:         ceil((@font-size-base * 0.85)); // ~12px\n\n@font-size-h1:            floor((@font-size-base * 2.6)); // ~36px\n@font-size-h2:            floor((@font-size-base * 2.15)); // ~30px\n@font-size-h3:            ceil((@font-size-base * 1.7)); // ~24px\n@font-size-h4:            ceil((@font-size-base * 1.25)); // ~18px\n@font-size-h5:            @font-size-base;\n@font-size-h6:            ceil((@font-size-base * 0.85)); // ~12px\n\n//** Unit-less `line-height` for use in components like buttons.\n@line-height-base:        1.428571429; // 20/14\n//** Computed \"line-height\" (`font-size` * `line-height`) for use with `margin`, `padding`, etc.\n@line-height-computed:    floor((@font-size-base * @line-height-base)); // ~20px\n\n//** By default, this inherits from the `<body>`.\n@headings-font-family:    inherit;\n@headings-font-weight:    500;\n@headings-line-height:    1.1;\n@headings-color:          inherit;\n\n\n//-- Iconography\n//\n//## Specify custom locations of the include Glyphicons icon font. Useful for those including Bootstrap via Bower.\n\n@icon-font-path:          \"../fonts/\";\n@icon-font-name:          \"glyphicons-halflings-regular\";\n@icon-font-svg-id:        \"glyphicons_halflingsregular\";\n\n//== Components\n//\n//## Define common padding and border radius sizes and more. Values based on 14px text and 1.428 line-height (~20px to start).\n\n@padding-base-vertical:     6px;\n@padding-base-horizontal:   12px;\n\n@padding-large-vertical:    10px;\n@padding-large-horizontal:  16px;\n\n@padding-small-vertical:    5px;\n@padding-small-horizontal:  10px;\n\n@padding-xs-vertical:       1px;\n@padding-xs-horizontal:     5px;\n\n@line-height-large:         1.33;\n@line-height-small:         1.5;\n\n@border-radius-base:        4px;\n@border-radius-large:       6px;\n@border-radius-small:       3px;\n\n//** Global color for active items (e.g., navs or dropdowns).\n@component-active-color:    #fff;\n//** Global background color for active items (e.g., navs or dropdowns).\n@component-active-bg:       @brand-primary;\n\n//** Width of the `border` for generating carets that indicator dropdowns.\n@caret-width-base:          4px;\n//** Carets increase slightly in size for larger components.\n@caret-width-large:         5px;\n\n\n//== Tables\n//\n//## Customizes the `.table` component with basic values, each used across all table variations.\n\n//** Padding for `<th>`s and `<td>`s.\n@table-cell-padding:            8px;\n//** Padding for cells in `.table-condensed`.\n@table-condensed-cell-padding:  5px;\n\n//** Default background color used for all tables.\n@table-bg:                      transparent;\n//** Background color used for `.table-striped`.\n@table-bg-accent:               #f9f9f9;\n//** Background color used for `.table-hover`.\n@table-bg-hover:                #f5f5f5;\n@table-bg-active:               @table-bg-hover;\n\n//** Border color for table and cell borders.\n@table-border-color:            #ddd;\n\n\n//== Buttons\n//\n//## For each of Bootstrap's buttons, define text, background and border color.\n\n@btn-font-weight:                normal;\n\n@btn-default-color:              #333;\n@btn-default-bg:                 #fff;\n@btn-default-border:             #ccc;\n\n@btn-primary-color:              #fff;\n@btn-primary-bg:                 @brand-primary;\n@btn-primary-border:             darken(@btn-primary-bg, 5%);\n\n@btn-success-color:              #fff;\n@btn-success-bg:                 @brand-success;\n@btn-success-border:             darken(@btn-success-bg, 5%);\n\n@btn-info-color:                 #fff;\n@btn-info-bg:                    @brand-info;\n@btn-info-border:                darken(@btn-info-bg, 5%);\n\n@btn-warning-color:              #fff;\n@btn-warning-bg:                 @brand-warning;\n@btn-warning-border:             darken(@btn-warning-bg, 5%);\n\n@btn-danger-color:               #fff;\n@btn-danger-bg:                  @brand-danger;\n@btn-danger-border:              darken(@btn-danger-bg, 5%);\n\n@btn-link-disabled-color:        @gray-light;\n\n\n//== Forms\n//\n//##\n\n//** `<input>` background color\n@input-bg:                       #fff;\n//** `<input disabled>` background color\n@input-bg-disabled:              @gray-lighter;\n\n//** Text color for `<input>`s\n@input-color:                    @gray;\n//** `<input>` border color\n@input-border:                   #ccc;\n//** `<input>` border radius\n@input-border-radius:            @border-radius-base;\n//** Border color for inputs on focus\n@input-border-focus:             #66afe9;\n\n//** Placeholder text color\n@input-color-placeholder:        @gray-light;\n\n//** Default `.form-control` height\n@input-height-base:              (@line-height-computed + (@padding-base-vertical * 2) + 2);\n//** Large `.form-control` height\n@input-height-large:             (ceil(@font-size-large * @line-height-large) + (@padding-large-vertical * 2) + 2);\n//** Small `.form-control` height\n@input-height-small:             (floor(@font-size-small * @line-height-small) + (@padding-small-vertical * 2) + 2);\n\n@legend-color:                   @gray-dark;\n@legend-border-color:            #e5e5e5;\n\n//** Background color for textual input addons\n@input-group-addon-bg:           @gray-lighter;\n//** Border color for textual input addons\n@input-group-addon-border-color: @input-border;\n\n\n//== Dropdowns\n//\n//## Dropdown menu container and contents.\n\n//** Background for the dropdown menu.\n@dropdown-bg:                    #fff;\n//** Dropdown menu `border-color`.\n@dropdown-border:                rgba(0,0,0,.15);\n//** Dropdown menu `border-color` **for IE8**.\n@dropdown-fallback-border:       #ccc;\n//** Divider color for between dropdown items.\n@dropdown-divider-bg:            #e5e5e5;\n\n//** Dropdown link text color.\n@dropdown-link-color:            @gray-dark;\n//** Hover color for dropdown links.\n@dropdown-link-hover-color:      darken(@gray-dark, 5%);\n//** Hover background for dropdown links.\n@dropdown-link-hover-bg:         #f5f5f5;\n\n//** Active dropdown menu item text color.\n@dropdown-link-active-color:     @component-active-color;\n//** Active dropdown menu item background color.\n@dropdown-link-active-bg:        @component-active-bg;\n\n//** Disabled dropdown menu item background color.\n@dropdown-link-disabled-color:   @gray-light;\n\n//** Text color for headers within dropdown menus.\n@dropdown-header-color:          @gray-light;\n\n// Note: Deprecated @dropdown-caret-color as of v3.1.0\n@dropdown-caret-color:           #000;\n\n\n//-- Z-index master list\n//\n// Warning: Avoid customizing these values. They're used for a bird's eye view\n// of components dependent on the z-axis and are designed to all work together.\n//\n// Note: These variables are not generated into the Customizer.\n\n@zindex-navbar:            1000;\n@zindex-dropdown:          1000;\n@zindex-popover:           1010;\n@zindex-tooltip:           1030;\n@zindex-navbar-fixed:      1030;\n@zindex-modal-background:  1040;\n@zindex-modal:             1050;\n\n\n//== Media queries breakpoints\n//\n//## Define the breakpoints at which your layout will change, adapting to different screen sizes.\n\n// Extra small screen / phone\n// Note: Deprecated @screen-xs and @screen-phone as of v3.0.1\n@screen-xs:                  480px;\n@screen-xs-min:              @screen-xs;\n@screen-phone:               @screen-xs-min;\n\n// Small screen / tablet\n// Note: Deprecated @screen-sm and @screen-tablet as of v3.0.1\n@screen-sm:                  768px;\n@screen-sm-min:              @screen-sm;\n@screen-tablet:              @screen-sm-min;\n\n// Medium screen / desktop\n// Note: Deprecated @screen-md and @screen-desktop as of v3.0.1\n@screen-md:                  992px;\n@screen-md-min:              @screen-md;\n@screen-desktop:             @screen-md-min;\n\n// Large screen / wide desktop\n// Note: Deprecated @screen-lg and @screen-lg-desktop as of v3.0.1\n@screen-lg:                  1200px;\n@screen-lg-min:              @screen-lg;\n@screen-lg-desktop:          @screen-lg-min;\n\n// So media queries don't overlap when required, provide a maximum\n@screen-xs-max:              (@screen-sm-min - 1);\n@screen-sm-max:              (@screen-md-min - 1);\n@screen-md-max:              (@screen-lg-min - 1);\n\n\n//== Grid system\n//\n//## Define your custom responsive grid.\n\n//** Number of columns in the grid.\n@grid-columns:              12;\n//** Padding between columns. Gets divided in half for the left and right.\n@grid-gutter-width:         30px;\n// Navbar collapse\n//** Point at which the navbar becomes uncollapsed.\n@grid-float-breakpoint:     @screen-sm-min;\n//** Point at which the navbar begins collapsing.\n@grid-float-breakpoint-max: (@grid-float-breakpoint - 1);\n\n\n//== Container sizes\n//\n//## Define the maximum width of `.container` for different screen sizes.\n\n// Small screen / tablet\n@container-tablet:             ((720px + @grid-gutter-width));\n//** For `@screen-sm-min` and up.\n@container-sm:                 @container-tablet;\n\n// Medium screen / desktop\n@container-desktop:            ((940px + @grid-gutter-width));\n//** For `@screen-md-min` and up.\n@container-md:                 @container-desktop;\n\n// Large screen / wide desktop\n@container-large-desktop:      ((1140px + @grid-gutter-width));\n//** For `@screen-lg-min` and up.\n@container-lg:                 @container-large-desktop;\n\n\n//== Navbar\n//\n//##\n\n// Basics of a navbar\n@navbar-height:                    50px;\n@navbar-margin-bottom:             @line-height-computed;\n@navbar-border-radius:             @border-radius-base;\n@navbar-padding-horizontal:        floor((@grid-gutter-width / 2));\n@navbar-padding-vertical:          ((@navbar-height - @line-height-computed) / 2);\n@navbar-collapse-max-height:       340px;\n\n@navbar-default-color:             #777;\n@navbar-default-bg:                #f8f8f8;\n@navbar-default-border:            darken(@navbar-default-bg, 6.5%);\n\n// Navbar links\n@navbar-default-link-color:                #777;\n@navbar-default-link-hover-color:          #333;\n@navbar-default-link-hover-bg:             transparent;\n@navbar-default-link-active-color:         #555;\n@navbar-default-link-active-bg:            darken(@navbar-default-bg, 6.5%);\n@navbar-default-link-disabled-color:       #ccc;\n@navbar-default-link-disabled-bg:          transparent;\n\n// Navbar brand label\n@navbar-default-brand-color:               @navbar-default-link-color;\n@navbar-default-brand-hover-color:         darken(@navbar-default-brand-color, 10%);\n@navbar-default-brand-hover-bg:            transparent;\n\n// Navbar toggle\n@navbar-default-toggle-hover-bg:           #ddd;\n@navbar-default-toggle-icon-bar-bg:        #888;\n@navbar-default-toggle-border-color:       #ddd;\n\n\n// Inverted navbar\n// Reset inverted navbar basics\n@navbar-inverse-color:                      @gray-light;\n@navbar-inverse-bg:                         #222;\n@navbar-inverse-border:                     darken(@navbar-inverse-bg, 10%);\n\n// Inverted navbar links\n@navbar-inverse-link-color:                 @gray-light;\n@navbar-inverse-link-hover-color:           #fff;\n@navbar-inverse-link-hover-bg:              transparent;\n@navbar-inverse-link-active-color:          @navbar-inverse-link-hover-color;\n@navbar-inverse-link-active-bg:             darken(@navbar-inverse-bg, 10%);\n@navbar-inverse-link-disabled-color:        #444;\n@navbar-inverse-link-disabled-bg:           transparent;\n\n// Inverted navbar brand label\n@navbar-inverse-brand-color:                @navbar-inverse-link-color;\n@navbar-inverse-brand-hover-color:          #fff;\n@navbar-inverse-brand-hover-bg:             transparent;\n\n// Inverted navbar toggle\n@navbar-inverse-toggle-hover-bg:            #333;\n@navbar-inverse-toggle-icon-bar-bg:         #fff;\n@navbar-inverse-toggle-border-color:        #333;\n\n\n//== Navs\n//\n//##\n\n//=== Shared nav styles\n@nav-link-padding:                          10px 15px;\n@nav-link-hover-bg:                         @gray-lighter;\n\n@nav-disabled-link-color:                   @gray-light;\n@nav-disabled-link-hover-color:             @gray-light;\n\n@nav-open-link-hover-color:                 #fff;\n\n//== Tabs\n@nav-tabs-border-color:                     #ddd;\n\n@nav-tabs-link-hover-border-color:          @gray-lighter;\n\n@nav-tabs-active-link-hover-bg:             @body-bg;\n@nav-tabs-active-link-hover-color:          @gray;\n@nav-tabs-active-link-hover-border-color:   #ddd;\n\n@nav-tabs-justified-link-border-color:            #ddd;\n@nav-tabs-justified-active-link-border-color:     @body-bg;\n\n//== Pills\n@nav-pills-border-radius:                   @border-radius-base;\n@nav-pills-active-link-hover-bg:            @component-active-bg;\n@nav-pills-active-link-hover-color:         @component-active-color;\n\n\n//== Pagination\n//\n//##\n\n@pagination-color:                     @link-color;\n@pagination-bg:                        #fff;\n@pagination-border:                    #ddd;\n\n@pagination-hover-color:               @link-hover-color;\n@pagination-hover-bg:                  @gray-lighter;\n@pagination-hover-border:              #ddd;\n\n@pagination-active-color:              #fff;\n@pagination-active-bg:                 @brand-primary;\n@pagination-active-border:             @brand-primary;\n\n@pagination-disabled-color:            @gray-light;\n@pagination-disabled-bg:               #fff;\n@pagination-disabled-border:           #ddd;\n\n\n//== Pager\n//\n//##\n\n@pager-bg:                             @pagination-bg;\n@pager-border:                         @pagination-border;\n@pager-border-radius:                  15px;\n\n@pager-hover-bg:                       @pagination-hover-bg;\n\n@pager-active-bg:                      @pagination-active-bg;\n@pager-active-color:                   @pagination-active-color;\n\n@pager-disabled-color:                 @pagination-disabled-color;\n\n\n//== Jumbotron\n//\n//##\n\n@jumbotron-padding:              30px;\n@jumbotron-color:                inherit;\n@jumbotron-bg:                   @gray-lighter;\n@jumbotron-heading-color:        inherit;\n@jumbotron-font-size:            ceil((@font-size-base * 1.5));\n\n\n//== Form states and alerts\n//\n//## Define colors for form feedback states and, by default, alerts.\n\n@state-success-text:             #3c763d;\n@state-success-bg:               #dff0d8;\n@state-success-border:           darken(spin(@state-success-bg, -10), 5%);\n\n@state-info-text:                #31708f;\n@state-info-bg:                  #d9edf7;\n@state-info-border:              darken(spin(@state-info-bg, -10), 7%);\n\n@state-warning-text:             #8a6d3b;\n@state-warning-bg:               #fcf8e3;\n@state-warning-border:           darken(spin(@state-warning-bg, -10), 5%);\n\n@state-danger-text:              #a94442;\n@state-danger-bg:                #f2dede;\n@state-danger-border:            darken(spin(@state-danger-bg, -10), 5%);\n\n\n//== Tooltips\n//\n//##\n\n//** Tooltip max width\n@tooltip-max-width:           200px;\n//** Tooltip text color\n@tooltip-color:               #fff;\n//** Tooltip background color\n@tooltip-bg:                  #000;\n@tooltip-opacity:             .9;\n\n//** Tooltip arrow width\n@tooltip-arrow-width:         5px;\n//** Tooltip arrow color\n@tooltip-arrow-color:         @tooltip-bg;\n\n\n//== Popovers\n//\n//##\n\n//** Popover body background color\n@popover-bg:                          #fff;\n//** Popover maximum width\n@popover-max-width:                   276px;\n//** Popover border color\n@popover-border-color:                rgba(0,0,0,.2);\n//** Popover fallback border color\n@popover-fallback-border-color:       #ccc;\n\n//** Popover title background color\n@popover-title-bg:                    darken(@popover-bg, 3%);\n\n//** Popover arrow width\n@popover-arrow-width:                 10px;\n//** Popover arrow color\n@popover-arrow-color:                 #fff;\n\n//** Popover outer arrow width\n@popover-arrow-outer-width:           (@popover-arrow-width + 1);\n//** Popover outer arrow color\n@popover-arrow-outer-color:           fadein(@popover-border-color, 5%);\n//** Popover outer arrow fallback color\n@popover-arrow-outer-fallback-color:  darken(@popover-fallback-border-color, 20%);\n\n\n//== Labels\n//\n//##\n\n//** Default label background color\n@label-default-bg:            @gray-light;\n//** Primary label background color\n@label-primary-bg:            @brand-primary;\n//** Success label background color\n@label-success-bg:            @brand-success;\n//** Info label background color\n@label-info-bg:               @brand-info;\n//** Warning label background color\n@label-warning-bg:            @brand-warning;\n//** Danger label background color\n@label-danger-bg:             @brand-danger;\n\n//** Default label text color\n@label-color:                 #fff;\n//** Default text color of a linked label\n@label-link-hover-color:      #fff;\n\n\n//== Modals\n//\n//##\n\n//** Padding applied to the modal body\n@modal-inner-padding:         20px;\n\n//** Padding applied to the modal title\n@modal-title-padding:         15px;\n//** Modal title line-height\n@modal-title-line-height:     @line-height-base;\n\n//** Background color of modal content area\n@modal-content-bg:                             #fff;\n//** Modal content border color\n@modal-content-border-color:                   rgba(0,0,0,.2);\n//** Modal content border color **for IE8**\n@modal-content-fallback-border-color:          #999;\n\n//** Modal backdrop background color\n@modal-backdrop-bg:           #000;\n//** Modal backdrop opacity\n@modal-backdrop-opacity:      .5;\n//** Modal header border color\n@modal-header-border-color:   #e5e5e5;\n//** Modal footer border color\n@modal-footer-border-color:   @modal-header-border-color;\n\n@modal-lg:                    900px;\n@modal-md:                    600px;\n@modal-sm:                    300px;\n\n\n//== Alerts\n//\n//## Define alert colors, border radius, and padding.\n\n@alert-padding:               15px;\n@alert-border-radius:         @border-radius-base;\n@alert-link-font-weight:      bold;\n\n@alert-success-bg:            @state-success-bg;\n@alert-success-text:          @state-success-text;\n@alert-success-border:        @state-success-border;\n\n@alert-info-bg:               @state-info-bg;\n@alert-info-text:             @state-info-text;\n@alert-info-border:           @state-info-border;\n\n@alert-warning-bg:            @state-warning-bg;\n@alert-warning-text:          @state-warning-text;\n@alert-warning-border:        @state-warning-border;\n\n@alert-danger-bg:             @state-danger-bg;\n@alert-danger-text:           @state-danger-text;\n@alert-danger-border:         @state-danger-border;\n\n\n//== Progress bars\n//\n//##\n\n//** Background color of the whole progress component\n@progress-bg:                 #f5f5f5;\n//** Progress bar text color\n@progress-bar-color:          #fff;\n\n//** Default progress bar color\n@progress-bar-bg:             @brand-primary;\n//** Success progress bar color\n@progress-bar-success-bg:     @brand-success;\n//** Warning progress bar color\n@progress-bar-warning-bg:     @brand-warning;\n//** Danger progress bar color\n@progress-bar-danger-bg:      @brand-danger;\n//** Info progress bar color\n@progress-bar-info-bg:        @brand-info;\n\n\n//== List group\n//\n//##\n\n//** Background color on `.list-group-item`\n@list-group-bg:                 #fff;\n//** `.list-group-item` border color\n@list-group-border:             #ddd;\n//** List group border radius\n@list-group-border-radius:      @border-radius-base;\n\n//** Background color of single list elements on hover\n@list-group-hover-bg:           #f5f5f5;\n//** Text color of active list elements\n@list-group-active-color:       @component-active-color;\n//** Background color of active list elements\n@list-group-active-bg:          @component-active-bg;\n//** Border color of active list elements\n@list-group-active-border:      @list-group-active-bg;\n@list-group-active-text-color:  lighten(@list-group-active-bg, 40%);\n\n@list-group-link-color:         #555;\n@list-group-link-heading-color: #333;\n\n\n//== Panels\n//\n//##\n\n@panel-bg:                    #fff;\n@panel-body-padding:          15px;\n@panel-border-radius:         @border-radius-base;\n\n//** Border color for elements within panels\n@panel-inner-border:          #ddd;\n@panel-footer-bg:             #f5f5f5;\n\n@panel-default-text:          @gray-dark;\n@panel-default-border:        #ddd;\n@panel-default-heading-bg:    #f5f5f5;\n\n@panel-primary-text:          #fff;\n@panel-primary-border:        @brand-primary;\n@panel-primary-heading-bg:    @brand-primary;\n\n@panel-success-text:          @state-success-text;\n@panel-success-border:        @state-success-border;\n@panel-success-heading-bg:    @state-success-bg;\n\n@panel-info-text:             @state-info-text;\n@panel-info-border:           @state-info-border;\n@panel-info-heading-bg:       @state-info-bg;\n\n@panel-warning-text:          @state-warning-text;\n@panel-warning-border:        @state-warning-border;\n@panel-warning-heading-bg:    @state-warning-bg;\n\n@panel-danger-text:           @state-danger-text;\n@panel-danger-border:         @state-danger-border;\n@panel-danger-heading-bg:     @state-danger-bg;\n\n\n//== Thumbnails\n//\n//##\n\n//** Padding around the thumbnail image\n@thumbnail-padding:           4px;\n//** Thumbnail background color\n@thumbnail-bg:                @body-bg;\n//** Thumbnail border color\n@thumbnail-border:            #ddd;\n//** Thumbnail border radius\n@thumbnail-border-radius:     @border-radius-base;\n\n//** Custom text color for thumbnail captions\n@thumbnail-caption-color:     @text-color;\n//** Padding around the thumbnail caption\n@thumbnail-caption-padding:   9px;\n\n\n//== Wells\n//\n//##\n\n@well-bg:                     #f5f5f5;\n@well-border:                 darken(@well-bg, 7%);\n\n\n//== Badges\n//\n//##\n\n@badge-color:                 #fff;\n//** Linked badge text color on hover\n@badge-link-hover-color:      #fff;\n@badge-bg:                    @gray-light;\n\n//** Badge text color in active nav link\n@badge-active-color:          @link-color;\n//** Badge background color in active nav link\n@badge-active-bg:             #fff;\n\n@badge-font-weight:           bold;\n@badge-line-height:           1;\n@badge-border-radius:         10px;\n\n\n//== Breadcrumbs\n//\n//##\n\n@breadcrumb-padding-vertical:   8px;\n@breadcrumb-padding-horizontal: 15px;\n//** Breadcrumb background color\n@breadcrumb-bg:                 #f5f5f5;\n//** Breadcrumb text color\n@breadcrumb-color:              #ccc;\n//** Text color of current page in the breadcrumb\n@breadcrumb-active-color:       @gray-light;\n//** Textual separator for between breadcrumb elements\n@breadcrumb-separator:          \"/\";\n\n\n//== Carousel\n//\n//##\n\n@carousel-text-shadow:                        0 1px 2px rgba(0,0,0,.6);\n\n@carousel-control-color:                      #fff;\n@carousel-control-width:                      15%;\n@carousel-control-opacity:                    .5;\n@carousel-control-font-size:                  20px;\n\n@carousel-indicator-active-bg:                #fff;\n@carousel-indicator-border-color:             #fff;\n\n@carousel-caption-color:                      #fff;\n\n\n//== Close\n//\n//##\n\n@close-font-weight:           bold;\n@close-color:                 #000;\n@close-text-shadow:           0 1px 0 #fff;\n\n\n//== Code\n//\n//##\n\n@code-color:                  #c7254e;\n@code-bg:                     #f9f2f4;\n\n@kbd-color:                   #fff;\n@kbd-bg:                      #333;\n\n@pre-bg:                      #f5f5f5;\n@pre-color:                   @gray-dark;\n@pre-border-color:            #ccc;\n@pre-scrollable-max-height:   340px;\n\n\n//== Type\n//\n//##\n\n//** Text muted color\n@text-muted:                  @gray-light;\n//** Abbreviations and acronyms border color\n@abbr-border-color:           @gray-light;\n//** Headings small color\n@headings-small-color:        @gray-light;\n//** Blockquote small color\n@blockquote-small-color:      @gray-light;\n//** Blockquote font size\n@blockquote-font-size:        (@font-size-base * 1.25);\n//** Blockquote border color\n@blockquote-border-color:     @gray-lighter;\n//** Page header border color\n@page-header-border-color:    @gray-lighter;\n\n\n//== Miscellaneous\n//\n//##\n\n//** Horizontal line color.\n@hr-border:                   @gray-lighter;\n\n//** Horizontal offset for forms and lists.\n@component-offset-horizontal: 180px;\n","//\n// Thumbnails\n// --------------------------------------------------\n\n\n// Mixin and adjust the regular image class\n.thumbnail {\n  display: block;\n  padding: @thumbnail-padding;\n  margin-bottom: @line-height-computed;\n  line-height: @line-height-base;\n  background-color: @thumbnail-bg;\n  border: 1px solid @thumbnail-border;\n  border-radius: @thumbnail-border-radius;\n  .transition(all .2s ease-in-out);\n\n  > img,\n  a > img {\n    &:extend(.img-responsive);\n    margin-left: auto;\n    margin-right: auto;\n  }\n\n  // Add a hover state for linked versions only\n  a&:hover,\n  a&:focus,\n  a&.active {\n    border-color: @link-color;\n  }\n\n  // Image captions\n  .caption {\n    padding: @thumbnail-caption-padding;\n    color: @thumbnail-caption-color;\n  }\n}\n","//\n// Carousel\n// --------------------------------------------------\n\n\n// Wrapper for the slide container and indicators\n.carousel {\n  position: relative;\n}\n\n.carousel-inner {\n  position: relative;\n  overflow: hidden;\n  width: 100%;\n\n  > .item {\n    display: none;\n    position: relative;\n    .transition(.6s ease-in-out left);\n\n    // Account for jankitude on images\n    > img,\n    > a > img {\n      &:extend(.img-responsive);\n      line-height: 1;\n    }\n  }\n\n  > .active,\n  > .next,\n  > .prev { display: block; }\n\n  > .active {\n    left: 0;\n  }\n\n  > .next,\n  > .prev {\n    position: absolute;\n    top: 0;\n    width: 100%;\n  }\n\n  > .next {\n    left: 100%;\n  }\n  > .prev {\n    left: -100%;\n  }\n  > .next.left,\n  > .prev.right {\n    left: 0;\n  }\n\n  > .active.left {\n    left: -100%;\n  }\n  > .active.right {\n    left: 100%;\n  }\n\n}\n\n// Left/right controls for nav\n// ---------------------------\n\n.carousel-control {\n  position: absolute;\n  top: 0;\n  left: 0;\n  bottom: 0;\n  width: @carousel-control-width;\n  .opacity(@carousel-control-opacity);\n  font-size: @carousel-control-font-size;\n  color: @carousel-control-color;\n  text-align: center;\n  text-shadow: @carousel-text-shadow;\n  // We can't have this transition here because WebKit cancels the carousel\n  // animation if you trip this while in the middle of another animation.\n\n  // Set gradients for backgrounds\n  &.left {\n    #gradient > .horizontal(@start-color: rgba(0,0,0,.5); @end-color: rgba(0,0,0,.0001));\n  }\n  &.right {\n    left: auto;\n    right: 0;\n    #gradient > .horizontal(@start-color: rgba(0,0,0,.0001); @end-color: rgba(0,0,0,.5));\n  }\n\n  // Hover/focus state\n  &:hover,\n  &:focus {\n    outline: none;\n    color: @carousel-control-color;\n    text-decoration: none;\n    .opacity(.9);\n  }\n\n  // Toggles\n  .icon-prev,\n  .icon-next,\n  .glyphicon-chevron-left,\n  .glyphicon-chevron-right {\n    position: absolute;\n    top: 50%;\n    z-index: 5;\n    display: inline-block;\n  }\n  .icon-prev,\n  .glyphicon-chevron-left {\n    left: 50%;\n  }\n  .icon-next,\n  .glyphicon-chevron-right {\n    right: 50%;\n  }\n  .icon-prev,\n  .icon-next {\n    width:  20px;\n    height: 20px;\n    margin-top: -10px;\n    margin-left: -10px;\n    font-family: serif;\n  }\n\n  .icon-prev {\n    &:before {\n      content: '\\2039';// SINGLE LEFT-POINTING ANGLE QUOTATION MARK (U+2039)\n    }\n  }\n  .icon-next {\n    &:before {\n      content: '\\203a';// SINGLE RIGHT-POINTING ANGLE QUOTATION MARK (U+203A)\n    }\n  }\n}\n\n// Optional indicator pips\n//\n// Add an unordered list with the following class and add a list item for each\n// slide your carousel holds.\n\n.carousel-indicators {\n  position: absolute;\n  bottom: 10px;\n  left: 50%;\n  z-index: 15;\n  width: 60%;\n  margin-left: -30%;\n  padding-left: 0;\n  list-style: none;\n  text-align: center;\n\n  li {\n    display: inline-block;\n    width:  10px;\n    height: 10px;\n    margin: 1px;\n    text-indent: -999px;\n    border: 1px solid @carousel-indicator-border-color;\n    border-radius: 10px;\n    cursor: pointer;\n\n    // IE8-9 hack for event handling\n    //\n    // Internet Explorer 8-9 does not support clicks on elements without a set\n    // `background-color`. We cannot use `filter` since that's not viewed as a\n    // background color by the browser. Thus, a hack is needed.\n    //\n    // For IE8, we set solid black as it doesn't support `rgba()`. For IE9, we\n    // set alpha transparency for the best results possible.\n    background-color: #000 \\9; // IE8\n    background-color: rgba(0,0,0,0); // IE9\n  }\n  .active {\n    margin: 0;\n    width:  12px;\n    height: 12px;\n    background-color: @carousel-indicator-active-bg;\n  }\n}\n\n// Optional captions\n// -----------------------------\n// Hidden by default for smaller viewports\n.carousel-caption {\n  position: absolute;\n  left: 15%;\n  right: 15%;\n  bottom: 20px;\n  z-index: 10;\n  padding-top: 20px;\n  padding-bottom: 20px;\n  color: @carousel-caption-color;\n  text-align: center;\n  text-shadow: @carousel-text-shadow;\n  & .btn {\n    text-shadow: none; // No shadow for button elements in carousel-caption\n  }\n}\n\n\n// Scale up controls for tablets and up\n@media screen and (min-width: @screen-sm-min) {\n\n  // Scale up the controls a smidge\n  .carousel-control {\n    .glyphicon-chevron-left,\n    .glyphicon-chevron-right,\n    .icon-prev,\n    .icon-next {\n      width: 30px;\n      height: 30px;\n      margin-top: -15px;\n      margin-left: -15px;\n      font-size: 30px;\n    }\n  }\n\n  // Show and left align the captions\n  .carousel-caption {\n    left: 20%;\n    right: 20%;\n    padding-bottom: 30px;\n  }\n\n  // Move up the indicators\n  .carousel-indicators {\n    bottom: 20px;\n  }\n}\n","//\n// Typography\n// --------------------------------------------------\n\n\n// Headings\n// -------------------------\n\nh1, h2, h3, h4, h5, h6,\n.h1, .h2, .h3, .h4, .h5, .h6 {\n  font-family: @headings-font-family;\n  font-weight: @headings-font-weight;\n  line-height: @headings-line-height;\n  color: @headings-color;\n\n  small,\n  .small {\n    font-weight: normal;\n    line-height: 1;\n    color: @headings-small-color;\n  }\n}\n\nh1, .h1,\nh2, .h2,\nh3, .h3 {\n  margin-top: @line-height-computed;\n  margin-bottom: (@line-height-computed / 2);\n\n  small,\n  .small {\n    font-size: 65%;\n  }\n}\nh4, .h4,\nh5, .h5,\nh6, .h6 {\n  margin-top: (@line-height-computed / 2);\n  margin-bottom: (@line-height-computed / 2);\n\n  small,\n  .small {\n    font-size: 75%;\n  }\n}\n\nh1, .h1 { font-size: @font-size-h1; }\nh2, .h2 { font-size: @font-size-h2; }\nh3, .h3 { font-size: @font-size-h3; }\nh4, .h4 { font-size: @font-size-h4; }\nh5, .h5 { font-size: @font-size-h5; }\nh6, .h6 { font-size: @font-size-h6; }\n\n\n// Body text\n// -------------------------\n\np {\n  margin: 0 0 (@line-height-computed / 2);\n}\n\n.lead {\n  margin-bottom: @line-height-computed;\n  font-size: floor((@font-size-base * 1.15));\n  font-weight: 200;\n  line-height: 1.4;\n\n  @media (min-width: @screen-sm-min) {\n    font-size: (@font-size-base * 1.5);\n  }\n}\n\n\n// Emphasis & misc\n// -------------------------\n\n// Ex: 14px base font * 85% = about 12px\nsmall,\n.small  { font-size: 85%; }\n\n// Undo browser default styling\ncite    { font-style: normal; }\n\n// Alignment\n.text-left           { text-align: left; }\n.text-right          { text-align: right; }\n.text-center         { text-align: center; }\n.text-justify        { text-align: justify; }\n\n// Contextual colors\n.text-muted {\n  color: @text-muted;\n}\n.text-primary {\n  .text-emphasis-variant(@brand-primary);\n}\n.text-success {\n  .text-emphasis-variant(@state-success-text);\n}\n.text-info {\n  .text-emphasis-variant(@state-info-text);\n}\n.text-warning {\n  .text-emphasis-variant(@state-warning-text);\n}\n.text-danger {\n  .text-emphasis-variant(@state-danger-text);\n}\n\n// Contextual backgrounds\n// For now we'll leave these alongside the text classes until v4 when we can\n// safely shift things around (per SemVer rules).\n.bg-primary {\n  // Given the contrast here, this is the only class to have its color inverted\n  // automatically.\n  color: #fff;\n  .bg-variant(@brand-primary);\n}\n.bg-success {\n  .bg-variant(@state-success-bg);\n}\n.bg-info {\n  .bg-variant(@state-info-bg);\n}\n.bg-warning {\n  .bg-variant(@state-warning-bg);\n}\n.bg-danger {\n  .bg-variant(@state-danger-bg);\n}\n\n\n// Page header\n// -------------------------\n\n.page-header {\n  padding-bottom: ((@line-height-computed / 2) - 1);\n  margin: (@line-height-computed * 2) 0 @line-height-computed;\n  border-bottom: 1px solid @page-header-border-color;\n}\n\n\n// Lists\n// --------------------------------------------------\n\n// Unordered and Ordered lists\nul,\nol {\n  margin-top: 0;\n  margin-bottom: (@line-height-computed / 2);\n  ul,\n  ol {\n    margin-bottom: 0;\n  }\n}\n\n// List options\n\n// Unstyled keeps list items block level, just removes default browser padding and list-style\n.list-unstyled {\n  padding-left: 0;\n  list-style: none;\n}\n\n// Inline turns list items into inline-block\n.list-inline {\n  .list-unstyled();\n  margin-left: -5px;\n\n  > li {\n    display: inline-block;\n    padding-left: 5px;\n    padding-right: 5px;\n  }\n}\n\n// Description Lists\ndl {\n  margin-top: 0; // Remove browser default\n  margin-bottom: @line-height-computed;\n}\ndt,\ndd {\n  line-height: @line-height-base;\n}\ndt {\n  font-weight: bold;\n}\ndd {\n  margin-left: 0; // Undo browser default\n}\n\n// Horizontal description lists\n//\n// Defaults to being stacked without any of the below styles applied, until the\n// grid breakpoint is reached (default of ~768px).\n\n@media (min-width: @grid-float-breakpoint) {\n  .dl-horizontal {\n    dt {\n      float: left;\n      width: (@component-offset-horizontal - 20);\n      clear: left;\n      text-align: right;\n      .text-overflow();\n    }\n    dd {\n      margin-left: @component-offset-horizontal;\n      &:extend(.clearfix all); // Clear the floated `dt` if an empty `dd` is present\n    }\n  }\n}\n\n// MISC\n// ----\n\n// Abbreviations and acronyms\nabbr[title],\n// Add data-* attribute to help out our tooltip plugin, per https://github.com/twbs/bootstrap/issues/5257\nabbr[data-original-title] {\n  cursor: help;\n  border-bottom: 1px dotted @abbr-border-color;\n}\n.initialism {\n  font-size: 90%;\n  text-transform: uppercase;\n}\n\n// Blockquotes\nblockquote {\n  padding: (@line-height-computed / 2) @line-height-computed;\n  margin: 0 0 @line-height-computed;\n  font-size: @blockquote-font-size;\n  border-left: 5px solid @blockquote-border-color;\n\n  p,\n  ul,\n  ol {\n    &:last-child {\n      margin-bottom: 0;\n    }\n  }\n\n  // Note: Deprecated small and .small as of v3.1.0\n  // Context: https://github.com/twbs/bootstrap/issues/11660\n  footer,\n  small,\n  .small {\n    display: block;\n    font-size: 80%; // back to default font-size\n    line-height: @line-height-base;\n    color: @blockquote-small-color;\n\n    &:before {\n      content: '\\2014 \\00A0'; // em dash, nbsp\n    }\n  }\n}\n\n// Opposite alignment of blockquote\n//\n// Heads up: `blockquote.pull-right` has been deprecated as of v3.1.0.\n.blockquote-reverse,\nblockquote.pull-right {\n  padding-right: 15px;\n  padding-left: 0;\n  border-right: 5px solid @blockquote-border-color;\n  border-left: 0;\n  text-align: right;\n\n  // Account for citation\n  footer,\n  small,\n  .small {\n    &:before { content: ''; }\n    &:after {\n      content: '\\00A0 \\2014'; // nbsp, em dash\n    }\n  }\n}\n\n// Quotes\nblockquote:before,\nblockquote:after {\n  content: \"\";\n}\n\n// Addresses\naddress {\n  margin-bottom: @line-height-computed;\n  font-style: normal;\n  line-height: @line-height-base;\n}\n","//\n// Code (inline and block)\n// --------------------------------------------------\n\n\n// Inline and block code styles\ncode,\nkbd,\npre,\nsamp {\n  font-family: @font-family-monospace;\n}\n\n// Inline code\ncode {\n  padding: 2px 4px;\n  font-size: 90%;\n  color: @code-color;\n  background-color: @code-bg;\n  white-space: nowrap;\n  border-radius: @border-radius-base;\n}\n\n// User input typically entered via keyboard\nkbd {\n  padding: 2px 4px;\n  font-size: 90%;\n  color: @kbd-color;\n  background-color: @kbd-bg;\n  border-radius: @border-radius-small;\n  box-shadow: inset 0 -1px 0 rgba(0,0,0,.25);\n}\n\n// Blocks of code\npre {\n  display: block;\n  padding: ((@line-height-computed - 1) / 2);\n  margin: 0 0 (@line-height-computed / 2);\n  font-size: (@font-size-base - 1); // 14px to 13px\n  line-height: @line-height-base;\n  word-break: break-all;\n  word-wrap: break-word;\n  color: @pre-color;\n  background-color: @pre-bg;\n  border: 1px solid @pre-border-color;\n  border-radius: @border-radius-base;\n\n  // Account for some code outputs that place code tags in pre tags\n  code {\n    padding: 0;\n    font-size: inherit;\n    color: inherit;\n    white-space: pre-wrap;\n    background-color: transparent;\n    border-radius: 0;\n  }\n}\n\n// Enable scrollable blocks of code\n.pre-scrollable {\n  max-height: @pre-scrollable-max-height;\n  overflow-y: scroll;\n}\n","//\n// Grid system\n// --------------------------------------------------\n\n\n// Container widths\n//\n// Set the container width, and override it for fixed navbars in media queries.\n\n.container {\n  .container-fixed();\n\n  @media (min-width: @screen-sm-min) {\n    width: @container-sm;\n  }\n  @media (min-width: @screen-md-min) {\n    width: @container-md;\n  }\n  @media (min-width: @screen-lg-min) {\n    width: @container-lg;\n  }\n}\n\n\n// Fluid container\n//\n// Utilizes the mixin meant for fixed width containers, but without any defined\n// width for fluid, full width layouts.\n\n.container-fluid {\n  .container-fixed();\n}\n\n\n// Row\n//\n// Rows contain and clear the floats of your columns.\n\n.row {\n  .make-row();\n}\n\n\n// Columns\n//\n// Common styles for small and large grid columns\n\n.make-grid-columns();\n\n\n// Extra small grid\n//\n// Columns, offsets, pushes, and pulls for extra small devices like\n// smartphones.\n\n.make-grid(xs);\n\n\n// Small grid\n//\n// Columns, offsets, pushes, and pulls for the small device range, from phones\n// to tablets.\n\n@media (min-width: @screen-sm-min) {\n  .make-grid(sm);\n}\n\n\n// Medium grid\n//\n// Columns, offsets, pushes, and pulls for the desktop device range.\n\n@media (min-width: @screen-md-min) {\n  .make-grid(md);\n}\n\n\n// Large grid\n//\n// Columns, offsets, pushes, and pulls for the large desktop device range.\n\n@media (min-width: @screen-lg-min) {\n  .make-grid(lg);\n}\n","//\n// Tables\n// --------------------------------------------------\n\n\ntable {\n  max-width: 100%;\n  background-color: @table-bg;\n}\nth {\n  text-align: left;\n}\n\n\n// Baseline styles\n\n.table {\n  width: 100%;\n  margin-bottom: @line-height-computed;\n  // Cells\n  > thead,\n  > tbody,\n  > tfoot {\n    > tr {\n      > th,\n      > td {\n        padding: @table-cell-padding;\n        line-height: @line-height-base;\n        vertical-align: top;\n        border-top: 1px solid @table-border-color;\n      }\n    }\n  }\n  // Bottom align for column headings\n  > thead > tr > th {\n    vertical-align: bottom;\n    border-bottom: 2px solid @table-border-color;\n  }\n  // Remove top border from thead by default\n  > caption + thead,\n  > colgroup + thead,\n  > thead:first-child {\n    > tr:first-child {\n      > th,\n      > td {\n        border-top: 0;\n      }\n    }\n  }\n  // Account for multiple tbody instances\n  > tbody + tbody {\n    border-top: 2px solid @table-border-color;\n  }\n\n  // Nesting\n  .table {\n    background-color: @body-bg;\n  }\n}\n\n\n// Condensed table w/ half padding\n\n.table-condensed {\n  > thead,\n  > tbody,\n  > tfoot {\n    > tr {\n      > th,\n      > td {\n        padding: @table-condensed-cell-padding;\n      }\n    }\n  }\n}\n\n\n// Bordered version\n//\n// Add borders all around the table and between all the columns.\n\n.table-bordered {\n  border: 1px solid @table-border-color;\n  > thead,\n  > tbody,\n  > tfoot {\n    > tr {\n      > th,\n      > td {\n        border: 1px solid @table-border-color;\n      }\n    }\n  }\n  > thead > tr {\n    > th,\n    > td {\n      border-bottom-width: 2px;\n    }\n  }\n}\n\n\n// Zebra-striping\n//\n// Default zebra-stripe styles (alternating gray and transparent backgrounds)\n\n.table-striped {\n  > tbody > tr:nth-child(odd) {\n    > td,\n    > th {\n      background-color: @table-bg-accent;\n    }\n  }\n}\n\n\n// Hover effect\n//\n// Placed here since it has to come after the potential zebra striping\n\n.table-hover {\n  > tbody > tr:hover {\n    > td,\n    > th {\n      background-color: @table-bg-hover;\n    }\n  }\n}\n\n\n// Table cell sizing\n//\n// Reset default table behavior\n\ntable col[class*=\"col-\"] {\n  position: static; // Prevent border hiding in Firefox and IE9/10 (see https://github.com/twbs/bootstrap/issues/11623)\n  float: none;\n  display: table-column;\n}\ntable {\n  td,\n  th {\n    &[class*=\"col-\"] {\n      position: static; // Prevent border hiding in Firefox and IE9/10 (see https://github.com/twbs/bootstrap/issues/11623)\n      float: none;\n      display: table-cell;\n    }\n  }\n}\n\n\n// Table backgrounds\n//\n// Exact selectors below required to override `.table-striped` and prevent\n// inheritance to nested tables.\n\n// Generate the contextual variants\n.table-row-variant(active; @table-bg-active);\n.table-row-variant(success; @state-success-bg);\n.table-row-variant(info; @state-info-bg);\n.table-row-variant(warning; @state-warning-bg);\n.table-row-variant(danger; @state-danger-bg);\n\n\n// Responsive tables\n//\n// Wrap your tables in `.table-responsive` and we'll make them mobile friendly\n// by enabling horizontal scrolling. Only applies <768px. Everything above that\n// will display normally.\n\n@media (max-width: @screen-xs-max) {\n  .table-responsive {\n    width: 100%;\n    margin-bottom: (@line-height-computed * 0.75);\n    overflow-y: hidden;\n    overflow-x: scroll;\n    -ms-overflow-style: -ms-autohiding-scrollbar;\n    border: 1px solid @table-border-color;\n    -webkit-overflow-scrolling: touch;\n\n    // Tighten up spacing\n    > .table {\n      margin-bottom: 0;\n\n      // Ensure the content doesn't wrap\n      > thead,\n      > tbody,\n      > tfoot {\n        > tr {\n          > th,\n          > td {\n            white-space: nowrap;\n          }\n        }\n      }\n    }\n\n    // Special overrides for the bordered tables\n    > .table-bordered {\n      border: 0;\n\n      // Nuke the appropriate borders so that the parent can handle them\n      > thead,\n      > tbody,\n      > tfoot {\n        > tr {\n          > th:first-child,\n          > td:first-child {\n            border-left: 0;\n          }\n          > th:last-child,\n          > td:last-child {\n            border-right: 0;\n          }\n        }\n      }\n\n      // Only nuke the last row's bottom-border in `tbody` and `tfoot` since\n      // chances are there will be only one `tr` in a `thead` and that would\n      // remove the border altogether.\n      > tbody,\n      > tfoot {\n        > tr:last-child {\n          > th,\n          > td {\n            border-bottom: 0;\n          }\n        }\n      }\n\n    }\n  }\n}\n","//\n// Forms\n// --------------------------------------------------\n\n\n// Normalize non-controls\n//\n// Restyle and baseline non-control form elements.\n\nfieldset {\n  padding: 0;\n  margin: 0;\n  border: 0;\n  // Chrome and Firefox set a `min-width: -webkit-min-content;` on fieldsets,\n  // so we reset that to ensure it behaves more like a standard block element.\n  // See https://github.com/twbs/bootstrap/issues/12359.\n  min-width: 0;\n}\n\nlegend {\n  display: block;\n  width: 100%;\n  padding: 0;\n  margin-bottom: @line-height-computed;\n  font-size: (@font-size-base * 1.5);\n  line-height: inherit;\n  color: @legend-color;\n  border: 0;\n  border-bottom: 1px solid @legend-border-color;\n}\n\nlabel {\n  display: inline-block;\n  margin-bottom: 5px;\n  font-weight: bold;\n}\n\n\n// Normalize form controls\n//\n// While most of our form styles require extra classes, some basic normalization\n// is required to ensure optimum display with or without those classes to better\n// address browser inconsistencies.\n\n// Override content-box in Normalize (* isn't specific enough)\ninput[type=\"search\"] {\n  .box-sizing(border-box);\n}\n\n// Position radios and checkboxes better\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n  margin: 4px 0 0;\n  margin-top: 1px \\9; /* IE8-9 */\n  line-height: normal;\n}\n\n// Set the height of file controls to match text inputs\ninput[type=\"file\"] {\n  display: block;\n}\n\n// Make range inputs behave like textual form controls\ninput[type=\"range\"] {\n  display: block;\n  width: 100%;\n}\n\n// Make multiple select elements height not fixed\nselect[multiple],\nselect[size] {\n  height: auto;\n}\n\n// Focus for file, radio, and checkbox\ninput[type=\"file\"]:focus,\ninput[type=\"radio\"]:focus,\ninput[type=\"checkbox\"]:focus {\n  .tab-focus();\n}\n\n// Adjust output element\noutput {\n  display: block;\n  padding-top: (@padding-base-vertical + 1);\n  font-size: @font-size-base;\n  line-height: @line-height-base;\n  color: @input-color;\n}\n\n\n// Common form controls\n//\n// Shared size and type resets for form controls. Apply `.form-control` to any\n// of the following form controls:\n//\n// select\n// textarea\n// input[type=\"text\"]\n// input[type=\"password\"]\n// input[type=\"datetime\"]\n// input[type=\"datetime-local\"]\n// input[type=\"date\"]\n// input[type=\"month\"]\n// input[type=\"time\"]\n// input[type=\"week\"]\n// input[type=\"number\"]\n// input[type=\"email\"]\n// input[type=\"url\"]\n// input[type=\"search\"]\n// input[type=\"tel\"]\n// input[type=\"color\"]\n\n.form-control {\n  display: block;\n  width: 100%;\n  height: @input-height-base; // Make inputs at least the height of their button counterpart (base line-height + padding + border)\n  padding: @padding-base-vertical @padding-base-horizontal;\n  font-size: @font-size-base;\n  line-height: @line-height-base;\n  color: @input-color;\n  background-color: @input-bg;\n  background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214\n  border: 1px solid @input-border;\n  border-radius: @input-border-radius;\n  .box-shadow(inset 0 1px 1px rgba(0,0,0,.075));\n  .transition(~\"border-color ease-in-out .15s, box-shadow ease-in-out .15s\");\n\n  // Customize the `:focus` state to imitate native WebKit styles.\n  .form-control-focus();\n\n  // Placeholder\n  .placeholder();\n\n  // Disabled and read-only inputs\n  //\n  // HTML5 says that controls under a fieldset > legend:first-child won't be\n  // disabled if the fieldset is disabled. Due to implementation difficulty, we\n  // don't honor that edge case; we style them as disabled anyway.\n  &[disabled],\n  &[readonly],\n  fieldset[disabled] & {\n    cursor: not-allowed;\n    background-color: @input-bg-disabled;\n    opacity: 1; // iOS fix for unreadable disabled content\n  }\n\n  // Reset height for `textarea`s\n  textarea& {\n    height: auto;\n  }\n}\n\n\n// Search inputs in iOS\n//\n// This overrides the extra rounded corners on search inputs in iOS so that our\n// `.form-control` class can properly style them. Note that this cannot simply\n// be added to `.form-control` as it's not specific enough. For details, see\n// https://github.com/twbs/bootstrap/issues/11586.\n\ninput[type=\"search\"] {\n  -webkit-appearance: none;\n}\n\n\n// Special styles for iOS date input\n//\n// In Mobile Safari, date inputs require a pixel line-height that matches the\n// given height of the input.\n\ninput[type=\"date\"] {\n  line-height: @input-height-base;\n}\n\n\n// Form groups\n//\n// Designed to help with the organization and spacing of vertical forms. For\n// horizontal forms, use the predefined grid classes.\n\n.form-group {\n  margin-bottom: 15px;\n}\n\n\n// Checkboxes and radios\n//\n// Indent the labels to position radios/checkboxes as hanging controls.\n\n.radio,\n.checkbox {\n  display: block;\n  min-height: @line-height-computed; // clear the floating input if there is no label text\n  margin-top: 10px;\n  margin-bottom: 10px;\n  padding-left: 20px;\n  label {\n    display: inline;\n    font-weight: normal;\n    cursor: pointer;\n  }\n}\n.radio input[type=\"radio\"],\n.radio-inline input[type=\"radio\"],\n.checkbox input[type=\"checkbox\"],\n.checkbox-inline input[type=\"checkbox\"] {\n  float: left;\n  margin-left: -20px;\n}\n.radio + .radio,\n.checkbox + .checkbox {\n  margin-top: -5px; // Move up sibling radios or checkboxes for tighter spacing\n}\n\n// Radios and checkboxes on same line\n.radio-inline,\n.checkbox-inline {\n  display: inline-block;\n  padding-left: 20px;\n  margin-bottom: 0;\n  vertical-align: middle;\n  font-weight: normal;\n  cursor: pointer;\n}\n.radio-inline + .radio-inline,\n.checkbox-inline + .checkbox-inline {\n  margin-top: 0;\n  margin-left: 10px; // space out consecutive inline controls\n}\n\n// Apply same disabled cursor tweak as for inputs\n//\n// Note: Neither radios nor checkboxes can be readonly.\ninput[type=\"radio\"],\ninput[type=\"checkbox\"],\n.radio,\n.radio-inline,\n.checkbox,\n.checkbox-inline {\n  &[disabled],\n  fieldset[disabled] & {\n    cursor: not-allowed;\n  }\n}\n\n\n// Form control sizing\n//\n// Build on `.form-control` with modifier classes to decrease or increase the\n// height and font-size of form controls.\n\n.input-sm {\n  .input-size(@input-height-small; @padding-small-vertical; @padding-small-horizontal; @font-size-small; @line-height-small; @border-radius-small);\n}\n\n.input-lg {\n  .input-size(@input-height-large; @padding-large-vertical; @padding-large-horizontal; @font-size-large; @line-height-large; @border-radius-large);\n}\n\n\n// Form control feedback states\n//\n// Apply contextual and semantic states to individual form controls.\n\n.has-feedback {\n  // Enable absolute positioning\n  position: relative;\n\n  // Ensure icons don't overlap text\n  .form-control {\n    padding-right: (@input-height-base * 1.25);\n  }\n\n  // Feedback icon (requires .glyphicon classes)\n  .form-control-feedback {\n    position: absolute;\n    top: (@line-height-computed + 5); // Height of the `label` and its margin\n    right: 0;\n    display: block;\n    width: @input-height-base;\n    height: @input-height-base;\n    line-height: @input-height-base;\n    text-align: center;\n  }\n}\n\n// Feedback states\n.has-success {\n  .form-control-validation(@state-success-text; @state-success-text; @state-success-bg);\n}\n.has-warning {\n  .form-control-validation(@state-warning-text; @state-warning-text; @state-warning-bg);\n}\n.has-error {\n  .form-control-validation(@state-danger-text; @state-danger-text; @state-danger-bg);\n}\n\n\n// Static form control text\n//\n// Apply class to a `p` element to make any string of text align with labels in\n// a horizontal form layout.\n\n.form-control-static {\n  margin-bottom: 0; // Remove default margin from `p`\n}\n\n\n// Help text\n//\n// Apply to any element you wish to create light text for placement immediately\n// below a form control. Use for general help, formatting, or instructional text.\n\n.help-block {\n  display: block; // account for any element using help-block\n  margin-top: 5px;\n  margin-bottom: 10px;\n  color: lighten(@text-color, 25%); // lighten the text some for contrast\n}\n\n\n\n// Inline forms\n//\n// Make forms appear inline(-block) by adding the `.form-inline` class. Inline\n// forms begin stacked on extra small (mobile) devices and then go inline when\n// viewports reach <768px.\n//\n// Requires wrapping inputs and labels with `.form-group` for proper display of\n// default HTML form controls and our custom form controls (e.g., input groups).\n//\n// Heads up! This is mixin-ed into `.navbar-form` in navbars.less.\n\n.form-inline {\n\n  // Kick in the inline\n  @media (min-width: @screen-sm-min) {\n    // Inline-block all the things for \"inline\"\n    .form-group {\n      display: inline-block;\n      margin-bottom: 0;\n      vertical-align: middle;\n    }\n\n    // In navbar-form, allow folks to *not* use `.form-group`\n    .form-control {\n      display: inline-block;\n      width: auto; // Prevent labels from stacking above inputs in `.form-group`\n      vertical-align: middle;\n    }\n    // Input groups need that 100% width though\n    .input-group > .form-control {\n      width: 100%;\n    }\n\n    .control-label {\n      margin-bottom: 0;\n      vertical-align: middle;\n    }\n\n    // Remove default margin on radios/checkboxes that were used for stacking, and\n    // then undo the floating of radios and checkboxes to match (which also avoids\n    // a bug in WebKit: https://github.com/twbs/bootstrap/issues/1969).\n    .radio,\n    .checkbox {\n      display: inline-block;\n      margin-top: 0;\n      margin-bottom: 0;\n      padding-left: 0;\n      vertical-align: middle;\n    }\n    .radio input[type=\"radio\"],\n    .checkbox input[type=\"checkbox\"] {\n      float: none;\n      margin-left: 0;\n    }\n\n    // Validation states\n    //\n    // Reposition the icon because it's now within a grid column and columns have\n    // `position: relative;` on them. Also accounts for the grid gutter padding.\n    .has-feedback .form-control-feedback {\n      top: 0;\n    }\n  }\n}\n\n\n// Horizontal forms\n//\n// Horizontal forms are built on grid classes and allow you to create forms with\n// labels on the left and inputs on the right.\n\n.form-horizontal {\n\n  // Consistent vertical alignment of labels, radios, and checkboxes\n  .control-label,\n  .radio,\n  .checkbox,\n  .radio-inline,\n  .checkbox-inline {\n    margin-top: 0;\n    margin-bottom: 0;\n    padding-top: (@padding-base-vertical + 1); // Default padding plus a border\n  }\n  // Account for padding we're adding to ensure the alignment and of help text\n  // and other content below items\n  .radio,\n  .checkbox {\n    min-height: (@line-height-computed + (@padding-base-vertical + 1));\n  }\n\n  // Make form groups behave like rows\n  .form-group {\n    .make-row();\n  }\n\n  .form-control-static {\n    padding-top: (@padding-base-vertical + 1);\n  }\n\n  // Only right align form labels here when the columns stop stacking\n  @media (min-width: @screen-sm-min) {\n    .control-label {\n      text-align: right;\n    }\n  }\n\n  // Validation states\n  //\n  // Reposition the icon because it's now within a grid column and columns have\n  // `position: relative;` on them. Also accounts for the grid gutter padding.\n  .has-feedback .form-control-feedback {\n    top: 0;\n    right: (@grid-gutter-width / 2);\n  }\n}\n","//\n// Buttons\n// --------------------------------------------------\n\n\n// Base styles\n// --------------------------------------------------\n\n.btn {\n  display: inline-block;\n  margin-bottom: 0; // For input.btn\n  font-weight: @btn-font-weight;\n  text-align: center;\n  vertical-align: middle;\n  cursor: pointer;\n  background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214\n  border: 1px solid transparent;\n  white-space: nowrap;\n  .button-size(@padding-base-vertical; @padding-base-horizontal; @font-size-base; @line-height-base; @border-radius-base);\n  .user-select(none);\n\n  &,\n  &:active,\n  &.active {\n    &:focus {\n      .tab-focus();\n    }\n  }\n\n  &:hover,\n  &:focus {\n    color: @btn-default-color;\n    text-decoration: none;\n  }\n\n  &:active,\n  &.active {\n    outline: 0;\n    background-image: none;\n    .box-shadow(inset 0 3px 5px rgba(0,0,0,.125));\n  }\n\n  &.disabled,\n  &[disabled],\n  fieldset[disabled] & {\n    cursor: not-allowed;\n    pointer-events: none; // Future-proof disabling of clicks\n    .opacity(.65);\n    .box-shadow(none);\n  }\n}\n\n\n// Alternate buttons\n// --------------------------------------------------\n\n.btn-default {\n  .button-variant(@btn-default-color; @btn-default-bg; @btn-default-border);\n}\n.btn-primary {\n  .button-variant(@btn-primary-color; @btn-primary-bg; @btn-primary-border);\n}\n// Success appears as green\n.btn-success {\n  .button-variant(@btn-success-color; @btn-success-bg; @btn-success-border);\n}\n// Info appears as blue-green\n.btn-info {\n  .button-variant(@btn-info-color; @btn-info-bg; @btn-info-border);\n}\n// Warning appears as orange\n.btn-warning {\n  .button-variant(@btn-warning-color; @btn-warning-bg; @btn-warning-border);\n}\n// Danger and error appear as red\n.btn-danger {\n  .button-variant(@btn-danger-color; @btn-danger-bg; @btn-danger-border);\n}\n\n\n// Link buttons\n// -------------------------\n\n// Make a button look and behave like a link\n.btn-link {\n  color: @link-color;\n  font-weight: normal;\n  cursor: pointer;\n  border-radius: 0;\n\n  &,\n  &:active,\n  &[disabled],\n  fieldset[disabled] & {\n    background-color: transparent;\n    .box-shadow(none);\n  }\n  &,\n  &:hover,\n  &:focus,\n  &:active {\n    border-color: transparent;\n  }\n  &:hover,\n  &:focus {\n    color: @link-hover-color;\n    text-decoration: underline;\n    background-color: transparent;\n  }\n  &[disabled],\n  fieldset[disabled] & {\n    &:hover,\n    &:focus {\n      color: @btn-link-disabled-color;\n      text-decoration: none;\n    }\n  }\n}\n\n\n// Button Sizes\n// --------------------------------------------------\n\n.btn-lg {\n  // line-height: ensure even-numbered height of button next to large input\n  .button-size(@padding-large-vertical; @padding-large-horizontal; @font-size-large; @line-height-large; @border-radius-large);\n}\n.btn-sm {\n  // line-height: ensure proper height of button next to small input\n  .button-size(@padding-small-vertical; @padding-small-horizontal; @font-size-small; @line-height-small; @border-radius-small);\n}\n.btn-xs {\n  .button-size(@padding-xs-vertical; @padding-xs-horizontal; @font-size-small; @line-height-small; @border-radius-small);\n}\n\n\n// Block button\n// --------------------------------------------------\n\n.btn-block {\n  display: block;\n  width: 100%;\n  padding-left: 0;\n  padding-right: 0;\n}\n\n// Vertically space out multiple block buttons\n.btn-block + .btn-block {\n  margin-top: 5px;\n}\n\n// Specificity overrides\ninput[type=\"submit\"],\ninput[type=\"reset\"],\ninput[type=\"button\"] {\n  &.btn-block {\n    width: 100%;\n  }\n}\n","//\n// Button groups\n// --------------------------------------------------\n\n// Make the div behave like a button\n.btn-group,\n.btn-group-vertical {\n  position: relative;\n  display: inline-block;\n  vertical-align: middle; // match .btn alignment given font-size hack above\n  > .btn {\n    position: relative;\n    float: left;\n    // Bring the \"active\" button to the front\n    &:hover,\n    &:focus,\n    &:active,\n    &.active {\n      z-index: 2;\n    }\n    &:focus {\n      // Remove focus outline when dropdown JS adds it after closing the menu\n      outline: none;\n    }\n  }\n}\n\n// Prevent double borders when buttons are next to each other\n.btn-group {\n  .btn + .btn,\n  .btn + .btn-group,\n  .btn-group + .btn,\n  .btn-group + .btn-group {\n    margin-left: -1px;\n  }\n}\n\n// Optional: Group multiple button groups together for a toolbar\n.btn-toolbar {\n  margin-left: -5px; // Offset the first child's margin\n  &:extend(.clearfix all);\n\n  .btn-group,\n  .input-group {\n    float: left;\n  }\n  > .btn,\n  > .btn-group,\n  > .input-group {\n    margin-left: 5px;\n  }\n}\n\n.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) {\n  border-radius: 0;\n}\n\n// Set corners individual because sometimes a single button can be in a .btn-group and we need :first-child and :last-child to both match\n.btn-group > .btn:first-child {\n  margin-left: 0;\n  &:not(:last-child):not(.dropdown-toggle) {\n    .border-right-radius(0);\n  }\n}\n// Need .dropdown-toggle since :last-child doesn't apply given a .dropdown-menu immediately after it\n.btn-group > .btn:last-child:not(:first-child),\n.btn-group > .dropdown-toggle:not(:first-child) {\n  .border-left-radius(0);\n}\n\n// Custom edits for including btn-groups within btn-groups (useful for including dropdown buttons within a btn-group)\n.btn-group > .btn-group {\n  float: left;\n}\n.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn {\n  border-radius: 0;\n}\n.btn-group > .btn-group:first-child {\n  > .btn:last-child,\n  > .dropdown-toggle {\n    .border-right-radius(0);\n  }\n}\n.btn-group > .btn-group:last-child > .btn:first-child {\n  .border-left-radius(0);\n}\n\n// On active and open, don't show outline\n.btn-group .dropdown-toggle:active,\n.btn-group.open .dropdown-toggle {\n  outline: 0;\n}\n\n\n// Sizing\n//\n// Remix the default button sizing classes into new ones for easier manipulation.\n\n.btn-group-xs > .btn { &:extend(.btn-xs); }\n.btn-group-sm > .btn { &:extend(.btn-sm); }\n.btn-group-lg > .btn { &:extend(.btn-lg); }\n\n\n// Split button dropdowns\n// ----------------------\n\n// Give the line between buttons some depth\n.btn-group > .btn + .dropdown-toggle {\n  padding-left: 8px;\n  padding-right: 8px;\n}\n.btn-group > .btn-lg + .dropdown-toggle {\n  padding-left: 12px;\n  padding-right: 12px;\n}\n\n// The clickable button for toggling the menu\n// Remove the gradient and set the same inset shadow as the :active state\n.btn-group.open .dropdown-toggle {\n  .box-shadow(inset 0 3px 5px rgba(0,0,0,.125));\n\n  // Show no shadow for `.btn-link` since it has no other button styles.\n  &.btn-link {\n    .box-shadow(none);\n  }\n}\n\n\n// Reposition the caret\n.btn .caret {\n  margin-left: 0;\n}\n// Carets in other button sizes\n.btn-lg .caret {\n  border-width: @caret-width-large @caret-width-large 0;\n  border-bottom-width: 0;\n}\n// Upside down carets for .dropup\n.dropup .btn-lg .caret {\n  border-width: 0 @caret-width-large @caret-width-large;\n}\n\n\n// Vertical button groups\n// ----------------------\n\n.btn-group-vertical {\n  > .btn,\n  > .btn-group,\n  > .btn-group > .btn {\n    display: block;\n    float: none;\n    width: 100%;\n    max-width: 100%;\n  }\n\n  // Clear floats so dropdown menus can be properly placed\n  > .btn-group {\n    &:extend(.clearfix all);\n    > .btn {\n      float: none;\n    }\n  }\n\n  > .btn + .btn,\n  > .btn + .btn-group,\n  > .btn-group + .btn,\n  > .btn-group + .btn-group {\n    margin-top: -1px;\n    margin-left: 0;\n  }\n}\n\n.btn-group-vertical > .btn {\n  &:not(:first-child):not(:last-child) {\n    border-radius: 0;\n  }\n  &:first-child:not(:last-child) {\n    border-top-right-radius: @border-radius-base;\n    .border-bottom-radius(0);\n  }\n  &:last-child:not(:first-child) {\n    border-bottom-left-radius: @border-radius-base;\n    .border-top-radius(0);\n  }\n}\n.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn {\n  border-radius: 0;\n}\n.btn-group-vertical > .btn-group:first-child:not(:last-child) {\n  > .btn:last-child,\n  > .dropdown-toggle {\n    .border-bottom-radius(0);\n  }\n}\n.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child {\n  .border-top-radius(0);\n}\n\n\n\n// Justified button groups\n// ----------------------\n\n.btn-group-justified {\n  display: table;\n  width: 100%;\n  table-layout: fixed;\n  border-collapse: separate;\n  > .btn,\n  > .btn-group {\n    float: none;\n    display: table-cell;\n    width: 1%;\n  }\n  > .btn-group .btn {\n    width: 100%;\n  }\n}\n\n\n// Checkbox and radio options\n[data-toggle=\"buttons\"] > .btn > input[type=\"radio\"],\n[data-toggle=\"buttons\"] > .btn > input[type=\"checkbox\"] {\n  display: none;\n}\n","//\n// Component animations\n// --------------------------------------------------\n\n// Heads up!\n//\n// We don't use the `.opacity()` mixin here since it causes a bug with text\n// fields in IE7-8. Source: https://github.com/twitter/bootstrap/pull/3552.\n\n.fade {\n  opacity: 0;\n  .transition(opacity .15s linear);\n  &.in {\n    opacity: 1;\n  }\n}\n\n.collapse {\n  display: none;\n  &.in {\n    display: block;\n  }\n}\n.collapsing {\n  position: relative;\n  height: 0;\n  overflow: hidden;\n  .transition(height .35s ease);\n}\n","//\n// Glyphicons for Bootstrap\n//\n// Since icons are fonts, they can be placed anywhere text is placed and are\n// thus automatically sized to match the surrounding child. To use, create an\n// inline element with the appropriate classes, like so:\n//\n// <a href=\"#\"><span class=\"glyphicon glyphicon-star\"></span> Star</a>\n\n// Import the fonts\n@font-face {\n  font-family: 'Glyphicons Halflings';\n  src: ~\"url('@{icon-font-path}@{icon-font-name}.eot')\";\n  src: ~\"url('@{icon-font-path}@{icon-font-name}.eot?#iefix') format('embedded-opentype')\",\n       ~\"url('@{icon-font-path}@{icon-font-name}.woff') format('woff')\",\n       ~\"url('@{icon-font-path}@{icon-font-name}.ttf') format('truetype')\",\n       ~\"url('@{icon-font-path}@{icon-font-name}.svg#@{icon-font-svg-id}') format('svg')\";\n}\n\n// Catchall baseclass\n.glyphicon {\n  position: relative;\n  top: 1px;\n  display: inline-block;\n  font-family: 'Glyphicons Halflings';\n  font-style: normal;\n  font-weight: normal;\n  line-height: 1;\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n}\n\n// Individual icons\n.glyphicon-asterisk               { &:before { content: \"\\2a\"; } }\n.glyphicon-plus                   { &:before { content: \"\\2b\"; } }\n.glyphicon-euro                   { &:before { content: \"\\20ac\"; } }\n.glyphicon-minus                  { &:before { content: \"\\2212\"; } }\n.glyphicon-cloud                  { &:before { content: \"\\2601\"; } }\n.glyphicon-envelope               { &:before { content: \"\\2709\"; } }\n.glyphicon-pencil                 { &:before { content: \"\\270f\"; } }\n.glyphicon-glass                  { &:before { content: \"\\e001\"; } }\n.glyphicon-music                  { &:before { content: \"\\e002\"; } }\n.glyphicon-search                 { &:before { content: \"\\e003\"; } }\n.glyphicon-heart                  { &:before { content: \"\\e005\"; } }\n.glyphicon-star                   { &:before { content: \"\\e006\"; } }\n.glyphicon-star-empty             { &:before { content: \"\\e007\"; } }\n.glyphicon-user                   { &:before { content: \"\\e008\"; } }\n.glyphicon-film                   { &:before { content: \"\\e009\"; } }\n.glyphicon-th-large               { &:before { content: \"\\e010\"; } }\n.glyphicon-th                     { &:before { content: \"\\e011\"; } }\n.glyphicon-th-list                { &:before { content: \"\\e012\"; } }\n.glyphicon-ok                     { &:before { content: \"\\e013\"; } }\n.glyphicon-remove                 { &:before { content: \"\\e014\"; } }\n.glyphicon-zoom-in                { &:before { content: \"\\e015\"; } }\n.glyphicon-zoom-out               { &:before { content: \"\\e016\"; } }\n.glyphicon-off                    { &:before { content: \"\\e017\"; } }\n.glyphicon-signal                 { &:before { content: \"\\e018\"; } }\n.glyphicon-cog                    { &:before { content: \"\\e019\"; } }\n.glyphicon-trash                  { &:before { content: \"\\e020\"; } }\n.glyphicon-home                   { &:before { content: \"\\e021\"; } }\n.glyphicon-file                   { &:before { content: \"\\e022\"; } }\n.glyphicon-time                   { &:before { content: \"\\e023\"; } }\n.glyphicon-road                   { &:before { content: \"\\e024\"; } }\n.glyphicon-download-alt           { &:before { content: \"\\e025\"; } }\n.glyphicon-download               { &:before { content: \"\\e026\"; } }\n.glyphicon-upload                 { &:before { content: \"\\e027\"; } }\n.glyphicon-inbox                  { &:before { content: \"\\e028\"; } }\n.glyphicon-play-circle            { &:before { content: \"\\e029\"; } }\n.glyphicon-repeat                 { &:before { content: \"\\e030\"; } }\n.glyphicon-refresh                { &:before { content: \"\\e031\"; } }\n.glyphicon-list-alt               { &:before { content: \"\\e032\"; } }\n.glyphicon-lock                   { &:before { content: \"\\e033\"; } }\n.glyphicon-flag                   { &:before { content: \"\\e034\"; } }\n.glyphicon-headphones             { &:before { content: \"\\e035\"; } }\n.glyphicon-volume-off             { &:before { content: \"\\e036\"; } }\n.glyphicon-volume-down            { &:before { content: \"\\e037\"; } }\n.glyphicon-volume-up              { &:before { content: \"\\e038\"; } }\n.glyphicon-qrcode                 { &:before { content: \"\\e039\"; } }\n.glyphicon-barcode                { &:before { content: \"\\e040\"; } }\n.glyphicon-tag                    { &:before { content: \"\\e041\"; } }\n.glyphicon-tags                   { &:before { content: \"\\e042\"; } }\n.glyphicon-book                   { &:before { content: \"\\e043\"; } }\n.glyphicon-bookmark               { &:before { content: \"\\e044\"; } }\n.glyphicon-print                  { &:before { content: \"\\e045\"; } }\n.glyphicon-camera                 { &:before { content: \"\\e046\"; } }\n.glyphicon-font                   { &:before { content: \"\\e047\"; } }\n.glyphicon-bold                   { &:before { content: \"\\e048\"; } }\n.glyphicon-italic                 { &:before { content: \"\\e049\"; } }\n.glyphicon-text-height            { &:before { content: \"\\e050\"; } }\n.glyphicon-text-width             { &:before { content: \"\\e051\"; } }\n.glyphicon-align-left             { &:before { content: \"\\e052\"; } }\n.glyphicon-align-center           { &:before { content: \"\\e053\"; } }\n.glyphicon-align-right            { &:before { content: \"\\e054\"; } }\n.glyphicon-align-justify          { &:before { content: \"\\e055\"; } }\n.glyphicon-list                   { &:before { content: \"\\e056\"; } }\n.glyphicon-indent-left            { &:before { content: \"\\e057\"; } }\n.glyphicon-indent-right           { &:before { content: \"\\e058\"; } }\n.glyphicon-facetime-video         { &:before { content: \"\\e059\"; } }\n.glyphicon-picture                { &:before { content: \"\\e060\"; } }\n.glyphicon-map-marker             { &:before { content: \"\\e062\"; } }\n.glyphicon-adjust                 { &:before { content: \"\\e063\"; } }\n.glyphicon-tint                   { &:before { content: \"\\e064\"; } }\n.glyphicon-edit                   { &:before { content: \"\\e065\"; } }\n.glyphicon-share                  { &:before { content: \"\\e066\"; } }\n.glyphicon-check                  { &:before { content: \"\\e067\"; } }\n.glyphicon-move                   { &:before { content: \"\\e068\"; } }\n.glyphicon-step-backward          { &:before { content: \"\\e069\"; } }\n.glyphicon-fast-backward          { &:before { content: \"\\e070\"; } }\n.glyphicon-backward               { &:before { content: \"\\e071\"; } }\n.glyphicon-play                   { &:before { content: \"\\e072\"; } }\n.glyphicon-pause                  { &:before { content: \"\\e073\"; } }\n.glyphicon-stop                   { &:before { content: \"\\e074\"; } }\n.glyphicon-forward                { &:before { content: \"\\e075\"; } }\n.glyphicon-fast-forward           { &:before { content: \"\\e076\"; } }\n.glyphicon-step-forward           { &:before { content: \"\\e077\"; } }\n.glyphicon-eject                  { &:before { content: \"\\e078\"; } }\n.glyphicon-chevron-left           { &:before { content: \"\\e079\"; } }\n.glyphicon-chevron-right          { &:before { content: \"\\e080\"; } }\n.glyphicon-plus-sign              { &:before { content: \"\\e081\"; } }\n.glyphicon-minus-sign             { &:before { content: \"\\e082\"; } }\n.glyphicon-remove-sign            { &:before { content: \"\\e083\"; } }\n.glyphicon-ok-sign                { &:before { content: \"\\e084\"; } }\n.glyphicon-question-sign          { &:before { content: \"\\e085\"; } }\n.glyphicon-info-sign              { &:before { content: \"\\e086\"; } }\n.glyphicon-screenshot             { &:before { content: \"\\e087\"; } }\n.glyphicon-remove-circle          { &:before { content: \"\\e088\"; } }\n.glyphicon-ok-circle              { &:before { content: \"\\e089\"; } }\n.glyphicon-ban-circle             { &:before { content: \"\\e090\"; } }\n.glyphicon-arrow-left             { &:before { content: \"\\e091\"; } }\n.glyphicon-arrow-right            { &:before { content: \"\\e092\"; } }\n.glyphicon-arrow-up               { &:before { content: \"\\e093\"; } }\n.glyphicon-arrow-down             { &:before { content: \"\\e094\"; } }\n.glyphicon-share-alt              { &:before { content: \"\\e095\"; } }\n.glyphicon-resize-full            { &:before { content: \"\\e096\"; } }\n.glyphicon-resize-small           { &:before { content: \"\\e097\"; } }\n.glyphicon-exclamation-sign       { &:before { content: \"\\e101\"; } }\n.glyphicon-gift                   { &:before { content: \"\\e102\"; } }\n.glyphicon-leaf                   { &:before { content: \"\\e103\"; } }\n.glyphicon-fire                   { &:before { content: \"\\e104\"; } }\n.glyphicon-eye-open               { &:before { content: \"\\e105\"; } }\n.glyphicon-eye-close              { &:before { content: \"\\e106\"; } }\n.glyphicon-warning-sign           { &:before { content: \"\\e107\"; } }\n.glyphicon-plane                  { &:before { content: \"\\e108\"; } }\n.glyphicon-calendar               { &:before { content: \"\\e109\"; } }\n.glyphicon-random                 { &:before { content: \"\\e110\"; } }\n.glyphicon-comment                { &:before { content: \"\\e111\"; } }\n.glyphicon-magnet                 { &:before { content: \"\\e112\"; } }\n.glyphicon-chevron-up             { &:before { content: \"\\e113\"; } }\n.glyphicon-chevron-down           { &:before { content: \"\\e114\"; } }\n.glyphicon-retweet                { &:before { content: \"\\e115\"; } }\n.glyphicon-shopping-cart          { &:before { content: \"\\e116\"; } }\n.glyphicon-folder-close           { &:before { content: \"\\e117\"; } }\n.glyphicon-folder-open            { &:before { content: \"\\e118\"; } }\n.glyphicon-resize-vertical        { &:before { content: \"\\e119\"; } }\n.glyphicon-resize-horizontal      { &:before { content: \"\\e120\"; } }\n.glyphicon-hdd                    { &:before { content: \"\\e121\"; } }\n.glyphicon-bullhorn               { &:before { content: \"\\e122\"; } }\n.glyphicon-bell                   { &:before { content: \"\\e123\"; } }\n.glyphicon-certificate            { &:before { content: \"\\e124\"; } }\n.glyphicon-thumbs-up              { &:before { content: \"\\e125\"; } }\n.glyphicon-thumbs-down            { &:before { content: \"\\e126\"; } }\n.glyphicon-hand-right             { &:before { content: \"\\e127\"; } }\n.glyphicon-hand-left              { &:before { content: \"\\e128\"; } }\n.glyphicon-hand-up                { &:before { content: \"\\e129\"; } }\n.glyphicon-hand-down              { &:before { content: \"\\e130\"; } }\n.glyphicon-circle-arrow-right     { &:before { content: \"\\e131\"; } }\n.glyphicon-circle-arrow-left      { &:before { content: \"\\e132\"; } }\n.glyphicon-circle-arrow-up        { &:before { content: \"\\e133\"; } }\n.glyphicon-circle-arrow-down      { &:before { content: \"\\e134\"; } }\n.glyphicon-globe                  { &:before { content: \"\\e135\"; } }\n.glyphicon-wrench                 { &:before { content: \"\\e136\"; } }\n.glyphicon-tasks                  { &:before { content: \"\\e137\"; } }\n.glyphicon-filter                 { &:before { content: \"\\e138\"; } }\n.glyphicon-briefcase              { &:before { content: \"\\e139\"; } }\n.glyphicon-fullscreen             { &:before { content: \"\\e140\"; } }\n.glyphicon-dashboard              { &:before { content: \"\\e141\"; } }\n.glyphicon-paperclip              { &:before { content: \"\\e142\"; } }\n.glyphicon-heart-empty            { &:before { content: \"\\e143\"; } }\n.glyphicon-link                   { &:before { content: \"\\e144\"; } }\n.glyphicon-phone                  { &:before { content: \"\\e145\"; } }\n.glyphicon-pushpin                { &:before { content: \"\\e146\"; } }\n.glyphicon-usd                    { &:before { content: \"\\e148\"; } }\n.glyphicon-gbp                    { &:before { content: \"\\e149\"; } }\n.glyphicon-sort                   { &:before { content: \"\\e150\"; } }\n.glyphicon-sort-by-alphabet       { &:before { content: \"\\e151\"; } }\n.glyphicon-sort-by-alphabet-alt   { &:before { content: \"\\e152\"; } }\n.glyphicon-sort-by-order          { &:before { content: \"\\e153\"; } }\n.glyphicon-sort-by-order-alt      { &:before { content: \"\\e154\"; } }\n.glyphicon-sort-by-attributes     { &:before { content: \"\\e155\"; } }\n.glyphicon-sort-by-attributes-alt { &:before { content: \"\\e156\"; } }\n.glyphicon-unchecked              { &:before { content: \"\\e157\"; } }\n.glyphicon-expand                 { &:before { content: \"\\e158\"; } }\n.glyphicon-collapse-down          { &:before { content: \"\\e159\"; } }\n.glyphicon-collapse-up            { &:before { content: \"\\e160\"; } }\n.glyphicon-log-in                 { &:before { content: \"\\e161\"; } }\n.glyphicon-flash                  { &:before { content: \"\\e162\"; } }\n.glyphicon-log-out                { &:before { content: \"\\e163\"; } }\n.glyphicon-new-window             { &:before { content: \"\\e164\"; } }\n.glyphicon-record                 { &:before { content: \"\\e165\"; } }\n.glyphicon-save                   { &:before { content: \"\\e166\"; } }\n.glyphicon-open                   { &:before { content: \"\\e167\"; } }\n.glyphicon-saved                  { &:before { content: \"\\e168\"; } }\n.glyphicon-import                 { &:before { content: \"\\e169\"; } }\n.glyphicon-export                 { &:before { content: \"\\e170\"; } }\n.glyphicon-send                   { &:before { content: \"\\e171\"; } }\n.glyphicon-floppy-disk            { &:before { content: \"\\e172\"; } }\n.glyphicon-floppy-saved           { &:before { content: \"\\e173\"; } }\n.glyphicon-floppy-remove          { &:before { content: \"\\e174\"; } }\n.glyphicon-floppy-save            { &:before { content: \"\\e175\"; } }\n.glyphicon-floppy-open            { &:before { content: \"\\e176\"; } }\n.glyphicon-credit-card            { &:before { content: \"\\e177\"; } }\n.glyphicon-transfer               { &:before { content: \"\\e178\"; } }\n.glyphicon-cutlery                { &:before { content: \"\\e179\"; } }\n.glyphicon-header                 { &:before { content: \"\\e180\"; } }\n.glyphicon-compressed             { &:before { content: \"\\e181\"; } }\n.glyphicon-earphone               { &:before { content: \"\\e182\"; } }\n.glyphicon-phone-alt              { &:before { content: \"\\e183\"; } }\n.glyphicon-tower                  { &:before { content: \"\\e184\"; } }\n.glyphicon-stats                  { &:before { content: \"\\e185\"; } }\n.glyphicon-sd-video               { &:before { content: \"\\e186\"; } }\n.glyphicon-hd-video               { &:before { content: \"\\e187\"; } }\n.glyphicon-subtitles              { &:before { content: \"\\e188\"; } }\n.glyphicon-sound-stereo           { &:before { content: \"\\e189\"; } }\n.glyphicon-sound-dolby            { &:before { content: \"\\e190\"; } }\n.glyphicon-sound-5-1              { &:before { content: \"\\e191\"; } }\n.glyphicon-sound-6-1              { &:before { content: \"\\e192\"; } }\n.glyphicon-sound-7-1              { &:before { content: \"\\e193\"; } }\n.glyphicon-copyright-mark         { &:before { content: \"\\e194\"; } }\n.glyphicon-registration-mark      { &:before { content: \"\\e195\"; } }\n.glyphicon-cloud-download         { &:before { content: \"\\e197\"; } }\n.glyphicon-cloud-upload           { &:before { content: \"\\e198\"; } }\n.glyphicon-tree-conifer           { &:before { content: \"\\e199\"; } }\n.glyphicon-tree-deciduous         { &:before { content: \"\\e200\"; } }\n","//\n// Dropdown menus\n// --------------------------------------------------\n\n\n// Dropdown arrow/caret\n.caret {\n  display: inline-block;\n  width: 0;\n  height: 0;\n  margin-left: 2px;\n  vertical-align: middle;\n  border-top:   @caret-width-base solid;\n  border-right: @caret-width-base solid transparent;\n  border-left:  @caret-width-base solid transparent;\n}\n\n// The dropdown wrapper (div)\n.dropdown {\n  position: relative;\n}\n\n// Prevent the focus on the dropdown toggle when closing dropdowns\n.dropdown-toggle:focus {\n  outline: 0;\n}\n\n// The dropdown menu (ul)\n.dropdown-menu {\n  position: absolute;\n  top: 100%;\n  left: 0;\n  z-index: @zindex-dropdown;\n  display: none; // none by default, but block on \"open\" of the menu\n  float: left;\n  min-width: 160px;\n  padding: 5px 0;\n  margin: 2px 0 0; // override default ul\n  list-style: none;\n  font-size: @font-size-base;\n  background-color: @dropdown-bg;\n  border: 1px solid @dropdown-fallback-border; // IE8 fallback\n  border: 1px solid @dropdown-border;\n  border-radius: @border-radius-base;\n  .box-shadow(0 6px 12px rgba(0,0,0,.175));\n  background-clip: padding-box;\n\n  // Aligns the dropdown menu to right\n  //\n  // Deprecated as of 3.1.0 in favor of `.dropdown-menu-[dir]`\n  &.pull-right {\n    right: 0;\n    left: auto;\n  }\n\n  // Dividers (basically an hr) within the dropdown\n  .divider {\n    .nav-divider(@dropdown-divider-bg);\n  }\n\n  // Links within the dropdown menu\n  > li > a {\n    display: block;\n    padding: 3px 20px;\n    clear: both;\n    font-weight: normal;\n    line-height: @line-height-base;\n    color: @dropdown-link-color;\n    white-space: nowrap; // prevent links from randomly breaking onto new lines\n  }\n}\n\n// Hover/Focus state\n.dropdown-menu > li > a {\n  &:hover,\n  &:focus {\n    text-decoration: none;\n    color: @dropdown-link-hover-color;\n    background-color: @dropdown-link-hover-bg;\n  }\n}\n\n// Active state\n.dropdown-menu > .active > a {\n  &,\n  &:hover,\n  &:focus {\n    color: @dropdown-link-active-color;\n    text-decoration: none;\n    outline: 0;\n    background-color: @dropdown-link-active-bg;\n  }\n}\n\n// Disabled state\n//\n// Gray out text and ensure the hover/focus state remains gray\n\n.dropdown-menu > .disabled > a {\n  &,\n  &:hover,\n  &:focus {\n    color: @dropdown-link-disabled-color;\n  }\n}\n// Nuke hover/focus effects\n.dropdown-menu > .disabled > a {\n  &:hover,\n  &:focus {\n    text-decoration: none;\n    background-color: transparent;\n    background-image: none; // Remove CSS gradient\n    .reset-filter();\n    cursor: not-allowed;\n  }\n}\n\n// Open state for the dropdown\n.open {\n  // Show the menu\n  > .dropdown-menu {\n    display: block;\n  }\n\n  // Remove the outline when :focus is triggered\n  > a {\n    outline: 0;\n  }\n}\n\n// Menu positioning\n//\n// Add extra class to `.dropdown-menu` to flip the alignment of the dropdown\n// menu with the parent.\n.dropdown-menu-right {\n  left: auto; // Reset the default from `.dropdown-menu`\n  right: 0;\n}\n// With v3, we enabled auto-flipping if you have a dropdown within a right\n// aligned nav component. To enable the undoing of that, we provide an override\n// to restore the default dropdown menu alignment.\n//\n// This is only for left-aligning a dropdown menu within a `.navbar-right` or\n// `.pull-right` nav component.\n.dropdown-menu-left {\n  left: 0;\n  right: auto;\n}\n\n// Dropdown section headers\n.dropdown-header {\n  display: block;\n  padding: 3px 20px;\n  font-size: @font-size-small;\n  line-height: @line-height-base;\n  color: @dropdown-header-color;\n}\n\n// Backdrop to catch body clicks on mobile, etc.\n.dropdown-backdrop {\n  position: fixed;\n  left: 0;\n  right: 0;\n  bottom: 0;\n  top: 0;\n  z-index: (@zindex-dropdown - 10);\n}\n\n// Right aligned dropdowns\n.pull-right > .dropdown-menu {\n  right: 0;\n  left: auto;\n}\n\n// Allow for dropdowns to go bottom up (aka, dropup-menu)\n//\n// Just add .dropup after the standard .dropdown class and you're set, bro.\n// TODO: abstract this so that the navbar fixed styles are not placed here?\n\n.dropup,\n.navbar-fixed-bottom .dropdown {\n  // Reverse the caret\n  .caret {\n    border-top: 0;\n    border-bottom: @caret-width-base solid;\n    content: \"\";\n  }\n  // Different positioning for bottom up menu\n  .dropdown-menu {\n    top: auto;\n    bottom: 100%;\n    margin-bottom: 1px;\n  }\n}\n\n\n// Component alignment\n//\n// Reiterate per navbar.less and the modified component alignment there.\n\n@media (min-width: @grid-float-breakpoint) {\n  .navbar-right {\n    .dropdown-menu {\n      .dropdown-menu-right();\n    }\n    // Necessary for overrides of the default right aligned menu.\n    // Will remove come v4 in all likelihood.\n    .dropdown-menu-left {\n      .dropdown-menu-left();\n    }\n  }\n}\n\n","//\n// Input groups\n// --------------------------------------------------\n\n// Base styles\n// -------------------------\n.input-group {\n  position: relative; // For dropdowns\n  display: table;\n  border-collapse: separate; // prevent input groups from inheriting border styles from table cells when placed within a table\n\n  // Undo padding and float of grid classes\n  &[class*=\"col-\"] {\n    float: none;\n    padding-left: 0;\n    padding-right: 0;\n  }\n\n  .form-control {\n    // Ensure that the input is always above the *appended* addon button for\n    // proper border colors.\n    position: relative;\n    z-index: 2;\n\n    // IE9 fubars the placeholder attribute in text inputs and the arrows on\n    // select elements in input groups. To fix it, we float the input. Details:\n    // https://github.com/twbs/bootstrap/issues/11561#issuecomment-28936855\n    float: left;\n\n    width: 100%;\n    margin-bottom: 0;\n  }\n}\n\n// Sizing options\n//\n// Remix the default form control sizing classes into new ones for easier\n// manipulation.\n\n.input-group-lg > .form-control,\n.input-group-lg > .input-group-addon,\n.input-group-lg > .input-group-btn > .btn { .input-lg(); }\n.input-group-sm > .form-control,\n.input-group-sm > .input-group-addon,\n.input-group-sm > .input-group-btn > .btn { .input-sm(); }\n\n\n// Display as table-cell\n// -------------------------\n.input-group-addon,\n.input-group-btn,\n.input-group .form-control {\n  display: table-cell;\n\n  &:not(:first-child):not(:last-child) {\n    border-radius: 0;\n  }\n}\n// Addon and addon wrapper for buttons\n.input-group-addon,\n.input-group-btn {\n  width: 1%;\n  white-space: nowrap;\n  vertical-align: middle; // Match the inputs\n}\n\n// Text input groups\n// -------------------------\n.input-group-addon {\n  padding: @padding-base-vertical @padding-base-horizontal;\n  font-size: @font-size-base;\n  font-weight: normal;\n  line-height: 1;\n  color: @input-color;\n  text-align: center;\n  background-color: @input-group-addon-bg;\n  border: 1px solid @input-group-addon-border-color;\n  border-radius: @border-radius-base;\n\n  // Sizing\n  &.input-sm {\n    padding: @padding-small-vertical @padding-small-horizontal;\n    font-size: @font-size-small;\n    border-radius: @border-radius-small;\n  }\n  &.input-lg {\n    padding: @padding-large-vertical @padding-large-horizontal;\n    font-size: @font-size-large;\n    border-radius: @border-radius-large;\n  }\n\n  // Nuke default margins from checkboxes and radios to vertically center within.\n  input[type=\"radio\"],\n  input[type=\"checkbox\"] {\n    margin-top: 0;\n  }\n}\n\n// Reset rounded corners\n.input-group .form-control:first-child,\n.input-group-addon:first-child,\n.input-group-btn:first-child > .btn,\n.input-group-btn:first-child > .btn-group > .btn,\n.input-group-btn:first-child > .dropdown-toggle,\n.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle),\n.input-group-btn:last-child > .btn-group:not(:last-child) > .btn {\n  .border-right-radius(0);\n}\n.input-group-addon:first-child {\n  border-right: 0;\n}\n.input-group .form-control:last-child,\n.input-group-addon:last-child,\n.input-group-btn:last-child > .btn,\n.input-group-btn:last-child > .btn-group > .btn,\n.input-group-btn:last-child > .dropdown-toggle,\n.input-group-btn:first-child > .btn:not(:first-child),\n.input-group-btn:first-child > .btn-group:not(:first-child) > .btn {\n  .border-left-radius(0);\n}\n.input-group-addon:last-child {\n  border-left: 0;\n}\n\n// Button input groups\n// -------------------------\n.input-group-btn {\n  position: relative;\n  // Jankily prevent input button groups from wrapping with `white-space` and\n  // `font-size` in combination with `inline-block` on buttons.\n  font-size: 0;\n  white-space: nowrap;\n\n  // Negative margin for spacing, position for bringing hovered/focused/actived\n  // element above the siblings.\n  > .btn {\n    position: relative;\n    + .btn {\n      margin-left: -1px;\n    }\n    // Bring the \"active\" button to the front\n    &:hover,\n    &:focus,\n    &:active {\n      z-index: 2;\n    }\n  }\n\n  // Negative margin to only have a 1px border between the two\n  &:first-child {\n    > .btn,\n    > .btn-group {\n      margin-right: -1px;\n    }\n  }\n  &:last-child {\n    > .btn,\n    > .btn-group {\n      margin-left: -1px;\n    }\n  }\n}\n","//\n// Navs\n// --------------------------------------------------\n\n\n// Base class\n// --------------------------------------------------\n\n.nav {\n  margin-bottom: 0;\n  padding-left: 0; // Override default ul/ol\n  list-style: none;\n  &:extend(.clearfix all);\n\n  > li {\n    position: relative;\n    display: block;\n\n    > a {\n      position: relative;\n      display: block;\n      padding: @nav-link-padding;\n      &:hover,\n      &:focus {\n        text-decoration: none;\n        background-color: @nav-link-hover-bg;\n      }\n    }\n\n    // Disabled state sets text to gray and nukes hover/tab effects\n    &.disabled > a {\n      color: @nav-disabled-link-color;\n\n      &:hover,\n      &:focus {\n        color: @nav-disabled-link-hover-color;\n        text-decoration: none;\n        background-color: transparent;\n        cursor: not-allowed;\n      }\n    }\n  }\n\n  // Open dropdowns\n  .open > a {\n    &,\n    &:hover,\n    &:focus {\n      background-color: @nav-link-hover-bg;\n      border-color: @link-color;\n    }\n  }\n\n  // Nav dividers (deprecated with v3.0.1)\n  //\n  // This should have been removed in v3 with the dropping of `.nav-list`, but\n  // we missed it. We don't currently support this anywhere, but in the interest\n  // of maintaining backward compatibility in case you use it, it's deprecated.\n  .nav-divider {\n    .nav-divider();\n  }\n\n  // Prevent IE8 from misplacing imgs\n  //\n  // See https://github.com/h5bp/html5-boilerplate/issues/984#issuecomment-3985989\n  > li > a > img {\n    max-width: none;\n  }\n}\n\n\n// Tabs\n// -------------------------\n\n// Give the tabs something to sit on\n.nav-tabs {\n  border-bottom: 1px solid @nav-tabs-border-color;\n  > li {\n    float: left;\n    // Make the list-items overlay the bottom border\n    margin-bottom: -1px;\n\n    // Actual tabs (as links)\n    > a {\n      margin-right: 2px;\n      line-height: @line-height-base;\n      border: 1px solid transparent;\n      border-radius: @border-radius-base @border-radius-base 0 0;\n      &:hover {\n        border-color: @nav-tabs-link-hover-border-color @nav-tabs-link-hover-border-color @nav-tabs-border-color;\n      }\n    }\n\n    // Active state, and its :hover to override normal :hover\n    &.active > a {\n      &,\n      &:hover,\n      &:focus {\n        color: @nav-tabs-active-link-hover-color;\n        background-color: @nav-tabs-active-link-hover-bg;\n        border: 1px solid @nav-tabs-active-link-hover-border-color;\n        border-bottom-color: transparent;\n        cursor: default;\n      }\n    }\n  }\n  // pulling this in mainly for less shorthand\n  &.nav-justified {\n    .nav-justified();\n    .nav-tabs-justified();\n  }\n}\n\n\n// Pills\n// -------------------------\n.nav-pills {\n  > li {\n    float: left;\n\n    // Links rendered as pills\n    > a {\n      border-radius: @nav-pills-border-radius;\n    }\n    + li {\n      margin-left: 2px;\n    }\n\n    // Active state\n    &.active > a {\n      &,\n      &:hover,\n      &:focus {\n        color: @nav-pills-active-link-hover-color;\n        background-color: @nav-pills-active-link-hover-bg;\n      }\n    }\n  }\n}\n\n\n// Stacked pills\n.nav-stacked {\n  > li {\n    float: none;\n    + li {\n      margin-top: 2px;\n      margin-left: 0; // no need for this gap between nav items\n    }\n  }\n}\n\n\n// Nav variations\n// --------------------------------------------------\n\n// Justified nav links\n// -------------------------\n\n.nav-justified {\n  width: 100%;\n\n  > li {\n    float: none;\n     > a {\n      text-align: center;\n      margin-bottom: 5px;\n    }\n  }\n\n  > .dropdown .dropdown-menu {\n    top: auto;\n    left: auto;\n  }\n\n  @media (min-width: @screen-sm-min) {\n    > li {\n      display: table-cell;\n      width: 1%;\n      > a {\n        margin-bottom: 0;\n      }\n    }\n  }\n}\n\n// Move borders to anchors instead of bottom of list\n//\n// Mixin for adding on top the shared `.nav-justified` styles for our tabs\n.nav-tabs-justified {\n  border-bottom: 0;\n\n  > li > a {\n    // Override margin from .nav-tabs\n    margin-right: 0;\n    border-radius: @border-radius-base;\n  }\n\n  > .active > a,\n  > .active > a:hover,\n  > .active > a:focus {\n    border: 1px solid @nav-tabs-justified-link-border-color;\n  }\n\n  @media (min-width: @screen-sm-min) {\n    > li > a {\n      border-bottom: 1px solid @nav-tabs-justified-link-border-color;\n      border-radius: @border-radius-base @border-radius-base 0 0;\n    }\n    > .active > a,\n    > .active > a:hover,\n    > .active > a:focus {\n      border-bottom-color: @nav-tabs-justified-active-link-border-color;\n    }\n  }\n}\n\n\n// Tabbable tabs\n// -------------------------\n\n// Hide tabbable panes to start, show them when `.active`\n.tab-content {\n  > .tab-pane {\n    display: none;\n  }\n  > .active {\n    display: block;\n  }\n}\n\n\n// Dropdowns\n// -------------------------\n\n// Specific dropdowns\n.nav-tabs .dropdown-menu {\n  // make dropdown border overlap tab border\n  margin-top: -1px;\n  // Remove the top rounded corners here since there is a hard edge above the menu\n  .border-top-radius(0);\n}\n","//\n// Navbars\n// --------------------------------------------------\n\n\n// Wrapper and base class\n//\n// Provide a static navbar from which we expand to create full-width, fixed, and\n// other navbar variations.\n\n.navbar {\n  position: relative;\n  min-height: @navbar-height; // Ensure a navbar always shows (e.g., without a .navbar-brand in collapsed mode)\n  margin-bottom: @navbar-margin-bottom;\n  border: 1px solid transparent;\n\n  // Prevent floats from breaking the navbar\n  &:extend(.clearfix all);\n\n  @media (min-width: @grid-float-breakpoint) {\n    border-radius: @navbar-border-radius;\n  }\n}\n\n\n// Navbar heading\n//\n// Groups `.navbar-brand` and `.navbar-toggle` into a single component for easy\n// styling of responsive aspects.\n\n.navbar-header {\n  &:extend(.clearfix all);\n\n  @media (min-width: @grid-float-breakpoint) {\n    float: left;\n  }\n}\n\n\n// Navbar collapse (body)\n//\n// Group your navbar content into this for easy collapsing and expanding across\n// various device sizes. By default, this content is collapsed when <768px, but\n// will expand past that for a horizontal display.\n//\n// To start (on mobile devices) the navbar links, forms, and buttons are stacked\n// vertically and include a `max-height` to overflow in case you have too much\n// content for the user's viewport.\n\n.navbar-collapse {\n  max-height: @navbar-collapse-max-height;\n  overflow-x: visible;\n  padding-right: @navbar-padding-horizontal;\n  padding-left:  @navbar-padding-horizontal;\n  border-top: 1px solid transparent;\n  box-shadow: inset 0 1px 0 rgba(255,255,255,.1);\n  &:extend(.clearfix all);\n  -webkit-overflow-scrolling: touch;\n\n  &.in {\n    overflow-y: auto;\n  }\n\n  @media (min-width: @grid-float-breakpoint) {\n    width: auto;\n    border-top: 0;\n    box-shadow: none;\n\n    &.collapse {\n      display: block !important;\n      height: auto !important;\n      padding-bottom: 0; // Override default setting\n      overflow: visible !important;\n    }\n\n    &.in {\n      overflow-y: visible;\n    }\n\n    // Undo the collapse side padding for navbars with containers to ensure\n    // alignment of right-aligned contents.\n    .navbar-fixed-top &,\n    .navbar-static-top &,\n    .navbar-fixed-bottom & {\n      padding-left: 0;\n      padding-right: 0;\n    }\n  }\n}\n\n\n// Both navbar header and collapse\n//\n// When a container is present, change the behavior of the header and collapse.\n\n.container,\n.container-fluid {\n  > .navbar-header,\n  > .navbar-collapse {\n    margin-right: -@navbar-padding-horizontal;\n    margin-left:  -@navbar-padding-horizontal;\n\n    @media (min-width: @grid-float-breakpoint) {\n      margin-right: 0;\n      margin-left:  0;\n    }\n  }\n}\n\n\n//\n// Navbar alignment options\n//\n// Display the navbar across the entirety of the page or fixed it to the top or\n// bottom of the page.\n\n// Static top (unfixed, but 100% wide) navbar\n.navbar-static-top {\n  z-index: @zindex-navbar;\n  border-width: 0 0 1px;\n\n  @media (min-width: @grid-float-breakpoint) {\n    border-radius: 0;\n  }\n}\n\n// Fix the top/bottom navbars when screen real estate supports it\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n  position: fixed;\n  right: 0;\n  left: 0;\n  z-index: @zindex-navbar-fixed;\n\n  // Undo the rounded corners\n  @media (min-width: @grid-float-breakpoint) {\n    border-radius: 0;\n  }\n}\n.navbar-fixed-top {\n  top: 0;\n  border-width: 0 0 1px;\n}\n.navbar-fixed-bottom {\n  bottom: 0;\n  margin-bottom: 0; // override .navbar defaults\n  border-width: 1px 0 0;\n}\n\n\n// Brand/project name\n\n.navbar-brand {\n  float: left;\n  padding: @navbar-padding-vertical @navbar-padding-horizontal;\n  font-size: @font-size-large;\n  line-height: @line-height-computed;\n  height: @navbar-height;\n\n  &:hover,\n  &:focus {\n    text-decoration: none;\n  }\n\n  @media (min-width: @grid-float-breakpoint) {\n    .navbar > .container &,\n    .navbar > .container-fluid & {\n      margin-left: -@navbar-padding-horizontal;\n    }\n  }\n}\n\n\n// Navbar toggle\n//\n// Custom button for toggling the `.navbar-collapse`, powered by the collapse\n// JavaScript plugin.\n\n.navbar-toggle {\n  position: relative;\n  float: right;\n  margin-right: @navbar-padding-horizontal;\n  padding: 9px 10px;\n  .navbar-vertical-align(34px);\n  background-color: transparent;\n  background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214\n  border: 1px solid transparent;\n  border-radius: @border-radius-base;\n\n  // We remove the `outline` here, but later compensate by attaching `:hover`\n  // styles to `:focus`.\n  &:focus {\n    outline: none;\n  }\n\n  // Bars\n  .icon-bar {\n    display: block;\n    width: 22px;\n    height: 2px;\n    border-radius: 1px;\n  }\n  .icon-bar + .icon-bar {\n    margin-top: 4px;\n  }\n\n  @media (min-width: @grid-float-breakpoint) {\n    display: none;\n  }\n}\n\n\n// Navbar nav links\n//\n// Builds on top of the `.nav` components with its own modifier class to make\n// the nav the full height of the horizontal nav (above 768px).\n\n.navbar-nav {\n  margin: (@navbar-padding-vertical / 2) -@navbar-padding-horizontal;\n\n  > li > a {\n    padding-top:    10px;\n    padding-bottom: 10px;\n    line-height: @line-height-computed;\n  }\n\n  @media (max-width: @grid-float-breakpoint-max) {\n    // Dropdowns get custom display when collapsed\n    .open .dropdown-menu {\n      position: static;\n      float: none;\n      width: auto;\n      margin-top: 0;\n      background-color: transparent;\n      border: 0;\n      box-shadow: none;\n      > li > a,\n      .dropdown-header {\n        padding: 5px 15px 5px 25px;\n      }\n      > li > a {\n        line-height: @line-height-computed;\n        &:hover,\n        &:focus {\n          background-image: none;\n        }\n      }\n    }\n  }\n\n  // Uncollapse the nav\n  @media (min-width: @grid-float-breakpoint) {\n    float: left;\n    margin: 0;\n\n    > li {\n      float: left;\n      > a {\n        padding-top:    @navbar-padding-vertical;\n        padding-bottom: @navbar-padding-vertical;\n      }\n    }\n\n    &.navbar-right:last-child {\n      margin-right: -@navbar-padding-horizontal;\n    }\n  }\n}\n\n\n// Component alignment\n//\n// Repurpose the pull utilities as their own navbar utilities to avoid specificity\n// issues with parents and chaining. Only do this when the navbar is uncollapsed\n// though so that navbar contents properly stack and align in mobile.\n\n@media (min-width: @grid-float-breakpoint) {\n  .navbar-left  { .pull-left(); }\n  .navbar-right { .pull-right(); }\n}\n\n\n// Navbar form\n//\n// Extension of the `.form-inline` with some extra flavor for optimum display in\n// our navbars.\n\n.navbar-form {\n  margin-left: -@navbar-padding-horizontal;\n  margin-right: -@navbar-padding-horizontal;\n  padding: 10px @navbar-padding-horizontal;\n  border-top: 1px solid transparent;\n  border-bottom: 1px solid transparent;\n  @shadow: inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.1);\n  .box-shadow(@shadow);\n\n  // Mixin behavior for optimum display\n  .form-inline();\n\n  .form-group {\n    @media (max-width: @grid-float-breakpoint-max) {\n      margin-bottom: 5px;\n    }\n  }\n\n  // Vertically center in expanded, horizontal navbar\n  .navbar-vertical-align(@input-height-base);\n\n  // Undo 100% width for pull classes\n  @media (min-width: @grid-float-breakpoint) {\n    width: auto;\n    border: 0;\n    margin-left: 0;\n    margin-right: 0;\n    padding-top: 0;\n    padding-bottom: 0;\n    .box-shadow(none);\n\n    // Outdent the form if last child to line up with content down the page\n    &.navbar-right:last-child {\n      margin-right: -@navbar-padding-horizontal;\n    }\n  }\n}\n\n\n// Dropdown menus\n\n// Menu position and menu carets\n.navbar-nav > li > .dropdown-menu {\n  margin-top: 0;\n  .border-top-radius(0);\n}\n// Menu position and menu caret support for dropups via extra dropup class\n.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu {\n  .border-bottom-radius(0);\n}\n\n\n// Buttons in navbars\n//\n// Vertically center a button within a navbar (when *not* in a form).\n\n.navbar-btn {\n  .navbar-vertical-align(@input-height-base);\n\n  &.btn-sm {\n    .navbar-vertical-align(@input-height-small);\n  }\n  &.btn-xs {\n    .navbar-vertical-align(22);\n  }\n}\n\n\n// Text in navbars\n//\n// Add a class to make any element properly align itself vertically within the navbars.\n\n.navbar-text {\n  .navbar-vertical-align(@line-height-computed);\n\n  @media (min-width: @grid-float-breakpoint) {\n    float: left;\n    margin-left: @navbar-padding-horizontal;\n    margin-right: @navbar-padding-horizontal;\n\n    // Outdent the form if last child to line up with content down the page\n    &.navbar-right:last-child {\n      margin-right: 0;\n    }\n  }\n}\n\n// Alternate navbars\n// --------------------------------------------------\n\n// Default navbar\n.navbar-default {\n  background-color: @navbar-default-bg;\n  border-color: @navbar-default-border;\n\n  .navbar-brand {\n    color: @navbar-default-brand-color;\n    &:hover,\n    &:focus {\n      color: @navbar-default-brand-hover-color;\n      background-color: @navbar-default-brand-hover-bg;\n    }\n  }\n\n  .navbar-text {\n    color: @navbar-default-color;\n  }\n\n  .navbar-nav {\n    > li > a {\n      color: @navbar-default-link-color;\n\n      &:hover,\n      &:focus {\n        color: @navbar-default-link-hover-color;\n        background-color: @navbar-default-link-hover-bg;\n      }\n    }\n    > .active > a {\n      &,\n      &:hover,\n      &:focus {\n        color: @navbar-default-link-active-color;\n        background-color: @navbar-default-link-active-bg;\n      }\n    }\n    > .disabled > a {\n      &,\n      &:hover,\n      &:focus {\n        color: @navbar-default-link-disabled-color;\n        background-color: @navbar-default-link-disabled-bg;\n      }\n    }\n  }\n\n  .navbar-toggle {\n    border-color: @navbar-default-toggle-border-color;\n    &:hover,\n    &:focus {\n      background-color: @navbar-default-toggle-hover-bg;\n    }\n    .icon-bar {\n      background-color: @navbar-default-toggle-icon-bar-bg;\n    }\n  }\n\n  .navbar-collapse,\n  .navbar-form {\n    border-color: @navbar-default-border;\n  }\n\n  // Dropdown menu items\n  .navbar-nav {\n    // Remove background color from open dropdown\n    > .open > a {\n      &,\n      &:hover,\n      &:focus {\n        background-color: @navbar-default-link-active-bg;\n        color: @navbar-default-link-active-color;\n      }\n    }\n\n    @media (max-width: @grid-float-breakpoint-max) {\n      // Dropdowns get custom display when collapsed\n      .open .dropdown-menu {\n        > li > a {\n          color: @navbar-default-link-color;\n          &:hover,\n          &:focus {\n            color: @navbar-default-link-hover-color;\n            background-color: @navbar-default-link-hover-bg;\n          }\n        }\n        > .active > a {\n          &,\n          &:hover,\n          &:focus {\n            color: @navbar-default-link-active-color;\n            background-color: @navbar-default-link-active-bg;\n          }\n        }\n        > .disabled > a {\n          &,\n          &:hover,\n          &:focus {\n            color: @navbar-default-link-disabled-color;\n            background-color: @navbar-default-link-disabled-bg;\n          }\n        }\n      }\n    }\n  }\n\n\n  // Links in navbars\n  //\n  // Add a class to ensure links outside the navbar nav are colored correctly.\n\n  .navbar-link {\n    color: @navbar-default-link-color;\n    &:hover {\n      color: @navbar-default-link-hover-color;\n    }\n  }\n\n}\n\n// Inverse navbar\n\n.navbar-inverse {\n  background-color: @navbar-inverse-bg;\n  border-color: @navbar-inverse-border;\n\n  .navbar-brand {\n    color: @navbar-inverse-brand-color;\n    &:hover,\n    &:focus {\n      color: @navbar-inverse-brand-hover-color;\n      background-color: @navbar-inverse-brand-hover-bg;\n    }\n  }\n\n  .navbar-text {\n    color: @navbar-inverse-color;\n  }\n\n  .navbar-nav {\n    > li > a {\n      color: @navbar-inverse-link-color;\n\n      &:hover,\n      &:focus {\n        color: @navbar-inverse-link-hover-color;\n        background-color: @navbar-inverse-link-hover-bg;\n      }\n    }\n    > .active > a {\n      &,\n      &:hover,\n      &:focus {\n        color: @navbar-inverse-link-active-color;\n        background-color: @navbar-inverse-link-active-bg;\n      }\n    }\n    > .disabled > a {\n      &,\n      &:hover,\n      &:focus {\n        color: @navbar-inverse-link-disabled-color;\n        background-color: @navbar-inverse-link-disabled-bg;\n      }\n    }\n  }\n\n  // Darken the responsive nav toggle\n  .navbar-toggle {\n    border-color: @navbar-inverse-toggle-border-color;\n    &:hover,\n    &:focus {\n      background-color: @navbar-inverse-toggle-hover-bg;\n    }\n    .icon-bar {\n      background-color: @navbar-inverse-toggle-icon-bar-bg;\n    }\n  }\n\n  .navbar-collapse,\n  .navbar-form {\n    border-color: darken(@navbar-inverse-bg, 7%);\n  }\n\n  // Dropdowns\n  .navbar-nav {\n    > .open > a {\n      &,\n      &:hover,\n      &:focus {\n        background-color: @navbar-inverse-link-active-bg;\n        color: @navbar-inverse-link-active-color;\n      }\n    }\n\n    @media (max-width: @grid-float-breakpoint-max) {\n      // Dropdowns get custom display\n      .open .dropdown-menu {\n        > .dropdown-header {\n          border-color: @navbar-inverse-border;\n        }\n        .divider {\n          background-color: @navbar-inverse-border;\n        }\n        > li > a {\n          color: @navbar-inverse-link-color;\n          &:hover,\n          &:focus {\n            color: @navbar-inverse-link-hover-color;\n            background-color: @navbar-inverse-link-hover-bg;\n          }\n        }\n        > .active > a {\n          &,\n          &:hover,\n          &:focus {\n            color: @navbar-inverse-link-active-color;\n            background-color: @navbar-inverse-link-active-bg;\n          }\n        }\n        > .disabled > a {\n          &,\n          &:hover,\n          &:focus {\n            color: @navbar-inverse-link-disabled-color;\n            background-color: @navbar-inverse-link-disabled-bg;\n          }\n        }\n      }\n    }\n  }\n\n  .navbar-link {\n    color: @navbar-inverse-link-color;\n    &:hover {\n      color: @navbar-inverse-link-hover-color;\n    }\n  }\n\n}\n","//\n// Utility classes\n// --------------------------------------------------\n\n\n// Floats\n// -------------------------\n\n.clearfix {\n  .clearfix();\n}\n.center-block {\n  .center-block();\n}\n.pull-right {\n  float: right !important;\n}\n.pull-left {\n  float: left !important;\n}\n\n\n// Toggling content\n// -------------------------\n\n// Note: Deprecated .hide in favor of .hidden or .sr-only (as appropriate) in v3.0.1\n.hide {\n  display: none !important;\n}\n.show {\n  display: block !important;\n}\n.invisible {\n  visibility: hidden;\n}\n.text-hide {\n  .text-hide();\n}\n\n\n// Hide from screenreaders and browsers\n//\n// Credit: HTML5 Boilerplate\n\n.hidden {\n  display: none !important;\n  visibility: hidden !important;\n}\n\n\n// For Affix plugin\n// -------------------------\n\n.affix {\n  position: fixed;\n}\n","//\n// Breadcrumbs\n// --------------------------------------------------\n\n\n.breadcrumb {\n  padding: @breadcrumb-padding-vertical @breadcrumb-padding-horizontal;\n  margin-bottom: @line-height-computed;\n  list-style: none;\n  background-color: @breadcrumb-bg;\n  border-radius: @border-radius-base;\n\n  > li {\n    display: inline-block;\n\n    + li:before {\n      content: \"@{breadcrumb-separator}\\00a0\"; // Unicode space added since inline-block means non-collapsing white-space\n      padding: 0 5px;\n      color: @breadcrumb-color;\n    }\n  }\n\n  > .active {\n    color: @breadcrumb-active-color;\n  }\n}\n","//\n// Pagination (multiple pages)\n// --------------------------------------------------\n.pagination {\n  display: inline-block;\n  padding-left: 0;\n  margin: @line-height-computed 0;\n  border-radius: @border-radius-base;\n\n  > li {\n    display: inline; // Remove list-style and block-level defaults\n    > a,\n    > span {\n      position: relative;\n      float: left; // Collapse white-space\n      padding: @padding-base-vertical @padding-base-horizontal;\n      line-height: @line-height-base;\n      text-decoration: none;\n      color: @pagination-color;\n      background-color: @pagination-bg;\n      border: 1px solid @pagination-border;\n      margin-left: -1px;\n    }\n    &:first-child {\n      > a,\n      > span {\n        margin-left: 0;\n        .border-left-radius(@border-radius-base);\n      }\n    }\n    &:last-child {\n      > a,\n      > span {\n        .border-right-radius(@border-radius-base);\n      }\n    }\n  }\n\n  > li > a,\n  > li > span {\n    &:hover,\n    &:focus {\n      color: @pagination-hover-color;\n      background-color: @pagination-hover-bg;\n      border-color: @pagination-hover-border;\n    }\n  }\n\n  > .active > a,\n  > .active > span {\n    &,\n    &:hover,\n    &:focus {\n      z-index: 2;\n      color: @pagination-active-color;\n      background-color: @pagination-active-bg;\n      border-color: @pagination-active-border;\n      cursor: default;\n    }\n  }\n\n  > .disabled {\n    > span,\n    > span:hover,\n    > span:focus,\n    > a,\n    > a:hover,\n    > a:focus {\n      color: @pagination-disabled-color;\n      background-color: @pagination-disabled-bg;\n      border-color: @pagination-disabled-border;\n      cursor: not-allowed;\n    }\n  }\n}\n\n// Sizing\n// --------------------------------------------------\n\n// Large\n.pagination-lg {\n  .pagination-size(@padding-large-vertical; @padding-large-horizontal; @font-size-large; @border-radius-large);\n}\n\n// Small\n.pagination-sm {\n  .pagination-size(@padding-small-vertical; @padding-small-horizontal; @font-size-small; @border-radius-small);\n}\n","//\n// Pager pagination\n// --------------------------------------------------\n\n\n.pager {\n  padding-left: 0;\n  margin: @line-height-computed 0;\n  list-style: none;\n  text-align: center;\n  &:extend(.clearfix all);\n  li {\n    display: inline;\n    > a,\n    > span {\n      display: inline-block;\n      padding: 5px 14px;\n      background-color: @pager-bg;\n      border: 1px solid @pager-border;\n      border-radius: @pager-border-radius;\n    }\n\n    > a:hover,\n    > a:focus {\n      text-decoration: none;\n      background-color: @pager-hover-bg;\n    }\n  }\n\n  .next {\n    > a,\n    > span {\n      float: right;\n    }\n  }\n\n  .previous {\n    > a,\n    > span {\n      float: left;\n    }\n  }\n\n  .disabled {\n    > a,\n    > a:hover,\n    > a:focus,\n    > span {\n      color: @pager-disabled-color;\n      background-color: @pager-bg;\n      cursor: not-allowed;\n    }\n  }\n\n}\n","//\n// Labels\n// --------------------------------------------------\n\n.label {\n  display: inline;\n  padding: .2em .6em .3em;\n  font-size: 75%;\n  font-weight: bold;\n  line-height: 1;\n  color: @label-color;\n  text-align: center;\n  white-space: nowrap;\n  vertical-align: baseline;\n  border-radius: .25em;\n\n  // Add hover effects, but only for links\n  &[href] {\n    &:hover,\n    &:focus {\n      color: @label-link-hover-color;\n      text-decoration: none;\n      cursor: pointer;\n    }\n  }\n\n  // Empty labels collapse automatically (not available in IE8)\n  &:empty {\n    display: none;\n  }\n\n  // Quick fix for labels in buttons\n  .btn & {\n    position: relative;\n    top: -1px;\n  }\n}\n\n// Colors\n// Contextual variations (linked labels get darker on :hover)\n\n.label-default {\n  .label-variant(@label-default-bg);\n}\n\n.label-primary {\n  .label-variant(@label-primary-bg);\n}\n\n.label-success {\n  .label-variant(@label-success-bg);\n}\n\n.label-info {\n  .label-variant(@label-info-bg);\n}\n\n.label-warning {\n  .label-variant(@label-warning-bg);\n}\n\n.label-danger {\n  .label-variant(@label-danger-bg);\n}\n","//\n// Badges\n// --------------------------------------------------\n\n\n// Base classes\n.badge {\n  display: inline-block;\n  min-width: 10px;\n  padding: 3px 7px;\n  font-size: @font-size-small;\n  font-weight: @badge-font-weight;\n  color: @badge-color;\n  line-height: @badge-line-height;\n  vertical-align: baseline;\n  white-space: nowrap;\n  text-align: center;\n  background-color: @badge-bg;\n  border-radius: @badge-border-radius;\n\n  // Empty badges collapse automatically (not available in IE8)\n  &:empty {\n    display: none;\n  }\n\n  // Quick fix for badges in buttons\n  .btn & {\n    position: relative;\n    top: -1px;\n  }\n  .btn-xs & {\n    top: 0;\n    padding: 1px 5px;\n  }\n}\n\n// Hover state, but only for links\na.badge {\n  &:hover,\n  &:focus {\n    color: @badge-link-hover-color;\n    text-decoration: none;\n    cursor: pointer;\n  }\n}\n\n// Account for counters in navs\na.list-group-item.active > .badge,\n.nav-pills > .active > a > .badge {\n  color: @badge-active-color;\n  background-color: @badge-active-bg;\n}\n.nav-pills > li > a > .badge {\n  margin-left: 3px;\n}\n","//\n// Jumbotron\n// --------------------------------------------------\n\n\n.jumbotron {\n  padding: @jumbotron-padding;\n  margin-bottom: @jumbotron-padding;\n  color: @jumbotron-color;\n  background-color: @jumbotron-bg;\n\n  h1,\n  .h1 {\n    color: @jumbotron-heading-color;\n  }\n  p {\n    margin-bottom: (@jumbotron-padding / 2);\n    font-size: @jumbotron-font-size;\n    font-weight: 200;\n  }\n\n  .container & {\n    border-radius: @border-radius-large; // Only round corners at higher resolutions if contained in a container\n  }\n\n  .container {\n    max-width: 100%;\n  }\n\n  @media screen and (min-width: @screen-sm-min) {\n    padding-top:    (@jumbotron-padding * 1.6);\n    padding-bottom: (@jumbotron-padding * 1.6);\n\n    .container & {\n      padding-left:  (@jumbotron-padding * 2);\n      padding-right: (@jumbotron-padding * 2);\n    }\n\n    h1,\n    .h1 {\n      font-size: (@font-size-base * 4.5);\n    }\n  }\n}\n","//\n// Alerts\n// --------------------------------------------------\n\n\n// Base styles\n// -------------------------\n\n.alert {\n  padding: @alert-padding;\n  margin-bottom: @line-height-computed;\n  border: 1px solid transparent;\n  border-radius: @alert-border-radius;\n\n  // Headings for larger alerts\n  h4 {\n    margin-top: 0;\n    // Specified for the h4 to prevent conflicts of changing @headings-color\n    color: inherit;\n  }\n  // Provide class for links that match alerts\n  .alert-link {\n    font-weight: @alert-link-font-weight;\n  }\n\n  // Improve alignment and spacing of inner content\n  > p,\n  > ul {\n    margin-bottom: 0;\n  }\n  > p + p {\n    margin-top: 5px;\n  }\n}\n\n// Dismissable alerts\n//\n// Expand the right padding and account for the close button's positioning.\n\n.alert-dismissable {\n padding-right: (@alert-padding + 20);\n\n  // Adjust close link position\n  .close {\n    position: relative;\n    top: -2px;\n    right: -21px;\n    color: inherit;\n  }\n}\n\n// Alternate styles\n//\n// Generate contextual modifier classes for colorizing the alert.\n\n.alert-success {\n  .alert-variant(@alert-success-bg; @alert-success-border; @alert-success-text);\n}\n.alert-info {\n  .alert-variant(@alert-info-bg; @alert-info-border; @alert-info-text);\n}\n.alert-warning {\n  .alert-variant(@alert-warning-bg; @alert-warning-border; @alert-warning-text);\n}\n.alert-danger {\n  .alert-variant(@alert-danger-bg; @alert-danger-border; @alert-danger-text);\n}\n","//\n// Progress bars\n// --------------------------------------------------\n\n\n// Bar animations\n// -------------------------\n\n// WebKit\n@-webkit-keyframes progress-bar-stripes {\n  from  { background-position: 40px 0; }\n  to    { background-position: 0 0; }\n}\n\n// Spec and IE10+\n@keyframes progress-bar-stripes {\n  from  { background-position: 40px 0; }\n  to    { background-position: 0 0; }\n}\n\n\n\n// Bar itself\n// -------------------------\n\n// Outer container\n.progress {\n  overflow: hidden;\n  height: @line-height-computed;\n  margin-bottom: @line-height-computed;\n  background-color: @progress-bg;\n  border-radius: @border-radius-base;\n  .box-shadow(inset 0 1px 2px rgba(0,0,0,.1));\n}\n\n// Bar of progress\n.progress-bar {\n  float: left;\n  width: 0%;\n  height: 100%;\n  font-size: @font-size-small;\n  line-height: @line-height-computed;\n  color: @progress-bar-color;\n  text-align: center;\n  background-color: @progress-bar-bg;\n  .box-shadow(inset 0 -1px 0 rgba(0,0,0,.15));\n  .transition(width .6s ease);\n}\n\n// Striped bars\n.progress-striped .progress-bar {\n  #gradient > .striped();\n  background-size: 40px 40px;\n}\n\n// Call animation for the active one\n.progress.active .progress-bar {\n  .animation(progress-bar-stripes 2s linear infinite);\n}\n\n\n\n// Variations\n// -------------------------\n\n.progress-bar-success {\n  .progress-bar-variant(@progress-bar-success-bg);\n}\n\n.progress-bar-info {\n  .progress-bar-variant(@progress-bar-info-bg);\n}\n\n.progress-bar-warning {\n  .progress-bar-variant(@progress-bar-warning-bg);\n}\n\n.progress-bar-danger {\n  .progress-bar-variant(@progress-bar-danger-bg);\n}\n","// Media objects\n// Source: http://stubbornella.org/content/?p=497\n// --------------------------------------------------\n\n\n// Common styles\n// -------------------------\n\n// Clear the floats\n.media,\n.media-body {\n  overflow: hidden;\n  zoom: 1;\n}\n\n// Proper spacing between instances of .media\n.media,\n.media .media {\n  margin-top: 15px;\n}\n.media:first-child {\n  margin-top: 0;\n}\n\n// For images and videos, set to block\n.media-object {\n  display: block;\n}\n\n// Reset margins on headings for tighter default spacing\n.media-heading {\n  margin: 0 0 5px;\n}\n\n\n// Media image alignment\n// -------------------------\n\n.media {\n  > .pull-left {\n    margin-right: 10px;\n  }\n  > .pull-right {\n    margin-left: 10px;\n  }\n}\n\n\n// Media list variation\n// -------------------------\n\n// Undo default ul/ol styles\n.media-list {\n  padding-left: 0;\n  list-style: none;\n}\n","//\n// List groups\n// --------------------------------------------------\n\n\n// Base class\n//\n// Easily usable on <ul>, <ol>, or <div>.\n\n.list-group {\n  // No need to set list-style: none; since .list-group-item is block level\n  margin-bottom: 20px;\n  padding-left: 0; // reset padding because ul and ol\n}\n\n\n// Individual list items\n//\n// Use on `li`s or `div`s within the `.list-group` parent.\n\n.list-group-item {\n  position: relative;\n  display: block;\n  padding: 10px 15px;\n  // Place the border on the list items and negative margin up for better styling\n  margin-bottom: -1px;\n  background-color: @list-group-bg;\n  border: 1px solid @list-group-border;\n\n  // Round the first and last items\n  &:first-child {\n    .border-top-radius(@list-group-border-radius);\n  }\n  &:last-child {\n    margin-bottom: 0;\n    .border-bottom-radius(@list-group-border-radius);\n  }\n\n  // Align badges within list items\n  > .badge {\n    float: right;\n  }\n  > .badge + .badge {\n    margin-right: 5px;\n  }\n}\n\n\n// Linked list items\n//\n// Use anchor elements instead of `li`s or `div`s to create linked list items.\n// Includes an extra `.active` modifier class for showing selected items.\n\na.list-group-item {\n  color: @list-group-link-color;\n\n  .list-group-item-heading {\n    color: @list-group-link-heading-color;\n  }\n\n  // Hover state\n  &:hover,\n  &:focus {\n    text-decoration: none;\n    background-color: @list-group-hover-bg;\n  }\n\n  // Active class on item itself, not parent\n  &.active,\n  &.active:hover,\n  &.active:focus {\n    z-index: 2; // Place active items above their siblings for proper border styling\n    color: @list-group-active-color;\n    background-color: @list-group-active-bg;\n    border-color: @list-group-active-border;\n\n    // Force color to inherit for custom content\n    .list-group-item-heading {\n      color: inherit;\n    }\n    .list-group-item-text {\n      color: @list-group-active-text-color;\n    }\n  }\n}\n\n\n// Contextual variants\n//\n// Add modifier classes to change text and background color on individual items.\n// Organizationally, this must come after the `:hover` states.\n\n.list-group-item-variant(success; @state-success-bg; @state-success-text);\n.list-group-item-variant(info; @state-info-bg; @state-info-text);\n.list-group-item-variant(warning; @state-warning-bg; @state-warning-text);\n.list-group-item-variant(danger; @state-danger-bg; @state-danger-text);\n\n\n// Custom content options\n//\n// Extra classes for creating well-formatted content within `.list-group-item`s.\n\n.list-group-item-heading {\n  margin-top: 0;\n  margin-bottom: 5px;\n}\n.list-group-item-text {\n  margin-bottom: 0;\n  line-height: 1.3;\n}\n","//\n// Panels\n// --------------------------------------------------\n\n\n// Base class\n.panel {\n  margin-bottom: @line-height-computed;\n  background-color: @panel-bg;\n  border: 1px solid transparent;\n  border-radius: @panel-border-radius;\n  .box-shadow(0 1px 1px rgba(0,0,0,.05));\n}\n\n// Panel contents\n.panel-body {\n  padding: @panel-body-padding;\n  &:extend(.clearfix all);\n}\n\n// Optional heading\n.panel-heading {\n  padding: 10px 15px;\n  border-bottom: 1px solid transparent;\n  .border-top-radius((@panel-border-radius - 1));\n\n  > .dropdown .dropdown-toggle {\n    color: inherit;\n  }\n}\n\n// Within heading, strip any `h*` tag of its default margins for spacing.\n.panel-title {\n  margin-top: 0;\n  margin-bottom: 0;\n  font-size: ceil((@font-size-base * 1.125));\n  color: inherit;\n\n  > a {\n    color: inherit;\n  }\n}\n\n// Optional footer (stays gray in every modifier class)\n.panel-footer {\n  padding: 10px 15px;\n  background-color: @panel-footer-bg;\n  border-top: 1px solid @panel-inner-border;\n  .border-bottom-radius((@panel-border-radius - 1));\n}\n\n\n// List groups in panels\n//\n// By default, space out list group content from panel headings to account for\n// any kind of custom content between the two.\n\n.panel {\n  > .list-group {\n    margin-bottom: 0;\n\n    .list-group-item {\n      border-width: 1px 0;\n      border-radius: 0;\n    }\n\n    // Add border top radius for first one\n    &:first-child {\n      .list-group-item:first-child {\n        border-top: 0;\n        .border-top-radius((@panel-border-radius - 1));\n      }\n    }\n    // Add border bottom radius for last one\n    &:last-child {\n      .list-group-item:last-child {\n        border-bottom: 0;\n        .border-bottom-radius((@panel-border-radius - 1));\n      }\n    }\n  }\n}\n// Collapse space between when there's no additional content.\n.panel-heading + .list-group {\n  .list-group-item:first-child {\n    border-top-width: 0;\n  }\n}\n\n\n// Tables in panels\n//\n// Place a non-bordered `.table` within a panel (not within a `.panel-body`) and\n// watch it go full width.\n\n.panel {\n  > .table,\n  > .table-responsive > .table {\n    margin-bottom: 0;\n  }\n  // Add border top radius for first one\n  > .table:first-child,\n  > .table-responsive:first-child > .table:first-child {\n    .border-top-radius((@panel-border-radius - 1));\n\n    > thead:first-child,\n    > tbody:first-child {\n      > tr:first-child {\n        td:first-child,\n        th:first-child {\n          border-top-left-radius: (@panel-border-radius - 1);\n        }\n        td:last-child,\n        th:last-child {\n          border-top-right-radius: (@panel-border-radius - 1);\n        }\n      }\n    }\n  }\n  // Add border bottom radius for last one\n  > .table:last-child,\n  > .table-responsive:last-child > .table:last-child {\n    .border-bottom-radius((@panel-border-radius - 1));\n\n    > tbody:last-child,\n    > tfoot:last-child {\n      > tr:last-child {\n        td:first-child,\n        th:first-child {\n          border-bottom-left-radius: (@panel-border-radius - 1);\n        }\n        td:last-child,\n        th:last-child {\n          border-bottom-right-radius: (@panel-border-radius - 1);\n        }\n      }\n    }\n  }\n  > .panel-body + .table,\n  > .panel-body + .table-responsive {\n    border-top: 1px solid @table-border-color;\n  }\n  > .table > tbody:first-child > tr:first-child th,\n  > .table > tbody:first-child > tr:first-child td {\n    border-top: 0;\n  }\n  > .table-bordered,\n  > .table-responsive > .table-bordered {\n    border: 0;\n    > thead,\n    > tbody,\n    > tfoot {\n      > tr {\n        > th:first-child,\n        > td:first-child {\n          border-left: 0;\n        }\n        > th:last-child,\n        > td:last-child {\n          border-right: 0;\n        }\n      }\n    }\n    > thead,\n    > tbody {\n      > tr:first-child {\n        > td,\n        > th {\n          border-bottom: 0;\n        }\n      }\n    }\n    > tbody,\n    > tfoot {\n      > tr:last-child {\n        > td,\n        > th {\n          border-bottom: 0;\n        }\n      }\n    }\n  }\n  > .table-responsive {\n    border: 0;\n    margin-bottom: 0;\n  }\n}\n\n\n// Collapsable panels (aka, accordion)\n//\n// Wrap a series of panels in `.panel-group` to turn them into an accordion with\n// the help of our collapse JavaScript plugin.\n\n.panel-group {\n  margin-bottom: @line-height-computed;\n\n  // Tighten up margin so it's only between panels\n  .panel {\n    margin-bottom: 0;\n    border-radius: @panel-border-radius;\n    overflow: hidden; // crop contents when collapsed\n    + .panel {\n      margin-top: 5px;\n    }\n  }\n\n  .panel-heading {\n    border-bottom: 0;\n    + .panel-collapse .panel-body {\n      border-top: 1px solid @panel-inner-border;\n    }\n  }\n  .panel-footer {\n    border-top: 0;\n    + .panel-collapse .panel-body {\n      border-bottom: 1px solid @panel-inner-border;\n    }\n  }\n}\n\n\n// Contextual variations\n.panel-default {\n  .panel-variant(@panel-default-border; @panel-default-text; @panel-default-heading-bg; @panel-default-border);\n}\n.panel-primary {\n  .panel-variant(@panel-primary-border; @panel-primary-text; @panel-primary-heading-bg; @panel-primary-border);\n}\n.panel-success {\n  .panel-variant(@panel-success-border; @panel-success-text; @panel-success-heading-bg; @panel-success-border);\n}\n.panel-info {\n  .panel-variant(@panel-info-border; @panel-info-text; @panel-info-heading-bg; @panel-info-border);\n}\n.panel-warning {\n  .panel-variant(@panel-warning-border; @panel-warning-text; @panel-warning-heading-bg; @panel-warning-border);\n}\n.panel-danger {\n  .panel-variant(@panel-danger-border; @panel-danger-text; @panel-danger-heading-bg; @panel-danger-border);\n}\n","//\n// Wells\n// --------------------------------------------------\n\n\n// Base class\n.well {\n  min-height: 20px;\n  padding: 19px;\n  margin-bottom: 20px;\n  background-color: @well-bg;\n  border: 1px solid @well-border;\n  border-radius: @border-radius-base;\n  .box-shadow(inset 0 1px 1px rgba(0,0,0,.05));\n  blockquote {\n    border-color: #ddd;\n    border-color: rgba(0,0,0,.15);\n  }\n}\n\n// Sizes\n.well-lg {\n  padding: 24px;\n  border-radius: @border-radius-large;\n}\n.well-sm {\n  padding: 9px;\n  border-radius: @border-radius-small;\n}\n","//\n// Close icons\n// --------------------------------------------------\n\n\n.close {\n  float: right;\n  font-size: (@font-size-base * 1.5);\n  font-weight: @close-font-weight;\n  line-height: 1;\n  color: @close-color;\n  text-shadow: @close-text-shadow;\n  .opacity(.2);\n\n  &:hover,\n  &:focus {\n    color: @close-color;\n    text-decoration: none;\n    cursor: pointer;\n    .opacity(.5);\n  }\n\n  // Additional properties for button version\n  // iOS requires the button element instead of an anchor tag.\n  // If you want the anchor version, it requires `href=\"#\"`.\n  button& {\n    padding: 0;\n    cursor: pointer;\n    background: transparent;\n    border: 0;\n    -webkit-appearance: none;\n  }\n}\n","//\n// Modals\n// --------------------------------------------------\n\n// .modal-open      - body class for killing the scroll\n// .modal           - container to scroll within\n// .modal-dialog    - positioning shell for the actual modal\n// .modal-content   - actual modal w/ bg and corners and shit\n\n// Kill the scroll on the body\n.modal-open {\n  overflow: hidden;\n}\n\n// Container that the modal scrolls within\n.modal {\n  display: none;\n  overflow: auto;\n  overflow-y: scroll;\n  position: fixed;\n  top: 0;\n  right: 0;\n  bottom: 0;\n  left: 0;\n  z-index: @zindex-modal;\n  -webkit-overflow-scrolling: touch;\n\n  // Prevent Chrome on Windows from adding a focus outline. For details, see\n  // https://github.com/twbs/bootstrap/pull/10951.\n  outline: 0;\n\n  // When fading in the modal, animate it to slide down\n  &.fade .modal-dialog {\n    .translate(0, -25%);\n    .transition-transform(~\"0.3s ease-out\");\n  }\n  &.in .modal-dialog { .translate(0, 0)}\n}\n\n// Shell div to position the modal with bottom padding\n.modal-dialog {\n  position: relative;\n  width: auto;\n  margin: 10px;\n}\n\n// Actual modal\n.modal-content {\n  position: relative;\n  background-color: @modal-content-bg;\n  border: 1px solid @modal-content-fallback-border-color; //old browsers fallback (ie8 etc)\n  border: 1px solid @modal-content-border-color;\n  border-radius: @border-radius-large;\n  .box-shadow(0 3px 9px rgba(0,0,0,.5));\n  background-clip: padding-box;\n  // Remove focus outline from opened modal\n  outline: none;\n}\n\n// Modal background\n.modal-backdrop {\n  position: fixed;\n  top: 0;\n  right: 0;\n  bottom: 0;\n  left: 0;\n  z-index: @zindex-modal-background;\n  background-color: @modal-backdrop-bg;\n  // Fade for backdrop\n  &.fade { .opacity(0); }\n  &.in { .opacity(@modal-backdrop-opacity); }\n}\n\n// Modal header\n// Top section of the modal w/ title and dismiss\n.modal-header {\n  padding: @modal-title-padding;\n  border-bottom: 1px solid @modal-header-border-color;\n  min-height: (@modal-title-padding + @modal-title-line-height);\n}\n// Close icon\n.modal-header .close {\n  margin-top: -2px;\n}\n\n// Title text within header\n.modal-title {\n  margin: 0;\n  line-height: @modal-title-line-height;\n}\n\n// Modal body\n// Where all modal content resides (sibling of .modal-header and .modal-footer)\n.modal-body {\n  position: relative;\n  padding: @modal-inner-padding;\n}\n\n// Footer (for actions)\n.modal-footer {\n  margin-top: 15px;\n  padding: (@modal-inner-padding - 1) @modal-inner-padding @modal-inner-padding;\n  text-align: right; // right align buttons\n  border-top: 1px solid @modal-footer-border-color;\n  &:extend(.clearfix all); // clear it in case folks use .pull-* classes on buttons\n\n  // Properly space out buttons\n  .btn + .btn {\n    margin-left: 5px;\n    margin-bottom: 0; // account for input[type=\"submit\"] which gets the bottom margin like all other inputs\n  }\n  // but override that for button groups\n  .btn-group .btn + .btn {\n    margin-left: -1px;\n  }\n  // and override it for block buttons as well\n  .btn-block + .btn-block {\n    margin-left: 0;\n  }\n}\n\n// Scale up the modal\n@media (min-width: @screen-sm-min) {\n  // Automatically set modal's width for larger viewports\n  .modal-dialog {\n    width: @modal-md;\n    margin: 30px auto;\n  }\n  .modal-content {\n    .box-shadow(0 5px 15px rgba(0,0,0,.5));\n  }\n\n  // Modal sizes\n  .modal-sm { width: @modal-sm; }\n}\n\n@media (min-width: @screen-md-min) {\n  .modal-lg { width: @modal-lg; }\n}\n","//\n// Tooltips\n// --------------------------------------------------\n\n\n// Base class\n.tooltip {\n  position: absolute;\n  z-index: @zindex-tooltip;\n  display: block;\n  visibility: visible;\n  font-size: @font-size-small;\n  line-height: 1.4;\n  .opacity(0);\n\n  &.in     { .opacity(@tooltip-opacity); }\n  &.top    { margin-top:  -3px; padding: @tooltip-arrow-width 0; }\n  &.right  { margin-left:  3px; padding: 0 @tooltip-arrow-width; }\n  &.bottom { margin-top:   3px; padding: @tooltip-arrow-width 0; }\n  &.left   { margin-left: -3px; padding: 0 @tooltip-arrow-width; }\n}\n\n// Wrapper for the tooltip content\n.tooltip-inner {\n  max-width: @tooltip-max-width;\n  padding: 3px 8px;\n  color: @tooltip-color;\n  text-align: center;\n  text-decoration: none;\n  background-color: @tooltip-bg;\n  border-radius: @border-radius-base;\n}\n\n// Arrows\n.tooltip-arrow {\n  position: absolute;\n  width: 0;\n  height: 0;\n  border-color: transparent;\n  border-style: solid;\n}\n.tooltip {\n  &.top .tooltip-arrow {\n    bottom: 0;\n    left: 50%;\n    margin-left: -@tooltip-arrow-width;\n    border-width: @tooltip-arrow-width @tooltip-arrow-width 0;\n    border-top-color: @tooltip-arrow-color;\n  }\n  &.top-left .tooltip-arrow {\n    bottom: 0;\n    left: @tooltip-arrow-width;\n    border-width: @tooltip-arrow-width @tooltip-arrow-width 0;\n    border-top-color: @tooltip-arrow-color;\n  }\n  &.top-right .tooltip-arrow {\n    bottom: 0;\n    right: @tooltip-arrow-width;\n    border-width: @tooltip-arrow-width @tooltip-arrow-width 0;\n    border-top-color: @tooltip-arrow-color;\n  }\n  &.right .tooltip-arrow {\n    top: 50%;\n    left: 0;\n    margin-top: -@tooltip-arrow-width;\n    border-width: @tooltip-arrow-width @tooltip-arrow-width @tooltip-arrow-width 0;\n    border-right-color: @tooltip-arrow-color;\n  }\n  &.left .tooltip-arrow {\n    top: 50%;\n    right: 0;\n    margin-top: -@tooltip-arrow-width;\n    border-width: @tooltip-arrow-width 0 @tooltip-arrow-width @tooltip-arrow-width;\n    border-left-color: @tooltip-arrow-color;\n  }\n  &.bottom .tooltip-arrow {\n    top: 0;\n    left: 50%;\n    margin-left: -@tooltip-arrow-width;\n    border-width: 0 @tooltip-arrow-width @tooltip-arrow-width;\n    border-bottom-color: @tooltip-arrow-color;\n  }\n  &.bottom-left .tooltip-arrow {\n    top: 0;\n    left: @tooltip-arrow-width;\n    border-width: 0 @tooltip-arrow-width @tooltip-arrow-width;\n    border-bottom-color: @tooltip-arrow-color;\n  }\n  &.bottom-right .tooltip-arrow {\n    top: 0;\n    right: @tooltip-arrow-width;\n    border-width: 0 @tooltip-arrow-width @tooltip-arrow-width;\n    border-bottom-color: @tooltip-arrow-color;\n  }\n}\n","//\n// Popovers\n// --------------------------------------------------\n\n\n.popover {\n  position: absolute;\n  top: 0;\n  left: 0;\n  z-index: @zindex-popover;\n  display: none;\n  max-width: @popover-max-width;\n  padding: 1px;\n  text-align: left; // Reset given new insertion method\n  background-color: @popover-bg;\n  background-clip: padding-box;\n  border: 1px solid @popover-fallback-border-color;\n  border: 1px solid @popover-border-color;\n  border-radius: @border-radius-large;\n  .box-shadow(0 5px 10px rgba(0,0,0,.2));\n\n  // Overrides for proper insertion\n  white-space: normal;\n\n  // Offset the popover to account for the popover arrow\n  &.top     { margin-top: -@popover-arrow-width; }\n  &.right   { margin-left: @popover-arrow-width; }\n  &.bottom  { margin-top: @popover-arrow-width; }\n  &.left    { margin-left: -@popover-arrow-width; }\n}\n\n.popover-title {\n  margin: 0; // reset heading margin\n  padding: 8px 14px;\n  font-size: @font-size-base;\n  font-weight: normal;\n  line-height: 18px;\n  background-color: @popover-title-bg;\n  border-bottom: 1px solid darken(@popover-title-bg, 5%);\n  border-radius: 5px 5px 0 0;\n}\n\n.popover-content {\n  padding: 9px 14px;\n}\n\n// Arrows\n//\n// .arrow is outer, .arrow:after is inner\n\n.popover > .arrow {\n  &,\n  &:after {\n    position: absolute;\n    display: block;\n    width: 0;\n    height: 0;\n    border-color: transparent;\n    border-style: solid;\n  }\n}\n.popover > .arrow {\n  border-width: @popover-arrow-outer-width;\n}\n.popover > .arrow:after {\n  border-width: @popover-arrow-width;\n  content: \"\";\n}\n\n.popover {\n  &.top > .arrow {\n    left: 50%;\n    margin-left: -@popover-arrow-outer-width;\n    border-bottom-width: 0;\n    border-top-color: @popover-arrow-outer-fallback-color; // IE8 fallback\n    border-top-color: @popover-arrow-outer-color;\n    bottom: -@popover-arrow-outer-width;\n    &:after {\n      content: \" \";\n      bottom: 1px;\n      margin-left: -@popover-arrow-width;\n      border-bottom-width: 0;\n      border-top-color: @popover-arrow-color;\n    }\n  }\n  &.right > .arrow {\n    top: 50%;\n    left: -@popover-arrow-outer-width;\n    margin-top: -@popover-arrow-outer-width;\n    border-left-width: 0;\n    border-right-color: @popover-arrow-outer-fallback-color; // IE8 fallback\n    border-right-color: @popover-arrow-outer-color;\n    &:after {\n      content: \" \";\n      left: 1px;\n      bottom: -@popover-arrow-width;\n      border-left-width: 0;\n      border-right-color: @popover-arrow-color;\n    }\n  }\n  &.bottom > .arrow {\n    left: 50%;\n    margin-left: -@popover-arrow-outer-width;\n    border-top-width: 0;\n    border-bottom-color: @popover-arrow-outer-fallback-color; // IE8 fallback\n    border-bottom-color: @popover-arrow-outer-color;\n    top: -@popover-arrow-outer-width;\n    &:after {\n      content: \" \";\n      top: 1px;\n      margin-left: -@popover-arrow-width;\n      border-top-width: 0;\n      border-bottom-color: @popover-arrow-color;\n    }\n  }\n\n  &.left > .arrow {\n    top: 50%;\n    right: -@popover-arrow-outer-width;\n    margin-top: -@popover-arrow-outer-width;\n    border-right-width: 0;\n    border-left-color: @popover-arrow-outer-fallback-color; // IE8 fallback\n    border-left-color: @popover-arrow-outer-color;\n    &:after {\n      content: \" \";\n      right: 1px;\n      border-right-width: 0;\n      border-left-color: @popover-arrow-color;\n      bottom: -@popover-arrow-width;\n    }\n  }\n\n}\n","//\n// Responsive: Utility classes\n// --------------------------------------------------\n\n\n// IE10 in Windows (Phone) 8\n//\n// Support for responsive views via media queries is kind of borked in IE10, for\n// Surface/desktop in split view and for Windows Phone 8. This particular fix\n// must be accompanied by a snippet of JavaScript to sniff the user agent and\n// apply some conditional CSS to *only* the Surface/desktop Windows 8. Look at\n// our Getting Started page for more information on this bug.\n//\n// For more information, see the following:\n//\n// Issue: https://github.com/twbs/bootstrap/issues/10497\n// Docs: http://getbootstrap.com/getting-started/#browsers\n// Source: http://timkadlec.com/2012/10/ie10-snap-mode-and-responsive-design/\n\n@-ms-viewport {\n  width: device-width;\n}\n\n\n// Visibility utilities\n.visible-xs,\n.visible-sm,\n.visible-md,\n.visible-lg {\n  .responsive-invisibility();\n}\n\n.visible-xs {\n  @media (max-width: @screen-xs-max) {\n    .responsive-visibility();\n  }\n}\n.visible-sm {\n  @media (min-width: @screen-sm-min) and (max-width: @screen-sm-max) {\n    .responsive-visibility();\n  }\n}\n.visible-md {\n  @media (min-width: @screen-md-min) and (max-width: @screen-md-max) {\n    .responsive-visibility();\n  }\n}\n.visible-lg {\n  @media (min-width: @screen-lg-min) {\n    .responsive-visibility();\n  }\n}\n\n.hidden-xs {\n  @media (max-width: @screen-xs-max) {\n    .responsive-invisibility();\n  }\n}\n.hidden-sm {\n  @media (min-width: @screen-sm-min) and (max-width: @screen-sm-max) {\n    .responsive-invisibility();\n  }\n}\n.hidden-md {\n  @media (min-width: @screen-md-min) and (max-width: @screen-md-max) {\n    .responsive-invisibility();\n  }\n}\n.hidden-lg {\n  @media (min-width: @screen-lg-min) {\n    .responsive-invisibility();\n  }\n}\n\n\n// Print utilities\n//\n// Media queries are placed on the inside to be mixin-friendly.\n\n.visible-print {\n  .responsive-invisibility();\n\n  @media print {\n    .responsive-visibility();\n  }\n}\n\n.hidden-print {\n  @media print {\n    .responsive-invisibility();\n  }\n}\n"]}
\ No newline at end of file
diff --git a/pelican-plugins/bootstrap-rst/bootstrap/css/bootstrap.min.css b/pelican-plugins/bootstrap-rst/bootstrap/css/bootstrap.min.css
new file mode 100644
index 0000000000000000000000000000000000000000..679272d25859d55e9931101ef56656e2c50e5ea5
--- /dev/null
+++ b/pelican-plugins/bootstrap-rst/bootstrap/css/bootstrap.min.css
@@ -0,0 +1,7 @@
+/*!
+ * Bootstrap v3.1.1 (http://getbootstrap.com)
+ * Copyright 2011-2014 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ */
+
+/*! normalize.css v3.0.0 | MIT License | git.io/normalize */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background:0 0}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{font-size:2em;margin:.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}@media print{*{text-shadow:none!important;color:#000!important;background:transparent!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100%!important}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}select{background:#fff!important}.navbar{display:none}.table td,.table th{background-color:#fff!important}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table-bordered th,.table-bordered td{border:1px solid #ddd!important}}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}:before,:after{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:62.5%;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}input,button,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#428bca;text-decoration:none}a:hover,a:focus{color:#2a6496;text-decoration:underline}a:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.img-responsive,.thumbnail>img,.thumbnail a>img,.carousel-inner>.item>img,.carousel-inner>.item>a>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;transition:all .2s ease-in-out;display:inline-block;max-width:100%;height:auto}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0,0,0,0);border:0}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small,.h1 small,.h2 small,.h3 small,.h4 small,.h5 small,.h6 small,h1 .small,h2 .small,h3 .small,h4 .small,h5 .small,h6 .small,.h1 .small,.h2 .small,.h3 .small,.h4 .small,.h5 .small,.h6 .small{font-weight:400;line-height:1;color:#999}h1,.h1,h2,.h2,h3,.h3{margin-top:20px;margin-bottom:10px}h1 small,.h1 small,h2 small,.h2 small,h3 small,.h3 small,h1 .small,.h1 .small,h2 .small,.h2 .small,h3 .small,.h3 .small{font-size:65%}h4,.h4,h5,.h5,h6,.h6{margin-top:10px;margin-bottom:10px}h4 small,.h4 small,h5 small,.h5 small,h6 small,.h6 small,h4 .small,.h4 .small,h5 .small,.h5 .small,h6 .small,.h6 .small{font-size:75%}h1,.h1{font-size:36px}h2,.h2{font-size:30px}h3,.h3{font-size:24px}h4,.h4{font-size:18px}h5,.h5{font-size:14px}h6,.h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:200;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}small,.small{font-size:85%}cite{font-style:normal}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-muted{color:#999}.text-primary{color:#428bca}a.text-primary:hover{color:#3071a9}.text-success{color:#3c763d}a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#428bca}a.bg-primary:hover{background-color:#3071a9}.bg-success{background-color:#dff0d8}a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ul,ol{margin-top:0;margin-bottom:10px}ul ul,ol ul,ul ol,ol ol{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none;margin-left:-5px}.list-inline>li{display:inline-block;padding-left:5px;padding-right:5px}dl{margin-top:0;margin-bottom:20px}dt,dd{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;clear:left;text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[title],abbr[data-original-title]{cursor:help;border-bottom:1px dotted #999}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote p:last-child,blockquote ul:last-child,blockquote ol:last-child{margin-bottom:0}blockquote footer,blockquote small,blockquote .small{display:block;font-size:80%;line-height:1.42857143;color:#999}blockquote footer:before,blockquote small:before,blockquote .small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;border-right:5px solid #eee;border-left:0;text-align:right}.blockquote-reverse footer:before,blockquote.pull-right footer:before,.blockquote-reverse small:before,blockquote.pull-right small:before,.blockquote-reverse .small:before,blockquote.pull-right .small:before{content:''}.blockquote-reverse footer:after,blockquote.pull-right footer:after,.blockquote-reverse small:after,blockquote.pull-right small:after,.blockquote-reverse .small:after,blockquote.pull-right .small:after{content:'\00A0 \2014'}blockquote:before,blockquote:after{content:""}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;white-space:nowrap;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;box-shadow:inset 0 -1px 0 rgba(0,0,0,.25)}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;word-break:break-all;word-wrap:break-word;color:#333;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{margin-right:auto;margin-left:auto;padding-left:15px;padding-right:15px}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{margin-right:auto;margin-left:auto;padding-left:15px;padding-right:15px}.row{margin-left:-15px;margin-right:-15px}.col-xs-1,.col-sm-1,.col-md-1,.col-lg-1,.col-xs-2,.col-sm-2,.col-md-2,.col-lg-2,.col-xs-3,.col-sm-3,.col-md-3,.col-lg-3,.col-xs-4,.col-sm-4,.col-md-4,.col-lg-4,.col-xs-5,.col-sm-5,.col-md-5,.col-lg-5,.col-xs-6,.col-sm-6,.col-md-6,.col-lg-6,.col-xs-7,.col-sm-7,.col-md-7,.col-lg-7,.col-xs-8,.col-sm-8,.col-md-8,.col-lg-8,.col-xs-9,.col-sm-9,.col-md-9,.col-lg-9,.col-xs-10,.col-sm-10,.col-md-10,.col-lg-10,.col-xs-11,.col-sm-11,.col-md-11,.col-lg-11,.col-xs-12,.col-sm-12,.col-md-12,.col-lg-12{position:relative;min-height:1px;padding-left:15px;padding-right:15px}.col-xs-1,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9,.col-xs-10,.col-xs-11,.col-xs-12{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:0}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:0}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-10,.col-sm-11,.col-sm-12{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:0}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:0}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-10,.col-md-11,.col-md-12{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:0}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:0}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-10,.col-lg-11,.col-lg-12{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:0}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:0}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{max-width:100%;background-color:transparent}th{text-align:left}.table{width:100%;margin-bottom:20px}.table>thead>tr>th,.table>tbody>tr>th,.table>tfoot>tr>th,.table>thead>tr>td,.table>tbody>tr>td,.table>tfoot>tr>td{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>th,.table>caption+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>td,.table>thead:first-child>tr:first-child>td{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>thead>tr>th,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>tbody>tr>td,.table-condensed>tfoot>tr>td{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>thead>tr>th,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>tbody>tr>td,.table-bordered>tfoot>tr>td{border:1px solid #ddd}.table-bordered>thead>tr>th,.table-bordered>thead>tr>td{border-bottom-width:2px}.table-striped>tbody>tr:nth-child(odd)>td,.table-striped>tbody>tr:nth-child(odd)>th{background-color:#f9f9f9}.table-hover>tbody>tr:hover>td,.table-hover>tbody>tr:hover>th{background-color:#f5f5f5}table col[class*=col-]{position:static;float:none;display:table-column}table td[class*=col-],table th[class*=col-]{position:static;float:none;display:table-cell}.table>thead>tr>td.active,.table>tbody>tr>td.active,.table>tfoot>tr>td.active,.table>thead>tr>th.active,.table>tbody>tr>th.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>tbody>tr.active>td,.table>tfoot>tr.active>td,.table>thead>tr.active>th,.table>tbody>tr.active>th,.table>tfoot>tr.active>th{background-color:#f5f5f5}.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover,.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr.active:hover>th{background-color:#e8e8e8}.table>thead>tr>td.success,.table>tbody>tr>td.success,.table>tfoot>tr>td.success,.table>thead>tr>th.success,.table>tbody>tr>th.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>tbody>tr.success>td,.table>tfoot>tr.success>td,.table>thead>tr.success>th,.table>tbody>tr.success>th,.table>tfoot>tr.success>th{background-color:#dff0d8}.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover,.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr.success:hover>th{background-color:#d0e9c6}.table>thead>tr>td.info,.table>tbody>tr>td.info,.table>tfoot>tr>td.info,.table>thead>tr>th.info,.table>tbody>tr>th.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>tbody>tr.info>td,.table>tfoot>tr.info>td,.table>thead>tr.info>th,.table>tbody>tr.info>th,.table>tfoot>tr.info>th{background-color:#d9edf7}.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover,.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr.info:hover>th{background-color:#c4e3f3}.table>thead>tr>td.warning,.table>tbody>tr>td.warning,.table>tfoot>tr>td.warning,.table>thead>tr>th.warning,.table>tbody>tr>th.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>tbody>tr.warning>td,.table>tfoot>tr.warning>td,.table>thead>tr.warning>th,.table>tbody>tr.warning>th,.table>tfoot>tr.warning>th{background-color:#fcf8e3}.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover,.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr.warning:hover>th{background-color:#faf2cc}.table>thead>tr>td.danger,.table>tbody>tr>td.danger,.table>tfoot>tr>td.danger,.table>thead>tr>th.danger,.table>tbody>tr>th.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>tbody>tr.danger>td,.table>tfoot>tr.danger>td,.table>thead>tr.danger>th,.table>tbody>tr.danger>th,.table>tfoot>tr.danger>th{background-color:#f2dede}.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover,.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr.danger:hover>th{background-color:#ebcccc}@media (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;overflow-x:scroll;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd;-webkit-overflow-scrolling:touch}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>thead>tr>th,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tfoot>tr>td{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>thead>tr>th:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.table-responsive>.table-bordered>thead>tr>th:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>th,.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>td{border-bottom:0}}fieldset{padding:0;margin:0;border:0;min-width:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;margin-bottom:5px;font-weight:700}input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=radio],input[type=checkbox]{margin:4px 0 0;margin-top:1px \9;line-height:normal}input[type=file]{display:block}input[type=range]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type=file]:focus,input[type=radio]:focus,input[type=checkbox]:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{cursor:not-allowed;background-color:#eee;opacity:1}textarea.form-control{height:auto}input[type=search]{-webkit-appearance:none}input[type=date]{line-height:34px}.form-group{margin-bottom:15px}.radio,.checkbox{display:block;min-height:20px;margin-top:10px;margin-bottom:10px;padding-left:20px}.radio label,.checkbox label{display:inline;font-weight:400;cursor:pointer}.radio input[type=radio],.radio-inline input[type=radio],.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox]{float:left;margin-left:-20px}.radio+.radio,.checkbox+.checkbox{margin-top:-5px}.radio-inline,.checkbox-inline{display:inline-block;padding-left:20px;margin-bottom:0;vertical-align:middle;font-weight:400;cursor:pointer}.radio-inline+.radio-inline,.checkbox-inline+.checkbox-inline{margin-top:0;margin-left:10px}input[type=radio][disabled],input[type=checkbox][disabled],.radio[disabled],.radio-inline[disabled],.checkbox[disabled],.checkbox-inline[disabled],fieldset[disabled] input[type=radio],fieldset[disabled] input[type=checkbox],fieldset[disabled] .radio,fieldset[disabled] .radio-inline,fieldset[disabled] .checkbox,fieldset[disabled] .checkbox-inline{cursor:not-allowed}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}textarea.input-sm,select[multiple].input-sm{height:auto}.input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}select.input-lg{height:46px;line-height:46px}textarea.input-lg,select[multiple].input-lg{height:auto}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.has-feedback .form-control-feedback{position:absolute;top:25px;right:0;display:block;width:34px;height:34px;line-height:34px;text-align:center}.has-success .help-block,.has-success .control-label,.has-success .radio,.has-success .checkbox,.has-success .radio-inline,.has-success .checkbox-inline{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;border-color:#3c763d;background-color:#dff0d8}.has-success .form-control-feedback{color:#3c763d}.has-warning .help-block,.has-warning .control-label,.has-warning .radio,.has-warning .checkbox,.has-warning .radio-inline,.has-warning .checkbox-inline{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;border-color:#8a6d3b;background-color:#fcf8e3}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .help-block,.has-error .control-label,.has-error .radio,.has-error .checkbox,.has-error .radio-inline,.has-error .checkbox-inline{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;border-color:#a94442;background-color:#f2dede}.has-error .form-control-feedback{color:#a94442}.form-control-static{margin-bottom:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .radio,.form-inline .checkbox{display:inline-block;margin-top:0;margin-bottom:0;padding-left:0;vertical-align:middle}.form-inline .radio input[type=radio],.form-inline .checkbox input[type=checkbox]{float:none;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .control-label,.form-horizontal .radio,.form-horizontal .checkbox,.form-horizontal .radio-inline,.form-horizontal .checkbox-inline{margin-top:0;margin-bottom:0;padding-top:7px}.form-horizontal .radio,.form-horizontal .checkbox{min-height:27px}.form-horizontal .form-group{margin-left:-15px;margin-right:-15px}.form-horizontal .form-control-static{padding-top:7px}@media (min-width:768px){.form-horizontal .control-label{text-align:right}}.form-horizontal .has-feedback .form-control-feedback{top:0;right:15px}.btn{display:inline-block;margin-bottom:0;font-weight:400;text-align:center;vertical-align:middle;cursor:pointer;background-image:none;border:1px solid transparent;white-space:nowrap;padding:6px 12px;font-size:14px;line-height:1.42857143;border-radius:4px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.btn:focus,.btn:active:focus,.btn.active:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn:hover,.btn:focus{color:#333;text-decoration:none}.btn:active,.btn.active{outline:0;background-image:none;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;pointer-events:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default:hover,.btn-default:focus,.btn-default:active,.btn-default.active,.open .dropdown-toggle.btn-default{color:#333;background-color:#ebebeb;border-color:#adadad}.btn-default:active,.btn-default.active,.open .dropdown-toggle.btn-default{background-image:none}.btn-default.disabled,.btn-default[disabled],fieldset[disabled] .btn-default,.btn-default.disabled:hover,.btn-default[disabled]:hover,fieldset[disabled] .btn-default:hover,.btn-default.disabled:focus,.btn-default[disabled]:focus,fieldset[disabled] .btn-default:focus,.btn-default.disabled:active,.btn-default[disabled]:active,fieldset[disabled] .btn-default:active,.btn-default.disabled.active,.btn-default[disabled].active,fieldset[disabled] .btn-default.active{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#428bca;border-color:#357ebd}.btn-primary:hover,.btn-primary:focus,.btn-primary:active,.btn-primary.active,.open .dropdown-toggle.btn-primary{color:#fff;background-color:#3276b1;border-color:#285e8e}.btn-primary:active,.btn-primary.active,.open .dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled,.btn-primary[disabled],fieldset[disabled] .btn-primary,.btn-primary.disabled:hover,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary:hover,.btn-primary.disabled:focus,.btn-primary[disabled]:focus,fieldset[disabled] .btn-primary:focus,.btn-primary.disabled:active,.btn-primary[disabled]:active,fieldset[disabled] .btn-primary:active,.btn-primary.disabled.active,.btn-primary[disabled].active,fieldset[disabled] .btn-primary.active{background-color:#428bca;border-color:#357ebd}.btn-primary .badge{color:#428bca;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success:hover,.btn-success:focus,.btn-success:active,.btn-success.active,.open .dropdown-toggle.btn-success{color:#fff;background-color:#47a447;border-color:#398439}.btn-success:active,.btn-success.active,.open .dropdown-toggle.btn-success{background-image:none}.btn-success.disabled,.btn-success[disabled],fieldset[disabled] .btn-success,.btn-success.disabled:hover,.btn-success[disabled]:hover,fieldset[disabled] .btn-success:hover,.btn-success.disabled:focus,.btn-success[disabled]:focus,fieldset[disabled] .btn-success:focus,.btn-success.disabled:active,.btn-success[disabled]:active,fieldset[disabled] .btn-success:active,.btn-success.disabled.active,.btn-success[disabled].active,fieldset[disabled] .btn-success.active{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info:hover,.btn-info:focus,.btn-info:active,.btn-info.active,.open .dropdown-toggle.btn-info{color:#fff;background-color:#39b3d7;border-color:#269abc}.btn-info:active,.btn-info.active,.open .dropdown-toggle.btn-info{background-image:none}.btn-info.disabled,.btn-info[disabled],fieldset[disabled] .btn-info,.btn-info.disabled:hover,.btn-info[disabled]:hover,fieldset[disabled] .btn-info:hover,.btn-info.disabled:focus,.btn-info[disabled]:focus,fieldset[disabled] .btn-info:focus,.btn-info.disabled:active,.btn-info[disabled]:active,fieldset[disabled] .btn-info:active,.btn-info.disabled.active,.btn-info[disabled].active,fieldset[disabled] .btn-info.active{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning:hover,.btn-warning:focus,.btn-warning:active,.btn-warning.active,.open .dropdown-toggle.btn-warning{color:#fff;background-color:#ed9c28;border-color:#d58512}.btn-warning:active,.btn-warning.active,.open .dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled,.btn-warning[disabled],fieldset[disabled] .btn-warning,.btn-warning.disabled:hover,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning:hover,.btn-warning.disabled:focus,.btn-warning[disabled]:focus,fieldset[disabled] .btn-warning:focus,.btn-warning.disabled:active,.btn-warning[disabled]:active,fieldset[disabled] .btn-warning:active,.btn-warning.disabled.active,.btn-warning[disabled].active,fieldset[disabled] .btn-warning.active{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger:hover,.btn-danger:focus,.btn-danger:active,.btn-danger.active,.open .dropdown-toggle.btn-danger{color:#fff;background-color:#d2322d;border-color:#ac2925}.btn-danger:active,.btn-danger.active,.open .dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled,.btn-danger[disabled],fieldset[disabled] .btn-danger,.btn-danger.disabled:hover,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger:hover,.btn-danger.disabled:focus,.btn-danger[disabled]:focus,fieldset[disabled] .btn-danger:focus,.btn-danger.disabled:active,.btn-danger[disabled]:active,fieldset[disabled] .btn-danger:active,.btn-danger.disabled.active,.btn-danger[disabled].active,fieldset[disabled] .btn-danger.active{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{color:#428bca;font-weight:400;cursor:pointer;border-radius:0}.btn-link,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:hover,.btn-link:focus,.btn-link:active{border-color:transparent}.btn-link:hover,.btn-link:focus{color:#2a6496;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,fieldset[disabled] .btn-link:hover,.btn-link[disabled]:focus,fieldset[disabled] .btn-link:focus{color:#999;text-decoration:none}.btn-lg,.btn-group-lg>.btn{padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}.btn-sm,.btn-group-sm>.btn{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-xs,.btn-group-xs>.btn{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%;padding-left:0;padding-right:0}.btn-block+.btn-block{margin-top:5px}input[type=submit].btn-block,input[type=reset].btn-block,input[type=button].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition:height .35s ease;transition:height .35s ease}@font-face{font-family:'Glyphicons Halflings';src:url(../fonts/glyphicons-halflings-regular.eot);src:url(../fonts/glyphicons-halflings-regular.eot?#iefix) format('embedded-opentype'),url(../fonts/glyphicons-halflings-regular.woff) format('woff'),url(../fonts/glyphicons-halflings-regular.ttf) format('truetype'),url(../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) format('svg')}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\2a"}.glyphicon-plus:before{content:"\2b"}.glyphicon-euro:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px solid;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;list-style:none;font-size:14px;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175);background-clip:padding-box}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{text-decoration:none;color:#262626;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{color:#fff;text-decoration:none;outline:0;background-color:#428bca}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{color:#999}.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{text-decoration:none;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);cursor:not-allowed}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{left:auto;right:0}.dropdown-menu-left{left:0;right:auto}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#999}.dropdown-backdrop{position:fixed;left:0;right:0;bottom:0;top:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{border-top:0;border-bottom:4px solid;content:""}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:1px}@media (min-width:768px){.navbar-right .dropdown-menu{left:auto;right:0}.navbar-right .dropdown-menu-left{left:0;right:auto}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group>.btn,.btn-group-vertical>.btn{position:relative;float:left}.btn-group>.btn:hover,.btn-group-vertical>.btn:hover,.btn-group>.btn:focus,.btn-group-vertical>.btn:focus,.btn-group>.btn:active,.btn-group-vertical>.btn:active,.btn-group>.btn.active,.btn-group-vertical>.btn.active{z-index:2}.btn-group>.btn:focus,.btn-group-vertical>.btn:focus{outline:0}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-top-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child>.btn:last-child,.btn-group>.btn-group:first-child>.dropdown-toggle{border-bottom-right-radius:0;border-top-right-radius:0}.btn-group>.btn-group:last-child>.btn:first-child{border-bottom-left-radius:0;border-top-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-left:8px;padding-right:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-left:12px;padding-right:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-bottom-left-radius:4px;border-top-right-radius:0;border-top-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-right-radius:0;border-top-left-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{float:none;display:table-cell;width:1%}.btn-group-justified>.btn-group .btn{width:100%}[data-toggle=buttons]>.btn>input[type=radio],[data-toggle=buttons]>.btn>input[type=checkbox]{display:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*=col-]{float:none;padding-left:0;padding-right:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn,select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn,select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn{height:auto}.input-group-addon,.input-group-btn,.input-group .form-control{display:table-cell}.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child),.input-group .form-control:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:400;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type=radio],.input-group-addon input[type=checkbox]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group-btn:last-child>.btn-group:not(:last-child)>.btn{border-bottom-right-radius:0;border-top-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:first-child>.btn-group:not(:first-child)>.btn{border-bottom-left-radius:0;border-top-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:hover,.input-group-btn>.btn:focus,.input-group-btn>.btn:active{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{margin-left:-1px}.nav{margin-bottom:0;padding-left:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:hover,.nav>li>a:focus{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#999}.nav>li.disabled>a:hover,.nav>li.disabled>a:focus{color:#999;text-decoration:none;background-color:transparent;cursor:not-allowed}.nav .open>a,.nav .open>a:hover,.nav .open>a:focus{background-color:#eee;border-color:#428bca}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:hover,.nav-tabs>li.active>a:focus{color:#555;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent;cursor:default}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{text-align:center;margin-bottom:5px}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:hover,.nav-pills>li.active>a:focus{color:#fff;background-color:#428bca}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{text-align:center;margin-bottom:5px}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-right-radius:0;border-top-left-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{max-height:340px;overflow-x:visible;padding-right:15px;padding-left:15px;border-top:1px solid transparent;box-shadow:inset 0 1px 0 rgba(255,255,255,.1);-webkit-overflow-scrolling:touch}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;box-shadow:none}.navbar-collapse.collapse{display:block!important;height:auto!important;padding-bottom:0;overflow:visible!important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{padding-left:0;padding-right:0}}.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030}@media (min-width:768px){.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;padding:15px;font-size:18px;line-height:20px;height:50px}.navbar-brand:hover,.navbar-brand:focus{text-decoration:none}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;margin-right:15px;padding:9px 10px;margin-top:8px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;box-shadow:none}.navbar-nav .open .dropdown-menu>li>a,.navbar-nav .open .dropdown-menu .dropdown-header{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:hover,.navbar-nav .open .dropdown-menu>li>a:focus{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}.navbar-nav.navbar-right:last-child{margin-right:-15px}}@media (min-width:768px){.navbar-left{float:left!important}.navbar-right{float:right!important}}.navbar-form{margin-left:-15px;margin-right:-15px;padding:10px 15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);margin-top:8px;margin-bottom:8px}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .radio,.navbar-form .checkbox{display:inline-block;margin-top:0;margin-bottom:0;padding-left:0;vertical-align:middle}.navbar-form .radio input[type=radio],.navbar-form .checkbox input[type=checkbox]{float:none;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}}@media (min-width:768px){.navbar-form{width:auto;border:0;margin-left:0;margin-right:0;padding-top:0;padding-bottom:0;-webkit-box-shadow:none;box-shadow:none}.navbar-form.navbar-right:last-child{margin-right:-15px}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-right-radius:0;border-top-left-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-left:15px;margin-right:15px}.navbar-text.navbar-right:last-child{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:hover,.navbar-default .navbar-brand:focus{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:hover,.navbar-default .navbar-nav>li>a:focus{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:hover,.navbar-default .navbar-nav>.active>a:focus{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:hover,.navbar-default .navbar-nav>.disabled>a:focus{color:#ccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:hover,.navbar-default .navbar-toggle:focus{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:hover,.navbar-default .navbar-nav>.open>a:focus{background-color:#e7e7e7;color:#555}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#ccc;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#999}.navbar-inverse .navbar-brand:hover,.navbar-inverse .navbar-brand:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#999}.navbar-inverse .navbar-nav>li>a{color:#999}.navbar-inverse .navbar-nav>li>a:hover,.navbar-inverse .navbar-nav>li>a:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:hover,.navbar-inverse .navbar-nav>.active>a:focus{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:hover,.navbar-inverse .navbar-nav>.disabled>a:focus{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:hover,.navbar-inverse .navbar-toggle:focus{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:hover,.navbar-inverse .navbar-nav>.open>a:focus{background-color:#080808;color:#fff}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#999}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#999}.navbar-inverse .navbar-link:hover{color:#fff}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{content:"/\00a0";padding:0 5px;color:#ccc}.breadcrumb>.active{color:#999}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;line-height:1.42857143;text-decoration:none;color:#428bca;background-color:#fff;border:1px solid #ddd;margin-left:-1px}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-bottom-left-radius:4px;border-top-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-bottom-right-radius:4px;border-top-right-radius:4px}.pagination>li>a:hover,.pagination>li>span:hover,.pagination>li>a:focus,.pagination>li>span:focus{color:#2a6496;background-color:#eee;border-color:#ddd}.pagination>.active>a,.pagination>.active>span,.pagination>.active>a:hover,.pagination>.active>span:hover,.pagination>.active>a:focus,.pagination>.active>span:focus{z-index:2;color:#fff;background-color:#428bca;border-color:#428bca;cursor:default}.pagination>.disabled>span,.pagination>.disabled>span:hover,.pagination>.disabled>span:focus,.pagination>.disabled>a,.pagination>.disabled>a:hover,.pagination>.disabled>a:focus{color:#999;background-color:#fff;border-color:#ddd;cursor:not-allowed}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-bottom-left-radius:6px;border-top-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-bottom-right-radius:6px;border-top-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-bottom-left-radius:3px;border-top-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-bottom-right-radius:3px;border-top-right-radius:3px}.pager{padding-left:0;margin:20px 0;list-style:none;text-align:center}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:hover,.pager li>a:focus{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>a:focus,.pager .disabled>span{color:#999;background-color:#fff;cursor:not-allowed}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}.label[href]:hover,.label[href]:focus{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#999}.label-default[href]:hover,.label-default[href]:focus{background-color:gray}.label-primary{background-color:#428bca}.label-primary[href]:hover,.label-primary[href]:focus{background-color:#3071a9}.label-success{background-color:#5cb85c}.label-success[href]:hover,.label-success[href]:focus{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:hover,.label-info[href]:focus{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:hover,.label-warning[href]:focus{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:hover,.label-danger[href]:focus{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:700;color:#fff;line-height:1;vertical-align:baseline;white-space:nowrap;text-align:center;background-color:#999;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-xs .badge{top:0;padding:1px 5px}a.badge:hover,a.badge:focus{color:#fff;text-decoration:none;cursor:pointer}a.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#428bca;background-color:#fff}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding:30px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron h1,.jumbotron .h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.container .jumbotron{border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron{padding-left:60px;padding-right:60px}.jumbotron h1,.jumbotron .h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.thumbnail>img,.thumbnail a>img{margin-left:auto;margin-right:auto}a.thumbnail:hover,a.thumbnail:focus,a.thumbnail.active{border-color:#428bca}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:700}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable{padding-right:35px}.alert-dismissable .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{background-color:#dff0d8;border-color:#d6e9c6;color:#3c763d}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{background-color:#d9edf7;border-color:#bce8f1;color:#31708f}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{background-color:#fcf8e3;border-color:#faebcc;color:#8a6d3b}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{background-color:#f2dede;border-color:#ebccd1;color:#a94442}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{overflow:hidden;height:20px;margin-bottom:20px;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.progress-bar{float:left;width:0;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#428bca;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);-webkit-transition:width .6s ease;transition:width .6s ease}.progress-striped .progress-bar{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:40px 40px}.progress.active .progress-bar{-webkit-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.media,.media-body{overflow:hidden;zoom:1}.media,.media .media{margin-top:15px}.media:first-child{margin-top:0}.media-object{display:block}.media-heading{margin:0 0 5px}.media>.pull-left{margin-right:10px}.media>.pull-right{margin-left:10px}.media-list{padding-left:0;list-style:none}.list-group{margin-bottom:20px;padding-left:0}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-right-radius:4px;border-top-left-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}a.list-group-item{color:#555}a.list-group-item .list-group-item-heading{color:#333}a.list-group-item:hover,a.list-group-item:focus{text-decoration:none;background-color:#f5f5f5}a.list-group-item.active,a.list-group-item.active:hover,a.list-group-item.active:focus{z-index:2;color:#fff;background-color:#428bca;border-color:#428bca}a.list-group-item.active .list-group-item-heading,a.list-group-item.active:hover .list-group-item-heading,a.list-group-item.active:focus .list-group-item-heading{color:inherit}a.list-group-item.active .list-group-item-text,a.list-group-item.active:hover .list-group-item-text,a.list-group-item.active:focus .list-group-item-text{color:#e1edf7}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:hover,a.list-group-item-success:focus{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:hover,a.list-group-item-success.active:focus{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:hover,a.list-group-item-info:focus{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:hover,a.list-group-item-info.active:focus{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:hover,a.list-group-item-warning:focus{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:hover,a.list-group-item-warning.active:focus{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:hover,a.list-group-item-danger:focus{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:hover,a.list-group-item-danger.active:focus{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.05);box-shadow:0 1px 1px rgba(0,0,0,.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-right-radius:3px;border-top-left-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group{margin-bottom:0}.panel>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-right-radius:3px;border-top-left-radius:3px}.panel>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.panel>.table,.panel>.table-responsive>.table{margin-bottom:0}.panel>.table:first-child,.panel>.table-responsive:first-child>.table:first-child{border-top-right-radius:3px;border-top-left-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table:last-child,.panel>.table-responsive:last-child>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child th,.panel>.table>tbody:first-child>tr:first-child td{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{border:0;margin-bottom:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px;overflow:hidden}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse .panel-body{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse .panel-body{border-top-color:#ddd}.panel-default>.panel-footer+.panel-collapse .panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#428bca}.panel-primary>.panel-heading{color:#fff;background-color:#428bca;border-color:#428bca}.panel-primary>.panel-heading+.panel-collapse .panel-body{border-top-color:#428bca}.panel-primary>.panel-footer+.panel-collapse .panel-body{border-bottom-color:#428bca}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse .panel-body{border-top-color:#d6e9c6}.panel-success>.panel-footer+.panel-collapse .panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse .panel-body{border-top-color:#bce8f1}.panel-info>.panel-footer+.panel-collapse .panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse .panel-body{border-top-color:#faebcc}.panel-warning>.panel-footer+.panel-collapse .panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse .panel-body{border-top-color:#ebccd1}.panel-danger>.panel-footer+.panel-collapse .panel-body{border-bottom-color:#ebccd1}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.2;filter:alpha(opacity=20)}.close:hover,.close:focus{color:#000;text-decoration:none;cursor:pointer;opacity:.5;filter:alpha(opacity=50)}button.close{padding:0;cursor:pointer;background:0 0;border:0;-webkit-appearance:none}.modal-open{overflow:hidden}.modal{display:none;overflow:auto;overflow-y:scroll;position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transform:translate(0,-25%);-ms-transform:translate(0,-25%);transform:translate(0,-25%);-webkit-transition:-webkit-transform .3s ease-out;-moz-transition:-moz-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out}.modal.in .modal-dialog{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);transform:translate(0,0)}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;border:1px solid #999;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 3px 9px rgba(0,0,0,.5);box-shadow:0 3px 9px rgba(0,0,0,.5);background-clip:padding-box;outline:0}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{opacity:0;filter:alpha(opacity=0)}.modal-backdrop.in{opacity:.5;filter:alpha(opacity=50)}.modal-header{padding:15px;border-bottom:1px solid #e5e5e5;min-height:16.42857143px}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:20px}.modal-footer{margin-top:15px;padding:19px 20px 20px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-left:5px;margin-bottom:0}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,.5);box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1030;display:block;visibility:visible;font-size:12px;line-height:1.4;opacity:0;filter:alpha(opacity=0)}.tooltip.in{opacity:.9;filter:alpha(opacity=90)}.tooltip.top{margin-top:-3px;padding:5px 0}.tooltip.right{margin-left:3px;padding:0 5px}.tooltip.bottom{margin-top:3px;padding:5px 0}.tooltip.left{margin-left:-3px;padding:0 5px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;text-decoration:none;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{bottom:0;left:5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;right:5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;left:5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;right:5px;border-width:0 5px 5px;border-bottom-color:#000}.popover{position:absolute;top:0;left:0;z-index:1010;display:none;max-width:276px;padding:1px;text-align:left;background-color:#fff;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);white-space:normal}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{margin:0;padding:8px 14px;font-size:14px;font-weight:400;line-height:18px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:11px}.popover>.arrow:after{border-width:10px;content:""}.popover.top>.arrow{left:50%;margin-left:-11px;border-bottom-width:0;border-top-color:#999;border-top-color:rgba(0,0,0,.25);bottom:-11px}.popover.top>.arrow:after{content:" ";bottom:1px;margin-left:-10px;border-bottom-width:0;border-top-color:#fff}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-left-width:0;border-right-color:#999;border-right-color:rgba(0,0,0,.25)}.popover.right>.arrow:after{content:" ";left:1px;bottom:-10px;border-left-width:0;border-right-color:#fff}.popover.bottom>.arrow{left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,.25);top:-11px}.popover.bottom>.arrow:after{content:" ";top:1px;margin-left:-10px;border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,.25)}.popover.left>.arrow:after{content:" ";right:1px;border-right-width:0;border-left-color:#fff;bottom:-10px}.carousel{position:relative}.carousel-inner{position:relative;overflow:hidden;width:100%}.carousel-inner>.item{display:none;position:relative;-webkit-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>img,.carousel-inner>.item>a>img{line-height:1}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;left:0;bottom:0;width:15%;opacity:.5;filter:alpha(opacity=50);font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-control.left{background-image:-webkit-linear-gradient(left,color-stop(rgba(0,0,0,.5) 0),color-stop(rgba(0,0,0,.0001) 100%));background-image:linear-gradient(to right,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1)}.carousel-control.right{left:auto;right:0;background-image:-webkit-linear-gradient(left,color-stop(rgba(0,0,0,.0001) 0),color-stop(rgba(0,0,0,.5) 100%));background-image:linear-gradient(to right,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1)}.carousel-control:hover,.carousel-control:focus{outline:0;color:#fff;text-decoration:none;opacity:.9;filter:alpha(opacity=90)}.carousel-control .icon-prev,.carousel-control .icon-next,.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right{position:absolute;top:50%;z-index:5;display:inline-block}.carousel-control .icon-prev,.carousel-control .glyphicon-chevron-left{left:50%}.carousel-control .icon-next,.carousel-control .glyphicon-chevron-right{right:50%}.carousel-control .icon-prev,.carousel-control .icon-next{width:20px;height:20px;margin-top:-10px;margin-left:-10px;font-family:serif}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;margin-left:-30%;padding-left:0;list-style:none;text-align:center}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;border:1px solid #fff;border-radius:10px;cursor:pointer;background-color:#000 \9;background-color:rgba(0,0,0,0)}.carousel-indicators .active{margin:0;width:12px;height:12px;background-color:#fff}.carousel-caption{position:absolute;left:15%;right:15%;bottom:20px;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-prev,.carousel-control .icon-next{width:30px;height:30px;margin-top:-15px;margin-left:-15px;font-size:30px}.carousel-caption{left:20%;right:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.clearfix:before,.clearfix:after,.container:before,.container:after,.container-fluid:before,.container-fluid:after,.row:before,.row:after,.form-horizontal .form-group:before,.form-horizontal .form-group:after,.btn-toolbar:before,.btn-toolbar:after,.btn-group-vertical>.btn-group:before,.btn-group-vertical>.btn-group:after,.nav:before,.nav:after,.navbar:before,.navbar:after,.navbar-header:before,.navbar-header:after,.navbar-collapse:before,.navbar-collapse:after,.pager:before,.pager:after,.panel-body:before,.panel-body:after,.modal-footer:before,.modal-footer:after{content:" ";display:table}.clearfix:after,.container:after,.container-fluid:after,.row:after,.form-horizontal .form-group:after,.btn-toolbar:after,.btn-group-vertical>.btn-group:after,.nav:after,.navbar:after,.navbar-header:after,.navbar-collapse:after,.pager:after,.panel-body:after,.modal-footer:after{clear:both}.center-block{display:block;margin-left:auto;margin-right:auto}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none!important;visibility:hidden!important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-xs,.visible-sm,.visible-md,.visible-lg{display:none!important}@media (max-width:767px){.visible-xs{display:block!important}table.visible-xs{display:table}tr.visible-xs{display:table-row!important}th.visible-xs,td.visible-xs{display:table-cell!important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block!important}table.visible-sm{display:table}tr.visible-sm{display:table-row!important}th.visible-sm,td.visible-sm{display:table-cell!important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block!important}table.visible-md{display:table}tr.visible-md{display:table-row!important}th.visible-md,td.visible-md{display:table-cell!important}}@media (min-width:1200px){.visible-lg{display:block!important}table.visible-lg{display:table}tr.visible-lg{display:table-row!important}th.visible-lg,td.visible-lg{display:table-cell!important}}@media (max-width:767px){.hidden-xs{display:none!important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none!important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none!important}}@media (min-width:1200px){.hidden-lg{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:block!important}table.visible-print{display:table}tr.visible-print{display:table-row!important}th.visible-print,td.visible-print{display:table-cell!important}}@media print{.hidden-print{display:none!important}}
\ No newline at end of file
diff --git a/pelican-plugins/bootstrap-rst/bootstrap/fonts/glyphicons-halflings-regular.eot b/pelican-plugins/bootstrap-rst/bootstrap/fonts/glyphicons-halflings-regular.eot
new file mode 100644
index 0000000000000000000000000000000000000000..4a4ca865d67e86f961bc6e2ef00bffa4e34bb9ed
Binary files /dev/null and b/pelican-plugins/bootstrap-rst/bootstrap/fonts/glyphicons-halflings-regular.eot differ
diff --git a/pelican-plugins/bootstrap-rst/bootstrap/fonts/glyphicons-halflings-regular.svg b/pelican-plugins/bootstrap-rst/bootstrap/fonts/glyphicons-halflings-regular.svg
new file mode 100644
index 0000000000000000000000000000000000000000..e3e2dc739dd851f2d7d291be032e30b909e3e95f
--- /dev/null
+++ b/pelican-plugins/bootstrap-rst/bootstrap/fonts/glyphicons-halflings-regular.svg
@@ -0,0 +1,229 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
+<svg xmlns="http://www.w3.org/2000/svg">
+<metadata></metadata>
+<defs>
+<font id="glyphicons_halflingsregular" horiz-adv-x="1200" >
+<font-face units-per-em="1200" ascent="960" descent="-240" />
+<missing-glyph horiz-adv-x="500" />
+<glyph />
+<glyph />
+<glyph unicode="&#xd;" />
+<glyph unicode=" " />
+<glyph unicode="*" d="M100 500v200h259l-183 183l141 141l183 -183v259h200v-259l183 183l141 -141l-183 -183h259v-200h-259l183 -183l-141 -141l-183 183v-259h-200v259l-183 -183l-141 141l183 183h-259z" />
+<glyph unicode="+" d="M0 400v300h400v400h300v-400h400v-300h-400v-400h-300v400h-400z" />
+<glyph unicode="&#xa0;" />
+<glyph unicode="&#x2000;" horiz-adv-x="652" />
+<glyph unicode="&#x2001;" horiz-adv-x="1304" />
+<glyph unicode="&#x2002;" horiz-adv-x="652" />
+<glyph unicode="&#x2003;" horiz-adv-x="1304" />
+<glyph unicode="&#x2004;" horiz-adv-x="434" />
+<glyph unicode="&#x2005;" horiz-adv-x="326" />
+<glyph unicode="&#x2006;" horiz-adv-x="217" />
+<glyph unicode="&#x2007;" horiz-adv-x="217" />
+<glyph unicode="&#x2008;" horiz-adv-x="163" />
+<glyph unicode="&#x2009;" horiz-adv-x="260" />
+<glyph unicode="&#x200a;" horiz-adv-x="72" />
+<glyph unicode="&#x202f;" horiz-adv-x="260" />
+<glyph unicode="&#x205f;" horiz-adv-x="326" />
+<glyph unicode="&#x20ac;" d="M100 500l100 100h113q0 47 5 100h-218l100 100h135q37 167 112 257q117 141 297 141q242 0 354 -189q60 -103 66 -209h-181q0 55 -25.5 99t-63.5 68t-75 36.5t-67 12.5q-24 0 -52.5 -10t-62.5 -32t-65.5 -67t-50.5 -107h379l-100 -100h-300q-6 -46 -6 -100h406l-100 -100 h-300q9 -74 33 -132t52.5 -91t62 -54.5t59 -29t46.5 -7.5q29 0 66 13t75 37t63.5 67.5t25.5 96.5h174q-31 -172 -128 -278q-107 -117 -274 -117q-205 0 -324 158q-36 46 -69 131.5t-45 205.5h-217z" />
+<glyph unicode="&#x2212;" d="M200 400h900v300h-900v-300z" />
+<glyph unicode="&#x25fc;" horiz-adv-x="500" d="M0 0z" />
+<glyph unicode="&#x2601;" d="M-14 494q0 -80 56.5 -137t135.5 -57h750q120 0 205 86.5t85 207.5t-85 207t-205 86q-46 0 -90 -14q-44 97 -134.5 156.5t-200.5 59.5q-152 0 -260 -107.5t-108 -260.5q0 -25 2 -37q-66 -14 -108.5 -67.5t-42.5 -122.5z" />
+<glyph unicode="&#x2709;" d="M0 100l400 400l200 -200l200 200l400 -400h-1200zM0 300v600l300 -300zM0 1100l600 -603l600 603h-1200zM900 600l300 300v-600z" />
+<glyph unicode="&#x270f;" d="M-13 -13l333 112l-223 223zM187 403l214 -214l614 614l-214 214zM887 1103l214 -214l99 92q13 13 13 32.5t-13 33.5l-153 153q-15 13 -33 13t-33 -13z" />
+<glyph unicode="&#xe001;" d="M0 1200h1200l-500 -550v-550h300v-100h-800v100h300v550z" />
+<glyph unicode="&#xe002;" d="M14 84q18 -55 86 -75.5t147 5.5q65 21 109 69t44 90v606l600 155v-521q-64 16 -138 -7q-79 -26 -122.5 -83t-25.5 -111q18 -55 86 -75.5t147 4.5q70 23 111.5 63.5t41.5 95.5v881q0 10 -7 15.5t-17 2.5l-752 -193q-10 -3 -17 -12.5t-7 -19.5v-689q-64 17 -138 -7 q-79 -25 -122.5 -82t-25.5 -112z" />
+<glyph unicode="&#xe003;" d="M23 693q0 200 142 342t342 142t342 -142t142 -342q0 -142 -78 -261l300 -300q7 -8 7 -18t-7 -18l-109 -109q-8 -7 -18 -7t-18 7l-300 300q-119 -78 -261 -78q-200 0 -342 142t-142 342zM176 693q0 -136 97 -233t234 -97t233.5 96.5t96.5 233.5t-96.5 233.5t-233.5 96.5 t-234 -97t-97 -233z" />
+<glyph unicode="&#xe005;" d="M100 784q0 64 28 123t73 100.5t104.5 64t119 20.5t120 -38.5t104.5 -104.5q48 69 109.5 105t121.5 38t118.5 -20.5t102.5 -64t71 -100.5t27 -123q0 -57 -33.5 -117.5t-94 -124.5t-126.5 -127.5t-150 -152.5t-146 -174q-62 85 -145.5 174t-149.5 152.5t-126.5 127.5 t-94 124.5t-33.5 117.5z" />
+<glyph unicode="&#xe006;" d="M-72 800h479l146 400h2l146 -400h472l-382 -278l145 -449l-384 275l-382 -275l146 447zM168 71l2 1z" />
+<glyph unicode="&#xe007;" d="M-72 800h479l146 400h2l146 -400h472l-382 -278l145 -449l-384 275l-382 -275l146 447zM168 71l2 1zM237 700l196 -142l-73 -226l192 140l195 -141l-74 229l193 140h-235l-77 211l-78 -211h-239z" />
+<glyph unicode="&#xe008;" d="M0 0v143l400 257v100q-37 0 -68.5 74.5t-31.5 125.5v200q0 124 88 212t212 88t212 -88t88 -212v-200q0 -51 -31.5 -125.5t-68.5 -74.5v-100l400 -257v-143h-1200z" />
+<glyph unicode="&#xe009;" d="M0 0v1100h1200v-1100h-1200zM100 100h100v100h-100v-100zM100 300h100v100h-100v-100zM100 500h100v100h-100v-100zM100 700h100v100h-100v-100zM100 900h100v100h-100v-100zM300 100h600v400h-600v-400zM300 600h600v400h-600v-400zM1000 100h100v100h-100v-100z M1000 300h100v100h-100v-100zM1000 500h100v100h-100v-100zM1000 700h100v100h-100v-100zM1000 900h100v100h-100v-100z" />
+<glyph unicode="&#xe010;" d="M0 50v400q0 21 14.5 35.5t35.5 14.5h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5zM0 650v400q0 21 14.5 35.5t35.5 14.5h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400 q-21 0 -35.5 14.5t-14.5 35.5zM600 50v400q0 21 14.5 35.5t35.5 14.5h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5zM600 650v400q0 21 14.5 35.5t35.5 14.5h400q21 0 35.5 -14.5t14.5 -35.5v-400 q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5z" />
+<glyph unicode="&#xe011;" d="M0 50v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM0 450v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200 q-21 0 -35.5 14.5t-14.5 35.5zM0 850v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM400 50v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5 t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM400 450v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM400 850v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5 v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM800 50v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM800 450v200q0 21 14.5 35.5t35.5 14.5h200 q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM800 850v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5z" />
+<glyph unicode="&#xe012;" d="M0 50v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM0 450q0 -21 14.5 -35.5t35.5 -14.5h200q21 0 35.5 14.5t14.5 35.5v200q0 21 -14.5 35.5t-35.5 14.5h-200q-21 0 -35.5 -14.5 t-14.5 -35.5v-200zM0 850v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM400 50v200q0 21 14.5 35.5t35.5 14.5h700q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5 t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5zM400 450v200q0 21 14.5 35.5t35.5 14.5h700q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5zM400 850v200q0 21 14.5 35.5t35.5 14.5h700q21 0 35.5 -14.5t14.5 -35.5 v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5z" />
+<glyph unicode="&#xe013;" d="M29 454l419 -420l818 820l-212 212l-607 -607l-206 207z" />
+<glyph unicode="&#xe014;" d="M106 318l282 282l-282 282l212 212l282 -282l282 282l212 -212l-282 -282l282 -282l-212 -212l-282 282l-282 -282z" />
+<glyph unicode="&#xe015;" d="M23 693q0 200 142 342t342 142t342 -142t142 -342q0 -142 -78 -261l300 -300q7 -8 7 -18t-7 -18l-109 -109q-8 -7 -18 -7t-18 7l-300 300q-119 -78 -261 -78q-200 0 -342 142t-142 342zM176 693q0 -136 97 -233t234 -97t233.5 96.5t96.5 233.5t-96.5 233.5t-233.5 96.5 t-234 -97t-97 -233zM300 600v200h100v100h200v-100h100v-200h-100v-100h-200v100h-100z" />
+<glyph unicode="&#xe016;" d="M23 694q0 200 142 342t342 142t342 -142t142 -342q0 -141 -78 -262l300 -299q7 -7 7 -18t-7 -18l-109 -109q-8 -8 -18 -8t-18 8l-300 300q-119 -78 -261 -78q-200 0 -342 142t-142 342zM176 694q0 -136 97 -233t234 -97t233.5 97t96.5 233t-96.5 233t-233.5 97t-234 -97 t-97 -233zM300 601h400v200h-400v-200z" />
+<glyph unicode="&#xe017;" d="M23 600q0 183 105 331t272 210v-166q-103 -55 -165 -155t-62 -220q0 -177 125 -302t302 -125t302 125t125 302q0 120 -62 220t-165 155v166q167 -62 272 -210t105 -331q0 -118 -45.5 -224.5t-123 -184t-184 -123t-224.5 -45.5t-224.5 45.5t-184 123t-123 184t-45.5 224.5 zM500 750q0 -21 14.5 -35.5t35.5 -14.5h100q21 0 35.5 14.5t14.5 35.5v400q0 21 -14.5 35.5t-35.5 14.5h-100q-21 0 -35.5 -14.5t-14.5 -35.5v-400z" />
+<glyph unicode="&#xe018;" d="M100 1h200v300h-200v-300zM400 1v500h200v-500h-200zM700 1v800h200v-800h-200zM1000 1v1200h200v-1200h-200z" />
+<glyph unicode="&#xe019;" d="M26 601q0 -33 6 -74l151 -38l2 -6q14 -49 38 -93l3 -5l-80 -134q45 -59 105 -105l133 81l5 -3q45 -26 94 -39l5 -2l38 -151q40 -5 74 -5q27 0 74 5l38 151l6 2q46 13 93 39l5 3l134 -81q56 44 104 105l-80 134l3 5q24 44 39 93l1 6l152 38q5 40 5 74q0 28 -5 73l-152 38 l-1 6q-16 51 -39 93l-3 5l80 134q-44 58 -104 105l-134 -81l-5 3q-45 25 -93 39l-6 1l-38 152q-40 5 -74 5q-27 0 -74 -5l-38 -152l-5 -1q-50 -14 -94 -39l-5 -3l-133 81q-59 -47 -105 -105l80 -134l-3 -5q-25 -47 -38 -93l-2 -6l-151 -38q-6 -48 -6 -73zM385 601 q0 88 63 151t152 63t152 -63t63 -151q0 -89 -63 -152t-152 -63t-152 63t-63 152z" />
+<glyph unicode="&#xe020;" d="M100 1025v50q0 10 7.5 17.5t17.5 7.5h275v100q0 41 29.5 70.5t70.5 29.5h300q41 0 70.5 -29.5t29.5 -70.5v-100h275q10 0 17.5 -7.5t7.5 -17.5v-50q0 -11 -7 -18t-18 -7h-1050q-11 0 -18 7t-7 18zM200 100v800h900v-800q0 -41 -29.5 -71t-70.5 -30h-700q-41 0 -70.5 30 t-29.5 71zM300 100h100v700h-100v-700zM500 100h100v700h-100v-700zM500 1100h300v100h-300v-100zM700 100h100v700h-100v-700zM900 100h100v700h-100v-700z" />
+<glyph unicode="&#xe021;" d="M1 601l656 644l644 -644h-200v-600h-300v400h-300v-400h-300v600h-200z" />
+<glyph unicode="&#xe022;" d="M100 25v1150q0 11 7 18t18 7h475v-500h400v-675q0 -11 -7 -18t-18 -7h-850q-11 0 -18 7t-7 18zM700 800v300l300 -300h-300z" />
+<glyph unicode="&#xe023;" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -171 121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM500 500v400h100 v-300h200v-100h-300z" />
+<glyph unicode="&#xe024;" d="M-100 0l431 1200h209l-21 -300h162l-20 300h208l431 -1200h-538l-41 400h-242l-40 -400h-539zM488 500h224l-27 300h-170z" />
+<glyph unicode="&#xe025;" d="M0 0v400h490l-290 300h200v500h300v-500h200l-290 -300h490v-400h-1100zM813 200h175v100h-175v-100z" />
+<glyph unicode="&#xe026;" d="M1 600q0 122 47.5 233t127.5 191t191 127.5t233 47.5t233 -47.5t191 -127.5t127.5 -191t47.5 -233t-47.5 -233t-127.5 -191t-191 -127.5t-233 -47.5t-233 47.5t-191 127.5t-127.5 191t-47.5 233zM188 600q0 -170 121 -291t291 -121t291 121t121 291t-121 291t-291 121 t-291 -121t-121 -291zM350 600h150v300h200v-300h150l-250 -300z" />
+<glyph unicode="&#xe027;" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -171 121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM350 600l250 300 l250 -300h-150v-300h-200v300h-150z" />
+<glyph unicode="&#xe028;" d="M0 25v475l200 700h800l199 -700l1 -475q0 -11 -7 -18t-18 -7h-1150q-11 0 -18 7t-7 18zM200 500h200l50 -200h300l50 200h200l-97 500h-606z" />
+<glyph unicode="&#xe029;" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -172 121.5 -293t292.5 -121t292.5 121t121.5 293q0 171 -121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM500 397v401 l297 -200z" />
+<glyph unicode="&#xe030;" d="M23 600q0 -118 45.5 -224.5t123 -184t184 -123t224.5 -45.5t224.5 45.5t184 123t123 184t45.5 224.5h-150q0 -177 -125 -302t-302 -125t-302 125t-125 302t125 302t302 125q136 0 246 -81l-146 -146h400v400l-145 -145q-157 122 -355 122q-118 0 -224.5 -45.5t-184 -123 t-123 -184t-45.5 -224.5z" />
+<glyph unicode="&#xe031;" d="M23 600q0 118 45.5 224.5t123 184t184 123t224.5 45.5q198 0 355 -122l145 145v-400h-400l147 147q-112 80 -247 80q-177 0 -302 -125t-125 -302h-150zM100 0v400h400l-147 -147q112 -80 247 -80q177 0 302 125t125 302h150q0 -118 -45.5 -224.5t-123 -184t-184 -123 t-224.5 -45.5q-198 0 -355 122z" />
+<glyph unicode="&#xe032;" d="M100 0h1100v1200h-1100v-1200zM200 100v900h900v-900h-900zM300 200v100h100v-100h-100zM300 400v100h100v-100h-100zM300 600v100h100v-100h-100zM300 800v100h100v-100h-100zM500 200h500v100h-500v-100zM500 400v100h500v-100h-500zM500 600v100h500v-100h-500z M500 800v100h500v-100h-500z" />
+<glyph unicode="&#xe033;" d="M0 100v600q0 41 29.5 70.5t70.5 29.5h100v200q0 82 59 141t141 59h300q82 0 141 -59t59 -141v-200h100q41 0 70.5 -29.5t29.5 -70.5v-600q0 -41 -29.5 -70.5t-70.5 -29.5h-900q-41 0 -70.5 29.5t-29.5 70.5zM400 800h300v150q0 21 -14.5 35.5t-35.5 14.5h-200 q-21 0 -35.5 -14.5t-14.5 -35.5v-150z" />
+<glyph unicode="&#xe034;" d="M100 0v1100h100v-1100h-100zM300 400q60 60 127.5 84t127.5 17.5t122 -23t119 -30t110 -11t103 42t91 120.5v500q-40 -81 -101.5 -115.5t-127.5 -29.5t-138 25t-139.5 40t-125.5 25t-103 -29.5t-65 -115.5v-500z" />
+<glyph unicode="&#xe035;" d="M0 275q0 -11 7 -18t18 -7h50q11 0 18 7t7 18v300q0 127 70.5 231.5t184.5 161.5t245 57t245 -57t184.5 -161.5t70.5 -231.5v-300q0 -11 7 -18t18 -7h50q11 0 18 7t7 18v300q0 116 -49.5 227t-131 192.5t-192.5 131t-227 49.5t-227 -49.5t-192.5 -131t-131 -192.5 t-49.5 -227v-300zM200 20v460q0 8 6 14t14 6h160q8 0 14 -6t6 -14v-460q0 -8 -6 -14t-14 -6h-160q-8 0 -14 6t-6 14zM800 20v460q0 8 6 14t14 6h160q8 0 14 -6t6 -14v-460q0 -8 -6 -14t-14 -6h-160q-8 0 -14 6t-6 14z" />
+<glyph unicode="&#xe036;" d="M0 400h300l300 -200v800l-300 -200h-300v-400zM688 459l141 141l-141 141l71 71l141 -141l141 141l71 -71l-141 -141l141 -141l-71 -71l-141 141l-141 -141z" />
+<glyph unicode="&#xe037;" d="M0 400h300l300 -200v800l-300 -200h-300v-400zM700 857l69 53q111 -135 111 -310q0 -169 -106 -302l-67 54q86 110 86 248q0 146 -93 257z" />
+<glyph unicode="&#xe038;" d="M0 401v400h300l300 200v-800l-300 200h-300zM702 858l69 53q111 -135 111 -310q0 -170 -106 -303l-67 55q86 110 86 248q0 145 -93 257zM889 951l7 -8q123 -151 123 -344q0 -189 -119 -339l-7 -8l81 -66l6 8q142 178 142 405q0 230 -144 408l-6 8z" />
+<glyph unicode="&#xe039;" d="M0 0h500v500h-200v100h-100v-100h-200v-500zM0 600h100v100h400v100h100v100h-100v300h-500v-600zM100 100v300h300v-300h-300zM100 800v300h300v-300h-300zM200 200v100h100v-100h-100zM200 900h100v100h-100v-100zM500 500v100h300v-300h200v-100h-100v-100h-200v100 h-100v100h100v200h-200zM600 0v100h100v-100h-100zM600 1000h100v-300h200v-300h300v200h-200v100h200v500h-600v-200zM800 800v300h300v-300h-300zM900 0v100h300v-100h-300zM900 900v100h100v-100h-100zM1100 200v100h100v-100h-100z" />
+<glyph unicode="&#xe040;" d="M0 200h100v1000h-100v-1000zM100 0v100h300v-100h-300zM200 200v1000h100v-1000h-100zM500 0v91h100v-91h-100zM500 200v1000h200v-1000h-200zM700 0v91h100v-91h-100zM800 200v1000h100v-1000h-100zM900 0v91h200v-91h-200zM1000 200v1000h200v-1000h-200z" />
+<glyph unicode="&#xe041;" d="M0 700l1 475q0 10 7.5 17.5t17.5 7.5h474l700 -700l-500 -500zM148 953q0 -42 29 -71q30 -30 71.5 -30t71.5 30q29 29 29 71t-29 71q-30 30 -71.5 30t-71.5 -30q-29 -29 -29 -71z" />
+<glyph unicode="&#xe042;" d="M1 700l1 475q0 11 7 18t18 7h474l700 -700l-500 -500zM148 953q0 -42 30 -71q29 -30 71 -30t71 30q30 29 30 71t-30 71q-29 30 -71 30t-71 -30q-30 -29 -30 -71zM701 1200h100l700 -700l-500 -500l-50 50l450 450z" />
+<glyph unicode="&#xe043;" d="M100 0v1025l175 175h925v-1000l-100 -100v1000h-750l-100 -100h750v-1000h-900z" />
+<glyph unicode="&#xe044;" d="M200 0l450 444l450 -443v1150q0 20 -14.5 35t-35.5 15h-800q-21 0 -35.5 -15t-14.5 -35v-1151z" />
+<glyph unicode="&#xe045;" d="M0 100v700h200l100 -200h600l100 200h200v-700h-200v200h-800v-200h-200zM253 829l40 -124h592l62 124l-94 346q-2 11 -10 18t-18 7h-450q-10 0 -18 -7t-10 -18zM281 24l38 152q2 10 11.5 17t19.5 7h500q10 0 19.5 -7t11.5 -17l38 -152q2 -10 -3.5 -17t-15.5 -7h-600 q-10 0 -15.5 7t-3.5 17z" />
+<glyph unicode="&#xe046;" d="M0 200q0 -41 29.5 -70.5t70.5 -29.5h1000q41 0 70.5 29.5t29.5 70.5v600q0 41 -29.5 70.5t-70.5 29.5h-150q-4 8 -11.5 21.5t-33 48t-53 61t-69 48t-83.5 21.5h-200q-41 0 -82 -20.5t-70 -50t-52 -59t-34 -50.5l-12 -20h-150q-41 0 -70.5 -29.5t-29.5 -70.5v-600z M356 500q0 100 72 172t172 72t172 -72t72 -172t-72 -172t-172 -72t-172 72t-72 172zM494 500q0 -44 31 -75t75 -31t75 31t31 75t-31 75t-75 31t-75 -31t-31 -75zM900 700v100h100v-100h-100z" />
+<glyph unicode="&#xe047;" d="M53 0h365v66q-41 0 -72 11t-49 38t1 71l92 234h391l82 -222q16 -45 -5.5 -88.5t-74.5 -43.5v-66h417v66q-34 1 -74 43q-18 19 -33 42t-21 37l-6 13l-385 998h-93l-399 -1006q-24 -48 -52 -75q-12 -12 -33 -25t-36 -20l-15 -7v-66zM416 521l178 457l46 -140l116 -317h-340 z" />
+<glyph unicode="&#xe048;" d="M100 0v89q41 7 70.5 32.5t29.5 65.5v827q0 28 -1 39.5t-5.5 26t-15.5 21t-29 14t-49 14.5v71l471 -1q120 0 213 -88t93 -228q0 -55 -11.5 -101.5t-28 -74t-33.5 -47.5t-28 -28l-12 -7q8 -3 21.5 -9t48 -31.5t60.5 -58t47.5 -91.5t21.5 -129q0 -84 -59 -156.5t-142 -111 t-162 -38.5h-500zM400 200h161q89 0 153 48.5t64 132.5q0 90 -62.5 154.5t-156.5 64.5h-159v-400zM400 700h139q76 0 130 61.5t54 138.5q0 82 -84 130.5t-239 48.5v-379z" />
+<glyph unicode="&#xe049;" d="M200 0v57q77 7 134.5 40.5t65.5 80.5l173 849q10 56 -10 74t-91 37q-6 1 -10.5 2.5t-9.5 2.5v57h425l2 -57q-33 -8 -62 -25.5t-46 -37t-29.5 -38t-17.5 -30.5l-5 -12l-128 -825q-10 -52 14 -82t95 -36v-57h-500z" />
+<glyph unicode="&#xe050;" d="M-75 200h75v800h-75l125 167l125 -167h-75v-800h75l-125 -167zM300 900v300h150h700h150v-300h-50q0 29 -8 48.5t-18.5 30t-33.5 15t-39.5 5.5t-50.5 1h-200v-850l100 -50v-100h-400v100l100 50v850h-200q-34 0 -50.5 -1t-40 -5.5t-33.5 -15t-18.5 -30t-8.5 -48.5h-49z " />
+<glyph unicode="&#xe051;" d="M33 51l167 125v-75h800v75l167 -125l-167 -125v75h-800v-75zM100 901v300h150h700h150v-300h-50q0 29 -8 48.5t-18 30t-33.5 15t-40 5.5t-50.5 1h-200v-650l100 -50v-100h-400v100l100 50v650h-200q-34 0 -50.5 -1t-39.5 -5.5t-33.5 -15t-18.5 -30t-8 -48.5h-50z" />
+<glyph unicode="&#xe052;" d="M0 50q0 -20 14.5 -35t35.5 -15h1100q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-1100q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM0 350q0 -20 14.5 -35t35.5 -15h800q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-800q-21 0 -35.5 -14.5t-14.5 -35.5 v-100zM0 650q0 -20 14.5 -35t35.5 -15h1000q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-1000q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM0 950q0 -20 14.5 -35t35.5 -15h600q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-600q-21 0 -35.5 -14.5 t-14.5 -35.5v-100z" />
+<glyph unicode="&#xe053;" d="M0 50q0 -20 14.5 -35t35.5 -15h1100q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-1100q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM0 650q0 -20 14.5 -35t35.5 -15h1100q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-1100q-21 0 -35.5 -14.5t-14.5 -35.5 v-100zM200 350q0 -20 14.5 -35t35.5 -15h700q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-700q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM200 950q0 -20 14.5 -35t35.5 -15h700q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-700q-21 0 -35.5 -14.5 t-14.5 -35.5v-100z" />
+<glyph unicode="&#xe054;" d="M0 50v100q0 21 14.5 35.5t35.5 14.5h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-1100q-21 0 -35.5 15t-14.5 35zM100 650v100q0 21 14.5 35.5t35.5 14.5h1000q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-1000q-21 0 -35.5 15 t-14.5 35zM300 350v100q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-800q-21 0 -35.5 15t-14.5 35zM500 950v100q0 21 14.5 35.5t35.5 14.5h600q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-600 q-21 0 -35.5 15t-14.5 35z" />
+<glyph unicode="&#xe055;" d="M0 50v100q0 21 14.5 35.5t35.5 14.5h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-1100q-21 0 -35.5 15t-14.5 35zM0 350v100q0 21 14.5 35.5t35.5 14.5h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-1100q-21 0 -35.5 15 t-14.5 35zM0 650v100q0 21 14.5 35.5t35.5 14.5h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-1100q-21 0 -35.5 15t-14.5 35zM0 950v100q0 21 14.5 35.5t35.5 14.5h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-1100 q-21 0 -35.5 15t-14.5 35z" />
+<glyph unicode="&#xe056;" d="M0 50v100q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-100q-21 0 -35.5 15t-14.5 35zM0 350v100q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-100q-21 0 -35.5 15 t-14.5 35zM0 650v100q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-100q-21 0 -35.5 15t-14.5 35zM0 950v100q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-100q-21 0 -35.5 15 t-14.5 35zM300 50v100q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-800q-21 0 -35.5 15t-14.5 35zM300 350v100q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-800 q-21 0 -35.5 15t-14.5 35zM300 650v100q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-800q-21 0 -35.5 15t-14.5 35zM300 950v100q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15 h-800q-21 0 -35.5 15t-14.5 35z" />
+<glyph unicode="&#xe057;" d="M-101 500v100h201v75l166 -125l-166 -125v75h-201zM300 0h100v1100h-100v-1100zM500 50q0 -20 14.5 -35t35.5 -15h600q20 0 35 15t15 35v100q0 21 -15 35.5t-35 14.5h-600q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM500 350q0 -20 14.5 -35t35.5 -15h300q20 0 35 15t15 35 v100q0 21 -15 35.5t-35 14.5h-300q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM500 650q0 -20 14.5 -35t35.5 -15h500q20 0 35 15t15 35v100q0 21 -15 35.5t-35 14.5h-500q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM500 950q0 -20 14.5 -35t35.5 -15h100q20 0 35 15t15 35v100 q0 21 -15 35.5t-35 14.5h-100q-21 0 -35.5 -14.5t-14.5 -35.5v-100z" />
+<glyph unicode="&#xe058;" d="M1 50q0 -20 14.5 -35t35.5 -15h600q20 0 35 15t15 35v100q0 21 -15 35.5t-35 14.5h-600q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM1 350q0 -20 14.5 -35t35.5 -15h300q20 0 35 15t15 35v100q0 21 -15 35.5t-35 14.5h-300q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM1 650 q0 -20 14.5 -35t35.5 -15h500q20 0 35 15t15 35v100q0 21 -15 35.5t-35 14.5h-500q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM1 950q0 -20 14.5 -35t35.5 -15h100q20 0 35 15t15 35v100q0 21 -15 35.5t-35 14.5h-100q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM801 0v1100h100v-1100 h-100zM934 550l167 -125v75h200v100h-200v75z" />
+<glyph unicode="&#xe059;" d="M0 275v650q0 31 22 53t53 22h750q31 0 53 -22t22 -53v-650q0 -31 -22 -53t-53 -22h-750q-31 0 -53 22t-22 53zM900 600l300 300v-600z" />
+<glyph unicode="&#xe060;" d="M0 44v1012q0 18 13 31t31 13h1112q19 0 31.5 -13t12.5 -31v-1012q0 -18 -12.5 -31t-31.5 -13h-1112q-18 0 -31 13t-13 31zM100 263l247 182l298 -131l-74 156l293 318l236 -288v500h-1000v-737zM208 750q0 56 39 95t95 39t95 -39t39 -95t-39 -95t-95 -39t-95 39t-39 95z " />
+<glyph unicode="&#xe062;" d="M148 745q0 124 60.5 231.5t165 172t226.5 64.5q123 0 227 -63t164.5 -169.5t60.5 -229.5t-73 -272q-73 -114 -166.5 -237t-150.5 -189l-57 -66q-10 9 -27 26t-66.5 70.5t-96 109t-104 135.5t-100.5 155q-63 139 -63 262zM342 772q0 -107 75.5 -182.5t181.5 -75.5 q107 0 182.5 75.5t75.5 182.5t-75.5 182t-182.5 75t-182 -75.5t-75 -181.5z" />
+<glyph unicode="&#xe063;" d="M1 600q0 122 47.5 233t127.5 191t191 127.5t233 47.5t233 -47.5t191 -127.5t127.5 -191t47.5 -233t-47.5 -233t-127.5 -191t-191 -127.5t-233 -47.5t-233 47.5t-191 127.5t-127.5 191t-47.5 233zM173 600q0 -177 125.5 -302t301.5 -125v854q-176 0 -301.5 -125 t-125.5 -302z" />
+<glyph unicode="&#xe064;" d="M117 406q0 94 34 186t88.5 172.5t112 159t115 177t87.5 194.5q21 -71 57.5 -142.5t76 -130.5t83 -118.5t82 -117t70 -116t50 -125.5t18.5 -136q0 -89 -39 -165.5t-102 -126.5t-140 -79.5t-156 -33.5q-114 6 -211.5 53t-161.5 139t-64 210zM243 414q14 -82 59.5 -136 t136.5 -80l16 98q-7 6 -18 17t-34 48t-33 77q-15 73 -14 143.5t10 122.5l9 51q-92 -110 -119.5 -185t-12.5 -156z" />
+<glyph unicode="&#xe065;" d="M0 400v300q0 165 117.5 282.5t282.5 117.5q366 -6 397 -14l-186 -186h-311q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v125l200 200v-225q0 -165 -117.5 -282.5t-282.5 -117.5h-300q-165 0 -282.5 117.5 t-117.5 282.5zM436 341l161 50l412 412l-114 113l-405 -405zM995 1015l113 -113l113 113l-21 85l-92 28z" />
+<glyph unicode="&#xe066;" d="M0 400v300q0 165 117.5 282.5t282.5 117.5h261l2 -80q-133 -32 -218 -120h-145q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5l200 153v-53q0 -165 -117.5 -282.5t-282.5 -117.5h-300q-165 0 -282.5 117.5t-117.5 282.5 zM423 524q30 38 81.5 64t103 35.5t99 14t77.5 3.5l29 -1v-209l360 324l-359 318v-216q-7 0 -19 -1t-48 -8t-69.5 -18.5t-76.5 -37t-76.5 -59t-62 -88t-39.5 -121.5z" />
+<glyph unicode="&#xe067;" d="M0 400v300q0 165 117.5 282.5t282.5 117.5h300q61 0 127 -23l-178 -177h-349q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v69l200 200v-169q0 -165 -117.5 -282.5t-282.5 -117.5h-300q-165 0 -282.5 117.5 t-117.5 282.5zM342 632l283 -284l567 567l-137 137l-430 -431l-146 147z" />
+<glyph unicode="&#xe068;" d="M0 603l300 296v-198h200v200h-200l300 300l295 -300h-195v-200h200v198l300 -296l-300 -300v198h-200v-200h195l-295 -300l-300 300h200v200h-200v-198z" />
+<glyph unicode="&#xe069;" d="M200 50v1000q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-437l500 487v-1100l-500 488v-438q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5z" />
+<glyph unicode="&#xe070;" d="M0 50v1000q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-437l500 487v-487l500 487v-1100l-500 488v-488l-500 488v-438q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5z" />
+<glyph unicode="&#xe071;" d="M136 550l564 550v-487l500 487v-1100l-500 488v-488z" />
+<glyph unicode="&#xe072;" d="M200 0l900 550l-900 550v-1100z" />
+<glyph unicode="&#xe073;" d="M200 150q0 -21 14.5 -35.5t35.5 -14.5h200q21 0 35.5 14.5t14.5 35.5v800q0 21 -14.5 35.5t-35.5 14.5h-200q-21 0 -35.5 -14.5t-14.5 -35.5v-800zM600 150q0 -21 14.5 -35.5t35.5 -14.5h200q21 0 35.5 14.5t14.5 35.5v800q0 21 -14.5 35.5t-35.5 14.5h-200 q-21 0 -35.5 -14.5t-14.5 -35.5v-800z" />
+<glyph unicode="&#xe074;" d="M200 150q0 -20 14.5 -35t35.5 -15h800q21 0 35.5 15t14.5 35v800q0 21 -14.5 35.5t-35.5 14.5h-800q-21 0 -35.5 -14.5t-14.5 -35.5v-800z" />
+<glyph unicode="&#xe075;" d="M0 0v1100l500 -487v487l564 -550l-564 -550v488z" />
+<glyph unicode="&#xe076;" d="M0 0v1100l500 -487v487l500 -487v437q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-1000q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v438l-500 -488v488z" />
+<glyph unicode="&#xe077;" d="M300 0v1100l500 -487v437q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-1000q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v438z" />
+<glyph unicode="&#xe078;" d="M100 250v100q0 21 14.5 35.5t35.5 14.5h1000q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1000q-21 0 -35.5 14.5t-14.5 35.5zM100 500h1100l-550 564z" />
+<glyph unicode="&#xe079;" d="M185 599l592 -592l240 240l-353 353l353 353l-240 240z" />
+<glyph unicode="&#xe080;" d="M272 194l353 353l-353 353l241 240l572 -571l21 -22l-1 -1v-1l-592 -591z" />
+<glyph unicode="&#xe081;" d="M3 600q0 162 80 299.5t217.5 217.5t299.5 80t299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5zM300 500h200v-200h200v200h200v200h-200v200h-200v-200h-200v-200z" />
+<glyph unicode="&#xe082;" d="M3 600q0 162 80 299.5t217.5 217.5t299.5 80t299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5zM300 500h600v200h-600v-200z" />
+<glyph unicode="&#xe083;" d="M3 600q0 162 80 299.5t217.5 217.5t299.5 80t299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5zM246 459l213 -213l141 142l141 -142l213 213l-142 141l142 141l-213 212l-141 -141l-141 142l-212 -213l141 -141 z" />
+<glyph unicode="&#xe084;" d="M3 600q0 162 80 299.5t217.5 217.5t299.5 80t299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5zM270 551l276 -277l411 411l-175 174l-236 -236l-102 102z" />
+<glyph unicode="&#xe085;" d="M3 600q0 162 80 299.5t217.5 217.5t299.5 80t299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5zM364 700h143q4 0 11.5 -1t11 -1t6.5 3t3 9t1 11t3.5 8.5t3.5 6t5.5 4t6.5 2.5t9 1.5t9 0.5h11.5h12.5 q19 0 30 -10t11 -26q0 -22 -4 -28t-27 -22q-5 -1 -12.5 -3t-27 -13.5t-34 -27t-26.5 -46t-11 -68.5h200q5 3 14 8t31.5 25.5t39.5 45.5t31 69t14 94q0 51 -17.5 89t-42 58t-58.5 32t-58.5 15t-51.5 3q-50 0 -90.5 -12t-75 -38.5t-53.5 -74.5t-19 -114zM500 300h200v100h-200 v-100z" />
+<glyph unicode="&#xe086;" d="M3 600q0 162 80 299.5t217.5 217.5t299.5 80t299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5zM400 300h400v100h-100v300h-300v-100h100v-200h-100v-100zM500 800h200v100h-200v-100z" />
+<glyph unicode="&#xe087;" d="M0 500v200h195q31 125 98.5 199.5t206.5 100.5v200h200v-200q54 -20 113 -60t112.5 -105.5t71.5 -134.5h203v-200h-203q-25 -102 -116.5 -186t-180.5 -117v-197h-200v197q-140 27 -208 102.5t-98 200.5h-194zM290 500q24 -73 79.5 -127.5t130.5 -78.5v206h200v-206 q149 48 201 206h-201v200h200q-25 74 -75.5 127t-124.5 77v-204h-200v203q-75 -23 -130 -77t-79 -126h209v-200h-210z" />
+<glyph unicode="&#xe088;" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -171 121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM356 465l135 135 l-135 135l109 109l135 -135l135 135l109 -109l-135 -135l135 -135l-109 -109l-135 135l-135 -135z" />
+<glyph unicode="&#xe089;" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -171 121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM322 537l141 141 l87 -87l204 205l142 -142l-346 -345z" />
+<glyph unicode="&#xe090;" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -115 62 -215l568 567q-100 62 -216 62q-171 0 -292.5 -121.5t-121.5 -292.5zM391 245q97 -59 209 -59q171 0 292.5 121.5t121.5 292.5 q0 112 -59 209z" />
+<glyph unicode="&#xe091;" d="M0 547l600 453v-300h600v-300h-600v-301z" />
+<glyph unicode="&#xe092;" d="M0 400v300h600v300l600 -453l-600 -448v301h-600z" />
+<glyph unicode="&#xe093;" d="M204 600l450 600l444 -600h-298v-600h-300v600h-296z" />
+<glyph unicode="&#xe094;" d="M104 600h296v600h300v-600h298l-449 -600z" />
+<glyph unicode="&#xe095;" d="M0 200q6 132 41 238.5t103.5 193t184 138t271.5 59.5v271l600 -453l-600 -448v301q-95 -2 -183 -20t-170 -52t-147 -92.5t-100 -135.5z" />
+<glyph unicode="&#xe096;" d="M0 0v400l129 -129l294 294l142 -142l-294 -294l129 -129h-400zM635 777l142 -142l294 294l129 -129v400h-400l129 -129z" />
+<glyph unicode="&#xe097;" d="M34 176l295 295l-129 129h400v-400l-129 130l-295 -295zM600 600v400l129 -129l295 295l142 -141l-295 -295l129 -130h-400z" />
+<glyph unicode="&#xe101;" d="M23 600q0 118 45.5 224.5t123 184t184 123t224.5 45.5t224.5 -45.5t184 -123t123 -184t45.5 -224.5t-45.5 -224.5t-123 -184t-184 -123t-224.5 -45.5t-224.5 45.5t-184 123t-123 184t-45.5 224.5zM456 851l58 -302q4 -20 21.5 -34.5t37.5 -14.5h54q20 0 37.5 14.5 t21.5 34.5l58 302q4 20 -8 34.5t-32 14.5h-207q-21 0 -33 -14.5t-8 -34.5zM500 300h200v100h-200v-100z" />
+<glyph unicode="&#xe102;" d="M0 800h100v-200h400v300h200v-300h400v200h100v100h-111q1 1 1 6.5t-1.5 15t-3.5 17.5l-34 172q-11 39 -41.5 63t-69.5 24q-32 0 -61 -17l-239 -144q-22 -13 -40 -35q-19 24 -40 36l-238 144q-33 18 -62 18q-39 0 -69.5 -23t-40.5 -61l-35 -177q-2 -8 -3 -18t-1 -15v-6 h-111v-100zM100 0h400v400h-400v-400zM200 900q-3 0 14 48t36 96l18 47l213 -191h-281zM700 0v400h400v-400h-400zM731 900l202 197q5 -12 12 -32.5t23 -64t25 -72t7 -28.5h-269z" />
+<glyph unicode="&#xe103;" d="M0 -22v143l216 193q-9 53 -13 83t-5.5 94t9 113t38.5 114t74 124q47 60 99.5 102.5t103 68t127.5 48t145.5 37.5t184.5 43.5t220 58.5q0 -189 -22 -343t-59 -258t-89 -181.5t-108.5 -120t-122 -68t-125.5 -30t-121.5 -1.5t-107.5 12.5t-87.5 17t-56.5 7.5l-99 -55z M238.5 300.5q19.5 -6.5 86.5 76.5q55 66 367 234q70 38 118.5 69.5t102 79t99 111.5t86.5 148q22 50 24 60t-6 19q-7 5 -17 5t-26.5 -14.5t-33.5 -39.5q-35 -51 -113.5 -108.5t-139.5 -89.5l-61 -32q-369 -197 -458 -401q-48 -111 -28.5 -117.5z" />
+<glyph unicode="&#xe104;" d="M111 408q0 -33 5 -63q9 -56 44 -119.5t105 -108.5q31 -21 64 -16t62 23.5t57 49.5t48 61.5t35 60.5q32 66 39 184.5t-13 157.5q79 -80 122 -164t26 -184q-5 -33 -20.5 -69.5t-37.5 -80.5q-10 -19 -14.5 -29t-12 -26t-9 -23.5t-3 -19t2.5 -15.5t11 -9.5t19.5 -5t30.5 2.5 t42 8q57 20 91 34t87.5 44.5t87 64t65.5 88.5t47 122q38 172 -44.5 341.5t-246.5 278.5q22 -44 43 -129q39 -159 -32 -154q-15 2 -33 9q-79 33 -120.5 100t-44 175.5t48.5 257.5q-13 -8 -34 -23.5t-72.5 -66.5t-88.5 -105.5t-60 -138t-8 -166.5q2 -12 8 -41.5t8 -43t6 -39.5 t3.5 -39.5t-1 -33.5t-6 -31.5t-13.5 -24t-21 -20.5t-31 -12q-38 -10 -67 13t-40.5 61.5t-15 81.5t10.5 75q-52 -46 -83.5 -101t-39 -107t-7.5 -85z" />
+<glyph unicode="&#xe105;" d="M-61 600l26 40q6 10 20 30t49 63.5t74.5 85.5t97 90t116.5 83.5t132.5 59t145.5 23.5t145.5 -23.5t132.5 -59t116.5 -83.5t97 -90t74.5 -85.5t49 -63.5t20 -30l26 -40l-26 -40q-6 -10 -20 -30t-49 -63.5t-74.5 -85.5t-97 -90t-116.5 -83.5t-132.5 -59t-145.5 -23.5 t-145.5 23.5t-132.5 59t-116.5 83.5t-97 90t-74.5 85.5t-49 63.5t-20 30zM120 600q7 -10 40.5 -58t56 -78.5t68 -77.5t87.5 -75t103 -49.5t125 -21.5t123.5 20t100.5 45.5t85.5 71.5t66.5 75.5t58 81.5t47 66q-1 1 -28.5 37.5t-42 55t-43.5 53t-57.5 63.5t-58.5 54 q49 -74 49 -163q0 -124 -88 -212t-212 -88t-212 88t-88 212q0 85 46 158q-102 -87 -226 -258zM377 656q49 -124 154 -191l105 105q-37 24 -75 72t-57 84l-20 36z" />
+<glyph unicode="&#xe106;" d="M-61 600l26 40q6 10 20 30t49 63.5t74.5 85.5t97 90t116.5 83.5t132.5 59t145.5 23.5q61 0 121 -17l37 142h148l-314 -1200h-148l37 143q-82 21 -165 71.5t-140 102t-109.5 112t-72 88.5t-29.5 43zM120 600q210 -282 393 -336l37 141q-107 18 -178.5 101.5t-71.5 193.5 q0 85 46 158q-102 -87 -226 -258zM377 656q49 -124 154 -191l47 47l23 87q-30 28 -59 69t-44 68l-14 26zM780 161l38 145q22 15 44.5 34t46 44t40.5 44t41 50.5t33.5 43.5t33 44t24.5 34q-97 127 -140 175l39 146q67 -54 131.5 -125.5t87.5 -103.5t36 -52l26 -40l-26 -40 q-7 -12 -25.5 -38t-63.5 -79.5t-95.5 -102.5t-124 -100t-146.5 -79z" />
+<glyph unicode="&#xe107;" d="M-97.5 34q13.5 -34 50.5 -34h1294q37 0 50.5 35.5t-7.5 67.5l-642 1056q-20 34 -48 36.5t-48 -29.5l-642 -1066q-21 -32 -7.5 -66zM155 200l445 723l445 -723h-345v100h-200v-100h-345zM500 600l100 -300l100 300v100h-200v-100z" />
+<glyph unicode="&#xe108;" d="M100 262v41q0 20 11 44.5t26 38.5l363 325v339q0 62 44 106t106 44t106 -44t44 -106v-339l363 -325q15 -14 26 -38.5t11 -44.5v-41q0 -20 -12 -26.5t-29 5.5l-359 249v-263q100 -91 100 -113v-64q0 -20 -13 -28.5t-32 0.5l-94 78h-222l-94 -78q-19 -9 -32 -0.5t-13 28.5 v64q0 22 100 113v263l-359 -249q-17 -12 -29 -5.5t-12 26.5z" />
+<glyph unicode="&#xe109;" d="M0 50q0 -20 14.5 -35t35.5 -15h1000q21 0 35.5 15t14.5 35v750h-1100v-750zM0 900h1100v150q0 21 -14.5 35.5t-35.5 14.5h-150v100h-100v-100h-500v100h-100v-100h-150q-21 0 -35.5 -14.5t-14.5 -35.5v-150zM100 100v100h100v-100h-100zM100 300v100h100v-100h-100z M100 500v100h100v-100h-100zM300 100v100h100v-100h-100zM300 300v100h100v-100h-100zM300 500v100h100v-100h-100zM500 100v100h100v-100h-100zM500 300v100h100v-100h-100zM500 500v100h100v-100h-100zM700 100v100h100v-100h-100zM700 300v100h100v-100h-100zM700 500 v100h100v-100h-100zM900 100v100h100v-100h-100zM900 300v100h100v-100h-100zM900 500v100h100v-100h-100z" />
+<glyph unicode="&#xe110;" d="M0 200v200h259l600 600h241v198l300 -295l-300 -300v197h-159l-600 -600h-341zM0 800h259l122 -122l141 142l-181 180h-341v-200zM678 381l141 142l122 -123h159v198l300 -295l-300 -300v197h-241z" />
+<glyph unicode="&#xe111;" d="M0 400v600q0 41 29.5 70.5t70.5 29.5h1000q41 0 70.5 -29.5t29.5 -70.5v-600q0 -41 -29.5 -70.5t-70.5 -29.5h-596l-304 -300v300h-100q-41 0 -70.5 29.5t-29.5 70.5z" />
+<glyph unicode="&#xe112;" d="M100 600v200h300v-250q0 -113 6 -145q17 -92 102 -117q39 -11 92 -11q37 0 66.5 5.5t50 15.5t36 24t24 31.5t14 37.5t7 42t2.5 45t0 47v25v250h300v-200q0 -42 -3 -83t-15 -104t-31.5 -116t-58 -109.5t-89 -96.5t-129 -65.5t-174.5 -25.5t-174.5 25.5t-129 65.5t-89 96.5 t-58 109.5t-31.5 116t-15 104t-3 83zM100 900v300h300v-300h-300zM800 900v300h300v-300h-300z" />
+<glyph unicode="&#xe113;" d="M-30 411l227 -227l352 353l353 -353l226 227l-578 579z" />
+<glyph unicode="&#xe114;" d="M70 797l580 -579l578 579l-226 227l-353 -353l-352 353z" />
+<glyph unicode="&#xe115;" d="M-198 700l299 283l300 -283h-203v-400h385l215 -200h-800v600h-196zM402 1000l215 -200h381v-400h-198l299 -283l299 283h-200v600h-796z" />
+<glyph unicode="&#xe116;" d="M18 939q-5 24 10 42q14 19 39 19h896l38 162q5 17 18.5 27.5t30.5 10.5h94q20 0 35 -14.5t15 -35.5t-15 -35.5t-35 -14.5h-54l-201 -961q-2 -4 -6 -10.5t-19 -17.5t-33 -11h-31v-50q0 -20 -14.5 -35t-35.5 -15t-35.5 15t-14.5 35v50h-300v-50q0 -20 -14.5 -35t-35.5 -15 t-35.5 15t-14.5 35v50h-50q-21 0 -35.5 15t-14.5 35q0 21 14.5 35.5t35.5 14.5h535l48 200h-633q-32 0 -54.5 21t-27.5 43z" />
+<glyph unicode="&#xe117;" d="M0 0v800h1200v-800h-1200zM0 900v100h200q0 41 29.5 70.5t70.5 29.5h300q41 0 70.5 -29.5t29.5 -70.5h500v-100h-1200z" />
+<glyph unicode="&#xe118;" d="M1 0l300 700h1200l-300 -700h-1200zM1 400v600h200q0 41 29.5 70.5t70.5 29.5h300q41 0 70.5 -29.5t29.5 -70.5h500v-200h-1000z" />
+<glyph unicode="&#xe119;" d="M302 300h198v600h-198l298 300l298 -300h-198v-600h198l-298 -300z" />
+<glyph unicode="&#xe120;" d="M0 600l300 298v-198h600v198l300 -298l-300 -297v197h-600v-197z" />
+<glyph unicode="&#xe121;" d="M0 100v100q0 41 29.5 70.5t70.5 29.5h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5zM31 400l172 739q5 22 23 41.5t38 19.5h672q19 0 37.5 -22.5t23.5 -45.5l172 -732h-1138zM800 100h100v100h-100v-100z M1000 100h100v100h-100v-100z" />
+<glyph unicode="&#xe122;" d="M-101 600v50q0 24 25 49t50 38l25 13v-250l-11 5.5t-24 14t-30 21.5t-24 27.5t-11 31.5zM100 500v250v8v8v7t0.5 7t1.5 5.5t2 5t3 4t4.5 3.5t6 1.5t7.5 0.5h200l675 250v-850l-675 200h-38l47 -276q2 -12 -3 -17.5t-11 -6t-21 -0.5h-8h-83q-20 0 -34.5 14t-18.5 35 q-55 337 -55 351zM1100 200v850q0 21 14.5 35.5t35.5 14.5q20 0 35 -14.5t15 -35.5v-850q0 -20 -15 -35t-35 -15q-21 0 -35.5 15t-14.5 35z" />
+<glyph unicode="&#xe123;" d="M74 350q0 21 13.5 35.5t33.5 14.5h18l117 173l63 327q15 77 76 140t144 83l-18 32q-6 19 3 32t29 13h94q20 0 29 -10.5t3 -29.5q-18 -36 -18 -37q83 -19 144 -82.5t76 -140.5l63 -327l118 -173h17q20 0 33.5 -14.5t13.5 -35.5q0 -20 -13 -40t-31 -27q-8 -3 -23 -8.5 t-65 -20t-103 -25t-132.5 -19.5t-158.5 -9q-125 0 -245.5 20.5t-178.5 40.5l-58 20q-18 7 -31 27.5t-13 40.5zM497 110q12 -49 40 -79.5t63 -30.5t63 30.5t39 79.5q-48 -6 -102 -6t-103 6z" />
+<glyph unicode="&#xe124;" d="M21 445l233 -45l-78 -224l224 78l45 -233l155 179l155 -179l45 233l224 -78l-78 224l234 45l-180 155l180 156l-234 44l78 225l-224 -78l-45 233l-155 -180l-155 180l-45 -233l-224 78l78 -225l-233 -44l179 -156z" />
+<glyph unicode="&#xe125;" d="M0 200h200v600h-200v-600zM300 275q0 -75 100 -75h61q124 -100 139 -100h250q46 0 83 57l238 344q29 31 29 74v100q0 44 -30.5 84.5t-69.5 40.5h-328q28 118 28 125v150q0 44 -30.5 84.5t-69.5 40.5h-50q-27 0 -51 -20t-38 -48l-96 -198l-145 -196q-20 -26 -20 -63v-400z M400 300v375l150 213l100 212h50v-175l-50 -225h450v-125l-250 -375h-214l-136 100h-100z" />
+<glyph unicode="&#xe126;" d="M0 400v600h200v-600h-200zM300 525v400q0 75 100 75h61q124 100 139 100h250q46 0 83 -57l238 -344q29 -31 29 -74v-100q0 -44 -30.5 -84.5t-69.5 -40.5h-328q28 -118 28 -125v-150q0 -44 -30.5 -84.5t-69.5 -40.5h-50q-27 0 -51 20t-38 48l-96 198l-145 196 q-20 26 -20 63zM400 525l150 -212l100 -213h50v175l-50 225h450v125l-250 375h-214l-136 -100h-100v-375z" />
+<glyph unicode="&#xe127;" d="M8 200v600h200v-600h-200zM308 275v525q0 17 14 35.5t28 28.5l14 9l362 230q14 6 25 6q17 0 29 -12l109 -112q14 -14 14 -34q0 -18 -11 -32l-85 -121h302q85 0 138.5 -38t53.5 -110t-54.5 -111t-138.5 -39h-107l-130 -339q-7 -22 -20.5 -41.5t-28.5 -19.5h-341 q-7 0 -90 81t-83 94zM408 289l100 -89h293l131 339q6 21 19.5 41t28.5 20h203q16 0 25 15t9 36q0 20 -9 34.5t-25 14.5h-457h-6.5h-7.5t-6.5 0.5t-6 1t-5 1.5t-5.5 2.5t-4 4t-4 5.5q-5 12 -5 20q0 14 10 27l147 183l-86 83l-339 -236v-503z" />
+<glyph unicode="&#xe128;" d="M-101 651q0 72 54 110t139 38l302 -1l-85 121q-11 16 -11 32q0 21 14 34l109 113q13 12 29 12q11 0 25 -6l365 -230q7 -4 17 -10.5t26.5 -26t16.5 -36.5v-526q0 -13 -86 -93.5t-94 -80.5h-341q-16 0 -29.5 20t-19.5 41l-130 339h-107q-84 0 -139 39t-55 111zM-1 601h222 q15 0 28.5 -20.5t19.5 -40.5l131 -339h293l107 89v502l-343 237l-87 -83l145 -184q10 -11 10 -26q0 -11 -5 -20q-1 -3 -3.5 -5.5l-4 -4t-5 -2.5t-5.5 -1.5t-6.5 -1t-6.5 -0.5h-7.5h-6.5h-476v-100zM1000 201v600h200v-600h-200z" />
+<glyph unicode="&#xe129;" d="M97 719l230 -363q4 -6 10.5 -15.5t26 -25t36.5 -15.5h525q13 0 94 83t81 90v342q0 15 -20 28.5t-41 19.5l-339 131v106q0 84 -39 139t-111 55t-110 -53.5t-38 -138.5v-302l-121 84q-15 12 -33.5 11.5t-32.5 -13.5l-112 -110q-22 -22 -6 -53zM172 739l83 86l183 -146 q22 -18 47 -5q3 1 5.5 3.5l4 4t2.5 5t1.5 5.5t1 6.5t0.5 6.5v7.5v6.5v456q0 22 25 31t50 -0.5t25 -30.5v-202q0 -16 20 -29.5t41 -19.5l339 -130v-294l-89 -100h-503zM400 0v200h600v-200h-600z" />
+<glyph unicode="&#xe130;" d="M2 585q-16 -31 6 -53l112 -110q13 -13 32 -13.5t34 10.5l121 85q0 -51 -0.5 -153.5t-0.5 -148.5q0 -84 38.5 -138t110.5 -54t111 55t39 139v106l339 131q20 6 40.5 19.5t20.5 28.5v342q0 7 -81 90t-94 83h-525q-17 0 -35.5 -14t-28.5 -28l-10 -15zM77 565l236 339h503 l89 -100v-294l-340 -130q-20 -6 -40 -20t-20 -29v-202q0 -22 -25 -31t-50 0t-25 31v456v14.5t-1.5 11.5t-5 12t-9.5 7q-24 13 -46 -5l-184 -146zM305 1104v200h600v-200h-600z" />
+<glyph unicode="&#xe131;" d="M5 597q0 122 47.5 232.5t127.5 190.5t190.5 127.5t232.5 47.5q162 0 299.5 -80t217.5 -218t80 -300t-80 -299.5t-217.5 -217.5t-299.5 -80t-300 80t-218 217.5t-80 299.5zM298 701l2 -201h300l-2 -194l402 294l-402 298v-197h-300z" />
+<glyph unicode="&#xe132;" d="M0 597q0 122 47.5 232.5t127.5 190.5t190.5 127.5t231.5 47.5q122 0 232.5 -47.5t190.5 -127.5t127.5 -190.5t47.5 -232.5q0 -162 -80 -299.5t-218 -217.5t-300 -80t-299.5 80t-217.5 217.5t-80 299.5zM200 600l402 -294l-2 194h300l2 201h-300v197z" />
+<glyph unicode="&#xe133;" d="M5 597q0 122 47.5 232.5t127.5 190.5t190.5 127.5t232.5 47.5q162 0 299.5 -80t217.5 -218t80 -300t-80 -299.5t-217.5 -217.5t-299.5 -80t-300 80t-218 217.5t-80 299.5zM300 600h200v-300h200v300h200l-300 400z" />
+<glyph unicode="&#xe134;" d="M5 597q0 122 47.5 232.5t127.5 190.5t190.5 127.5t232.5 47.5q162 0 299.5 -80t217.5 -218t80 -300t-80 -299.5t-217.5 -217.5t-299.5 -80t-300 80t-218 217.5t-80 299.5zM300 600l300 -400l300 400h-200v300h-200v-300h-200z" />
+<glyph unicode="&#xe135;" d="M5 597q0 122 47.5 232.5t127.5 190.5t190.5 127.5t232.5 47.5q121 0 231.5 -47.5t190.5 -127.5t127.5 -190.5t47.5 -232.5q0 -162 -80 -299.5t-217.5 -217.5t-299.5 -80t-300 80t-218 217.5t-80 299.5zM254 780q-8 -33 5.5 -92.5t7.5 -87.5q0 -9 17 -44t16 -60 q12 0 23 -5.5t23 -15t20 -13.5q24 -12 108 -42q22 -8 53 -31.5t59.5 -38.5t57.5 -11q8 -18 -15 -55t-20 -57q42 -71 87 -80q0 -6 -3 -15.5t-3.5 -14.5t4.5 -17q104 -3 221 112q30 29 47 47t34.5 49t20.5 62q-14 9 -37 9.5t-36 7.5q-14 7 -49 15t-52 19q-9 0 -39.5 -0.5 t-46.5 -1.5t-39 -6.5t-39 -16.5q-50 -35 -66 -12q-4 2 -3.5 25.5t0.5 25.5q-6 13 -26.5 17t-24.5 7q2 22 -2 41t-16.5 28t-38.5 -20q-23 -25 -42 4q-19 28 -8 58q6 16 22 22q6 -1 26 -1.5t33.5 -4t19.5 -13.5q12 -19 32 -37.5t34 -27.5l14 -8q0 3 9.5 39.5t5.5 57.5 q-4 23 14.5 44.5t22.5 31.5q5 14 10 35t8.5 31t15.5 22.5t34 21.5q-6 18 10 37q8 0 23.5 -1.5t24.5 -1.5t20.5 4.5t20.5 15.5q-10 23 -30.5 42.5t-38 30t-49 26.5t-43.5 23q11 39 2 44q31 -13 58 -14.5t39 3.5l11 4q7 36 -16.5 53.5t-64.5 28.5t-56 23q-19 -3 -37 0 q-15 -12 -36.5 -21t-34.5 -12t-44 -8t-39 -6q-15 -3 -45.5 0.5t-45.5 -2.5q-21 -7 -52 -26.5t-34 -34.5q-3 -11 6.5 -22.5t8.5 -18.5q-3 -34 -27.5 -90.5t-29.5 -79.5zM518 916q3 12 16 30t16 25q10 -10 18.5 -10t14 6t14.5 14.5t16 12.5q0 -24 17 -66.5t17 -43.5 q-9 2 -31 5t-36 5t-32 8t-30 14zM692 1003h1h-1z" />
+<glyph unicode="&#xe136;" d="M0 164.5q0 21.5 15 37.5l600 599q-33 101 6 201.5t135 154.5q164 92 306 -9l-259 -138l145 -232l251 126q13 -175 -151 -267q-123 -70 -253 -23l-596 -596q-15 -16 -36.5 -16t-36.5 16l-111 110q-15 15 -15 36.5z" />
+<glyph unicode="&#xe137;" horiz-adv-x="1220" d="M0 196v100q0 41 29.5 70.5t70.5 29.5h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5zM0 596v100q0 41 29.5 70.5t70.5 29.5h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000 q-41 0 -70.5 29.5t-29.5 70.5zM0 996v100q0 41 29.5 70.5t70.5 29.5h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5zM600 596h500v100h-500v-100zM800 196h300v100h-300v-100zM900 996h200v100h-200v-100z" />
+<glyph unicode="&#xe138;" d="M100 1100v100h1000v-100h-1000zM150 1000h900l-350 -500v-300l-200 -200v500z" />
+<glyph unicode="&#xe139;" d="M0 200v200h1200v-200q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5zM0 500v400q0 41 29.5 70.5t70.5 29.5h300v100q0 41 29.5 70.5t70.5 29.5h200q41 0 70.5 -29.5t29.5 -70.5v-100h300q41 0 70.5 -29.5t29.5 -70.5v-400h-500v100h-200v-100h-500z M500 1000h200v100h-200v-100z" />
+<glyph unicode="&#xe140;" d="M0 0v400l129 -129l200 200l142 -142l-200 -200l129 -129h-400zM0 800l129 129l200 -200l142 142l-200 200l129 129h-400v-400zM729 329l142 142l200 -200l129 129v-400h-400l129 129zM729 871l200 200l-129 129h400v-400l-129 129l-200 -200z" />
+<glyph unicode="&#xe141;" d="M0 596q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM182 596q0 -172 121.5 -293t292.5 -121t292.5 121t121.5 293q0 171 -121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM291 655 q0 23 15.5 38.5t38.5 15.5t39 -16t16 -38q0 -23 -16 -39t-39 -16q-22 0 -38 16t-16 39zM400 850q0 22 16 38.5t39 16.5q22 0 38 -16t16 -39t-16 -39t-38 -16q-23 0 -39 16.5t-16 38.5zM514 609q0 32 20.5 56.5t51.5 29.5l122 126l1 1q-9 14 -9 28q0 22 16 38.5t39 16.5 q22 0 38 -16t16 -39t-16 -39t-38 -16q-14 0 -29 10l-55 -145q17 -22 17 -51q0 -36 -25.5 -61.5t-61.5 -25.5t-61.5 25.5t-25.5 61.5zM800 655q0 22 16 38t39 16t38.5 -15.5t15.5 -38.5t-16 -39t-38 -16q-23 0 -39 16t-16 39z" />
+<glyph unicode="&#xe142;" d="M-40 375q-13 -95 35 -173q35 -57 94 -89t129 -32q63 0 119 28q33 16 65 40.5t52.5 45.5t59.5 64q40 44 57 61l394 394q35 35 47 84t-3 96q-27 87 -117 104q-20 2 -29 2q-46 0 -78.5 -16.5t-67.5 -51.5l-389 -396l-7 -7l69 -67l377 373q20 22 39 38q23 23 50 23 q38 0 53 -36q16 -39 -20 -75l-547 -547q-52 -52 -125 -52q-55 0 -100 33t-54 96q-5 35 2.5 66t31.5 63t42 50t56 54q24 21 44 41l348 348q52 52 82.5 79.5t84 54t107.5 26.5q25 0 48 -4q95 -17 154 -94.5t51 -175.5q-7 -101 -98 -192l-252 -249l-253 -256l7 -7l69 -60 l517 511q67 67 95 157t11 183q-16 87 -67 154t-130 103q-69 33 -152 33q-107 0 -197 -55q-40 -24 -111 -95l-512 -512q-68 -68 -81 -163z" />
+<glyph unicode="&#xe143;" d="M80 784q0 131 98.5 229.5t230.5 98.5q143 0 241 -129q103 129 246 129q129 0 226 -98.5t97 -229.5q0 -46 -17.5 -91t-61 -99t-77 -89.5t-104.5 -105.5q-197 -191 -293 -322l-17 -23l-16 23q-43 58 -100 122.5t-92 99.5t-101 100q-71 70 -104.5 105.5t-77 89.5t-61 99 t-17.5 91zM250 784q0 -27 30.5 -70t61.5 -75.5t95 -94.5l22 -22q93 -90 190 -201q82 92 195 203l12 12q64 62 97.5 97t64.5 79t31 72q0 71 -48 119.5t-105 48.5q-74 0 -132 -83l-118 -171l-114 174q-51 80 -123 80q-60 0 -109.5 -49.5t-49.5 -118.5z" />
+<glyph unicode="&#xe144;" d="M57 353q0 -95 66 -159l141 -142q68 -66 159 -66q93 0 159 66l283 283q66 66 66 159t-66 159l-141 141q-8 9 -19 17l-105 -105l212 -212l-389 -389l-247 248l95 95l-18 18q-46 45 -75 101l-55 -55q-66 -66 -66 -159zM269 706q0 -93 66 -159l141 -141q7 -7 19 -17l105 105 l-212 212l389 389l247 -247l-95 -96l18 -17q47 -49 77 -100l29 29q35 35 62.5 88t27.5 96q0 93 -66 159l-141 141q-66 66 -159 66q-95 0 -159 -66l-283 -283q-66 -64 -66 -159z" />
+<glyph unicode="&#xe145;" d="M200 100v953q0 21 30 46t81 48t129 38t163 15t162 -15t127 -38t79 -48t29 -46v-953q0 -41 -29.5 -70.5t-70.5 -29.5h-600q-41 0 -70.5 29.5t-29.5 70.5zM300 300h600v700h-600v-700zM496 150q0 -43 30.5 -73.5t73.5 -30.5t73.5 30.5t30.5 73.5t-30.5 73.5t-73.5 30.5 t-73.5 -30.5t-30.5 -73.5z" />
+<glyph unicode="&#xe146;" d="M0 0l303 380l207 208l-210 212h300l267 279l-35 36q-15 14 -15 35t15 35q14 15 35 15t35 -15l283 -282q15 -15 15 -36t-15 -35q-14 -15 -35 -15t-35 15l-36 35l-279 -267v-300l-212 210l-208 -207z" />
+<glyph unicode="&#xe148;" d="M295 433h139q5 -77 48.5 -126.5t117.5 -64.5v335q-6 1 -15.5 4t-11.5 3q-46 14 -79 26.5t-72 36t-62.5 52t-40 72.5t-16.5 99q0 92 44 159.5t109 101t144 40.5v78h100v-79q38 -4 72.5 -13.5t75.5 -31.5t71 -53.5t51.5 -84t24.5 -118.5h-159q-8 72 -35 109.5t-101 50.5 v-307l64 -14q34 -7 64 -16.5t70 -31.5t67.5 -52t47.5 -80.5t20 -112.5q0 -139 -89 -224t-244 -96v-77h-100v78q-152 17 -237 104q-40 40 -52.5 93.5t-15.5 139.5zM466 889q0 -29 8 -51t16.5 -34t29.5 -22.5t31 -13.5t38 -10q7 -2 11 -3v274q-61 -8 -97.5 -37.5t-36.5 -102.5 zM700 237q170 18 170 151q0 64 -44 99.5t-126 60.5v-311z" />
+<glyph unicode="&#xe149;" d="M100 600v100h166q-24 49 -44 104q-10 26 -14.5 55.5t-3 72.5t25 90t68.5 87q97 88 263 88q129 0 230 -89t101 -208h-153q0 52 -34 89.5t-74 51.5t-76 14q-37 0 -79 -14.5t-62 -35.5q-41 -44 -41 -101q0 -28 16.5 -69.5t28 -62.5t41.5 -72h241v-100h-197q8 -50 -2.5 -115 t-31.5 -94q-41 -59 -99 -113q35 11 84 18t70 7q33 1 103 -16t103 -17q76 0 136 30l50 -147q-41 -25 -80.5 -36.5t-59 -13t-61.5 -1.5q-23 0 -128 33t-155 29q-39 -4 -82 -17t-66 -25l-24 -11l-55 145l16.5 11t15.5 10t13.5 9.5t14.5 12t14.5 14t17.5 18.5q48 55 54 126.5 t-30 142.5h-221z" />
+<glyph unicode="&#xe150;" d="M2 300l298 -300l298 300h-198v900h-200v-900h-198zM602 900l298 300l298 -300h-198v-900h-200v900h-198z" />
+<glyph unicode="&#xe151;" d="M2 300h198v900h200v-900h198l-298 -300zM700 0v200h100v-100h200v-100h-300zM700 400v100h300v-200h-99v-100h-100v100h99v100h-200zM700 700v500h300v-500h-100v100h-100v-100h-100zM801 900h100v200h-100v-200z" />
+<glyph unicode="&#xe152;" d="M2 300h198v900h200v-900h198l-298 -300zM700 0v500h300v-500h-100v100h-100v-100h-100zM700 700v200h100v-100h200v-100h-300zM700 1100v100h300v-200h-99v-100h-100v100h99v100h-200zM801 200h100v200h-100v-200z" />
+<glyph unicode="&#xe153;" d="M2 300l298 -300l298 300h-198v900h-200v-900h-198zM800 100v400h300v-500h-100v100h-200zM800 1100v100h200v-500h-100v400h-100zM901 200h100v200h-100v-200z" />
+<glyph unicode="&#xe154;" d="M2 300l298 -300l298 300h-198v900h-200v-900h-198zM800 400v100h200v-500h-100v400h-100zM800 800v400h300v-500h-100v100h-200zM901 900h100v200h-100v-200z" />
+<glyph unicode="&#xe155;" d="M2 300l298 -300l298 300h-198v900h-200v-900h-198zM700 100v200h500v-200h-500zM700 400v200h400v-200h-400zM700 700v200h300v-200h-300zM700 1000v200h200v-200h-200z" />
+<glyph unicode="&#xe156;" d="M2 300l298 -300l298 300h-198v900h-200v-900h-198zM700 100v200h200v-200h-200zM700 400v200h300v-200h-300zM700 700v200h400v-200h-400zM700 1000v200h500v-200h-500z" />
+<glyph unicode="&#xe157;" d="M0 400v300q0 165 117.5 282.5t282.5 117.5h300q162 0 281 -118.5t119 -281.5v-300q0 -165 -118.5 -282.5t-281.5 -117.5h-300q-165 0 -282.5 117.5t-117.5 282.5zM200 300q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5 h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500z" />
+<glyph unicode="&#xe158;" d="M0 400v300q0 163 119 281.5t281 118.5h300q165 0 282.5 -117.5t117.5 -282.5v-300q0 -165 -117.5 -282.5t-282.5 -117.5h-300q-163 0 -281.5 117.5t-118.5 282.5zM200 300q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5 h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500zM400 300l333 250l-333 250v-500z" />
+<glyph unicode="&#xe159;" d="M0 400v300q0 163 117.5 281.5t282.5 118.5h300q163 0 281.5 -119t118.5 -281v-300q0 -165 -117.5 -282.5t-282.5 -117.5h-300q-165 0 -282.5 117.5t-117.5 282.5zM200 300q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5 h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500zM300 700l250 -333l250 333h-500z" />
+<glyph unicode="&#xe160;" d="M0 400v300q0 165 117.5 282.5t282.5 117.5h300q165 0 282.5 -117.5t117.5 -282.5v-300q0 -162 -118.5 -281t-281.5 -119h-300q-165 0 -282.5 118.5t-117.5 281.5zM200 300q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5 h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500zM300 400h500l-250 333z" />
+<glyph unicode="&#xe161;" d="M0 400v300h300v200l400 -350l-400 -350v200h-300zM500 0v200h500q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5h-500v200h400q165 0 282.5 -117.5t117.5 -282.5v-300q0 -165 -117.5 -282.5t-282.5 -117.5h-400z" />
+<glyph unicode="&#xe162;" d="M217 519q8 -19 31 -19h302q-155 -438 -160 -458q-5 -21 4 -32l9 -8h9q14 0 26 15q11 13 274.5 321.5t264.5 308.5q14 19 5 36q-8 17 -31 17l-301 -1q1 4 78 219.5t79 227.5q2 15 -5 27l-9 9h-9q-15 0 -25 -16q-4 -6 -98 -111.5t-228.5 -257t-209.5 -237.5q-16 -19 -6 -41 z" />
+<glyph unicode="&#xe163;" d="M0 400q0 -165 117.5 -282.5t282.5 -117.5h300q47 0 100 15v185h-500q-41 0 -70.5 29.5t-29.5 70.5v500q0 41 29.5 70.5t70.5 29.5h500v185q-14 4 -114 7.5t-193 5.5l-93 2q-165 0 -282.5 -117.5t-117.5 -282.5v-300zM600 400v300h300v200l400 -350l-400 -350v200h-300z " />
+<glyph unicode="&#xe164;" d="M0 400q0 -165 117.5 -282.5t282.5 -117.5h300q163 0 281.5 117.5t118.5 282.5v98l-78 73l-122 -123v-148q0 -41 -29.5 -70.5t-70.5 -29.5h-500q-41 0 -70.5 29.5t-29.5 70.5v500q0 41 29.5 70.5t70.5 29.5h156l118 122l-74 78h-100q-165 0 -282.5 -117.5t-117.5 -282.5 v-300zM496 709l353 342l-149 149h500v-500l-149 149l-342 -353z" />
+<glyph unicode="&#xe165;" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -171 121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM406 600 q0 80 57 137t137 57t137 -57t57 -137t-57 -137t-137 -57t-137 57t-57 137z" />
+<glyph unicode="&#xe166;" d="M0 0v275q0 11 7 18t18 7h1048q11 0 19 -7.5t8 -17.5v-275h-1100zM100 800l445 -500l450 500h-295v400h-300v-400h-300zM900 150h100v50h-100v-50z" />
+<glyph unicode="&#xe167;" d="M0 0v275q0 11 7 18t18 7h1048q11 0 19 -7.5t8 -17.5v-275h-1100zM100 700h300v-300h300v300h295l-445 500zM900 150h100v50h-100v-50z" />
+<glyph unicode="&#xe168;" d="M0 0v275q0 11 7 18t18 7h1048q11 0 19 -7.5t8 -17.5v-275h-1100zM100 705l305 -305l596 596l-154 155l-442 -442l-150 151zM900 150h100v50h-100v-50z" />
+<glyph unicode="&#xe169;" d="M0 0v275q0 11 7 18t18 7h1048q11 0 19 -7.5t8 -17.5v-275h-1100zM100 988l97 -98l212 213l-97 97zM200 400l697 1l3 699l-250 -239l-149 149l-212 -212l149 -149zM900 150h100v50h-100v-50z" />
+<glyph unicode="&#xe170;" d="M0 0v275q0 11 7 18t18 7h1048q11 0 19 -7.5t8 -17.5v-275h-1100zM200 612l212 -212l98 97l-213 212zM300 1200l239 -250l-149 -149l212 -212l149 148l249 -237l-1 697zM900 150h100v50h-100v-50z" />
+<glyph unicode="&#xe171;" d="M23 415l1177 784v-1079l-475 272l-310 -393v416h-392zM494 210l672 938l-672 -712v-226z" />
+<glyph unicode="&#xe172;" d="M0 150v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100l200 -200v-850q0 -21 -15 -35.5t-35 -14.5h-150v400h-700v-400h-150q-21 0 -35.5 14.5t-14.5 35.5zM600 1000h100v200h-100v-200z" />
+<glyph unicode="&#xe173;" d="M0 150v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100l200 -200v-218l-276 -275l-120 120l-126 -127h-378v-400h-150q-21 0 -35.5 14.5t-14.5 35.5zM581 306l123 123l120 -120l353 352l123 -123l-475 -476zM600 1000h100v200h-100v-200z" />
+<glyph unicode="&#xe174;" d="M0 150v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100l200 -200v-269l-103 -103l-170 170l-298 -298h-329v-400h-150q-21 0 -35.5 14.5t-14.5 35.5zM600 1000h100v200h-100v-200zM700 133l170 170l-170 170l127 127l170 -170l170 170l127 -128l-170 -169l170 -170 l-127 -127l-170 170l-170 -170z" />
+<glyph unicode="&#xe175;" d="M0 150v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100l200 -200v-300h-400v-200h-500v-400h-150q-21 0 -35.5 14.5t-14.5 35.5zM600 300l300 -300l300 300h-200v300h-200v-300h-200zM600 1000v200h100v-200h-100z" />
+<glyph unicode="&#xe176;" d="M0 150v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100l200 -200v-402l-200 200l-298 -298h-402v-400h-150q-21 0 -35.5 14.5t-14.5 35.5zM600 300h200v-300h200v300h200l-300 300zM600 1000v200h100v-200h-100z" />
+<glyph unicode="&#xe177;" d="M0 250q0 -21 14.5 -35.5t35.5 -14.5h1100q21 0 35.5 14.5t14.5 35.5v550h-1200v-550zM0 900h1200v150q0 21 -14.5 35.5t-35.5 14.5h-1100q-21 0 -35.5 -14.5t-14.5 -35.5v-150zM100 300v200h400v-200h-400z" />
+<glyph unicode="&#xe178;" d="M0 400l300 298v-198h400v-200h-400v-198zM100 800v200h100v-200h-100zM300 800v200h100v-200h-100zM500 800v200h400v198l300 -298l-300 -298v198h-400zM800 300v200h100v-200h-100zM1000 300h100v200h-100v-200z" />
+<glyph unicode="&#xe179;" d="M100 700v400l50 100l50 -100v-300h100v300l50 100l50 -100v-300h100v300l50 100l50 -100v-400l-100 -203v-447q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v447zM800 597q0 -29 10.5 -55.5t25 -43t29 -28.5t25.5 -18l10 -5v-397q0 -21 14.5 -35.5 t35.5 -14.5h200q21 0 35.5 14.5t14.5 35.5v1106q0 31 -18 40.5t-44 -7.5l-276 -116q-25 -17 -43.5 -51.5t-18.5 -65.5v-359z" />
+<glyph unicode="&#xe180;" d="M100 0h400v56q-75 0 -87.5 6t-12.5 44v394h500v-394q0 -38 -12.5 -44t-87.5 -6v-56h400v56q-4 0 -11 0.5t-24 3t-30 7t-24 15t-11 24.5v888q0 22 25 34.5t50 13.5l25 2v56h-400v-56q75 0 87.5 -6t12.5 -44v-394h-500v394q0 38 12.5 44t87.5 6v56h-400v-56q4 0 11 -0.5 t24 -3t30 -7t24 -15t11 -24.5v-888q0 -22 -25 -34.5t-50 -13.5l-25 -2v-56z" />
+<glyph unicode="&#xe181;" d="M0 300q0 -41 29.5 -70.5t70.5 -29.5h300q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5h-300q-41 0 -70.5 -29.5t-29.5 -70.5v-500zM100 100h400l200 200h105l295 98v-298h-425l-100 -100h-375zM100 300v200h300v-200h-300zM100 600v200h300v-200h-300z M100 1000h400l200 -200v-98l295 98h105v200h-425l-100 100h-375zM700 402v163l400 133v-163z" />
+<glyph unicode="&#xe182;" d="M16.5 974.5q0.5 -21.5 16 -90t46.5 -140t104 -177.5t175 -208q103 -103 207.5 -176t180 -103.5t137 -47t92.5 -16.5l31 1l163 162q17 18 13.5 41t-22.5 37l-192 136q-19 14 -45 12t-42 -19l-118 -118q-142 101 -268 227t-227 268l118 118q17 17 20 41.5t-11 44.5 l-139 194q-14 19 -36.5 22t-40.5 -14l-162 -162q-1 -11 -0.5 -32.5z" />
+<glyph unicode="&#xe183;" d="M0 50v212q0 20 10.5 45.5t24.5 39.5l365 303v50q0 4 1 10.5t12 22.5t30 28.5t60 23t97 10.5t97 -10t60 -23.5t30 -27.5t12 -24l1 -10v-50l365 -303q14 -14 24.5 -39.5t10.5 -45.5v-212q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-20 0 -35 14.5t-15 35.5zM0 712 q0 -21 14.5 -33.5t34.5 -8.5l202 33q20 4 34.5 21t14.5 38v146q141 24 300 24t300 -24v-146q0 -21 14.5 -38t34.5 -21l202 -33q20 -4 34.5 8.5t14.5 33.5v200q-6 8 -19 20.5t-63 45t-112 57t-171 45t-235 20.5q-92 0 -175 -10.5t-141.5 -27t-108.5 -36.5t-81.5 -40 t-53.5 -36.5t-31 -27.5l-9 -10v-200z" />
+<glyph unicode="&#xe184;" d="M100 0v100h1100v-100h-1100zM175 200h950l-125 150v250l100 100v400h-100v-200h-100v200h-200v-200h-100v200h-200v-200h-100v200h-100v-400l100 -100v-250z" />
+<glyph unicode="&#xe185;" d="M100 0h300v400q0 41 -29.5 70.5t-70.5 29.5h-100q-41 0 -70.5 -29.5t-29.5 -70.5v-400zM500 0v1000q0 41 29.5 70.5t70.5 29.5h100q41 0 70.5 -29.5t29.5 -70.5v-1000h-300zM900 0v700q0 41 29.5 70.5t70.5 29.5h100q41 0 70.5 -29.5t29.5 -70.5v-700h-300z" />
+<glyph unicode="&#xe186;" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 300h300v300h-200v100h200v100h-300v-300h200v-100h-200v-100zM600 300h200v100h100v300h-100v100h-200v-500 zM700 400v300h100v-300h-100z" />
+<glyph unicode="&#xe187;" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 300h100v200h100v-200h100v500h-100v-200h-100v200h-100v-500zM600 300h200v100h100v300h-100v100h-200v-500 zM700 400v300h100v-300h-100z" />
+<glyph unicode="&#xe188;" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 300h300v100h-200v300h200v100h-300v-500zM600 300h300v100h-200v300h200v100h-300v-500z" />
+<glyph unicode="&#xe189;" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 550l300 -150v300zM600 400l300 150l-300 150v-300z" />
+<glyph unicode="&#xe190;" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 300v500h700v-500h-700zM300 400h130q41 0 68 42t27 107t-28.5 108t-66.5 43h-130v-300zM575 549 q0 -65 27 -107t68 -42h130v300h-130q-38 0 -66.5 -43t-28.5 -108z" />
+<glyph unicode="&#xe191;" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 300h300v300h-200v100h200v100h-300v-300h200v-100h-200v-100zM601 300h100v100h-100v-100zM700 700h100 v-400h100v500h-200v-100z" />
+<glyph unicode="&#xe192;" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 300h300v400h-200v100h-100v-500zM301 400v200h100v-200h-100zM601 300h100v100h-100v-100zM700 700h100 v-400h100v500h-200v-100z" />
+<glyph unicode="&#xe193;" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 700v100h300v-300h-99v-100h-100v100h99v200h-200zM201 300v100h100v-100h-100zM601 300v100h100v-100h-100z M700 700v100h200v-500h-100v400h-100z" />
+<glyph unicode="&#xe194;" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -171 121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM400 500v200 l100 100h300v-100h-300v-200h300v-100h-300z" />
+<glyph unicode="&#xe195;" d="M0 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM182 600q0 -171 121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM400 400v400h300 l100 -100v-100h-100v100h-200v-100h200v-100h-200v-100h-100zM700 400v100h100v-100h-100z" />
+<glyph unicode="&#xe197;" d="M-14 494q0 -80 56.5 -137t135.5 -57h222v300h400v-300h128q120 0 205 86.5t85 207.5t-85 207t-205 86q-46 0 -90 -14q-44 97 -134.5 156.5t-200.5 59.5q-152 0 -260 -107.5t-108 -260.5q0 -25 2 -37q-66 -14 -108.5 -67.5t-42.5 -122.5zM300 200h200v300h200v-300h200 l-300 -300z" />
+<glyph unicode="&#xe198;" d="M-14 494q0 -80 56.5 -137t135.5 -57h8l414 414l403 -403q94 26 154.5 104.5t60.5 178.5q0 120 -85 206.5t-205 86.5q-46 0 -90 -14q-44 97 -134.5 156.5t-200.5 59.5q-152 0 -260 -107.5t-108 -260.5q0 -25 2 -37q-66 -14 -108.5 -67.5t-42.5 -122.5zM300 200l300 300 l300 -300h-200v-300h-200v300h-200z" />
+<glyph unicode="&#xe199;" d="M100 200h400v-155l-75 -45h350l-75 45v155h400l-270 300h170l-270 300h170l-300 333l-300 -333h170l-270 -300h170z" />
+<glyph unicode="&#xe200;" d="M121 700q0 -53 28.5 -97t75.5 -65q-4 -16 -4 -38q0 -74 52.5 -126.5t126.5 -52.5q56 0 100 30v-306l-75 -45h350l-75 45v306q46 -30 100 -30q74 0 126.5 52.5t52.5 126.5q0 24 -9 55q50 32 79.5 83t29.5 112q0 90 -61.5 155.5t-150.5 71.5q-26 89 -99.5 145.5 t-167.5 56.5q-116 0 -197.5 -81.5t-81.5 -197.5q0 -4 1 -11.5t1 -11.5q-14 2 -23 2q-74 0 -126.5 -52.5t-52.5 -126.5z" />
+</font>
+</defs></svg> 
\ No newline at end of file
diff --git a/pelican-plugins/bootstrap-rst/bootstrap/fonts/glyphicons-halflings-regular.ttf b/pelican-plugins/bootstrap-rst/bootstrap/fonts/glyphicons-halflings-regular.ttf
new file mode 100644
index 0000000000000000000000000000000000000000..67fa00bf83801d2fa568546b982c80d27f6ef74e
Binary files /dev/null and b/pelican-plugins/bootstrap-rst/bootstrap/fonts/glyphicons-halflings-regular.ttf differ
diff --git a/pelican-plugins/bootstrap-rst/bootstrap/fonts/glyphicons-halflings-regular.woff b/pelican-plugins/bootstrap-rst/bootstrap/fonts/glyphicons-halflings-regular.woff
new file mode 100644
index 0000000000000000000000000000000000000000..8c54182aa5d4d1ab3c9171976b615c1dcb1dc187
Binary files /dev/null and b/pelican-plugins/bootstrap-rst/bootstrap/fonts/glyphicons-halflings-regular.woff differ
diff --git a/pelican-plugins/bootstrap-rst/bootstrap/js/bootstrap.js b/pelican-plugins/bootstrap-rst/bootstrap/js/bootstrap.js
new file mode 100644
index 0000000000000000000000000000000000000000..8ae571b6da5be9c7dcd95ba25896ae39e1917445
--- /dev/null
+++ b/pelican-plugins/bootstrap-rst/bootstrap/js/bootstrap.js
@@ -0,0 +1,1951 @@
+/*!
+ * Bootstrap v3.1.1 (http://getbootstrap.com)
+ * Copyright 2011-2014 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ */
+
+if (typeof jQuery === 'undefined') { throw new Error('Bootstrap\'s JavaScript requires jQuery') }
+
+/* ========================================================================
+ * Bootstrap: transition.js v3.1.1
+ * http://getbootstrap.com/javascript/#transitions
+ * ========================================================================
+ * Copyright 2011-2014 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+  'use strict';
+
+  // CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/)
+  // ============================================================
+
+  function transitionEnd() {
+    var el = document.createElement('bootstrap')
+
+    var transEndEventNames = {
+      'WebkitTransition' : 'webkitTransitionEnd',
+      'MozTransition'    : 'transitionend',
+      'OTransition'      : 'oTransitionEnd otransitionend',
+      'transition'       : 'transitionend'
+    }
+
+    for (var name in transEndEventNames) {
+      if (el.style[name] !== undefined) {
+        return { end: transEndEventNames[name] }
+      }
+    }
+
+    return false // explicit for ie8 (  ._.)
+  }
+
+  // http://blog.alexmaccaw.com/css-transitions
+  $.fn.emulateTransitionEnd = function (duration) {
+    var called = false, $el = this
+    $(this).one($.support.transition.end, function () { called = true })
+    var callback = function () { if (!called) $($el).trigger($.support.transition.end) }
+    setTimeout(callback, duration)
+    return this
+  }
+
+  $(function () {
+    $.support.transition = transitionEnd()
+  })
+
+}(jQuery);
+
+/* ========================================================================
+ * Bootstrap: alert.js v3.1.1
+ * http://getbootstrap.com/javascript/#alerts
+ * ========================================================================
+ * Copyright 2011-2014 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+  'use strict';
+
+  // ALERT CLASS DEFINITION
+  // ======================
+
+  var dismiss = '[data-dismiss="alert"]'
+  var Alert   = function (el) {
+    $(el).on('click', dismiss, this.close)
+  }
+
+  Alert.prototype.close = function (e) {
+    var $this    = $(this)
+    var selector = $this.attr('data-target')
+
+    if (!selector) {
+      selector = $this.attr('href')
+      selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
+    }
+
+    var $parent = $(selector)
+
+    if (e) e.preventDefault()
+
+    if (!$parent.length) {
+      $parent = $this.hasClass('alert') ? $this : $this.parent()
+    }
+
+    $parent.trigger(e = $.Event('close.bs.alert'))
+
+    if (e.isDefaultPrevented()) return
+
+    $parent.removeClass('in')
+
+    function removeElement() {
+      $parent.trigger('closed.bs.alert').remove()
+    }
+
+    $.support.transition && $parent.hasClass('fade') ?
+      $parent
+        .one($.support.transition.end, removeElement)
+        .emulateTransitionEnd(150) :
+      removeElement()
+  }
+
+
+  // ALERT PLUGIN DEFINITION
+  // =======================
+
+  var old = $.fn.alert
+
+  $.fn.alert = function (option) {
+    return this.each(function () {
+      var $this = $(this)
+      var data  = $this.data('bs.alert')
+
+      if (!data) $this.data('bs.alert', (data = new Alert(this)))
+      if (typeof option == 'string') data[option].call($this)
+    })
+  }
+
+  $.fn.alert.Constructor = Alert
+
+
+  // ALERT NO CONFLICT
+  // =================
+
+  $.fn.alert.noConflict = function () {
+    $.fn.alert = old
+    return this
+  }
+
+
+  // ALERT DATA-API
+  // ==============
+
+  $(document).on('click.bs.alert.data-api', dismiss, Alert.prototype.close)
+
+}(jQuery);
+
+/* ========================================================================
+ * Bootstrap: button.js v3.1.1
+ * http://getbootstrap.com/javascript/#buttons
+ * ========================================================================
+ * Copyright 2011-2014 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+  'use strict';
+
+  // BUTTON PUBLIC CLASS DEFINITION
+  // ==============================
+
+  var Button = function (element, options) {
+    this.$element  = $(element)
+    this.options   = $.extend({}, Button.DEFAULTS, options)
+    this.isLoading = false
+  }
+
+  Button.DEFAULTS = {
+    loadingText: 'loading...'
+  }
+
+  Button.prototype.setState = function (state) {
+    var d    = 'disabled'
+    var $el  = this.$element
+    var val  = $el.is('input') ? 'val' : 'html'
+    var data = $el.data()
+
+    state = state + 'Text'
+
+    if (!data.resetText) $el.data('resetText', $el[val]())
+
+    $el[val](data[state] || this.options[state])
+
+    // push to event loop to allow forms to submit
+    setTimeout($.proxy(function () {
+      if (state == 'loadingText') {
+        this.isLoading = true
+        $el.addClass(d).attr(d, d)
+      } else if (this.isLoading) {
+        this.isLoading = false
+        $el.removeClass(d).removeAttr(d)
+      }
+    }, this), 0)
+  }
+
+  Button.prototype.toggle = function () {
+    var changed = true
+    var $parent = this.$element.closest('[data-toggle="buttons"]')
+
+    if ($parent.length) {
+      var $input = this.$element.find('input')
+      if ($input.prop('type') == 'radio') {
+        if ($input.prop('checked') && this.$element.hasClass('active')) changed = false
+        else $parent.find('.active').removeClass('active')
+      }
+      if (changed) $input.prop('checked', !this.$element.hasClass('active')).trigger('change')
+    }
+
+    if (changed) this.$element.toggleClass('active')
+  }
+
+
+  // BUTTON PLUGIN DEFINITION
+  // ========================
+
+  var old = $.fn.button
+
+  $.fn.button = function (option) {
+    return this.each(function () {
+      var $this   = $(this)
+      var data    = $this.data('bs.button')
+      var options = typeof option == 'object' && option
+
+      if (!data) $this.data('bs.button', (data = new Button(this, options)))
+
+      if (option == 'toggle') data.toggle()
+      else if (option) data.setState(option)
+    })
+  }
+
+  $.fn.button.Constructor = Button
+
+
+  // BUTTON NO CONFLICT
+  // ==================
+
+  $.fn.button.noConflict = function () {
+    $.fn.button = old
+    return this
+  }
+
+
+  // BUTTON DATA-API
+  // ===============
+
+  $(document).on('click.bs.button.data-api', '[data-toggle^=button]', function (e) {
+    var $btn = $(e.target)
+    if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn')
+    $btn.button('toggle')
+    e.preventDefault()
+  })
+
+}(jQuery);
+
+/* ========================================================================
+ * Bootstrap: carousel.js v3.1.1
+ * http://getbootstrap.com/javascript/#carousel
+ * ========================================================================
+ * Copyright 2011-2014 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+  'use strict';
+
+  // CAROUSEL CLASS DEFINITION
+  // =========================
+
+  var Carousel = function (element, options) {
+    this.$element    = $(element)
+    this.$indicators = this.$element.find('.carousel-indicators')
+    this.options     = options
+    this.paused      =
+    this.sliding     =
+    this.interval    =
+    this.$active     =
+    this.$items      = null
+
+    this.options.pause == 'hover' && this.$element
+      .on('mouseenter', $.proxy(this.pause, this))
+      .on('mouseleave', $.proxy(this.cycle, this))
+  }
+
+  Carousel.DEFAULTS = {
+    interval: 5000,
+    pause: 'hover',
+    wrap: true
+  }
+
+  Carousel.prototype.cycle =  function (e) {
+    e || (this.paused = false)
+
+    this.interval && clearInterval(this.interval)
+
+    this.options.interval
+      && !this.paused
+      && (this.interval = setInterval($.proxy(this.next, this), this.options.interval))
+
+    return this
+  }
+
+  Carousel.prototype.getActiveIndex = function () {
+    this.$active = this.$element.find('.item.active')
+    this.$items  = this.$active.parent().children()
+
+    return this.$items.index(this.$active)
+  }
+
+  Carousel.prototype.to = function (pos) {
+    var that        = this
+    var activeIndex = this.getActiveIndex()
+
+    if (pos > (this.$items.length - 1) || pos < 0) return
+
+    if (this.sliding)       return this.$element.one('slid.bs.carousel', function () { that.to(pos) })
+    if (activeIndex == pos) return this.pause().cycle()
+
+    return this.slide(pos > activeIndex ? 'next' : 'prev', $(this.$items[pos]))
+  }
+
+  Carousel.prototype.pause = function (e) {
+    e || (this.paused = true)
+
+    if (this.$element.find('.next, .prev').length && $.support.transition) {
+      this.$element.trigger($.support.transition.end)
+      this.cycle(true)
+    }
+
+    this.interval = clearInterval(this.interval)
+
+    return this
+  }
+
+  Carousel.prototype.next = function () {
+    if (this.sliding) return
+    return this.slide('next')
+  }
+
+  Carousel.prototype.prev = function () {
+    if (this.sliding) return
+    return this.slide('prev')
+  }
+
+  Carousel.prototype.slide = function (type, next) {
+    var $active   = this.$element.find('.item.active')
+    var $next     = next || $active[type]()
+    var isCycling = this.interval
+    var direction = type == 'next' ? 'left' : 'right'
+    var fallback  = type == 'next' ? 'first' : 'last'
+    var that      = this
+
+    if (!$next.length) {
+      if (!this.options.wrap) return
+      $next = this.$element.find('.item')[fallback]()
+    }
+
+    if ($next.hasClass('active')) return this.sliding = false
+
+    var e = $.Event('slide.bs.carousel', { relatedTarget: $next[0], direction: direction })
+    this.$element.trigger(e)
+    if (e.isDefaultPrevented()) return
+
+    this.sliding = true
+
+    isCycling && this.pause()
+
+    if (this.$indicators.length) {
+      this.$indicators.find('.active').removeClass('active')
+      this.$element.one('slid.bs.carousel', function () {
+        var $nextIndicator = $(that.$indicators.children()[that.getActiveIndex()])
+        $nextIndicator && $nextIndicator.addClass('active')
+      })
+    }
+
+    if ($.support.transition && this.$element.hasClass('slide')) {
+      $next.addClass(type)
+      $next[0].offsetWidth // force reflow
+      $active.addClass(direction)
+      $next.addClass(direction)
+      $active
+        .one($.support.transition.end, function () {
+          $next.removeClass([type, direction].join(' ')).addClass('active')
+          $active.removeClass(['active', direction].join(' '))
+          that.sliding = false
+          setTimeout(function () { that.$element.trigger('slid.bs.carousel') }, 0)
+        })
+        .emulateTransitionEnd($active.css('transition-duration').slice(0, -1) * 1000)
+    } else {
+      $active.removeClass('active')
+      $next.addClass('active')
+      this.sliding = false
+      this.$element.trigger('slid.bs.carousel')
+    }
+
+    isCycling && this.cycle()
+
+    return this
+  }
+
+
+  // CAROUSEL PLUGIN DEFINITION
+  // ==========================
+
+  var old = $.fn.carousel
+
+  $.fn.carousel = function (option) {
+    return this.each(function () {
+      var $this   = $(this)
+      var data    = $this.data('bs.carousel')
+      var options = $.extend({}, Carousel.DEFAULTS, $this.data(), typeof option == 'object' && option)
+      var action  = typeof option == 'string' ? option : options.slide
+
+      if (!data) $this.data('bs.carousel', (data = new Carousel(this, options)))
+      if (typeof option == 'number') data.to(option)
+      else if (action) data[action]()
+      else if (options.interval) data.pause().cycle()
+    })
+  }
+
+  $.fn.carousel.Constructor = Carousel
+
+
+  // CAROUSEL NO CONFLICT
+  // ====================
+
+  $.fn.carousel.noConflict = function () {
+    $.fn.carousel = old
+    return this
+  }
+
+
+  // CAROUSEL DATA-API
+  // =================
+
+  $(document).on('click.bs.carousel.data-api', '[data-slide], [data-slide-to]', function (e) {
+    var $this   = $(this), href
+    var $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
+    var options = $.extend({}, $target.data(), $this.data())
+    var slideIndex = $this.attr('data-slide-to')
+    if (slideIndex) options.interval = false
+
+    $target.carousel(options)
+
+    if (slideIndex = $this.attr('data-slide-to')) {
+      $target.data('bs.carousel').to(slideIndex)
+    }
+
+    e.preventDefault()
+  })
+
+  $(window).on('load', function () {
+    $('[data-ride="carousel"]').each(function () {
+      var $carousel = $(this)
+      $carousel.carousel($carousel.data())
+    })
+  })
+
+}(jQuery);
+
+/* ========================================================================
+ * Bootstrap: collapse.js v3.1.1
+ * http://getbootstrap.com/javascript/#collapse
+ * ========================================================================
+ * Copyright 2011-2014 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+  'use strict';
+
+  // COLLAPSE PUBLIC CLASS DEFINITION
+  // ================================
+
+  var Collapse = function (element, options) {
+    this.$element      = $(element)
+    this.options       = $.extend({}, Collapse.DEFAULTS, options)
+    this.transitioning = null
+
+    if (this.options.parent) this.$parent = $(this.options.parent)
+    if (this.options.toggle) this.toggle()
+  }
+
+  Collapse.DEFAULTS = {
+    toggle: true
+  }
+
+  Collapse.prototype.dimension = function () {
+    var hasWidth = this.$element.hasClass('width')
+    return hasWidth ? 'width' : 'height'
+  }
+
+  Collapse.prototype.show = function () {
+    if (this.transitioning || this.$element.hasClass('in')) return
+
+    var startEvent = $.Event('show.bs.collapse')
+    this.$element.trigger(startEvent)
+    if (startEvent.isDefaultPrevented()) return
+
+    var actives = this.$parent && this.$parent.find('> .panel > .in')
+
+    if (actives && actives.length) {
+      var hasData = actives.data('bs.collapse')
+      if (hasData && hasData.transitioning) return
+      actives.collapse('hide')
+      hasData || actives.data('bs.collapse', null)
+    }
+
+    var dimension = this.dimension()
+
+    this.$element
+      .removeClass('collapse')
+      .addClass('collapsing')
+      [dimension](0)
+
+    this.transitioning = 1
+
+    var complete = function () {
+      this.$element
+        .removeClass('collapsing')
+        .addClass('collapse in')
+        [dimension]('auto')
+      this.transitioning = 0
+      this.$element.trigger('shown.bs.collapse')
+    }
+
+    if (!$.support.transition) return complete.call(this)
+
+    var scrollSize = $.camelCase(['scroll', dimension].join('-'))
+
+    this.$element
+      .one($.support.transition.end, $.proxy(complete, this))
+      .emulateTransitionEnd(350)
+      [dimension](this.$element[0][scrollSize])
+  }
+
+  Collapse.prototype.hide = function () {
+    if (this.transitioning || !this.$element.hasClass('in')) return
+
+    var startEvent = $.Event('hide.bs.collapse')
+    this.$element.trigger(startEvent)
+    if (startEvent.isDefaultPrevented()) return
+
+    var dimension = this.dimension()
+
+    this.$element
+      [dimension](this.$element[dimension]())
+      [0].offsetHeight
+
+    this.$element
+      .addClass('collapsing')
+      .removeClass('collapse')
+      .removeClass('in')
+
+    this.transitioning = 1
+
+    var complete = function () {
+      this.transitioning = 0
+      this.$element
+        .trigger('hidden.bs.collapse')
+        .removeClass('collapsing')
+        .addClass('collapse')
+    }
+
+    if (!$.support.transition) return complete.call(this)
+
+    this.$element
+      [dimension](0)
+      .one($.support.transition.end, $.proxy(complete, this))
+      .emulateTransitionEnd(350)
+  }
+
+  Collapse.prototype.toggle = function () {
+    this[this.$element.hasClass('in') ? 'hide' : 'show']()
+  }
+
+
+  // COLLAPSE PLUGIN DEFINITION
+  // ==========================
+
+  var old = $.fn.collapse
+
+  $.fn.collapse = function (option) {
+    return this.each(function () {
+      var $this   = $(this)
+      var data    = $this.data('bs.collapse')
+      var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option)
+
+      if (!data && options.toggle && option == 'show') option = !option
+      if (!data) $this.data('bs.collapse', (data = new Collapse(this, options)))
+      if (typeof option == 'string') data[option]()
+    })
+  }
+
+  $.fn.collapse.Constructor = Collapse
+
+
+  // COLLAPSE NO CONFLICT
+  // ====================
+
+  $.fn.collapse.noConflict = function () {
+    $.fn.collapse = old
+    return this
+  }
+
+
+  // COLLAPSE DATA-API
+  // =================
+
+  $(document).on('click.bs.collapse.data-api', '[data-toggle=collapse]', function (e) {
+    var $this   = $(this), href
+    var target  = $this.attr('data-target')
+        || e.preventDefault()
+        || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') //strip for ie7
+    var $target = $(target)
+    var data    = $target.data('bs.collapse')
+    var option  = data ? 'toggle' : $this.data()
+    var parent  = $this.attr('data-parent')
+    var $parent = parent && $(parent)
+
+    if (!data || !data.transitioning) {
+      if ($parent) $parent.find('[data-toggle=collapse][data-parent="' + parent + '"]').not($this).addClass('collapsed')
+      $this[$target.hasClass('in') ? 'addClass' : 'removeClass']('collapsed')
+    }
+
+    $target.collapse(option)
+  })
+
+}(jQuery);
+
+/* ========================================================================
+ * Bootstrap: dropdown.js v3.1.1
+ * http://getbootstrap.com/javascript/#dropdowns
+ * ========================================================================
+ * Copyright 2011-2014 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+  'use strict';
+
+  // DROPDOWN CLASS DEFINITION
+  // =========================
+
+  var backdrop = '.dropdown-backdrop'
+  var toggle   = '[data-toggle=dropdown]'
+  var Dropdown = function (element) {
+    $(element).on('click.bs.dropdown', this.toggle)
+  }
+
+  Dropdown.prototype.toggle = function (e) {
+    var $this = $(this)
+
+    if ($this.is('.disabled, :disabled')) return
+
+    var $parent  = getParent($this)
+    var isActive = $parent.hasClass('open')
+
+    clearMenus()
+
+    if (!isActive) {
+      if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) {
+        // if mobile we use a backdrop because click events don't delegate
+        $('<div class="dropdown-backdrop"/>').insertAfter($(this)).on('click', clearMenus)
+      }
+
+      var relatedTarget = { relatedTarget: this }
+      $parent.trigger(e = $.Event('show.bs.dropdown', relatedTarget))
+
+      if (e.isDefaultPrevented()) return
+
+      $parent
+        .toggleClass('open')
+        .trigger('shown.bs.dropdown', relatedTarget)
+
+      $this.focus()
+    }
+
+    return false
+  }
+
+  Dropdown.prototype.keydown = function (e) {
+    if (!/(38|40|27)/.test(e.keyCode)) return
+
+    var $this = $(this)
+
+    e.preventDefault()
+    e.stopPropagation()
+
+    if ($this.is('.disabled, :disabled')) return
+
+    var $parent  = getParent($this)
+    var isActive = $parent.hasClass('open')
+
+    if (!isActive || (isActive && e.keyCode == 27)) {
+      if (e.which == 27) $parent.find(toggle).focus()
+      return $this.click()
+    }
+
+    var desc = ' li:not(.divider):visible a'
+    var $items = $parent.find('[role=menu]' + desc + ', [role=listbox]' + desc)
+
+    if (!$items.length) return
+
+    var index = $items.index($items.filter(':focus'))
+
+    if (e.keyCode == 38 && index > 0)                 index--                        // up
+    if (e.keyCode == 40 && index < $items.length - 1) index++                        // down
+    if (!~index)                                      index = 0
+
+    $items.eq(index).focus()
+  }
+
+  function clearMenus(e) {
+    $(backdrop).remove()
+    $(toggle).each(function () {
+      var $parent = getParent($(this))
+      var relatedTarget = { relatedTarget: this }
+      if (!$parent.hasClass('open')) return
+      $parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
+      if (e.isDefaultPrevented()) return
+      $parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
+    })
+  }
+
+  function getParent($this) {
+    var selector = $this.attr('data-target')
+
+    if (!selector) {
+      selector = $this.attr('href')
+      selector = selector && /#[A-Za-z]/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
+    }
+
+    var $parent = selector && $(selector)
+
+    return $parent && $parent.length ? $parent : $this.parent()
+  }
+
+
+  // DROPDOWN PLUGIN DEFINITION
+  // ==========================
+
+  var old = $.fn.dropdown
+
+  $.fn.dropdown = function (option) {
+    return this.each(function () {
+      var $this = $(this)
+      var data  = $this.data('bs.dropdown')
+
+      if (!data) $this.data('bs.dropdown', (data = new Dropdown(this)))
+      if (typeof option == 'string') data[option].call($this)
+    })
+  }
+
+  $.fn.dropdown.Constructor = Dropdown
+
+
+  // DROPDOWN NO CONFLICT
+  // ====================
+
+  $.fn.dropdown.noConflict = function () {
+    $.fn.dropdown = old
+    return this
+  }
+
+
+  // APPLY TO STANDARD DROPDOWN ELEMENTS
+  // ===================================
+
+  $(document)
+    .on('click.bs.dropdown.data-api', clearMenus)
+    .on('click.bs.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() })
+    .on('click.bs.dropdown.data-api', toggle, Dropdown.prototype.toggle)
+    .on('keydown.bs.dropdown.data-api', toggle + ', [role=menu], [role=listbox]', Dropdown.prototype.keydown)
+
+}(jQuery);
+
+/* ========================================================================
+ * Bootstrap: modal.js v3.1.1
+ * http://getbootstrap.com/javascript/#modals
+ * ========================================================================
+ * Copyright 2011-2014 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+  'use strict';
+
+  // MODAL CLASS DEFINITION
+  // ======================
+
+  var Modal = function (element, options) {
+    this.options   = options
+    this.$element  = $(element)
+    this.$backdrop =
+    this.isShown   = null
+
+    if (this.options.remote) {
+      this.$element
+        .find('.modal-content')
+        .load(this.options.remote, $.proxy(function () {
+          this.$element.trigger('loaded.bs.modal')
+        }, this))
+    }
+  }
+
+  Modal.DEFAULTS = {
+    backdrop: true,
+    keyboard: true,
+    show: true
+  }
+
+  Modal.prototype.toggle = function (_relatedTarget) {
+    return this[!this.isShown ? 'show' : 'hide'](_relatedTarget)
+  }
+
+  Modal.prototype.show = function (_relatedTarget) {
+    var that = this
+    var e    = $.Event('show.bs.modal', { relatedTarget: _relatedTarget })
+
+    this.$element.trigger(e)
+
+    if (this.isShown || e.isDefaultPrevented()) return
+
+    this.isShown = true
+
+    this.escape()
+
+    this.$element.on('click.dismiss.bs.modal', '[data-dismiss="modal"]', $.proxy(this.hide, this))
+
+    this.backdrop(function () {
+      var transition = $.support.transition && that.$element.hasClass('fade')
+
+      if (!that.$element.parent().length) {
+        that.$element.appendTo(document.body) // don't move modals dom position
+      }
+
+      that.$element
+        .show()
+        .scrollTop(0)
+
+      if (transition) {
+        that.$element[0].offsetWidth // force reflow
+      }
+
+      that.$element
+        .addClass('in')
+        .attr('aria-hidden', false)
+
+      that.enforceFocus()
+
+      var e = $.Event('shown.bs.modal', { relatedTarget: _relatedTarget })
+
+      transition ?
+        that.$element.find('.modal-dialog') // wait for modal to slide in
+          .one($.support.transition.end, function () {
+            that.$element.focus().trigger(e)
+          })
+          .emulateTransitionEnd(300) :
+        that.$element.focus().trigger(e)
+    })
+  }
+
+  Modal.prototype.hide = function (e) {
+    if (e) e.preventDefault()
+
+    e = $.Event('hide.bs.modal')
+
+    this.$element.trigger(e)
+
+    if (!this.isShown || e.isDefaultPrevented()) return
+
+    this.isShown = false
+
+    this.escape()
+
+    $(document).off('focusin.bs.modal')
+
+    this.$element
+      .removeClass('in')
+      .attr('aria-hidden', true)
+      .off('click.dismiss.bs.modal')
+
+    $.support.transition && this.$element.hasClass('fade') ?
+      this.$element
+        .one($.support.transition.end, $.proxy(this.hideModal, this))
+        .emulateTransitionEnd(300) :
+      this.hideModal()
+  }
+
+  Modal.prototype.enforceFocus = function () {
+    $(document)
+      .off('focusin.bs.modal') // guard against infinite focus loop
+      .on('focusin.bs.modal', $.proxy(function (e) {
+        if (this.$element[0] !== e.target && !this.$element.has(e.target).length) {
+          this.$element.focus()
+        }
+      }, this))
+  }
+
+  Modal.prototype.escape = function () {
+    if (this.isShown && this.options.keyboard) {
+      this.$element.on('keyup.dismiss.bs.modal', $.proxy(function (e) {
+        e.which == 27 && this.hide()
+      }, this))
+    } else if (!this.isShown) {
+      this.$element.off('keyup.dismiss.bs.modal')
+    }
+  }
+
+  Modal.prototype.hideModal = function () {
+    var that = this
+    this.$element.hide()
+    this.backdrop(function () {
+      that.removeBackdrop()
+      that.$element.trigger('hidden.bs.modal')
+    })
+  }
+
+  Modal.prototype.removeBackdrop = function () {
+    this.$backdrop && this.$backdrop.remove()
+    this.$backdrop = null
+  }
+
+  Modal.prototype.backdrop = function (callback) {
+    var animate = this.$element.hasClass('fade') ? 'fade' : ''
+
+    if (this.isShown && this.options.backdrop) {
+      var doAnimate = $.support.transition && animate
+
+      this.$backdrop = $('<div class="modal-backdrop ' + animate + '" />')
+        .appendTo(document.body)
+
+      this.$element.on('click.dismiss.bs.modal', $.proxy(function (e) {
+        if (e.target !== e.currentTarget) return
+        this.options.backdrop == 'static'
+          ? this.$element[0].focus.call(this.$element[0])
+          : this.hide.call(this)
+      }, this))
+
+      if (doAnimate) this.$backdrop[0].offsetWidth // force reflow
+
+      this.$backdrop.addClass('in')
+
+      if (!callback) return
+
+      doAnimate ?
+        this.$backdrop
+          .one($.support.transition.end, callback)
+          .emulateTransitionEnd(150) :
+        callback()
+
+    } else if (!this.isShown && this.$backdrop) {
+      this.$backdrop.removeClass('in')
+
+      $.support.transition && this.$element.hasClass('fade') ?
+        this.$backdrop
+          .one($.support.transition.end, callback)
+          .emulateTransitionEnd(150) :
+        callback()
+
+    } else if (callback) {
+      callback()
+    }
+  }
+
+
+  // MODAL PLUGIN DEFINITION
+  // =======================
+
+  var old = $.fn.modal
+
+  $.fn.modal = function (option, _relatedTarget) {
+    return this.each(function () {
+      var $this   = $(this)
+      var data    = $this.data('bs.modal')
+      var options = $.extend({}, Modal.DEFAULTS, $this.data(), typeof option == 'object' && option)
+
+      if (!data) $this.data('bs.modal', (data = new Modal(this, options)))
+      if (typeof option == 'string') data[option](_relatedTarget)
+      else if (options.show) data.show(_relatedTarget)
+    })
+  }
+
+  $.fn.modal.Constructor = Modal
+
+
+  // MODAL NO CONFLICT
+  // =================
+
+  $.fn.modal.noConflict = function () {
+    $.fn.modal = old
+    return this
+  }
+
+
+  // MODAL DATA-API
+  // ==============
+
+  $(document).on('click.bs.modal.data-api', '[data-toggle="modal"]', function (e) {
+    var $this   = $(this)
+    var href    = $this.attr('href')
+    var $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))) //strip for ie7
+    var option  = $target.data('bs.modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data())
+
+    if ($this.is('a')) e.preventDefault()
+
+    $target
+      .modal(option, this)
+      .one('hide', function () {
+        $this.is(':visible') && $this.focus()
+      })
+  })
+
+  $(document)
+    .on('show.bs.modal', '.modal', function () { $(document.body).addClass('modal-open') })
+    .on('hidden.bs.modal', '.modal', function () { $(document.body).removeClass('modal-open') })
+
+}(jQuery);
+
+/* ========================================================================
+ * Bootstrap: tooltip.js v3.1.1
+ * http://getbootstrap.com/javascript/#tooltip
+ * Inspired by the original jQuery.tipsy by Jason Frame
+ * ========================================================================
+ * Copyright 2011-2014 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+  'use strict';
+
+  // TOOLTIP PUBLIC CLASS DEFINITION
+  // ===============================
+
+  var Tooltip = function (element, options) {
+    this.type       =
+    this.options    =
+    this.enabled    =
+    this.timeout    =
+    this.hoverState =
+    this.$element   = null
+
+    this.init('tooltip', element, options)
+  }
+
+  Tooltip.DEFAULTS = {
+    animation: true,
+    placement: 'top',
+    selector: false,
+    template: '<div class="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',
+    trigger: 'hover focus',
+    title: '',
+    delay: 0,
+    html: false,
+    container: false
+  }
+
+  Tooltip.prototype.init = function (type, element, options) {
+    this.enabled  = true
+    this.type     = type
+    this.$element = $(element)
+    this.options  = this.getOptions(options)
+
+    var triggers = this.options.trigger.split(' ')
+
+    for (var i = triggers.length; i--;) {
+      var trigger = triggers[i]
+
+      if (trigger == 'click') {
+        this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this))
+      } else if (trigger != 'manual') {
+        var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
+        var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
+
+        this.$element.on(eventIn  + '.' + this.type, this.options.selector, $.proxy(this.enter, this))
+        this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this))
+      }
+    }
+
+    this.options.selector ?
+      (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) :
+      this.fixTitle()
+  }
+
+  Tooltip.prototype.getDefaults = function () {
+    return Tooltip.DEFAULTS
+  }
+
+  Tooltip.prototype.getOptions = function (options) {
+    options = $.extend({}, this.getDefaults(), this.$element.data(), options)
+
+    if (options.delay && typeof options.delay == 'number') {
+      options.delay = {
+        show: options.delay,
+        hide: options.delay
+      }
+    }
+
+    return options
+  }
+
+  Tooltip.prototype.getDelegateOptions = function () {
+    var options  = {}
+    var defaults = this.getDefaults()
+
+    this._options && $.each(this._options, function (key, value) {
+      if (defaults[key] != value) options[key] = value
+    })
+
+    return options
+  }
+
+  Tooltip.prototype.enter = function (obj) {
+    var self = obj instanceof this.constructor ?
+      obj : $(obj.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type)
+
+    clearTimeout(self.timeout)
+
+    self.hoverState = 'in'
+
+    if (!self.options.delay || !self.options.delay.show) return self.show()
+
+    self.timeout = setTimeout(function () {
+      if (self.hoverState == 'in') self.show()
+    }, self.options.delay.show)
+  }
+
+  Tooltip.prototype.leave = function (obj) {
+    var self = obj instanceof this.constructor ?
+      obj : $(obj.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type)
+
+    clearTimeout(self.timeout)
+
+    self.hoverState = 'out'
+
+    if (!self.options.delay || !self.options.delay.hide) return self.hide()
+
+    self.timeout = setTimeout(function () {
+      if (self.hoverState == 'out') self.hide()
+    }, self.options.delay.hide)
+  }
+
+  Tooltip.prototype.show = function () {
+    var e = $.Event('show.bs.' + this.type)
+
+    if (this.hasContent() && this.enabled) {
+      this.$element.trigger(e)
+
+      if (e.isDefaultPrevented()) return
+      var that = this;
+
+      var $tip = this.tip()
+
+      this.setContent()
+
+      if (this.options.animation) $tip.addClass('fade')
+
+      var placement = typeof this.options.placement == 'function' ?
+        this.options.placement.call(this, $tip[0], this.$element[0]) :
+        this.options.placement
+
+      var autoToken = /\s?auto?\s?/i
+      var autoPlace = autoToken.test(placement)
+      if (autoPlace) placement = placement.replace(autoToken, '') || 'top'
+
+      $tip
+        .detach()
+        .css({ top: 0, left: 0, display: 'block' })
+        .addClass(placement)
+
+      this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element)
+
+      var pos          = this.getPosition()
+      var actualWidth  = $tip[0].offsetWidth
+      var actualHeight = $tip[0].offsetHeight
+
+      if (autoPlace) {
+        var $parent = this.$element.parent()
+
+        var orgPlacement = placement
+        var docScroll    = document.documentElement.scrollTop || document.body.scrollTop
+        var parentWidth  = this.options.container == 'body' ? window.innerWidth  : $parent.outerWidth()
+        var parentHeight = this.options.container == 'body' ? window.innerHeight : $parent.outerHeight()
+        var parentLeft   = this.options.container == 'body' ? 0 : $parent.offset().left
+
+        placement = placement == 'bottom' && pos.top   + pos.height  + actualHeight - docScroll > parentHeight  ? 'top'    :
+                    placement == 'top'    && pos.top   - docScroll   - actualHeight < 0                         ? 'bottom' :
+                    placement == 'right'  && pos.right + actualWidth > parentWidth                              ? 'left'   :
+                    placement == 'left'   && pos.left  - actualWidth < parentLeft                               ? 'right'  :
+                    placement
+
+        $tip
+          .removeClass(orgPlacement)
+          .addClass(placement)
+      }
+
+      var calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight)
+
+      this.applyPlacement(calculatedOffset, placement)
+      this.hoverState = null
+
+      var complete = function() {
+        that.$element.trigger('shown.bs.' + that.type)
+      }
+
+      $.support.transition && this.$tip.hasClass('fade') ?
+        $tip
+          .one($.support.transition.end, complete)
+          .emulateTransitionEnd(150) :
+        complete()
+    }
+  }
+
+  Tooltip.prototype.applyPlacement = function (offset, placement) {
+    var replace
+    var $tip   = this.tip()
+    var width  = $tip[0].offsetWidth
+    var height = $tip[0].offsetHeight
+
+    // manually read margins because getBoundingClientRect includes difference
+    var marginTop = parseInt($tip.css('margin-top'), 10)
+    var marginLeft = parseInt($tip.css('margin-left'), 10)
+
+    // we must check for NaN for ie 8/9
+    if (isNaN(marginTop))  marginTop  = 0
+    if (isNaN(marginLeft)) marginLeft = 0
+
+    offset.top  = offset.top  + marginTop
+    offset.left = offset.left + marginLeft
+
+    // $.fn.offset doesn't round pixel values
+    // so we use setOffset directly with our own function B-0
+    $.offset.setOffset($tip[0], $.extend({
+      using: function (props) {
+        $tip.css({
+          top: Math.round(props.top),
+          left: Math.round(props.left)
+        })
+      }
+    }, offset), 0)
+
+    $tip.addClass('in')
+
+    // check to see if placing tip in new offset caused the tip to resize itself
+    var actualWidth  = $tip[0].offsetWidth
+    var actualHeight = $tip[0].offsetHeight
+
+    if (placement == 'top' && actualHeight != height) {
+      replace = true
+      offset.top = offset.top + height - actualHeight
+    }
+
+    if (/bottom|top/.test(placement)) {
+      var delta = 0
+
+      if (offset.left < 0) {
+        delta       = offset.left * -2
+        offset.left = 0
+
+        $tip.offset(offset)
+
+        actualWidth  = $tip[0].offsetWidth
+        actualHeight = $tip[0].offsetHeight
+      }
+
+      this.replaceArrow(delta - width + actualWidth, actualWidth, 'left')
+    } else {
+      this.replaceArrow(actualHeight - height, actualHeight, 'top')
+    }
+
+    if (replace) $tip.offset(offset)
+  }
+
+  Tooltip.prototype.replaceArrow = function (delta, dimension, position) {
+    this.arrow().css(position, delta ? (50 * (1 - delta / dimension) + '%') : '')
+  }
+
+  Tooltip.prototype.setContent = function () {
+    var $tip  = this.tip()
+    var title = this.getTitle()
+
+    $tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title)
+    $tip.removeClass('fade in top bottom left right')
+  }
+
+  Tooltip.prototype.hide = function () {
+    var that = this
+    var $tip = this.tip()
+    var e    = $.Event('hide.bs.' + this.type)
+
+    function complete() {
+      if (that.hoverState != 'in') $tip.detach()
+      that.$element.trigger('hidden.bs.' + that.type)
+    }
+
+    this.$element.trigger(e)
+
+    if (e.isDefaultPrevented()) return
+
+    $tip.removeClass('in')
+
+    $.support.transition && this.$tip.hasClass('fade') ?
+      $tip
+        .one($.support.transition.end, complete)
+        .emulateTransitionEnd(150) :
+      complete()
+
+    this.hoverState = null
+
+    return this
+  }
+
+  Tooltip.prototype.fixTitle = function () {
+    var $e = this.$element
+    if ($e.attr('title') || typeof($e.attr('data-original-title')) != 'string') {
+      $e.attr('data-original-title', $e.attr('title') || '').attr('title', '')
+    }
+  }
+
+  Tooltip.prototype.hasContent = function () {
+    return this.getTitle()
+  }
+
+  Tooltip.prototype.getPosition = function () {
+    var el = this.$element[0]
+    return $.extend({}, (typeof el.getBoundingClientRect == 'function') ? el.getBoundingClientRect() : {
+      width: el.offsetWidth,
+      height: el.offsetHeight
+    }, this.$element.offset())
+  }
+
+  Tooltip.prototype.getCalculatedOffset = function (placement, pos, actualWidth, actualHeight) {
+    return placement == 'bottom' ? { top: pos.top + pos.height,   left: pos.left + pos.width / 2 - actualWidth / 2  } :
+           placement == 'top'    ? { top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2  } :
+           placement == 'left'   ? { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth } :
+        /* placement == 'right' */ { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width   }
+  }
+
+  Tooltip.prototype.getTitle = function () {
+    var title
+    var $e = this.$element
+    var o  = this.options
+
+    title = $e.attr('data-original-title')
+      || (typeof o.title == 'function' ? o.title.call($e[0]) :  o.title)
+
+    return title
+  }
+
+  Tooltip.prototype.tip = function () {
+    return this.$tip = this.$tip || $(this.options.template)
+  }
+
+  Tooltip.prototype.arrow = function () {
+    return this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow')
+  }
+
+  Tooltip.prototype.validate = function () {
+    if (!this.$element[0].parentNode) {
+      this.hide()
+      this.$element = null
+      this.options  = null
+    }
+  }
+
+  Tooltip.prototype.enable = function () {
+    this.enabled = true
+  }
+
+  Tooltip.prototype.disable = function () {
+    this.enabled = false
+  }
+
+  Tooltip.prototype.toggleEnabled = function () {
+    this.enabled = !this.enabled
+  }
+
+  Tooltip.prototype.toggle = function (e) {
+    var self = e ? $(e.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type) : this
+    self.tip().hasClass('in') ? self.leave(self) : self.enter(self)
+  }
+
+  Tooltip.prototype.destroy = function () {
+    clearTimeout(this.timeout)
+    this.hide().$element.off('.' + this.type).removeData('bs.' + this.type)
+  }
+
+
+  // TOOLTIP PLUGIN DEFINITION
+  // =========================
+
+  var old = $.fn.tooltip
+
+  $.fn.tooltip = function (option) {
+    return this.each(function () {
+      var $this   = $(this)
+      var data    = $this.data('bs.tooltip')
+      var options = typeof option == 'object' && option
+
+      if (!data && option == 'destroy') return
+      if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options)))
+      if (typeof option == 'string') data[option]()
+    })
+  }
+
+  $.fn.tooltip.Constructor = Tooltip
+
+
+  // TOOLTIP NO CONFLICT
+  // ===================
+
+  $.fn.tooltip.noConflict = function () {
+    $.fn.tooltip = old
+    return this
+  }
+
+}(jQuery);
+
+/* ========================================================================
+ * Bootstrap: popover.js v3.1.1
+ * http://getbootstrap.com/javascript/#popovers
+ * ========================================================================
+ * Copyright 2011-2014 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+  'use strict';
+
+  // POPOVER PUBLIC CLASS DEFINITION
+  // ===============================
+
+  var Popover = function (element, options) {
+    this.init('popover', element, options)
+  }
+
+  if (!$.fn.tooltip) throw new Error('Popover requires tooltip.js')
+
+  Popover.DEFAULTS = $.extend({}, $.fn.tooltip.Constructor.DEFAULTS, {
+    placement: 'right',
+    trigger: 'click',
+    content: '',
+    template: '<div class="popover"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'
+  })
+
+
+  // NOTE: POPOVER EXTENDS tooltip.js
+  // ================================
+
+  Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype)
+
+  Popover.prototype.constructor = Popover
+
+  Popover.prototype.getDefaults = function () {
+    return Popover.DEFAULTS
+  }
+
+  Popover.prototype.setContent = function () {
+    var $tip    = this.tip()
+    var title   = this.getTitle()
+    var content = this.getContent()
+
+    $tip.find('.popover-title')[this.options.html ? 'html' : 'text'](title)
+    $tip.find('.popover-content')[ // we use append for html objects to maintain js events
+      this.options.html ? (typeof content == 'string' ? 'html' : 'append') : 'text'
+    ](content)
+
+    $tip.removeClass('fade top bottom left right in')
+
+    // IE8 doesn't accept hiding via the `:empty` pseudo selector, we have to do
+    // this manually by checking the contents.
+    if (!$tip.find('.popover-title').html()) $tip.find('.popover-title').hide()
+  }
+
+  Popover.prototype.hasContent = function () {
+    return this.getTitle() || this.getContent()
+  }
+
+  Popover.prototype.getContent = function () {
+    var $e = this.$element
+    var o  = this.options
+
+    return $e.attr('data-content')
+      || (typeof o.content == 'function' ?
+            o.content.call($e[0]) :
+            o.content)
+  }
+
+  Popover.prototype.arrow = function () {
+    return this.$arrow = this.$arrow || this.tip().find('.arrow')
+  }
+
+  Popover.prototype.tip = function () {
+    if (!this.$tip) this.$tip = $(this.options.template)
+    return this.$tip
+  }
+
+
+  // POPOVER PLUGIN DEFINITION
+  // =========================
+
+  var old = $.fn.popover
+
+  $.fn.popover = function (option) {
+    return this.each(function () {
+      var $this   = $(this)
+      var data    = $this.data('bs.popover')
+      var options = typeof option == 'object' && option
+
+      if (!data && option == 'destroy') return
+      if (!data) $this.data('bs.popover', (data = new Popover(this, options)))
+      if (typeof option == 'string') data[option]()
+    })
+  }
+
+  $.fn.popover.Constructor = Popover
+
+
+  // POPOVER NO CONFLICT
+  // ===================
+
+  $.fn.popover.noConflict = function () {
+    $.fn.popover = old
+    return this
+  }
+
+}(jQuery);
+
+/* ========================================================================
+ * Bootstrap: scrollspy.js v3.1.1
+ * http://getbootstrap.com/javascript/#scrollspy
+ * ========================================================================
+ * Copyright 2011-2014 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+  'use strict';
+
+  // SCROLLSPY CLASS DEFINITION
+  // ==========================
+
+  function ScrollSpy(element, options) {
+    var href
+    var process  = $.proxy(this.process, this)
+
+    this.$element       = $(element).is('body') ? $(window) : $(element)
+    this.$body          = $('body')
+    this.$scrollElement = this.$element.on('scroll.bs.scroll-spy.data-api', process)
+    this.options        = $.extend({}, ScrollSpy.DEFAULTS, options)
+    this.selector       = (this.options.target
+      || ((href = $(element).attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
+      || '') + ' .nav li > a'
+    this.offsets        = $([])
+    this.targets        = $([])
+    this.activeTarget   = null
+
+    this.refresh()
+    this.process()
+  }
+
+  ScrollSpy.DEFAULTS = {
+    offset: 10
+  }
+
+  ScrollSpy.prototype.refresh = function () {
+    var offsetMethod = this.$element[0] == window ? 'offset' : 'position'
+
+    this.offsets = $([])
+    this.targets = $([])
+
+    var self     = this
+    var $targets = this.$body
+      .find(this.selector)
+      .map(function () {
+        var $el   = $(this)
+        var href  = $el.data('target') || $el.attr('href')
+        var $href = /^#./.test(href) && $(href)
+
+        return ($href
+          && $href.length
+          && $href.is(':visible')
+          && [[ $href[offsetMethod]().top + (!$.isWindow(self.$scrollElement.get(0)) && self.$scrollElement.scrollTop()), href ]]) || null
+      })
+      .sort(function (a, b) { return a[0] - b[0] })
+      .each(function () {
+        self.offsets.push(this[0])
+        self.targets.push(this[1])
+      })
+  }
+
+  ScrollSpy.prototype.process = function () {
+    var scrollTop    = this.$scrollElement.scrollTop() + this.options.offset
+    var scrollHeight = this.$scrollElement[0].scrollHeight || this.$body[0].scrollHeight
+    var maxScroll    = scrollHeight - this.$scrollElement.height()
+    var offsets      = this.offsets
+    var targets      = this.targets
+    var activeTarget = this.activeTarget
+    var i
+
+    if (scrollTop >= maxScroll) {
+      return activeTarget != (i = targets.last()[0]) && this.activate(i)
+    }
+
+    if (activeTarget && scrollTop <= offsets[0]) {
+      return activeTarget != (i = targets[0]) && this.activate(i)
+    }
+
+    for (i = offsets.length; i--;) {
+      activeTarget != targets[i]
+        && scrollTop >= offsets[i]
+        && (!offsets[i + 1] || scrollTop <= offsets[i + 1])
+        && this.activate( targets[i] )
+    }
+  }
+
+  ScrollSpy.prototype.activate = function (target) {
+    this.activeTarget = target
+
+    $(this.selector)
+      .parentsUntil(this.options.target, '.active')
+      .removeClass('active')
+
+    var selector = this.selector +
+        '[data-target="' + target + '"],' +
+        this.selector + '[href="' + target + '"]'
+
+    var active = $(selector)
+      .parents('li')
+      .addClass('active')
+
+    if (active.parent('.dropdown-menu').length) {
+      active = active
+        .closest('li.dropdown')
+        .addClass('active')
+    }
+
+    active.trigger('activate.bs.scrollspy')
+  }
+
+
+  // SCROLLSPY PLUGIN DEFINITION
+  // ===========================
+
+  var old = $.fn.scrollspy
+
+  $.fn.scrollspy = function (option) {
+    return this.each(function () {
+      var $this   = $(this)
+      var data    = $this.data('bs.scrollspy')
+      var options = typeof option == 'object' && option
+
+      if (!data) $this.data('bs.scrollspy', (data = new ScrollSpy(this, options)))
+      if (typeof option == 'string') data[option]()
+    })
+  }
+
+  $.fn.scrollspy.Constructor = ScrollSpy
+
+
+  // SCROLLSPY NO CONFLICT
+  // =====================
+
+  $.fn.scrollspy.noConflict = function () {
+    $.fn.scrollspy = old
+    return this
+  }
+
+
+  // SCROLLSPY DATA-API
+  // ==================
+
+  $(window).on('load', function () {
+    $('[data-spy="scroll"]').each(function () {
+      var $spy = $(this)
+      $spy.scrollspy($spy.data())
+    })
+  })
+
+}(jQuery);
+
+/* ========================================================================
+ * Bootstrap: tab.js v3.1.1
+ * http://getbootstrap.com/javascript/#tabs
+ * ========================================================================
+ * Copyright 2011-2014 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+  'use strict';
+
+  // TAB CLASS DEFINITION
+  // ====================
+
+  var Tab = function (element) {
+    this.element = $(element)
+  }
+
+  Tab.prototype.show = function () {
+    var $this    = this.element
+    var $ul      = $this.closest('ul:not(.dropdown-menu)')
+    var selector = $this.data('target')
+
+    if (!selector) {
+      selector = $this.attr('href')
+      selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
+    }
+
+    if ($this.parent('li').hasClass('active')) return
+
+    var previous = $ul.find('.active:last a')[0]
+    var e        = $.Event('show.bs.tab', {
+      relatedTarget: previous
+    })
+
+    $this.trigger(e)
+
+    if (e.isDefaultPrevented()) return
+
+    var $target = $(selector)
+
+    this.activate($this.parent('li'), $ul)
+    this.activate($target, $target.parent(), function () {
+      $this.trigger({
+        type: 'shown.bs.tab',
+        relatedTarget: previous
+      })
+    })
+  }
+
+  Tab.prototype.activate = function (element, container, callback) {
+    var $active    = container.find('> .active')
+    var transition = callback
+      && $.support.transition
+      && $active.hasClass('fade')
+
+    function next() {
+      $active
+        .removeClass('active')
+        .find('> .dropdown-menu > .active')
+        .removeClass('active')
+
+      element.addClass('active')
+
+      if (transition) {
+        element[0].offsetWidth // reflow for transition
+        element.addClass('in')
+      } else {
+        element.removeClass('fade')
+      }
+
+      if (element.parent('.dropdown-menu')) {
+        element.closest('li.dropdown').addClass('active')
+      }
+
+      callback && callback()
+    }
+
+    transition ?
+      $active
+        .one($.support.transition.end, next)
+        .emulateTransitionEnd(150) :
+      next()
+
+    $active.removeClass('in')
+  }
+
+
+  // TAB PLUGIN DEFINITION
+  // =====================
+
+  var old = $.fn.tab
+
+  $.fn.tab = function ( option ) {
+    return this.each(function () {
+      var $this = $(this)
+      var data  = $this.data('bs.tab')
+
+      if (!data) $this.data('bs.tab', (data = new Tab(this)))
+      if (typeof option == 'string') data[option]()
+    })
+  }
+
+  $.fn.tab.Constructor = Tab
+
+
+  // TAB NO CONFLICT
+  // ===============
+
+  $.fn.tab.noConflict = function () {
+    $.fn.tab = old
+    return this
+  }
+
+
+  // TAB DATA-API
+  // ============
+
+  $(document).on('click.bs.tab.data-api', '[data-toggle="tab"], [data-toggle="pill"]', function (e) {
+    e.preventDefault()
+    $(this).tab('show')
+  })
+
+}(jQuery);
+
+/* ========================================================================
+ * Bootstrap: affix.js v3.1.1
+ * http://getbootstrap.com/javascript/#affix
+ * ========================================================================
+ * Copyright 2011-2014 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+  'use strict';
+
+  // AFFIX CLASS DEFINITION
+  // ======================
+
+  var Affix = function (element, options) {
+    this.options = $.extend({}, Affix.DEFAULTS, options)
+    this.$window = $(window)
+      .on('scroll.bs.affix.data-api', $.proxy(this.checkPosition, this))
+      .on('click.bs.affix.data-api',  $.proxy(this.checkPositionWithEventLoop, this))
+
+    this.$element     = $(element)
+    this.affixed      =
+    this.unpin        =
+    this.pinnedOffset = null
+
+    this.checkPosition()
+  }
+
+  Affix.RESET = 'affix affix-top affix-bottom'
+
+  Affix.DEFAULTS = {
+    offset: 0
+  }
+
+  Affix.prototype.getPinnedOffset = function () {
+    if (this.pinnedOffset) return this.pinnedOffset
+    this.$element.removeClass(Affix.RESET).addClass('affix')
+    var scrollTop = this.$window.scrollTop()
+    var position  = this.$element.offset()
+    return (this.pinnedOffset = position.top - scrollTop)
+  }
+
+  Affix.prototype.checkPositionWithEventLoop = function () {
+    setTimeout($.proxy(this.checkPosition, this), 1)
+  }
+
+  Affix.prototype.checkPosition = function () {
+    if (!this.$element.is(':visible')) return
+
+    var scrollHeight = $(document).height()
+    var scrollTop    = this.$window.scrollTop()
+    var position     = this.$element.offset()
+    var offset       = this.options.offset
+    var offsetTop    = offset.top
+    var offsetBottom = offset.bottom
+
+    if (this.affixed == 'top') position.top += scrollTop
+
+    if (typeof offset != 'object')         offsetBottom = offsetTop = offset
+    if (typeof offsetTop == 'function')    offsetTop    = offset.top(this.$element)
+    if (typeof offsetBottom == 'function') offsetBottom = offset.bottom(this.$element)
+
+    var affix = this.unpin   != null && (scrollTop + this.unpin <= position.top) ? false :
+                offsetBottom != null && (position.top + this.$element.height() >= scrollHeight - offsetBottom) ? 'bottom' :
+                offsetTop    != null && (scrollTop <= offsetTop) ? 'top' : false
+
+    if (this.affixed === affix) return
+    if (this.unpin) this.$element.css('top', '')
+
+    var affixType = 'affix' + (affix ? '-' + affix : '')
+    var e         = $.Event(affixType + '.bs.affix')
+
+    this.$element.trigger(e)
+
+    if (e.isDefaultPrevented()) return
+
+    this.affixed = affix
+    this.unpin = affix == 'bottom' ? this.getPinnedOffset() : null
+
+    this.$element
+      .removeClass(Affix.RESET)
+      .addClass(affixType)
+      .trigger($.Event(affixType.replace('affix', 'affixed')))
+
+    if (affix == 'bottom') {
+      this.$element.offset({ top: scrollHeight - offsetBottom - this.$element.height() })
+    }
+  }
+
+
+  // AFFIX PLUGIN DEFINITION
+  // =======================
+
+  var old = $.fn.affix
+
+  $.fn.affix = function (option) {
+    return this.each(function () {
+      var $this   = $(this)
+      var data    = $this.data('bs.affix')
+      var options = typeof option == 'object' && option
+
+      if (!data) $this.data('bs.affix', (data = new Affix(this, options)))
+      if (typeof option == 'string') data[option]()
+    })
+  }
+
+  $.fn.affix.Constructor = Affix
+
+
+  // AFFIX NO CONFLICT
+  // =================
+
+  $.fn.affix.noConflict = function () {
+    $.fn.affix = old
+    return this
+  }
+
+
+  // AFFIX DATA-API
+  // ==============
+
+  $(window).on('load', function () {
+    $('[data-spy="affix"]').each(function () {
+      var $spy = $(this)
+      var data = $spy.data()
+
+      data.offset = data.offset || {}
+
+      if (data.offsetBottom) data.offset.bottom = data.offsetBottom
+      if (data.offsetTop)    data.offset.top    = data.offsetTop
+
+      $spy.affix(data)
+    })
+  })
+
+}(jQuery);
diff --git a/pelican-plugins/bootstrap-rst/bootstrap/js/bootstrap.min.js b/pelican-plugins/bootstrap-rst/bootstrap/js/bootstrap.min.js
new file mode 100644
index 0000000000000000000000000000000000000000..b04a0e82fffee109e8cd5e48b3f3aa2a9b2aceff
--- /dev/null
+++ b/pelican-plugins/bootstrap-rst/bootstrap/js/bootstrap.min.js
@@ -0,0 +1,6 @@
+/*!
+ * Bootstrap v3.1.1 (http://getbootstrap.com)
+ * Copyright 2011-2014 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ */
+if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one(a.support.transition.end,function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b()})}(jQuery),+function(a){"use strict";var b='[data-dismiss="alert"]',c=function(c){a(c).on("click",b,this.close)};c.prototype.close=function(b){function c(){f.trigger("closed.bs.alert").remove()}var d=a(this),e=d.attr("data-target");e||(e=d.attr("href"),e=e&&e.replace(/.*(?=#[^\s]*$)/,""));var f=a(e);b&&b.preventDefault(),f.length||(f=d.hasClass("alert")?d:d.parent()),f.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(f.removeClass("in"),a.support.transition&&f.hasClass("fade")?f.one(a.support.transition.end,c).emulateTransitionEnd(150):c())};var d=a.fn.alert;a.fn.alert=function(b){return this.each(function(){var d=a(this),e=d.data("bs.alert");e||d.data("bs.alert",e=new c(this)),"string"==typeof b&&e[b].call(d)})},a.fn.alert.Constructor=c,a.fn.alert.noConflict=function(){return a.fn.alert=d,this},a(document).on("click.bs.alert.data-api",b,c.prototype.close)}(jQuery),+function(a){"use strict";var b=function(c,d){this.$element=a(c),this.options=a.extend({},b.DEFAULTS,d),this.isLoading=!1};b.DEFAULTS={loadingText:"loading..."},b.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",f.resetText||d.data("resetText",d[e]()),d[e](f[b]||this.options[b]),setTimeout(a.proxy(function(){"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c))},this),0)},b.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")&&(c.prop("checked")&&this.$element.hasClass("active")?a=!1:b.find(".active").removeClass("active")),a&&c.prop("checked",!this.$element.hasClass("active")).trigger("change")}a&&this.$element.toggleClass("active")};var c=a.fn.button;a.fn.button=function(c){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof c&&c;e||d.data("bs.button",e=new b(this,f)),"toggle"==c?e.toggle():c&&e.setState(c)})},a.fn.button.Constructor=b,a.fn.button.noConflict=function(){return a.fn.button=c,this},a(document).on("click.bs.button.data-api","[data-toggle^=button]",function(b){var c=a(b.target);c.hasClass("btn")||(c=c.closest(".btn")),c.button("toggle"),b.preventDefault()})}(jQuery),+function(a){"use strict";var b=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=this.sliding=this.interval=this.$active=this.$items=null,"hover"==this.options.pause&&this.$element.on("mouseenter",a.proxy(this.pause,this)).on("mouseleave",a.proxy(this.cycle,this))};b.DEFAULTS={interval:5e3,pause:"hover",wrap:!0},b.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},b.prototype.getActiveIndex=function(){return this.$active=this.$element.find(".item.active"),this.$items=this.$active.parent().children(),this.$items.index(this.$active)},b.prototype.to=function(b){var c=this,d=this.getActiveIndex();return b>this.$items.length-1||0>b?void 0:this.sliding?this.$element.one("slid.bs.carousel",function(){c.to(b)}):d==b?this.pause().cycle():this.slide(b>d?"next":"prev",a(this.$items[b]))},b.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},b.prototype.next=function(){return this.sliding?void 0:this.slide("next")},b.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},b.prototype.slide=function(b,c){var d=this.$element.find(".item.active"),e=c||d[b](),f=this.interval,g="next"==b?"left":"right",h="next"==b?"first":"last",i=this;if(!e.length){if(!this.options.wrap)return;e=this.$element.find(".item")[h]()}if(e.hasClass("active"))return this.sliding=!1;var j=a.Event("slide.bs.carousel",{relatedTarget:e[0],direction:g});return this.$element.trigger(j),j.isDefaultPrevented()?void 0:(this.sliding=!0,f&&this.pause(),this.$indicators.length&&(this.$indicators.find(".active").removeClass("active"),this.$element.one("slid.bs.carousel",function(){var b=a(i.$indicators.children()[i.getActiveIndex()]);b&&b.addClass("active")})),a.support.transition&&this.$element.hasClass("slide")?(e.addClass(b),e[0].offsetWidth,d.addClass(g),e.addClass(g),d.one(a.support.transition.end,function(){e.removeClass([b,g].join(" ")).addClass("active"),d.removeClass(["active",g].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger("slid.bs.carousel")},0)}).emulateTransitionEnd(1e3*d.css("transition-duration").slice(0,-1))):(d.removeClass("active"),e.addClass("active"),this.sliding=!1,this.$element.trigger("slid.bs.carousel")),f&&this.cycle(),this)};var c=a.fn.carousel;a.fn.carousel=function(c){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},b.DEFAULTS,d.data(),"object"==typeof c&&c),g="string"==typeof c?c:f.slide;e||d.data("bs.carousel",e=new b(this,f)),"number"==typeof c?e.to(c):g?e[g]():f.interval&&e.pause().cycle()})},a.fn.carousel.Constructor=b,a.fn.carousel.noConflict=function(){return a.fn.carousel=c,this},a(document).on("click.bs.carousel.data-api","[data-slide], [data-slide-to]",function(b){var c,d=a(this),e=a(d.attr("data-target")||(c=d.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"")),f=a.extend({},e.data(),d.data()),g=d.attr("data-slide-to");g&&(f.interval=!1),e.carousel(f),(g=d.attr("data-slide-to"))&&e.data("bs.carousel").to(g),b.preventDefault()}),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var b=a(this);b.carousel(b.data())})})}(jQuery),+function(a){"use strict";var b=function(c,d){this.$element=a(c),this.options=a.extend({},b.DEFAULTS,d),this.transitioning=null,this.options.parent&&(this.$parent=a(this.options.parent)),this.options.toggle&&this.toggle()};b.DEFAULTS={toggle:!0},b.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},b.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b=a.Event("show.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.$parent&&this.$parent.find("> .panel > .in");if(c&&c.length){var d=c.data("bs.collapse");if(d&&d.transitioning)return;c.collapse("hide"),d||c.data("bs.collapse",null)}var e=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[e](0),this.transitioning=1;var f=function(){this.$element.removeClass("collapsing").addClass("collapse in")[e]("auto"),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return f.call(this);var g=a.camelCase(["scroll",e].join("-"));this.$element.one(a.support.transition.end,a.proxy(f,this)).emulateTransitionEnd(350)[e](this.$element[0][g])}}},b.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse").removeClass("in"),this.transitioning=1;var d=function(){this.transitioning=0,this.$element.trigger("hidden.bs.collapse").removeClass("collapsing").addClass("collapse")};return a.support.transition?void this.$element[c](0).one(a.support.transition.end,a.proxy(d,this)).emulateTransitionEnd(350):d.call(this)}}},b.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()};var c=a.fn.collapse;a.fn.collapse=function(c){return this.each(function(){var d=a(this),e=d.data("bs.collapse"),f=a.extend({},b.DEFAULTS,d.data(),"object"==typeof c&&c);!e&&f.toggle&&"show"==c&&(c=!c),e||d.data("bs.collapse",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.collapse.Constructor=b,a.fn.collapse.noConflict=function(){return a.fn.collapse=c,this},a(document).on("click.bs.collapse.data-api","[data-toggle=collapse]",function(b){var c,d=a(this),e=d.attr("data-target")||b.preventDefault()||(c=d.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,""),f=a(e),g=f.data("bs.collapse"),h=g?"toggle":d.data(),i=d.attr("data-parent"),j=i&&a(i);g&&g.transitioning||(j&&j.find('[data-toggle=collapse][data-parent="'+i+'"]').not(d).addClass("collapsed"),d[f.hasClass("in")?"addClass":"removeClass"]("collapsed")),f.collapse(h)})}(jQuery),+function(a){"use strict";function b(b){a(d).remove(),a(e).each(function(){var d=c(a(this)),e={relatedTarget:this};d.hasClass("open")&&(d.trigger(b=a.Event("hide.bs.dropdown",e)),b.isDefaultPrevented()||d.removeClass("open").trigger("hidden.bs.dropdown",e))})}function c(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}var d=".dropdown-backdrop",e="[data-toggle=dropdown]",f=function(b){a(b).on("click.bs.dropdown",this.toggle)};f.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=c(e),g=f.hasClass("open");if(b(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a('<div class="dropdown-backdrop"/>').insertAfter(a(this)).on("click",b);var h={relatedTarget:this};if(f.trigger(d=a.Event("show.bs.dropdown",h)),d.isDefaultPrevented())return;f.toggleClass("open").trigger("shown.bs.dropdown",h),e.focus()}return!1}},f.prototype.keydown=function(b){if(/(38|40|27)/.test(b.keyCode)){var d=a(this);if(b.preventDefault(),b.stopPropagation(),!d.is(".disabled, :disabled")){var f=c(d),g=f.hasClass("open");if(!g||g&&27==b.keyCode)return 27==b.which&&f.find(e).focus(),d.click();var h=" li:not(.divider):visible a",i=f.find("[role=menu]"+h+", [role=listbox]"+h);if(i.length){var j=i.index(i.filter(":focus"));38==b.keyCode&&j>0&&j--,40==b.keyCode&&j<i.length-1&&j++,~j||(j=0),i.eq(j).focus()}}}};var g=a.fn.dropdown;a.fn.dropdown=function(b){return this.each(function(){var c=a(this),d=c.data("bs.dropdown");d||c.data("bs.dropdown",d=new f(this)),"string"==typeof b&&d[b].call(c)})},a.fn.dropdown.Constructor=f,a.fn.dropdown.noConflict=function(){return a.fn.dropdown=g,this},a(document).on("click.bs.dropdown.data-api",b).on("click.bs.dropdown.data-api",".dropdown form",function(a){a.stopPropagation()}).on("click.bs.dropdown.data-api",e,f.prototype.toggle).on("keydown.bs.dropdown.data-api",e+", [role=menu], [role=listbox]",f.prototype.keydown)}(jQuery),+function(a){"use strict";var b=function(b,c){this.options=c,this.$element=a(b),this.$backdrop=this.isShown=null,this.options.remote&&this.$element.find(".modal-content").load(this.options.remote,a.proxy(function(){this.$element.trigger("loaded.bs.modal")},this))};b.DEFAULTS={backdrop:!0,keyboard:!0,show:!0},b.prototype.toggle=function(a){return this[this.isShown?"hide":"show"](a)},b.prototype.show=function(b){var c=this,d=a.Event("show.bs.modal",{relatedTarget:b});this.$element.trigger(d),this.isShown||d.isDefaultPrevented()||(this.isShown=!0,this.escape(),this.$element.on("click.dismiss.bs.modal",'[data-dismiss="modal"]',a.proxy(this.hide,this)),this.backdrop(function(){var d=a.support.transition&&c.$element.hasClass("fade");c.$element.parent().length||c.$element.appendTo(document.body),c.$element.show().scrollTop(0),d&&c.$element[0].offsetWidth,c.$element.addClass("in").attr("aria-hidden",!1),c.enforceFocus();var e=a.Event("shown.bs.modal",{relatedTarget:b});d?c.$element.find(".modal-dialog").one(a.support.transition.end,function(){c.$element.focus().trigger(e)}).emulateTransitionEnd(300):c.$element.focus().trigger(e)}))},b.prototype.hide=function(b){b&&b.preventDefault(),b=a.Event("hide.bs.modal"),this.$element.trigger(b),this.isShown&&!b.isDefaultPrevented()&&(this.isShown=!1,this.escape(),a(document).off("focusin.bs.modal"),this.$element.removeClass("in").attr("aria-hidden",!0).off("click.dismiss.bs.modal"),a.support.transition&&this.$element.hasClass("fade")?this.$element.one(a.support.transition.end,a.proxy(this.hideModal,this)).emulateTransitionEnd(300):this.hideModal())},b.prototype.enforceFocus=function(){a(document).off("focusin.bs.modal").on("focusin.bs.modal",a.proxy(function(a){this.$element[0]===a.target||this.$element.has(a.target).length||this.$element.focus()},this))},b.prototype.escape=function(){this.isShown&&this.options.keyboard?this.$element.on("keyup.dismiss.bs.modal",a.proxy(function(a){27==a.which&&this.hide()},this)):this.isShown||this.$element.off("keyup.dismiss.bs.modal")},b.prototype.hideModal=function(){var a=this;this.$element.hide(),this.backdrop(function(){a.removeBackdrop(),a.$element.trigger("hidden.bs.modal")})},b.prototype.removeBackdrop=function(){this.$backdrop&&this.$backdrop.remove(),this.$backdrop=null},b.prototype.backdrop=function(b){var c=this.$element.hasClass("fade")?"fade":"";if(this.isShown&&this.options.backdrop){var d=a.support.transition&&c;if(this.$backdrop=a('<div class="modal-backdrop '+c+'" />').appendTo(document.body),this.$element.on("click.dismiss.bs.modal",a.proxy(function(a){a.target===a.currentTarget&&("static"==this.options.backdrop?this.$element[0].focus.call(this.$element[0]):this.hide.call(this))},this)),d&&this.$backdrop[0].offsetWidth,this.$backdrop.addClass("in"),!b)return;d?this.$backdrop.one(a.support.transition.end,b).emulateTransitionEnd(150):b()}else!this.isShown&&this.$backdrop?(this.$backdrop.removeClass("in"),a.support.transition&&this.$element.hasClass("fade")?this.$backdrop.one(a.support.transition.end,b).emulateTransitionEnd(150):b()):b&&b()};var c=a.fn.modal;a.fn.modal=function(c,d){return this.each(function(){var e=a(this),f=e.data("bs.modal"),g=a.extend({},b.DEFAULTS,e.data(),"object"==typeof c&&c);f||e.data("bs.modal",f=new b(this,g)),"string"==typeof c?f[c](d):g.show&&f.show(d)})},a.fn.modal.Constructor=b,a.fn.modal.noConflict=function(){return a.fn.modal=c,this},a(document).on("click.bs.modal.data-api",'[data-toggle="modal"]',function(b){var c=a(this),d=c.attr("href"),e=a(c.attr("data-target")||d&&d.replace(/.*(?=#[^\s]+$)/,"")),f=e.data("bs.modal")?"toggle":a.extend({remote:!/#/.test(d)&&d},e.data(),c.data());c.is("a")&&b.preventDefault(),e.modal(f,this).one("hide",function(){c.is(":visible")&&c.focus()})}),a(document).on("show.bs.modal",".modal",function(){a(document.body).addClass("modal-open")}).on("hidden.bs.modal",".modal",function(){a(document.body).removeClass("modal-open")})}(jQuery),+function(a){"use strict";var b=function(a,b){this.type=this.options=this.enabled=this.timeout=this.hoverState=this.$element=null,this.init("tooltip",a,b)};b.DEFAULTS={animation:!0,placement:"top",selector:!1,template:'<div class="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',trigger:"hover focus",title:"",delay:0,html:!1,container:!1},b.prototype.init=function(b,c,d){this.enabled=!0,this.type=b,this.$element=a(c),this.options=this.getOptions(d);for(var e=this.options.trigger.split(" "),f=e.length;f--;){var g=e[f];if("click"==g)this.$element.on("click."+this.type,this.options.selector,a.proxy(this.toggle,this));else if("manual"!=g){var h="hover"==g?"mouseenter":"focusin",i="hover"==g?"mouseleave":"focusout";this.$element.on(h+"."+this.type,this.options.selector,a.proxy(this.enter,this)),this.$element.on(i+"."+this.type,this.options.selector,a.proxy(this.leave,this))}}this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},b.prototype.getDefaults=function(){return b.DEFAULTS},b.prototype.getOptions=function(b){return b=a.extend({},this.getDefaults(),this.$element.data(),b),b.delay&&"number"==typeof b.delay&&(b.delay={show:b.delay,hide:b.delay}),b},b.prototype.getDelegateOptions=function(){var b={},c=this.getDefaults();return this._options&&a.each(this._options,function(a,d){c[a]!=d&&(b[a]=d)}),b},b.prototype.enter=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget)[this.type](this.getDelegateOptions()).data("bs."+this.type);return clearTimeout(c.timeout),c.hoverState="in",c.options.delay&&c.options.delay.show?void(c.timeout=setTimeout(function(){"in"==c.hoverState&&c.show()},c.options.delay.show)):c.show()},b.prototype.leave=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget)[this.type](this.getDelegateOptions()).data("bs."+this.type);return clearTimeout(c.timeout),c.hoverState="out",c.options.delay&&c.options.delay.hide?void(c.timeout=setTimeout(function(){"out"==c.hoverState&&c.hide()},c.options.delay.hide)):c.hide()},b.prototype.show=function(){var b=a.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){if(this.$element.trigger(b),b.isDefaultPrevented())return;var c=this,d=this.tip();this.setContent(),this.options.animation&&d.addClass("fade");var e="function"==typeof this.options.placement?this.options.placement.call(this,d[0],this.$element[0]):this.options.placement,f=/\s?auto?\s?/i,g=f.test(e);g&&(e=e.replace(f,"")||"top"),d.detach().css({top:0,left:0,display:"block"}).addClass(e),this.options.container?d.appendTo(this.options.container):d.insertAfter(this.$element);var h=this.getPosition(),i=d[0].offsetWidth,j=d[0].offsetHeight;if(g){var k=this.$element.parent(),l=e,m=document.documentElement.scrollTop||document.body.scrollTop,n="body"==this.options.container?window.innerWidth:k.outerWidth(),o="body"==this.options.container?window.innerHeight:k.outerHeight(),p="body"==this.options.container?0:k.offset().left;e="bottom"==e&&h.top+h.height+j-m>o?"top":"top"==e&&h.top-m-j<0?"bottom":"right"==e&&h.right+i>n?"left":"left"==e&&h.left-i<p?"right":e,d.removeClass(l).addClass(e)}var q=this.getCalculatedOffset(e,h,i,j);this.applyPlacement(q,e),this.hoverState=null;var r=function(){c.$element.trigger("shown.bs."+c.type)};a.support.transition&&this.$tip.hasClass("fade")?d.one(a.support.transition.end,r).emulateTransitionEnd(150):r()}},b.prototype.applyPlacement=function(b,c){var d,e=this.tip(),f=e[0].offsetWidth,g=e[0].offsetHeight,h=parseInt(e.css("margin-top"),10),i=parseInt(e.css("margin-left"),10);isNaN(h)&&(h=0),isNaN(i)&&(i=0),b.top=b.top+h,b.left=b.left+i,a.offset.setOffset(e[0],a.extend({using:function(a){e.css({top:Math.round(a.top),left:Math.round(a.left)})}},b),0),e.addClass("in");var j=e[0].offsetWidth,k=e[0].offsetHeight;if("top"==c&&k!=g&&(d=!0,b.top=b.top+g-k),/bottom|top/.test(c)){var l=0;b.left<0&&(l=-2*b.left,b.left=0,e.offset(b),j=e[0].offsetWidth,k=e[0].offsetHeight),this.replaceArrow(l-f+j,j,"left")}else this.replaceArrow(k-g,k,"top");d&&e.offset(b)},b.prototype.replaceArrow=function(a,b,c){this.arrow().css(c,a?50*(1-a/b)+"%":"")},b.prototype.setContent=function(){var a=this.tip(),b=this.getTitle();a.find(".tooltip-inner")[this.options.html?"html":"text"](b),a.removeClass("fade in top bottom left right")},b.prototype.hide=function(){function b(){"in"!=c.hoverState&&d.detach(),c.$element.trigger("hidden.bs."+c.type)}var c=this,d=this.tip(),e=a.Event("hide.bs."+this.type);return this.$element.trigger(e),e.isDefaultPrevented()?void 0:(d.removeClass("in"),a.support.transition&&this.$tip.hasClass("fade")?d.one(a.support.transition.end,b).emulateTransitionEnd(150):b(),this.hoverState=null,this)},b.prototype.fixTitle=function(){var a=this.$element;(a.attr("title")||"string"!=typeof a.attr("data-original-title"))&&a.attr("data-original-title",a.attr("title")||"").attr("title","")},b.prototype.hasContent=function(){return this.getTitle()},b.prototype.getPosition=function(){var b=this.$element[0];return a.extend({},"function"==typeof b.getBoundingClientRect?b.getBoundingClientRect():{width:b.offsetWidth,height:b.offsetHeight},this.$element.offset())},b.prototype.getCalculatedOffset=function(a,b,c,d){return"bottom"==a?{top:b.top+b.height,left:b.left+b.width/2-c/2}:"top"==a?{top:b.top-d,left:b.left+b.width/2-c/2}:"left"==a?{top:b.top+b.height/2-d/2,left:b.left-c}:{top:b.top+b.height/2-d/2,left:b.left+b.width}},b.prototype.getTitle=function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||("function"==typeof c.title?c.title.call(b[0]):c.title)},b.prototype.tip=function(){return this.$tip=this.$tip||a(this.options.template)},b.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},b.prototype.validate=function(){this.$element[0].parentNode||(this.hide(),this.$element=null,this.options=null)},b.prototype.enable=function(){this.enabled=!0},b.prototype.disable=function(){this.enabled=!1},b.prototype.toggleEnabled=function(){this.enabled=!this.enabled},b.prototype.toggle=function(b){var c=b?a(b.currentTarget)[this.type](this.getDelegateOptions()).data("bs."+this.type):this;c.tip().hasClass("in")?c.leave(c):c.enter(c)},b.prototype.destroy=function(){clearTimeout(this.timeout),this.hide().$element.off("."+this.type).removeData("bs."+this.type)};var c=a.fn.tooltip;a.fn.tooltip=function(c){return this.each(function(){var d=a(this),e=d.data("bs.tooltip"),f="object"==typeof c&&c;(e||"destroy"!=c)&&(e||d.data("bs.tooltip",e=new b(this,f)),"string"==typeof c&&e[c]())})},a.fn.tooltip.Constructor=b,a.fn.tooltip.noConflict=function(){return a.fn.tooltip=c,this}}(jQuery),+function(a){"use strict";var b=function(a,b){this.init("popover",a,b)};if(!a.fn.tooltip)throw new Error("Popover requires tooltip.js");b.DEFAULTS=a.extend({},a.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:'<div class="popover"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'}),b.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),b.prototype.constructor=b,b.prototype.getDefaults=function(){return b.DEFAULTS},b.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content")[this.options.html?"string"==typeof c?"html":"append":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},b.prototype.hasContent=function(){return this.getTitle()||this.getContent()},b.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},b.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")},b.prototype.tip=function(){return this.$tip||(this.$tip=a(this.options.template)),this.$tip};var c=a.fn.popover;a.fn.popover=function(c){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof c&&c;(e||"destroy"!=c)&&(e||d.data("bs.popover",e=new b(this,f)),"string"==typeof c&&e[c]())})},a.fn.popover.Constructor=b,a.fn.popover.noConflict=function(){return a.fn.popover=c,this}}(jQuery),+function(a){"use strict";function b(c,d){var e,f=a.proxy(this.process,this);this.$element=a(a(c).is("body")?window:c),this.$body=a("body"),this.$scrollElement=this.$element.on("scroll.bs.scroll-spy.data-api",f),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||(e=a(c).attr("href"))&&e.replace(/.*(?=#[^\s]+$)/,"")||"")+" .nav li > a",this.offsets=a([]),this.targets=a([]),this.activeTarget=null,this.refresh(),this.process()}b.DEFAULTS={offset:10},b.prototype.refresh=function(){var b=this.$element[0]==window?"offset":"position";this.offsets=a([]),this.targets=a([]);{var c=this;this.$body.find(this.selector).map(function(){var d=a(this),e=d.data("target")||d.attr("href"),f=/^#./.test(e)&&a(e);return f&&f.length&&f.is(":visible")&&[[f[b]().top+(!a.isWindow(c.$scrollElement.get(0))&&c.$scrollElement.scrollTop()),e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){c.offsets.push(this[0]),c.targets.push(this[1])})}},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.$scrollElement[0].scrollHeight||this.$body[0].scrollHeight,d=c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(b>=d)return g!=(a=f.last()[0])&&this.activate(a);if(g&&b<=e[0])return g!=(a=f[0])&&this.activate(a);for(a=e.length;a--;)g!=f[a]&&b>=e[a]&&(!e[a+1]||b<=e[a+1])&&this.activate(f[a])},b.prototype.activate=function(b){this.activeTarget=b,a(this.selector).parentsUntil(this.options.target,".active").removeClass("active");var c=this.selector+'[data-target="'+b+'"],'+this.selector+'[href="'+b+'"]',d=a(c).parents("li").addClass("active");d.parent(".dropdown-menu").length&&(d=d.closest("li.dropdown").addClass("active")),d.trigger("activate.bs.scrollspy")};var c=a.fn.scrollspy;a.fn.scrollspy=function(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.scrollspy.Constructor=b,a.fn.scrollspy.noConflict=function(){return a.fn.scrollspy=c,this},a(window).on("load",function(){a('[data-spy="scroll"]').each(function(){var b=a(this);b.scrollspy(b.data())})})}(jQuery),+function(a){"use strict";var b=function(b){this.element=a(b)};b.prototype.show=function(){var b=this.element,c=b.closest("ul:not(.dropdown-menu)"),d=b.data("target");if(d||(d=b.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,"")),!b.parent("li").hasClass("active")){var e=c.find(".active:last a")[0],f=a.Event("show.bs.tab",{relatedTarget:e});if(b.trigger(f),!f.isDefaultPrevented()){var g=a(d);this.activate(b.parent("li"),c),this.activate(g,g.parent(),function(){b.trigger({type:"shown.bs.tab",relatedTarget:e})})}}},b.prototype.activate=function(b,c,d){function e(){f.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),b.addClass("active"),g?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu")&&b.closest("li.dropdown").addClass("active"),d&&d()}var f=c.find("> .active"),g=d&&a.support.transition&&f.hasClass("fade");g?f.one(a.support.transition.end,e).emulateTransitionEnd(150):e(),f.removeClass("in")};var c=a.fn.tab;a.fn.tab=function(c){return this.each(function(){var d=a(this),e=d.data("bs.tab");e||d.data("bs.tab",e=new b(this)),"string"==typeof c&&e[c]()})},a.fn.tab.Constructor=b,a.fn.tab.noConflict=function(){return a.fn.tab=c,this},a(document).on("click.bs.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',function(b){b.preventDefault(),a(this).tab("show")})}(jQuery),+function(a){"use strict";var b=function(c,d){this.options=a.extend({},b.DEFAULTS,d),this.$window=a(window).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(c),this.affixed=this.unpin=this.pinnedOffset=null,this.checkPosition()};b.RESET="affix affix-top affix-bottom",b.DEFAULTS={offset:0},b.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(b.RESET).addClass("affix");var a=this.$window.scrollTop(),c=this.$element.offset();return this.pinnedOffset=c.top-a},b.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},b.prototype.checkPosition=function(){if(this.$element.is(":visible")){var c=a(document).height(),d=this.$window.scrollTop(),e=this.$element.offset(),f=this.options.offset,g=f.top,h=f.bottom;"top"==this.affixed&&(e.top+=d),"object"!=typeof f&&(h=g=f),"function"==typeof g&&(g=f.top(this.$element)),"function"==typeof h&&(h=f.bottom(this.$element));var i=null!=this.unpin&&d+this.unpin<=e.top?!1:null!=h&&e.top+this.$element.height()>=c-h?"bottom":null!=g&&g>=d?"top":!1;if(this.affixed!==i){this.unpin&&this.$element.css("top","");var j="affix"+(i?"-"+i:""),k=a.Event(j+".bs.affix");this.$element.trigger(k),k.isDefaultPrevented()||(this.affixed=i,this.unpin="bottom"==i?this.getPinnedOffset():null,this.$element.removeClass(b.RESET).addClass(j).trigger(a.Event(j.replace("affix","affixed"))),"bottom"==i&&this.$element.offset({top:c-h-this.$element.height()}))}}};var c=a.fn.affix;a.fn.affix=function(c){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof c&&c;e||d.data("bs.affix",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.affix.Constructor=b,a.fn.affix.noConflict=function(){return a.fn.affix=c,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var b=a(this),c=b.data();c.offset=c.offset||{},c.offsetBottom&&(c.offset.bottom=c.offsetBottom),c.offsetTop&&(c.offset.top=c.offsetTop),b.affix(c)})})}(jQuery);
\ No newline at end of file
diff --git a/pelican-plugins/bootstrap-rst/custom.css b/pelican-plugins/bootstrap-rst/custom.css
new file mode 100644
index 0000000000000000000000000000000000000000..825e8ac4c2391c9a1d146625b8bd32e61388841f
--- /dev/null
+++ b/pelican-plugins/bootstrap-rst/custom.css
@@ -0,0 +1,76 @@
+/* Swap h2/h3 sizes */
+/* h2,.h2 { font-size: 24px; }*/
+/*h3,.h3 { font-size: 30px; }*/
+
+/* sidebar title */
+.sidebar-title { display: none; }
+.sidebar .topic-title { display: none; }
+
+/* admonition title */
+.admonition-title { display: none; }
+
+
+/* page header h1 */
+.section > h1 {
+  padding-bottom: 9px;
+  margin: 40px 0 20px;
+  border-bottom: 1px solid #eee;
+}
+
+
+/* Sidebar */
+.sidebar ul {
+    font-size: 110%;
+    padding: 0;
+    margin: 0;
+    list-style: none;
+    /*padding-top: .5em;*/
+    padding-bottom: 1em;
+}
+
+.sidebar p {
+    padding: 0;
+    margin: 0;
+    padding-bottom: .1em;
+}
+
+.sidebar ul p {
+    text-align: left;
+    padding: 0;
+    margin: 0;
+    padding-top: .25em;
+}
+
+.sidebar ul ul {
+    font-size: 85%;
+    padding: 0;
+    margin: 0;
+    list-style: none;
+    margin-top: .5em;
+    margin-bottom: .5em;
+    margin-left: 10px;
+    padding-left: 10px;
+    border-left: 1px solid #eeeeff;
+}
+.sidebar ul ul p {
+    padding: 0;
+    margin: 0;
+    padding-top: .25em;
+}
+
+/* kbd -> .kbd */
+.kbd {
+  font-family: monospace, monospace;
+  font-size: 1em;
+}
+.kbd {
+  font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
+}
+.kbd {
+  padding: 2px 4px;
+  font-size: 90%;
+  color: #fff;
+  background-color: #333;
+  border-radius: 3px;
+  box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25);
+}
diff --git a/pelican-plugins/bootstrap-rst/directives.py b/pelican-plugins/bootstrap-rst/directives.py
new file mode 100644
index 0000000000000000000000000000000000000000..3a02c1784a4039428793f6af803afe07cb6311fc
--- /dev/null
+++ b/pelican-plugins/bootstrap-rst/directives.py
@@ -0,0 +1,465 @@
+# -*- coding: utf-8 -*-
+# -----------------------------------------------------------------------------
+# Bootstrap RST
+# Copyright (c) 2014, Nicolas P. Rougier
+# Distributed under the (new) BSD License. See LICENSE.txt for more info.
+# -----------------------------------------------------------------------------
+from docutils import nodes
+from docutils.parsers.rst.directives.body import BasePseudoSection
+from docutils.parsers.rst import Directive, directives, states, roles
+from docutils.parsers.rst.roles import set_classes
+from docutils.nodes import fully_normalize_name, whitespace_normalize_name
+from docutils.parsers.rst.directives.tables import Table
+from docutils.parsers.rst.roles import set_classes
+from docutils.transforms import misc
+
+
+class button(nodes.Inline, nodes.Element): pass
+class progress(nodes.Inline, nodes.Element): pass
+class alert(nodes.General, nodes.Element): pass
+class callout(nodes.General, nodes.Element): pass
+
+
+
+class Alert(Directive):
+    required_arguments, optional_arguments = 0,0
+    has_content = True
+    option_spec = {'type': directives.unchanged,
+                   'dismissable': directives.flag,
+                   'class': directives.class_option }
+
+    def run(self):
+        # Raise an error if the directive does not have contents.
+        self.assert_has_content()
+        text = '\n'.join(self.content)
+
+        # Create the node, to be populated by `nested_parse`.
+        node = alert(text, **self.options)
+        node['classes'] = ['alert']
+        node['classes'] += self.options.get('class', [])
+        if 'type' in self.options:
+            node['classes'] += ['alert-%s' % node['type']]
+        node.dismissable = False
+        if 'dismissable' in self.options:
+            node['classes'] += ['alert-dismissable']
+            node.dismissable = True
+
+        # Parse the directive contents.
+        self.state.nested_parse(self.content, self.content_offset, node)
+        return [node]
+
+
+class Callout(Directive):
+    required_arguments, optional_arguments = 0,1
+    has_content = True
+
+    def run(self):
+        # Raise an error if the directive does not have contents.
+        self.assert_has_content()
+        text = '\n'.join(self.content)
+
+        # Create the node, to be populated by `nested_parse`.
+        node = callout(self.block_text, **self.options)
+        node['classes'] = ['bs-callout']
+        if len(self.arguments):
+            type = 'bs-callout-' + self.arguments[0]
+        else:
+            type = 'bs-callout-info'
+        node['classes'] += [type]
+
+        # Parse the directive contents.
+        self.state.nested_parse(self.content, self.content_offset, node)
+        return [node]
+
+
+
+class Container(Directive):
+    optional_arguments = 1
+    final_argument_whitespace = True
+    option_spec = {'name': directives.unchanged}
+    has_content = True
+    default_class = None
+
+    def run(self):
+        self.assert_has_content()
+        text = '\n'.join(self.content)
+        try:
+            if self.arguments:
+                classes = directives.class_option(self.arguments[0])
+            else:
+                classes = self.default_class
+        except ValueError:
+            raise self.error(
+                'Invalid class attribute value for "%s" directive: "%s".'
+                % (self.name, self.arguments[0]))
+        node = nodes.container(text)
+        node['classes'].extend(classes)
+        self.add_name(node)
+        self.state.nested_parse(self.content, self.content_offset, node)
+        return [node]
+
+class Thumbnail(Container):
+    default_class = ['thumbnail']
+
+class Caption(Container):
+    default_class = ['caption']
+
+class Jumbotron(Container):
+    default_class = ['jumbotron']
+
+class PageHeader(Container):
+    default_class = ['page-header']
+
+
+
+class Lead(Directive):
+    required_arguments, optional_arguments = 0,0
+    has_content = True
+    option_spec = {'class':  directives.class_option }
+    def run(self):
+        self.assert_has_content()
+        text = '\n'.join(self.content)
+        node = nodes.container(text, **self.options)
+        node['classes'] = ['lead']
+        node['classes'] += self.options.get('class', [])
+        self.state.nested_parse(self.content, self.content_offset, node)
+        return [node]
+
+
+class Paragraph(Directive):
+    required_arguments, optional_arguments = 0,0
+    has_content = True
+    option_spec = {'class':  directives.class_option }
+
+    def run(self):
+        # Raise an error if the directive does not have contents.
+        self.assert_has_content()
+        text = '\n'.join(self.content)
+
+        # Create the node, to be populated by `nested_parse`.
+        node = nodes.paragraph(text, **self.options)
+        node['classes'] += self.options.get('class', [])
+
+        # Parse the directive contents.
+        self.state.nested_parse(self.content, self.content_offset, node)
+        return [node]
+
+
+class PageRow(Directive):
+
+    """
+    Directive to declare a container that is column-aware.
+    """
+
+    required_arguments, optional_arguments = 0,1
+    final_argument_whitespace = True
+    has_content = True
+    option_spec = {'class':  directives.class_option }
+    def run(self):
+        self.assert_has_content()
+        node = nodes.container(self.content)
+        node['classes'] = ['row']
+        if self.arguments:
+            node['classes'] += [self.arguments[0]]
+        node['classes'] += self.options.get('class', [])
+
+        self.add_name(node)
+        self.state.nested_parse(self.content, self.content_offset, node)
+        return [node]
+
+
+
+class PageColumn(Directive):
+
+    """
+    Directive to declare column with width and offset.
+    """
+
+    required_arguments, optional_arguments = 0,0
+    final_argument_whitespace = True
+    has_content = True
+    option_spec = {'width':  directives.positive_int,
+                   'offset': directives.positive_int,
+                   'push':   directives.positive_int,
+                   'pull':   directives.positive_int,
+                   'size':   lambda x: directives.choice(x, ('xs', 'sm', 'md', 'lg')),
+                   'class':  directives.class_option }
+    def run(self):
+        self.assert_has_content()
+        text = '\n'.join(self.content)
+        node = nodes.container(text)
+        width = self.options.get('width', 1)
+        size = self.options.get('size', 'md')
+        node['classes'] += ["col-%s-%d" % (size, width)]
+
+        offset = self.options.get('offset', 0)
+        if offset > 0:
+            node['classes'] += ["col-%s-offset-%d" % (size, offset)]
+
+        push = self.options.get('push', 0)
+        if push > 0:
+            node['classes'] += ["col-%s-push-%d" % (size, push)]
+
+        pull = self.options.get('pull', 0)
+        if pull > 0:
+            node['classes'] += ["col-%s-pull-%d" % (size, pull)]
+
+        node['classes'] += self.options.get('class', [])
+
+        self.add_name(node)
+        self.state.nested_parse(self.content, self.content_offset, node)
+        return [node]
+
+
+
+class Button(Directive):
+
+    """
+    Directive to declare a button
+    """
+
+    required_arguments, optional_arguments = 0,0
+    final_argument_whitespace = True
+    has_content = True
+    option_spec = {'class'   : directives.class_option,
+                   'target'  : directives.unchanged_required }
+    def run(self):
+        self.assert_has_content()
+        node = button()
+        node['target'] = self.options.get('target', None)
+        node['classes'] = self.options.get('class', [])
+        self.state.nested_parse(self.content, self.content_offset, node)
+        self.add_name(node)
+        return [node]
+
+
+
+class Progress(Directive):
+
+    """
+    Directive to declare a progress bar.
+    """
+
+    required_arguments, optional_arguments = 0,1
+    final_argument_whitespace = True
+    has_content = False
+    option_spec = { 'class'   : directives.class_option,
+                    'label'   : directives.unchanged,
+                    'value'   : directives.unchanged_required,
+                    'min'     : directives.unchanged_required,
+                    'max'     : directives.unchanged_required }
+    def run(self):
+        node = progress()
+        node['classes']   = self.options.get('class', '')
+        node['value_min'] = self.options.get('min_value', '0')
+        node['value_max'] = self.options.get('max_value', '100')
+        node['value']     = self.options.get('value', '50')
+        node['label']     = self.options.get('label', '')
+        if self.arguments:
+            node['value'] = self.arguments[0].rstrip(' %')
+            #if 'label' not in self.options:
+            #    node['label'] = self.arguments[0]
+        return [node]
+
+
+
+class Header(Directive):
+
+    """Contents of document header."""
+
+    required_arguments, optional_arguments = 0,1
+    has_content = True
+    option_spec = {'class':  directives.class_option }
+
+    def run(self):
+        self.assert_has_content()
+        header = self.state_machine.document.get_decoration().get_header()
+        header['classes'] += self.options.get('class', [])
+        if self.arguments:
+            header['classes'] += [self.arguments[0]]
+        self.state.nested_parse(self.content, self.content_offset, header)
+        return []
+
+
+class Footer(Directive):
+
+    """Contents of document footer."""
+
+    required_arguments, optional_arguments = 0,1
+    has_content = True
+    option_spec = {'class':  directives.class_option }
+
+    def run(self):
+        self.assert_has_content()
+        footer = self.state_machine.document.get_decoration().get_footer()
+        footer['classes'] += self.options.get('class', [])
+        if self.arguments:
+            footer['classes'] += [self.arguments[0]]
+        self.state.nested_parse(self.content, self.content_offset, footer)
+        return []
+
+
+
+
+
+
+
+# List item class
+# -----------------------------------------------------------------------------
+class ItemClass(Directive):
+
+    """
+    Set a "list-class" attribute on the directive content or the next element.
+    When applied to the next element, a "pending" element is inserted, and a
+    transform does the work later.
+    """
+
+    required_arguments = 1
+    optional_arguments = 0
+    final_argument_whitespace = True
+    has_content = False
+
+    def run(self):
+        try:
+            class_value = directives.class_option(self.arguments[0])
+        except ValueError:
+            raise self.error(
+                'Invalid class attribute value for "%s" directive: "%s".'
+                % (self.name, self.arguments[0]))
+
+        parent = self.state.parent
+        if isinstance(parent,nodes.list_item):
+            parent['classes'].extend(class_value)
+        return []
+
+
+# PATCH: Make a row inherit from the class attribute
+# --------------------------------------------------------------
+class ListTable(Table):
+
+    """
+    Implement tables whose data is encoded as a uniform two-level bullet list.
+    For further ideas, see
+    http://docutils.sf.net/docs/dev/rst/alternatives.html#list-driven-tables
+    """
+
+    option_spec = {'header-rows': directives.nonnegative_int,
+                   'stub-columns': directives.nonnegative_int,
+                   'widths': directives.positive_int_list,
+                   'class': directives.class_option,
+                   'name': directives.unchanged}
+
+    def run(self):
+        if not self.content:
+            error = self.state_machine.reporter.error(
+                'The "%s" directive is empty; content required.' % self.name,
+                nodes.literal_block(self.block_text, self.block_text),
+                line=self.lineno)
+            return [error]
+        title, messages = self.make_title()
+        node = nodes.Element()          # anonymous container for parsing
+        self.state.nested_parse(self.content, self.content_offset, node)
+        try:
+            num_cols, col_widths = self.check_list_content(node)
+            table_data = [[item.children for item in row_list[0]]
+                          for row_list in node[0]]
+            header_rows = self.options.get('header-rows', 0)
+            stub_columns = self.options.get('stub-columns', 0)
+            self.check_table_dimensions(table_data, header_rows, stub_columns)
+        except SystemMessagePropagation as detail:
+            return [detail.args[0]]
+        #table_node = self.build_table_from_list(table_data, col_widths,
+        #                                        header_rows, stub_columns)
+        table_node = self.build_table_from_list(node[0], col_widths,
+                                                header_rows, stub_columns)
+        table_node['classes'] += self.options.get('class', [])
+        self.add_name(table_node)
+        if title:
+            table_node.insert(0, title)
+        return [table_node] + messages
+
+    def check_list_content(self, node):
+        if len(node) != 1 or not isinstance(node[0], nodes.bullet_list):
+            error = self.state_machine.reporter.error(
+                'Error parsing content block for the "%s" directive: '
+                'exactly one bullet list expected.' % self.name,
+                nodes.literal_block(self.block_text, self.block_text),
+                line=self.lineno)
+            raise SystemMessagePropagation(error)
+        list_node = node[0]
+        # Check for a uniform two-level bullet list:
+        for item_index in range(len(list_node)):
+            item = list_node[item_index]
+            if len(item) != 1 or not isinstance(item[0], nodes.bullet_list):
+                error = self.state_machine.reporter.error(
+                    'Error parsing content block for the "%s" directive: '
+                    'two-level bullet list expected, but row %s does not '
+                    'contain a second-level bullet list.'
+                    % (self.name, item_index + 1), nodes.literal_block(
+                    self.block_text, self.block_text), line=self.lineno)
+                raise SystemMessagePropagation(error)
+            elif item_index:
+                # ATTN pychecker users: num_cols is guaranteed to be set in the
+                # "else" clause below for item_index==0, before this branch is
+                # triggered.
+                if len(item[0]) != num_cols:
+                    error = self.state_machine.reporter.error(
+                        'Error parsing content block for the "%s" directive: '
+                        'uniform two-level bullet list expected, but row %s '
+                        'does not contain the same number of items as row 1 '
+                        '(%s vs %s).'
+                        % (self.name, item_index + 1, len(item[0]), num_cols),
+                        nodes.literal_block(self.block_text, self.block_text),
+                        line=self.lineno)
+                    raise SystemMessagePropagation(error)
+            else:
+                num_cols = len(item[0])
+        col_widths = self.get_column_widths(num_cols)
+        return num_cols, col_widths
+
+    def build_table_from_list(Self, table_data, col_widths, header_rows, stub_columns):
+        table = nodes.table()
+        tgroup = nodes.tgroup(cols=len(col_widths))
+        table += tgroup
+        for col_width in col_widths:
+            colspec = nodes.colspec(colwidth=col_width)
+            if stub_columns:
+                colspec.attributes['stub'] = 1
+                stub_columns -= 1
+            tgroup += colspec
+        rows = []
+        for row in table_data:
+            row_node = nodes.row()
+            row_node['classes'] = row[0]['classes']
+            for cell in row[0]:
+                cell = cell.children
+                entry = nodes.entry()
+                entry += cell
+                row_node += entry
+            rows.append(row_node)
+        if header_rows:
+            thead = nodes.thead()
+            thead.extend(rows[:header_rows])
+            tgroup += thead
+        tbody = nodes.tbody()
+        tbody.extend(rows[header_rows:])
+        tgroup += tbody
+        return table
+
+
+
+directives.register_directive('item-class', ItemClass)
+directives.register_directive('list-table', ListTable)
+directives.register_directive('thumbnail', Thumbnail)
+directives.register_directive('caption', Caption)
+directives.register_directive('jumbotron', Jumbotron)
+directives.register_directive('page-header', PageHeader)
+directives.register_directive('lead', Lead)
+directives.register_directive('progress', Progress)
+directives.register_directive('alert', Alert)
+directives.register_directive('callout', Callout)
+directives.register_directive('row', PageRow)
+directives.register_directive('column', PageColumn)
+directives.register_directive('button', Button)
+directives.register_directive('footer', Footer)
+directives.register_directive('header', Header)
diff --git a/pelican-plugins/bootstrap-rst/doc/CSS-buttons.txt b/pelican-plugins/bootstrap-rst/doc/CSS-buttons.txt
new file mode 100644
index 0000000000000000000000000000000000000000..755405990de408957354e79e5ed91359ba5ded40
--- /dev/null
+++ b/pelican-plugins/bootstrap-rst/doc/CSS-buttons.txt
@@ -0,0 +1,185 @@
+Buttons
+===============================================================================
+
+Options
+-------
+
+Use any of the available button classes to quickly create a styled button.
+
+.. container:: bs-example
+
+   .. button:: Default
+
+   .. button:: Primary
+      :class: primary
+
+   .. button:: Success
+      :class: success
+
+   .. button:: Info
+      :class: info
+
+   .. button:: Warning
+      :class: warning
+
+   .. button:: Danger
+      :class: danger
+
+   .. button:: Link
+      :class: link
+
+.. code::
+   :class: highlight
+
+   .. button:: Default
+      :class: default
+
+   .. button:: Primary
+      :class: primary
+
+   .. button:: Success
+      :class: success
+
+   .. button:: Info
+      :class: info
+
+   .. button:: Warning
+      :class: warning
+
+   .. button:: Danger
+      :class: danger
+
+   .. button:: Link
+      :class: link
+
+
+Sizes
+-----
+
+Fancy larger or smaller buttons? Add :code:`large`, :code:`small`, or
+:code:`tiny` for additional sizes.
+
+.. container:: bs-example
+
+   .. button:: Large button
+      :class: primary large
+   .. button:: Large button
+      :class: large
+
+   |
+
+   .. button:: Default button
+      :class: primary
+   .. button:: Default button
+
+   |
+
+   .. button:: Small button
+      :class: primary small
+   .. button:: Small button
+      :class: small
+
+   |
+
+   .. button:: Tiny button
+      :class: primary tiny
+   .. button:: Tiny button
+      :class: tiny
+
+.. code::
+   :class: highlight
+
+   .. button:: Large button
+      :class: primary large
+
+   .. button:: Large button
+      :class: large
+
+   .. button:: Default button
+      :class: primary
+
+   .. button:: Default button
+
+   .. button:: Small button
+      :class: primary small
+
+   .. button:: Small button
+      :class: small
+
+   .. button:: Tiny button
+      :class: primary tiny
+
+   .. button:: Tiny button
+      :class: tiny
+
+Create block level buttons—those that span the full width of a parent by adding
+:code:`block`.
+
+.. container:: bs-example
+
+   .. container:: well
+
+      .. button:: Block level button
+         :class: primary large block
+
+      .. button:: Block level button
+         :class: large block
+
+.. code::
+   :class: highlight
+
+   .. button:: Block level button
+      :class: primary large block
+
+   .. button:: Block level button
+      :class: large block
+
+
+Active state
+------------
+Buttons will appear pressed (with a darker background, darker border, and inset
+shadow) when :code:`active`.
+
+.. container:: bs-example
+
+   .. button:: Primary button
+      :class: primary large active
+
+   .. button:: Button
+      :class: large active
+
+
+.. code::
+   :class: highlight
+
+   .. button:: Primary button
+      :class: primary large active
+
+   .. button:: Button
+      :class: large active
+
+
+
+Disabled state
+--------------
+
+Make buttons look unclickable by fading them back 50% using the :code:`disabled`
+option.
+
+.. container:: bs-example
+
+   .. button:: Primary button
+      :class: primary large disabled
+
+   .. button:: Button
+      :class: large disabled
+
+
+.. code::
+   :class: highlight
+
+   .. button:: Primary button
+      :class: primary large disabled
+
+   .. button:: Button
+      :class: large disabled
diff --git a/pelican-plugins/bootstrap-rst/doc/CSS-code.txt b/pelican-plugins/bootstrap-rst/doc/CSS-code.txt
new file mode 100644
index 0000000000000000000000000000000000000000..5e9b9ec4e480908a29303cc8f33cdc686168aaec
--- /dev/null
+++ b/pelican-plugins/bootstrap-rst/doc/CSS-code.txt
@@ -0,0 +1,48 @@
+Code
+===============================================================================
+
+Inline
+------
+
+Wrap inline snippets of code with `:code:`.
+
+.. container:: bs-example
+
+   For example, `<section>` should be wrapped as inline.
+
+.. code::
+   :class: highlight
+
+   For example, :code:`<section>` should be wrapped as inline.
+
+User input
+----------
+
+Use the `:kbd:` to indicate input that is typically entered via keyboard.
+
+.. container:: bs-example
+
+   To switch directories, type :kbd:`cd` followed by the name of the directory.
+
+.. code::
+   :class: highlight
+
+   To switch directories, type :kbd:`cd` followed by the name of the directory.
+
+Basic block
+-----------
+
+Use `.. code::` for multiple lines of code. Be sure to escape any angle brackets in
+the code for proper rendering.
+
+.. container:: bs-example
+
+   Sample text here...
+
+.. code::
+   :class: highlight
+
+   Sample text here...
+
+You may optionally add the pre-scrollable class, which will set a max-height
+of 350px and provide a y-axis scrollbar.
diff --git a/pelican-plugins/bootstrap-rst/doc/CSS-grid-system.txt b/pelican-plugins/bootstrap-rst/doc/CSS-grid-system.txt
new file mode 100644
index 0000000000000000000000000000000000000000..aa319c6a86e4aeb7bce619f7ed0068117c629133
--- /dev/null
+++ b/pelican-plugins/bootstrap-rst/doc/CSS-grid-system.txt
@@ -0,0 +1,235 @@
+Grid system
+===============================================================================
+.. lead::
+
+   Bootstrap includes a responsive, mobile first fluid grid system that
+   appropriately scales up to 12 columns as the device or viewport size
+   increases. It includes predefined classes for easy layout options, as well
+   as powerful mixins for generating more semantic layouts.
+.. ----------------------------------------------------------------------------
+
+
+
+Introduction
+------------
+
+Grid systems are used for creating page layouts through a series of rows and
+columns that house your content. Here's how the Bootstrap grid system works:
+
+* Rows must be placed within a `.container` (fixed-width) or `.container-fluid`
+  (full-width) for proper alignment and padding.
+* Use rows to create horizontal groups of columns.
+* Content should be placed within columns, and only columns may be immediate children of rows.
+* Predefined grid classes like `.row` and `.col-xs-4` are available for quickly
+  making grid layouts. Less mixins can also be used for more semantic layouts.
+* Columns create gutters (gaps between column content) via padding. That
+  padding is offset in rows for the first and last column via negative margin
+  on `.row` s.
+* Grid columns are created by specifying the number of twelve available columns
+  you wish to span. For example, three equal columns would use three `.col-xs-4`.
+  Look to the examples for applying these principles to your code.
+
+Look to the examples for applying these principles to your code.
+
+Media queries
+-------------
+
+We use the following media queries in our Less files to create the key
+breakpoints in our grid system.
+
+.. code:: css
+   :class: highlight
+
+   /* Extra small devices (phones, less than 768px) */
+   /* No media query since this is the default in Bootstrap */
+
+   /* Small devices (tablets, 768px and up) */
+   @media (min-width: @screen-sm-min) { ... }
+
+   /* Medium devices (desktops, 992px and up) */
+   @media (min-width: @screen-md-min) { ... }
+
+   /* Large devices (large desktops, 1200px and up) */
+   @media (min-width: @screen-lg-min) { ... }
+
+We occasionally expand on these media queries to include a max-width to limit
+CSS to a narrower set of devices.
+
+.. code:: css
+   :class: highlight
+
+   @media (max-width: @screen-xs-max) { ... }
+   @media (min-width: @screen-sm-min) and (max-width: @screen-sm-max) { ... }
+   @media (min-width: @screen-md-min) and (max-width: @screen-md-max) { ... }
+   @media (min-width: @screen-lg-min) { ... }
+
+
+Grid options
+------------
+
+See how aspects of the Bootstrap grid system work across multiple devices with
+a handy table.
+
+.. table::
+   :class: table table-bordered table-striped
+
+   +---------------------+---------------------+------------------+-------------------+--------------------+
+   |                     | Extra small devices | Small devices    | Medium devices    | Large devices      |
+   |                     | Phones (<768px)     | Tablets (≥768px) | Desktops (≥992px) | Desktops (≥1200px) |
+   +=====================+=====================+==================+===================+====================+
+   | **Grid Behavior**   | Horizontal at all   | Collapsed to start, horizontal above breakpoints          |
+   |                     | times               |                                                           |
+   +---------------------+---------------------+------------------+-------------------+--------------------+
+   | **Container Width** | None (auto)         | 750px            | 970px             | 1170px             |
+   +---------------------+---------------------+------------------+-------------------+--------------------+
+   | **Class Predix**    | `col-xs-`           | `col-sd-`        | `col-md-`         | `col-lg-`          |
+   +---------------------+---------------------+------------------+-------------------+--------------------+
+   | **# Columns**       | 12                                                                              |
+   +---------------------+---------------------+------------------+-------------------+--------------------+
+   | **Column Width**    | :text-muted:`Auto`  | 60px             | 78px              | 95px               |
+   +---------------------+---------------------+------------------+-------------------+--------------------+
+   | **Gutter width**    | 30px (15px on each side of a column)                                            |
+   +---------------------+---------------------+------------------+-------------------+--------------------+
+   | **Netsable**        | Yes                                                                             |
+   +---------------------+---------------------+------------------+-------------------+--------------------+
+   | **Offsets**         | Yes                                                                             |
+   +---------------------+---------------------+------------------+-------------------+--------------------+
+   | **Column Ordering** | Yes                                                                             |
+   +---------------------+---------------------+------------------+-------------------+--------------------+
+
+Grid classes apply to devices with screen widths greater than or equal to the
+breakpoint sizes, and override grid classes targeted at smaller
+devices. Therefore, applying any `.col-md-` class to an element will not only
+affect its styling on medium devices but also on large devices if a `.col-lg-`
+class is not present.
+
+
+Example: Stacked-to-horizontal
+------------------------------
+
+Using a single set of `.col-md-*` grid classes, you can create a basic grid
+system that starts out stacked on mobile devices and tablet devices (the extra
+small to small range) before becoming horizontal on desktop (medium)
+devices. Place grid columns in any `.row`.
+
+.. row:: show-grid
+
+   .. column:: .col-md-1
+   .. column:: .col-md-1
+   .. column:: .col-md-1
+   .. column:: .col-md-1
+   .. column:: .col-md-1
+   .. column:: .col-md-1
+   .. column:: .col-md-1
+   .. column:: .col-md-1
+   .. column:: .col-md-1
+   .. column:: .col-md-1
+   .. column:: .col-md-1
+   .. column:: .col-md-1
+
+.. row:: show-grid
+
+   .. column:: .col-md-8
+      :width: 8
+   .. column:: .col-md-4
+      :width: 4
+
+.. row:: show-grid
+
+   .. column:: .col-md-4
+      :width: 4
+   .. column:: .col-md-4
+      :width: 4
+   .. column:: .col-md-4
+      :width: 4
+
+.. row:: show-grid
+
+   .. column:: .col-md-6
+      :width: 6
+   .. column:: .col-md-6
+      :width: 6
+
+
+Offsetting columns
+------------------
+
+Move columns to the right using `.col-md-offset-*` classes. These classes
+increase the left margin of a column by * columns. For example,
+`.col-md-offset-4` moves `.col-md-4` over four columns.
+
+
+.. row:: show-grid
+
+   .. column:: .col-md-4
+      :width: 4
+   .. column:: .col-md-4
+      :width: 4
+      :offset: 4
+
+
+.. row:: show-grid
+
+   .. column:: .col-md-3
+      :width: 3
+      :offset: 3
+
+   .. column:: .col-md-3
+      :width: 3
+      :offset: 3
+
+
+.. row:: show-grid
+
+   .. column:: .col-md-6
+      :width: 6
+      :offset: 3
+
+
+Nesting columns
+---------------
+
+To nest your content with the default grid, add a new `.row` and set of
+`.col-md-*` columns within an existing `.col-md-*` column. Nested rows should
+include a set of columns that add up to 12 or less.
+
+.. row:: show-grid
+
+   .. column:: Level 1: .col-md-9
+      :width: 9
+
+      .. row:: show-grid
+
+         .. column:: Level 2: .col-md-6
+            :width: 6
+         .. column:: Level 2: .col-md-6
+            :width: 6
+
+
+Column ordering
+---------------
+
+Easily change the order of our built-in grid columns with .col-md-push-* and
+.col-md-pull-* modifier classes.
+
+
+.. row:: show-grid
+
+   .. column:: .col-md-9 . col-push-3
+      :width: 9
+      :push: 3
+
+   .. column:: .col-md-3 . col-pull-9
+      :width: 3
+      :pull: 9
+
+.. code::
+   :class: highlight
+
+   .. column:: .col-md-9 . col-push-3
+      :width: 9
+      :push: 3
+
+   .. column:: .col-md-3 . col-pull-9
+      :width: 3
+      :pull: 9
diff --git a/pelican-plugins/bootstrap-rst/doc/CSS-helpers.txt b/pelican-plugins/bootstrap-rst/doc/CSS-helpers.txt
new file mode 100644
index 0000000000000000000000000000000000000000..f7aa5ab1d0a859310ca8f8c6f15ad57e52fab28f
--- /dev/null
+++ b/pelican-plugins/bootstrap-rst/doc/CSS-helpers.txt
@@ -0,0 +1,68 @@
+Helper classes
+===============================================================================
+
+Contextual colors
+-----------------
+Convey meaning through color with a handful of emphasis utility classes. These
+may also be applied to links and will darken on hover just like our default
+link styles.
+
+.. container:: bs-example
+
+   | :text-muted:`Fusce dapibus, tellus ac cursus commodo, tortor mauris nibh.`
+   | :text-primary:`Nullam id dolor id nibh ultricies vehicula ut id elit.`
+   | :text-success:`Duis mollis, est non commodo luctus, nisi erat porttitor ligula.`
+   | :text-info:`Maecenas sed diam eget risus varius blandit sit amet non magna.`
+   | :text-warning:`Etiam porta sem malesuada magna mollis euismod.`
+   | :text-danger:`Donec ullamcorper nulla non metus auctor fringilla.`
+
+.. code:: rst
+   :class: highlight
+
+   :text-muted:`Fusce dapibus, tellus ac cursus commodo, tortor mauris nibh.`
+   :text-primary:`Nullam id dolor id nibh ultricies vehicula ut id elit.`
+   :text-success:`Duis mollis, est non commodo luctus, nisi erat porttitor ligula.`
+   :text-info:`Maecenas sed diam eget risus varius blandit sit amet non magna.`
+   :text-warning:`Etiam porta sem malesuada magna mollis euismod.`
+   :text-danger:`Donec ullamcorper nulla non metus auctor fringilla.`
+
+
+
+Contextual background
+---------------------
+
+Similar to the contextual text color classes, easily set the background of an
+element to any contextual class. Anchor components will darken on hover, just
+like the text classes.
+
+
+.. container:: bs-example bs-example-bg-classes
+
+   .. container:: bg-primary
+
+      Nullam id dolor id nibh ultricies vehicula ut id elit.
+
+   .. container:: bg-success
+
+      Duis mollis, est non commodo luctus, nisi erat porttitor ligula.
+
+   .. container:: bg-info
+
+      Maecenas sed diam eget risus varius blandit sit amet non magna.
+
+   .. container:: bg-warning
+
+      Etiam porta sem malesuada magna mollis euismod.
+
+   .. container:: bg-danger
+
+      Donec ullamcorper nulla non metus auctor fringilla.
+
+.. code:: rst
+   :class: highlight
+
+   :bg-primary:`Nullam id dolor id nibh ultricies vehicula ut id elit.`
+   :bg-success:`Duis mollis, est non commodo luctus, nisi erat porttitor ligula.`
+   :bg-info:`Maecenas sed diam eget risus varius blandit sit amet non magna.`
+   :bg-warning:`Etiam porta sem malesuada magna mollis euismod.`
+   :bg-danger:`Donec ullamcorper nulla non metus auctor fringilla.`
diff --git a/pelican-plugins/bootstrap-rst/doc/CSS-images.txt b/pelican-plugins/bootstrap-rst/doc/CSS-images.txt
new file mode 100644
index 0000000000000000000000000000000000000000..a38425a058ffff93e81ebf042d2b7961087beeaf
--- /dev/null
+++ b/pelican-plugins/bootstrap-rst/doc/CSS-images.txt
@@ -0,0 +1,52 @@
+Images
+===============================================================================
+
+Responsive images
+-----------------
+
+Images in Bootstrap 3 can be made responsive-friendly via the addition of the
+.img-responsive class. This applies max-width: 100%; and height: auto; to the
+image so that it scales nicely to the parent element.
+
+
+.. code::
+   :class: highlight
+
+   .. image:: image.png
+      :class: img-responsive
+
+Image shapes
+------------
+
+Add classes to an `<img>` element to easily style images in any project.
+
+
+.. callout:: danger
+
+   :h4:`Cross-browser compatibility`
+   Keep in mind that Internet Explorer 8 lacks support for rounded corners.
+
+
+.. container:: bs-example
+
+   .. image:: 140x140.png
+      :class: img-rounded
+
+   .. image:: 140x140.png
+      :class: img-circle
+
+   .. image:: 140x140.png
+      :class: img-thumbnail
+
+
+.. code::
+   :class: highlight
+
+   .. image:: 140x140.png
+      :class: img-rounded
+
+   .. image:: 140x140.png
+      :class: img-circle
+
+   .. image:: 140x140.png
+      :class: img-thumbnail
diff --git a/pelican-plugins/bootstrap-rst/doc/CSS-overview.txt b/pelican-plugins/bootstrap-rst/doc/CSS-overview.txt
new file mode 100644
index 0000000000000000000000000000000000000000..c358591683dd1c65c66127b7bdf03f072bb6739f
--- /dev/null
+++ b/pelican-plugins/bootstrap-rst/doc/CSS-overview.txt
@@ -0,0 +1,92 @@
+Overview
+===============================================================================
+
+.. lead::
+
+   Get the lowdown on the key pieces of Bootstrap's infrastructure, including
+   our approach to better, faster, stronger web development.
+
+
+
+HTML5 doctype
+-------------
+
+Bootstrap makes use of certain HTML elements and CSS properties that require
+the use of the HTML5 doctype. Include it at the beginning of all your projects.
+
+.. code:: html
+   :class: highlight
+
+   <!DOCTYPE html>
+   <html lang="en">
+   ...
+   </html>
+
+
+Mobile first
+-------------
+
+With Bootstrap 2, we added optional mobile friendly styles for key aspects of
+the framework. With Bootstrap 3, we've rewritten the project to be mobile
+friendly from the start. Instead of adding on optional mobile styles, they're
+baked right into the core. In fact, **Bootstrap is mobile first**. Mobile first
+styles can be found throughout the entire library instead of in separate files.
+
+To ensure proper rendering and touch zooming, **add the viewport meta tag** to
+your :code:`<head>`.
+
+.. code:: html
+   :class: highlight
+
+   <meta name="viewport" content="width=device-width, initial-scale=1">
+
+You can disable zooming capabilities on mobile devices by adding
+:code:`user-scalable=no` to the viewport meta tag. This disables zooming,
+meaning users are only able to scroll, and results in your site feeling a bit
+more like a native application. Overall, we don't recommend this on every site,
+so use caution!
+
+.. code:: html
+   :class: highlight
+
+   <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
+
+Typography and links
+--------------------
+
+Bootstrap sets basic global display, typography, and link styles. Specifically,
+we:
+
+* Set :code:`background-color: #fff`; on the body
+* Use the :code:`@font-family-base`, :code:`@font-size-base`, and
+  :code:`@line-height-base` attributes as our typographic base
+* Set the global link color via :code:`@link-color` and apply link underlines
+  only on :hover
+
+These styles can be found within :code:`scaffolding.less`.
+
+
+Normalize.css
+-------------
+
+For improved cross-browser rendering, we use `Normalize.css
+<http://necolas.github.io/normalize.css/>`_, a project by `Nicolas Gallagher
+<https://twitter.com/necolas>`_ and `Jonathan Neal <https://twitter.com/jon_neal>`_.
+
+
+Containers
+----------
+
+Easily center a page's contents by wrapping its contents in a
+.container. Containers set width at various media query breakpoints to match
+our grid system.
+
+Note that, due to padding and fixed widths, containers are not nestable by
+default.
+
+.. code::
+   :class: highlight
+
+   .. container::
+
+      ...
diff --git a/pelican-plugins/bootstrap-rst/doc/CSS-tables.txt b/pelican-plugins/bootstrap-rst/doc/CSS-tables.txt
new file mode 100644
index 0000000000000000000000000000000000000000..3a1bff8a244351ccc1332855bf0e7d0f82250ef4
--- /dev/null
+++ b/pelican-plugins/bootstrap-rst/doc/CSS-tables.txt
@@ -0,0 +1,292 @@
+Tables
+======
+
+Basic example
+-------------
+
+For basic styling—light padding and only horizontal dividers—add the base class
+.table to any `<table>`. It may seem super redundant, but given the widespread
+use of tables for other plugins like calendars and date pickers, we've opted to
+isolate our custom table styles.
+
+.. container:: bs-example
+
+   = ========== ========= ========
+   # First Name Last Name Username
+   = ========== ========= ========
+   1 Mark       Otto      @mdo
+   2 Jacob      Thornton  @fat
+   3 Larry      the Bird  @twitter
+   = ========== ========= ========
+
+.. code::
+   :class: highlight
+
+   = ========== ========= ========
+   # First Name Last Name Username
+   = ========== ========= ========
+   1 Mark       Otto      @mdo
+   2 Jacob      Thornton  @fat
+   3 Larry      the Bird  @twitter
+   = ========== ========= ========
+
+
+Striped rows
+------------
+
+Use `table-striped` to add zebra-striping to any table row within the `<tbody>`.
+
+.. ----------------------------------------------------------------------------
+.. callout:: danger
+
+   :h4:`Cross-browser compatibility`
+   Striped tables are styled via the `:nth-child` CSS selector, which is not
+   available in Internet Explorer 8.
+.. ----------------------------------------------------------------------------
+
+
+.. container:: bs-example
+
+   .. class:: table-striped
+
+      = ========== ========= ========
+      # First Name Last Name Username
+      = ========== ========= ========
+      1 Mark       Otto      @mdo
+      2 Jacob      Thornton  @fat
+      3 Larry      the Bird  @twitter
+      = ========== ========= ========
+
+.. code::
+   :class: highlight
+
+   .. class:: table-striped
+
+      = ========== ========= ========
+      # First Name Last Name Username
+      = ========== ========= ========
+      1 Mark       Otto      @mdo
+      2 Jacob      Thornton  @fat
+      3 Larry      the Bird  @twitter
+      = ========== ========= ========
+
+Bordered table
+--------------
+
+Add `table-bordered` for borders on all sides of the table and cells.
+
+.. container:: bs-example
+
+   .. class:: table-bordered table-striped
+
+      = ========== ========= ========
+      # First Name Last Name Username
+      = ========== ========= ========
+      1 Mark       Otto      @mdo
+      2 Jacob      Thornton  @fat
+      3 Larry      the Bird  @twitter
+      = ========== ========= ========
+
+.. code::
+   :class: highlight
+
+   .. class:: table-bordered table-striped
+
+      = ========== ========= ========
+      # First Name Last Name Username
+      = ========== ========= ========
+      1 Mark       Otto      @mdo
+      2 Jacob      Thornton  @fat
+      3 Larry      the Bird  @twitter
+      = ========== ========= ========
+
+Hover rows
+----------
+
+Add `table-hover` to enable a hover state on table rows within a `<tbody>`.
+
+.. container:: bs-example
+
+   .. class:: table-hover
+
+      = ========== ========= ========
+      # First Name Last Name Username
+      = ========== ========= ========
+      1 Mark       Otto      @mdo
+      2 Jacob      Thornton  @fat
+      3 Larry      the Bird  @twitter
+      = ========== ========= ========
+
+.. code::
+   :class: highlight
+
+   .. class:: table-hover
+
+      = ========== ========= ========
+      # First Name Last Name Username
+      = ========== ========= ========
+      1 Mark       Otto      @mdo
+      2 Jacob      Thornton  @fat
+      3 Larry      the Bird  @twitter
+      = ========== ========= ========
+
+
+Condensed table
+---------------
+
+Add `table-condensed` to make tables more compact by cutting cell padding in half.
+
+.. container:: bs-example
+
+   .. class:: table-condensed
+
+      = ========== ========= ========
+      # First Name Last Name Username
+      = ========== ========= ========
+      1 Mark       Otto      @mdo
+      2 Jacob      Thornton  @fat
+      3 Larry      the Bird  @twitter
+      = ========== ========= ========
+
+.. code::
+   :class: highlight
+
+   .. class:: table-condensed
+
+      = ========== ========= ========
+      # First Name Last Name Username
+      = ========== ========= ========
+      1 Mark       Otto      @mdo
+      2 Jacob      Thornton  @fat
+      3 Larry      the Bird  @twitter
+      = ========== ========= ========
+
+Contextual classes
+------------------
+
+Use contextual classes to color table rows or individual cells.
+
+.. container:: bs-example
+
+   .. list-table::
+      :widths: 10 30 30 30
+
+      * - #
+        - Column heading
+        - Column heading
+        - Column heading
+
+      * .. class:: active
+
+           - 1
+           - Column content
+           - Column content
+           - Column content
+
+      * - 2
+        - Column heading
+        - Column heading
+        - Column heading
+
+      * .. class:: success
+
+           - 3
+           - Column content
+           - Column content
+           - Column content
+
+      * - 4
+        - Column heading
+        - Column heading
+        - Column heading
+
+      * .. class:: warning
+
+           - 5
+           - Column content
+           - Column content
+           - Column content
+
+      * - 6
+        - Column heading
+        - Column heading
+        - Column heading
+
+
+      * .. class:: danger
+
+           - 7
+           - Column content
+           - Column content
+           - Column content
+
+      * - 8
+        - Column heading
+        - Column heading
+        - Column heading
+
+
+      * .. class:: info
+
+           - 9
+           - Column content
+           - Column content
+           - Column content
+
+.. code::
+   :class: highlight
+
+   .. list-table::
+      :widths: 10 30 30 30
+
+      * - #
+        - Column heading
+        - Column heading
+        - Column heading
+
+      * .. class:: active
+
+           - 1
+           - Column content
+           - Column content
+           - Column content
+
+
+
+Responsive tables
+-----------------
+
+Create responsive tables by wrapping any `.table` in `.table-responsive` to
+make them scroll horizontally up to small devices (under 768px). When viewing
+on anything larger than 768px wide, you will not see any difference in these
+tables.
+
+
+.. container:: bs-example
+
+   .. container:: table-responsive
+
+      .. class:: table-bordered table-striped
+
+         = ============= ============= ============= ============= =============
+         # Table heading Table heading Table heading Table heading Table heading
+         = ============= ============= ============= ============= =============
+         1 Table cell    Table cell    Table cell    Table cell    Table cell
+         2 Table cell    Table cell    Table cell    Table cell    Table cell
+         3 Table cell    Table cell    Table cell    Table cell    Table cell
+         = ============= ============= ============= ============= =============
+
+.. code::
+   :class: highlight
+
+   .. container:: table-responsive
+
+      .. class:: table-bordered table-striped
+
+         = ============= ============= ============= ============= =============
+         # Table heading Table heading Table heading Table heading Table heading
+         = ============= ============= ============= ============= =============
+         1 Table cell    Table cell    Table cell    Table cell    Table cell
+         2 Table cell    Table cell    Table cell    Table cell    Table cell
+         3 Table cell    Table cell    Table cell    Table cell    Table cell
+         = ============= ============= ============= ============= =============
diff --git a/pelican-plugins/bootstrap-rst/doc/CSS-typography.txt b/pelican-plugins/bootstrap-rst/doc/CSS-typography.txt
new file mode 100644
index 0000000000000000000000000000000000000000..744dc2ca27c09b6b4ff0fc554055611cadfcdfeb
--- /dev/null
+++ b/pelican-plugins/bootstrap-rst/doc/CSS-typography.txt
@@ -0,0 +1,441 @@
+Typography
+===============================================================================
+
+Headings
+--------
+
+All HTML headings, :code:`<h1>` through :code:`<h6>`, are available. :code:`.h1`
+through :code:`.h6` classes are also available, for when you want to match the font
+styling of a heading but still want your text to be displayed inline.
+
+.. container:: bs-example
+
+   .. list-table::
+      :widths: 75 25
+      :class: table
+
+      * - :h1:`h1. Bootstrap heading`
+        - **Semibold 36 px**
+      * - :h2:`h2. Bootstrap heading`
+        - **Semibold 30 px**
+      * - :h3:`h3. Bootstrap heading`
+        - **Semibold 24 px**
+      * - :h4:`h4. Bootstrap heading`
+        - **Semibold 18 px**
+      * - :h5:`h5. Bootstrap heading`
+        - **Semibold 14 px**
+      * - :h6:`h6. Bootstrap heading`
+        - **Semibold 12 px**
+
+.. code::
+   :class: highlight
+
+   h1. Bootstrap heading
+   =====================
+
+   h2. Bootstrap heading
+   ---------------------
+
+   h3. Bootstrap heading
+   +++++++++++++++++++++
+
+   h4. Bootstrap heading
+   ~~~~~~~~~~~~~~~~~~~~~
+
+   h5. Bootstrap heading
+   *********************
+
+   h6. Bootstrap heading
+   ¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨
+
+Create lighter, secondary text in any heading with a generic :code:`small` tag.
+
+
+.. container:: bs-example
+
+   .. list-table::
+      :widths: 100
+      :class: table
+
+      * - :h1:`h1. Bootstrap heading <small>Secondary text</small>`
+      * - :h2:`h2. Bootstrap heading <small>Secondary text</small>`
+      * - :h3:`h3. Bootstrap heading <small>Secondary text</small>`
+      * - :h4:`h4. Bootstrap heading <small>Secondary text</small>`
+      * - :h5:`h5. Bootstrap heading <small>Secondary text</small>`
+      * - :h6:`h6. Bootstrap heading <small>Secondary text</small>`
+
+.. code::
+   :class: highlight
+
+   h1. Bootstrap heading :small:`Secondary text`
+   =============================================
+
+   h2. Bootstrap heading :small:`Secondary text`
+   ---------------------------------------------
+
+   h3. Bootstrap heading :small:`Secondary text`
+   +++++++++++++++++++++++++++++++++++++++++++++
+
+   h4. Bootstrap heading :small:`Secondary text`
+   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+   h5. Bootstrap heading :small:`Secondary text`
+   *********************************************
+
+   h6. Bootstrap heading :small:`Secondary text`
+   ¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨
+
+
+Body copy
+---------
+
+Bootstrap's global default `font-size` is **14px**, with a line-height of
+**1.428**. This is applied to the `<body>` and all paragraphs. In addition,
+`<p>` (paragraphs) receive a bottom margin of half their computed line-height
+(10px by default).
+
+
+.. container:: bs-example
+
+   Nullam quis risus eget urna mollis ornare vel eu leo. Cum sociis natoque
+   penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nullam id
+   dolor id nibh ultricies vehicula.
+
+   Cum sociis natoque penatibus et magnis dis parturient montes, nascetur
+   ridiculus mus. Donec ullamcorper nulla non metus auctor fringilla. Duis
+   mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia
+   odio sem nec elit. Donec ullamcorper nulla non metus auctor fringilla.
+
+   Maecenas sed diam eget risus varius blandit sit amet non magna. Donec id
+   elit non mi porta gravida at eget metus. Duis mollis, est non commodo
+   luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit.
+
+.. code::
+   :class: highlight
+
+   ...
+
+
+Lead body copy
+++++++++++++++
+
+Make a paragraph stand out by adding lead.
+
+
+.. container:: bs-example
+
+   .. lead:: Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor
+             auctor. Duis mollis, est non commodo luctus.
+
+
+.. code::
+   :class: highlight
+
+
+   .. lead:: Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor
+             auctor. Duis mollis, est non commodo luctus.
+
+
+Built with Less
++++++++++++++++
+
+The typographic scale is based on two Less variables in **variables.less**:
+`@font-size-base` and `@line-height-base`. The first is the base font-size used
+throughout and the second is the base line-height. We use those variables and
+some simple math to create the margins, paddings, and line-heights of all our
+type and more. Customize them and Bootstrap adapts.
+
+
+Emphasis
+--------
+Make use of HTML's default emphasis tags with lightweight styles.
+
+
+Small text
+++++++++++
+
+For de-emphasizing inline or blocks of text, use the `small` tag to set text at
+85% the size of the parent. Heading elements receive their own font-size for
+nested `small` elements.
+
+.. container:: bs-example
+
+   :small:`This line of text is meant to be treated as fine print.`
+
+
+.. code::
+   :class: highlight
+
+   :small:`This line of text is meant to be treated as fine print.`
+
+
+Bold
+++++
+
+For emphasizing a snippet of text with a heavier font-weight.
+
+.. container:: bs-example
+
+   The following snippet of text is **rendered as bold text**.
+
+.. code::
+   :class: highlight
+
+   The following snippet of text is **rendered as bold text**.
+
+
+Italics
++++++++
+
+For emphasizing a snippet of text with italics.
+
+.. container:: bs-example
+
+   The following snippet of text is *rendered as italicized text*.
+
+.. code::
+   :class: highlight
+
+   The following snippet of text is *rendered as italicized text*.
+
+
+.. admonition:: :h4:`Alternate elements`
+   :class: bs-callout bs-callout-info
+
+   Feel free to use `<b>` and `<i>` in HTML5. `<b>` is meant to highlight words or
+   phrases without conveying additional importance while `<i>` is mostly for
+   voice, technical terms, etc.
+
+
+Alignment classes
++++++++++++++++++
+
+Easily realign text to components with text alignment classes.
+
+.. container:: bs-example
+
+   .. class:: text-left
+
+      Left aligned text.
+
+   .. class:: text-center
+
+      Center aligned text.
+
+   .. class:: text-right
+
+      Right aligned text.
+
+   .. class:: text-justify
+
+      Justified text.
+
+
+.. code::
+   :class: highlight
+
+   .. class:: text-left
+
+      Left aligned text.
+
+   .. class:: text-center
+
+      Center aligned text.
+
+   .. class:: text-right
+
+      Right aligned text.
+
+   .. class:: text-justify
+
+      Justified text.
+
+
+
+Lists
+-----
+
+Unordered
++++++++++
+
+A list of items in which the order does *not* explicitly matter.
+
+.. container:: bs-example
+
+   * Lorem ipsum dolor sit amet
+   * Consectetur adipiscing elit
+   * Integer molestie lorem at massa
+   * Facilisis in pretium nisl aliquet
+   * Nulla volutpat aliquam velit
+
+     * Phasellus iaculis neque
+     * Purus sodales ultricies
+     * Vestibulum laoreet porttitor sem
+     * Ac tristique libero volutpat at
+
+   * Faucibus porta lacus fringilla vel
+   * Aenean sit amet erat nunc
+   * Eget porttitor lorem
+
+.. code::
+   :class: highlight
+
+   * Lorem ipsum dolor sit amet
+   * Consectetur adipiscing elit
+     ...
+
+
+Ordered
++++++++
+
+A list of items in which the order *does* matter.
+
+.. container:: bs-example
+
+   1. Lorem ipsum dolor sit amet
+   #. Consectetur adipiscing elit
+   #. Integer molestie lorem at massa
+   #. Facilisis in pretium nisl aliquet
+   #. Nulla volutpat aliquam velit
+   #. Faucibus porta lacus fringilla vel
+   #. Aenean sit amet erat nunc
+   #. Eget porttitor lorem
+
+.. code::
+   :class: highlight
+
+   1. Lorem ipsum dolor sit amet
+   #. Consectetur adipiscing elit
+      ...
+
+Unstyled
+++++++++
+
+Remove the default `list-style` and left margin on list items (immediate children
+only). **This only applies to immediate children list items**, meaning you will
+need to add the class for any nested lists as well.
+
+.. container:: bs-example
+
+   .. class:: list-unstyled
+
+      * Lorem ipsum dolor sit amet
+      * Consectetur adipiscing elit
+      * Integer molestie lorem at massa
+      * Facilisis in pretium nisl aliquet
+      * Nulla volutpat aliquam velit
+
+        * Phasellus iaculis neque
+        * Purus sodales ultricies
+        * Vestibulum laoreet porttitor sem
+        * Ac tristique libero volutpat at
+
+      * Faucibus porta lacus fringilla vel
+      * Aenean sit amet erat nunc
+      * Eget porttitor lorem
+
+.. code::
+   :class: highlight
+
+   .. class:: list-unstyled
+
+      * Lorem ipsum dolor sit amet
+      * Consectetur adipiscing elit
+        ...
+
+
+Inline
+++++++
+
+.. container:: bs-example
+
+   .. class:: list-inline
+
+      * Lorem ipsum
+      * Phasellus iaculis
+      * Nulla volutpat
+
+.. code::
+   :class: highlight
+
+   .. class:: list-inline
+
+      * Lorem ipsum
+      * Phasellus iaculis
+      * Nulla volutpat
+
+
+Description
++++++++++++
+
+.. container:: bs-example
+
+   A list of terms with their associated descriptions.
+
+   Description lists
+    A description list is perfect for defining terms.
+
+   Euismod
+    Vestibulum id ligula porta felis euismod semper eget lacinia odio sem nec elit.
+    Donec id elit non mi porta gravida at eget metus.
+
+   Malesuada porta
+    Etiam porta sem malesuada magna mollis euismod.
+
+
+.. code::
+   :class: highlight
+
+   Description lists
+    A description list is perfect for defining terms.
+
+   Euismod
+    Vestibulum id ligula porta felis euismod semper eget lacinia odio sem nec elit.
+    Donec id elit non mi porta gravida at eget metus.
+
+   Malesuada porta
+    Etiam porta sem malesuada magna mollis euismod.
+
+
+Horizontal description
+~~~~~~~~~~~~~~~~~~~~~~
+
+Make terms and descriptions in <dl> line up side-by-side. Starts off stacked
+like default <dl>s, but when the navbar expands, so do these.
+
+.. container:: bs-example
+
+   .. class:: dl-horizontal
+
+      Description lists
+       A description list is perfect for defining terms.
+
+      Euismod
+       Vestibulum id ligula porta felis euismod semper eget lacinia odio sem nec elit.
+       Donec id elit non mi porta gravida at eget metus.
+
+      Malesuada porta
+       Etiam porta sem malesuada magna mollis euismod.
+
+.. code::
+   :class: highlight
+
+   .. class:: dl-horizontal
+
+      Description lists
+       A description list is perfect for defining terms.
+
+      Euismod
+       Vestibulum id ligula porta felis euismod semper eget lacinia odio sem nec elit.
+       Donec id elit non mi porta gravida at eget metus.
+
+      Malesuada porta
+       Etiam porta sem malesuada magna mollis euismod.
+
+
+.. admonition:: :h4:`Auto-truncating`
+   :class: bs-callout bs-callout-info
+
+   Horizontal description lists will truncate terms that are too long to fit in
+   the left column with `text-overflow`. In narrower viewports, they will change
+   to the default stacked layout.
diff --git a/pelican-plugins/bootstrap-rst/doc/CSS.rst b/pelican-plugins/bootstrap-rst/doc/CSS.rst
new file mode 100644
index 0000000000000000000000000000000000000000..44bae155eaf0c9f84d023d7eca5c249bc35c595a
--- /dev/null
+++ b/pelican-plugins/bootstrap-rst/doc/CSS.rst
@@ -0,0 +1,62 @@
+.. ----------------------------------------------------------------------------
+.. default-role:: code
+.. include:: bootstrap-roles.txt
+.. include:: bootstrap-glyphs.txt
+.. ----------------------------------------------------------------------------
+
+.. ----------------------------------------------------------------------------
+.. header::
+
+   .. container:: bs-docs-header
+
+      .. container:: container
+
+         :h1:`Bootstrap RST`
+
+         Overview of the project, its contents, and how to get started with a
+         simple template.
+.. ----------------------------------------------------------------------------
+
+.. ----------------------------------------------------------------------------
+.. footer:: bs-docs-footer
+
+   Bootstrap RST - Copyright (c) 2014 Nicolas P. Rougier
+
+   `Bootstrap <http://getbootstrap.com>`_ - Copyright (c) 2011-2014 Twitter, Inc
+
+   Code licensed under MIT, documentation under CC BY 3.0.
+
+   `Get page source <doc/CSS.rst>`_
+
+   .. class:: bs-docs-footer-links muted
+
+      * `GitHub <https://github.com/rougier/bootstrap-rst>`_
+      * ·
+      * `Examples <examples.html>`_
+      * ·
+      * `Documentation <about.html>`_
+      * ·
+      * `About <about.html>`_
+      * ·
+      * `Issues <https://github.com/rougier/bootstrap-rst/issues>`_
+      * ·
+      * `Releases <https://github.com/rougier/bootstrap-rst/releases>`_
+
+.. ----------------------------------------------------------------------------
+
+
+.. ----------------------------------------------------------------------------
+.. sidebar:: sidebar
+
+   .. contents:: content
+      :depth: 2
+.. ----------------------------------------------------------------------------
+
+.. include:: CSS-overview.txt
+.. include:: CSS-grid-system.txt
+.. include:: CSS-typography.txt
+.. include:: CSS-code.txt
+.. include:: CSS-tables.txt
+.. include:: CSS-buttons.txt
+.. include:: CSS-images.txt
+.. include:: CSS-helpers.txt
diff --git a/pelican-plugins/bootstrap-rst/doc/bootstrap-glyphs.txt b/pelican-plugins/bootstrap-rst/doc/bootstrap-glyphs.txt
new file mode 100644
index 0000000000000000000000000000000000000000..4ad7cc9e55a9207907952d8e640f28efa7c0d1b4
--- /dev/null
+++ b/pelican-plugins/bootstrap-rst/doc/bootstrap-glyphs.txt
@@ -0,0 +1,805 @@
+.. ----------------------------------------------------------------------------
+.. Bootstrap RST - glyph definitions
+.. ----------------------------------------------------------------------------
+
+.. role:: glyphicon-class
+
+.. |asterisk| raw:: html
+
+   <span class="glyphicon glyphicon-asterisk"></span>
+
+.. |plus| raw:: html
+
+   <span class="glyphicon glyphicon-plus"></span>
+
+.. |euro| raw:: html
+
+   <span class="glyphicon glyphicon-euro"></span>
+
+.. |minus| raw:: html
+
+   <span class="glyphicon glyphicon-minus"></span>
+
+.. |cloud| raw:: html
+
+   <span class="glyphicon glyphicon-cloud"></span>
+
+.. |envelope| raw:: html
+
+   <span class="glyphicon glyphicon-envelope"></span>
+
+.. |pencil| raw:: html
+
+   <span class="glyphicon glyphicon-pencil"></span>
+
+.. |glass| raw:: html
+
+   <span class="glyphicon glyphicon-glass"></span>
+
+.. |music| raw:: html
+
+   <span class="glyphicon glyphicon-music"></span>
+
+.. |search| raw:: html
+
+   <span class="glyphicon glyphicon-search"></span>
+
+.. |heart| raw:: html
+
+   <span class="glyphicon glyphicon-heart"></span>
+
+.. |star| raw:: html
+
+   <span class="glyphicon glyphicon-star"></span>
+
+.. |star-empty| raw:: html
+
+   <span class="glyphicon glyphicon-star-empty"></span>
+
+.. |user| raw:: html
+
+   <span class="glyphicon glyphicon-user"></span>
+
+.. |film| raw:: html
+
+   <span class="glyphicon glyphicon-film"></span>
+
+.. |th-large| raw:: html
+
+   <span class="glyphicon glyphicon-th-large"></span>
+
+.. |th| raw:: html
+
+   <span class="glyphicon glyphicon-th"></span>
+
+.. |th-list| raw:: html
+
+   <span class="glyphicon glyphicon-th-list"></span>
+
+.. |ok| raw:: html
+
+   <span class="glyphicon glyphicon-ok"></span>
+
+.. |remove| raw:: html
+
+   <span class="glyphicon glyphicon-remove"></span>
+
+.. |zoom-in| raw:: html
+
+   <span class="glyphicon glyphicon-zoom-in"></span>
+
+.. |zoom-out| raw:: html
+
+   <span class="glyphicon glyphicon-zoom-out"></span>
+
+.. |off| raw:: html
+
+   <span class="glyphicon glyphicon-off"></span>
+
+.. |signal| raw:: html
+
+   <span class="glyphicon glyphicon-signal"></span>
+
+.. |cog| raw:: html
+
+   <span class="glyphicon glyphicon-cog"></span>
+
+.. |trash| raw:: html
+
+   <span class="glyphicon glyphicon-trash"></span>
+
+.. |home| raw:: html
+
+   <span class="glyphicon glyphicon-home"></span>
+
+.. |file| raw:: html
+
+   <span class="glyphicon glyphicon-file"></span>
+
+.. |time| raw:: html
+
+   <span class="glyphicon glyphicon-time"></span>
+
+.. |road| raw:: html
+
+   <span class="glyphicon glyphicon-road"></span>
+
+.. |download-alt| raw:: html
+
+   <span class="glyphicon glyphicon-download-alt"></span>
+
+.. |download| raw:: html
+
+   <span class="glyphicon glyphicon-download"></span>
+
+.. |upload| raw:: html
+
+   <span class="glyphicon glyphicon-upload"></span>
+
+.. |inbox| raw:: html
+
+   <span class="glyphicon glyphicon-inbox"></span>
+
+.. |play-circle| raw:: html
+
+   <span class="glyphicon glyphicon-play-circle"></span>
+
+.. |repeat| raw:: html
+
+   <span class="glyphicon glyphicon-repeat"></span>
+
+.. |refresh| raw:: html
+
+   <span class="glyphicon glyphicon-refresh"></span>
+
+.. |list-alt| raw:: html
+
+   <span class="glyphicon glyphicon-list-alt"></span>
+
+.. |lock| raw:: html
+
+   <span class="glyphicon glyphicon-lock"></span>
+
+.. |flag| raw:: html
+
+   <span class="glyphicon glyphicon-flag"></span>
+
+.. |headphones| raw:: html
+
+   <span class="glyphicon glyphicon-headphones"></span>
+
+.. |volume-off| raw:: html
+
+   <span class="glyphicon glyphicon-volume-off"></span>
+
+.. |volume-down| raw:: html
+
+   <span class="glyphicon glyphicon-volume-down"></span>
+
+.. |volume-up| raw:: html
+
+   <span class="glyphicon glyphicon-volume-up"></span>
+
+.. |qrcode| raw:: html
+
+   <span class="glyphicon glyphicon-qrcode"></span>
+
+.. |barcode| raw:: html
+
+   <span class="glyphicon glyphicon-barcode"></span>
+
+.. |tag| raw:: html
+
+   <span class="glyphicon glyphicon-tag"></span>
+
+.. |tags| raw:: html
+
+   <span class="glyphicon glyphicon-tags"></span>
+
+.. |book| raw:: html
+
+   <span class="glyphicon glyphicon-book"></span>
+
+.. |bookmark| raw:: html
+
+   <span class="glyphicon glyphicon-bookmark"></span>
+
+.. |print| raw:: html
+
+   <span class="glyphicon glyphicon-print"></span>
+
+.. |camera| raw:: html
+
+   <span class="glyphicon glyphicon-camera"></span>
+
+.. |font| raw:: html
+
+   <span class="glyphicon glyphicon-font"></span>
+
+.. |bold| raw:: html
+
+   <span class="glyphicon glyphicon-bold"></span>
+
+.. |italic| raw:: html
+
+   <span class="glyphicon glyphicon-italic"></span>
+
+.. |text-height| raw:: html
+
+   <span class="glyphicon glyphicon-text-height"></span>
+
+.. |text-width| raw:: html
+
+   <span class="glyphicon glyphicon-text-width"></span>
+
+.. |align-left| raw:: html
+
+   <span class="glyphicon glyphicon-align-left"></span>
+
+.. |align-center| raw:: html
+
+   <span class="glyphicon glyphicon-align-center"></span>
+
+.. |align-right| raw:: html
+
+   <span class="glyphicon glyphicon-align-right"></span>
+
+.. |align-justify| raw:: html
+
+   <span class="glyphicon glyphicon-align-justify"></span>
+
+.. |list| raw:: html
+
+   <span class="glyphicon glyphicon-list"></span>
+
+.. |indent-left| raw:: html
+
+   <span class="glyphicon glyphicon-indent-left"></span>
+
+.. |indent-right| raw:: html
+
+   <span class="glyphicon glyphicon-indent-right"></span>
+
+.. |facetime-video| raw:: html
+
+   <span class="glyphicon glyphicon-facetime-video"></span>
+
+.. |picture| raw:: html
+
+   <span class="glyphicon glyphicon-picture"></span>
+
+.. |map-marker| raw:: html
+
+   <span class="glyphicon glyphicon-map-marker"></span>
+
+.. |adjust| raw:: html
+
+   <span class="glyphicon glyphicon-adjust"></span>
+
+.. |tint| raw:: html
+
+   <span class="glyphicon glyphicon-tint"></span>
+
+.. |edit| raw:: html
+
+   <span class="glyphicon glyphicon-edit"></span>
+
+.. |share| raw:: html
+
+   <span class="glyphicon glyphicon-share"></span>
+
+.. |check| raw:: html
+
+   <span class="glyphicon glyphicon-check"></span>
+
+.. |move| raw:: html
+
+   <span class="glyphicon glyphicon-move"></span>
+
+.. |step-backward| raw:: html
+
+   <span class="glyphicon glyphicon-step-backward"></span>
+
+.. |fast-backward| raw:: html
+
+   <span class="glyphicon glyphicon-fast-backward"></span>
+
+.. |backward| raw:: html
+
+   <span class="glyphicon glyphicon-backward"></span>
+
+.. |play| raw:: html
+
+   <span class="glyphicon glyphicon-play"></span>
+
+.. |pause| raw:: html
+
+   <span class="glyphicon glyphicon-pause"></span>
+
+.. |stop| raw:: html
+
+   <span class="glyphicon glyphicon-stop"></span>
+
+.. |forward| raw:: html
+
+   <span class="glyphicon glyphicon-forward"></span>
+
+.. |fast-forward| raw:: html
+
+   <span class="glyphicon glyphicon-fast-forward"></span>
+
+.. |step-forward| raw:: html
+
+   <span class="glyphicon glyphicon-step-forward"></span>
+
+.. |eject| raw:: html
+
+   <span class="glyphicon glyphicon-eject"></span>
+
+.. |chevron-left| raw:: html
+
+   <span class="glyphicon glyphicon-chevron-left"></span>
+
+.. |chevron-right| raw:: html
+
+   <span class="glyphicon glyphicon-chevron-right"></span>
+
+.. |plus-sign| raw:: html
+
+   <span class="glyphicon glyphicon-plus-sign"></span>
+
+.. |minus-sign| raw:: html
+
+   <span class="glyphicon glyphicon-minus-sign"></span>
+
+.. |remove-sign| raw:: html
+
+   <span class="glyphicon glyphicon-remove-sign"></span>
+
+.. |ok-sign| raw:: html
+
+   <span class="glyphicon glyphicon-ok-sign"></span>
+
+.. |question-sign| raw:: html
+
+   <span class="glyphicon glyphicon-question-sign"></span>
+
+.. |info-sign| raw:: html
+
+   <span class="glyphicon glyphicon-info-sign"></span>
+
+.. |screenshot| raw:: html
+
+   <span class="glyphicon glyphicon-screenshot"></span>
+
+.. |remove-circle| raw:: html
+
+   <span class="glyphicon glyphicon-remove-circle"></span>
+
+.. |ok-circle| raw:: html
+
+   <span class="glyphicon glyphicon-ok-circle"></span>
+
+.. |ban-circle| raw:: html
+
+   <span class="glyphicon glyphicon-ban-circle"></span>
+
+.. |arrow-left| raw:: html
+
+   <span class="glyphicon glyphicon-arrow-left"></span>
+
+.. |arrow-right| raw:: html
+
+   <span class="glyphicon glyphicon-arrow-right"></span>
+
+.. |arrow-up| raw:: html
+
+   <span class="glyphicon glyphicon-arrow-up"></span>
+
+.. |arrow-down| raw:: html
+
+   <span class="glyphicon glyphicon-arrow-down"></span>
+
+.. |share-alt| raw:: html
+
+   <span class="glyphicon glyphicon-share-alt"></span>
+
+.. |resize-full| raw:: html
+
+   <span class="glyphicon glyphicon-resize-full"></span>
+
+.. |resize-small| raw:: html
+
+   <span class="glyphicon glyphicon-resize-small"></span>
+
+.. |exclamation-sign| raw:: html
+
+   <span class="glyphicon glyphicon-exclamation-sign"></span>
+
+.. |gift| raw:: html
+
+   <span class="glyphicon glyphicon-gift"></span>
+
+.. |leaf| raw:: html
+
+   <span class="glyphicon glyphicon-leaf"></span>
+
+.. |fire| raw:: html
+
+   <span class="glyphicon glyphicon-fire"></span>
+
+.. |eye-open| raw:: html
+
+   <span class="glyphicon glyphicon-eye-open"></span>
+
+.. |eye-close| raw:: html
+
+   <span class="glyphicon glyphicon-eye-close"></span>
+
+.. |warning-sign| raw:: html
+
+   <span class="glyphicon glyphicon-warning-sign"></span>
+
+.. |plane| raw:: html
+
+   <span class="glyphicon glyphicon-plane"></span>
+
+.. |calendar| raw:: html
+
+   <span class="glyphicon glyphicon-calendar"></span>
+
+.. |random| raw:: html
+
+   <span class="glyphicon glyphicon-random"></span>
+
+.. |comment| raw:: html
+
+   <span class="glyphicon glyphicon-comment"></span>
+
+.. |magnet| raw:: html
+
+   <span class="glyphicon glyphicon-magnet"></span>
+
+.. |chevron-up| raw:: html
+
+   <span class="glyphicon glyphicon-chevron-up"></span>
+
+.. |chevron-down| raw:: html
+
+   <span class="glyphicon glyphicon-chevron-down"></span>
+
+.. |retweet| raw:: html
+
+   <span class="glyphicon glyphicon-retweet"></span>
+
+.. |shopping-cart| raw:: html
+
+   <span class="glyphicon glyphicon-shopping-cart"></span>
+
+.. |folder-close| raw:: html
+
+   <span class="glyphicon glyphicon-folder-close"></span>
+
+.. |folder-open| raw:: html
+
+   <span class="glyphicon glyphicon-folder-open"></span>
+
+.. |resize-vertical| raw:: html
+
+   <span class="glyphicon glyphicon-resize-vertical"></span>
+
+.. |resize-horizontal| raw:: html
+
+   <span class="glyphicon glyphicon-resize-horizontal"></span>
+
+.. |hdd| raw:: html
+
+   <span class="glyphicon glyphicon-hdd"></span>
+
+.. |bullhorn| raw:: html
+
+   <span class="glyphicon glyphicon-bullhorn"></span>
+
+.. |bell| raw:: html
+
+   <span class="glyphicon glyphicon-bell"></span>
+
+.. |certificate| raw:: html
+
+   <span class="glyphicon glyphicon-certificate"></span>
+
+.. |thumbs-up| raw:: html
+
+   <span class="glyphicon glyphicon-thumbs-up"></span>
+
+.. |thumbs-down| raw:: html
+
+   <span class="glyphicon glyphicon-thumbs-down"></span>
+
+.. |hand-right| raw:: html
+
+   <span class="glyphicon glyphicon-hand-right"></span>
+
+.. |hand-left| raw:: html
+
+   <span class="glyphicon glyphicon-hand-left"></span>
+
+.. |hand-up| raw:: html
+
+   <span class="glyphicon glyphicon-hand-up"></span>
+
+.. |hand-down| raw:: html
+
+   <span class="glyphicon glyphicon-hand-down"></span>
+
+.. |circle-arrow-right| raw:: html
+
+   <span class="glyphicon glyphicon-circle-arrow-right"></span>
+
+.. |circle-arrow-left| raw:: html
+
+   <span class="glyphicon glyphicon-circle-arrow-left"></span>
+
+.. |circle-arrow-up| raw:: html
+
+   <span class="glyphicon glyphicon-circle-arrow-up"></span>
+
+.. |circle-arrow-down| raw:: html
+
+   <span class="glyphicon glyphicon-circle-arrow-down"></span>
+
+.. |globe| raw:: html
+
+   <span class="glyphicon glyphicon-globe"></span>
+
+.. |wrench| raw:: html
+
+   <span class="glyphicon glyphicon-wrench"></span>
+
+.. |tasks| raw:: html
+
+   <span class="glyphicon glyphicon-tasks"></span>
+
+.. |filter| raw:: html
+
+   <span class="glyphicon glyphicon-filter"></span>
+
+.. |briefcase| raw:: html
+
+   <span class="glyphicon glyphicon-briefcase"></span>
+
+.. |fullscreen| raw:: html
+
+   <span class="glyphicon glyphicon-fullscreen"></span>
+
+.. |dashboard| raw:: html
+
+   <span class="glyphicon glyphicon-dashboard"></span>
+
+.. |paperclip| raw:: html
+
+   <span class="glyphicon glyphicon-paperclip"></span>
+
+.. |heart-empty| raw:: html
+
+   <span class="glyphicon glyphicon-heart-empty"></span>
+
+.. |link| raw:: html
+
+   <span class="glyphicon glyphicon-link"></span>
+
+.. |phone| raw:: html
+
+   <span class="glyphicon glyphicon-phone"></span>
+
+.. |pushpin| raw:: html
+
+   <span class="glyphicon glyphicon-pushpin"></span>
+
+.. |usd| raw:: html
+
+   <span class="glyphicon glyphicon-usd"></span>
+
+.. |gbp| raw:: html
+
+   <span class="glyphicon glyphicon-gbp"></span>
+
+.. |sort| raw:: html
+
+   <span class="glyphicon glyphicon-sort"></span>
+
+.. |sort-by-alphabet| raw:: html
+
+   <span class="glyphicon glyphicon-sort-by-alphabet"></span>
+
+.. |sort-by-alphabet-alt| raw:: html
+
+   <span class="glyphicon glyphicon-sort-by-alphabet-alt"></span>
+
+.. |sort-by-order| raw:: html
+
+   <span class="glyphicon glyphicon-sort-by-order"></span>
+
+.. |sort-by-order-alt| raw:: html
+
+   <span class="glyphicon glyphicon-sort-by-order-alt"></span>
+
+.. |sort-by-attributes| raw:: html
+
+   <span class="glyphicon glyphicon-sort-by-attributes"></span>
+
+.. |sort-by-attributes-alt| raw:: html
+
+   <span class="glyphicon glyphicon-sort-by-attributes-alt"></span>
+
+.. |unchecked| raw:: html
+
+   <span class="glyphicon glyphicon-unchecked"></span>
+
+.. |expand| raw:: html
+
+   <span class="glyphicon glyphicon-expand"></span>
+
+.. |collapse-down| raw:: html
+
+   <span class="glyphicon glyphicon-collapse-down"></span>
+
+.. |collapse-up| raw:: html
+
+   <span class="glyphicon glyphicon-collapse-up"></span>
+
+.. |log-in| raw:: html
+
+   <span class="glyphicon glyphicon-log-in"></span>
+
+.. |flash| raw:: html
+
+   <span class="glyphicon glyphicon-flash"></span>
+
+.. |log-out| raw:: html
+
+   <span class="glyphicon glyphicon-log-out"></span>
+
+.. |new-window| raw:: html
+
+   <span class="glyphicon glyphicon-new-window"></span>
+
+.. |record| raw:: html
+
+   <span class="glyphicon glyphicon-record"></span>
+
+.. |save| raw:: html
+
+   <span class="glyphicon glyphicon-save"></span>
+
+.. |open| raw:: html
+
+   <span class="glyphicon glyphicon-open"></span>
+
+.. |saved| raw:: html
+
+   <span class="glyphicon glyphicon-saved"></span>
+
+.. |import| raw:: html
+
+   <span class="glyphicon glyphicon-import"></span>
+
+.. |export| raw:: html
+
+   <span class="glyphicon glyphicon-export"></span>
+
+.. |send| raw:: html
+
+   <span class="glyphicon glyphicon-send"></span>
+
+.. |floppy-disk| raw:: html
+
+   <span class="glyphicon glyphicon-floppy-disk"></span>
+
+.. |floppy-saved| raw:: html
+
+   <span class="glyphicon glyphicon-floppy-saved"></span>
+
+.. |floppy-remove| raw:: html
+
+   <span class="glyphicon glyphicon-floppy-remove"></span>
+
+.. |floppy-save| raw:: html
+
+   <span class="glyphicon glyphicon-floppy-save"></span>
+
+.. |floppy-open| raw:: html
+
+   <span class="glyphicon glyphicon-floppy-open"></span>
+
+.. |credit-card| raw:: html
+
+   <span class="glyphicon glyphicon-credit-card"></span>
+
+.. |transfer| raw:: html
+
+   <span class="glyphicon glyphicon-transfer"></span>
+
+.. |cutlery| raw:: html
+
+   <span class="glyphicon glyphicon-cutlery"></span>
+
+.. |header| raw:: html
+
+   <span class="glyphicon glyphicon-header"></span>
+
+.. |compressed| raw:: html
+
+   <span class="glyphicon glyphicon-compressed"></span>
+
+.. |earphone| raw:: html
+
+   <span class="glyphicon glyphicon-earphone"></span>
+
+.. |phone-alt| raw:: html
+
+   <span class="glyphicon glyphicon-phone-alt"></span>
+
+.. |tower| raw:: html
+
+   <span class="glyphicon glyphicon-tower"></span>
+
+.. |stats| raw:: html
+
+   <span class="glyphicon glyphicon-stats"></span>
+
+.. |sd-video| raw:: html
+
+   <span class="glyphicon glyphicon-sd-video"></span>
+
+.. |hd-video| raw:: html
+
+   <span class="glyphicon glyphicon-hd-video"></span>
+
+.. |subtitles| raw:: html
+
+   <span class="glyphicon glyphicon-subtitles"></span>
+
+.. |sound-stereo| raw:: html
+
+   <span class="glyphicon glyphicon-sound-stereo"></span>
+
+.. |sound-dolby| raw:: html
+
+   <span class="glyphicon glyphicon-sound-dolby"></span>
+
+.. |sound-5-1| raw:: html
+
+   <span class="glyphicon glyphicon-sound-5-1"></span>
+
+.. |sound-6-1| raw:: html
+
+   <span class="glyphicon glyphicon-sound-6-1"></span>
+
+.. |sound-7-1| raw:: html
+
+   <span class="glyphicon glyphicon-sound-7-1"></span>
+
+.. |copyright-mark| raw:: html
+
+   <span class="glyphicon glyphicon-copyright-mark"></span>
+
+.. |registration-mark| raw:: html
+
+   <span class="glyphicon glyphicon-registration-mark"></span>
+
+.. |cloud-download| raw:: html
+
+   <span class="glyphicon glyphicon-cloud-download"></span>
+
+.. |cloud-upload| raw:: html
+
+   <span class="glyphicon glyphicon-cloud-upload"></span>
+
+.. |tree-conifer| raw:: html
+
+   <span class="glyphicon glyphicon-tree-conifer"></span>
+
+.. |tree-deciduous| raw:: html
+
+   <span class="glyphicon glyphicon-tree-deciduous"></span>
diff --git a/pelican-plugins/bootstrap-rst/doc/bootstrap-roles.txt b/pelican-plugins/bootstrap-rst/doc/bootstrap-roles.txt
new file mode 100644
index 0000000000000000000000000000000000000000..7bd53610d58a84bfa8a2b19da041886dfcf6ede8
--- /dev/null
+++ b/pelican-plugins/bootstrap-rst/doc/bootstrap-roles.txt
@@ -0,0 +1,21 @@
+.. ----------------------------------------------------------------------------
+.. Bootstrap RST - role definitions
+.. ----------------------------------------------------------------------------
+
+.. role:: small
+.. role:: kbd
+.. role:: badge
+
+.. role:: text-muted
+.. role:: text-primary
+.. role:: text-success
+.. role:: text-info
+.. role:: text-warning
+.. role:: text-danger
+
+.. role:: bg-muted
+.. role:: bg-primary
+.. role:: bg-success
+.. role:: bg-info
+.. role:: bg-warning
+.. role:: bg-danger
diff --git a/pelican-plugins/bootstrap-rst/doc/components-alerts.txt b/pelican-plugins/bootstrap-rst/doc/components-alerts.txt
new file mode 100644
index 0000000000000000000000000000000000000000..6fcd68fe530d3ac7018e3cd4532ace04ee7dfb33
--- /dev/null
+++ b/pelican-plugins/bootstrap-rst/doc/components-alerts.txt
@@ -0,0 +1,70 @@
+Alerts
+===============================================================================
+.. lead:: Provide contextual feedback messages for typical user actions with
+          the handful of available and flexible alert messages. For inline
+          dismissal, use the `alerts jQuery plugin
+          <http://getbootstrap.com/javascript/>`_.
+.. ----------------------------------------------------------------------------
+
+Examples
+--------
+
+Wrap any text and an optional dismiss button in `.alert` and one of the four
+contextual classes (e.g., `.alert-success`) for basic alert messages.
+
+.. callout:: danger
+
+   :h4:`No default class`
+   Alerts don't have default classes, only base and modifier classes. A default
+   gray alert doesn't make too much sense, so you're required to specify a type
+   via contextual class. Choose from success, info, warning, or danger.
+
+
+.. container:: bs-example
+
+   .. alert:: **Well done!** You successfully read this important alert message.
+      :type: success
+
+   .. alert:: **Heads up!** This alert needs your attention, but it's not super important.
+      :type: info
+
+   .. alert:: **Warning!** Better check yourself, you're not looking too good.
+      :type: warning
+
+   .. alert:: **Oh snap!** Change a few things up and try submitting again.
+      :type: danger
+
+
+.. code::
+   :class: highlight
+
+   .. alert:: **Well done!** You successfully read this important alert message.
+      :type: success
+
+   .. alert:: **Heads up!** This alert needs your attention, but it's not super important.
+      :type: info
+
+   .. alert:: **Warning!** Better check yourself, you're not looking too good.
+      :type: warning
+
+   .. alert:: **Oh snap!** Change a few things up and try submitting again.
+      :type: danger
+
+
+Dismissable alerts
+------------------
+
+Build on any alert by adding an optional `.alert-dismissable` and close button.
+
+.. container:: bs-example
+
+   .. alert:: **Warning!** Better check yourself, you're not looking too good.
+      :type: warning
+      :dismissable:
+
+
+.. callout:: warning
+
+   :h4:`Ensure proper behavior across all devices`
+   Be sure to use the `<button>` element with the `data-dismiss="alert"` data
+   attribute.
diff --git a/pelican-plugins/bootstrap-rst/doc/components-badges.txt b/pelican-plugins/bootstrap-rst/doc/components-badges.txt
new file mode 100644
index 0000000000000000000000000000000000000000..2871fb43677679ca9fd7f6cd2f8904887c611c4e
--- /dev/null
+++ b/pelican-plugins/bootstrap-rst/doc/components-badges.txt
@@ -0,0 +1,15 @@
+Badges
+===============================================================================
+.. lead:: Easily highlight new or unread items by adding a <span class="badge">
+          to links, Bootstrap navs, and more.
+
+
+.. container:: bs-example
+
+  `Inbox <#>`_ :badge:`42`
+
+
+Self collapsing
+
+When there are no new or unread items, badges will simply collapse (via CSS's
+`:empty` selector) provided no content exists within.
diff --git a/pelican-plugins/bootstrap-rst/doc/components-breadcrumbs.txt b/pelican-plugins/bootstrap-rst/doc/components-breadcrumbs.txt
new file mode 100644
index 0000000000000000000000000000000000000000..b00ed8038a5073d39c4ec39687823169d7de04b1
--- /dev/null
+++ b/pelican-plugins/bootstrap-rst/doc/components-breadcrumbs.txt
@@ -0,0 +1,43 @@
+Breadcrumbs
+===============================================================================
+.. lead:: Indicate the current page's location within a navigational hierarchy.
+
+
+Separators are automatically added in CSS through `:before` and `content.`
+
+
+.. container:: bs-example
+
+   .. class:: breadcrumb
+
+      * Home
+
+   .. class:: breadcrumb
+
+      * Home
+      * Library
+
+   .. class:: breadcrumb
+
+      * Home
+      * Library
+      * Data
+
+
+.. code::
+   :class: highlight
+
+   .. class:: breadcrumb
+
+      * Home
+
+   .. class:: breadcrumb
+
+      * Home
+      * Library
+
+   .. class:: breadcrumb
+
+      * Home
+      * Library
+      * Data
diff --git a/pelican-plugins/bootstrap-rst/doc/components-glyphicons.txt b/pelican-plugins/bootstrap-rst/doc/components-glyphicons.txt
new file mode 100644
index 0000000000000000000000000000000000000000..9c75c328f908e47eddcbb59032da0e6538385fb1
--- /dev/null
+++ b/pelican-plugins/bootstrap-rst/doc/components-glyphicons.txt
@@ -0,0 +1,37 @@
+Glyphicons
+===============================================================================
+
+Available glyphs
+----------------
+
+Includes 200 glyphs in font format from the Glyphicon Halflings
+set. `Glyphicons <http://glyphicons.com>`_ Halflings are normally not available
+for free, but their creator has made them available for Bootstrap free of
+cost. As a thank you, we only ask that you include a link back to `Glyphicons
+<http://glyphicons.com>`_ whenever possible.
+
+.. include:: glyphicons-list.txt
+
+How to use
+----------
+
+For performance reasons, all icons require a base class and individual icon
+class. To use, place the following code just about anywhere. Be sure to leave a
+space between the icon and text for proper padding.
+
+.. callout:: danger
+
+   :h4:`Don't mix with other`
+   Icon classes cannot be directly combined with other components. They should
+   not be used along with other classes on the same element. Instead, add a
+   nested `<span>` and apply the icon classes to the `<span>`.
+
+.. container:: bs-example
+
+   |search| |star|
+
+
+.. code::
+   :class: highlight
+
+   |search| |star|
diff --git a/pelican-plugins/bootstrap-rst/doc/components-jumbotron.txt b/pelican-plugins/bootstrap-rst/doc/components-jumbotron.txt
new file mode 100644
index 0000000000000000000000000000000000000000..4175c4f56fe555fcc952f0314dae68902033a665
--- /dev/null
+++ b/pelican-plugins/bootstrap-rst/doc/components-jumbotron.txt
@@ -0,0 +1,45 @@
+Jumbotron
+===============================================================================
+
+A lightweight, flexible component that can optionally extend the entire
+viewport to showcase key content on your site.
+
+.. container:: bs-example
+
+   .. jumbotron::
+
+      :h1:`Hello, world!`
+
+      This is a simple hero unit, a simple jumbotron-style component for
+      calling extra attention to featured content or information.
+
+      .. button:: Learn more
+         :class: primary large
+
+.. code::
+   :class: highlight
+
+
+   .. jumbotron::
+
+      :h1:`Hello, world!`
+
+      This is a simple hero unit, a simple jumbotron-style component for
+      calling extra attention to featured content or information.
+
+      .. button:: Learn more
+         :class: primary large
+
+
+To make the jumbotron full width, and without rounded corners, place it outside
+all .containers and instead add a .container within.
+
+.. code::
+   :class: highlight
+
+
+   .. jumbotron::
+
+      .. container::
+
+         ...
diff --git a/pelican-plugins/bootstrap-rst/doc/components-labels.txt b/pelican-plugins/bootstrap-rst/doc/components-labels.txt
new file mode 100644
index 0000000000000000000000000000000000000000..3be35940ca0813f7339896a887c6f918627e02be
--- /dev/null
+++ b/pelican-plugins/bootstrap-rst/doc/components-labels.txt
@@ -0,0 +1,64 @@
+Labels
+===============================================================================
+
+Examples
+--------
+
+.. container:: bs-example
+
+   :h1:`Example heading <span class="label label-default">New</span>`
+
+   :h2:`Example heading <span class="label label-default">New</span>`
+
+   :h3:`Example heading <span class="label label-default">New</span>`
+
+   :h4:`Example heading <span class="label label-default">New</span>`
+
+   :h5:`Example heading <span class="label label-default">New</span>`
+
+   :h6:`Example heading <span class="label label-default">New</span>`
+
+
+.. code::
+   :class: highlight
+
+   h1. Example heading :label-default:`New`
+   ========================================
+
+   h2. Example heading :label-default:`New`
+   ----------------------------------------
+
+   h3. Example heading :label-default:`New`
+   ++++++++++++++++++++++++++++++++++++++++
+
+   h4. Example heading :label-default:`New`
+   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+   h5. Example heading :label-default:`New`
+   ****************************************
+
+   h6. Example heading :label-default:`New`
+   ¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨
+
+
+Available variations
+--------------------
+
+.. container:: bs-example
+
+   :label-default:`Default`
+   :label-primary:`Primary`
+   :label-success:`Success`
+   :label-info:`Info`
+   :label-warning:`Warning`
+   :label-danger:`Danger`
+
+.. code::
+   :class: highlight
+
+   :label-default:`Default`
+   :label-primary:`Primary`
+   :label-success:`Success`
+   :label-info:`Info`
+   :label-warning:`Warning`
+   :label-danger:`Danger`
diff --git a/pelican-plugins/bootstrap-rst/doc/components-page-header.txt b/pelican-plugins/bootstrap-rst/doc/components-page-header.txt
new file mode 100644
index 0000000000000000000000000000000000000000..65b10ca4a0da02076d8fe827d9f4da6aa7d57c5f
--- /dev/null
+++ b/pelican-plugins/bootstrap-rst/doc/components-page-header.txt
@@ -0,0 +1,26 @@
+Page header
+===============================================================================
+
+A simple shell for an `h1` to appropriately space out and segment sections of
+content on a page. It can utilize the `h1`'s default `small` element, as well as
+most other components (with additional styles).
+
+
+
+.. lead:: Easily highlight new or unread items by adding a <span class="badge">
+          to links, Bootstrap navs, and more.
+
+
+.. container:: bs-example
+
+   .. container:: page-header
+
+      :h1:`Example page header <small>Subtext for header</small>`
+
+
+.. code::
+   :class: highlight
+
+   .. page-header::
+
+      :h1:`Example page header <small>Subtext for header</small>`
diff --git a/pelican-plugins/bootstrap-rst/doc/components-pagination.txt b/pelican-plugins/bootstrap-rst/doc/components-pagination.txt
new file mode 100644
index 0000000000000000000000000000000000000000..55cd04d260305782a0cdecaf84d7678797022ae3
--- /dev/null
+++ b/pelican-plugins/bootstrap-rst/doc/components-pagination.txt
@@ -0,0 +1,210 @@
+Pagination
+===============================================================================
+.. lead:: Provide pagination links for your site or app with the multi-page
+          pagination component, or the simpler pager alternative.
+.. ----------------------------------------------------------------------------
+
+Default pagination
+------------------
+
+Simple pagination inspired by Rdio, great for apps and search results. The
+large block is hard to miss, easily scalable, and provides large click areas.
+
+
+
+.. container:: bs-example
+
+   .. class:: pagination
+
+      * `« <#>`_
+      * `1 <#>`_
+      * `2 <#>`_
+      * `3 <#>`_
+      * `4 <#>`_
+      * `5 <#>`_
+      * `» <#>`_
+
+.. code::
+   :class: highlight
+
+   .. class:: pagination
+
+      * `« <#>`_
+      * `1 <#>`_
+      * `2 <#>`_
+      * `3 <#>`_
+      * `4 <#>`_
+      * `5 <#>`_
+      * `» <#>`_
+
+
+Disabled and active states
+--------------------------
+
+Links are customizable for different circumstances. Use `.disabled` for
+unclickable links and `.active` to indicate the current page.
+
+
+.. container:: bs-example
+
+   .. class:: pagination
+
+      * .. item-class:: disabled
+        `« <#>`_
+      * .. item-class:: active
+        `1 <#>`_
+      * `2 <#>`_
+      * `3 <#>`_
+      * `4 <#>`_
+      * `5 <#>`_
+      * `» <#>`_
+
+.. code::
+   :class: highlight
+
+   .. class:: pagination
+
+      * .. item-class:: disabled
+        `« <#>`_
+      * .. item-class:: active
+        `1 <#>`_
+      * `2 <#>`_
+      * `3 <#>`_
+      * `4 <#>`_
+      * `5 <#>`_
+      * `» <#>`_
+
+Sizing
+------
+
+Fancy larger or smaller pagination? Add `.pagination-lg` or `.pagination-sm` for
+additional sizes.
+
+.. container:: bs-example
+
+   .. class:: pagination pagination-lg
+
+      * `« <#>`_
+      * `1 <#>`_
+      * `2 <#>`_
+      * `3 <#>`_
+      * `4 <#>`_
+      * `5 <#>`_
+      * `» <#>`_
+
+   |
+
+   .. class:: pagination
+
+      * `« <#>`_
+      * `1 <#>`_
+      * `2 <#>`_
+      * `3 <#>`_
+      * `4 <#>`_
+      * `5 <#>`_
+      * `» <#>`_
+
+   |
+
+   .. class:: pagination pagination-sm
+
+      * `« <#>`_
+      * `1 <#>`_
+      * `2 <#>`_
+      * `3 <#>`_
+      * `4 <#>`_
+      * `5 <#>`_
+      * `» <#>`_
+
+.. code::
+   :class: highlight
+
+
+   .. class:: pagination pagination-lg
+
+      ...
+
+   .. class:: pagination
+
+      ...
+
+   .. class:: pagination pagination-sm
+
+      ...
+
+
+Pager
+===============================================================================
+
+Quick previous and next links for simple pagination implementations with light
+markup and styles. It's great for simple sites like blogs or magazines.
+
+Default example
+---------------
+
+By default, the pager centers links.
+
+.. container:: bs-example
+
+   .. class:: pager
+
+      * `Previous <#>`_
+      * `Next <#>`_
+
+.. code::
+   :class: highlight
+
+   .. class:: pager
+
+      * `Previous <#>`_
+      * `Next <#>`_
+
+
+Aligned links
+-------------
+
+Alternatively, you can align each link to the sides:
+
+.. container:: bs-example
+
+   .. class:: pager
+
+      * .. item-class:: previous
+        `← Older <#>`_
+      * .. item-class:: next
+        `Newer → <#>`_
+
+.. code::
+   :class: highlight
+
+   .. class:: pager
+
+      * .. item-class:: previous
+        `← Older <#>`_
+      * .. item-class:: next
+        `Newer → <#>`_
+
+
+Optional disabled state
+-----------------------
+
+Pager links also use the general `disabled` utility class from the pagination.
+
+.. container:: bs-example
+
+   .. class:: pager
+
+      * .. item-class:: previous disabled
+        `← Older <#>`_
+      * .. item-class:: next
+        `Newer → <#>`_
+
+.. code::
+   :class: highlight
+
+   .. class:: pager
+
+      * .. item-class:: previous disabled
+        `← Older <#>`_
+      * .. item-class:: next
+        `Newer → <#>`_
diff --git a/pelican-plugins/bootstrap-rst/doc/components-progress.txt b/pelican-plugins/bootstrap-rst/doc/components-progress.txt
new file mode 100644
index 0000000000000000000000000000000000000000..11e634a646ecbca8767bd5c40b94c227098aa553
--- /dev/null
+++ b/pelican-plugins/bootstrap-rst/doc/components-progress.txt
@@ -0,0 +1,140 @@
+Progress bars
+===============================================================================
+.. lead:: Provide up-to-date feedback on the progress of a workflow or action
+          with simple yet flexible progress bars.
+.. ----------------------------------------------------------------------------
+
+.. callout:: danger
+
+   :h4:`Cross-browser compatibility`
+   Progress bars use CSS3 transitions and animations to achieve some of their
+   effects. These features are not supported in Internet Explorer 9 and below
+   or older versions of Firefox. Opera 12 does not support animations.
+
+
+Basic example
+-------------
+
+Default progress bar.
+
+.. container:: bs-example
+
+   .. progress:: 60%
+
+.. code::
+   :class: highlight
+
+   .. progress:: 60%
+
+
+With label
+----------
+
+Remove the `.sr-only` class from within the progress bar to show a visible
+percentage. For low percentages, consider adding a `min-width` to ensure the
+label's text is fully visible.
+
+.. container:: bs-example
+
+   .. progress:: 60%
+      :label: 60%
+
+.. code::
+   :class: highlight
+
+   .. progress:: 60%
+      :label: 60%
+
+
+Contextual alternatives
+-----------------------
+
+Progress bars use some of the same button and alert classes for consistent
+styles.
+
+.. container:: bs-example
+
+   .. progress:: 40%
+      :class: success
+
+   .. progress:: 20%
+      :class: info
+
+   .. progress:: 60%
+      :class: warning
+
+   .. progress:: 80%
+      :class: danger
+
+.. code::
+   :class: highlight
+
+   .. progress:: 40%
+      :class: success
+
+   .. progress:: 20%
+      :class: info
+
+   .. progress:: 60%
+      :class: warning
+
+   .. progress:: 80%
+      :class: danger
+
+
+Striped
+-------
+
+Uses a gradient to create a striped effect. Not available in IE8.
+
+.. container:: bs-example
+
+   .. progress:: 40%
+      :class: success striped
+
+   .. progress:: 20%
+      :class: info striped
+
+   .. progress:: 60%
+      :class: warning striped
+
+   .. progress:: 80%
+      :class: danger striped
+
+.. code::
+   :class: highlight
+
+   .. progress:: 40%
+      :class: success striped
+
+   .. progress:: 20%
+      :class: info striped
+
+   .. progress:: 60%
+      :class: warning striped
+
+   .. progress:: 80%
+      :class: danger striped
+
+
+Animated
+--------
+
+Add `.active` to `.progress-striped` to animate the stripes right to left. Not
+available in IE9 and below.
+
+.. container:: bs-example
+
+   .. progress:: 45%
+      :class: active
+
+.. code::
+   :class: highlight
+
+   .. progress:: 45%
+      :class: active
+
+Stacked
+-------
+
+:text-muted:`N/A`
diff --git a/pelican-plugins/bootstrap-rst/doc/components-thumbnails.txt b/pelican-plugins/bootstrap-rst/doc/components-thumbnails.txt
new file mode 100644
index 0000000000000000000000000000000000000000..a558658a3fb95e1e552617c8813c11c6efe16a38
--- /dev/null
+++ b/pelican-plugins/bootstrap-rst/doc/components-thumbnails.txt
@@ -0,0 +1,157 @@
+Thumbnails
+===============================================================================
+.. lead:: Extend Bootstrap's grid system with the thumbnail component to easily
+          display grids of images, videos, text, and more.
+
+Default example
+---------------
+
+By default, Bootstrap's thumbnails are designed to showcase linked images with
+minimal required markup.
+
+.. container:: bs-example
+
+   .. row::
+
+      .. column::
+         :width: 3
+
+         .. image:: 171x180.png
+            :class: thumbnail
+
+      .. column::
+         :width: 3
+
+         .. image:: 171x180.png
+            :class: thumbnail
+
+      .. column::
+         :width: 3
+
+         .. image:: 171x180.png
+            :class: thumbnail
+
+      .. column::
+         :width: 3
+
+         .. image:: 171x180.png
+            :class: thumbnail
+
+.. code::
+   :class: highlight
+
+      .. column::
+         :width: 3
+
+         .. image:: 171x180.png
+            :class: thumbnail
+
+      .. column::
+         :width: 3
+
+         .. image:: 171x180.png
+            :class: thumbnail
+
+      .. column::
+         :width: 3
+
+         .. image:: 171x180.png
+            :class: thumbnail
+
+      .. column::
+         :width: 3
+
+         .. image:: 171x180.png
+            :class: thumbnail
+
+Custom content
+--------------
+
+With a bit of extra markup, it's possible to add any kind of HTML content like
+headings, paragraphs, or buttons into thumbnails.
+
+.. container:: bs-example
+
+   .. row::
+
+      .. column::
+         :width: 4
+
+         .. thumbnail::
+
+            .. image:: 300x200.png
+
+            .. caption::
+
+               :h3:`Thumbnail label`
+
+               Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec
+               id elit non mi porta gravida at eget metus. Nullam id dolor id nibh
+               ultricies vehicula ut id elit.
+
+               .. button:: Button
+                  :class: primary
+
+               .. button:: Button
+
+
+      .. column::
+         :width: 4
+
+         .. thumbnail::
+
+            .. image:: 300x200.png
+
+            .. caption::
+
+               :h3:`Thumbnail label`
+
+               Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec
+               id elit non mi porta gravida at eget metus. Nullam id dolor id nibh
+               ultricies vehicula ut id elit.
+
+               .. button:: Button
+                  :class: primary
+
+               .. button:: Button
+
+
+      .. column::
+         :width: 4
+
+         .. thumbnail::
+
+            .. image:: 300x200.png
+
+            .. caption::
+
+               :h3:`Thumbnail label`
+
+               Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec
+               id elit non mi porta gravida at eget metus. Nullam id dolor id nibh
+               ultricies vehicula ut id elit.
+
+               .. button:: Button
+                  :class: primary
+
+               .. button:: Button
+
+.. code::
+   :class: highlight
+
+   .. thumbnail::
+
+      .. image:: 300x200.png
+
+      .. caption::
+
+         :h3:`Thumbnail label`
+
+         Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec
+         id elit non mi porta gravida at eget metus. Nullam id dolor id nibh
+         ultricies vehicula ut id elit.
+
+         .. button:: Button
+            :class: primary
+
+         .. button:: Button
diff --git a/pelican-plugins/bootstrap-rst/doc/components-wells.txt b/pelican-plugins/bootstrap-rst/doc/components-wells.txt
new file mode 100644
index 0000000000000000000000000000000000000000..b5462af814094d234ac30b835b83600832312b3b
--- /dev/null
+++ b/pelican-plugins/bootstrap-rst/doc/components-wells.txt
@@ -0,0 +1,53 @@
+Wells
+===============================================================================
+
+Default well
+------------
+
+Use the well as a simple effect on an element to give it an inset effect.
+
+.. container:: bs-example
+
+   .. class:: well
+
+      Look, I'm in a well!
+
+.. code::
+   :class: highlight
+
+   .. class:: well
+
+      Look, I'm in a well!
+
+
+Optional classes
+----------------
+
+Control padding and rounded corners with two optional modifier classes.
+
+.. container:: bs-example
+
+   .. class:: well well-lg
+
+      Look, I'm in a well!
+
+.. code::
+   :class: highlight
+
+   .. class:: well well-lg
+
+      Look, I'm in a well!
+
+
+.. container:: bs-example
+
+   .. class:: well well-sm
+
+      Look, I'm in a well!
+
+.. code::
+   :class: highlight
+
+   .. class:: well well-sm
+
+      Look, I'm in a well!
diff --git a/pelican-plugins/bootstrap-rst/doc/components.rst b/pelican-plugins/bootstrap-rst/doc/components.rst
new file mode 100644
index 0000000000000000000000000000000000000000..54846cbf7e7f7d4ff9b22d06d6e54e22d6f5fcc2
--- /dev/null
+++ b/pelican-plugins/bootstrap-rst/doc/components.rst
@@ -0,0 +1,65 @@
+.. ----------------------------------------------------------------------------
+.. default-role:: code
+.. include:: bootstrap-roles.txt
+.. include:: bootstrap-glyphs.txt
+.. ----------------------------------------------------------------------------
+
+.. ----------------------------------------------------------------------------
+.. header::
+
+   .. container:: bs-docs-header
+
+      .. container:: container
+
+         :h1:`Components`
+
+         Over a dozen reusable components built to provide iconography,
+         dropdowns, input groups, navigation, alerts, and much more.
+.. ----------------------------------------------------------------------------
+
+.. ----------------------------------------------------------------------------
+.. footer:: bs-docs-footer
+
+   Bootstrap RST - Copyright (c) 2014 Nicolas P. Rougier
+
+   `Bootstrap <http://getbootstrap.com>`_ - Copyright (c) 2011-2014 Twitter, Inc
+
+   Code licensed under MIT, documentation under CC BY 3.0.
+
+   `Get page source <doc/components.rst>`_
+
+   .. class:: bs-docs-footer-links muted
+
+      * `GitHub <https://github.com/rougier/bootstrap-rst>`_
+      * ·
+      * `Examples <examples.html>`_
+      * ·
+      * `Documentation <about.html>`_
+      * ·
+      * `About <about.html>`_
+      * ·
+      * `Issues <https://github.com/rougier/bootstrap-rst/issues>`_
+      * ·
+      * `Releases <https://github.com/rougier/bootstrap-rst/releases>`_
+.. ----------------------------------------------------------------------------
+
+
+.. ----------------------------------------------------------------------------
+.. sidebar:: sidebar
+
+   .. contents:: content
+      :depth: 2
+.. ----------------------------------------------------------------------------
+
+
+.. include:: components-glyphicons.txt
+.. include:: components-breadcrumbs.txt
+.. include:: components-pagination.txt
+.. include:: components-labels.txt
+.. include:: components-badges.txt
+.. include:: components-jumbotron.txt
+.. include:: components-page-header.txt
+.. include:: components-thumbnails.txt
+.. include:: components-alerts.txt
+.. include:: components-progress.txt
+.. include:: components-wells.txt
diff --git a/pelican-plugins/bootstrap-rst/doc/getting-started.rst b/pelican-plugins/bootstrap-rst/doc/getting-started.rst
new file mode 100644
index 0000000000000000000000000000000000000000..dfd644e844ab14177a13548327da5a2d45a126bc
--- /dev/null
+++ b/pelican-plugins/bootstrap-rst/doc/getting-started.rst
@@ -0,0 +1,160 @@
+.. default-role:: code
+.. role:: text-muted
+.. role:: sr-only
+
+.. |caret| raw:: html
+
+   <span class="caret"></span>
+
+.. include:: glyphicons-defs.txt
+
+
+.. ----------------------------------------------------------------------------
+.. header::
+   :class: bs-docs-header
+
+   .. container:: container
+
+      :h1:`Getting started`
+
+      An overview of Bootstrap RST, how to download and use, basic
+      templates and examples, and more.
+.. ----------------------------------------------------------------------------
+
+.. ----------------------------------------------------------------------------
+.. footer::
+   :class: bs-docs-footer
+
+   Bootstrap RST - Copyright 2014 Nicolas P. Rougier
+
+   Code licensed under MIT, documentation under CC BY 3.0.
+
+   `Get page source <getting-started.rst>`_
+
+   .. class:: bs-docs-footer-links muted
+
+      * GitHub
+      * ·
+      * Examples
+      * ·
+      * Documentation
+      * ·
+      * About
+      * ·
+      * Issues
+      * ·
+      * Releases
+.. ----------------------------------------------------------------------------
+
+
+.. ----------------------------------------------------------------------------
+.. sidebar:: sidebar
+
+   .. contents:: content
+      :depth: 2
+.. ----------------------------------------------------------------------------
+
+
+Download
+===============================================================================
+.. lead:: Bootstrap RST has a few easy ways to quickly get started, each one
+          appealing to a different skill level and use case. Read through to
+          see what suits your particular needs.
+.. ----------------------------------------------------------------------------
+
+
+.. row:: bs-downloads
+
+   .. -------------------------------------------------------------------------
+   .. column::
+      :width: 4
+
+      :h3:`Bootstrap RST`
+
+      Compiled and minified CSS, JavaScript, and fonts. No docs or original
+      source files are included.
+
+      .. button:: Get Bootstrap RST
+         :class: outline large
+   .. -------------------------------------------------------------------------
+   .. column::
+      :width: 4
+
+      :h3:`Source`
+
+      Compiled and minified CSS, JavaScript, and fonts. No docs or original
+      source files are included.
+
+      .. button:: Download source
+         :class: outline large
+         :target: #
+   .. -------------------------------------------------------------------------
+   .. column::
+      :width: 4
+
+      :h3:`Bootstrap`
+
+      Compiled and minified CSS, JavaScript, and fonts. No docs or original
+      source files are included.
+
+      .. button:: Download Bootstrap
+         :class: outline large
+   .. -------------------------------------------------------------------------
+
+
+Browser and device support
+===============================================================================
+.. lead:: Bootstrap is built to work best in the latest desktop and mobile
+          browsers, meaning older browsers might display differently styled,
+          though fully functional, renderings of certain components.
+
+Supported browsers
+------------------
+
+Specifically, we support the latest versions of the following browsers and
+platforms. On Windows, we support Internet Explorer 8-11. More specific support
+information is provided below.
+
+.. class:: table table-bordered table-striped
+
+   +----------+--------+-------------------+-------------------+----------+-------------------+
+   |          | Chrome | Firefox           | Internet Explorer | Opera    | Safari            |
+   +==========+========+===================+===================+==========+===================+
+   | Android  | |ok|   | |remove|          | :text-muted:`N/A` | |remove| | :text-muted:`N/A` |
+   +----------+--------+-------------------+-------------------+----------+-------------------+
+   | iOS      | |ok|   | :text-muted:`N/A` | :text-muted:`N/A` | |remove| | |ok|              |
+   +----------+--------+-------------------+-------------------+----------+-------------------+
+   | Mac OS X |	|ok|   | |ok|              | :text-muted:`N/A` | |ok|     | |ok|              |
+   +----------+--------+-------------------+-------------------+----------+-------------------+
+   | Windows  |	|ok|   | |ok|	           | |ok|              | |ok|     | |remove|          |
+   +----------+--------+-------------------+-------------------+----------+-------------------+
+
+
+.. ----------------------------------------------------------------------------
+.. container:: btn-group
+
+   .. button:: 1
+   .. button:: 2
+   .. container:: btn-group
+
+      .. button:: |caret| :sr-only:`Dropdown`
+         :class: toggle
+
+      .. class:: dropdown-menu
+
+         * `Drowpdown link <www.loria.fr>`_
+         * `Drowpdown link <www.loria.fr>`_
+.. ----------------------------------------------------------------------------
+
+.. ----------------------------------------------------------------------------
+.. container:: btn-group
+
+   .. button:: Default
+   .. button:: |caret| :sr-only:`Dropdown`
+      :class: toggle
+
+   .. class:: dropdown-menu
+
+      * `Drowpdown link <www.loria.fr>`_
+      * `Drowpdown link <www.loria.fr>`_
+.. ----------------------------------------------------------------------------
diff --git a/pelican-plugins/bootstrap-rst/doc/glyphicons-defs.txt b/pelican-plugins/bootstrap-rst/doc/glyphicons-defs.txt
new file mode 100644
index 0000000000000000000000000000000000000000..277ca93375712a8434a620e2c0c6729c40965b8a
--- /dev/null
+++ b/pelican-plugins/bootstrap-rst/doc/glyphicons-defs.txt
@@ -0,0 +1,801 @@
+.. role:: glyphicon-class
+
+.. |asterisk| raw:: html
+
+   <span class="glyphicon glyphicon-asterisk"></span>
+
+.. |plus| raw:: html
+
+   <span class="glyphicon glyphicon-plus"></span>
+
+.. |euro| raw:: html
+
+   <span class="glyphicon glyphicon-euro"></span>
+
+.. |minus| raw:: html
+
+   <span class="glyphicon glyphicon-minus"></span>
+
+.. |cloud| raw:: html
+
+   <span class="glyphicon glyphicon-cloud"></span>
+
+.. |envelope| raw:: html
+
+   <span class="glyphicon glyphicon-envelope"></span>
+
+.. |pencil| raw:: html
+
+   <span class="glyphicon glyphicon-pencil"></span>
+
+.. |glass| raw:: html
+
+   <span class="glyphicon glyphicon-glass"></span>
+
+.. |music| raw:: html
+
+   <span class="glyphicon glyphicon-music"></span>
+
+.. |search| raw:: html
+
+   <span class="glyphicon glyphicon-search"></span>
+
+.. |heart| raw:: html
+
+   <span class="glyphicon glyphicon-heart"></span>
+
+.. |star| raw:: html
+
+   <span class="glyphicon glyphicon-star"></span>
+
+.. |star-empty| raw:: html
+
+   <span class="glyphicon glyphicon-star-empty"></span>
+
+.. |user| raw:: html
+
+   <span class="glyphicon glyphicon-user"></span>
+
+.. |film| raw:: html
+
+   <span class="glyphicon glyphicon-film"></span>
+
+.. |th-large| raw:: html
+
+   <span class="glyphicon glyphicon-th-large"></span>
+
+.. |th| raw:: html
+
+   <span class="glyphicon glyphicon-th"></span>
+
+.. |th-list| raw:: html
+
+   <span class="glyphicon glyphicon-th-list"></span>
+
+.. |ok| raw:: html
+
+   <span class="glyphicon glyphicon-ok"></span>
+
+.. |remove| raw:: html
+
+   <span class="glyphicon glyphicon-remove"></span>
+
+.. |zoom-in| raw:: html
+
+   <span class="glyphicon glyphicon-zoom-in"></span>
+
+.. |zoom-out| raw:: html
+
+   <span class="glyphicon glyphicon-zoom-out"></span>
+
+.. |off| raw:: html
+
+   <span class="glyphicon glyphicon-off"></span>
+
+.. |signal| raw:: html
+
+   <span class="glyphicon glyphicon-signal"></span>
+
+.. |cog| raw:: html
+
+   <span class="glyphicon glyphicon-cog"></span>
+
+.. |trash| raw:: html
+
+   <span class="glyphicon glyphicon-trash"></span>
+
+.. |home| raw:: html
+
+   <span class="glyphicon glyphicon-home"></span>
+
+.. |file| raw:: html
+
+   <span class="glyphicon glyphicon-file"></span>
+
+.. |time| raw:: html
+
+   <span class="glyphicon glyphicon-time"></span>
+
+.. |road| raw:: html
+
+   <span class="glyphicon glyphicon-road"></span>
+
+.. |download-alt| raw:: html
+
+   <span class="glyphicon glyphicon-download-alt"></span>
+
+.. |download| raw:: html
+
+   <span class="glyphicon glyphicon-download"></span>
+
+.. |upload| raw:: html
+
+   <span class="glyphicon glyphicon-upload"></span>
+
+.. |inbox| raw:: html
+
+   <span class="glyphicon glyphicon-inbox"></span>
+
+.. |play-circle| raw:: html
+
+   <span class="glyphicon glyphicon-play-circle"></span>
+
+.. |repeat| raw:: html
+
+   <span class="glyphicon glyphicon-repeat"></span>
+
+.. |refresh| raw:: html
+
+   <span class="glyphicon glyphicon-refresh"></span>
+
+.. |list-alt| raw:: html
+
+   <span class="glyphicon glyphicon-list-alt"></span>
+
+.. |lock| raw:: html
+
+   <span class="glyphicon glyphicon-lock"></span>
+
+.. |flag| raw:: html
+
+   <span class="glyphicon glyphicon-flag"></span>
+
+.. |headphones| raw:: html
+
+   <span class="glyphicon glyphicon-headphones"></span>
+
+.. |volume-off| raw:: html
+
+   <span class="glyphicon glyphicon-volume-off"></span>
+
+.. |volume-down| raw:: html
+
+   <span class="glyphicon glyphicon-volume-down"></span>
+
+.. |volume-up| raw:: html
+
+   <span class="glyphicon glyphicon-volume-up"></span>
+
+.. |qrcode| raw:: html
+
+   <span class="glyphicon glyphicon-qrcode"></span>
+
+.. |barcode| raw:: html
+
+   <span class="glyphicon glyphicon-barcode"></span>
+
+.. |tag| raw:: html
+
+   <span class="glyphicon glyphicon-tag"></span>
+
+.. |tags| raw:: html
+
+   <span class="glyphicon glyphicon-tags"></span>
+
+.. |book| raw:: html
+
+   <span class="glyphicon glyphicon-book"></span>
+
+.. |bookmark| raw:: html
+
+   <span class="glyphicon glyphicon-bookmark"></span>
+
+.. |print| raw:: html
+
+   <span class="glyphicon glyphicon-print"></span>
+
+.. |camera| raw:: html
+
+   <span class="glyphicon glyphicon-camera"></span>
+
+.. |font| raw:: html
+
+   <span class="glyphicon glyphicon-font"></span>
+
+.. |bold| raw:: html
+
+   <span class="glyphicon glyphicon-bold"></span>
+
+.. |italic| raw:: html
+
+   <span class="glyphicon glyphicon-italic"></span>
+
+.. |text-height| raw:: html
+
+   <span class="glyphicon glyphicon-text-height"></span>
+
+.. |text-width| raw:: html
+
+   <span class="glyphicon glyphicon-text-width"></span>
+
+.. |align-left| raw:: html
+
+   <span class="glyphicon glyphicon-align-left"></span>
+
+.. |align-center| raw:: html
+
+   <span class="glyphicon glyphicon-align-center"></span>
+
+.. |align-right| raw:: html
+
+   <span class="glyphicon glyphicon-align-right"></span>
+
+.. |align-justify| raw:: html
+
+   <span class="glyphicon glyphicon-align-justify"></span>
+
+.. |list| raw:: html
+
+   <span class="glyphicon glyphicon-list"></span>
+
+.. |indent-left| raw:: html
+
+   <span class="glyphicon glyphicon-indent-left"></span>
+
+.. |indent-right| raw:: html
+
+   <span class="glyphicon glyphicon-indent-right"></span>
+
+.. |facetime-video| raw:: html
+
+   <span class="glyphicon glyphicon-facetime-video"></span>
+
+.. |picture| raw:: html
+
+   <span class="glyphicon glyphicon-picture"></span>
+
+.. |map-marker| raw:: html
+
+   <span class="glyphicon glyphicon-map-marker"></span>
+
+.. |adjust| raw:: html
+
+   <span class="glyphicon glyphicon-adjust"></span>
+
+.. |tint| raw:: html
+
+   <span class="glyphicon glyphicon-tint"></span>
+
+.. |edit| raw:: html
+
+   <span class="glyphicon glyphicon-edit"></span>
+
+.. |share| raw:: html
+
+   <span class="glyphicon glyphicon-share"></span>
+
+.. |check| raw:: html
+
+   <span class="glyphicon glyphicon-check"></span>
+
+.. |move| raw:: html
+
+   <span class="glyphicon glyphicon-move"></span>
+
+.. |step-backward| raw:: html
+
+   <span class="glyphicon glyphicon-step-backward"></span>
+
+.. |fast-backward| raw:: html
+
+   <span class="glyphicon glyphicon-fast-backward"></span>
+
+.. |backward| raw:: html
+
+   <span class="glyphicon glyphicon-backward"></span>
+
+.. |play| raw:: html
+
+   <span class="glyphicon glyphicon-play"></span>
+
+.. |pause| raw:: html
+
+   <span class="glyphicon glyphicon-pause"></span>
+
+.. |stop| raw:: html
+
+   <span class="glyphicon glyphicon-stop"></span>
+
+.. |forward| raw:: html
+
+   <span class="glyphicon glyphicon-forward"></span>
+
+.. |fast-forward| raw:: html
+
+   <span class="glyphicon glyphicon-fast-forward"></span>
+
+.. |step-forward| raw:: html
+
+   <span class="glyphicon glyphicon-step-forward"></span>
+
+.. |eject| raw:: html
+
+   <span class="glyphicon glyphicon-eject"></span>
+
+.. |chevron-left| raw:: html
+
+   <span class="glyphicon glyphicon-chevron-left"></span>
+
+.. |chevron-right| raw:: html
+
+   <span class="glyphicon glyphicon-chevron-right"></span>
+
+.. |plus-sign| raw:: html
+
+   <span class="glyphicon glyphicon-plus-sign"></span>
+
+.. |minus-sign| raw:: html
+
+   <span class="glyphicon glyphicon-minus-sign"></span>
+
+.. |remove-sign| raw:: html
+
+   <span class="glyphicon glyphicon-remove-sign"></span>
+
+.. |ok-sign| raw:: html
+
+   <span class="glyphicon glyphicon-ok-sign"></span>
+
+.. |question-sign| raw:: html
+
+   <span class="glyphicon glyphicon-question-sign"></span>
+
+.. |info-sign| raw:: html
+
+   <span class="glyphicon glyphicon-info-sign"></span>
+
+.. |screenshot| raw:: html
+
+   <span class="glyphicon glyphicon-screenshot"></span>
+
+.. |remove-circle| raw:: html
+
+   <span class="glyphicon glyphicon-remove-circle"></span>
+
+.. |ok-circle| raw:: html
+
+   <span class="glyphicon glyphicon-ok-circle"></span>
+
+.. |ban-circle| raw:: html
+
+   <span class="glyphicon glyphicon-ban-circle"></span>
+
+.. |arrow-left| raw:: html
+
+   <span class="glyphicon glyphicon-arrow-left"></span>
+
+.. |arrow-right| raw:: html
+
+   <span class="glyphicon glyphicon-arrow-right"></span>
+
+.. |arrow-up| raw:: html
+
+   <span class="glyphicon glyphicon-arrow-up"></span>
+
+.. |arrow-down| raw:: html
+
+   <span class="glyphicon glyphicon-arrow-down"></span>
+
+.. |share-alt| raw:: html
+
+   <span class="glyphicon glyphicon-share-alt"></span>
+
+.. |resize-full| raw:: html
+
+   <span class="glyphicon glyphicon-resize-full"></span>
+
+.. |resize-small| raw:: html
+
+   <span class="glyphicon glyphicon-resize-small"></span>
+
+.. |exclamation-sign| raw:: html
+
+   <span class="glyphicon glyphicon-exclamation-sign"></span>
+
+.. |gift| raw:: html
+
+   <span class="glyphicon glyphicon-gift"></span>
+
+.. |leaf| raw:: html
+
+   <span class="glyphicon glyphicon-leaf"></span>
+
+.. |fire| raw:: html
+
+   <span class="glyphicon glyphicon-fire"></span>
+
+.. |eye-open| raw:: html
+
+   <span class="glyphicon glyphicon-eye-open"></span>
+
+.. |eye-close| raw:: html
+
+   <span class="glyphicon glyphicon-eye-close"></span>
+
+.. |warning-sign| raw:: html
+
+   <span class="glyphicon glyphicon-warning-sign"></span>
+
+.. |plane| raw:: html
+
+   <span class="glyphicon glyphicon-plane"></span>
+
+.. |calendar| raw:: html
+
+   <span class="glyphicon glyphicon-calendar"></span>
+
+.. |random| raw:: html
+
+   <span class="glyphicon glyphicon-random"></span>
+
+.. |comment| raw:: html
+
+   <span class="glyphicon glyphicon-comment"></span>
+
+.. |magnet| raw:: html
+
+   <span class="glyphicon glyphicon-magnet"></span>
+
+.. |chevron-up| raw:: html
+
+   <span class="glyphicon glyphicon-chevron-up"></span>
+
+.. |chevron-down| raw:: html
+
+   <span class="glyphicon glyphicon-chevron-down"></span>
+
+.. |retweet| raw:: html
+
+   <span class="glyphicon glyphicon-retweet"></span>
+
+.. |shopping-cart| raw:: html
+
+   <span class="glyphicon glyphicon-shopping-cart"></span>
+
+.. |folder-close| raw:: html
+
+   <span class="glyphicon glyphicon-folder-close"></span>
+
+.. |folder-open| raw:: html
+
+   <span class="glyphicon glyphicon-folder-open"></span>
+
+.. |resize-vertical| raw:: html
+
+   <span class="glyphicon glyphicon-resize-vertical"></span>
+
+.. |resize-horizontal| raw:: html
+
+   <span class="glyphicon glyphicon-resize-horizontal"></span>
+
+.. |hdd| raw:: html
+
+   <span class="glyphicon glyphicon-hdd"></span>
+
+.. |bullhorn| raw:: html
+
+   <span class="glyphicon glyphicon-bullhorn"></span>
+
+.. |bell| raw:: html
+
+   <span class="glyphicon glyphicon-bell"></span>
+
+.. |certificate| raw:: html
+
+   <span class="glyphicon glyphicon-certificate"></span>
+
+.. |thumbs-up| raw:: html
+
+   <span class="glyphicon glyphicon-thumbs-up"></span>
+
+.. |thumbs-down| raw:: html
+
+   <span class="glyphicon glyphicon-thumbs-down"></span>
+
+.. |hand-right| raw:: html
+
+   <span class="glyphicon glyphicon-hand-right"></span>
+
+.. |hand-left| raw:: html
+
+   <span class="glyphicon glyphicon-hand-left"></span>
+
+.. |hand-up| raw:: html
+
+   <span class="glyphicon glyphicon-hand-up"></span>
+
+.. |hand-down| raw:: html
+
+   <span class="glyphicon glyphicon-hand-down"></span>
+
+.. |circle-arrow-right| raw:: html
+
+   <span class="glyphicon glyphicon-circle-arrow-right"></span>
+
+.. |circle-arrow-left| raw:: html
+
+   <span class="glyphicon glyphicon-circle-arrow-left"></span>
+
+.. |circle-arrow-up| raw:: html
+
+   <span class="glyphicon glyphicon-circle-arrow-up"></span>
+
+.. |circle-arrow-down| raw:: html
+
+   <span class="glyphicon glyphicon-circle-arrow-down"></span>
+
+.. |globe| raw:: html
+
+   <span class="glyphicon glyphicon-globe"></span>
+
+.. |wrench| raw:: html
+
+   <span class="glyphicon glyphicon-wrench"></span>
+
+.. |tasks| raw:: html
+
+   <span class="glyphicon glyphicon-tasks"></span>
+
+.. |filter| raw:: html
+
+   <span class="glyphicon glyphicon-filter"></span>
+
+.. |briefcase| raw:: html
+
+   <span class="glyphicon glyphicon-briefcase"></span>
+
+.. |fullscreen| raw:: html
+
+   <span class="glyphicon glyphicon-fullscreen"></span>
+
+.. |dashboard| raw:: html
+
+   <span class="glyphicon glyphicon-dashboard"></span>
+
+.. |paperclip| raw:: html
+
+   <span class="glyphicon glyphicon-paperclip"></span>
+
+.. |heart-empty| raw:: html
+
+   <span class="glyphicon glyphicon-heart-empty"></span>
+
+.. |link| raw:: html
+
+   <span class="glyphicon glyphicon-link"></span>
+
+.. |phone| raw:: html
+
+   <span class="glyphicon glyphicon-phone"></span>
+
+.. |pushpin| raw:: html
+
+   <span class="glyphicon glyphicon-pushpin"></span>
+
+.. |usd| raw:: html
+
+   <span class="glyphicon glyphicon-usd"></span>
+
+.. |gbp| raw:: html
+
+   <span class="glyphicon glyphicon-gbp"></span>
+
+.. |sort| raw:: html
+
+   <span class="glyphicon glyphicon-sort"></span>
+
+.. |sort-by-alphabet| raw:: html
+
+   <span class="glyphicon glyphicon-sort-by-alphabet"></span>
+
+.. |sort-by-alphabet-alt| raw:: html
+
+   <span class="glyphicon glyphicon-sort-by-alphabet-alt"></span>
+
+.. |sort-by-order| raw:: html
+
+   <span class="glyphicon glyphicon-sort-by-order"></span>
+
+.. |sort-by-order-alt| raw:: html
+
+   <span class="glyphicon glyphicon-sort-by-order-alt"></span>
+
+.. |sort-by-attributes| raw:: html
+
+   <span class="glyphicon glyphicon-sort-by-attributes"></span>
+
+.. |sort-by-attributes-alt| raw:: html
+
+   <span class="glyphicon glyphicon-sort-by-attributes-alt"></span>
+
+.. |unchecked| raw:: html
+
+   <span class="glyphicon glyphicon-unchecked"></span>
+
+.. |expand| raw:: html
+
+   <span class="glyphicon glyphicon-expand"></span>
+
+.. |collapse-down| raw:: html
+
+   <span class="glyphicon glyphicon-collapse-down"></span>
+
+.. |collapse-up| raw:: html
+
+   <span class="glyphicon glyphicon-collapse-up"></span>
+
+.. |log-in| raw:: html
+
+   <span class="glyphicon glyphicon-log-in"></span>
+
+.. |flash| raw:: html
+
+   <span class="glyphicon glyphicon-flash"></span>
+
+.. |log-out| raw:: html
+
+   <span class="glyphicon glyphicon-log-out"></span>
+
+.. |new-window| raw:: html
+
+   <span class="glyphicon glyphicon-new-window"></span>
+
+.. |record| raw:: html
+
+   <span class="glyphicon glyphicon-record"></span>
+
+.. |save| raw:: html
+
+   <span class="glyphicon glyphicon-save"></span>
+
+.. |open| raw:: html
+
+   <span class="glyphicon glyphicon-open"></span>
+
+.. |saved| raw:: html
+
+   <span class="glyphicon glyphicon-saved"></span>
+
+.. |import| raw:: html
+
+   <span class="glyphicon glyphicon-import"></span>
+
+.. |export| raw:: html
+
+   <span class="glyphicon glyphicon-export"></span>
+
+.. |send| raw:: html
+
+   <span class="glyphicon glyphicon-send"></span>
+
+.. |floppy-disk| raw:: html
+
+   <span class="glyphicon glyphicon-floppy-disk"></span>
+
+.. |floppy-saved| raw:: html
+
+   <span class="glyphicon glyphicon-floppy-saved"></span>
+
+.. |floppy-remove| raw:: html
+
+   <span class="glyphicon glyphicon-floppy-remove"></span>
+
+.. |floppy-save| raw:: html
+
+   <span class="glyphicon glyphicon-floppy-save"></span>
+
+.. |floppy-open| raw:: html
+
+   <span class="glyphicon glyphicon-floppy-open"></span>
+
+.. |credit-card| raw:: html
+
+   <span class="glyphicon glyphicon-credit-card"></span>
+
+.. |transfer| raw:: html
+
+   <span class="glyphicon glyphicon-transfer"></span>
+
+.. |cutlery| raw:: html
+
+   <span class="glyphicon glyphicon-cutlery"></span>
+
+.. |header| raw:: html
+
+   <span class="glyphicon glyphicon-header"></span>
+
+.. |compressed| raw:: html
+
+   <span class="glyphicon glyphicon-compressed"></span>
+
+.. |earphone| raw:: html
+
+   <span class="glyphicon glyphicon-earphone"></span>
+
+.. |phone-alt| raw:: html
+
+   <span class="glyphicon glyphicon-phone-alt"></span>
+
+.. |tower| raw:: html
+
+   <span class="glyphicon glyphicon-tower"></span>
+
+.. |stats| raw:: html
+
+   <span class="glyphicon glyphicon-stats"></span>
+
+.. |sd-video| raw:: html
+
+   <span class="glyphicon glyphicon-sd-video"></span>
+
+.. |hd-video| raw:: html
+
+   <span class="glyphicon glyphicon-hd-video"></span>
+
+.. |subtitles| raw:: html
+
+   <span class="glyphicon glyphicon-subtitles"></span>
+
+.. |sound-stereo| raw:: html
+
+   <span class="glyphicon glyphicon-sound-stereo"></span>
+
+.. |sound-dolby| raw:: html
+
+   <span class="glyphicon glyphicon-sound-dolby"></span>
+
+.. |sound-5-1| raw:: html
+
+   <span class="glyphicon glyphicon-sound-5-1"></span>
+
+.. |sound-6-1| raw:: html
+
+   <span class="glyphicon glyphicon-sound-6-1"></span>
+
+.. |sound-7-1| raw:: html
+
+   <span class="glyphicon glyphicon-sound-7-1"></span>
+
+.. |copyright-mark| raw:: html
+
+   <span class="glyphicon glyphicon-copyright-mark"></span>
+
+.. |registration-mark| raw:: html
+
+   <span class="glyphicon glyphicon-registration-mark"></span>
+
+.. |cloud-download| raw:: html
+
+   <span class="glyphicon glyphicon-cloud-download"></span>
+
+.. |cloud-upload| raw:: html
+
+   <span class="glyphicon glyphicon-cloud-upload"></span>
+
+.. |tree-conifer| raw:: html
+
+   <span class="glyphicon glyphicon-tree-conifer"></span>
+
+.. |tree-deciduous| raw:: html
+
+   <span class="glyphicon glyphicon-tree-deciduous"></span>
diff --git a/pelican-plugins/bootstrap-rst/doc/glyphicons-list.txt b/pelican-plugins/bootstrap-rst/doc/glyphicons-list.txt
new file mode 100644
index 0000000000000000000000000000000000000000..bbdb990a6816d4ea408b5e0d1747a10bd1c36a95
--- /dev/null
+++ b/pelican-plugins/bootstrap-rst/doc/glyphicons-list.txt
@@ -0,0 +1,225 @@
+.. row::
+
+   .. column::
+      :width: 3
+
+      .. class:: list-unstyled
+
+         * |asterisk| : asterisk
+         * |plus| : plus
+         * |euro| : euro
+         * |minus| : minus
+         * |cloud| : cloud
+         * |envelope| : envelope
+         * |pencil| : pencil
+         * |glass| : glass
+         * |music| : music
+         * |search| : search
+         * |heart| : heart
+         * |star| : star
+         * |star-empty| : star-empty
+         * |user| : user
+         * |film| : film
+         * |th-large| : th-large
+         * |th| : th
+         * |th-list| : th-list
+         * |ok| : ok
+         * |remove| : remove
+         * |zoom-in| : zoom-in
+         * |zoom-out| : zoom-out
+         * |off| : off
+         * |signal| : signal
+         * |cog| : cog
+         * |trash| : trash
+         * |home| : home
+         * |file| : file
+         * |time| : time
+         * |road| : road
+         * |download-alt| : download-alt
+         * |download| : download
+         * |upload| : upload
+         * |inbox| : inbox
+         * |play-circle| : play-circle
+         * |repeat| : repeat
+         * |refresh| : refresh
+         * |list-alt| : list-alt
+         * |lock| : lock
+         * |flag| : flag
+         * |headphones| : headphones
+         * |volume-off| : volume-off
+         * |volume-down| : volume-down
+         * |volume-up| : volume-up
+         * |qrcode| : qrcode
+         * |barcode| : barcode
+         * |tag| : tag
+         * |tags| : tags
+         * |book| : book
+         * |bookmark| : bookmark
+
+   .. column::
+      :width: 3
+
+      .. class:: list-unstyled
+
+         * |print| : print
+         * |camera| : camera
+         * |font| : font
+         * |bold| : bold
+         * |italic| : italic
+         * |text-height| : text-height
+         * |text-width| : text-width
+         * |align-left| : align-left
+         * |align-center| : align-center
+         * |align-right| : align-right
+         * |align-justify| : align-justify
+         * |list| : list
+         * |indent-left| : indent-left
+         * |indent-right| : indent-right
+         * |facetime-video| : facetime-video
+         * |picture| : picture
+         * |map-marker| : map-marker
+         * |adjust| : adjust
+         * |tint| : tint
+         * |edit| : edit
+         * |share| : share
+         * |check| : check
+         * |move| : move
+         * |step-backward| : step-backward
+         * |fast-backward| : fast-backward
+         * |backward| : backward
+         * |play| : play
+         * |pause| : pause
+         * |stop| : stop
+         * |forward| : forward
+         * |fast-forward| : fast-forward
+         * |step-forward| : step-forward
+         * |eject| : eject
+         * |chevron-left| : chevron-left
+         * |chevron-right| : chevron-right
+         * |plus-sign| : plus-sign
+         * |minus-sign| : minus-sign
+         * |remove-sign| : remove-sign
+         * |ok-sign| : ok-sign
+         * |question-sign| : question-sign
+         * |info-sign| : info-sign
+         * |screenshot| : screenshot
+         * |remove-circle| : remove-circle
+         * |ok-circle| : ok-circle
+         * |ban-circle| : ban-circle
+         * |arrow-left| : arrow-left
+         * |arrow-right| : arrow-right
+         * |arrow-up| : arrow-up
+         * |arrow-down| : arrow-down
+         * |share-alt| : share-alt
+
+   .. column::
+      :width: 3
+
+      .. class:: list-unstyled
+
+         * |resize-full| : resize-full
+         * |resize-small| : resize-small
+         * |exclamation-sign| : exclamation-sign
+         * |gift| : gift
+         * |leaf| : leaf
+         * |fire| : fire
+         * |eye-open| : eye-open
+         * |eye-close| : eye-close
+         * |warning-sign| : warning-sign
+         * |plane| : plane
+         * |calendar| : calendar
+         * |random| : random
+         * |comment| : comment
+         * |magnet| : magnet
+         * |chevron-up| : chevron-up
+         * |chevron-down| : chevron-down
+         * |retweet| : retweet
+         * |shopping-cart| : shopping-cart
+         * |folder-close| : folder-close
+         * |folder-open| : folder-open
+         * |resize-vertical| : resize-vertical
+         * |resize-horizontal| : resize-horizontal
+         * |hdd| : hdd
+         * |bullhorn| : bullhorn
+         * |bell| : bell
+         * |certificate| : certificate
+         * |thumbs-up| : thumbs-up
+         * |thumbs-down| : thumbs-down
+         * |hand-right| : hand-right
+         * |hand-left| : hand-left
+         * |hand-up| : hand-up
+         * |hand-down| : hand-down
+         * |circle-arrow-right| : circle-arrow-right
+         * |circle-arrow-left| : circle-arrow-left
+         * |circle-arrow-up| : circle-arrow-up
+         * |circle-arrow-down| : circle-arrow-down
+         * |globe| : globe
+         * |wrench| : wrench
+         * |tasks| : tasks
+         * |filter| : filter
+         * |briefcase| : briefcase
+         * |fullscreen| : fullscreen
+         * |dashboard| : dashboard
+         * |paperclip| : paperclip
+         * |heart-empty| : heart-empty
+         * |link| : link
+         * |phone| : phone
+         * |pushpin| : pushpin
+         * |usd| : usd
+         * |gbp| : gbp
+
+   .. column::
+      :width: 3
+
+      .. class:: list-unstyled
+
+         * |sort| : sort
+         * |sort-by-alphabet| : sort-by-alphabet
+         * |sort-by-alphabet-alt| : sort-by-alphabet-alt
+         * |sort-by-order| : sort-by-order
+         * |sort-by-order-alt| : sort-by-order-alt
+         * |sort-by-attributes| : sort-by-attributes
+         * |sort-by-attributes-alt| : sort-by-attributes-alt
+         * |unchecked| : unchecked
+         * |expand| : expand
+         * |collapse-down| : collapse-down
+         * |collapse-up| : collapse-up
+         * |log-in| : log-in
+         * |flash| : flash
+         * |log-out| : log-out
+         * |new-window| : new-window
+         * |record| : record
+         * |save| : save
+         * |open| : open
+         * |saved| : saved
+         * |import| : import
+         * |export| : export
+         * |send| : send
+         * |floppy-disk| : floppy-disk
+         * |floppy-saved| : floppy-saved
+         * |floppy-remove| : floppy-remove
+         * |floppy-save| : floppy-save
+         * |floppy-open| : floppy-open
+         * |credit-card| : credit-card
+         * |transfer| : transfer
+         * |cutlery| : cutlery
+         * |header| : header
+         * |compressed| : compressed
+         * |earphone| : earphone
+         * |phone-alt| : phone-alt
+         * |tower| : tower
+         * |stats| : stats
+         * |sd-video| : sd-video
+         * |hd-video| : hd-video
+         * |subtitles| : subtitles
+         * |sound-stereo| : sound-stereo
+         * |sound-dolby| : sound-dolby
+         * |sound-5-1| : sound-5-1
+         * |sound-6-1| : sound-6-1
+         * |sound-7-1| : sound-7-1
+         * |copyright-mark| : copyright-mark
+         * |registration-mark| : registration-mark
+         * |cloud-download| : cloud-download
+         * |cloud-upload| : cloud-upload
+         * |tree-conifer| : tree-conifer
+         * |tree-deciduous| : tree-deciduous
diff --git a/pelican-plugins/bootstrap-rst/doc/index.rst b/pelican-plugins/bootstrap-rst/doc/index.rst
new file mode 100644
index 0000000000000000000000000000000000000000..70375bb692b9606d8b493677daa5a174a9107a24
--- /dev/null
+++ b/pelican-plugins/bootstrap-rst/doc/index.rst
@@ -0,0 +1,403 @@
+.. default-role:: code
+
+.. role:: text-muted
+.. role:: text-primary
+.. role:: text-success
+.. role:: text-info
+.. role:: text-warning
+.. role:: text-danger
+
+
+.. ----------------------------------------------------------------------------
+.. header:: bs-docs-header
+
+   .. container:: container
+
+      :h1:`Bootstrap RST`
+
+      An overview of Bootstrap RST, basic templates and examples, and more.
+.. ----------------------------------------------------------------------------
+
+.. ----------------------------------------------------------------------------
+.. footer:: bs-docs-footer
+
+   Bootstrap RST - Copyright (c) 2014 Nicolas P. Rougier
+
+   `Bootstrap <http://getbootstrap.com>`_ - Copyright (c) 2011-2014 Twitter, Inc
+
+   Code licensed under MIT, documentation under CC BY 3.0.
+
+   `Get page source <doc/index.rst>`_
+
+   .. class:: bs-docs-footer-links muted
+
+      * `GitHub <https://github.com/rougier/bootstrap-rst>`_
+      * ·
+      * `Examples <examples.html>`_
+      * ·
+      * `Documentation <about.html>`_
+      * ·
+      * `About <about.html>`_
+      * ·
+      * `Issues <https://github.com/rougier/bootstrap-rst/issues>`_
+      * ·
+      * `Releases <https://github.com/rougier/bootstrap-rst/releases>`_
+.. ----------------------------------------------------------------------------
+
+
+.. ----------------------------------------------------------------------------
+.. sidebar:: sidebar
+
+   .. contents:: content
+      :depth: 2
+.. ----------------------------------------------------------------------------
+
+Bootstrap RST
+===============================================================================
+.. lead:: Bootstrap RST provides easy acces to bootstrap using the restructured
+          text markup language.
+
+Bootstrap RST offers an easy access to the `bootstrap
+<http://getbootstrap.com/>`_ framework using the `restructured text`_ markup
+language. Bootstrap RST provides a set of new directives and roles (row,
+column, button, list, etc.)  that allow seamless integration of the bootstrap
+framework. Have a look at the `source`_ of this page for example and see below
+for what is available so far.
+
+.. _Nicolas P. Rougier: http://www.loria.fr/~rougier/
+.. _restructured text: http://docutils.sourceforge.net/rst.html
+.. _source: doc/index.rst
+
+
+
+Work in Progress
+===============================================================================
+.. lead:: Bootstrap RST is work in progress. We report here what has been done
+          so far, what needs to be done and what is not relevant.
+.. ----------------------------------------------------------------------------
+
+
+CSS
+---
+
+Global CSS settings, fundamental HTML elements styled and enhanced with
+extensible classes, and an advanced grid system.
+
+
+.. row::
+
+   .. -------------------------------------------------------------------------
+   .. column::
+      :width: 3
+
+      :h4:`Grid system`
+
+      .. progress:: 100%
+         :class: success
+         :label: Done !
+      See `grids <CSS.html#grid-system>`_
+   .. -------------------------------------------------------------------------
+   .. column::
+      :width: 3
+
+      :h4:`Typography`
+
+      .. progress:: 100%
+         :class: success
+         :label: Done !
+      See `typography <CSS.html#typography>`_
+
+   .. -------------------------------------------------------------------------
+   .. column::
+      :width: 3
+
+      :h4:`Code`
+
+      .. progress:: 100%
+         :class: success
+         :label: Done !
+      See `code <CSS.html#code>`_
+
+   .. -------------------------------------------------------------------------
+   .. column::
+      :width: 3
+
+      :h4:`Tables`
+
+      .. progress:: 100%
+         :class: success
+         :label: Done !
+
+      See `tables <CSS.html#tables>`_
+   .. -------------------------------------------------------------------------
+
+
+.. row::
+
+   .. -------------------------------------------------------------------------
+   .. column::
+      :width: 3
+
+      :h4:`Forms`
+      :text-muted:`N/A`
+   .. -------------------------------------------------------------------------
+   .. column::
+      :width: 3
+
+      :h4:`Buttons`
+
+      .. progress:: 100%
+         :class: success
+         :label: Done !
+      See `buttons <CSS.html#buttons>`_
+   .. -------------------------------------------------------------------------
+   .. column::
+      :width: 3
+
+      :h4:`Images`
+
+      .. progress:: 100%
+         :class: success
+         :label: Done !
+      See `images <CSS.html#images>`_
+   .. -------------------------------------------------------------------------
+   .. column::
+      :width: 3
+
+      :h4:`Helper classes`
+
+      .. progress:: 50%
+         :class: warning
+         :label: WIP
+
+      See `helper classes <CSS.html#helper-classes>`_
+   .. -------------------------------------------------------------------------
+
+
+
+Components
+----------
+
+Over a dozen reusable components built to provide iconography, dropdowns, input
+groups, navigation, alerts, and much more.
+
+
+.. row::
+
+   .. -------------------------------------------------------------------------
+   .. column::
+      :width: 3
+
+      :h4:`Glyphicons`
+
+      .. progress:: 100%
+         :class: success
+         :label: Done !
+      See `glyphicons <components.html#glyphicons>`_
+   .. -------------------------------------------------------------------------
+   .. column::
+      :width: 3
+
+      :h4:`Dropdowns`
+
+      .. progress:: 100%
+         :class: success
+         :label: Done !
+      See `dropdowns <components.html#dropdowns>`_
+   .. -------------------------------------------------------------------------
+   .. column::
+      :width: 3
+
+      :h4:`Button groups`
+
+      .. progress:: 100%
+         :class: success
+         :label: Done !
+      See `button groups <components.html#button-groups>`_
+   .. -------------------------------------------------------------------------
+   .. column::
+      :width: 3
+
+      :h4:`Glyphicons`
+
+      .. progress:: 100%
+         :class: success
+         :label: Done !
+      See `button dropdowns <components.html#button-dropdowns>`_
+   .. -------------------------------------------------------------------------
+
+
+.. row::
+
+   .. -------------------------------------------------------------------------
+   .. column::
+      :width: 3
+
+      :h4:`Input groups`
+      :text-muted:`N/A`
+   .. -------------------------------------------------------------------------
+   .. column::
+      :width: 3
+
+      :h4:`Navs`
+
+      .. progress:: 25%
+         :class: danger
+         :label: WIP
+      See `navs <components.html#navs>`_
+   .. -------------------------------------------------------------------------
+   .. column::
+      :width: 3
+
+      :h4:`Navbar`
+
+      .. progress:: 25%
+         :class: danger
+         :label: WIP
+      See `navbar <components.html#navbar>`_
+   .. -------------------------------------------------------------------------
+   .. column::
+      :width: 3
+
+      :h4:`Breadcrumbs`
+
+      .. progress:: 100%
+         :class: success
+         :label: Done !
+      See `breadcrumbs <components.html#breadcrumbs>`_
+   .. -------------------------------------------------------------------------
+
+
+.. row::
+
+   .. -------------------------------------------------------------------------
+   .. column::
+      :width: 3
+
+      :h4:`Pagination`
+
+      .. progress:: 25%
+         :class: danger
+         :label: WIP
+      See `pagination <components.html#pagination>`_
+   .. -------------------------------------------------------------------------
+   .. column::
+      :width: 3
+
+      :h4:`Labels`
+
+      .. progress:: 100%
+         :class: success
+         :label: Done !
+      See `labels <components.html#labels>`_
+   .. -------------------------------------------------------------------------
+   .. column::
+      :width: 3
+
+      :h4:`Badges`
+
+      .. progress:: 100%
+         :class: success
+         :label: Done !
+      See `badges <components.html#badges>`_
+   .. -------------------------------------------------------------------------
+   .. column::
+      :width: 3
+
+      :h4:`Jumbotron`
+
+      .. progress:: 100%
+         :class: success
+         :label: Done !
+      See `jumbotron <components.html#jumbotron>`_
+   .. -------------------------------------------------------------------------
+
+
+.. row::
+
+   .. -------------------------------------------------------------------------
+   .. column::
+      :width: 3
+
+      :h4:`Page header`
+
+      .. progress:: 100%
+         :class: success
+         :label: Done !
+      See `page header <components.html#page-header>`_
+   .. -------------------------------------------------------------------------
+   .. column::
+      :width: 3
+
+      :h4:`Thumbnails`
+
+      .. progress:: 100%
+         :class: success
+         :label: Done !
+      See `thumbnails <components.html#thumbnails>`_
+   .. -------------------------------------------------------------------------
+   .. column::
+      :width: 3
+
+      :h4:`Alerts`
+
+      .. progress:: 100%
+         :class: success
+         :label: Done !
+      See `alerts <components.html#alerts>`_
+   .. -------------------------------------------------------------------------
+   .. column::
+      :width: 3
+
+      :h4:`Progress bars`
+
+      .. progress:: 100%
+         :class: success
+         :label: Done !
+      See `progress bars <components.html#progress-bars>`_
+   .. -------------------------------------------------------------------------
+
+
+.. row::
+
+   .. -------------------------------------------------------------------------
+   .. column::
+      :width: 3
+
+      :h4:`Media object`
+
+      .. progress:: 25%
+         :class: danger
+         :label: WIP
+      See `media object <components.html#media-object>`_
+   .. -------------------------------------------------------------------------
+   .. column::
+      :width: 3
+
+      :h4:`List group`
+
+      .. progress:: 25%
+         :class: danger
+         :label: WIP
+      See `list group <components.html#list-group>`_
+   .. -------------------------------------------------------------------------
+   .. column::
+      :width: 3
+
+      :h4:`Panels`
+
+      .. progress:: 25%
+         :class: danger
+         :label: WIP
+      See `panels <components.html#panels>`_
+   .. -------------------------------------------------------------------------
+   .. column::
+      :width: 3
+
+      :h4:`Wells`
+
+      .. progress:: 100%
+         :class: success
+         :label: Done !
+      See `wells <components.html#wells>`_
+   .. -------------------------------------------------------------------------
diff --git a/pelican-plugins/bootstrap-rst/docs-min.css b/pelican-plugins/bootstrap-rst/docs-min.css
new file mode 100644
index 0000000000000000000000000000000000000000..3dc750bd6350219c8676c10333c8d52fd0fa8043
--- /dev/null
+++ b/pelican-plugins/bootstrap-rst/docs-min.css
@@ -0,0 +1,1542 @@
+/*!
+ * Bootstrap Docs (http://getbootstrap.com)
+ * Copyright 2011-2014 Twitter, Inc.
+ * Licensed under the Creative Commons Attribution 3.0 Unported License. For
+ * details, see http://creativecommons.org/licenses/by/3.0/.
+ */
+body {
+    position: relative
+}
+
+.table code {
+    font-size: 13px;
+    font-weight: 400
+}
+
+.btn-outline {
+    color: #563d7c;
+    background-color: transparent;
+    border-color: #563d7c
+}
+
+.btn-outline:hover, .btn-outline:focus, .btn-outline:active {
+    color: #fff;
+    background-color: #563d7c;
+    border-color: #563d7c
+}
+
+.btn-outline-inverse {
+    color: #fff;
+    background-color: transparent;
+    border-color: #cdbfe3
+}
+
+.btn-outline-inverse:hover, .btn-outline-inverse:focus, .btn-outline-inverse:active {
+    color: #563d7c;
+    text-shadow: none;
+    background-color: #fff;
+    border-color: #fff
+}
+
+.bs-docs-booticon {
+    display: block;
+    font-weight: 500;
+    color: #fff;
+    background-color: #563d7c;
+    border-radius: 15%;
+    cursor: default;
+    text-align: center
+}
+
+.bs-docs-booticon-sm {
+    width: 30px;
+    height: 30px;
+    font-size: 20px;
+    line-height: 28px
+}
+
+.bs-docs-booticon-lg {
+    width: 144px;
+    height: 144px;
+    font-size: 108px;
+    line-height: 140px
+}
+
+.bs-docs-booticon-inverse {
+    color: #563d7c;
+    background-color: #fff
+}
+
+.bs-docs-booticon-outline {
+    background-color: transparent;
+    border: 1px solid #cdbfe3
+}
+
+.bs-docs-nav {
+    margin-bottom: 0;
+    background-color: #fff;
+    border-bottom: 0
+}
+
+.bs-home-nav .bs-nav-b {
+    display: none
+}
+
+.bs-docs-nav .navbar-brand, .bs-docs-nav .navbar-nav>li>a {
+    color: #563d7c;
+    font-weight: 500
+}
+
+.bs-docs-nav .navbar-nav>li>a:hover, .bs-docs-nav .navbar-nav>.active>a,
+.bs-docs-nav .navbar-nav>.active>a:hover {
+    color: #463265;
+    background-color: #f9f9f9
+}
+
+.bs-docs-nav .navbar-toggle .icon-bar {
+    background-color: #563d7c
+}
+
+.bs-docs-nav .navbar-header .navbar-toggle {
+    border-color: #fff
+}
+
+.bs-docs-nav .navbar-header .navbar-toggle:hover, .bs-docs-nav .navbar-header .navbar-toggle:focus {
+    background-color: #f9f9f9;
+    border-color: #f9f9f9
+}
+
+.bs-docs-footer {
+    padding-top: 40px;
+    padding-bottom: 40px;
+    margin-top: 100px;
+    color: #777;
+    text-align: center;
+    border-top: 1px solid #e5e5e5
+}
+
+.bs-docs-footer-links {
+    margin-top: 20px;
+    padding-left: 0;
+    color: #999
+}
+
+.bs-docs-footer-links li {
+    display: inline;
+    padding: 0 2px
+}
+
+.bs-docs-footer-links li:first-child {
+    padding-left: 0
+}
+
+@media (min-width:768px){.bs-docs-footer p {
+        margin-bottom: 0
+    }
+}
+
+.bs-docs-social {
+    margin-bottom: 20px;
+    text-align: center
+}
+
+.bs-docs-social-buttons {
+    display: inline-block;
+    margin-bottom: 0;
+    padding-left: 0;
+    list-style: none
+}
+
+.bs-docs-social-buttons li {
+    display: inline-block;
+    line-height: 1;
+    padding: 5px 8px
+}
+
+.bs-docs-social-buttons .twitter-follow-button {
+    width: 225px !important
+}
+
+.bs-docs-social-buttons .twitter-share-button {
+    width: 98px !important
+}
+
+.github-btn {
+    border: 0;
+    overflow: hidden
+}
+
+.bs-docs-masthead, .bs-docs-header {
+    position: relative;
+    padding: 30px 15px;
+    color: #cdbfe3;
+    text-align: center;
+    text-shadow: 0 1px 0 rgba(0, 0, 0, .1);
+    background-color: #6f5499;
+    background-image: -webkit-linear-gradient(top, #563d7c 0, #6f5499 100%);
+    background-image: linear-gradient(to bottom, #563d7c 0, #6f5499 100%);
+    background-repeat: repeat-x;
+    filter: progid: DXImageTransform.Microsoft.gradient(startColorstr='#563d7c', endColorstr='#6F5499', GradientType=0)
+}
+
+.bs-docs-masthead .bs-docs-booticon {
+    margin: 0 auto 30px
+}
+
+.bs-docs-masthead h1 {
+    font-weight: 300;
+    line-height: 1;
+    color: #fff
+}
+
+.bs-docs-masthead .lead {
+    margin: 0 auto 30px;
+    font-size: 20px;
+    color: #fff
+}
+
+.bs-docs-masthead .version {
+    margin-top: -15px;
+    margin-bottom: 30px;
+    color: #9783b9
+}
+
+.bs-docs-masthead .btn {
+    width: 100%;
+    padding: 15px 30px;
+    font-size: 20px
+}
+
+@media (min-width:480px){.bs-docs-masthead .btn {
+        width: auto
+    }
+}
+
+@media (min-width:768px){.bs-docs-masthead {
+        padding-top: 80px;
+        padding-bottom: 80px
+    }
+
+    .bs-docs-masthead h1 {
+        font-size: 60px
+    }
+
+    .bs-docs-masthead .lead {
+        font-size: 24px
+    }
+}
+
+@media (min-width:992px){.bs-docs-masthead .lead {
+        width: 80%;
+        font-size: 30px
+    }
+}
+
+.bs-docs-header {
+    margin-bottom: 40px;
+    font-size: 20px
+}
+
+.bs-docs-header h1 {
+    margin-top: 0;
+    color: #fff
+}
+
+.bs-docs-header p {
+    margin-bottom: 0;
+    font-weight: 300;
+    line-height: 1.4
+}
+
+.bs-docs-header .container {
+    position: relative
+}
+
+@media (min-width:768px){.bs-docs-header {
+        padding-top: 60px;
+        padding-bottom: 60px;
+        font-size: 24px;
+        text-align: left
+    }
+
+    .bs-docs-header h1 {
+        font-size: 60px;
+        line-height: 1
+    }
+}
+
+@media (min-width:992px){.bs-docs-header h1, .bs-docs-header p {
+        margin-right: 380px
+    }
+}
+
+.carbonad {
+    width: auto !important;
+    margin: 30px -30px -31px !important;
+    padding: 20px !important;
+    overflow: hidden;
+    height: auto !important;
+    font-size: 13px !important;
+    line-height: 16px !important;
+    text-align: left;
+    background: transparent !important;
+    border: solid #866ab3 !important;
+    border-width: 1px 0 !important
+}
+
+.carbonad-img {
+    margin: 0 !important
+}
+
+.carbonad-text, .carbonad-tag {
+    float: none !important;
+    display: block !important;
+    width: auto !important;
+    height: auto !important;
+    margin-left: 145px !important;
+    font-family: "Helvetica Neue", Helvetica, Arial, sans-serif !important
+}
+
+.carbonad-text {
+    padding-top: 0 !important
+}
+
+.carbonad-tag {
+    color: inherit !important;
+    text-align: left !important
+}
+
+.carbonad-text a, .carbonad-tag a {
+    color: #fff !important
+}
+
+.carbonad #azcarbon>img {
+    display: none
+}
+
+@media (min-width:480px){.carbonad {
+        width: 330px !important;
+        margin: 20px auto !important;
+        border-radius: 4px;
+        border-width: 1px !important
+    }
+
+    .bs-docs-masthead .carbonad {
+        margin: 50px auto 0 !important
+    }
+}
+
+@media (min-width:768px){.carbonad {
+        margin-left: 0 !important;
+        margin-right: 0 !important
+    }
+}
+
+@media (min-width:992px){.carbonad {
+        position: absolute;
+        top: 0;
+        right: 15px;
+        margin: 0 !important;
+        padding: 15px !important;
+        width: 330px !important
+    }
+
+    .bs-docs-masthead .carbonad {
+        position: static
+    }
+}
+
+.bs-docs-featurette {
+    padding-top: 40px;
+    padding-bottom: 40px;
+    font-size: 16px;
+    line-height: 1.5;
+    color: #555;
+    text-align: center;
+    background-color: #fff;
+    border-bottom: 1px solid #e5e5e5
+}
+
+.bs-docs-featurette+.bs-docs-footer {
+    margin-top: 0;
+    border-top: 0
+}
+
+.bs-docs-featurette-title {
+    font-size: 30px;
+    font-weight: 400;
+    color: #333;
+    margin-bottom: 5px
+}
+
+.half-rule {
+    width: 100px;
+    margin: 40px auto
+}
+
+.bs-docs-featurette h3 {
+    font-weight: 400;
+    color: #333;
+    margin-bottom: 5px
+}
+
+.bs-docs-featurette-img {
+    display: block;
+    margin-bottom: 20px;
+    color: #333
+}
+
+.bs-docs-featurette-img:hover {
+    text-decoration: none;
+    color: #428bca
+}
+
+.bs-docs-featurette-img img {
+    display: block;
+    margin-bottom: 15px
+}
+
+.bs-docs-featured-sites {
+    margin-left: -1px;
+    margin-right: -1px
+}
+
+.bs-docs-featured-sites .col-sm-3 {
+    padding-left: 1px;
+    padding-right: 1px
+}
+
+@media (min-width:480px){.bs-docs-featurette .img-responsive {
+        margin-top: 30px
+    }
+}
+
+@media (min-width:768px){.bs-docs-featurette {
+        padding-top: 100px;
+        padding-bottom: 100px
+    }
+
+    .bs-docs-featurette-title {
+        font-size: 40px
+    }
+
+    .bs-docs-featurette .lead {
+        margin-left: auto;
+        margin-right: auto;
+        max-width: 80%
+    }
+
+    .bs-docs-featured-sites .col-sm-3:first-child img {
+        border-top-left-radius: 4px;
+        border-bottom-left-radius: 4px
+    }
+
+    .bs-docs-featured-sites .col-sm-3:last-child img {
+        border-top-right-radius: 4px;
+        border-bottom-right-radius: 4px
+    }
+
+    .bs-docs-featurette .img-responsive {
+        margin-top: 0
+    }
+}
+
+.bs-docs-sidebar.affix {
+    position: static
+}
+
+@media (min-width:768px){.bs-docs-sidebar {
+        padding-left: 20px
+    }
+}
+
+.bs-docs-sidenav {
+    margin-top: 20px;
+    margin-bottom: 20px
+}
+
+.bs-docs-sidebar .nav>li>a {
+    display: block;
+    font-size: 13px;
+    font-weight: 500;
+    color: #999;
+    padding: 4px 20px
+}
+
+.bs-docs-sidebar .nav>li>a:hover, .bs-docs-sidebar .nav>li>a:focus {
+    padding-left: 19px;
+    color: #563d7c;
+    text-decoration: none;
+    background-color: transparent;
+    border-left: 1px solid #563d7c
+}
+
+.bs-docs-sidebar .nav>.active>a, .bs-docs-sidebar .nav>.active:hover>a,
+.bs-docs-sidebar .nav>.active:focus>a {
+    padding-left: 18px;
+    font-weight: 700;
+    color: #563d7c;
+    background-color: transparent;
+    border-left: 2px solid #563d7c
+}
+
+.bs-docs-sidebar .nav .nav {
+    display: none;
+    padding-bottom: 10px
+}
+
+.bs-docs-sidebar .nav .nav>li>a {
+    padding-top: 1px;
+    padding-bottom: 1px;
+    padding-left: 30px;
+    font-size: 12px;
+    font-weight: 400
+}
+
+.bs-docs-sidebar .nav .nav>li>a:hover, .bs-docs-sidebar .nav .nav>li>a:focus {
+    padding-left: 29px
+}
+
+.bs-docs-sidebar .nav .nav>.active>a, .bs-docs-sidebar .nav .nav>.active:hover>a,
+.bs-docs-sidebar .nav .nav>.active:focus>a {
+    font-weight: 500;
+    padding-left: 28px
+}
+
+.back-to-top {
+    display: none;
+    margin-top: 10px;
+    margin-left: 10px;
+    padding: 4px 10px;
+    font-size: 12px;
+    font-weight: 500;
+    color: #999
+}
+
+.back-to-top:hover {
+    text-decoration: none;
+    color: #563d7c
+}
+
+@media (min-width:768px){.back-to-top {
+        display: block
+    }
+}
+
+@media (min-width:992px){.bs-docs-sidebar .nav>.active>ul {
+        display: block
+    }
+
+    .bs-docs-sidebar.affix, .bs-docs-sidebar.affix-bottom {
+        width: 213px
+    }
+
+    .bs-docs-sidebar.affix {
+        position: fixed;
+        top: 20px
+    }
+
+    .bs-docs-sidebar.affix-bottom {
+        position: absolute
+    }
+
+    .bs-docs-sidebar.affix-bottom .bs-docs-sidenav, .bs-docs-sidebar.affix .bs-docs-sidenav {
+        margin-top: 0;
+        margin-bottom: 0
+    }
+}
+
+@media (min-width:1200px){.bs-docs-sidebar.affix-bottom, .bs-docs-sidebar.affix {
+        width: 263px
+    }
+}
+
+.bs-docs-section {
+    margin-bottom: 60px
+}
+
+.bs-docs-section:last-child {
+    margin-bottom: 0
+}
+
+h1[id] {
+    margin-top: 0;
+    padding-top: 20px
+}
+
+.bs-callout {
+    margin: 20px 0;
+    padding: 20px;
+    border-left: 3px solid #eee
+}
+
+.bs-callout h4 {
+    margin-top: 0;
+    margin-bottom: 5px
+}
+
+.bs-callout p:last-child {
+    margin-bottom: 0
+}
+
+.bs-callout code {
+    background-color: #fff;
+    border-radius: 3px
+}
+
+.bs-callout-danger {
+    background-color: #fdf7f7;
+    border-color: #d9534f
+}
+
+.bs-callout-danger h4 {
+    color: #d9534f
+}
+
+.bs-callout-warning {
+    background-color: #fcf8f2;
+    border-color: #f0ad4e
+}
+
+.bs-callout-warning h4 {
+    color: #f0ad4e
+}
+
+.bs-callout-info {
+    background-color: #f4f8fa;
+    border-color: #5bc0de
+}
+
+.bs-callout-info h4 {
+    color: #5bc0de
+}
+
+.color-swatches {
+    margin: 0 -5px;
+    overflow: hidden
+}
+
+.color-swatch {
+    float: left;
+    width: 60px;
+    height: 60px;
+    margin: 0 5px;
+    border-radius: 3px
+}
+
+@media (min-width:768px){.color-swatch {
+        width: 100px;
+        height: 100px
+    }
+}
+
+.color-swatches .gray-darker {
+    background-color: #222
+}
+
+.color-swatches .gray-dark {
+    background-color: #333
+}
+
+.color-swatches .gray {
+    background-color: #555
+}
+
+.color-swatches .gray-light {
+    background-color: #999
+}
+
+.color-swatches .gray-lighter {
+    background-color: #eee
+}
+
+.color-swatches .brand-primary {
+    background-color: #428bca
+}
+
+.color-swatches .brand-success {
+    background-color: #5cb85c
+}
+
+.color-swatches .brand-warning {
+    background-color: #f0ad4e
+}
+
+.color-swatches .brand-danger {
+    background-color: #d9534f
+}
+
+.color-swatches .brand-info {
+    background-color: #5bc0de
+}
+
+.color-swatches .bs-purple {
+    background-color: #563d7c
+}
+
+.color-swatches .bs-purple-light {
+    background-color: #c7bfd3
+}
+
+.color-swatches .bs-purple-lighter {
+    background-color: #e5e1ea
+}
+
+.color-swatches .bs-gray {
+    background-color: #f9f9f9
+}
+
+.bs-team .team-member {
+    color: #555;
+    line-height: 32px
+}
+
+.bs-team .team-member:hover {
+    color: #333;
+    text-decoration: none
+}
+
+.bs-team .github-btn {
+    float: right;
+    margin-top: 6px;
+    width: 180px;
+    height: 20px
+}
+
+.bs-team img {
+    float: left;
+    width: 32px;
+    margin-right: 10px;
+    border-radius: 4px
+}
+
+.show-grid {
+    margin-bottom: 15px
+}
+
+.show-grid [class^=col-] {
+    padding-top: 10px;
+    padding-bottom: 10px;
+    background-color: #eee;
+    background-color: rgba(86, 61, 124, .15);
+    border: 1px solid #ddd;
+    border: 1px solid rgba(86, 61, 124, .2)
+}
+
+.bs-example {
+    position: relative;
+    padding: 45px 15px 15px;
+    margin: 0 -15px 15px;
+    background-color: #fafafa;
+    box-shadow: inset 0 3px 6px rgba(0, 0, 0, .05);
+    border-color: #e5e5e5 #eee #eee;
+    border-style: solid;
+    border-width: 1px 0
+}
+
+.bs-example:after {
+    content: "Example";
+    position: absolute;
+    top: 15px;
+    left: 15px;
+    font-size: 12px;
+    font-weight: 700;
+    color: #bbb;
+    text-transform: uppercase;
+    letter-spacing: 1px
+}
+
+.bs-example+.highlight {
+    margin: -15px -15px 15px;
+    border-radius: 0;
+    border-width: 0 0 1px
+}
+
+@media (min-width:768px){.bs-example {
+        margin-left: 0;
+        margin-right: 0;
+        background-color: #fff;
+        border-width: 1px;
+        border-color: #ddd;
+        border-radius: 4px 4px 0 0;
+        box-shadow: none
+    }
+
+    .bs-example+.highlight {
+        margin-top: -16px;
+        margin-left: 0;
+        margin-right: 0;
+        border-width: 1px;
+        border-bottom-left-radius: 4px;
+        border-bottom-right-radius: 4px
+    }
+}
+
+.bs-example .container {
+    width: auto
+}
+
+.bs-example>p:last-child, .bs-example>ul:last-child, .bs-example>ol:last-child,
+.bs-example>blockquote:last-child, .bs-example>.form-control:last-child,
+.bs-example>.table:last-child, .bs-example>.navbar:last-child,
+.bs-example>.jumbotron:last-child, .bs-example>.alert:last-child,
+.bs-example>.panel:last-child, .bs-example>.list-group:last-child,
+.bs-example>.well:last-child, .bs-example>.progress:last-child,
+.bs-example>.table-responsive:last-child>.table {
+    margin-bottom: 0
+}
+
+.bs-example>p>.close {
+    float: none
+}
+
+.bs-example-type .table .type-info {
+    color: #999;
+    vertical-align: middle
+}
+
+.bs-example-type .table td {
+    padding: 15px 0;
+    border-color: #eee
+}
+
+.bs-example-type .table tr:first-child td {
+    border-top: 0
+}
+
+.bs-example-type h1, .bs-example-type h2, .bs-example-type h3, .bs-example-type h4,
+.bs-example-type h5, .bs-example-type h6 {
+    margin: 0
+}
+
+.bs-example-bg-classes p {
+    padding: 15px
+}
+
+.bs-example>.img-circle, .bs-example>.img-rounded, .bs-example>.img-thumbnail {
+    margin: 5px
+}
+
+.bs-example>.table-responsive>.table {
+    background-color: #fff
+}
+
+.bs-example>.btn, .bs-example>.btn-group {
+    margin-top: 5px;
+    margin-bottom: 5px
+}
+
+.bs-example>.btn-toolbar+.btn-toolbar {
+    margin-top: 10px
+}
+
+.bs-example-control-sizing select, .bs-example-control-sizing input[type=text]+input[type=text] {
+    margin-top: 10px
+}
+
+.bs-example-form .input-group {
+    margin-bottom: 10px
+}
+
+.bs-example>textarea.form-control {
+    resize: vertical
+}
+
+.bs-example>.list-group {
+    max-width: 400px
+}
+
+.bs-example .navbar:last-child {
+    margin-bottom: 0
+}
+
+.bs-navbar-top-example, .bs-navbar-bottom-example {
+    z-index: 1;
+    padding: 0;
+    overflow: hidden
+}
+
+.bs-navbar-top-example .navbar-header, .bs-navbar-bottom-example .navbar-header {
+    margin-left: 0
+}
+
+.bs-navbar-top-example .navbar-fixed-top, .bs-navbar-bottom-example .navbar-fixed-bottom {
+    position: relative;
+    margin-left: 0;
+    margin-right: 0
+}
+
+.bs-navbar-top-example {
+    padding-bottom: 45px
+}
+
+.bs-navbar-top-example:after {
+    top: auto;
+    bottom: 15px
+}
+
+.bs-navbar-top-example .navbar-fixed-top {
+    top: -1px
+}
+
+.bs-navbar-bottom-example {
+    padding-top: 45px
+}
+
+.bs-navbar-bottom-example .navbar-fixed-bottom {
+    bottom: -1px
+}
+
+.bs-navbar-bottom-example .navbar {
+    margin-bottom: 0
+}
+
+@media (min-width:768px){.bs-navbar-top-example .navbar-fixed-top, .bs-navbar-bottom-example .navbar-fixed-bottom {
+        position: absolute
+    }
+
+    .bs-navbar-top-example {
+        border-radius: 0 0 4px 4px
+    }
+
+    .bs-navbar-bottom-example {
+        border-radius: 4px 4px 0 0
+    }
+}
+
+.bs-example .pagination {
+    margin-top: 10px;
+    margin-bottom: 10px
+}
+
+.bs-example>.pager {
+    margin-top: 0
+}
+
+.bs-example-modal {
+    background-color: #f5f5f5
+}
+
+.bs-example-modal .modal {
+    position: relative;
+    top: auto;
+    right: auto;
+    left: auto;
+    bottom: auto;
+    z-index: 1;
+    display: block
+}
+
+.bs-example-modal .modal-dialog {
+    left: auto;
+    margin-left: auto;
+    margin-right: auto
+}
+
+.bs-example>.dropdown>.dropdown-menu {
+    position: static;
+    display: block;
+    margin-bottom: 5px
+}
+
+.bs-example-tabs .nav-tabs {
+    margin-bottom: 15px
+}
+
+.bs-example-tooltips {
+    text-align: center
+}
+
+.bs-example-tooltips>.btn {
+    margin-top: 5px;
+    margin-bottom: 5px
+}
+
+.bs-example-popover {
+    padding-bottom: 24px;
+    background-color: #f9f9f9
+}
+
+.bs-example-popover .popover {
+    position: relative;
+    display: block;
+    float: left;
+    width: 260px;
+    margin: 20px
+}
+
+.scrollspy-example {
+    position: relative;
+    height: 200px;
+    margin-top: 10px;
+    overflow: auto
+}
+
+.highlight {
+    padding: 9px 14px;
+    margin-bottom: 14px;
+    background-color: #f7f7f9;
+    border: 1px solid #e1e1e8;
+    border-radius: 4px
+}
+
+.highlight pre {
+    padding: 0;
+    margin-top: 0;
+    margin-bottom: 0;
+    background-color: transparent;
+    border: 0;
+    white-space: nowrap
+}
+
+.highlight pre code {
+    font-size: inherit;
+    color: #333
+}
+
+.highlight pre .lineno {
+    display: inline-block;
+    width: 22px;
+    padding-right: 5px;
+    margin-right: 10px;
+    text-align: right;
+    color: #bebec5
+}
+
+.table-responsive .highlight pre {
+    white-space: normal
+}
+
+.bs-table th small, .responsive-utilities th small {
+    display: block;
+    font-weight: 400;
+    color: #999
+}
+
+.responsive-utilities tbody th {
+    font-weight: 400
+}
+
+.responsive-utilities td {
+    text-align: center
+}
+
+.responsive-utilities td.is-visible {
+    color: #468847;
+    background-color: #dff0d8 !important
+}
+
+.responsive-utilities td.is-hidden {
+    color: #ccc;
+    background-color: #f9f9f9 !important
+}
+
+.responsive-utilities-test {
+    margin-top: 5px
+}
+
+.responsive-utilities-test .col-xs-6 {
+    margin-bottom: 10px
+}
+
+.responsive-utilities-test span {
+    display: block;
+    padding: 15px 10px;
+    font-size: 14px;
+    font-weight: 700;
+    line-height: 1.1;
+    text-align: center;
+    border-radius: 4px
+}
+
+.visible-on .col-xs-6 .hidden-xs, .visible-on .col-xs-6 .hidden-sm,
+.visible-on .col-xs-6 .hidden-md, .visible-on .col-xs-6 .hidden-lg,
+.hidden-on .col-xs-6 .hidden-xs, .hidden-on .col-xs-6 .hidden-sm, .hidden-on .col-xs-6 .hidden-md,
+.hidden-on .col-xs-6 .hidden-lg {
+    color: #999;
+    border: 1px solid #ddd
+}
+
+.visible-on .col-xs-6 .visible-xs, .visible-on .col-xs-6 .visible-sm,
+.visible-on .col-xs-6 .visible-md, .visible-on .col-xs-6 .visible-lg,
+.hidden-on .col-xs-6 .visible-xs, .hidden-on .col-xs-6 .visible-sm,
+.hidden-on .col-xs-6 .visible-md, .hidden-on .col-xs-6 .visible-lg {
+    color: #468847;
+    background-color: #dff0d8;
+    border: 1px solid #d6e9c6
+}
+
+.bs-glyphicons {
+    margin: 0 -19px 20px -16px;
+    overflow: hidden
+}
+
+.bs-glyphicons-list {
+    padding-left: 0;
+    list-style: none
+}
+
+.bs-glyphicons li {
+    float: left;
+    width: 25%;
+    height: 115px;
+    padding: 10px;
+    font-size: 10px;
+    line-height: 1.4;
+    text-align: center;
+    border: 1px solid #fff;
+    background-color: #f9f9f9
+}
+
+.bs-glyphicons .glyphicon {
+    margin-top: 5px;
+    margin-bottom: 10px;
+    font-size: 24px
+}
+
+.bs-glyphicons .glyphicon-class {
+    display: block;
+    text-align: center;
+    word-wrap: break-word
+}
+
+.bs-glyphicons li:hover {
+    color: #fff;
+    background-color: #563d7c
+}
+
+@media (min-width:768px){.bs-glyphicons {
+        margin-left: 0;
+        margin-right: 0
+    }
+
+    .bs-glyphicons li {
+        width: 12.5%;
+        font-size: 12px
+    }
+}
+
+.bs-customizer .toggle {
+    float: right;
+    margin-top: 25px
+}
+
+.bs-customizer label {
+    margin-top: 10px;
+    font-weight: 500;
+    color: #555
+}
+
+.bs-customizer h2 {
+    margin-top: 0;
+    margin-bottom: 5px;
+    padding-top: 30px
+}
+
+.bs-customizer h3 {
+    margin-bottom: 0
+}
+
+.bs-customizer h4 {
+    margin-top: 15px;
+    margin-bottom: 0
+}
+
+.bs-customizer .bs-callout h4 {
+    margin-top: 0;
+    margin-bottom: 5px
+}
+
+.bs-customizer input[type=text] {
+    font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
+    background-color: #fafafa
+}
+
+.bs-customizer .help-block {
+    font-size: 12px;
+    margin-bottom: 5px
+}
+
+#less-section label {
+    font-weight: 400
+}
+
+.bs-customizer-input {
+    float: left;
+    width: 33.333333%;
+    padding-left: 15px;
+    padding-right: 15px
+}
+
+.bs-customize-download .btn-outline {
+    padding: 20px
+}
+
+.bs-customizer-alert {
+    position: fixed;
+    top: 0;
+    left: 0;
+    right: 0;
+    z-index: 1030;
+    padding: 15px 0;
+    color: #fff;
+    background-color: #d9534f;
+    box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25);
+    border-bottom: 1px solid #b94441
+}
+
+.bs-customizer-alert .close {
+    margin-top: -4px;
+    font-size: 24px
+}
+
+.bs-customizer-alert p {
+    margin-bottom: 0
+}
+
+.bs-customizer-alert .glyphicon {
+    margin-right: 5px
+}
+
+.bs-customizer-alert pre {
+    margin: 10px 0 0;
+    color: #fff;
+    background-color: #a83c3a;
+    border-color: #973634;
+    box-shadow: inset 0 2px 4px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1)
+}
+
+.bs-brand-logos {
+    display: table;
+    width: 100%;
+    margin-bottom: 15px;
+    overflow: hidden;
+    color: #563d7c;
+    background-color: #f9f9f9;
+    border-radius: 4px
+}
+
+.bs-brand-item {
+    padding: 60px 0;
+    text-align: center
+}
+
+.bs-brand-item+.bs-brand-item {
+    border-top: 1px solid #fff
+}
+
+.bs-brand-logos .inverse {
+    color: #fff;
+    background-color: #563d7c
+}
+
+.bs-brand-item h1, .bs-brand-item h3 {
+    margin-top: 0;
+    margin-bottom: 0
+}
+
+.bs-brand-item .bs-docs-booticon {
+    margin-left: auto;
+    margin-right: auto
+}
+
+.bs-brand-item .glyphicon {
+    width: 30px;
+    height: 30px;
+    margin: 10px auto -10px;
+    line-height: 30px;
+    color: #fff;
+    border-radius: 50%
+}
+
+.bs-brand-item .glyphicon-ok {
+    background-color: #5cb85c
+}
+
+.bs-brand-item .glyphicon-remove {
+    background-color: #d9534f
+}
+
+@media (min-width:768px){.bs-brand-item {
+        display: table-cell;
+        width: 1%
+    }
+
+    .bs-brand-item+.bs-brand-item {
+        border-top: 0;
+        border-left: 1px solid #fff
+    }
+
+    .bs-brand-item h1 {
+        font-size: 60px
+    }
+}
+
+.bs-examples .thumbnail {
+    margin-bottom: 10px
+}
+
+.bs-examples h4 {
+    margin-bottom: 5px
+}
+
+.bs-examples p {
+    margin-bottom: 20px
+}
+
+#focusedInput {
+    border-color: #ccc;
+    border-color: rgba(82, 168, 236, .8);
+    outline: 0;
+    outline: thin dotted \9;
+    -moz-box-shadow: 0 0 8px rgba(82, 168, 236, .6);
+    box-shadow: 0 0 8px rgba(82, 168, 236, .6)
+}
+
+.hll {
+    background-color: #ffc
+}
+
+.c {
+    color: #999
+}
+
+.err {
+    color: #A00;
+    background-color: #FAA
+}
+
+.k {
+    color: #069
+}
+
+.o {
+    color: #555
+}
+
+.cm {
+    color: #999
+}
+
+.cp {
+    color: #099
+}
+
+.c1 {
+    color: #999
+}
+
+.cs {
+    color: #999
+}
+
+.gd {
+    background-color: #FCC;
+    border: 1px solid #C00
+}
+
+.ge {
+    font-style: italic
+}
+
+.gr {
+    color: red
+}
+
+.gh {
+    color: #030
+}
+
+.gi {
+    background-color: #CFC;
+    border: 1px solid #0C0
+}
+
+.go {
+    color: #AAA
+}
+
+.gp {
+    color: #009
+}
+
+.gu {
+    color: #030
+}
+
+.gt {
+    color: #9C6
+}
+
+.kc {
+    color: #069
+}
+
+.kd {
+    color: #069
+}
+
+.kn {
+    color: #069
+}
+
+.kp {
+    color: #069
+}
+
+.kr {
+    color: #069
+}
+
+.kt {
+    color: #078
+}
+
+.m {
+    color: #F60
+}
+
+.s {
+    color: #d44950
+}
+
+.na {
+    color: #4f9fcf
+}
+
+.nb {
+    color: #366
+}
+
+.nc {
+    color: #0A8
+}
+
+.no {
+    color: #360
+}
+
+.nd {
+    color: #99F
+}
+
+.ni {
+    color: #999
+}
+
+.ne {
+    color: #C00
+}
+
+.nf {
+    color: #C0F
+}
+
+.nl {
+    color: #99F
+}
+
+.nn {
+    color: #0CF
+}
+
+.nt {
+    color: #2f6f9f
+}
+
+.nv {
+    color: #033
+}
+
+.ow {
+    color: #000
+}
+
+.w {
+    color: #bbb
+}
+
+.mf {
+    color: #F60
+}
+
+.mh {
+    color: #F60
+}
+
+.mi {
+    color: #F60
+}
+
+.mo {
+    color: #F60
+}
+
+.sb {
+    color: #C30
+}
+
+.sc {
+    color: #C30
+}
+
+.sd {
+    color: #C30;
+    font-style: italic
+}
+
+.s2 {
+    color: #C30
+}
+
+.se {
+    color: #C30
+}
+
+.sh {
+    color: #C30
+}
+
+.si {
+    color: #A00
+}
+
+.sx {
+    color: #C30
+}
+
+.sr {
+    color: #3AA
+}
+
+.s1 {
+    color: #C30
+}
+
+.ss {
+    color: #FC3
+}
+
+.bp {
+    color: #366
+}
+
+.vc {
+    color: #033
+}
+
+.vg {
+    color: #033
+}
+
+.vi {
+    color: #033
+}
+
+.il {
+    color: #F60
+}
+
+.css .o, .css .o+.nt, .css .nt+.nt {
+    color: #999
+}
diff --git a/pelican-plugins/bootstrap-rst/make-glyphicons.py b/pelican-plugins/bootstrap-rst/make-glyphicons.py
new file mode 100644
index 0000000000000000000000000000000000000000..0f7409e753d43fd6a300febc4ba56bd86851009c
--- /dev/null
+++ b/pelican-plugins/bootstrap-rst/make-glyphicons.py
@@ -0,0 +1,224 @@
+from __future__ import print_function
+glyphs = """<span class="glyphicon glyphicon-asterisk"></span>
+<span class="glyphicon glyphicon-plus"></span>
+<span class="glyphicon glyphicon-euro"></span>
+<span class="glyphicon glyphicon-minus"></span>
+<span class="glyphicon glyphicon-cloud"></span>
+<span class="glyphicon glyphicon-envelope"></span>
+<span class="glyphicon glyphicon-pencil"></span>
+<span class="glyphicon glyphicon-glass"></span>
+<span class="glyphicon glyphicon-music"></span>
+<span class="glyphicon glyphicon-search"></span>
+<span class="glyphicon glyphicon-heart"></span>
+<span class="glyphicon glyphicon-star"></span>
+<span class="glyphicon glyphicon-star-empty"></span>
+<span class="glyphicon glyphicon-user"></span>
+<span class="glyphicon glyphicon-film"></span>
+<span class="glyphicon glyphicon-th-large"></span>
+<span class="glyphicon glyphicon-th"></span>
+<span class="glyphicon glyphicon-th-list"></span>
+<span class="glyphicon glyphicon-ok"></span>
+<span class="glyphicon glyphicon-remove"></span>
+<span class="glyphicon glyphicon-zoom-in"></span>
+<span class="glyphicon glyphicon-zoom-out"></span>
+<span class="glyphicon glyphicon-off"></span>
+<span class="glyphicon glyphicon-signal"></span>
+<span class="glyphicon glyphicon-cog"></span>
+<span class="glyphicon glyphicon-trash"></span>
+<span class="glyphicon glyphicon-home"></span>
+<span class="glyphicon glyphicon-file"></span>
+<span class="glyphicon glyphicon-time"></span>
+<span class="glyphicon glyphicon-road"></span>
+<span class="glyphicon glyphicon-download-alt"></span>
+<span class="glyphicon glyphicon-download"></span>
+<span class="glyphicon glyphicon-upload"></span>
+<span class="glyphicon glyphicon-inbox"></span>
+<span class="glyphicon glyphicon-play-circle"></span>
+<span class="glyphicon glyphicon-repeat"></span>
+<span class="glyphicon glyphicon-refresh"></span>
+<span class="glyphicon glyphicon-list-alt"></span>
+<span class="glyphicon glyphicon-lock"></span>
+<span class="glyphicon glyphicon-flag"></span>
+<span class="glyphicon glyphicon-headphones"></span>
+<span class="glyphicon glyphicon-volume-off"></span>
+<span class="glyphicon glyphicon-volume-down"></span>
+<span class="glyphicon glyphicon-volume-up"></span>
+<span class="glyphicon glyphicon-qrcode"></span>
+<span class="glyphicon glyphicon-barcode"></span>
+<span class="glyphicon glyphicon-tag"></span>
+<span class="glyphicon glyphicon-tags"></span>
+<span class="glyphicon glyphicon-book"></span>
+<span class="glyphicon glyphicon-bookmark"></span>
+<span class="glyphicon glyphicon-print"></span>
+<span class="glyphicon glyphicon-camera"></span>
+<span class="glyphicon glyphicon-font"></span>
+<span class="glyphicon glyphicon-bold"></span>
+<span class="glyphicon glyphicon-italic"></span>
+<span class="glyphicon glyphicon-text-height"></span>
+<span class="glyphicon glyphicon-text-width"></span>
+<span class="glyphicon glyphicon-align-left"></span>
+<span class="glyphicon glyphicon-align-center"></span>
+<span class="glyphicon glyphicon-align-right"></span>
+<span class="glyphicon glyphicon-align-justify"></span>
+<span class="glyphicon glyphicon-list"></span>
+<span class="glyphicon glyphicon-indent-left"></span>
+<span class="glyphicon glyphicon-indent-right"></span>
+<span class="glyphicon glyphicon-facetime-video"></span>
+<span class="glyphicon glyphicon-picture"></span>
+<span class="glyphicon glyphicon-map-marker"></span>
+<span class="glyphicon glyphicon-adjust"></span>
+<span class="glyphicon glyphicon-tint"></span>
+<span class="glyphicon glyphicon-edit"></span>
+<span class="glyphicon glyphicon-share"></span>
+<span class="glyphicon glyphicon-check"></span>
+<span class="glyphicon glyphicon-move"></span>
+<span class="glyphicon glyphicon-step-backward"></span>
+<span class="glyphicon glyphicon-fast-backward"></span>
+<span class="glyphicon glyphicon-backward"></span>
+<span class="glyphicon glyphicon-play"></span>
+<span class="glyphicon glyphicon-pause"></span>
+<span class="glyphicon glyphicon-stop"></span>
+<span class="glyphicon glyphicon-forward"></span>
+<span class="glyphicon glyphicon-fast-forward"></span>
+<span class="glyphicon glyphicon-step-forward"></span>
+<span class="glyphicon glyphicon-eject"></span>
+<span class="glyphicon glyphicon-chevron-left"></span>
+<span class="glyphicon glyphicon-chevron-right"></span>
+<span class="glyphicon glyphicon-plus-sign"></span>
+<span class="glyphicon glyphicon-minus-sign"></span>
+<span class="glyphicon glyphicon-remove-sign"></span>
+<span class="glyphicon glyphicon-ok-sign"></span>
+<span class="glyphicon glyphicon-question-sign"></span>
+<span class="glyphicon glyphicon-info-sign"></span>
+<span class="glyphicon glyphicon-screenshot"></span>
+<span class="glyphicon glyphicon-remove-circle"></span>
+<span class="glyphicon glyphicon-ok-circle"></span>
+<span class="glyphicon glyphicon-ban-circle"></span>
+<span class="glyphicon glyphicon-arrow-left"></span>
+<span class="glyphicon glyphicon-arrow-right"></span>
+<span class="glyphicon glyphicon-arrow-up"></span>
+<span class="glyphicon glyphicon-arrow-down"></span>
+<span class="glyphicon glyphicon-share-alt"></span>
+<span class="glyphicon glyphicon-resize-full"></span>
+<span class="glyphicon glyphicon-resize-small"></span>
+<span class="glyphicon glyphicon-exclamation-sign"></span>
+<span class="glyphicon glyphicon-gift"></span>
+<span class="glyphicon glyphicon-leaf"></span>
+<span class="glyphicon glyphicon-fire"></span>
+<span class="glyphicon glyphicon-eye-open"></span>
+<span class="glyphicon glyphicon-eye-close"></span>
+<span class="glyphicon glyphicon-warning-sign"></span>
+<span class="glyphicon glyphicon-plane"></span>
+<span class="glyphicon glyphicon-calendar"></span>
+<span class="glyphicon glyphicon-random"></span>
+<span class="glyphicon glyphicon-comment"></span>
+<span class="glyphicon glyphicon-magnet"></span>
+<span class="glyphicon glyphicon-chevron-up"></span>
+<span class="glyphicon glyphicon-chevron-down"></span>
+<span class="glyphicon glyphicon-retweet"></span>
+<span class="glyphicon glyphicon-shopping-cart"></span>
+<span class="glyphicon glyphicon-folder-close"></span>
+<span class="glyphicon glyphicon-folder-open"></span>
+<span class="glyphicon glyphicon-resize-vertical"></span>
+<span class="glyphicon glyphicon-resize-horizontal"></span>
+<span class="glyphicon glyphicon-hdd"></span>
+<span class="glyphicon glyphicon-bullhorn"></span>
+<span class="glyphicon glyphicon-bell"></span>
+<span class="glyphicon glyphicon-certificate"></span>
+<span class="glyphicon glyphicon-thumbs-up"></span>
+<span class="glyphicon glyphicon-thumbs-down"></span>
+<span class="glyphicon glyphicon-hand-right"></span>
+<span class="glyphicon glyphicon-hand-left"></span>
+<span class="glyphicon glyphicon-hand-up"></span>
+<span class="glyphicon glyphicon-hand-down"></span>
+<span class="glyphicon glyphicon-circle-arrow-right"></span>
+<span class="glyphicon glyphicon-circle-arrow-left"></span>
+<span class="glyphicon glyphicon-circle-arrow-up"></span>
+<span class="glyphicon glyphicon-circle-arrow-down"></span>
+<span class="glyphicon glyphicon-globe"></span>
+<span class="glyphicon glyphicon-wrench"></span>
+<span class="glyphicon glyphicon-tasks"></span>
+<span class="glyphicon glyphicon-filter"></span>
+<span class="glyphicon glyphicon-briefcase"></span>
+<span class="glyphicon glyphicon-fullscreen"></span>
+<span class="glyphicon glyphicon-dashboard"></span>
+<span class="glyphicon glyphicon-paperclip"></span>
+<span class="glyphicon glyphicon-heart-empty"></span>
+<span class="glyphicon glyphicon-link"></span>
+<span class="glyphicon glyphicon-phone"></span>
+<span class="glyphicon glyphicon-pushpin"></span>
+<span class="glyphicon glyphicon-usd"></span>
+<span class="glyphicon glyphicon-gbp"></span>
+<span class="glyphicon glyphicon-sort"></span>
+<span class="glyphicon glyphicon-sort-by-alphabet"></span>
+<span class="glyphicon glyphicon-sort-by-alphabet-alt"></span>
+<span class="glyphicon glyphicon-sort-by-order"></span>
+<span class="glyphicon glyphicon-sort-by-order-alt"></span>
+<span class="glyphicon glyphicon-sort-by-attributes"></span>
+<span class="glyphicon glyphicon-sort-by-attributes-alt"></span>
+<span class="glyphicon glyphicon-unchecked"></span>
+<span class="glyphicon glyphicon-expand"></span>
+<span class="glyphicon glyphicon-collapse-down"></span>
+<span class="glyphicon glyphicon-collapse-up"></span>
+<span class="glyphicon glyphicon-log-in"></span>
+<span class="glyphicon glyphicon-flash"></span>
+<span class="glyphicon glyphicon-log-out"></span>
+<span class="glyphicon glyphicon-new-window"></span>
+<span class="glyphicon glyphicon-record"></span>
+<span class="glyphicon glyphicon-save"></span>
+<span class="glyphicon glyphicon-open"></span>
+<span class="glyphicon glyphicon-saved"></span>
+<span class="glyphicon glyphicon-import"></span>
+<span class="glyphicon glyphicon-export"></span>
+<span class="glyphicon glyphicon-send"></span>
+<span class="glyphicon glyphicon-floppy-disk"></span>
+<span class="glyphicon glyphicon-floppy-saved"></span>
+<span class="glyphicon glyphicon-floppy-remove"></span>
+<span class="glyphicon glyphicon-floppy-save"></span>
+<span class="glyphicon glyphicon-floppy-open"></span>
+<span class="glyphicon glyphicon-credit-card"></span>
+<span class="glyphicon glyphicon-transfer"></span>
+<span class="glyphicon glyphicon-cutlery"></span>
+<span class="glyphicon glyphicon-header"></span>
+<span class="glyphicon glyphicon-compressed"></span>
+<span class="glyphicon glyphicon-earphone"></span>
+<span class="glyphicon glyphicon-phone-alt"></span>
+<span class="glyphicon glyphicon-tower"></span>
+<span class="glyphicon glyphicon-stats"></span>
+<span class="glyphicon glyphicon-sd-video"></span>
+<span class="glyphicon glyphicon-hd-video"></span>
+<span class="glyphicon glyphicon-subtitles"></span>
+<span class="glyphicon glyphicon-sound-stereo"></span>
+<span class="glyphicon glyphicon-sound-dolby"></span>
+<span class="glyphicon glyphicon-sound-5-1"></span>
+<span class="glyphicon glyphicon-sound-6-1"></span>
+<span class="glyphicon glyphicon-sound-7-1"></span>
+<span class="glyphicon glyphicon-copyright-mark"></span>
+<span class="glyphicon glyphicon-registration-mark"></span>
+<span class="glyphicon glyphicon-cloud-download"></span>
+<span class="glyphicon glyphicon-cloud-upload"></span>
+<span class="glyphicon glyphicon-tree-conifer"></span>
+<span class="glyphicon glyphicon-tree-deciduous"></span>"""
+
+glyphs = glyphs.split('\n')
+
+
+#for glyph in glyphs.split('\n'):
+#    name = glyph[33:-9]
+#    print ".. |%s| raw:: html" % name
+#    print ""
+#    print "   %s" % glyph
+#    print ""
+
+
+print(".. row::")
+for i, glyph in enumerate(glyphs):
+    if i % 50 == 0:
+        print("")
+        print("   .. column::")
+        print("      :width: 3")
+        print("")
+        print("      .. list:: list-unstyled")
+        print("")
+    name = glyph[33:-9]
+    print("         * |%s| : %s" % (name,name))
diff --git a/pelican-plugins/bootstrap-rst/page.tmpl b/pelican-plugins/bootstrap-rst/page.tmpl
new file mode 100644
index 0000000000000000000000000000000000000000..643b28291847f573e82de5b915f8175e68a99e8c
--- /dev/null
+++ b/pelican-plugins/bootstrap-rst/page.tmpl
@@ -0,0 +1,22 @@
+%(head_prefix)s
+%(head)s
+<meta name="viewport" content="width=device-width, initial-scale=1">
+<!-- Bootstrap core CSS =================================================== -->
+<link href="bootstrap/css/bootstrap.min.css" rel="stylesheet" media="screen">
+<link href="docs-min.css" rel="stylesheet">
+<link href="custom.css" rel="stylesheet">
+%(stylesheet)s
+%(body_prefix)s
+%(body_pre_docinfo)s
+%(docinfo)s
+<div class="row">
+%(body)s
+</div>
+</div>
+
+<!-- Bootstrap core JavaScript ============================================ -->
+<!-- Placed at the end of the document so the pages load faster -->
+<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
+<script src="bootstrap/js/bootstrap.min.js"></script>
+
+%(body_suffix)s
diff --git a/pelican-plugins/bootstrap-rst/pygments.css b/pelican-plugins/bootstrap-rst/pygments.css
new file mode 100644
index 0000000000000000000000000000000000000000..6f4f6c869c56913f1fdbcda9f105ed6d82835b0e
--- /dev/null
+++ b/pelican-plugins/bootstrap-rst/pygments.css
@@ -0,0 +1,59 @@
+.hll { background-color: #ffffcc }
+.c { color: #999988; font-style: italic } /* Comment */
+.err { color: #a61717; background-color: #e3d2d2 } /* Error */
+.k { font-weight: bold } /* Keyword */
+.o { font-weight: bold } /* Operator */
+.cm { color: #999988; font-style: italic } /* Comment.Multiline */
+.cp { color: #999999; font-weight: bold } /* Comment.Preproc */
+.c1 { color: #999988; font-style: italic } /* Comment.Single */
+.cs { color: #999999; font-weight: bold; font-style: italic } /* Comment.Special */
+.gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
+.ge { font-style: italic } /* Generic.Emph */
+.gr { color: #aa0000 } /* Generic.Error */
+.gh { color: #999999 } /* Generic.Heading */
+.gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
+.go { color: #888888 } /* Generic.Output */
+.gp { color: #555555 } /* Generic.Prompt */
+.gs { font-weight: bold } /* Generic.Strong */
+.gu { color: #aaaaaa } /* Generic.Subheading */
+.gt { color: #aa0000 } /* Generic.Traceback */
+.kc { font-weight: bold } /* Keyword.Constant */
+.kd { font-weight: bold } /* Keyword.Declaration */
+.kn { font-weight: bold } /* Keyword.Namespace */
+.kp { font-weight: bold } /* Keyword.Pseudo */
+.kr { font-weight: bold } /* Keyword.Reserved */
+.kt { color: #445588; font-weight: bold } /* Keyword.Type */
+.m { color: #009999 } /* Literal.Number */
+.s { color: #bb8844 } /* Literal.String */
+.na { color: #008080 } /* Name.Attribute */
+.nb { color: #999999 } /* Name.Builtin */
+.nc { color: #445588; font-weight: bold } /* Name.Class */
+.no { color: #008080 } /* Name.Constant */
+.ni { color: #800080 } /* Name.Entity */
+.ne { color: #990000; font-weight: bold } /* Name.Exception */
+.nf { color: #990000; font-weight: bold } /* Name.Function */
+.nn { color: #555555 } /* Name.Namespace */
+.nt { color: #000080 } /* Name.Tag */
+.nv { color: #008080 } /* Name.Variable */
+.ow { font-weight: bold } /* Operator.Word */
+.w { color: #bbbbbb } /* Text.Whitespace */
+.mf { color: #009999 } /* Literal.Number.Float */
+.mh { color: #009999 } /* Literal.Number.Hex */
+.mi { color: #009999 } /* Literal.Number.Integer */
+.mo { color: #009999 } /* Literal.Number.Oct */
+.sb { color: #bb8844 } /* Literal.String.Backtick */
+.sc { color: #bb8844 } /* Literal.String.Char */
+.sd { color: #bb8844 } /* Literal.String.Doc */
+.s2 { color: #bb8844 } /* Literal.String.Double */
+.se { color: #bb8844 } /* Literal.String.Escape */
+.sh { color: #bb8844 } /* Literal.String.Heredoc */
+.si { color: #bb8844 } /* Literal.String.Interpol */
+.sx { color: #bb8844 } /* Literal.String.Other */
+.sr { color: #808000 } /* Literal.String.Regex */
+.s1 { color: #bb8844 } /* Literal.String.Single */
+.ss { color: #bb8844 } /* Literal.String.Symbol */
+.bp { color: #999999 } /* Name.Builtin.Pseudo */
+.vc { color: #008080 } /* Name.Variable.Class */
+.vg { color: #008080 } /* Name.Variable.Global */
+.vi { color: #008080 } /* Name.Variable.Instance */
+.il { color: #009999 } /* Literal.Number.Integer.Long */
diff --git a/pelican-plugins/bootstrap-rst/roles.py b/pelican-plugins/bootstrap-rst/roles.py
new file mode 100644
index 0000000000000000000000000000000000000000..06bc6896e317777c363d864b8270001b1051a049
--- /dev/null
+++ b/pelican-plugins/bootstrap-rst/roles.py
@@ -0,0 +1,39 @@
+# -*- coding: utf-8 -*-
+# -----------------------------------------------------------------------------
+# Bootstrap RST
+# Copyright (c) 2014, Nicolas P. Rougier
+# Distributed under the (new) BSD License. See LICENSE.txt for more info.
+# -----------------------------------------------------------------------------
+from docutils import nodes, utils
+from docutils.parsers.rst import roles
+
+class h1(nodes.Inline, nodes.TextElement): pass
+class h2(nodes.Inline, nodes.TextElement): pass
+class h3(nodes.Inline, nodes.TextElement): pass
+class h4(nodes.Inline, nodes.TextElement): pass
+class h5(nodes.Inline, nodes.TextElement): pass
+class h6(nodes.Inline, nodes.TextElement): pass
+
+class label_default(nodes.Inline, nodes.TextElement): pass
+class label_muted(nodes.Inline, nodes.TextElement): pass
+class label_primary(nodes.Inline, nodes.TextElement): pass
+class label_success(nodes.Inline, nodes.TextElement): pass
+class label_info(nodes.Inline, nodes.TextElement): pass
+class label_warning(nodes.Inline, nodes.TextElement): pass
+class label_danger(nodes.Inline, nodes.TextElement): pass
+
+
+roles.register_generic_role('h1',h1)
+roles.register_generic_role('h2',h2)
+roles.register_generic_role('h3',h3)
+roles.register_generic_role('h4',h4)
+roles.register_generic_role('h5',h5)
+roles.register_generic_role('h6',h6)
+
+roles.register_generic_role('label-default',label_default)
+roles.register_generic_role('label-muted',label_muted)
+roles.register_generic_role('label-primary',label_primary)
+roles.register_generic_role('label-success',label_success)
+roles.register_generic_role('label-info',label_info)
+roles.register_generic_role('label-warning',label_warning)
+roles.register_generic_role('label-danger',label_danger)
diff --git a/pelican-plugins/category_meta/README.md b/pelican-plugins/category_meta/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..02f909cbd3b531fbc8116f84a402014b7f571a07
--- /dev/null
+++ b/pelican-plugins/category_meta/README.md
@@ -0,0 +1,22 @@
+Category Metadata
+-----------------
+
+A plugin to read metadata for each category from an index file in that
+category's directory.
+
+For this plugin to work properly, your articles should not have a
+Category: tag in their metadata; instead, they should be stored in
+(subdirectories of) per-category directories.  Each per-category
+directory must have a file named 'index.ext' at its top level, where
+.ext is any extension that will be picked up by an article reader.
+The metadata of that article becomes the metadata for the category,
+copied over verbatim, with three special cases:
+
+ * The category's name is set to the article's title.
+ * The category's slug is set to the name of the parent directory
+   of the index.ext file.
+ * The _text_ of the article is stored as category.description.
+
+You can use this, for example, to control the slug used for each
+category independently of its name, or to add a description at the top
+of each category page.
diff --git a/pelican-plugins/category_meta/__init__.py b/pelican-plugins/category_meta/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..aa50e461efa7e83219aaf66cc4365d02d9b1aa87
--- /dev/null
+++ b/pelican-plugins/category_meta/__init__.py
@@ -0,0 +1 @@
+from .category_meta import *
diff --git a/pelican-plugins/category_meta/category_meta.py b/pelican-plugins/category_meta/category_meta.py
new file mode 100644
index 0000000000000000000000000000000000000000..de8a129921c7179ea5b4123c96aeb268b6811e11
--- /dev/null
+++ b/pelican-plugins/category_meta/category_meta.py
@@ -0,0 +1,132 @@
+'''Copyright 2014, 2015 Zack Weinberg
+
+Category Metadata
+-----------------
+
+A plugin to read metadata for each category from an index file in that
+category's directory.
+
+For this plugin to work properly, your articles should not have a
+Category: tag in their metadata; instead, they should be stored in
+(subdirectories of) per-category directories.  Each per-category
+directory must have a file named 'index.ext' at its top level, where
+.ext is any extension that will be picked up by an article reader.
+The metadata of that article becomes the metadata for the category,
+copied over verbatim, with three special cases:
+
+ * The category's name is set to the article's title.
+ * The category's slug is set to the name of the parent directory
+   of the index.ext file.
+ * The _text_ of the article is stored as category.description.
+'''
+
+from pelican import signals
+import os
+import re
+
+import logging
+logger = logging.getLogger(__name__)
+
+### CORE BUG: https://github.com/getpelican/pelican/issues/1547
+### Content.url_format does not honor category.slug (or author.slug).
+### The sanest way to work around this is to dynamically redefine each
+### article's class to a subclass of itself with the bug fixed.
+###
+### Core was fixed in rev 822fb134e041c6938c253dd4db71813c4d0dc74a,
+### which is not yet in any release, so we dynamically detect whether
+### the installed version of Pelican still has the bug.
+
+patched_subclasses = {}
+def make_patched_subclass(klass):
+    if klass.__name__ not in patched_subclasses:
+        class PatchedContent(klass):
+            @property
+            def url_format(self):
+                metadata = super(PatchedContent, self).url_format
+                if hasattr(self, 'author'):
+                    metadata['author'] = self.author.slug
+                if hasattr(self, 'category'):
+                    metadata['category'] = self.category.slug
+
+                return metadata
+        # Code in core uses Content class names as keys for things.
+        PatchedContent.__name__ = klass.__name__
+        patched_subclasses[klass.__name__] = PatchedContent
+    return patched_subclasses[klass.__name__]
+
+def patch_urlformat(cont):
+    # Test whether this content object needs to be patched.
+    md = cont.url_format
+    if ((hasattr(cont, 'author') and cont.author.slug != md['author']) or
+        (hasattr(cont, 'category') and cont.category.slug != md['category'])):
+        logger.debug("Detected bug 1547, applying workaround.")
+        cont.__class__ = make_patched_subclass(cont.__class__)
+
+### END OF BUG WORKAROUND
+
+def make_category(article, slug):
+    # Reuse the article's existing category object.
+    category = article.category
+
+    # Setting a category's name resets its slug, so do that first.
+    category.name = article.title
+    try:
+        category.slug = slug
+    except AttributeError:
+        category._slug = slug
+
+    # Description from article text.
+    # XXX Relative URLs in the article content may not be handled correctly.
+    setattr(category, 'description', article.content)
+
+    # Metadata, to the extent that this makes sense.
+    for k, v in article.metadata.items():
+        if k not in ('path', 'slug', 'category', 'name', 'title',
+                     'description', 'reader'):
+            setattr(category, k, v)
+
+    logger.debug("Category: %s -> %s", category.slug, category.name)
+    return category
+
+def pretaxonomy_hook(generator):
+    """This hook is invoked before the generator's .categories property is
+       filled in. Each article has already been assigned a category
+       object, but these objects are _not_ unique per category and so are
+       not safe to tack metadata onto (as is).
+
+       The category metadata we're looking for is represented as an
+       Article object, one per directory, whose filename is 'index.ext'.
+    """
+
+    category_objects = {}
+    real_articles = []
+
+    for article in generator.articles:
+        dirname, fname = os.path.split(article.source_path)
+        fname, _ = os.path.splitext(fname)
+        if fname == 'index':
+            category_objects[dirname] = \
+                make_category(article, os.path.basename(dirname))
+        else:
+            real_articles.append(article)
+
+    category_assignment = \
+        re.compile("^(" +
+                   "|".join(re.escape(prefix)
+                            for prefix in category_objects.keys()) +
+                   ")/")
+
+    for article in real_articles:
+        m = category_assignment.match(article.source_path)
+        if not m or m.group(1) not in category_objects:
+            logger.error("No category assignment for %s (%s)",
+                         article, article.source_path)
+            continue
+
+        article.category = category_objects[m.group(1)]
+        patch_urlformat(article)
+
+    generator.articles = real_articles
+
+def register():
+    signals.article_generator_pretaxonomy.connect(pretaxonomy_hook)
diff --git a/pelican-plugins/clean_summary/README.md b/pelican-plugins/clean_summary/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..b2307966c6d7364695a69e7dff284a624525d805
--- /dev/null
+++ b/pelican-plugins/clean_summary/README.md
@@ -0,0 +1,34 @@
+# Clean Summary Plugin
+
+This plugin cleans your summary of excess images. Images can take up much more
+space than text and lead to summaries being different sizes on archive and
+index pages. With this plugin, you can specify a maximum number of images that
+will appear in your summaries.
+
+There is also an option to include a minimum of one image.
+
+## Settings
+
+This plugin has two settings: `CLEAN_SUMMARY_MAXIMUM`, which takes an integer,
+and `CLEAN_SUMMARY_MINIMUM_ONE`, which takes a Boolean value. They default to
+`0` and `False`, respectively.
+
+`CLEAN_SUMMARY_MAXIMUM` sets the maximum number of images that will appear in
+your summary.
+
+If `CLEAN_SUMMARY_MINIMUM_ONE` is set to `True` and your summary doesn't already
+contain an image, the plugin will add the first image in your article (if one
+exists) to the beginning of the summary.
+
+## Requirements
+
+Requires Beautiful Soup:
+
+    pip install BeautifulSoup4
+
+## Usage with Summary Plugin
+
+If using the Summary plugin, make sure it appears in your plugin list before
+the Clean Summary plugin. For example:
+
+    PLUGINS = ['summary', 'clean_summary', ... ]
diff --git a/pelican-plugins/clean_summary/__init__.py b/pelican-plugins/clean_summary/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..1b966114096d07b9b3717936a43db1e5f5dc537a
--- /dev/null
+++ b/pelican-plugins/clean_summary/__init__.py
@@ -0,0 +1 @@
+from .clean_summary import *
diff --git a/pelican-plugins/clean_summary/clean_summary.py b/pelican-plugins/clean_summary/clean_summary.py
new file mode 100644
index 0000000000000000000000000000000000000000..9d0fe0e0908d2a13e66459db8d00b991d82e8724
--- /dev/null
+++ b/pelican-plugins/clean_summary/clean_summary.py
@@ -0,0 +1,52 @@
+"""
+Clean Summary
+-------------
+
+adds option to specify maximum number of images to appear in article summary
+also adds option to include an image by default if one exists in your article
+"""
+
+from pelican import signals
+from pelican.contents import Content, Article
+from pelican.generators import ArticlesGenerator
+from bs4 import BeautifulSoup
+from six import text_type
+
+def init(pelican):
+    global maximum_images
+    global minimum_one
+    maximum_images = pelican.settings.get('CLEAN_SUMMARY_MAXIMUM', 0)
+    minimum_one = pelican.settings.get('CLEAN_SUMMARY_MINIMUM_ONE', False)
+
+
+def clean_summary(instance):
+    if type(instance) == Article:
+        summary = instance.summary
+        summary = BeautifulSoup(instance.summary, 'html.parser')
+        images = summary.findAll('img')
+        if (len(images) > maximum_images):
+            for image in images[maximum_images:]:
+                image.extract()
+        if len(images) < 1 and minimum_one: #try to find one
+            content = BeautifulSoup(instance.content, 'html.parser')
+            first_image = content.find('img')
+            if first_image:
+                summary.insert(0, first_image)
+        instance._summary = text_type(summary)
+
+
+def run_plugin(generators):
+    for generator in generators:
+        if isinstance(generator, ArticlesGenerator):
+            for article in generator.articles:
+                clean_summary(article)
+
+
+def register():
+    signals.initialized.connect(init)
+    try:
+        signals.all_generators_finalized.connect(run_plugin)
+    except AttributeError:
+        # NOTE: This may result in #314 so shouldn't really be relied on
+        # https://github.com/getpelican/pelican-plugins/issues/314
+        signals.content_object_init.connect(clean_summary)
diff --git a/pelican-plugins/code_include/README.rst b/pelican-plugins/code_include/README.rst
new file mode 100644
index 0000000000000000000000000000000000000000..b7e08ec0f202ca45f6be6c4d56470abd46a1b0c6
--- /dev/null
+++ b/pelican-plugins/code_include/README.rst
@@ -0,0 +1,69 @@
+Include Pygments highlighted code with reStructuredText
+=======================================================
+
+:author: Colin Dunklau
+
+Use this plugin to make writing coding tutorials easier! You can
+maintain the example source files separately from the actual article.
+
+Based heavily on ``docutils.parsers.rst.directives.Include``. Include
+a file and output as a code block formatted with pelican's Pygments
+directive.
+
+Note that this is broken with the Docutils 0.10 release, there's a
+circular import. It was fixed in trunk:
+http://sourceforge.net/p/docutils/bugs/214/
+
+Directives
+----------
+
+.. code:: rst
+
+    .. code-include:: incfile.py
+        :lexer: string, name of the Pygments lexer to use, default 'text'
+        :encoding: string, encoding with which to open the file
+        :tab-width: integer, hard tabs are replaced with `tab-width` spaces
+        :start-line: integer, starting line to begin reading include file
+        :end-line: integer, last line from include file to display
+
+``start-line``, and ``end-line`` have the same meaning as in the
+docutils ``include`` directive, that is, they index from zero.
+
+Example
+-------
+
+./incfile.py:
+
+.. code:: python
+
+    # These two comment lines will not
+    # be included in the output
+    import random
+
+    insults = ['I fart in your general direction',
+               'your mother was a hampster',
+               'your father smelt of elderberries']
+
+    def insult():
+        print random.choice(insults)
+    # This comment line will be included
+    # ...but this one won't
+
+./yourfile.rst:
+
+.. code:: rst
+
+    How to Insult the English
+    =========================
+
+    :author: Pierre Devereaux
+
+    A function to help insult those silly English knnnnnnniggets:
+
+    .. code-include:: incfile.py
+        :lexer: python
+        :encoding: utf-8
+        :tab-width: 4
+        :start-line: 3
+        :end-line: 11
+
diff --git a/pelican-plugins/code_include/__init__.py b/pelican-plugins/code_include/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..6eca8be603322a9274952d5b19a32a54b50b9efe
--- /dev/null
+++ b/pelican-plugins/code_include/__init__.py
@@ -0,0 +1 @@
+from .code_include import *
diff --git a/pelican-plugins/code_include/code_include.py b/pelican-plugins/code_include/code_include.py
new file mode 100644
index 0000000000000000000000000000000000000000..b87c8dd84ea4b1faec8b83494c41696da0a379fd
--- /dev/null
+++ b/pelican-plugins/code_include/code_include.py
@@ -0,0 +1,87 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+import os.path
+
+from docutils import io, nodes, statemachine, utils
+from docutils.utils.error_reporting import SafeString, ErrorString
+from docutils.parsers.rst import directives, Directive
+
+from pelican.rstdirectives import Pygments
+
+
+class CodeInclude(Directive):
+    required_arguments = 1
+    optional_arguments = 0
+    final_argument_whitespace = True
+    option_spec = {'lexer': directives.unchanged,
+                   'encoding': directives.encoding,
+                   'tab-width': int,
+                   'start-line': int,
+                   'end-line': int}
+
+    def run(self):
+        """Include a file as part of the content of this reST file."""
+        if not self.state.document.settings.file_insertion_enabled:
+            raise self.warning('"%s" directive disabled.' % self.name)
+        source = self.state_machine.input_lines.source(
+            self.lineno - self.state_machine.input_offset - 1)
+        source_dir = os.path.dirname(os.path.abspath(source))
+
+        path = directives.path(self.arguments[0])
+        path = os.path.normpath(os.path.join(source_dir, path))
+        path = utils.relative_path(None, path)
+        path = nodes.reprunicode(path)
+
+        encoding = self.options.get(
+            'encoding', self.state.document.settings.input_encoding)
+        e_handler = self.state.document.settings.input_encoding_error_handler
+        tab_width = self.options.get(
+            'tab-width', self.state.document.settings.tab_width)
+
+        try:
+            self.state.document.settings.record_dependencies.add(path)
+            include_file = io.FileInput(source_path=path,
+                                        encoding=encoding,
+                                        error_handler=e_handler)
+        except UnicodeEncodeError as error:
+            raise self.severe('Problems with "%s" directive path:\n'
+                              'Cannot encode input file path "%s" '
+                              '(wrong locale?).' %
+                              (self.name, SafeString(path)))
+        except IOError as error:
+            raise self.severe('Problems with "%s" directive path:\n%s.' %
+                              (self.name, ErrorString(error)))
+        startline = self.options.get('start-line', None)
+        endline = self.options.get('end-line', None)
+        try:
+            if startline or (endline is not None):
+                lines = include_file.readlines()
+                rawtext = ''.join(lines[startline:endline])
+            else:
+                rawtext = include_file.read()
+        except UnicodeError as error:
+            raise self.severe('Problem with "%s" directive:\n%s' %
+                              (self.name, ErrorString(error)))
+
+        include_lines = statemachine.string2lines(rawtext, tab_width,
+                                                  convert_whitespace=True)
+
+        # default lexer to 'text'
+        lexer = self.options.get('lexer', 'text')
+
+        self.options['source'] = path
+        codeblock = Pygments(self.name,
+                             [lexer],  # arguments
+                             {},  # no options for this directive
+                             include_lines,  # content
+                             self.lineno,
+                             self.content_offset,
+                             self.block_text,
+                             self.state,
+                             self.state_machine)
+        return codeblock.run()
+
+
+def register():
+    directives.register_directive('code-include', CodeInclude)
diff --git a/pelican-plugins/collate_content/.gitignore b/pelican-plugins/collate_content/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..52e0e81a9e4d8813f7eb2d82c89d0df67ef72254
--- /dev/null
+++ b/pelican-plugins/collate_content/.gitignore
@@ -0,0 +1,3 @@
+
+# Python files
+*.pyc
diff --git a/pelican-plugins/collate_content/LICENSE b/pelican-plugins/collate_content/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..dba13ed2ddf783ee8118c6a581dbf75305f816a3
--- /dev/null
+++ b/pelican-plugins/collate_content/LICENSE
@@ -0,0 +1,661 @@
+                    GNU AFFERO GENERAL PUBLIC LICENSE
+                       Version 3, 19 November 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU Affero General Public License is a free, copyleft license for
+software and other kinds of works, specifically designed to ensure
+cooperation with the community in the case of network server software.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+our General Public Licenses are intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  Developers that use our General Public Licenses protect your rights
+with two steps: (1) assert copyright on the software, and (2) offer
+you this License which gives you legal permission to copy, distribute
+and/or modify the software.
+
+  A secondary benefit of defending all users' freedom is that
+improvements made in alternate versions of the program, if they
+receive widespread use, become available for other developers to
+incorporate.  Many developers of free software are heartened and
+encouraged by the resulting cooperation.  However, in the case of
+software used on network servers, this result may fail to come about.
+The GNU General Public License permits making a modified version and
+letting the public access it on a server without ever releasing its
+source code to the public.
+
+  The GNU Affero General Public License is designed specifically to
+ensure that, in such cases, the modified source code becomes available
+to the community.  It requires the operator of a network server to
+provide the source code of the modified version running there to the
+users of that server.  Therefore, public use of a modified version, on
+a publicly accessible server, gives the public access to the source
+code of the modified version.
+
+  An older license, called the Affero General Public License and
+published by Affero, was designed to accomplish similar goals.  This is
+a different license, not a version of the Affero GPL, but Affero has
+released a new version of the Affero GPL which permits relicensing under
+this license.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU Affero General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Remote Network Interaction; Use with the GNU General Public License.
+
+  Notwithstanding any other provision of this License, if you modify the
+Program, your modified version must prominently offer all users
+interacting with it remotely through a computer network (if your version
+supports such interaction) an opportunity to receive the Corresponding
+Source of your version by providing access to the Corresponding Source
+from a network server at no charge, through some standard or customary
+means of facilitating copying of software.  This Corresponding Source
+shall include the Corresponding Source for any work covered by version 3
+of the GNU General Public License that is incorporated pursuant to the
+following paragraph.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the work with which it is combined will remain governed by version
+3 of the GNU General Public License.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU Affero General Public License from time to time.  Such new versions
+will be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU Affero General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU Affero General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU Affero General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If your software can interact with users remotely through a computer
+network, you should also make sure that it provides a way for users to
+get its source.  For example, if your program is a web application, its
+interface could display a "Source" link that leads users to an archive
+of the code.  There are many ways you could offer source, and different
+solutions will be better for different programs; see section 13 for the
+specific requirements.
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU AGPL, see
+<http://www.gnu.org/licenses/>.
diff --git a/pelican-plugins/collate_content/README.md b/pelican-plugins/collate_content/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..7e173c2a2e79f44b8ebd8eb491d0214aad72d7ea
--- /dev/null
+++ b/pelican-plugins/collate_content/README.md
@@ -0,0 +1,57 @@
+# Collate Content
+*Author: Edward J. Stronge <ejstronge@gmail.com>*
+
+Makes categories of content available to the template as lists through a
+`collations` attribute. I primarily developed this to make it easier
+to work with `Page` objects that contain category metadata.
+
+## Installation
+
+If you downloaded this module as part of the pelican-plugins repository, 
+add it to your Pelican configuration as follows:
+
+    PLUGIN_PATH = '/path/to/pelican-plugins'
+    PLUGINS = ['pelican_collate_content', ]
+    
+Otherwise, you can import it into Python as a normal module if you place
+this repository in your `$PYTHONPATH`.
+
+## Usage
+
+To display all content in the category `Software Development`, 
+you could use the following block in a template. In the template, the category
+will appear in lowercase with internal whitespace and '-' characters
+converted to underscores:
+
+    ```html
+    <h1>Development blog</h1>
+    <ul> 
+    {% for a in collations.software_development_articles %}
+        <li>a.date - a.title</li> 
+    {% endfor %}
+    </ul>
+    
+    <h1> Open source projects </h1>
+    I'm an active contributor to the following projects:
+    
+    <ul>
+    {% for p in software_development_pages %}
+        <li> p.title - p.summary</li> 
+    {% endfor %}
+    </ul>
+    ```
+
+### Articles or pages with multiple categories
+
+Multiple categories are supported out-of-the box. Categories may not
+contain commas but otherwise work with no problem.
+
+## Settings
+
+To limit which categories and subcategories are collated, set the
+`CATEGORIES_TO_COLLATE` option in your Pelican configuration file.
+
+If this option is present and is a list, only categories present
+in `CATEGORIES_TO_COLLATE` will be collated:
+
+    CATEGORIES_TO_COLLATE = ['category-of-interest', 'another-cool-category']
diff --git a/pelican-plugins/collate_content/__init__.py b/pelican-plugins/collate_content/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..fcbd7a84c957c8f86e4218f4c3a3d36ba154d737
--- /dev/null
+++ b/pelican-plugins/collate_content/__init__.py
@@ -0,0 +1,10 @@
+"""
+__init__.py
+===========
+
+Edward J. Stronge
+(c) 2014
+
+Imports collate_content to facilitate Pelican's plugin loading process.
+"""
+from .collate_content import *
diff --git a/pelican-plugins/collate_content/collate_content.py b/pelican-plugins/collate_content/collate_content.py
new file mode 100644
index 0000000000000000000000000000000000000000..ed1d271dc9975498fad93f11141a46d75777de73
--- /dev/null
+++ b/pelican-plugins/collate_content/collate_content.py
@@ -0,0 +1,74 @@
+"""
+collate_content.py
+==================
+(c) 2014 - Edward Stronge
+
+Connects to the content generator finalized signal to combine
+articles and pages sharing a category into lists that will be
+available in the template context.
+
+Thanks to #pelican member @kura for suggestions on creating this
+plugin.
+"""
+from collections import defaultdict
+import functools
+import re
+
+from pelican import signals
+
+
+def group_content(generator, content_type):
+    """
+    Assembles articles and pages into lists based on each
+    article or page's content. These lists are available
+    through the global context passed to the template engine.
+
+    When multiple categories are present, splits category names
+    based on commas and trims whitespace surrounding a
+    category's name. Thus, commas may not appear within a category
+    but they can be used to delimit categories and may be surrounded by
+    arbitrary amounts of whitespace.
+
+    For each category, substitutes '_' for all whitespace and '-'
+    characters, then creates a list named `SUBSTITUTED_CATEGORY_NAME`_articles
+    or `SUBSTITUTED_CATEGORY_NAME`_pages for Articles or Pages,
+    respectively.
+
+    Note that the *original* category name must appear in the
+    `CATEGORIES_TO_COLLATE` when using this plugin with category
+    filtering enabled.
+    """
+    category_filter = generator.settings.get('CATEGORIES_TO_COLLATE', None)
+    filtering_active = type(category_filter) in (list, tuple, set)
+
+    collations = generator.context.get('collations', defaultdict(list))
+    for content in generator.context[content_type]:
+        category_list = [c.strip() for c in content.category.name.split(',')]
+        for category in category_list:
+            if filtering_active and category not in category_filter:
+                continue
+            category = substitute_category_name(category)
+            collations['%s_%s' % (category, content_type)].append(content)
+    generator.context['collations'] = collations
+
+
+def substitute_category_name(category_name):
+    """
+    Replaces whitespace and '-' characters in `category_name`
+    to allow category_name to be made into a valid Python
+    identifier.
+
+    Doesn't check all possible ways a string might be invalid;
+    the user of the collate_content module is advised to use
+    categories with Python-friendly names.
+    """
+    return re.sub(r'\s', '_', category_name).replace('-', '_').lower()
+
+ARTICLE_GROUPER = functools.partial(group_content, content_type='articles')
+PAGE_GROUPER = functools.partial(group_content, content_type='pages')
+
+
+def register():
+    """Register the new plugin"""
+    signals.article_generator_finalized.connect(ARTICLE_GROUPER)
+    signals.page_generator_finalized.connect(PAGE_GROUPER)
diff --git a/pelican-plugins/collate_content/test_collate_content.py b/pelican-plugins/collate_content/test_collate_content.py
new file mode 100644
index 0000000000000000000000000000000000000000..176827bfd876cbc0e5600c7bc6a488fd3c686816
--- /dev/null
+++ b/pelican-plugins/collate_content/test_collate_content.py
@@ -0,0 +1,252 @@
+"""
+test_collate_content.py
+=======================
+
+(c) 2014 - Edward J. Stronge
+
+Tests for the collate_content module
+"""
+from collections import defaultdict, namedtuple
+import os
+import random
+import tempfile
+import shutil
+import string
+import unittest
+
+from pelican import Pelican
+from pelican import ArticlesGenerator, PagesGenerator
+from pelican.settings import read_settings
+
+import collate_content as cc
+
+TEMP_PAGE_TEMPLATE = """Title: {title}
+Category: {category}
+"""
+
+Content = namedtuple('Content', ['title', 'path', 'category'])
+# Characters likely to appear in blog titles/categories. Could eventually
+# extend support to more characters that can't appear in a Python identifier
+BLOG_CHARACTERS = string.ascii_letters + ' -:'
+
+
+def make_content(directory, categories, count=5, categories_per_content=1):
+    """
+    make_content --> {(processed_category, original_category): articles, ...}
+
+    Writes random titles and categories into `count` temporary
+    files in `directory`. If desired, specify `categories_per_content`
+    to assign multiple categories to each written file.
+
+    Returns a dictionary whose keys are in `categories` with values
+    that are (title, path, category) tuples for the generated
+    content files.
+    """
+    new_content = defaultdict(list)
+    for _ in range(count):
+        title = get_random_text_and_whitespace()
+        category_choice = random.sample(categories, categories_per_content)
+        categories_string = ', '.join(category_choice)
+        output = TEMP_PAGE_TEMPLATE.format(
+            title=title, category=categories_string)
+        with tempfile.NamedTemporaryFile(
+                dir=directory, mode='w', suffix='.md', delete=False) as tmp:
+            tmp.write(output)
+            path = os.path.join(directory, tmp.name)
+
+        for each_cat in category_choice:
+            new_content[(cc.substitute_category_name(each_cat), each_cat)]\
+                .append(Content(title, path, categories_string))
+    return new_content
+
+
+def get_random_text_and_whitespace(length=10):
+    """
+    Returns at most `length` randomly-generated letters and/or
+    whitespace. The returned string will not begin or end in whitespace.
+    """
+    return "".join(random.sample(BLOG_CHARACTERS, length)).strip()
+
+
+def modified_pelican_run(self):
+    """Runs the generators and returns the context object
+
+    Modified from the Pelican object's run methods.
+    """
+
+    context = self.settings.copy()
+    context['filenames'] = {}  # share the dict between all the generators
+    context['localsiteurl'] = self.settings['SITEURL']  # share
+    context['generated_content'] = dict()
+    context['static_links'] = set()
+    generators = [
+        cls(
+            context=context,
+            settings=self.settings,
+            path=self.path,
+            theme=self.theme,
+            output_path=self.output_path,
+        ) for cls in self.get_generator_classes()
+    ]
+
+    for p in generators:
+        if hasattr(p, 'generate_context'):
+            p.generate_context()
+
+    writer = self.get_writer()
+
+    for p in generators:
+        if hasattr(p, 'generate_output'):
+            p.generate_output(writer)
+
+    next(g for g in generators if isinstance(g, ArticlesGenerator))
+    next(g for g in generators if isinstance(g, PagesGenerator))
+    return context
+
+
+class ContentCollationTester(unittest.TestCase):
+    """Test generation of lists of content based on their Category metadata"""
+
+    def setUp(self, settings_overrides=None, count=5,
+              categories_per_content=1, categories=None):
+        self.temp_input_dir = tempfile.mkdtemp(prefix="cc-input-")
+        page_directory = os.path.join(self.temp_input_dir, 'pages')
+        os.mkdir(page_directory)
+        self.temp_output_dir = tempfile.mkdtemp(prefix="cc-output-")
+
+        if categories is None:
+            categories = [get_random_text_and_whitespace() for _ in range(5)]
+
+        self.articles = make_content(
+            self.temp_input_dir, categories, count=count,
+            categories_per_content=categories_per_content)
+        self.pages = make_content(
+            page_directory, categories, count=count,
+            categories_per_content=categories_per_content)
+        settings = {
+            'PATH': self.temp_input_dir,
+            'PAGE_DIR': 'pages',
+            'OUTPUT_PATH': self.temp_output_dir,
+            'PLUGINS': [cc],
+            'DEFAULT_DATE': (2014, 6, 8),
+            }
+        if settings_overrides is not None:
+            settings.update(settings_overrides)
+        settings = read_settings(override=settings)
+        pelican = Pelican(settings=settings)
+        pelican.modified_run = modified_pelican_run
+        self.collations = pelican.modified_run(pelican)['collations']
+
+    def tearDown(self):
+        shutil.rmtree(self.temp_input_dir)
+        shutil.rmtree(self.temp_output_dir)
+
+
+class TestCollation(ContentCollationTester):
+    """Test generation of lists of content based on their Category metadata"""
+
+    def test_articles_with_one_category(self):
+
+        for substituted_category, original_category in self.articles.keys():
+            collation_key = '%s_articles' % substituted_category
+            self.assertIn(collation_key, self.collations)
+
+            collated_titles = [a.title for a in self.collations[collation_key]]
+
+            for title, _, _ in self.articles[
+                    (substituted_category, original_category)]:
+                self.assertIn(title, collated_titles)
+
+    def test_pages_with_one_category(self):
+
+        for substituted_category, original_category in self.pages.keys():
+            collation_key = '%s_pages' % substituted_category
+            self.assertIn(collation_key, self.collations)
+
+            collated_titles = [a.title for a in self.collations[collation_key]]
+
+            for title, _, _ in self.pages[
+                    (substituted_category, original_category)]:
+                self.assertIn(title, collated_titles)
+
+
+class TestCollationAndMultipleCategories(TestCollation):
+    """
+    Test collate_content with multiple categories specified in each
+    article and each page.
+    """
+    def setUp(self):
+        categories = [get_random_text_and_whitespace() for _ in range(5)]
+
+        ContentCollationTester.setUp(
+            self, categories=categories, categories_per_content=3)
+
+
+class TestFilteredCategories(ContentCollationTester):
+    """
+    Test collate_content with the `CATEGORIES_TO_COLLATE` setting
+    in effect
+    """
+
+    def setUp(self):
+        categories = [get_random_text_and_whitespace() for _ in range(5)]
+        self.retained_categories = categories[:2]
+        override = {'CATEGORIES_TO_COLLATE': self.retained_categories}
+
+        ContentCollationTester.setUp(
+            self, settings_overrides=override, categories=categories)
+
+    def test_articles_with_one_category_after_filtering(self):
+
+        for substituted_category, original_category in self.articles.keys():
+            collation_key = '%s_articles' % substituted_category
+
+            if original_category not in self.retained_categories:
+                self.assertNotIn(collation_key, self.collations)
+                continue
+
+            self.assertIn(collation_key, self.collations)
+
+            collated_titles = [a.title for a in self.collations[collation_key]]
+
+            for title, _, _ in self.articles[
+                    (substituted_category, original_category)]:
+                self.assertIn(title, collated_titles)
+
+    def test_pages_with_one_category_after_filtering(self):
+
+        for substituted_category, original_category in self.pages.keys():
+            collation_key = '%s_pages' % substituted_category
+
+            if original_category not in self.retained_categories:
+                self.assertNotIn(collation_key, self.collations)
+                continue
+
+            self.assertIn(collation_key, self.collations)
+
+            collated_titles = [a.title for a in self.collations[collation_key]]
+
+            for title, _, _ in self.pages[
+                    (substituted_category, original_category)]:
+                self.assertIn(title, collated_titles)
+
+
+class TestFilteredAndMultipleCategories(TestFilteredCategories):
+    """
+    Test collate_content with the `CATEGORIES_TO_COLLATE` setting
+    in effect as well as with multiple categories specified in each
+    article and each page.
+    """
+    def setUp(self):
+        categories = [get_random_text_and_whitespace() for _ in range(5)]
+        self.retained_categories = categories[:2]
+        override = {'CATEGORIES_TO_COLLATE': self.retained_categories}
+
+        ContentCollationTester.setUp(
+            self, settings_overrides=override, categories=categories,
+            categories_per_content=3)
+
+
+if __name__ == '__main__':
+    suite = unittest.TestLoader().loadTestsFromNames(['test_collate_content'])
+    unittest.TextTestRunner(verbosity=1).run(suite)
diff --git a/pelican-plugins/creole_reader/Readme.md b/pelican-plugins/creole_reader/Readme.md
new file mode 100644
index 0000000000000000000000000000000000000000..d72bc07e85da34598df8be9fdc8a8a73db9af299
--- /dev/null
+++ b/pelican-plugins/creole_reader/Readme.md
@@ -0,0 +1,41 @@
+# Creole Reader
+
+This plugins allows you to write your posts using the wikicreole syntax. Give to
+these files the creole extension. The metadata are between `<<header>> <</header>>`
+tags.
+
+## Dependency
+This plugin relies on [python-creole](https://pypi.python.org/pypi/python-creole/) to work. Install it with:
+`pip install python-creole`
+
+## Syntax
+Use ** for strong, // for emphasis, one = for 1st level titles. Please use the
+following macro for code highlighting:
+`<<code ext=".file_extension">> <</code>>`
+
+For the complete syntax, look at: http://www.wikicreole.org/
+
+## Basic example
+```
+<<header>>
+title: Créole
+tags: creole, python, pelican_open
+date: 2013-12-12
+<</header>>
+
+= Title 1
+== Title 2
+
+Some nice text with **strong** and //emphasis//.
+
+* A nice list
+** With sub-elements
+* Python
+
+<<code ext=".py">>
+print("Hello World")
+<</code>>
+
+# An ordered list
+# A second item
+```
diff --git a/pelican-plugins/creole_reader/__init__.py b/pelican-plugins/creole_reader/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..2e26d4625e4aca5868e1531877314a5283bafed7
--- /dev/null
+++ b/pelican-plugins/creole_reader/__init__.py
@@ -0,0 +1 @@
+from .creole_reader import *
diff --git a/pelican-plugins/creole_reader/creole_reader.py b/pelican-plugins/creole_reader/creole_reader.py
new file mode 100644
index 0000000000000000000000000000000000000000..c95651b0726cfd988b8335ae0caa36b7be054dfd
--- /dev/null
+++ b/pelican-plugins/creole_reader/creole_reader.py
@@ -0,0 +1,101 @@
+#-*- conding: utf-8 -*-
+
+'''
+Creole Reader
+-------------
+
+This plugins allows you to write your posts using the wikicreole syntax. Give to
+these files the creole extension.
+For the syntax, look at: http://www.wikicreole.org/
+'''
+
+from pelican import readers
+from pelican import signals
+from pelican import settings
+
+from pelican.utils import pelican_open
+
+try:
+    from creole import creole2html
+    creole = True
+except ImportError:
+    creole = False
+
+try:
+    from pygments import lexers
+    from pygments.formatters import HtmlFormatter
+    from pygments import highlight
+    PYGMENTS = True
+except:
+    PYGMENTS = False
+
+class CreoleReader(readers.BaseReader):
+    enabled = creole
+
+    file_extensions = ['creole']
+
+    def __init__(self, settings):
+        super(CreoleReader, self).__init__(settings)
+
+    def _parse_header_macro(self, text):
+        for line in text.split('\n'):
+            name, value = line.split(':')
+            name, value = name.strip(), value.strip()
+            if name == 'title':
+                self._metadata[name] = value
+            else:
+                self._metadata[name] = self.process_metadata(name, value)
+        return u''
+
+    def _no_highlight(self, text):
+        html = u'\n<pre><code>{}</code></pre>\n'.format(text)
+        return html
+
+    def _get_lexer(self, source_type, code):
+        try:
+            return lexers.get_lexer_by_name(source_type)
+        except:
+            return lexers.guess_lexer(code)
+
+    def _get_formatter(self):
+        formatter = HtmlFormatter(lineos = True, encoding='utf-8',
+                                  style='colorful', outencoding='utf-8',
+                                  cssclass='pygments')
+        return formatter
+
+    def _parse_code_macro(self, ext, text):
+        if not PYGMENTS:
+            return self._no_highlight(text)
+
+        try:
+            source_type = ''
+            if '.' in ext:
+                source_type = ext.strip().split('.')[1]
+            else:
+                source_type = ext.strip()
+        except IndexError:
+            source_type = ''
+        lexer = self._get_lexer(source_type, text)
+        formatter = self._get_formatter()
+
+        try:
+            return highlight(text, lexer, formatter).decode('utf-8')
+        except:
+            return self._no_highlight(text)
+
+    # You need to have a read method, which takes a filename and returns
+    # some content and the associated metadata.
+    def read(self, source_path):
+        """Parse content and metadata of creole files"""
+
+        self._metadata = {}
+        with pelican_open(source_path) as text:
+            content = creole2html(text, macros={'header': self._parse_header_macro,
+                                            'code': self._parse_code_macro})
+        return content, self._metadata
+
+def add_reader(readers):
+    readers.reader_classes['creole'] = CreoleReader
+
+def register():
+    signals.readers_init.connect(add_reader)
diff --git a/pelican-plugins/css-html-js-minify/__init__.py b/pelican-plugins/css-html-js-minify/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..7922403a0896a7966846688eb6e5c03be3ea6dc1
--- /dev/null
+++ b/pelican-plugins/css-html-js-minify/__init__.py
@@ -0,0 +1,2 @@
+# -*- coding: utf-8 -*-
+from .css_html_js_minify import *
diff --git a/pelican-plugins/css-html-js-minify/css_html_js_minify.py b/pelican-plugins/css-html-js-minify/css_html_js_minify.py
new file mode 100644
index 0000000000000000000000000000000000000000..d3019f823f5a13d15e70a69de02d475cec78b61e
--- /dev/null
+++ b/pelican-plugins/css-html-js-minify/css_html_js_minify.py
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+"""
+css-html-js-minify wrapper for Pelican
+"""
+
+import glob
+import os
+import sys
+
+from css_html_js_minify import (
+    process_single_css_file,
+    process_single_html_file,
+    process_single_js_file,
+)
+from pelican import signals
+
+def main(pelican):
+    for f in glob.iglob(pelican.output_path + '/**/*.htm*', recursive=True):
+        process_single_html_file(f, overwrite=True)
+    for f in glob.iglob(pelican.output_path + '/**/*.css', recursive=True):
+        process_single_css_file(f, overwrite=True)
+    for f in glob.iglob(pelican.output_path + '/**/*.js', recursive=True):
+        process_single_js_file(f, overwrite=True)
+
+def register():
+    signals.finalized.connect(main)
diff --git a/pelican-plugins/css-html-js-minify/readme.rst b/pelican-plugins/css-html-js-minify/readme.rst
new file mode 100644
index 0000000000000000000000000000000000000000..2ec9d1d9c671478904cf9397045c3cf60011444c
--- /dev/null
+++ b/pelican-plugins/css-html-js-minify/readme.rst
@@ -0,0 +1,9 @@
+css-html-js-minify wrapper for Pelican
+======================================
+
+This plugin simply finds all css, html and javascript files in the pelican
+output folder and minifies them in place using `css-html-js-minify`_.
+
+`css-html-js-minify`_ needs to be installed separately and importable.
+
+.. _`css-html-js-minify`: https://github.com/juancarlospaco/css-html-js-minify
diff --git a/pelican-plugins/ctags_generator/README.md b/pelican-plugins/ctags_generator/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..22b219663d2a0fdbbb19495383989bba0c3ed9fe
--- /dev/null
+++ b/pelican-plugins/ctags_generator/README.md
@@ -0,0 +1,21 @@
+# Summary
+
+This plugin generates a `tags` file following the [CTags format](http://ctags.sourceforge.net/FORMAT) in the `content/` directory,
+to provide autocompletion for code editors that support it.
+
+
+## Installation
+
+To enable, add the following to your settings.py:
+
+    PLUGIN_PATH = 'path/to/pelican-plugins'
+    PLUGINS = ['ctags_generator']
+
+`PLUGIN_PATH` can be a path relative to your settings file or an absolute path.
+
+
+## Tests
+
+To execute them:
+
+    nosetests -w ctags_generator
diff --git a/pelican-plugins/ctags_generator/__init__.py b/pelican-plugins/ctags_generator/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..51b563381789aed8f3419bcf8d8975c6f80e18b8
--- /dev/null
+++ b/pelican-plugins/ctags_generator/__init__.py
@@ -0,0 +1 @@
+from .ctags_generator import *
diff --git a/pelican-plugins/ctags_generator/ctags_generator.py b/pelican-plugins/ctags_generator/ctags_generator.py
new file mode 100644
index 0000000000000000000000000000000000000000..b5ced4895298b59bdb0f53efac8ca59c88232e20
--- /dev/null
+++ b/pelican-plugins/ctags_generator/ctags_generator.py
@@ -0,0 +1,29 @@
+# -*- coding: utf-8 -*-
+
+import os
+
+from pelican import signals
+
+
+CTAGS_TEMPLATE = '''{% for tag, articles in tags_articles %}
+{% for article in articles %}
+{{tag}}\t{{article}}\t0;"\ttag
+{% endfor %}
+{% endfor %}
+'''
+
+
+def generate_ctags(article_generator, writer):
+    tags_file_path = os.path.join(article_generator.path, 'tags')
+    if article_generator.settings.get('WRITE_SELECTED'):
+        article_generator.settings['WRITE_SELECTED'].append(tags_file_path)
+    writer.output_path = article_generator.path
+    try:
+        writer.write_file('tags', article_generator.env.from_string(CTAGS_TEMPLATE), article_generator.context,
+                          tags_articles=sorted(article_generator.tags.items()))
+    finally:
+        writer.output_path = article_generator.output_path
+
+
+def register():
+    signals.article_writer_finalized.connect(generate_ctags)
diff --git a/pelican-plugins/ctags_generator/test_content/article_with_duplicate_tags_authors.md b/pelican-plugins/ctags_generator/test_content/article_with_duplicate_tags_authors.md
new file mode 100644
index 0000000000000000000000000000000000000000..7ab046f9170fb79b29e8839f605d360e7dce5eef
--- /dev/null
+++ b/pelican-plugins/ctags_generator/test_content/article_with_duplicate_tags_authors.md
@@ -0,0 +1,15 @@
+Title: Test metadata duplicates
+Category: test
+Tags: foo, bar, foobar, foo, bar
+Authors: Author, First; Author, Second; Author, First
+Date: 2010-12-02 10:14
+Modified: 2010-12-02 10:20
+Summary: I have a lot to test
+
+Test Markdown File Header
+=========================
+
+Used for pelican test
+---------------------
+
+The quick brown fox jumped over the lazy dog's back.
diff --git a/pelican-plugins/ctags_generator/test_content/article_with_markdown_and_nonascii_summary.md b/pelican-plugins/ctags_generator/test_content/article_with_markdown_and_nonascii_summary.md
new file mode 100644
index 0000000000000000000000000000000000000000..d76ed4a12c4e3e1633ae29e777715afcdae262be
--- /dev/null
+++ b/pelican-plugins/ctags_generator/test_content/article_with_markdown_and_nonascii_summary.md
@@ -0,0 +1,19 @@
+Title: マックOS X 10.8でパイソンとVirtualenvをインストールと設定
+Slug: python-virtualenv-on-mac-osx-mountain-lion-10.8
+Date: 2012-12-20
+Modified: 2012-12-22
+Tags: パイソン, マック
+Category: 指導書
+Summary: パイソンとVirtualenvをまっくでインストールする方法について明確に説明します。
+
+Writing unicode is certainly fun.
+
+パイソンとVirtualenvをまっくでインストールする方法について明確に説明します。
+
+And let's mix languages.
+
+первый пост
+
+Now another.
+
+İlk yazı çok özel değil.
diff --git a/pelican-plugins/ctags_generator/test_content/article_with_md_extension.md b/pelican-plugins/ctags_generator/test_content/article_with_md_extension.md
new file mode 100644
index 0000000000000000000000000000000000000000..89b6980c3206e8cc737fdb8edc35fce59d48a76b
--- /dev/null
+++ b/pelican-plugins/ctags_generator/test_content/article_with_md_extension.md
@@ -0,0 +1,14 @@
+Title: Test md File
+Category: test
+Tags: foo, bar, foobar
+Date: 2010-12-02 10:14
+Modified: 2010-12-02 10:20
+Summary: I have a lot to test
+
+Test Markdown File Header
+=========================
+
+Used for pelican test
+---------------------
+
+The quick brown fox jumped over the lazy dog's back.
diff --git a/pelican-plugins/ctags_generator/test_ctags_generator.py b/pelican-plugins/ctags_generator/test_ctags_generator.py
new file mode 100644
index 0000000000000000000000000000000000000000..53a23c8f4b7b8aea7a12aefbf04f21c2efcbc97b
--- /dev/null
+++ b/pelican-plugins/ctags_generator/test_ctags_generator.py
@@ -0,0 +1,41 @@
+# -*- coding: utf-8 -*-
+import os, shutil
+
+from pelican.generators import ArticlesGenerator
+from pelican.tests.support import get_settings, unittest
+from pelican.writers import Writer
+
+from ctags_generator import generate_ctags
+
+
+CUR_DIR = os.path.dirname(__file__)
+TEST_CONTENT_DIR = os.path.join(CUR_DIR, 'test_content')
+
+
+class CtagsGeneratorTest(unittest.TestCase):
+
+    def test_generate_ctags(self):
+        settings = get_settings(filenames={})
+        settings['GENERATE_CTAGS'] = True
+
+        context = settings.copy()
+        context['generated_content'] = dict()
+        context['static_links'] = set()
+        generator = ArticlesGenerator(
+            context=context, settings=settings,
+            path=TEST_CONTENT_DIR, theme=settings['THEME'], output_path=TEST_CONTENT_DIR)
+        generator.generate_context()
+
+        writer = Writer(TEST_CONTENT_DIR, settings=settings)
+        generate_ctags(generator, writer)
+
+        output_path = os.path.join(TEST_CONTENT_DIR, 'tags')
+        self.assertTrue(os.path.exists(output_path))
+
+        try:
+            # output content is correct
+            with open(output_path, 'r') as output_file:
+                ctags = [l.split('\t')[0] for l in output_file.readlines()]
+                self.assertEqual(['bar', 'bar', 'foo', 'foo', 'foobar', 'foobar', 'マック', 'パイソン'], ctags)
+        finally:
+            os.remove(output_path)
diff --git a/pelican-plugins/custom_article_urls/README.md b/pelican-plugins/custom_article_urls/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..89e045970206cd93b46c4cceff087fb357aea97f
--- /dev/null
+++ b/pelican-plugins/custom_article_urls/README.md
@@ -0,0 +1,36 @@
+#Custom Article URLs#
+
+This plugin adds support for defining different default URLs for different
+categories, or different subcategories if using the subcategory plugin.
+
+##Usage##
+
+After adding `custom_article_urls` to your `PLUGINS`, add a
+`CUSTOM_ARTICLE_URLS` setting, which is a dictionary of rules. The rules are
+also a dictionary, consisting of the `URL` and the `SAVE_AS` values.
+
+For example, if you had two categories, *Category 1* and *Category 2*, and you
+would like *Category 1* saved as `category-1/article-slug/` and *Category 2*
+saved as `/year/month/article-slug/`, you would add:
+
+    CUSTOM_ARTICLE_URLS = {
+        'Category 1': {'URL': '{category}/{slug}/',
+            'SAVE_AS': '{category}/{slug}/index.html}',
+        'Category 2': {'URL': '{date:%Y}/{date:%B}/{slug}/',
+            'SAVE_AS': '{date:%Y}/{date:%B}/{slug}/index.html}',
+        }
+
+If you had any other categories, they would use the default `ARTICLE_SAVE_AS`
+and `ARTICLE_URL` settings.
+
+If you are using the subcategory plugin, you can define them the same way.
+For example, if *Category 1* had a subcategory called *Sub Category*, you could
+define its rules with::
+
+    'Category 1/Sub Category`: ...
+
+##Other Usage: Article Metadata##
+
+If you define `URL` and `Save_as` in your article metadata, then this plugin
+will not alter that value. So you can still specify special one-off URLs as
+you normally would.
diff --git a/pelican-plugins/custom_article_urls/__init__.py b/pelican-plugins/custom_article_urls/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..ed876abc6229cddbbbd93536fe283c2c68cf068b
--- /dev/null
+++ b/pelican-plugins/custom_article_urls/__init__.py
@@ -0,0 +1 @@
+from .custom_article_urls import *
diff --git a/pelican-plugins/custom_article_urls/custom_article_urls.py b/pelican-plugins/custom_article_urls/custom_article_urls.py
new file mode 100644
index 0000000000000000000000000000000000000000..a59c3873ccd63cebe3ed206dd0e1168c8ebb3901
--- /dev/null
+++ b/pelican-plugins/custom_article_urls/custom_article_urls.py
@@ -0,0 +1,49 @@
+# -*- coding: utf-8 -*-
+"""
+@Author: Alistair Magee
+
+Adds ability to specify custom urls for different categories 
+(or subcategories if using subcategory plugin) of article
+using a dictionary stored in pelican settings file as
+{category: {article_url_structure: stirng, article_save_as: string}}
+"""
+from pelican import signals
+from pelican.contents import Article, Category
+from six import text_type
+
+def custom_url(generator, metadata):
+    if 'CUSTOM_ARTICLE_URLS' in generator.settings:
+        custom_urls = generator.settings['CUSTOM_ARTICLE_URLS']
+        category = text_type(metadata['category'])
+        pattern_matched = {}
+        
+        if category in custom_urls:
+            pattern_matched = custom_urls[category]
+
+        if 'subcategories' in metadata: #using subcategory plugin
+            for subcategory in metadata['subcategories']:
+                if subcategory in custom_urls:
+                    pattern_matched = custom_urls[subcategory]
+
+        if pattern_matched:
+            #only alter url if hasn't been set in the metdata
+            ignore = False
+            if ('url', 'save_as') in metadata:
+                """ if both url and save_as are set in the metadata already
+                then there is already a custom url set, skip this one
+                """
+                ignore = True
+            elif ('status' in metadata) and (metadata['status'] == 'draft'):
+                ignore = True
+
+            if not ignore:
+                temp_article = Article("", metadata=metadata)
+                url_format = pattern_matched['URL']
+                save_as_format = pattern_matched['SAVE_AS']
+                url = url_format.format(**temp_article.url_format)
+                save_as = save_as_format.format(**temp_article.url_format)
+                metadata.update({'url': url, 'save_as': save_as})
+
+        
+def register():
+    signals.article_generator_context.connect(custom_url)
diff --git a/pelican-plugins/dateish/Readme.rst b/pelican-plugins/dateish/Readme.rst
new file mode 100644
index 0000000000000000000000000000000000000000..0de6361f937842aa6b4a7e7226e9a05ef8de2e62
--- /dev/null
+++ b/pelican-plugins/dateish/Readme.rst
@@ -0,0 +1,41 @@
+Dateish Plugin for Pelican
+==========================
+
+This plugin adds the ability to treat arbitrary metadata fields as datetime
+objects.
+
+Usage
+-----
+
+For example, if you have the following pieces of metadata in an article:
+
+.. code-block:: markdown
+
+    # my_article.markdown
+    Date: 2000-01-01
+    Idea_Date: 1993-03-04
+    Important_Dates: 2013-10-12
+                     2013-11-08
+                     2013-12-02
+
+Normally, the Idea_Date and Important_Dates variables will be strings, so
+you will not be able to use the strftime() Jinja filter on them.
+
+With this plugin, you define in your settings file a list of the names of
+the additional metadata fields you want to treat as dates:
+
+.. code-block:: python
+
+    # pelicanconf.py
+    DATEISH_PROPERTIES = ['idea_date', 'important_dates']
+
+Then you can use them in templates just like date:
+
+.. code-block:: html+jinja
+
+    # mytemplate.html
+    <p>Idea date: {{ article.idea_date | strftime('%d %B %Y') }}</p>
+    {% for d in article.important_dates %}
+        <p>Important date: {{ d | strftime('%d %B %Y') }}</p>
+    {% endfor %}
+
diff --git a/pelican-plugins/dateish/__init__.py b/pelican-plugins/dateish/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..52db509dc51274eb24d70cfd8994e5266f981207
--- /dev/null
+++ b/pelican-plugins/dateish/__init__.py
@@ -0,0 +1 @@
+from .dateish import *
diff --git a/pelican-plugins/dateish/dateish.py b/pelican-plugins/dateish/dateish.py
new file mode 100644
index 0000000000000000000000000000000000000000..5036db2bee15ea3c0c24eb991c3cb7c781d44e32
--- /dev/null
+++ b/pelican-plugins/dateish/dateish.py
@@ -0,0 +1,28 @@
+# -*- coding: utf-8 -*-
+"""
+Dateish Plugin for Pelican
+==========================
+
+This plugin adds the ability to treat arbitrary metadata fields as datetime
+objects.
+"""
+
+from pelican import signals
+from pelican.utils import get_date
+
+
+def dateish(generator):
+    if 'DATEISH_PROPERTIES' not in generator.settings:
+        return
+
+    for article in generator.articles:
+        for field in generator.settings['DATEISH_PROPERTIES']:
+            if hasattr(article, field):
+                value = getattr(article, field)
+                if type(value) == list:
+                    setattr(article, field, [get_date(d) for d in value])
+                else:
+                    setattr(article, field, get_date(value))
+
+def register():
+    signals.article_generator_finalized.connect(dateish)
diff --git a/pelican-plugins/disqus_static/README.rst b/pelican-plugins/disqus_static/README.rst
new file mode 100644
index 0000000000000000000000000000000000000000..77cc5fa7c15b0a74395807f248f84aa0847b8791
--- /dev/null
+++ b/pelican-plugins/disqus_static/README.rst
@@ -0,0 +1,60 @@
+Disqus static comment plugin for Pelican
+====================================
+
+This plugin adds a disqus_comments property to all articles.
+Comments are fetched at generation time using disqus API.
+
+Installation
+------------
+Because we use disqus API to retrieve the comments you need to create an application at
+http://disqus.com/api/applications/ which will provide you with a secret and public keys for the API.
+
+We use disqus-python package for communication with disqus API:
+``pip install disqus-python``
+
+Put ``disqus_static.py`` plugin in ``plugins`` folder in pelican installation 
+and use the following in your settings::
+
+    PLUGINS = [u"disqus_static"]
+
+    DISQUS_SITENAME = u'YOUR_SITENAME'
+    DISQUS_SECRET_KEY = u'YOUR_SECRET_KEY'
+    DISQUS_PUBLIC_KEY = u'YOUR_PUBLIC_KEY'
+
+Usage
+-----
+
+.. code-block:: html+jinja
+
+    {% if article.disqus_comments %}
+    <div id="disqus_static_comments">
+        <h4>{{ article.disqus_comment_count }} comments</h4>
+        <ul class="post-list">
+            {% for comment in article.disqus_comments recursive %}
+            <li class="post">
+                <div class="post-content">
+                    <div class="avatar hovercard">
+                        <img alt="Avatar" src="{{ comment.author.avatar.small.cache }}">
+                    </div>
+                    <div class="post-body">
+                        <header>
+                            <span class="publisher-anchor-color">{{ comment.author.name }}</span>
+                            <span class="time-ago" title="{{ comment.createdAt }}">{{ comment.createdAt }}</span>
+                        </header>
+                        <div class="post-message-container">
+                            <div class="post-message publisher-anchor-color ">
+                                {{ comment.message }}
+                            </div>
+                        </div>
+                    </div>
+                </div>
+                {% if comment.children %}
+                <ul class="children">
+                    {{ loop(comment.children) }}
+                </ul>
+                {% endif %}
+            </li>
+            {% endfor %}
+        </ul>
+    </div>
+    {% endif %}
diff --git a/pelican-plugins/disqus_static/__init__.py b/pelican-plugins/disqus_static/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..acda53f94d3766bb150b4f0bccaa915d09a9c97f
--- /dev/null
+++ b/pelican-plugins/disqus_static/__init__.py
@@ -0,0 +1 @@
+from .disqus_static import *
diff --git a/pelican-plugins/disqus_static/disqus_static.py b/pelican-plugins/disqus_static/disqus_static.py
new file mode 100644
index 0000000000000000000000000000000000000000..6fb087acef29f52eeb227a17ece5d2ac6e48d07c
--- /dev/null
+++ b/pelican-plugins/disqus_static/disqus_static.py
@@ -0,0 +1,80 @@
+# -*- coding: utf-8 -*-
+"""
+Disqus static comment plugin for Pelican
+====================================
+This plugin adds a disqus_comments property to all articles.          
+Comments are fetched at generation time using disqus API.
+"""
+
+from __future__ import unicode_literals
+from disqusapi import DisqusAPI, Paginator
+from pelican import signals
+
+def initialized(pelican):
+    from pelican.settings import DEFAULT_CONFIG
+    DEFAULT_CONFIG.setdefault('DISQUS_SECRET_KEY', '')
+    DEFAULT_CONFIG.setdefault('DISQUS_PUBLIC_KEY', '')
+    if pelican:
+        pelican.settings.setdefault('DISQUS_SECRET_KEY', '')
+        pelican.settings.setdefault('DISQUS_PUBLIC_KEY', '')
+
+def disqus_static(generator):
+    disqus = DisqusAPI(generator.settings['DISQUS_SECRET_KEY'], 
+                       generator.settings['DISQUS_PUBLIC_KEY'])
+    # first retrieve the threads
+    threads = Paginator(disqus.threads.list, 
+                        forum=generator.settings['DISQUS_SITENAME'])
+    # build a {thread_id: title} dict
+    thread_dict = {}
+    for thread in threads:
+        thread_dict[thread['id']] = thread['title']
+
+    # now retrieve the posts
+    posts = Paginator(disqus.posts.list, 
+                      forum=generator.settings['DISQUS_SITENAME'])
+
+    # build a {post_id: [child_post1, child_post2, ...]} dict
+    child_dict = {}
+    for post in posts:
+        if post['id'] not in child_dict.keys():
+            child_dict[post['id']] = []
+        if post['parent'] is not None:
+            if str(post['parent']) not in child_dict.keys():
+                child_dict[str(post['parent'])] = []
+            child_dict[str(post['parent'])].append(post)
+
+    # build a {title: [post1, post2, ...]} dict
+    post_dict = {}
+    for post in posts:
+        build_post_dict(post_dict, child_dict, thread_dict, post)
+
+    for article in generator.articles:
+        if article.title in post_dict:
+            article.disqus_comments = post_dict[article.title]
+            article.disqus_comment_count = sum([
+                postcounter(post) for post in post_dict[article.title]])
+
+def postcounter(node):
+    return 1 + sum([postcounter(n) for n in node['children']])
+
+def build_post_dict(post_dict, child_dict, thread_dict, post):
+    if post['thread'] not in thread_dict.keys():
+        return # invalid thread, should never happen
+
+    build_child_dict(child_dict, post)
+
+    if post['parent'] is not None:
+        return # this is a child post, don't want to display it here
+
+    if thread_dict[post['thread']] not in post_dict.keys():
+        post_dict[thread_dict[post['thread']]] = []
+    post_dict[thread_dict[post['thread']]].append(post)
+
+def build_child_dict(child_dict, post):
+    post['children'] = child_dict[post['id']]
+    for child in child_dict[post['id']]:
+        build_child_dict(child_dict, child)
+
+def register():
+    signals.initialized.connect(initialized)
+    signals.article_generator_finalized.connect(disqus_static)
diff --git a/pelican-plugins/disqus_static/requirements.txt b/pelican-plugins/disqus_static/requirements.txt
new file mode 100755
index 0000000000000000000000000000000000000000..7692c84ae4856a62e4ed9628393b580dd3de559d
--- /dev/null
+++ b/pelican-plugins/disqus_static/requirements.txt
@@ -0,0 +1,2 @@
+# Using latest repo version because latest pypi release is bugged: https://github.com/disqus/disqus-python/issues/34
+git+https://github.com/disqus/disqus-python.git
diff --git a/pelican-plugins/events/README.md b/pelican-plugins/events/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..63b95f50a01b85d2028f5eacbebf111263ed84ad
--- /dev/null
+++ b/pelican-plugins/events/README.md
@@ -0,0 +1,82 @@
+Events plugin
+=============
+
+This plugin allows you to put events in your content via metadata. An
+iCal file is generated containing all events.
+
+
+Dependencies
+------------
+
+This plugin depends on the `icalendar` package, which can be installed
+using APT, DNF/YUM or pip:
+
+```sh
+pip install icalendar
+```
+
+
+Settings
+--------
+
+You can define settings with the `PLUGIN_EVENTS` variable:
+
+```python
+PLUGIN_EVENTS = {
+    'ics_fname': 'calendar.ics',
+}
+```
+
+Settings:
+- `ics_fname`: Where the iCal file is written
+
+
+Usage
+-----
+
+You can use the following metadata in your content:
+- `event-start`: When the event will start in "YYYY-MM-DD hh:mm"
+- `event-end`: When the event will stop in "YYYY-MM-DD hh:mm"
+- `event-duration`: The duration of the event [1]
+- `event-location`: Where the event takes place
+
+[1] To specify the event duration, use a number followed by a time unit:
+- `w`: weeks
+- `d`: days
+- `h`: hours
+- `m`: minutes
+- `s`: seconds
+
+
+Examples
+--------
+
+Example in reST format:
+```reST
+:event-start: 2015-01-21 10:30
+:event-duration: 2h
+:event-location: somewhere
+```
+
+Example in Markdown format:
+```markdown
+Event-start: 2015-01-21 10:30
+Event-duration: 2h
+Event-location: somewhere
+```
+
+
+Dedicated page
+--------------
+
+To generate a sorted event list in its own dedicated page:
+- Copy the `events_list.html` template under the templates directory of your theme
+- Create a page for this list, for example in `content/pages/events_list.rst`
+- Include the following metadata in your content:
+```reST
+Events list
+###########
+:slug: events-list
+:summary:
+:template: events_list
+```
diff --git a/pelican-plugins/events/__init__.py b/pelican-plugins/events/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..7b9b76e29d2008e1497d3730a10210379c3a31c3
--- /dev/null
+++ b/pelican-plugins/events/__init__.py
@@ -0,0 +1 @@
+from .events import *
diff --git a/pelican-plugins/events/events.py b/pelican-plugins/events/events.py
new file mode 100644
index 0000000000000000000000000000000000000000..3f08b09852f558d624d9ff1b151ac286273da868
--- /dev/null
+++ b/pelican-plugins/events/events.py
@@ -0,0 +1,194 @@
+# -*- coding: utf-8 -*-
+"""
+events plugin for Pelican
+=========================
+
+This plugin looks for and parses an "events" directory and generates
+blog posts with a user-defined event date. (typically in the future)
+It also generates an ICalendar v2.0 calendar file.
+https://en.wikipedia.org/wiki/ICalendar
+
+
+Author: Federico Ceratto <federico.ceratto@gmail.com>
+Released under AGPLv3+ license, see LICENSE
+"""
+
+from datetime import datetime, timedelta
+from pelican import signals, utils, contents
+from collections import namedtuple, defaultdict
+import icalendar
+import logging
+import os.path
+import pytz
+
+log = logging.getLogger(__name__)
+
+TIME_MULTIPLIERS = {
+    'w': 'weeks',
+    'd': 'days',
+    'h': 'hours',
+    'm': 'minutes',
+    's': 'seconds'
+}
+
+events = []
+localized_events = defaultdict(list)
+
+
+def parse_tstamp(metadata, field_name):
+    """Parse a timestamp string in format "YYYY-MM-DD HH:MM"
+
+    :returns: datetime
+    """
+    try:
+        return datetime.strptime(metadata[field_name], '%Y-%m-%d %H:%M')
+    except Exception as e:
+        log.error("Unable to parse the '%s' field in the event named '%s': %s" \
+            % (field_name, metadata['title'], e))
+        raise
+
+
+def parse_timedelta(metadata):
+    """Parse a timedelta string in format [<num><multiplier> ]*
+    e.g. 2h 30m
+
+    :returns: timedelta
+    """
+
+    chunks = metadata['event-duration'].split()
+    tdargs = {}
+    for c in chunks:
+        try:
+            m = TIME_MULTIPLIERS[c[-1]]
+            val = int(c[:-1])
+            tdargs[m] = val
+        except KeyError:
+            log.error("""Unknown time multiplier '%s' value in the \
+'event-duration' field in the '%s' event. Supported multipliers \
+are: '%s'.""" % (c, metadata['title'], ' '.join(TIME_MULTIPLIERS)))
+            raise RuntimeError("Unknown time multiplier '%s'" % c)
+        except ValueError:
+            log.error("""Unable to parse '%s' value in the 'event-duration' \
+field in the '%s' event.""" % (c, metadata['title']))
+            raise ValueError("Unable to parse '%s'" % c)
+
+
+    return timedelta(**tdargs)
+
+
+def basic_isoformat(datetime_value):
+    return datetime_value.strftime("%Y%m%dT%H%M%S")
+
+
+def parse_article(content):
+    """Collect articles metadata to be used for building the event calendar
+
+    :returns: None
+    """
+    if not isinstance(content, contents.Article):
+        return
+
+    if 'event-start' not in content.metadata:
+        return
+
+    dtstart = parse_tstamp(content.metadata, 'event-start')
+
+    if 'event-end' in content.metadata:
+        dtend = parse_tstamp(content.metadata, 'event-end')
+
+    elif 'event-duration' in content.metadata:
+        dtdelta = parse_timedelta(content.metadata)
+        dtend = dtstart + dtdelta
+
+    else:
+        msg = "Either 'event-end' or 'event-duration' must be" + \
+            " speciefied in the event named '%s'" % content.metadata['title']
+        log.error(msg)
+        raise ValueError(msg)
+
+    content.event_plugin_data = {"dtstart": dtstart, "dtend": dtend}
+
+    events.append(content)
+
+
+def generate_ical_file(generator):
+    """Generate an iCalendar file
+    """
+    global events
+    ics_fname = generator.settings['PLUGIN_EVENTS']['ics_fname']
+    if not ics_fname:
+        return
+
+    ics_fname = os.path.join(generator.settings['OUTPUT_PATH'], ics_fname)
+    log.debug("Generating calendar at %s with %d events" % (ics_fname, len(events)))
+
+    tz = generator.settings.get('TIMEZONE', 'UTC')
+    tz = pytz.timezone(tz)
+
+    ical = icalendar.Calendar()
+    ical.add('prodid', '-//My calendar product//mxm.dk//')
+    ical.add('version', '2.0')
+
+    DEFAULT_LANG = generator.settings['DEFAULT_LANG']
+    curr_events = events if not localized_events else localized_events[DEFAULT_LANG]
+
+    for e in curr_events:
+        ie = icalendar.Event(
+            summary=e.metadata['summary'],
+            dtstart=basic_isoformat(e.event_plugin_data["dtstart"]),
+            dtend=basic_isoformat(e.event_plugin_data["dtend"]),
+            dtstamp=basic_isoformat(e.metadata['date']),
+            priority=5,
+            uid=e.metadata['title'] + e.metadata['summary'],
+        )
+        if 'event-location' in e.metadata:
+            ie.add('location', e.metadata['event-location'])
+
+        ical.add_component(ie)
+
+    with open(ics_fname, 'wb') as f:
+        f.write(ical.to_ical())
+
+
+def generate_localized_events(generator):
+    """ Generates localized events dict if i18n_subsites plugin is active """
+
+    if "i18n_subsites" in generator.settings["PLUGINS"]:
+        if not os.path.exists(generator.settings['OUTPUT_PATH']):
+            os.makedirs(generator.settings['OUTPUT_PATH'])
+
+        for e in events:
+            if "lang" in e.metadata:
+                localized_events[e.metadata["lang"]].append(e)
+            else:
+                log.debug("event %s contains no lang attribute" % (e.metadata["title"],))
+
+
+def generate_events_list(generator):
+    """Populate the event_list variable to be used in jinja templates"""
+
+    if not localized_events:
+        generator.context['events_list'] = sorted(events, reverse = True,
+                                                  key=lambda ev: (ev.event_plugin_data["dtstart"], ev.event_plugin_data["dtend"]))
+    else:
+        generator.context['events_list'] = {k: sorted(v, reverse = True,
+                                                      key=lambda ev: (ev.event_plugin_data["dtstart"], ev.event_plugin_data["dtend"]))
+                                            for k, v in localized_events.items()}
+
+def initialize_events(article_generator):
+    """
+    Clears the events list before generating articles to properly support plugins with
+    multiple generation passes like i18n_subsites
+    """
+
+    del events[:]
+    localized_events.clear()
+
+def register():
+    signals.article_generator_init.connect(initialize_events)
+    signals.content_object_init.connect(parse_article)
+    signals.article_generator_finalized.connect(generate_localized_events)
+    signals.article_generator_finalized.connect(generate_ical_file)
+    signals.article_generator_finalized.connect(generate_events_list)
+
+
diff --git a/pelican-plugins/events/events_list.html b/pelican-plugins/events/events_list.html
new file mode 100644
index 0000000000000000000000000000000000000000..98885c9b91b04f27c95b37d3078b2f04445a70d5
--- /dev/null
+++ b/pelican-plugins/events/events_list.html
@@ -0,0 +1,36 @@
+
+{% extends "base.html" %}
+
+{% block title %} Events list - {{ SITENAME }}{% endblock %}
+
+{% block content %}
+
+    {% if events_list %}
+    <ul class="post-list">
+    {% for event in events_list %}
+      <li>
+        <p>
+          <a href="{{ SITEURL }}/{{ event.url }}">
+            <b>{{ event.metadata["title"] }}</b>
+          </a>
+        </p>
+        <p>
+        {% if event.event_plugin_data["dtstart"].date() == event.event_plugin_data["dtend"].date() %}
+        From {{ event.event_plugin_data["dtstart"] }} to {{ event.event_plugin_data["dtend"].time() }}
+        {% else %}
+        From {{ event.event_plugin_data["dtstart"] }} to {{ event.event_plugin_data["dtend"] }}
+        {% endif %}
+        </p>
+
+        {% if event.location %}
+        <p>Location: {{ event.metadata["location"] }}</p>
+        {% endif %}
+
+        <p>{{ event.metadata["summary"] }}</p>
+
+      </li>
+    {% endfor %}
+    </ul>
+    {% endif %}
+
+{% endblock %}
diff --git a/pelican-plugins/events/requirements.txt b/pelican-plugins/events/requirements.txt
new file mode 100755
index 0000000000000000000000000000000000000000..44466d5c771da8bc9bdc55314155c7edd7cf266a
--- /dev/null
+++ b/pelican-plugins/events/requirements.txt
@@ -0,0 +1 @@
+icalendar
\ No newline at end of file
diff --git a/pelican-plugins/events/requiremets.txt b/pelican-plugins/events/requiremets.txt
new file mode 100755
index 0000000000000000000000000000000000000000..744b7a9972d757630878095f5ef80b30068c54b6
--- /dev/null
+++ b/pelican-plugins/events/requiremets.txt
@@ -0,0 +1 @@
+icalendar
diff --git a/pelican-plugins/extract_toc/README.md b/pelican-plugins/extract_toc/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..40d2bee55efe85cf4b1f71425e002919dbe0accc
--- /dev/null
+++ b/pelican-plugins/extract_toc/README.md
@@ -0,0 +1,137 @@
+Extract Table of Content
+========================
+
+A Pelican plugin to extract table of contents (ToC) from `article.content` and
+place it in its own `article.toc` variable for use in templates.
+
+Copyright (c) Talha Mansoor
+
+Author          | Talha Mansoor
+----------------|-----
+Author Email    | talha131@gmail.com
+Author Homepage | http://onCrashReboot.com
+Github Account  | https://github.com/talha131
+
+
+Acknowledgement
+---------------
+
+Thanks to [Avaris](https://github.com/avaris) for going out of the way to help
+me fix Unicode issues and doing a thorough code review.
+
+Thanks to [gw0](http://gw.tnode.com/) for adding Pandoc reader support.
+
+
+Why do you need it?
+===================
+
+Pelican can generate ToC of reST and Markdown files, using markup's respective
+directive and extension. Such ToC is generated and placed at the beginning of
+`article.content` like a string. Consequently it can not be placed anywhere
+else on the page (eg. `<nav>` HTML5 tag, in header, or at the end of your
+article's contents).
+
+To solve this problem, this plugin extracts ToC from `article.content` and
+places it in its own `article.toc` variable for use in templates.
+
+
+Requirements
+============
+
+`extract_toc` requires BeautifulSoup.
+
+```bash
+pip install beautifulsoup4
+```
+
+
+How to Use
+==========
+
+This plugin works by extracting the first occurrence of enclosed in:
+
+- `<div class="toc">` for the default Markdown reader
+- `<div class="contents topic">` for the default reStructuredText reader
+- `<nav class="TOC">` for the Pandoc reader
+
+If ToC appears in your article at more than one places, `extract_toc` will
+remove only the first occurrence. You shouldn't probably need to have multiple
+ToC in your article. In case you need to display it multiple times, you can
+print it via your template.
+
+
+Template example
+----------------
+
+Add something like this to your Pelican templates if missing:
+
+```python
+{% if article.toc %}
+    <nav class="toc">
+    {{ article.toc }}
+    </nav>
+{% endif %}
+```
+
+
+reStructuredText reader
+-----------------------
+
+To add a table of contents to your reStructuredText document (`.rst`) you need to add a `.. contents::` directive to its beginning. See the [docutils documentation](http://docutils.sourceforge.net/docs/ref/rst/directives.html#table-of-contents) for more details.
+
+```rst
+My super title
+##############
+
+:date: 2010-10-03
+:tags: thats, awesome
+
+.. contents::
+..
+   1  Head 1
+     1.1  Head 2
+   2  Head 3
+   3  head 4
+
+Heading 1
+---------
+
+Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa.
+```
+
+
+Markdown reader
+---------------
+
+To enable table of contents generation for the Markdown reader you need to set `MD_EXTENSIONS = (['toc'])` in your Pelican configuration file.
+
+To add a table of contents to your Markdown document (`.md`) you need to place the `[TOC]` marker to its beginning. See the [Python Markdown documentation](http://pythonhosted.org/Markdown/extensions/toc.html) for more details.
+
+```markdown
+title: My super title
+date: 4-4-2013
+tags: thats, awesome
+
+[TOC]
+
+# Heading 1 #
+
+Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa.
+```
+
+
+Pandoc reader
+-------------
+
+To enable table of contents generation for the Pandoc reader you need to set `PANDOC_ARGS = (['--toc', '--template=pandoc-template-toc'])` in your Pelican configuration file.
+
+Contents of the Pandoc template file `pandoc-template-toc.html5`:
+
+```html
+$if(toc)$
+<nav id="TOC">
+$toc$
+</nav>
+$endif$
+$body$
+```
diff --git a/pelican-plugins/extract_toc/__init__.py b/pelican-plugins/extract_toc/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..52c57781152f76c5ba831d583d4a46fc2420607a
--- /dev/null
+++ b/pelican-plugins/extract_toc/__init__.py
@@ -0,0 +1 @@
+from .extract_toc import *
diff --git a/pelican-plugins/extract_toc/extract_toc.py b/pelican-plugins/extract_toc/extract_toc.py
new file mode 100644
index 0000000000000000000000000000000000000000..c92c4a4e457d1709f7bcc1420445df3c17d99bfa
--- /dev/null
+++ b/pelican-plugins/extract_toc/extract_toc.py
@@ -0,0 +1,66 @@
+# -*- coding: utf-8 -*-
+"""
+Extract Table of Content
+========================
+
+A Pelican plugin to extract table of contents (ToC) from `article.content` and
+place it in its own `article.toc` variable for use in templates.
+"""
+
+from os import path
+from bs4 import BeautifulSoup
+from pelican import signals, readers, contents
+import logging
+
+logger = logging.getLogger(__name__)
+
+
+def extract_toc(content):
+    if isinstance(content, contents.Static):
+        return
+
+    soup = BeautifulSoup(content._content, 'html.parser')
+    filename = content.source_path
+    extension = path.splitext(filename)[1][1:]
+    toc = None
+
+    # default Markdown reader
+    if not toc and readers.MarkdownReader.enabled and extension in readers.MarkdownReader.file_extensions:
+        toc = soup.find('div', class_='toc')
+        if toc:
+            toc.extract()
+            if len(toc.find_next('ul').find_all('li')) == 0:
+                toc = None
+
+    # default reStructuredText reader
+    if not toc and readers.RstReader.enabled and extension in readers.RstReader.file_extensions:
+        toc = soup.find('div', class_='contents topic')
+        if toc:
+            toc.extract()
+            tag = BeautifulSoup(str(toc), 'html.parser')
+            tag.div['class'] = 'toc'
+            tag.div['id'] = ''
+            p = tag.find('p', class_='topic-title first')
+            if p:
+                p.extract()
+            toc = tag
+
+    # Pandoc reader (markdown and other formats)
+    if 'pandoc_reader' in content.settings['PLUGINS']:
+        try:
+            from pandoc_reader import PandocReader
+        except ImportError:
+            PandocReader = False
+        if not toc and PandocReader and PandocReader.enabled and extension in PandocReader.file_extensions:
+            toc = soup.find('nav', id='TOC')
+
+    if toc:
+        toc.extract()
+        content._content = soup.decode()
+        content.toc = toc.decode()
+        if content.toc.startswith('<html>'):
+            content.toc = content.toc[12:-14]
+
+
+def register():
+    signals.content_object_init.connect(extract_toc)
diff --git a/pelican-plugins/feed_summary/Readme.md b/pelican-plugins/feed_summary/Readme.md
new file mode 100644
index 0000000000000000000000000000000000000000..591fb34c4825daa65da38962ce395f0eb3cfcfcf
--- /dev/null
+++ b/pelican-plugins/feed_summary/Readme.md
@@ -0,0 +1,38 @@
+# DEPRECATED — Do Not Use #
+
+**As noted in the [Pelican 3.7 release notes](https://blog.getpelican.com/pelican-3.7-released.html), RSS feeds now provide summaries by default, so this plugin is no longer relevant and will eventually be removed from this repository.**
+
+-----------------------
+
+This plugin allows article summaries to be used in ATOM and RSS feeds instead of the entire article. It uses the
+built-in pelican `Summary:` metadata.
+
+The summary of an article can either be set explicitly with the `Summary:` metadata attribute as described in the
+[Pelican documentation](http://docs.getpelican.com/) (*Writing content* > *File metadata* section),
+or automatically generated using the number of words specified in the
+[SUMMARY_MAX_LENGTH](http://docs.getpelican.com/en/latest/settings.html) setting.
+
+## Usage ##
+To use this plugin, ensure the following are set in your `pelicanconf.py` file:
+
+    PLUGIN_PATH = '/path/to/pelican-plugins'
+    PLUGINS = [
+		'feed_summary',
+		]
+    FEED_USE_SUMMARY = True
+
+The default value of `FEED_USE_SUMMARY` is `False`, so it must be set to `True` to enable the plugin, even if it is loaded.
+
+This plugin is written for pelican 3.3 and later.
+
+
+## Implementation Notes ##
+
+This plugin derives `FeedSummaryWriter` from the `Writer` class, duplicating code of the `Writer._add_item_to_the_feed` method.
+
+When the `initialized` signal is sent, it alternates the `get_writer` method of the `Pelican` object to use `FeedSummaryWriter` instead of `Writer`.
+
+A little hackish, but currently this can't be done otherwise via the regular plugin methods.
+
+ * *Initial Code (PR #36): Michelle L. Gill <michelle.lynn.gill@gmail.com>*
+ * *Resumption of PR and Maintainer: Florian Jacob ( projects[PLUS]pelican[ÄT]florianjacob.de )*
diff --git a/pelican-plugins/feed_summary/__init__.py b/pelican-plugins/feed_summary/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..93db3bed80a887720df701e98a371b4498cd1104
--- /dev/null
+++ b/pelican-plugins/feed_summary/__init__.py
@@ -0,0 +1 @@
+from .feed_summary import *
diff --git a/pelican-plugins/feed_summary/feed_summary.py b/pelican-plugins/feed_summary/feed_summary.py
new file mode 100644
index 0000000000000000000000000000000000000000..0710669cf44486b31f6a25e516a21480927a561e
--- /dev/null
+++ b/pelican-plugins/feed_summary/feed_summary.py
@@ -0,0 +1,57 @@
+# -*- coding: utf-8 -*-
+"""
+Feed Summary
+============
+
+This plugin allows summaries to be used in feeds instead of the full length article.
+"""
+
+from __future__ import unicode_literals
+
+from jinja2 import Markup
+
+import six
+if not six.PY3:
+    from urlparse import urlparse
+else:
+    from urllib.parse import urlparse
+
+from pelican import signals
+from pelican.writers import Writer
+from pelican.utils import set_date_tzinfo
+
+from .magic_set import magic_set
+
+class FeedSummaryWriter(Writer):
+    def _add_item_to_the_feed(self, feed, item):
+        if self.settings['FEED_USE_SUMMARY']:
+            title = Markup(item.title).striptags()
+            link = '%s/%s' % (self.site_url, item.url)
+            feed.add_item(
+                title=title,
+                link=link,
+                unique_id='tag:%s,%s:%s' % (urlparse(link).netloc,
+                                            item.date.date(),
+                                            urlparse(link).path.lstrip('/')),
+                description=item.summary if hasattr(item, 'summary') else item.get_content(self.site_url),
+                categories=item.tags if hasattr(item, 'tags') else None,
+                author_name=getattr(item, 'author', ''),
+                pubdate=set_date_tzinfo(item.modified if hasattr(item, 'modified') else item.date,
+                    self.settings.get('TIMEZONE', None)))
+        else:
+            super(FeedSummaryWriter, self)._add_item_to_the_feed(feed, item)
+
+def set_feed_use_summary_default(pelican_object):
+    # modifying DEFAULT_CONFIG doesn't have any effect at this point in pelican setup
+    # everybody who uses DEFAULT_CONFIG is already used/copied it or uses the pelican_object.settings copy.
+
+    pelican_object.settings.setdefault('FEED_USE_SUMMARY', False)
+
+def patch_pelican_writer(pelican_object):
+    @magic_set(pelican_object)
+    def get_writer(self):
+        return FeedSummaryWriter(self.output_path,settings=self.settings)
+
+def register():
+    signals.initialized.connect(set_feed_use_summary_default)
+    signals.initialized.connect(patch_pelican_writer)
diff --git a/pelican-plugins/feed_summary/magic_set.py b/pelican-plugins/feed_summary/magic_set.py
new file mode 100644
index 0000000000000000000000000000000000000000..ee3d6f0a419bc7a4bf58be4794b9c77fb424b0fe
--- /dev/null
+++ b/pelican-plugins/feed_summary/magic_set.py
@@ -0,0 +1,92 @@
+import inspect
+import six
+
+# Modifies class methods (or instances of them) on the fly
+# http://blog.ianbicking.org/2007/08/08/opening-python-classes/
+# http://svn.colorstudy.com/home/ianb/recipes/magicset.py
+# including python 3 fixes for func_name => __name__ and types.ClassType => type
+
+def magic_set(obj):
+    """
+Adds a function/method to an object. Uses the name of the first
+argument as a hint about whether it is a method (``self``), class
+method (``cls`` or ``klass``), or static method (anything else).
+Works on both instances and classes.
+
+>>> class color:
+... def __init__(self, r, g, b):
+... self.r, self.g, self.b = r, g, b
+>>> c = color(0, 1, 0)
+>>> c # doctest: +ELLIPSIS
+<__main__.color instance at ...>
+>>> @magic_set(color)
+... def __repr__(self):
+... return '<color %s %s %s>' % (self.r, self.g, self.b)
+>>> c
+<color 0 1 0>
+>>> @magic_set(color)
+... def red(cls):
+... return cls(1, 0, 0)
+>>> color.red()
+<color 1 0 0>
+>>> c.red()
+<color 1 0 0>
+>>> @magic_set(color)
+... def name():
+... return 'color'
+>>> color.name()
+'color'
+>>> @magic_set(c)
+... def name(self):
+... return 'red'
+>>> c.name()
+'red'
+>>> @magic_set(c)
+... def name(cls):
+... return cls.__name__
+>>> c.name()
+'color'
+>>> @magic_set(c)
+... def pr(obj):
+... print obj
+>>> c.pr(1)
+1
+"""
+    def decorator(func):
+        is_class = isinstance(obj, six.class_types)
+        args, varargs, varkw, defaults = inspect.getargspec(func)
+        if not args or args[0] not in ('self', 'cls', 'klass'):
+            # Static function/method
+            if is_class:
+                replacement = staticmethod(func)
+            else:
+                replacement = func
+        elif args[0] == 'self':
+            if is_class:
+                replacement = func
+            else:
+                def replacement(*args, **kw):
+                    return func(obj, *args, **kw)
+                try:
+                    replacement.__name__ = func.__name__
+                except:
+                    pass
+        else:
+            if is_class:
+                replacement = classmethod(func)
+            else:
+                def replacement(*args, **kw):
+                    return func(obj.__class__, *args, **kw)
+                try:
+                    replacement.__name__ = func.__name__
+                except:
+                    pass
+        setattr(obj, func.__name__, replacement)
+        return replacement
+    return decorator
+
+if __name__ == '__main__':
+    import doctest
+    doctest.testmod()
+
+
diff --git a/pelican-plugins/figure-ref/LICENSE b/pelican-plugins/figure-ref/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..733c072369ca77331f392c40da7404c85c36542c
--- /dev/null
+++ b/pelican-plugins/figure-ref/LICENSE
@@ -0,0 +1,675 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    {one line to give the program's name and a brief idea of what it does.}
+    Copyright (C) {year}  {name of author}
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    {project}  Copyright (C) {year}  {fullname}
+    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+  The GNU General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.  But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
+
diff --git a/pelican-plugins/figure-ref/README.md b/pelican-plugins/figure-ref/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..3e21f0c5e4c9ec5bcf38c855ba56c066f7233fa8
--- /dev/null
+++ b/pelican-plugins/figure-ref/README.md
@@ -0,0 +1,45 @@
+figure-ref
+==========
+
+Provides a system to reference figures using labels, as happens in LaTeX.
+
+Requirements
+============
+
+`figure-ref` requires `BeautifulSoup4`.
+
+```bash
+pip install BeautifulSoup4
+```
+
+How to Use
+==========
+
+This plugin will search for labels within `<figcaption>` tags. Figures and
+figcaptions can be inserted via Restructured Text or using the
+[figureAltCaption](https://github.com/jdittrich/figureAltCaption) plugin with
+Markdown. Labelled figures take the form:
+```html
+<figure>
+  <img src="path/to/image.png">
+  <figcaption>
+  labelname :: This is the label text.
+  </figcaption>
+</figure>
+```
+In Markdown, using the aforementioned plugin, you can create such a figure
+with the syntax `![labelname :: This is the label text.](path/to/image.png)`.
+
+This would be traslated to
+```html
+<figure id="figref-labelname">
+  <img src="path/to/image.png">
+  <figcaption>
+  <strong>Figure 1:</strong> This is the label text.
+  </figcaption>
+</figure>
+```
+
+This figure can be referenced in a paragraph using the syntax `{#labelname}`.
+This will then be replaced by the figure number, which will act as a link
+to the figure.
diff --git a/pelican-plugins/figure-ref/__init__.py b/pelican-plugins/figure-ref/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..7029bec83423f275a443507bf4642cc66fa94866
--- /dev/null
+++ b/pelican-plugins/figure-ref/__init__.py
@@ -0,0 +1 @@
+from .figure_ref import *
diff --git a/pelican-plugins/figure-ref/figure-ref-40e04d32bff468a6b3e63c373c5d95fca39783fe.zip b/pelican-plugins/figure-ref/figure-ref-40e04d32bff468a6b3e63c373c5d95fca39783fe.zip
new file mode 100644
index 0000000000000000000000000000000000000000..6f3faa8a5dc00f56153735ff0ee7c64f44aae3ca
Binary files /dev/null and b/pelican-plugins/figure-ref/figure-ref-40e04d32bff468a6b3e63c373c5d95fca39783fe.zip differ
diff --git a/pelican-plugins/figure-ref/figure-ref-40e04d32bff468a6b3e63c373c5d95fca39783fe/.gitignore b/pelican-plugins/figure-ref/figure-ref-40e04d32bff468a6b3e63c373c5d95fca39783fe/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..f7bc639956ffd967332755b9b189bf198142c0bb
--- /dev/null
+++ b/pelican-plugins/figure-ref/figure-ref-40e04d32bff468a6b3e63c373c5d95fca39783fe/.gitignore
@@ -0,0 +1,5 @@
+*.pyc
+*.swp
+*~
+papers.bib
+test-blog
\ No newline at end of file
diff --git a/pelican-plugins/figure-ref/figure_ref.py b/pelican-plugins/figure-ref/figure_ref.py
new file mode 100644
index 0000000000000000000000000000000000000000..66d8713a045e6af1d492cbaa1b6b7fc180a0eed6
--- /dev/null
+++ b/pelican-plugins/figure-ref/figure_ref.py
@@ -0,0 +1,86 @@
+# -*- coding: utf-8 -*-
+"""
+figure_ref
+==============
+
+A Pelican plugin that provices a LaTeX-like system for referencing figure
+elements within an article or page. Figures whose figcaption elements begin
+with the format
+
+    labelname :: caption text
+
+will have `labelname ::` replaced by figure numbering. This figure can
+be referenced with the syntax {#labelname}, which will be replaced by
+the figure number. The figure number will provide a link to the figure.
+"""
+
+import logging
+import re
+
+from bs4 import BeautifulSoup, FeatureNotFound
+
+from pelican import signals
+from pelican.generators import ArticlesGenerator, PagesGenerator
+
+import sys
+if (sys.version_info[0]>2):
+    unicode = str
+
+__version__ = '0.0.1'
+
+REF_RE = re.compile("\{#\s*(\w+)\s*\}")
+LABEL_RE = re.compile("^\s*(\w+)\s*::")
+REF = "<a href='#figref-{}'>{}</a>"
+LABEL = "<strong>Figure {}:</strong> "
+
+logger = logging.getLogger(__name__)
+
+def process_content(article):
+    """
+    Substitute reference links for an individual article or page.
+    """
+    try:
+        soup = BeautifulSoup(article._content,'lxml')
+    except FeatureNotFound:
+        soup = BeautifulSoup(article._content,'html.parser')
+    
+    # Get figures and number them
+    figlist = []
+    for fig in soup.find_all('figcaption'):
+        caption = unicode(fig.string)
+        m = LABEL_RE.search(caption)
+        if m:
+            figlist.append(m.group(1))
+            fig.parent['id'] = 'figref-' + m.group(1)
+            new_tag = soup.new_tag("strong")
+            fig.string.replace_with(' ' + caption[m.end():])
+            new_tag.string = "Figure {}:".format(len(figlist))
+            fig.insert(0,new_tag)
+    
+    # Replace references to figures with links
+    def substitute(match):
+        try:
+            num = figlist.index(match.group(1)) + 1
+        except ValueError:
+            logger.warn('`figure_ref` unable to find figure with label "{}" in file {}'.format(match.group(1), article.source_path))
+            return match.string
+        return REF.format(match.group(1),num)
+        
+    article._content = REF_RE.sub(substitute, unicode(soup))
+
+
+
+def add_figure_refs(generators):
+    # Process the articles and pages
+    for generator in generators:
+        if isinstance(generator, ArticlesGenerator):
+            for article in generator.articles:
+                process_content(article)
+        elif isinstance(generator, PagesGenerator):
+            for page in generator.pages:
+                process_content(page)
+
+
+
+def register():
+    signals.all_generators_finalized.connect(add_figure_refs)
diff --git a/pelican-plugins/filetime_from_git/README.rst b/pelican-plugins/filetime_from_git/README.rst
new file mode 100644
index 0000000000000000000000000000000000000000..9290cb2a109f18156d02e174f6c247630cc92307
--- /dev/null
+++ b/pelican-plugins/filetime_from_git/README.rst
@@ -0,0 +1,60 @@
+Use Git commit to determine page date
+======================================
+If the blog content is managed by git repo, this plugin will set articles'
+and pages' ``metadata['date']`` according to git commit. This plugin depends
+on python package ``gitpython``, install::
+
+    pip install gitpython
+
+The date is determined via the following logic:
+
+* if a file is not tracked by Git, or a file is staged but never committed
+    - metadata['date'] = filesystem time
+    - metadata['modified'] = filesystem time
+* if a file is tracked, but no changes in staging area or working directory
+    - metadata['date'] = first commit time
+    - metadata['modified'] = last commit time
+* if a file is tracked, and has changes in stage area or working directory
+    - metadata['date'] = first commit time
+    - metadata['modified'] = filesystem time
+
+When this module is enabled, ``date`` and ``modified`` will be determined
+by Git status; no need to manually set in article/page metadata. And
+operations like copy and move will not affect the generated results.
+
+If you don't want a given article or page to use the Git time, set the
+metadata to ``gittime: off`` to disable it.
+
+Other options
+-------------
+
+### GIT_HISTORY_FOLLOWS_RENAME (default True)
+You can also set GIT_HISTORY_FOLLOWS_RENAME to True in your pelican config to 
+make the plugin follow file renames i.e. ensure the creation date matches
+the original file creation date, not the date is was renamed.
+
+### GIT_GENERATE_PERMALINK (default False)
+Use in combination with permalink plugin to generate permalinks using the original
+commit sha 
+
+### GIT_SHA_METADATA (default True)
+Adds sha of current and oldest commit to metadata
+
+### GIT_FILETIME_FROM_GIT (default True)
+Enable filetime from git behaviour
+
+Content specific options
+------------------------
+Adding metadata `gittime` = False will prevent the plugin trying to setting filetime for this
+content.
+
+Adding metadata `git_permalink` = False will prevent the plugin from adding permalink for this
+content.
+
+FAQ
+---
+
+### Q. I get a GitCommandError: 'git rev-list ...' when I run the plugin. What's up?
+Be sure to use the correct gitpython module for your distros git binary.
+Using the GIT_HISTORY_FOLLOWS_RENAME option to True may also make your problem go away as it uses
+a different method to find commits.
diff --git a/pelican-plugins/filetime_from_git/__init__.py b/pelican-plugins/filetime_from_git/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..b7281c8342918791799f74f8eab7478c55ab3a25
--- /dev/null
+++ b/pelican-plugins/filetime_from_git/__init__.py
@@ -0,0 +1 @@
+from .registration import *
diff --git a/pelican-plugins/filetime_from_git/actions.py b/pelican-plugins/filetime_from_git/actions.py
new file mode 100755
index 0000000000000000000000000000000000000000..3fed1764a3e77452faae70c76c72be2d35dbb162
--- /dev/null
+++ b/pelican-plugins/filetime_from_git/actions.py
@@ -0,0 +1,120 @@
+# -*- coding: utf-8 -*-
+import base64
+import hashlib
+import logging
+import os
+
+from pelican.utils import strftime
+
+from .registration import content_git_object_init
+from .utils import datetime_from_timestamp
+from .utils import string_to_bool
+
+
+logger = logging.getLogger(__name__)
+
+
+@content_git_object_init.connect
+def filetime_from_git(content, git_content):
+    '''
+    Update modification and creation times from git
+    '''
+    if not content.settings['GIT_FILETIME_FROM_GIT']:
+        # Disabled for everything
+        return
+
+    if not string_to_bool(content.metadata.get('gittime', 'yes')):
+        # Disable for this content
+        return
+
+    path = content.source_path
+    fs_creation_time = datetime_from_timestamp(os.stat(path).st_ctime, content)
+    fs_modified_time = datetime_from_timestamp(os.stat(path).st_mtime, content)
+
+    # 1. file is not managed by git
+    #    date: fs time
+    # 2. file is staged, but has no commits
+    #    date: fs time
+    # 3. file is managed, and clean
+    #    date: first commit time, update: last commit time or None
+    # 4. file is managed, but dirty
+    #    date: first commit time, update: fs time
+    if git_content.is_managed_by_git():
+        if git_content.is_committed():
+            content.date = git_content.get_oldest_commit_date()
+
+            if git_content.is_modified():
+                content.modified = fs_modified_time
+            else:
+                content.modified = git_content.get_newest_commit_date()
+        else:
+            # File isn't committed
+            content.date = fs_creation_time
+    else:
+        # file is not managed by git
+        content.date = fs_creation_time
+
+    # Clean up content attributes
+    if not hasattr(content, 'modified'):
+        content.modified = content.date
+
+    if hasattr(content, 'date'):
+        content.locale_date = strftime(content.date, content.date_format)
+
+    if hasattr(content, 'modified'):
+        content.locale_modified = strftime(
+            content.modified, content.date_format)
+
+
+@content_git_object_init.connect
+def git_sha_metadata(content, git_content):
+    '''
+    Add sha metadata to content
+    '''
+    if not content.settings['GIT_SHA_METADATA']:
+        return
+
+    if not git_content.is_committed():
+        return
+
+    content.metadata['gitsha_newest'] = str(git_content.get_newest_commit())
+    content.metadata['gitsha_oldest'] = str(git_content.get_oldest_commit())
+
+
+def update_hash_from_str(hsh, str_input):
+    """
+    Convert a str to object supporting buffer API and update a hash with it.
+    """
+    byte_input = str(str_input).encode("UTF-8")
+    hsh.update(byte_input)
+
+
+@content_git_object_init.connect
+def git_permalink(content, git_content):
+    '''
+    Add git based permalink id to content metadata
+    '''
+    if not content.settings['GIT_GENERATE_PERMALINK']:
+        return
+
+    if not string_to_bool(content.metadata.get('git_permalink', 'yes')):
+        # Disable for this content
+        return
+
+    if not git_content.is_committed():
+        return
+
+    permalink_hash = hashlib.sha1()
+    update_hash_from_str(permalink_hash, git_content.get_oldest_commit())
+    update_hash_from_str(permalink_hash, git_content.get_oldest_filename())
+    git_permalink_id_raw = base64.urlsafe_b64encode(permalink_hash.digest())
+    git_permalink_id = git_permalink_id_raw.decode("UTF-8")
+    permalink_id_metadata_key = content.settings['PERMALINK_ID_METADATA_KEY']
+
+    if permalink_id_metadata_key in content.metadata:
+        content.metadata[permalink_id_metadata_key] = (
+            ','.join((
+                content.metadata[permalink_id_metadata_key], git_permalink_id))
+        )
+    else:
+        content.metadata[permalink_id_metadata_key] = git_permalink_id
diff --git a/pelican-plugins/filetime_from_git/content_adapter.py b/pelican-plugins/filetime_from_git/content_adapter.py
new file mode 100644
index 0000000000000000000000000000000000000000..3be6b7b0855e1ea704db705604be8a0486d80cc5
--- /dev/null
+++ b/pelican-plugins/filetime_from_git/content_adapter.py
@@ -0,0 +1,99 @@
+# -*- coding: utf-8 -*-
+"""
+Wraps a content object to provide some git information
+"""
+import logging
+from pelican.utils import memoized
+from .git_wrapper import git_wrapper
+
+DEV_LOGGER = logging.getLogger(__name__)
+
+
+class GitContentAdapter(object):
+    """
+    Wraps a content object to provide some git information
+    """
+    def __init__(self, content):
+        self.content = content
+        self.git = git_wrapper('.')
+        self.tz_name = content.settings.get('TIMEZONE', None)
+        self.follow = content.settings['GIT_HISTORY_FOLLOWS_RENAME']
+
+    @memoized
+    def is_committed(self):
+        '''
+        Is committed
+        '''
+        return len(self.get_commits()) > 0
+
+    @memoized
+    def is_modified(self):
+        '''
+        Has content been modified since last commit
+        '''
+        return self.git.is_file_modified(self.content.source_path)
+
+    @memoized
+    def is_managed_by_git(self):
+        '''
+        Is content stored in a file managed by git
+        '''
+        return self.git.is_file_managed_by_git(self.content.source_path)
+
+    @memoized
+    def get_commits(self):
+        '''
+        Get all commits involving this filename
+        :returns: List of commits newest to oldest
+        '''
+        if not self.is_managed_by_git():
+            return []
+        return self.git.get_commits(self.content.source_path, self.follow)
+
+    @memoized
+    def get_oldest_commit(self):
+        '''
+        Get oldest commit involving this file
+
+        :returns: Oldest commit
+        '''
+        return self.git.get_commits(self.content.source_path, self.follow)[-1]
+
+    @memoized
+    def get_newest_commit(self):
+        '''
+        Get oldest commit involving this file
+
+        :returns: Newest commit
+        '''
+        return self.git.get_commits(self.content.source_path, follow=False)[0]
+
+    @memoized
+    def get_oldest_filename(self):
+        '''
+        Get the original filename of this content. Implies follow
+        '''
+        commit_and_name_iter = self.git.get_commits_and_names_iter(
+            self.content.source_path)
+        _commit, name = next(commit_and_name_iter)
+        return name
+
+    @memoized
+    def get_oldest_commit_date(self):
+        '''
+        Get datetime of oldest commit involving this file
+
+        :returns: Datetime of oldest commit
+        '''
+        oldest_commit = self.get_oldest_commit()
+        return self.git.get_commit_date(oldest_commit, self.tz_name)
+
+    @memoized
+    def get_newest_commit_date(self):
+        '''
+        Get datetime of newest commit involving this file
+
+        :returns: Datetime of newest commit
+        '''
+        newest_commit = self.get_newest_commit()
+        return self.git.get_commit_date(newest_commit, self.tz_name)
diff --git a/pelican-plugins/filetime_from_git/git_wrapper.py b/pelican-plugins/filetime_from_git/git_wrapper.py
new file mode 100644
index 0000000000000000000000000000000000000000..4c3107046f07507035d8729adec3dd3824dc4acd
--- /dev/null
+++ b/pelican-plugins/filetime_from_git/git_wrapper.py
@@ -0,0 +1,169 @@
+# -*- coding: utf-8 -*-
+"""
+Wrap python git interface for compatibility with older/newer version
+"""
+try:
+    from itertools import zip_longest
+except ImportError:
+    from six.moves import zip_longest
+import logging
+import os
+from time import mktime
+from datetime import datetime
+from pelican.utils import set_date_tzinfo
+from git import Git, Repo
+
+DEV_LOGGER = logging.getLogger(__name__)
+
+
+def grouper(iterable, n, fillvalue=None):
+    '''
+    Collect data into fixed-length chunks or blocks
+    '''
+    # grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx
+    args = [iter(iterable)] * n
+    return zip_longest(fillvalue=fillvalue, *args)
+
+
+class _GitWrapperCommon(object):
+    '''
+    Wrap git module to provide a more stable interface across versions
+    '''
+    def __init__(self, repo_path):
+        self.git = Git()
+        self.git.update_environment(
+            GIT_CONFIG_NOSYSTEM='true',
+            HOME=os.getcwd(),
+            XDG_CONFIG_HOME=os.getcwd()
+        )
+        self.repo = Repo(os.path.abspath("."), search_parent_directories=True)
+
+    def is_file_managed_by_git(self, path):
+        '''
+        :param path: Path to check
+        :returns: True if path is managed by git
+        '''
+        status, _stdout, _stderr = self.git.execute(
+            ['git', 'ls-files', path, '--error-unmatch'],
+            with_extended_output=True,
+            with_exceptions=False)
+        return status == 0
+
+    def is_file_modified(self, path):
+        '''
+        Does a file have local changes not yet committed
+
+        :returns: True if file has local changes
+        '''
+        status, _stdout, _stderr = self.git.execute(
+            ['git', 'diff', '--quiet', 'HEAD', path],
+            with_extended_output=True,
+            with_exceptions=False)
+        return status != 0
+
+    def get_commits_following(self, path):
+        '''
+        Get all commits including path following the file through
+        renames
+
+        :param path: Path which we will find commits for
+        :returns: Sequence of commit objects. Newest to oldest
+        '''
+        return [
+            commit for commit, _ in self.get_commits_and_names_iter(
+                path)]
+
+    def get_commits_and_names_iter(self, path):
+        '''
+        Get all commits including a given path following renames
+        '''
+        log_result = self.git.log(
+            '--pretty=%H',
+            '--follow',
+            '--name-only',
+            '--',
+            path).splitlines()
+
+        for commit_sha, _, filename in grouper(log_result, 3):
+            yield self.repo.commit(commit_sha), filename
+
+    def get_commits(self, path, follow=False):
+        '''
+        Get all commits including path
+
+        :param path: Path which we will find commits for
+        :param bool follow: If True we will follow path through renames
+
+        :returns: Sequence of commit objects. Newest to oldest
+        '''
+        if follow:
+            return self.get_commits_following(path)
+        else:
+            return self._get_commits(path)
+
+
+class _GitWrapperLegacy(_GitWrapperCommon):
+    def _get_commits(self, path):
+        '''
+        Get all commits including path without following renames
+
+        :param path: Path which we will find commits for
+
+        :returns: Sequence of commit objects. Newest to oldest
+        '''
+        return self.repo.commits(path=path)
+
+    @staticmethod
+    def get_commit_date(commit, tz_name):
+        '''
+        Get datetime of commit comitted_date
+        '''
+        return set_date_tzinfo(
+            datetime.fromtimestamp(mktime(commit.committed_date)),
+            tz_name=tz_name)
+
+
+class _GitWrapper(_GitWrapperCommon):
+    def _get_commits(self, path):
+        '''
+        Get all commits including path without following renames
+
+        :param path: Path which we will find commits for
+
+        :returns: Sequence of commit objects. Newest to oldest
+
+        .. NOTE ::
+            If this fails it could be that your gitpython version is out of
+            sync with the git binary on your distro.
+            Make sure you use the correct gitpython version.
+
+            Alternatively enabling GIT_FILETIME_FOLLOW may also make your
+            problem go away.
+        '''
+        return list(self.repo.iter_commits(paths=path))
+
+    @staticmethod
+    def get_commit_date(commit, tz_name):
+        '''
+        Get datetime of commit comitted_date
+        '''
+        return set_date_tzinfo(
+            datetime.fromtimestamp(commit.committed_date),
+            tz_name=tz_name)
+
+
+_wrapper_cache = {}
+
+
+def git_wrapper(path):
+    '''
+    Get appropriate wrapper factory and cache instance for path
+    '''
+    path = os.path.abspath(path)
+    if path not in _wrapper_cache:
+        if hasattr(Repo, 'commits'):
+            _wrapper_cache[path] = _GitWrapperLegacy(path)
+        else:
+            _wrapper_cache[path] = _GitWrapper(path)
+
+    return _wrapper_cache[path]
diff --git a/pelican-plugins/filetime_from_git/registration.py b/pelican-plugins/filetime_from_git/registration.py
new file mode 100644
index 0000000000000000000000000000000000000000..e91d25476e599abe81fa104198c9c00a9551e433
--- /dev/null
+++ b/pelican-plugins/filetime_from_git/registration.py
@@ -0,0 +1,30 @@
+# -*- coding: utf-8 -*-
+"""
+Handle registration and setup for plugin
+"""
+import logging
+from blinker import signal
+from .content_adapter import GitContentAdapter
+from pelican import signals
+
+DEV_LOGGER = logging.getLogger(__name__)
+
+content_git_object_init = signal('content_git_object_init')
+
+def send_content_git_object_init(content):
+    content_git_object_init.send(content, git_content=GitContentAdapter(content))
+
+
+def setup_option_defaults(pelican_inst):
+    pelican_inst.settings.setdefault('GIT_FILETIME_FROM_GIT', True)
+    pelican_inst.settings.setdefault('GIT_HISTORY_FOLLOWS_RENAME', True)
+    pelican_inst.settings.setdefault('GIT_SHA_METADATA', True)
+    pelican_inst.settings.setdefault('GIT_GENERATE_PERMALINK', False)
+
+
+def register():
+    signals.content_object_init.connect(send_content_git_object_init)
+    signals.initialized.connect(setup_option_defaults)
+
+    # Import actions
+    from . import actions
diff --git a/pelican-plugins/filetime_from_git/requirements.txt b/pelican-plugins/filetime_from_git/requirements.txt
new file mode 100755
index 0000000000000000000000000000000000000000..59348f98ea5fa7986978f820a0a0b03ad81e7528
--- /dev/null
+++ b/pelican-plugins/filetime_from_git/requirements.txt
@@ -0,0 +1 @@
+gitpython
diff --git a/pelican-plugins/filetime_from_git/utils.py b/pelican-plugins/filetime_from_git/utils.py
new file mode 100644
index 0000000000000000000000000000000000000000..d5bd52fe2ebae6b1f19a51eecad88766af9b9ce2
--- /dev/null
+++ b/pelican-plugins/filetime_from_git/utils.py
@@ -0,0 +1,39 @@
+# -*- coding: utf-8 -*-
+"""
+Utility functions
+"""
+from datetime import datetime
+import logging
+from pelican.utils import set_date_tzinfo
+
+DEV_LOGGER = logging.getLogger(__name__)
+
+
+STRING_BOOLS = {
+    'yes': True,
+    'no': False,
+    'true': True,
+    'false': False,
+    '0': False,
+    '1': True,
+    'on': True,
+    'off': False,
+}
+
+
+def string_to_bool(string):
+    '''
+    Convert a string to a bool based
+    '''
+    return STRING_BOOLS[string.strip().lower()]
+
+
+def datetime_from_timestamp(timestamp, content):
+    """
+    Helper function to add timezone information to datetime,
+    so that datetime is comparable to other datetime objects in recent versions
+    that now also have timezone information.
+    """
+    return set_date_tzinfo(
+        datetime.fromtimestamp(timestamp),
+        tz_name=content.settings.get('TIMEZONE', None))
diff --git a/pelican-plugins/filetime_from_hg/README.rst b/pelican-plugins/filetime_from_hg/README.rst
new file mode 100644
index 0000000000000000000000000000000000000000..cd20007033b0d574086ad8ea9882d649ee4e72f0
--- /dev/null
+++ b/pelican-plugins/filetime_from_hg/README.rst
@@ -0,0 +1,41 @@
+Use Mercurial commit to determine page date
+===========================================
+
+If your blog content is versioned via Mercurial, this plugin will set
+articles' and pages' ``metadata['date']`` to correspond to that of the
+hg commit.  This plugin depends on the ``hglib`` python package,
+which can be installed via::
+
+    sudo apt-get install python-hglib
+
+or::
+
+    pip install hglib
+
+The date is determined via the following logic:
+
+* if a file is not tracked by hg, or a file is added but never committed
+    - metadata['date'] = filesystem time
+    - metadata['modified'] = filesystem time
+* if a file is tracked, but no changes in working directory
+    - metadata['date'] = first commit time
+    - metadata['modified'] = last commit time
+* if a file is tracked, and has changes in working directory
+    - metadata['date'] = first commit time
+    - metadata['modified'] = filesystem time
+
+When this module is enabled, ``date`` and ``modified`` will be determined
+by hg status; no need to manually set in article/page metadata. And
+operations like copy and move will not affect the generated results.
+
+If you don't want a given article or page to use the hg time, set the
+metadata to ``hgtime: off`` to disable it.
+
+You can also set ``HG_FILETIME_FOLLOW`` to ``True`` in your settings to
+make the plugin follow file renames — i.e., ensure the creation date matches
+the original file creation date, not the date it was renamed.
+
+Credits
+=======
+
+This plugin is based on filetime_from_git.
diff --git a/pelican-plugins/filetime_from_hg/__init__.py b/pelican-plugins/filetime_from_hg/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..3ae2de77bf7d10c75c953edeb19684e676e5d9d4
--- /dev/null
+++ b/pelican-plugins/filetime_from_hg/__init__.py
@@ -0,0 +1 @@
+from .filetime_from_hg import *
diff --git a/pelican-plugins/filetime_from_hg/filetime_from_hg.py b/pelican-plugins/filetime_from_hg/filetime_from_hg.py
new file mode 100755
index 0000000000000000000000000000000000000000..77e0ae52c6e5bd3546ba03bef810bee567232b3d
--- /dev/null
+++ b/pelican-plugins/filetime_from_hg/filetime_from_hg.py
@@ -0,0 +1,81 @@
+# -*- coding: utf-8 -*-
+"""
+This plugin is mostly a copy of the filetime_from_git plugin
+by Zhang Cheng <StephenPCG@gmail.com> and others.
+
+Copyright (c) David Douard <david.douard@sdfa3.org>
+
+Compute Date and Modified metadata from Mercurial revisions.
+"""
+
+import os
+from pelican import signals, contents
+from pelican.utils import strftime, set_date_tzinfo
+from datetime import datetime
+
+import hglib
+
+def datetime_from_timestamp(timestamp, content):
+    """
+    Helper function to add timezone information to datetime,
+    so that datetime is comparable to other datetime objects in recent versions
+    that now also have timezone information.
+    """
+    return set_date_tzinfo(
+        datetime.fromtimestamp(timestamp),
+        tz_name=content.settings.get('TIMEZONE', None))
+
+
+def filetime_from_hg(content):
+    if isinstance(content, contents.Static):
+        return
+    if 'date' in content.metadata:
+        # if user did explicitely set a date, do not overwrite it
+        return
+    repo = hglib.open('.')
+    tz_name = content.settings.get('TIMEZONE', None)
+
+    hgtime = content.metadata.get('hgtime', 'yes').lower()
+    if hgtime in ('no', 'off', 'false', '0'):
+        return
+
+    # 1. file is not managed by hg
+    #    date: fs time
+    # 2. file is staged, but has no commits
+    #    date: fs time
+    # 3. file is managed, and clean
+    #    date: first commit time, update: last commit time or None
+    # 4. file is managed, but dirty
+    #    date: first commit time, update: fs time
+    path = content.source_path
+    root = repo.root()
+    filelog = repo.log(revrange='.:0', files=[path,],
+                       follow=content.settings.get('HG_FILETIME_FOLLOW', False))
+    if filelog:
+        # has commited
+        content.date = set_date_tzinfo(filelog[-1][6], tz_name)
+        if path in [os.path.join(root, mfile) for flag, mfile in repo.status(modified=True)]:
+            # file is modified in the wd
+            content.modified = datetime_from_timestamp(
+                os.stat(path).st_ctime, content)
+        else:
+            # file is not changed
+            if len(filelog) > 1:
+                content.modified = set_date_tzinfo(filelog[0][6], tz_name)
+    else:
+        # file is not managed by hg
+        content.date = datetime_from_timestamp(os.stat(path).st_ctime, content)
+
+    if not hasattr(content, 'modified'):
+        content.modified = content.date
+
+    content.locale_date = strftime(content.date, content.date_format)
+    content.locale_modified = strftime(content.modified, content.date_format)
+    # ensure the metadata directory is synchronized. Might be used by
+    # some other plugin (eg. series)
+    content.metadata['modified'] = content.modified
+    content.metadata['date'] = content.date
+
+
+def register():
+    signals.content_object_init.connect(filetime_from_hg)
diff --git a/pelican-plugins/filetime_from_hg/requirements.txt b/pelican-plugins/filetime_from_hg/requirements.txt
new file mode 100755
index 0000000000000000000000000000000000000000..1b4d43b09324b4f16a8d64a31d00157c6870e320
--- /dev/null
+++ b/pelican-plugins/filetime_from_hg/requirements.txt
@@ -0,0 +1 @@
+python-hglib
\ No newline at end of file
diff --git a/pelican-plugins/footer_insert/README.md b/pelican-plugins/footer_insert/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..9521060df62273a2f2d9c7673a40b8cd4ced1a65
--- /dev/null
+++ b/pelican-plugins/footer_insert/README.md
@@ -0,0 +1,17 @@
+# Footer Insert
+
+This plugin allows you to insert a `FOOTER_INSERT_HTML` to the end of the blog.
+
+eg.  add authors / blog infomation to every blog.
+
+## Usage
+
+1. Insert `FOOTER_INSERT_HTML` to your `pelicanconf.py`. You can use
+title / url / author / authors / slug / category / summary
+/ date infomation in the config like this: `%(title)s`.
+2. Insert this code to your artical template file, eg. `templates/article.html`:
+```
+{% if article.footer_insert_html %}
+  {{ article.footer_insert_html }}
+{% endif %}
+```
diff --git a/pelican-plugins/footer_insert/__init__.py b/pelican-plugins/footer_insert/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..0b673a705f0390359a59bd206e30ffc055d480d5
--- /dev/null
+++ b/pelican-plugins/footer_insert/__init__.py
@@ -0,0 +1 @@
+from .footer_insert import *
diff --git a/pelican-plugins/footer_insert/footer_insert.py b/pelican-plugins/footer_insert/footer_insert.py
new file mode 100644
index 0000000000000000000000000000000000000000..3647a77b2589c0f9e791d886140fc2aebc4645dc
--- /dev/null
+++ b/pelican-plugins/footer_insert/footer_insert.py
@@ -0,0 +1,30 @@
+"""
+Footer Insert
+"""
+
+from pelican import signals
+from pelican.contents import Content, Article
+
+
+def add_footer(content):
+    if not isinstance(content, Article):
+        return
+    
+    if 'FOOTER_INSERT_HTML' not in content.settings:
+        return
+    data_dict = {
+        'title': content.title,
+        'url': content.url,
+        'author': content.author.name,
+        'authors': ','.join([x.name for x in content.authors]),
+        'slug': content.slug,
+        'category': content.category,
+        'summary': content.summary,
+    }
+    if hasattr(content, 'date'):
+        data_dict['date'] = content.date
+    foot_insert_html = content.settings['FOOTER_INSERT_HTML'] % data_dict
+    content.footer_insert_html = foot_insert_html
+
+def register():
+    signals.content_object_init.connect(add_footer)
diff --git a/pelican-plugins/gallery/README.md b/pelican-plugins/gallery/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..69d66db364408825e6d4510294bcf4da2cd0d1c9
--- /dev/null
+++ b/pelican-plugins/gallery/README.md
@@ -0,0 +1,91 @@
+Gallery
+==================
+
+* Allows an article to contain an album of pictures.
+* All albums can also be syndicated into a central gallery page.
+
+## How to Use
+
+1. Group images into folders, with each folder representing an album.
+2. Place all album folders within a folder, which should reside under content.
+3. Insert `GALLERY_PATH` to your `pelicanconf.py` and set a path to that folder. By default it is `images/gallery`.
+
+		./content/images/gallery/{your albums}
+	
+### Articles
+
+Attach an album to an article/post by placing a gallery metadata tag with the name of the album.
+
+	gallery:album_name
+    
+The template has access to the album name.
+
+	article.album
+
+And the filename of images within an album.
+
+	article.albumimages
+
+### Gallery Page
+
+Create a page and a gallery template (named gallery.html). And inform pelican to use the gallery template for the page.
+
+	template:gallery
+    
+The template has access to a dictionary of lists.  
+The dictionary key is the name of the album and the lists contain the filenames.
+
+	page.gallery
+	
+## Examples
+
+### article.html
+
+	<h2><a href="{{ SITEURL }}/pages/gallery.html#{{ article.album }}">{{ article.album }}</a></h2>
+	    <ul>
+		{% for image in article.galleryimages %}
+		<li><a class="{{ article.album }} cboxElement" href="{{ SITEURL }}/static/images/gallery/{{ article.album }}/{{ image }}"><img src="{{ SITEURL }}/static/images/gallery200x200/{{ article.album }}/{{ image }}"></a></li>
+		{% endfor %}
+	    </ul>
+		
+### gallery.html
+
+	{% for album, images in page.gallery.iteritems() %}
+	<h2><a name="{{ album }}">{{ album }}</a></h2>
+	<ul>
+	    {% for image in images %}
+	    <li><a class="{{ album }} cboxElement" href="{{ SITEURL }}/static/images/gallery/{{album}}/{{ image }}" title="{{ image }}"><img src="{{ SITEURL }}/static/images/gallery200x200/{{album}}/{{ image }}"></a></li>
+	    {% endfor %}
+	</ul>
+	{% endfor %}
+
+### posts/foo.md
+
+	title:Foo
+	gallery:albumname
+	
+### pages/gallery.md
+
+	title:All Images
+	template:gallery
+	
+## Reasoning
+
+The album name and filenames are returned as opposed to the direct path to the images,
+to allow flexibility of different thumbnail sizes to be used it different locations of a website.
+
+	href="{{ SITEURL }}/static/images/gallery/{{album}}/{{ image }}"
+	href="{{ SITEURL }}/static/images/gallery200x200/{{album}}/{{ image }}"
+	
+It also allows a thumbnail to link to the full image,
+as well as the filename extension to be stripped and the title of an image to be displayed along side the title of an album.
+
+## Recommendation
+
+It is recommended to use this extension along with the thumbnailer plugin.
+
+	RESIZE = [
+            ('gallery', False, 200,200),
+          ]
+
+You may also wish to use this along with a gallery plugin such as [Colorbox](http://www.jacklmoore.com/colorbox/).
diff --git a/pelican-plugins/gallery/__init__.py b/pelican-plugins/gallery/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..c6059996f5112ae5deba795bfc48ea1dd15fc174
--- /dev/null
+++ b/pelican-plugins/gallery/__init__.py
@@ -0,0 +1 @@
+from .gallery import *
diff --git a/pelican-plugins/gallery/gallery.py b/pelican-plugins/gallery/gallery.py
new file mode 100644
index 0000000000000000000000000000000000000000..3c56c81c4b267cf297b49db7bcaf71b20d380c21
--- /dev/null
+++ b/pelican-plugins/gallery/gallery.py
@@ -0,0 +1,75 @@
+import os
+from pelican import signals
+
+
+def get_content_path(pelican):
+    return pelican.settings.get('PATH')
+
+
+def get_gallery_path(pelican):
+    gallery_path = pelican.settings.get('GALLERY_PATH', 'images/gallery')
+    content_path = get_content_path(pelican)
+
+    return os.path.join(content_path, gallery_path)
+
+
+def add_gallery_post(generator):
+    gallerycontentpath = get_gallery_path(generator)
+
+    for article in generator.articles:
+        if 'gallery' in article.metadata.keys():
+            album = article.metadata.get('gallery')
+            galleryimages = []
+
+            articlegallerypath=os.path.join(gallerycontentpath, album)
+
+            if(os.path.isdir(articlegallerypath)):
+                for i in os.listdir(articlegallerypath):
+                    if not i.startswith('.') and os.path.isfile(os.path.join(os.path.join(gallerycontentpath, album), i)):
+                        galleryimages.append(i)
+
+            article.album = album
+            article.galleryimages = sorted(galleryimages)
+
+
+def add_gallery_page(generator):
+    gallerycontentpath = get_gallery_path(generator)
+
+    for page in generator.pages:
+        if 'gallery' in page.metadata.keys():
+            album = page.metadata.get('gallery')
+            galleryimages = []
+
+            pagegallerypath=os.path.join(gallerycontentpath, album)
+
+            if(os.path.isdir(pagegallerypath)):
+                for i in os.listdir(pagegallerypath):
+                    if not i.startswith('.') and os.path.isfile(os.path.join(os.path.join(gallerycontentpath, album), i)):
+                        galleryimages.append(i)
+
+            page.album = album
+            page.galleryimages = sorted(galleryimages)
+
+
+def generate_gallery_page(generator):
+    gallerycontentpath = get_gallery_path(generator)
+
+    for page in generator.pages:
+        if page.metadata.get('template') == 'gallery':
+            gallery = dict()
+
+            for a in os.listdir(gallerycontentpath):
+                if not a.startswith('.') and os.path.isdir(os.path.join(gallerycontentpath, a)):
+
+                    for i in os.listdir(os.path.join(gallerycontentpath, a)):
+                        if not a.startswith('.') and os.path.isfile(os.path.join(os.path.join(gallerycontentpath, a), i)):
+                            gallery.setdefault(a, []).append(i)
+                    gallery[a].sort()
+
+            page.gallery=gallery
+
+
+def register():
+    signals.article_generator_finalized.connect(add_gallery_post)
+    signals.page_generator_finalized.connect(generate_gallery_page)
+    signals.page_generator_finalized.connect(add_gallery_page)
diff --git a/pelican-plugins/gist_directive/README.rst b/pelican-plugins/gist_directive/README.rst
new file mode 100644
index 0000000000000000000000000000000000000000..67aebada200fbd91e09c408181f0c8d2a6f1e971
--- /dev/null
+++ b/pelican-plugins/gist_directive/README.rst
@@ -0,0 +1,18 @@
+Pelican ``gist_directive`` plugin
+=================================
+
+This plugin adds a ``gist`` reStructuredText directive. Eg::
+
+   .. gist:: ionelmc/eb11afbcca187bad5273 setup.py python
+   
+It will download (with cache) and include the gist contents in the document.
+
+.. note::
+
+    It also supports embedding content from github directly.
+    
+    Example, to include ``https://raw.githubusercontent.com/ionelmc/cookiecutter-pylibrary/master/%7B%7Bcookiecutter.repo_name%7D%7D/setup.py``::
+    
+        .. github:: ionelmc/cookiecutter-pylibrary master/{{cookiecutter.repo_name}}/setup.py
+    
+    
\ No newline at end of file
diff --git a/pelican-plugins/gist_directive/__init__.py b/pelican-plugins/gist_directive/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..81abad2cc11140d3cacb7b96cd0bc726e825eb7c
--- /dev/null
+++ b/pelican-plugins/gist_directive/__init__.py
@@ -0,0 +1 @@
+from gist_directive import *
diff --git a/pelican-plugins/gist_directive/gist_directive.py b/pelican-plugins/gist_directive/gist_directive.py
new file mode 100644
index 0000000000000000000000000000000000000000..ea381b934c1acad08d3789363e122764d89616f8
--- /dev/null
+++ b/pelican-plugins/gist_directive/gist_directive.py
@@ -0,0 +1,73 @@
+try:
+    from urllib.request import urlopen
+except ImportError:
+    from urllib import urlopen
+import os
+import io
+
+from docutils.parsers.rst import directives
+from pelican.rstdirectives import Pygments
+
+
+def fetch(gid, filename, typ):
+    if not os.path.exists('.gists'):
+        os.mkdir('.gists')
+    key = os.path.join('.gists', ("%s/%s/%s" % (typ, gid, filename)).replace('/', ';'))
+    if os.path.isfile(key):
+        print('LOAD-CACHED:', key)
+        return io.open(key, encoding='utf8').read()
+    else:
+        if typ == 'gist':
+            url = 'https://gist.githubusercontent.com/%s/raw/%s' % (gid, filename)
+        elif typ == 'github':
+            url = 'https://raw.githubusercontent.com/%s/%s' % (gid, filename)
+        else:
+            raise RuntimeError(typ)
+        print('FETCHING:', url)
+        fp = urlopen(url)
+        if fp.getcode() != 200:
+            print('FAILED TO FETCH:', url)
+            print('   status code:', fp.getcode())
+            print('   response:')
+            try:
+                print(fp.read())
+            finally:
+                raise SystemExit()
+        data = fp.read()
+        with open(key, 'wb') as fh:
+            fh.write(data)
+        return data.decode('utf8')
+
+
+class Gist(Pygments):
+    """ Embed Github Gist Snippets in rst text
+
+        GIST_ID and FILENAME are required.
+
+        Usage:
+          .. gist:: GIST_ID FILENAME
+
+    """
+
+    required_arguments = 1
+    optional_arguments = 2
+    has_content = False
+    gist_type = 'gist'
+
+    def run(self):
+        gist = self.arguments[0]
+        filename = self.arguments[1] if len(self.arguments) > 1 else ''
+        language = self.arguments[2] if len(self.arguments) > 2 else None
+        self.arguments = [language]
+
+        self.content = fetch(gist, filename, self.gist_type).splitlines()
+        return super(Gist, self).run()
+
+
+class Github(Gist):
+    gist_type = 'github'
+
+
+def register():
+    directives.register_directive('gist', Gist)
+    directives.register_directive('github', Github)
diff --git a/pelican-plugins/github-wiki/ReadMe.md b/pelican-plugins/github-wiki/ReadMe.md
new file mode 100644
index 0000000000000000000000000000000000000000..bdbee0f945998c9e4838546d17f82334579ffc78
--- /dev/null
+++ b/pelican-plugins/github-wiki/ReadMe.md
@@ -0,0 +1,43 @@
+# GitHub Wiki
+A plugin to convert a flat GitHub wiki into a structured 'read-only' wiki on your pelican site.
+
+## Usage
+
+The plugin looks for a GitHub style wiki (flat collection of md files) at `content\wiki`. You can clone a wiki with:
+
+    git clone https://github.com/YOUR_USERNAME/YOUR_REPOSITORY.wiki.git
+
+And you can also do clever things to set this up as a submodule of your main pelican site repo.
+
+The plugin needs a `wikiarticle.html` template in your theme templates folder. An example template file is provided. 
+
+The resulting wiki will be available at `output\wiki`. 
+
+## Structure
+
+By default the generated wiki will also be flat with no heirarchy to the pages. This plugin allows you to automatically generate a virtual file structure.
+
+In order to provide a structure to the wiki each page can be given the `Path:` metadata.
+
+    Path: foo/bar/baz
+
+This will allow a correctly set up template to generate breadcrumbs and a structured menu where this page will be under the `foo/bar/baz`. Note: The plugin does not actually make this folder structure in the output (this is to make is easier to associate the 
+generated pages with their original wiki pages). 
+
+If a page has the same name and location in the structure as a virtual folder (i.e For the path given above, a file called `bar.md` with the path `Path: foo`) then the breadcrumbs and menu can link to that page as though it were the index of that vritual folder.
+
+## Template
+
+The template is given the following variables.
+
+- `content`: The template has the usual `content` variable available for the converted html of the wiki page. 
+- `breadcrumbs`: A list of tuples describing a 'path' to the current wiki page. The first value is the name of a parent page (in order). The second value is either 'a' or 'p'. 'a' means that the parent page exists and can be linked to, 'p' means the parent page 
+doesn't 
+exist and is just there for descriptibe purposes.
+- `links`: A list of tuples describing a site map of the wiki. The first value is the name of the page, the second value is either 'indexdir' or 'noindexdir' if the page has or does not have subpages respectively, and the third value is the level of the page 
+within the wiki (how many parents it has).
+
+Also provided is a simple javascript file to make the structured menu in the example template openable/closable. 
+
+Though you cannot edit this 'wiki' directly from your site an example of how to emulate wiki editing behaviour is shown in the example template. You can link directly to the github edit page for the wiki page you're on allowing your Github contributers to very 
+easily edit pages. Combined with hooks to redeploy your wiki this can emulate a real wiki quite well.
diff --git a/pelican-plugins/github-wiki/__init__.py b/pelican-plugins/github-wiki/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..9a005dc531f27404f9147b1e94ab89ff88e9b5bb
--- /dev/null
+++ b/pelican-plugins/github-wiki/__init__.py
@@ -0,0 +1 @@
+from .wiki import *
diff --git a/pelican-plugins/github-wiki/wiki.js b/pelican-plugins/github-wiki/wiki.js
new file mode 100644
index 0000000000000000000000000000000000000000..74916acd366e7fee002ff0babcad743d9860a17c
--- /dev/null
+++ b/pelican-plugins/github-wiki/wiki.js
@@ -0,0 +1,13 @@
+window.addEventListener("load", function() {
+  document.querySelector(".wiki-nav-button").addEventListener('click', function(event) {
+    document.querySelector(".wiki-links").classList.toggle('nodisplay');
+    event.target.text = event.target.text == "Expand Menu" ? "Collapse Menu" : "Expand Menu";
+  }, false);
+  var expanders = document.querySelectorAll(".wiki-dir-expander");
+  for (i = 0; i < expanders.length; ++i) {
+    expanders[i].addEventListener('click', function(event) {
+      event.target.parentElement.nextSibling.nextSibling.classList.toggle("nodisplay");
+      event.target.text = event.target.text == "+" ? "-" : "+";
+    }, false);
+  };
+}, false);
diff --git a/pelican-plugins/github-wiki/wiki.py b/pelican-plugins/github-wiki/wiki.py
new file mode 100644
index 0000000000000000000000000000000000000000..a6c11c4f87150dc993b657784c8f781040e21ae1
--- /dev/null
+++ b/pelican-plugins/github-wiki/wiki.py
@@ -0,0 +1,98 @@
+from pelican import signals, utils
+from collections import namedtuple, OrderedDict
+import os
+import re
+import logging
+
+def add_to_structure(structure, path_list):
+    folders = structure["folders"]
+    articles = structure["articles"]
+    subdir = path_list[0]
+    rest = path_list[1:]
+
+    if len(rest) > 1:
+        if subdir in folders:
+            folders[subdir] = add_to_structure(folders[subdir], rest)
+        else:
+            folders[subdir] = add_to_structure({"folders":{},"articles":[]}, rest)
+    else:
+       if subdir in folders:
+           folders[subdir]["articles"] += rest
+       else:
+           folders[subdir] = { "folders": {}, "articles": rest }
+
+    return { "folders": folders, "articles": articles }
+
+def parse_wiki_pages(generator):
+    settings = generator.settings
+    readers = generator.readers
+    contentpath = settings.get("PATH", "content")
+
+    root = os.path.realpath(
+        os.path.abspath(os.path.join(contentpath, "wiki", "")))
+
+    wikilist = []
+    structure = {"folders":{}, "articles":[]}
+    for (dirname, dirnames, filenames) in os.walk(root):
+        for filename in filenames:
+            if ".git" not in dirname and ".git" not in filename:
+                parsedfile = readers.read_file(dirname, filename)
+                metadata = parsedfile.metadata
+                try:
+                    path = metadata["path"]
+                    org = metadata["path"].split("/")
+                except KeyError:
+                    path = ""
+                    org = []
+                org.append(filename)
+                structure = add_to_structure(structure, org)
+                wikilist.append((path,filename,parsedfile))
+
+    structure = { "articles": structure["folders"]['']["articles"], "folders":structure["folders"] }
+
+    del(structure["folders"][""])
+    wikilist.sort()
+    generator.context['wikilist'] = wikilist
+    generator.context['wiki'] = structure
+
+
+def parse_dict(structure, level, nice_list):
+    folders = OrderedDict(sorted(structure["folders"].items(), key=lambda t: t[0]))
+    articles = sorted(structure["articles"])
+    for key in folders.keys():
+        if key + ".md" in articles:
+            nice_list.append((key, "indexdir", level))
+            articles.remove(key + ".md")
+        else:
+            nice_list.append((key, "noindexdir", level))
+        nice_list = parse_dict(folders[key], level + 1, nice_list)
+    for item in articles:
+        nice_list.append((item, "article", level))
+    return nice_list
+
+def generate_wiki_pages(generator, writer):
+    wiki_list = generator.context['wikilist']
+    structure = generator.context['wiki']
+    template = generator.get_template('wikiarticle')
+    nice_list = parse_dict(structure, 0, [])
+
+    for page in wiki_list:
+        filename = os.path.join('wiki', page[1].replace('.md', '.html'))
+        content = page[2].content
+        metadata = page[2].metadata
+        path = page[0]
+        breadcrumbs = []
+        for name in path.split('/'):
+            name_match = [item[1] for item in nice_list if item[0] == name]
+            if len(name_match) > 0 and name_match[0] == "indexdir":
+                breadcrumbs.append((name, "a"))
+            else:
+                breadcrumbs.append((name, "p"))
+        file = page[1]
+        writer.write_file(filename, template, generator.context,
+                          meta=metadata, content=content, file=file, path=path, links=nice_list, breadcrumbs=breadcrumbs)
+
+
+def register():
+    signals.article_generator_finalized.connect(parse_wiki_pages)
+    signals.article_writer_finalized.connect(generate_wiki_pages)
diff --git a/pelican-plugins/github-wiki/wikiarticle.html b/pelican-plugins/github-wiki/wikiarticle.html
new file mode 100644
index 0000000000000000000000000000000000000000..46f3119ed522bdf8e2315d3eb4f2eb220495e91b
--- /dev/null
+++ b/pelican-plugins/github-wiki/wikiarticle.html
@@ -0,0 +1,66 @@
+{% extends "base.html" %}
+{% block title %}{{ meta.title }} - {{ SITENAME }}{% endblock %}
+
+{% block head %}<script src="{{ SITEURL }}/theme/js/wiki.js"></script>{% endblock %}
+
+{% block banner %}{{ meta.title }}{% endblock banner %}
+
+{% block content %}
+
+<div class="article-content">
+
+  <h1 class="article-title">{{ meta.title }}</h1>
+
+  <nav><a class="wiki-nav-button">Expand Menu</a> / <a class="wiki-crumbs" href="{{ SITEURL }}/wiki/Home.html">Home</a>
+  {% set total_path = SITEURL + "/wiki" %}
+  {% for page, type in breadcrumbs %}
+    {% set total_path = total_path + "/" + page %}
+    {% if type == "a" %}
+      / <a class="wiki-crumbs" href="{{ total_path }}">{{ page }}</a>
+    {% elif type == "p" %}
+      / <p class="wiki-crumbs">{{ page }}</p>
+    {% endif %}
+  {% endfor %}</nav>
+  <nav class="wiki-nav">
+    <ul class="wiki-links nodisplay">
+    {% set vars = {'level': 0} %}
+    {% for link in links %}
+      {% if link[0] != "_Footer.md" %}
+        {% set linklevel = link[2] %}
+        {% if linklevel > vars.level %}
+          {% if vars.update({'level':linklevel}) %} {% endif %}
+          <ul class="nodisplay">
+        {% elif linklevel < vars.level %}
+          {% for i in range(vars.level - linklevel) %}
+            </ul>
+          {% endfor %}
+          {% if vars.update({'level':linklevel}) %} {% endif %}
+        {% endif %}
+        {% if not loop.last %}
+          {% if links[loop.index][2] > vars.level %}
+            {% if link[1] == "indexdir" %}
+              <li class="wiki-link"><a href="{{ SITEURL }}/wiki/{{ link[0].replace('.md','.html') }}">{{ link[0].replace('.md','') }}</a> <a class="wiki-dir-expander">+</a></li>
+            {% else %}
+              <li class="wiki-link">{{ link[0].replace('.md','') }} <a class="wiki-dir-expander">+</a></li>
+            {% endif %}
+          {% else %}
+            <li class="wiki-link"><a href="{{ SITEURL }}/wiki/{{ link[0].replace('.md','.html') }}">{{ link[0].replace('.md','') }}</a></li>
+          {% endif %}
+        {% else %}
+          <li class="wiki-link"><a href="{{ SITEURL }}/wiki/{{ link[0].replace('.md','.html') }}">{{ link[0].replace('.md','') }}</a></li>
+        {% endif %}
+      {% endif %}
+    {% endfor %}
+    </ul>
+  </nav>
+
+  {{ content }}
+  
+  <p>
+    <a href="https://github.com/YOUR_USER/YOUR_REPO/wiki/{{ file.replace(".md","") }}/_edit">Edit page.</a><br>
+    <a href="https://github.com/YOUR_USER/YOUR_REPO/wiki/_new">Add new page.</a>
+  </p>
+  
+</div>
+
+{% endblock %}
diff --git a/pelican-plugins/github_activity/Readme.rst b/pelican-plugins/github_activity/Readme.rst
new file mode 100644
index 0000000000000000000000000000000000000000..5e17eb0e71d1b7d8ba07da74214a8c217f77ac7b
--- /dev/null
+++ b/pelican-plugins/github_activity/Readme.rst
@@ -0,0 +1,35 @@
+GitHub activity
+---------------
+
+This plugin makes use of the `feedparser`_ library that you'll need to
+install.
+
+Set the ``GITHUB_ACTIVITY_FEED`` parameter to your GitHub activity feed.
+For example, to track Pelican project activity, the setting would be::
+
+     GITHUB_ACTIVITY_FEED = 'https://github.com/getpelican.atom'
+
+If you want to limit the amount of entries to a certain maximum set the
+``GITHUB_ACTIVITY_MAX_ENTRIES`` parameter.
+
+     GITHUB_ACTIVITY_MAX_ENTRIES = 10
+
+On the template side, you just have to iterate over the ``github_activity``
+variable, as in this example::
+
+     {% if GITHUB_ACTIVITY_FEED %}
+        <div class="social">
+                <h2>Github Activity</h2>
+                <ul>
+
+                {% for entry in github_activity %}
+                    <li><b>{{ entry[0] }}</b><br /> {{ entry[1] }}</li>
+                {% endfor %}
+                </ul>
+        </div><!-- /.github_activity -->
+     {% endif %}
+
+``github_activity`` is a list of lists. The first element is the title,
+and the second element is the raw HTML from GitHub.
+
+.. _feedparser: https://crate.io/packages/feedparser/
diff --git a/pelican-plugins/github_activity/__init__.py b/pelican-plugins/github_activity/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..19b6db89062bf653233ca7d96841091f97d42424
--- /dev/null
+++ b/pelican-plugins/github_activity/__init__.py
@@ -0,0 +1 @@
+from .github_activity import *
diff --git a/pelican-plugins/github_activity/github_activity.py b/pelican-plugins/github_activity/github_activity.py
new file mode 100644
index 0000000000000000000000000000000000000000..76e34058e5d038aa27c5b915d29dc47637188935
--- /dev/null
+++ b/pelican-plugins/github_activity/github_activity.py
@@ -0,0 +1,72 @@
+# -*- coding: utf-8 -*-
+
+# NEEDS WORK
+"""
+Copyright (c) Marco Milanesi <kpanic@gnufunk.org>
+
+Github Activity
+---------------
+A plugin to list your Github Activity
+"""
+
+from __future__ import unicode_literals, print_function
+
+import logging
+logger = logging.getLogger(__name__)
+
+from pelican import signals
+
+
+class GitHubActivity():
+    """
+        A class created to fetch github activity with feedparser
+    """
+    def __init__(self, generator):
+        import feedparser
+        self.activities = feedparser.parse(
+            generator.settings['GITHUB_ACTIVITY_FEED'])
+        self.max_entries = generator.settings['GITHUB_ACTIVITY_MAX_ENTRIES'] 
+
+    def fetch(self):
+        """
+            returns a list of html snippets fetched from github actitivy feed
+        """
+
+        entries = []
+        for activity in self.activities['entries']:
+            entries.append(
+                    [element for element in [activity['title'],
+                        activity['content'][0]['value']]])
+
+        return entries[0:self.max_entries]
+
+
+def fetch_github_activity(gen, metadata):
+    """
+        registered handler for the github activity plugin
+        it puts in generator.context the html needed to be displayed on a
+        template
+    """
+
+    if 'GITHUB_ACTIVITY_FEED' in gen.settings.keys():
+        gen.context['github_activity'] = gen.plugin_instance.fetch()
+
+
+def feed_parser_initialization(generator):
+    """
+        Initialization of feed parser
+    """
+
+    generator.plugin_instance = GitHubActivity(generator)
+
+
+def register():
+    """
+        Plugin registration
+    """
+    try:
+        signals.article_generator_init.connect(feed_parser_initialization)
+        signals.article_generator_context.connect(fetch_github_activity)
+    except ImportError:
+        logger.warning('`github_activity` failed to load dependency `feedparser`.'
+                       '`github_activity` plugin not loaded.')
diff --git a/pelican-plugins/global_license/Readme.rst b/pelican-plugins/global_license/Readme.rst
new file mode 100644
index 0000000000000000000000000000000000000000..0b314f067d22ecf792b8c475df42c4978d70f1d2
--- /dev/null
+++ b/pelican-plugins/global_license/Readme.rst
@@ -0,0 +1,6 @@
+Global license
+--------------
+
+This plugin allows you to define a ``LICENSE`` setting and adds the contents of that
+license variable to the article's context, making that variable available to use
+from within your theme's templates.
diff --git a/pelican-plugins/global_license/__init__.py b/pelican-plugins/global_license/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..ffe2862990c2d47880fec4cfc1a65258474513f7
--- /dev/null
+++ b/pelican-plugins/global_license/__init__.py
@@ -0,0 +1 @@
+from .global_license import *
diff --git a/pelican-plugins/global_license/global_license.py b/pelican-plugins/global_license/global_license.py
new file mode 100644
index 0000000000000000000000000000000000000000..fd37f5b86e3315649e539cf5207caad755698268
--- /dev/null
+++ b/pelican-plugins/global_license/global_license.py
@@ -0,0 +1,18 @@
+"""
+License plugin for Pelican
+==========================
+
+This plugin allows you to define a LICENSE setting and adds the contents of that
+license variable to the article's context, making that variable available to use
+from within your theme's templates.
+"""
+
+from pelican import signals
+
+def add_license(generator, metadata):
+    if 'license' not in metadata.keys()\
+        and 'LICENSE' in generator.settings.keys():
+            metadata['license'] = generator.settings['LICENSE']
+
+def register():
+    signals.article_generator_context.connect(add_license)
diff --git a/pelican-plugins/glossary/__init__.py b/pelican-plugins/glossary/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..44e4eaa75600abaa06fc5c9b3636497d08f6868d
--- /dev/null
+++ b/pelican-plugins/glossary/__init__.py
@@ -0,0 +1 @@
+from .glossary import *
diff --git a/pelican-plugins/glossary/glossary.py b/pelican-plugins/glossary/glossary.py
new file mode 100644
index 0000000000000000000000000000000000000000..867b448c4112fe8deab0868ced3cc2f09316b51e
--- /dev/null
+++ b/pelican-plugins/glossary/glossary.py
@@ -0,0 +1,70 @@
+"""
+Builds a glossary page containing definition lists found in articles
+and pages, and adds a `definitions` variable visible to all page templates.
+
+"""
+
+import bs4
+from pelican import signals
+
+
+class Definitions():
+    definitions = []
+    exclude = []
+
+
+def make_title(def_title):
+    return def_title.text
+
+
+def make_def(def_title):
+    return ''.join(str(t) for t in def_title.find_next('dd').contents)
+
+
+def make_anchor(def_title):
+    return def_title.text.lower().replace(' ', '-')
+
+
+def set_definitions(generator, metadata):
+    generator.context['definitions'] = Definitions.definitions
+
+
+def get_excludes(pelican):
+    Definitions.exclude = pelican.settings.get('GLOSSARY_EXCLUDE', [])
+
+
+def parse_content(content):
+    soup = bs4.BeautifulSoup(content._content, 'html.parser')
+
+    for def_list in soup.find_all('dl'):
+        defns = []
+        for def_title in def_list.find_all('dt'):
+            if def_title.text not in Definitions.exclude:
+                anchor_name = make_anchor(def_title)
+                anchor_tag = bs4.Tag(name="a", attrs={'name': anchor_name})
+                index = def_list.parent.index(def_list)-1
+                def_list.parent.insert(index, anchor_tag)
+
+                defns.append(
+                    {'title': make_title(def_title),
+                     'definition': make_def(def_title),
+                     'anchor': anchor_name,
+                     'source': content})
+
+        for defn in defns:
+            defn['see_also'] = [d for d in defns if d is not defn]
+
+        Definitions.definitions += defns
+
+    content._content = str(soup)
+
+
+def parse_articles(generator):
+    for article in generator.articles:
+        parse_content(article)
+
+
+def register():
+    signals.initialized.connect(get_excludes)
+    signals.article_generator_finalized.connect(parse_articles)
+    signals.page_generator_context.connect(set_definitions)
diff --git a/pelican-plugins/glossary/readme.md b/pelican-plugins/glossary/readme.md
new file mode 100644
index 0000000000000000000000000000000000000000..8a098eff72cd5ee08de6ac7197ee3d6e74fda872
--- /dev/null
+++ b/pelican-plugins/glossary/readme.md
@@ -0,0 +1,102 @@
+# Glossary
+
+Builds a glossary page containing definition lists found in articles.
+
+
+## Example
+
+If you have an article (Markdown or ReST) that generates the following:
+
+file `defns.html` titled "My definitions"
+```
+<dl>
+  <dt>My Term</dt>
+  <dd>This is definition for My Term.</dd>
+  <dt>Another Term</dt>
+  <dd>And another definition.</dd>
+</dl>
+```
+
+This plugin will do two things. First, it will add an anchor to the
+beginning of the <dl> tag, like so:
+
+file `defns.html` titled "My definitions"
+```
+<a name="another-term"></a>
+<a name="my-term"></a>
+<dl>
+  <dt>My Term</dt>
+  <dd>This is definition for My Term.</dd>
+  <dt>Another Term</dt>
+  <dd>And another definition.</dd>
+</dl>
+```
+
+Second, it will extract all such definitions and put them inside the
+`definitions` variable in the pelican context. It will be seen by all page
+templates.
+
+The `definitions` variable will have the following attributes:
++ `title`, the definition title, inside <dt> tags,
++ `definition`, the definition, inside <dd> tags,
++ `anchor`, the text inside the `name` attribute for the anchor link,
++ `source`, the article or page that contains this definition list,
++ `see_also`, containing a list of objects just like this one, made from
+  other definitions in the same list.
+
+For example, for the above html code, the `definitions` variable would look
+like the following:
+
+```
+definitions = [obj1, obj2]
+obj1.title = "My Term"
+obj1.definition = "This is definition for My Term."
+obj1.anchor = 'my-term'
+obj1.source = <Content object pointing to "My definitions" file>
+obj1.see_also = [obj2]
+
+obj2.title = "Another Term"
+obj2.definition = "And another definition."
+obj2.link = 'another-term'
+obj2.source = <Content object pointing to "My definitions" file>
+obj2.see_also = [obj1]
+```
+
+
+## Usage
+
+Next is an example usage of the `definitions` variable.
+
+glossary.html
+```
+{% for def in definitions | sort(attribute='title') %}
+<dl>
+  <a name="{{ def.anchor }}"></a>
+  <dt><h4>{{ def.title }}</h4></dt>
+  <dd>
+    <p>{{ def.definition }}</p>
+    <p><i>
+      <span>Defined in: <a href="{{ def.source.url }}#{{ def.anchor }}">{{ def.source.title }}</a>.</span>
+      {% if def.see_also %}
+      <span> See also: </span>
+      {% for also in def.see_also %}
+      <span><a href="{{ output_file }}#{{ also.anchor }}">{{ also.title }}</a>{% if not loop.last %}, {% else %}.{% endif %}</span>
+      {% endfor%}
+      {% endif %}
+    </i></p>
+  </dd>
+</dl>
+```
+
+This example generates new anchors in the glossary page, so that navigation
+through the `see also` links is done inside the same page, as well as link
+to the source page (with the correct anchor too).
+>>>>>>> Stashed changes
+
+## Notes
+
++ The `glossary` plugin supports the use of a `GLOSSARY_EXCLUDE` setting,
+  which can be set to an arbitrary list in your `pelicanconf.py`. By
+  default, it's equal to the empty list. `glossary` will add to
+  `definitions` all definitions **EXCEPT** those whose title is found
+  inside `GLOSSARY_EXCLUDE`.
diff --git a/pelican-plugins/goodreads_activity/Readme.md b/pelican-plugins/goodreads_activity/Readme.md
new file mode 100644
index 0000000000000000000000000000000000000000..095518a53e734a7267629335acec9a23c78b7651
--- /dev/null
+++ b/pelican-plugins/goodreads_activity/Readme.md
@@ -0,0 +1,80 @@
+Goodreads Activity
+==================
+
+A Pelican plugin to lists books from your Goodreads shelves.
+
+Copyright (c) Talha Mansoor
+
+Author          | Talha Mansoor
+----------------|-----
+Author Email    | talha131@gmail.com 
+Author Homepage | http://onCrashReboot.com 
+Github Account  | https://github.com/talha131 
+
+### Credits
+
+This plugin is inspired by Marco Milanesi <kpanic@gnufunk.org> Github activity plugin.
+
+Requirements
+============
+
+`goodreads_activity` requires feedparser.
+
+```bash
+pip install feedparser
+```
+
+How to Use
+==========
+
+**Important** Unlike Marco's Github activity plugin, this plugin returns
+a dictionary composed of the books in your Goodreads shelf and their
+details.
+
+To enable it, set `GOODREADS_ACTIVITY_FEED` in your pelican config file. It should point to the activity feed of your bookshelf.
+
+To find your self's activity feed,
+
+1.  Open Goodreads homepage and login
+2.  Click on My Books in the top navigational bar
+3.  Select the bookshelf you are interested in from the left hand column
+4.  Look for RSS link in the footer. Copy it's link.
+
+Here is an example feed of currently-reading shelf,
+
+```python
+GOODREADS_ACTIVITY_FEED='http://www.goodreads.com/review/list_rss/8028663?key=b025l3000336epw1pix047e853agggannc9932ed&shelf=currently-reading'
+```
+
+You can access the `goodreads_activity` in your Jinja2 template. `goodreads_activity` is a dictionary. Its valid keys are
+
+1.  `shelf_title` it has the title of your shelf
+2.  `books` it is an array of book dictionary
+
+Valid keys for `book` dictionary are
+
+1.  `title`
+2.  `author`
+3.  `link` link to your book review
+4.  `l_cover` large cover
+5.  `m_cover` medium cover
+6.  `s_cover` small cover
+7.  `description`
+8.  `rating`
+9.  `review`
+10. `tags`
+
+Template Example
+================
+
+```python
+{% if GOODREADS_ACTIVITY_FEED %}
+    <h2>{{ goodreads_activity.shelf_title }}</h2>
+    {% for book in goodreads_activity.books %}
+        <img src="{{book.s_cover}}"/>
+        <header>{{book.title}}<small> by {{book.author}}</small></header>
+        <article>{{book.description|truncate(end='')}}
+        <a href={{book.link}} target="_blank">...more</a></article>
+    {% endfor %}
+{% endif %}
+```
diff --git a/pelican-plugins/goodreads_activity/__init__.py b/pelican-plugins/goodreads_activity/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..176113db7b106b52230d4c8b934e272d48f643bf
--- /dev/null
+++ b/pelican-plugins/goodreads_activity/__init__.py
@@ -0,0 +1 @@
+from .goodreads_activity import *
\ No newline at end of file
diff --git a/pelican-plugins/goodreads_activity/goodreads_activity.py b/pelican-plugins/goodreads_activity/goodreads_activity.py
new file mode 100644
index 0000000000000000000000000000000000000000..d1fe81d7884304fc077dc971f650bee785888fff
--- /dev/null
+++ b/pelican-plugins/goodreads_activity/goodreads_activity.py
@@ -0,0 +1,63 @@
+# -*- coding: utf-8 -*-
+"""
+Goodreads Activity
+==================
+
+A Pelican plugin to lists books from your Goodreads shelves.
+
+Copyright (c) Talha Mansoor
+"""
+
+from __future__ import unicode_literals
+
+import logging
+logger = logging.getLogger(__name__)
+
+from pelican import signals
+
+
+class GoodreadsActivity():
+    def __init__(self, generator):
+        import feedparser
+        self.activities = feedparser.parse(
+            generator.settings['GOODREADS_ACTIVITY_FEED'])
+
+    def fetch(self):
+        goodreads_activity = {
+            'shelf_title': self.activities.feed.title,
+            'books': []
+        }
+        for entry in self.activities['entries']:
+            book = {
+                'title': entry.title,
+                'author': entry.author_name,
+                'link': entry.link,
+                'l_cover': entry.book_large_image_url,
+                'm_cover': entry.book_medium_image_url,
+                's_cover': entry.book_small_image_url,
+                'description': entry.book_description,
+                'rating': entry.user_rating,
+                'review': entry.user_review,
+                'tags': entry.user_shelves
+            }
+            goodreads_activity['books'].append(book)
+
+        return goodreads_activity
+
+
+def fetch_goodreads_activity(gen, metadata):
+    if 'GOODREADS_ACTIVITY_FEED' in gen.settings:
+        gen.context['goodreads_activity'] = gen.goodreads.fetch()
+
+
+def initialize_feedparser(generator):
+    generator.goodreads = GoodreadsActivity(generator)
+
+
+def register():
+    try:
+        signals.article_generator_init.connect(initialize_feedparser)
+        signals.article_generator_context.connect(fetch_goodreads_activity)
+    except ImportError:
+        logger.warning('`goodreads_activity` failed to load dependency `feedparser`.'
+                       '`goodreads_activity` plugin not loaded.')
diff --git a/pelican-plugins/googleplus_comments/Readme.md b/pelican-plugins/googleplus_comments/Readme.md
new file mode 100644
index 0000000000000000000000000000000000000000..f82455363462f3bfba89b031f050b6980b16f68a
--- /dev/null
+++ b/pelican-plugins/googleplus_comments/Readme.md
@@ -0,0 +1,20 @@
+GooglePlus Comments Plugin For Pelican
+==================================
+
+Adds GooglePlus comments to Pelican
+
+Add the plugin to `pelicanconf.py`:
+
+    PLUGIN_PATH = 'pelican-plugins'
+    PLUGINS = ["googleplus_comments"]
+
+Add a `<div>` for comments to the `article.html` of your template:
+
+    <div id="commentswrap">
+    <div id="comments"></div>
+    </div>
+    {{ article.metadata.googleplus_comments }}
+
+See it working, and ask for support:
+
+<http://zonca.github.io/2013/09/google-plus-comments-plugin-for-pelican.html>
diff --git a/pelican-plugins/googleplus_comments/__init__.py b/pelican-plugins/googleplus_comments/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..4cbb6d1b5e19e269f497deb9023b4ddaf7430539
--- /dev/null
+++ b/pelican-plugins/googleplus_comments/__init__.py
@@ -0,0 +1 @@
+from .googleplus_comments import *
diff --git a/pelican-plugins/googleplus_comments/googleplus_comments.py b/pelican-plugins/googleplus_comments/googleplus_comments.py
new file mode 100644
index 0000000000000000000000000000000000000000..60495dadd495e2844a0bb1ce8a5c2b9d49364dbf
--- /dev/null
+++ b/pelican-plugins/googleplus_comments/googleplus_comments.py
@@ -0,0 +1,29 @@
+# -*- coding: utf-8 -*-
+"""
+Google Comments Plugin For Pelican
+==================================
+
+Adds Google comments to Pelican
+"""
+
+from pelican import signals
+
+googleplus_comments_snippet = """
+    <script src="https://apis.google.com/js/plusone.js"></script>
+    <script>
+        $(document).ready(function () {
+            gapi.comments.render('comments', {
+                href: window.location,
+                width: '600',
+                first_party_property: 'BLOGGER',
+                view_type: 'FILTERED_POSTMOD'
+            });
+    });
+    </script>
+"""
+
+def add_googleplus_comments(generator, metadata):
+    metadata["googleplus_comments"] = googleplus_comments_snippet
+
+def register():
+    signals.article_generator_context.connect(add_googleplus_comments)
diff --git a/pelican-plugins/gravatar/Readme.rst b/pelican-plugins/gravatar/Readme.rst
new file mode 100644
index 0000000000000000000000000000000000000000..16024a084be66029b56e527482798c2579e4fab8
--- /dev/null
+++ b/pelican-plugins/gravatar/Readme.rst
@@ -0,0 +1,25 @@
+Gravatar
+--------
+
+**NOTE:** `This plugin has been moved to its own repository <https://github.com/pelican-plugins/avatar>`_. Please file any issues/PRs there. Once all plugins have been migrated to the `new Pelican Plugins organization <https://github.com/pelican-plugins>`_, this monolithic repository will be archived.
+
+This plugin assigns the ``author_gravatar`` variable to the Gravatar URL and
+makes the variable available within the article's context. You can add
+``AUTHOR_EMAIL`` to your settings file to define the default author's email
+address. Obviously, that email address must be associated with a Gravatar
+account.
+
+Alternatively, you can provide an email address from within article metadata.
+
+In reSTructuredText::
+
+    :email:  john.doe@example.com
+
+In Markdown::
+
+    Email:  john.doe@example.com
+
+If the email address is defined via at least one of the two methods above, the
+``author_gravatar`` variable is added to the article's context. For Markdown,
+it is critical that the 'E' in ``Email`` is capitalized.
+
diff --git a/pelican-plugins/gravatar/__init__.py b/pelican-plugins/gravatar/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..cc604874cc9c4512fd26f590c746a29bce8aa9ef
--- /dev/null
+++ b/pelican-plugins/gravatar/__init__.py
@@ -0,0 +1 @@
+from .gravatar import *
diff --git a/pelican-plugins/gravatar/gravatar.py b/pelican-plugins/gravatar/gravatar.py
new file mode 100644
index 0000000000000000000000000000000000000000..6e063f7b55260ca4949c1c863dd26d13712ccf01
--- /dev/null
+++ b/pelican-plugins/gravatar/gravatar.py
@@ -0,0 +1,31 @@
+"""
+Gravatar plugin for Pelican
+===========================
+
+This plugin assigns the ``author_gravatar`` variable to the Gravatar URL and
+makes the variable available within the article's context.
+"""
+
+import hashlib
+import six
+
+from pelican import signals
+
+
+def add_gravatar(generator, metadata):
+
+    #first check email
+    if 'email' not in metadata.keys()\
+        and 'AUTHOR_EMAIL' in generator.settings.keys():
+            metadata['email'] = generator.settings['AUTHOR_EMAIL']
+
+    #then add gravatar url
+    if 'email' in metadata.keys():
+        email_bytes = six.b(metadata['email']).lower()
+        gravatar_url = "https://www.gravatar.com/avatar/" + \
+                        hashlib.md5(email_bytes).hexdigest()
+        metadata['author_gravatar'] = gravatar_url
+
+
+def register():
+    signals.article_generator_context.connect(add_gravatar)
diff --git a/pelican-plugins/gzip_cache/Readme.rst b/pelican-plugins/gzip_cache/Readme.rst
new file mode 100644
index 0000000000000000000000000000000000000000..b27512011839bfad81c27485f20a7fa483e5bb4d
--- /dev/null
+++ b/pelican-plugins/gzip_cache/Readme.rst
@@ -0,0 +1,21 @@
+Gzip cache
+----------
+
+Certain web servers (e.g., Nginx) can use a static cache of gzip-compressed
+files to prevent the server from compressing files during an HTTP call. Since
+compression occurs at another time, these compressed files can be compressed
+at a higher compression level for increased optimization.
+
+The ``gzip_cache`` plugin compresses all common text type files into a ``.gz``
+file within the same directory as the original file.
+
+Settings
+--------
+
+* ``GZIP_CACHE_OVERWRITE``:
+  If True, the original files will be replaced by the gzip-compressed files. 
+  This is useful for static hosting services (e.g S3). Defaults to False.
+* ``GZIP_CACHE_EXCLUDE_TYPES``:
+  A tuple of file extensions to exclude in addition to the built-in ones.
+  Extensions must be specified with leading dot, e.g.
+  ``GZIP_CACHE_EXCLUDE_TYPES = ('.php', '.py', '.tmp')``
diff --git a/pelican-plugins/gzip_cache/__init__.py b/pelican-plugins/gzip_cache/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..fb76712f33ae93f03ca499ca1faaca7914e84eca
--- /dev/null
+++ b/pelican-plugins/gzip_cache/__init__.py
@@ -0,0 +1 @@
+from .gzip_cache import *
diff --git a/pelican-plugins/gzip_cache/gzip_cache.py b/pelican-plugins/gzip_cache/gzip_cache.py
new file mode 100644
index 0000000000000000000000000000000000000000..2985d69bba0293a1bbe6035d143913e1b561c520
--- /dev/null
+++ b/pelican-plugins/gzip_cache/gzip_cache.py
@@ -0,0 +1,129 @@
+'''
+Copyright (c) 2012 Matt Layman
+
+Gzip cache
+----------
+
+A plugin to create .gz cache files for optimization.
+'''
+
+import logging
+import os
+import zlib
+
+from pelican import signals
+
+logger = logging.getLogger(__name__)
+
+# A list of file types to exclude from possible compression
+EXCLUDE_TYPES = (
+    # Compressed types
+    '.bz2',
+    '.gz',
+
+    # Audio types
+    '.aac',
+    '.flac',
+    '.mp3',
+    '.wma',
+
+    # Image types
+    '.gif',
+    '.jpg',
+    '.jpeg',
+    '.png',
+
+    # Video types
+    '.avi',
+    '.mov',
+    '.mp4',
+    '.webm',
+
+    # Internally-compressed fonts. gzip can often shave ~50 more bytes off,
+    # but it's not worth it.
+    '.woff',
+    '.woff2',
+)
+
+COMPRESSION_LEVEL = 9 # Best Compression
+
+""" According to zlib manual: 'Add 16 to
+windowBits to write a simple gzip header and trailer around the
+compressed data instead of a zlib wrapper. The gzip header will
+have no file name, no extra data, no comment, no modification
+time (set to zero), no header crc, and the operating system
+will be set to 255 (unknown)'
+"""
+WBITS = zlib.MAX_WBITS | 16
+
+
+def create_gzip_cache(pelican):
+    '''Create a gzip cache file for every file that a webserver would
+    reasonably want to cache (e.g., text type files).
+
+    :param pelican: The Pelican instance
+    '''
+    user_exclude_types = pelican.settings.get('GZIP_CACHE_EXCLUDE_TYPES', ())
+    overwrite = should_overwrite(pelican.settings)
+    for dirpath, _, filenames in os.walk(pelican.settings['OUTPUT_PATH']):
+        for name in filenames:
+            if should_compress(name, user_exclude_types):
+                filepath = os.path.join(dirpath, name)
+                create_gzip_file(filepath, overwrite)
+
+
+def should_compress(filename, user_exclude_types):
+    '''Check if the filename is a type of file that should be compressed.
+
+    :param filename: A file name to check against
+    :param user_exclude_types: User-supplied list of extensions to exclude
+    '''
+    if filename.endswith(EXCLUDE_TYPES):
+        return False
+    if filename.endswith(user_exclude_types):
+        return False
+
+    return True
+
+def should_overwrite(settings):
+    '''Check if the gzipped files should overwrite the originals.
+
+    :param settings: The pelican instance settings
+    '''
+    return settings.get('GZIP_CACHE_OVERWRITE', False)
+
+def create_gzip_file(filepath, overwrite):
+    '''Create a gzipped file in the same directory with a filepath.gz name.
+
+    :param filepath: A file to compress
+    :param overwrite: Whether the original file should be overwritten
+    '''
+    compressed_path = filepath + '.gz'
+
+    with open(filepath, 'rb') as uncompressed:
+        gzip_compress_obj = zlib.compressobj(COMPRESSION_LEVEL,
+                                                zlib.DEFLATED, WBITS)
+
+        uncompressed_data = uncompressed.read()
+        gzipped_data = gzip_compress_obj.compress(uncompressed_data)
+        gzipped_data += gzip_compress_obj.flush()
+
+        if len(gzipped_data) >= len(uncompressed_data):
+            logger.debug('No improvement: %s' % filepath)
+            return
+
+        with open(compressed_path, 'wb') as compressed:
+            logger.debug('Compressing: %s' % filepath)
+            try:
+                compressed.write(gzipped_data)
+            except Exception as ex:
+                logger.critical('Gzip compression failed: %s' % ex)
+
+        if overwrite:
+            logger.debug('Overwriting: %s with %s' % (filepath, compressed_path))
+            os.remove(filepath)
+            os.rename(compressed_path, filepath)
+
+def register():
+    signals.finalized.connect(create_gzip_cache)
+
diff --git a/pelican-plugins/gzip_cache/test_gzip_cache.py b/pelican-plugins/gzip_cache/test_gzip_cache.py
new file mode 100644
index 0000000000000000000000000000000000000000..9c333ec8c87ec098a6cf61417a649ad0d262d940
--- /dev/null
+++ b/pelican-plugins/gzip_cache/test_gzip_cache.py
@@ -0,0 +1,107 @@
+# -*- coding: utf-8 -*-
+'''Core plugins unit tests'''
+
+import os
+import tempfile
+import unittest
+import time
+
+from contextlib import contextmanager
+from tempfile import mkdtemp
+from shutil import rmtree
+from hashlib import md5
+
+import gzip_cache
+
+@contextmanager
+def temporary_folder():
+    """creates a temporary folder, return it and delete it afterwards.
+
+    This allows to do something like this in tests:
+
+        >>> with temporary_folder() as d:
+            # do whatever you want
+    """
+    tempdir = mkdtemp()
+    try:
+        yield tempdir
+    finally:
+        rmtree(tempdir)
+
+
+class TestGzipCache(unittest.TestCase):
+
+    def test_should_compress(self):
+        user_exclude_types = ()
+
+        # Some filetypes should compress and others shouldn't.
+        self.assertTrue(gzip_cache.should_compress('foo.html', user_exclude_types))
+        self.assertTrue(gzip_cache.should_compress('bar.css', user_exclude_types))
+        self.assertTrue(gzip_cache.should_compress('baz.js', user_exclude_types))
+        self.assertTrue(gzip_cache.should_compress('foo.txt', user_exclude_types))
+
+        self.assertFalse(gzip_cache.should_compress('foo.gz', user_exclude_types))
+        self.assertFalse(gzip_cache.should_compress('bar.png', user_exclude_types))
+        self.assertFalse(gzip_cache.should_compress('baz.mp3', user_exclude_types))
+        self.assertFalse(gzip_cache.should_compress('foo.mov', user_exclude_types))
+
+        user_exclude_types = ('.html', '.xyz')
+        self.assertFalse(gzip_cache.should_compress('foo.html', user_exclude_types))
+        self.assertFalse(gzip_cache.should_compress('bar.xyz', user_exclude_types))
+        self.assertFalse(gzip_cache.should_compress('foo.gz', user_exclude_types))
+        self.assertTrue(gzip_cache.should_compress('baz.js', user_exclude_types))
+
+    def test_should_overwrite(self):
+        # Default to false if GZIP_CACHE_OVERWRITE is not set
+        settings = { }
+        self.assertFalse(gzip_cache.should_overwrite(settings))
+        settings = { 'GZIP_CACHE_OVERWRITE': False }
+        self.assertFalse(gzip_cache.should_overwrite(settings))
+        settings = { 'GZIP_CACHE_OVERWRITE': True }
+        self.assertTrue(gzip_cache.should_overwrite(settings))
+
+    def test_creates_gzip_file(self):
+        # A file matching the input filename with a .gz extension is created.
+
+        # The plugin walks over the output content after the finalized signal
+        # so it is safe to assume that the file exists (otherwise walk would
+        # not report it). Therefore, create a dummy file to use.
+        with temporary_folder() as tempdir:
+            _, a_html_filename = tempfile.mkstemp(suffix='.html', dir=tempdir)
+            with open(a_html_filename, 'w') as f:
+                f.write('A' * 24)  # under this length, compressing is useless and create_gzip_file will not create any file
+            gzip_cache.create_gzip_file(a_html_filename, False)
+            self.assertTrue(os.path.exists(a_html_filename + '.gz'))
+
+    def test_creates_same_gzip_file(self):
+        # Should create the same gzip file from the same contents.
+
+        # gzip will create a slightly different file because it includes
+        # a timestamp in the compressed file by default. This can cause
+        # problems for some caching strategies.
+        with temporary_folder() as tempdir:
+            _, a_html_filename = tempfile.mkstemp(suffix='.html', dir=tempdir)
+            with open(a_html_filename, 'w') as f:
+                f.write('A' * 24)  # under this length, compressing is useless and create_gzip_file will not create any file
+            a_gz_filename = a_html_filename + '.gz'
+            gzip_cache.create_gzip_file(a_html_filename, False)
+            gzip_hash = get_md5(a_gz_filename)
+            time.sleep(1)
+            gzip_cache.create_gzip_file(a_html_filename, False)
+            self.assertEqual(gzip_hash, get_md5(a_gz_filename))
+
+    def test_overwrites_gzip_file(self):
+        # A file matching the input filename with a .gz extension is not created.
+
+        # The plugin walks over the output content after the finalized signal
+        # so it is safe to assume that the file exists (otherwise walk would
+        # not report it). Therefore, create a dummy file to use.
+        with temporary_folder() as tempdir:
+            _, a_html_filename = tempfile.mkstemp(suffix='.html', dir=tempdir)
+            gzip_cache.create_gzip_file(a_html_filename, True)
+            self.assertFalse(os.path.exists(a_html_filename + '.gz'))
+
+def get_md5(filepath):
+    with open(filepath, 'rb') as fh:
+        return md5(fh.read()).hexdigest()
+
diff --git a/pelican-plugins/headerid/README.rst b/pelican-plugins/headerid/README.rst
new file mode 100644
index 0000000000000000000000000000000000000000..7bfa4029af483959d5ca3649cd9f83033e98650c
--- /dev/null
+++ b/pelican-plugins/headerid/README.rst
@@ -0,0 +1,16 @@
+Pelican ``headerid`` plugin
+===========================
+
+This plugin adds an anchor to each heading so you can deep-link to headers.
+It is intended for formats such as reStructuredText that do not natively
+generate these anchors.
+
+The ``HEADERID_LINK_CHAR`` config can be set to use a different char from ``*``
+for anchor text.
+
+For Markdown, this plugin is less relevant since the Python-Markdown library
+includes a Table of Contents extension that will generate link anchors.
+To enable the ``toc`` extension, add a line similar to the following example
+to your Pelican settings file::
+
+    MD_EXTENSIONS = ["codehilite(css_class=highlight)", "extra", "toc"]
diff --git a/pelican-plugins/headerid/__init__.py b/pelican-plugins/headerid/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..b88e8e2856787697c0e9b096c6fd6bc54f3476ba
--- /dev/null
+++ b/pelican-plugins/headerid/__init__.py
@@ -0,0 +1 @@
+from .headerid import *
diff --git a/pelican-plugins/headerid/headerid.py b/pelican-plugins/headerid/headerid.py
new file mode 100644
index 0000000000000000000000000000000000000000..ee9d2654e392f806621c0385ac8b72cc491db6f2
--- /dev/null
+++ b/pelican-plugins/headerid/headerid.py
@@ -0,0 +1,31 @@
+from pelican import readers
+from pelican.readers import PelicanHTMLTranslator
+from pelican import signals
+from docutils import nodes
+
+LINK_CHAR = '*'
+
+
+def init_headerid(sender):
+    global LINK_CHAR
+    char = sender.settings.get('HEADERID_LINK_CHAR')
+    if char:
+        LINK_CHAR = char
+
+def register():
+    signals.initialized.connect(init_headerid)
+
+
+    class HeaderIDPatchedPelicanHTMLTranslator(PelicanHTMLTranslator):
+        def depart_title(self, node):
+            close_tag = self.context[-1]
+            parent = node.parent
+            if isinstance(parent, nodes.section) and parent.hasattr('ids') and parent['ids']:
+                anchor_name = parent['ids'][0]
+                # add permalink anchor
+                if close_tag.startswith('</h'):
+                    self.body.append(
+                        '<a class="headerlink" href="#%s" title="Permalink to this headline">%s</a>' %
+                        (anchor_name, LINK_CHAR))
+            PelicanHTMLTranslator.depart_title(self, node)
+    readers.PelicanHTMLTranslator = HeaderIDPatchedPelicanHTMLTranslator
diff --git a/pelican-plugins/html_entity/README.rst b/pelican-plugins/html_entity/README.rst
new file mode 100644
index 0000000000000000000000000000000000000000..c772707c306a9b59f10a805ce52759fc7b8a6411
--- /dev/null
+++ b/pelican-plugins/html_entity/README.rst
@@ -0,0 +1,30 @@
+HTML Entities for reStructuredText
+##################################
+
+This plugin allows you to enter HTML entities such as &copy;, &lt;, &#149; inline in a RST document, as opposed
+to the tedious method of creating substitution definitions.
+
+Roles
+=====
+
+Adds one inline role:
+
+::
+
+    :html_entity:
+
+Usage
+=====
+
+::
+
+    :html_entity:`copy` 2013 :html_entity:`lt` Pelican :html_entity:`gt` Industries :html_entity:`149` All Rights Reserved
+
+produces:
+
+|copy| 2013 |lt| Pelican |gt| Industries |bullet| All Rights Reserved
+
+.. |copy|   unicode:: U+000A9 .. COPYRIGHT SIGN
+.. |lt|     unicode:: U+003C  .. LESS THAN
+.. |gt|     unicode:: U+003E  .. GREATER THAN
+.. |bullet| unicode:: U+2022  .. BULLET
diff --git a/pelican-plugins/html_entity/__init__.py b/pelican-plugins/html_entity/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/pelican-plugins/html_entity/html_entity.py b/pelican-plugins/html_entity/html_entity.py
new file mode 100644
index 0000000000000000000000000000000000000000..ffb70e79a2b0cfee345fd904d4460e9ce0a49794
--- /dev/null
+++ b/pelican-plugins/html_entity/html_entity.py
@@ -0,0 +1,46 @@
+"""
+HTML Entities for reStructured Text
+===================================
+
+Allows user to use HTML entities (&copy;, &#149;, etc.) in RST documents.
+
+Usage:
+:html_entity:`copy`
+:html_entity:`149`
+:html_entity:`#149`
+"""
+from __future__ import unicode_literals
+from docutils import nodes, utils
+from docutils.parsers.rst import roles
+from pelican.readers import PelicanHTMLTranslator
+import six
+
+
+class html_entity(nodes.Inline, nodes.Node):
+    # Subclassing Node directly since TextElement automatically appends the escaped element
+    def __init__(self, rawsource, text):
+        self.rawsource = rawsource
+        self.text = text
+        self.children = []
+        self.attributes = {}
+
+    def astext(self):
+        return self.text
+
+
+def entity_role(typ, rawtext, text, lineno, inliner, options={}, content=[]):
+    text = utils.unescape(text)
+    entity_code = text
+    try:
+        entity_code = "#{}".format(six.u(int(entity_code)))
+    except ValueError:
+        pass
+    entity_code = "&{};".format(entity_code)
+    return [html_entity(text, entity_code)], []
+
+
+def register():
+    roles.register_local_role('html_entity', entity_role)
+
+PelicanHTMLTranslator.visit_html_entity = lambda self, node: self.body.append(node.astext())
+PelicanHTMLTranslator.depart_html_entity = lambda self, node: None
diff --git a/pelican-plugins/html_rst_directive/Readme.rst b/pelican-plugins/html_rst_directive/Readme.rst
new file mode 100644
index 0000000000000000000000000000000000000000..93a7a1d280ab28646c74f5512d8e3a3d9c06648a
--- /dev/null
+++ b/pelican-plugins/html_rst_directive/Readme.rst
@@ -0,0 +1,45 @@
+HTML tags for reStructuredText
+------------------------------
+
+This plugin allows you to use HTML tags from within reST documents. 
+
+
+Directives
+----------
+
+
+::
+
+    .. html::
+
+        (HTML code)
+
+
+Example
+-------
+
+A search engine::
+
+    .. html::
+
+       <form action="http://seeks.fr/search" method="GET">
+         <input type="text" value="Pelican v2" title="Search" maxlength="2048" name="q" autocomplete="on" />
+         <input type="hidden" name="lang" value="en" />
+         <input type="submit" value="Seeks !" id="search_button" />
+       </form>
+
+
+A contact form::
+
+    .. html::
+
+        <form method="GET" action="mailto:some email">
+          <p>
+            <input type="text" placeholder="Subject" name="subject">
+            <br />
+            <textarea name="body" placeholder="Message">
+            </textarea>
+            <br />
+            <input type="reset"><input type="submit">
+          </p>
+        </form>
diff --git a/pelican-plugins/html_rst_directive/__init__.py b/pelican-plugins/html_rst_directive/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..57ec228af15cf838561f22872ec657f6987e5f2c
--- /dev/null
+++ b/pelican-plugins/html_rst_directive/__init__.py
@@ -0,0 +1 @@
+from .html_rst_directive import *
diff --git a/pelican-plugins/html_rst_directive/html_rst_directive.py b/pelican-plugins/html_rst_directive/html_rst_directive.py
new file mode 100644
index 0000000000000000000000000000000000000000..ed877da6e22354a42cedf016ab1da952f34d4583
--- /dev/null
+++ b/pelican-plugins/html_rst_directive/html_rst_directive.py
@@ -0,0 +1,30 @@
+# -*- coding: utf-8 -*-
+"""
+HTML tags for reStructuredText
+==============================
+
+This plugin allows you to use HTML tags from within reST documents. 
+
+"""
+
+from __future__ import unicode_literals
+from docutils import nodes
+from docutils.parsers.rst import directives, Directive
+
+
+class RawHtml(Directive):
+    required_arguments = 0
+    optional_arguments = 0
+    final_argument_whitespace = True
+    has_content = True
+
+    def run(self):
+        html = ' '.join(self.content)
+        node = nodes.raw('', html, format='html')
+        return [node]
+
+
+
+def register():
+    directives.register_directive('html', RawHtml)
+
diff --git a/pelican-plugins/i18n_subsites/README.rst b/pelican-plugins/i18n_subsites/README.rst
new file mode 100644
index 0000000000000000000000000000000000000000..340109b135ab79ce871b2d14f081495da4aadde9
--- /dev/null
+++ b/pelican-plugins/i18n_subsites/README.rst
@@ -0,0 +1,165 @@
+=======================
+ I18N Sub-sites Plugin
+=======================
+
+This plugin extends the translations functionality by creating
+internationalized sub-sites for the default site.
+
+This plugin is designed for Pelican 3.4 and later.
+
+What it does
+============
+
+1. When the content of the main site is being generated, the settings
+   are saved and the generation stops when content is ready to be
+   written. While reading source files and generating content objects,
+   the output queue is modified in certain ways:
+
+  - translations that will appear as native in a different (sub-)site
+    will be removed
+  - untranslated articles will be transformed to drafts if
+    ``I18N_UNTRANSLATED_ARTICLES`` is ``'hide'`` (default), removed if
+    ``'remove'`` or kept as they are if ``'keep'``.
+  - untranslated pages will be transformed into hidden pages if
+    ``I18N_UNTRANSLATED_PAGES`` is ``'hide'`` (default), removed if
+    ``'remove'`` or kept as they are if ``'keep'``.''
+  - additional content manipulation similar to articles and pages can
+    be specified for custom generators in the ``I18N_GENERATOR_INFO``
+    setting.
+
+2. For each language specified in the ``I18N_SUBSITES`` dictionary the
+   settings overrides are applied to the settings from the main site
+   and a new sub-site is generated in the same way as with the main
+   site until content is ready to be written.
+3. When all (sub-)sites are waiting for content writing, all removed
+   contents, translations and static files are interlinked across the
+   (sub-)sites.
+4. Finally, all the output is written.
+
+Setting it up
+=============
+
+For each extra used language code, a language-specific settings overrides
+dictionary must be given (but can be empty) in the ``I18N_SUBSITES`` dictionary
+
+.. code-block:: python
+
+    PLUGINS = ['i18n_subsites', ...]
+
+    # mapping: language_code -> settings_overrides_dict
+    I18N_SUBSITES = {
+        'cz': {
+	    'SITENAME': 'Hezkej blog',
+	    }
+	}
+
+You must also have the following in your pelican configuration
+
+.. code-block:: python
+    JINJA_ENVIRONMENT = {
+        'extensions': ['jinja2.ext.i18n'],
+    }
+
+
+
+Default and special overrides
+-----------------------------
+The settings overrides may contain arbitrary settings, however, there
+are some that are handled in a special way:
+
+``SITEURL``
+  Any overrides to this setting should ensure that there is some level
+  of hierarchy between all (sub-)sites, because Pelican makes all URLs
+  relative to ``SITEURL`` and the plugin can only cross-link between
+  the sites using this hierarchy. For instance, with the main site
+  ``http://example.com`` a sub-site ``http://example.com/de`` will
+  work, but ``http://de.example.com`` will not. If not overridden, the
+  language code (the language identifier used in the ``lang``
+  metadata) is appended to the main ``SITEURL`` for each sub-site.
+``OUTPUT_PATH``, ``CACHE_PATH``
+  If not overridden, the language code is appended as with ``SITEURL``.
+  Separate cache paths are required as parser results depend on the locale.
+``STATIC_PATHS``, ``THEME_STATIC_PATHS``
+  If not overridden, they are set to ``[]`` and all links to static
+  files are cross-linked to the main site.
+``THEME``, ``THEME_STATIC_DIR``
+  If overridden, the logic with ``THEME_STATIC_PATHS`` does not apply.
+``DEFAULT_LANG``
+  This should not be overridden as the plugin changes it to the
+  language code of each sub-site to change what is perceived as translations.
+
+Localizing templates
+--------------------
+
+Most importantly, this plugin can use localized templates for each
+sub-site. There are two approaches to having the templates localized:
+
+- You can set a different ``THEME`` override for each language in
+  ``I18N_SUBSITES``, e.g. by making a copy of a theme ``my_theme`` to
+  ``my_theme_lang`` and then editing the templates in the new
+  localized theme. This approach means you don't have to deal with
+  gettext ``*.po`` files, but it is harder to maintain over time.
+- You use only one theme and localize the templates using the
+  `jinja2.ext.i18n Jinja2 extension
+  <http://jinja.pocoo.org/docs/templates/#i18n>`_. For a kickstart
+  read this `guide <./localizing_using_jinja2.rst>`_.
+
+Additional context variables
+............................
+
+It may be convenient to add language buttons to your theme in addition
+to the translation links of articles and pages. These buttons could,
+for example, point to the ``SITEURL`` of each (sub-)site. For this
+reason the plugin adds these variables to the template context:
+
+``main_lang``
+  The language of the main site — the original ``DEFAULT_LANG``
+``main_siteurl``
+  The ``SITEURL`` of the main site — the original ``SITEURL``
+``lang_siteurls``
+  An ordered dictionary, mapping all used languages to their
+  ``SITEURL``. The ``main_lang`` is the first key with ``main_siteurl``
+  as the value. This dictionary is useful for implementing global
+  language buttons that show the language of the currently viewed
+  (sub-)site too.
+``extra_siteurls``
+  An ordered dictionary, subset of ``lang_siteurls``, the current
+  ``DEFAULT_LANG`` of the rendered (sub-)site is not included, so for
+  each (sub-)site ``set(extra_siteurls) == set(lang_siteurls) -
+  set([DEFAULT_LANG])``. This dictionary is useful for implementing
+  global language buttons that do not show the current language.
+``relpath_to_site``
+  A function that returns a relative path from the first (sub-)site to
+  the second (sub-)site where the (sub-)sites are identified by the
+  language codes given as two arguments.
+
+If you don't like the default ordering of the ordered dictionaries,
+use a Jinja2 filter to alter the ordering.
+
+All the siteurls above are always absolute even in the case of
+``RELATIVE_URLS == True`` (it would be to complicated to replicate the
+Pelican internals for local siteurls), so you may rather use something
+like ``{{ SITEURL }}/{{ relpath_to_site(DEFAULT_LANG, main_lang }}``
+to link to the main site.
+
+This short `howto <./implementing_language_buttons.rst>`_ shows two
+example implementations of language buttons.
+
+Usage notes
+===========
+- It is **mandatory** to specify ``lang`` metadata for each article
+  and page as ``DEFAULT_LANG`` is later changed for each sub-site, so
+  content without ``lang`` metadata would be rendered in every
+  (sub-)site.
+- As with the original translations functionality, ``slug`` metadata
+  is used to group translations. It is therefore often convenient to
+  compensate for this by overriding the content URL (which defaults to
+  slug) using the ``url`` and ``save_as`` metadata. You could also
+  give articles e.g. ``name`` metadata and use it in ``ARTICLE_URL =
+  '{name}.html'``.
+
+Development
+===========
+
+- A demo and a test site is in the ``gh-pages`` branch and can be seen
+  at http://smartass101.github.io/pelican-plugins/
diff --git a/pelican-plugins/i18n_subsites/__init__.py b/pelican-plugins/i18n_subsites/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..7dfbde09e42950bbd35d7f575da35cede36fd141
--- /dev/null
+++ b/pelican-plugins/i18n_subsites/__init__.py
@@ -0,0 +1 @@
+from .i18n_subsites import *
diff --git a/pelican-plugins/i18n_subsites/i18n_subsites.py b/pelican-plugins/i18n_subsites/i18n_subsites.py
new file mode 100644
index 0000000000000000000000000000000000000000..dc27799d41bbf7d89d701fc1bfd47006bd409a26
--- /dev/null
+++ b/pelican-plugins/i18n_subsites/i18n_subsites.py
@@ -0,0 +1,462 @@
+"""i18n_subsites plugin creates i18n-ized subsites of the default site
+
+This plugin is designed for Pelican 3.4 and later
+"""
+
+
+import os
+import six
+import logging
+import posixpath
+
+from copy import copy
+from itertools import chain
+from operator import attrgetter
+try:
+    from collections.abc import OrderedDict
+except ImportError:
+    from collections import OrderedDict
+from contextlib import contextmanager
+from six.moves.urllib.parse import urlparse
+
+import gettext
+import locale
+
+from pelican import signals
+from pelican.generators import ArticlesGenerator, PagesGenerator
+from pelican.settings import configure_settings
+try:
+    from pelican.contents import Draft
+except ImportError:
+    from pelican.contents import Article as Draft
+
+
+# Global vars
+_MAIN_SETTINGS = None     # settings dict of the main Pelican instance
+_MAIN_LANG = None         # lang of the main Pelican instance
+_MAIN_SITEURL = None      # siteurl of the main Pelican instance
+_MAIN_STATIC_FILES = None # list of Static instances the main Pelican instance
+_SUBSITE_QUEUE = {}   # map: lang -> settings overrides
+_SITE_DB = OrderedDict()           # OrderedDict: lang -> siteurl
+_SITES_RELPATH_DB = {}       # map: (lang, base_lang) -> relpath
+# map: generator -> list of removed contents that need interlinking
+_GENERATOR_DB = {}
+_NATIVE_CONTENT_URL_DB = {} # map: source_path -> content in its native lang
+_LOGGER = logging.getLogger(__name__)
+
+
+@contextmanager
+def temporary_locale(temp_locale=None):
+    '''Enable code to run in a context with a temporary locale
+
+    Resets the locale back when exiting context.
+    Can set a temporary locale if provided
+    '''
+    orig_locale = locale.setlocale(locale.LC_ALL)
+    if temp_locale is not None:
+        locale.setlocale(locale.LC_ALL, temp_locale)
+    yield
+    locale.setlocale(locale.LC_ALL, orig_locale)
+
+
+def initialize_dbs(settings):
+    '''Initialize internal DBs using the Pelican settings dict
+
+    This clears the DBs for e.g. autoreload mode to work
+    '''
+    global _MAIN_SETTINGS, _MAIN_SITEURL, _MAIN_LANG, _SUBSITE_QUEUE
+    _MAIN_SETTINGS = settings
+    _MAIN_LANG = settings['DEFAULT_LANG']
+    _MAIN_SITEURL = settings['SITEURL']
+    _SUBSITE_QUEUE = settings.get('I18N_SUBSITES', {}).copy()
+    prepare_site_db_and_overrides()
+    # clear databases in case of autoreload mode
+    _SITES_RELPATH_DB.clear()
+    _NATIVE_CONTENT_URL_DB.clear()
+    _GENERATOR_DB.clear()
+
+
+def prepare_site_db_and_overrides():
+    '''Prepare overrides and create _SITE_DB
+
+    _SITE_DB.keys() need to be ready for filter_translations
+    '''
+    _SITE_DB.clear()
+    _SITE_DB[_MAIN_LANG] = _MAIN_SITEURL
+    # make sure it works for both root-relative and absolute
+    main_siteurl = '/' if _MAIN_SITEURL == '' else _MAIN_SITEURL
+    for lang, overrides in _SUBSITE_QUEUE.items():
+        if 'SITEURL' not in overrides:
+            overrides['SITEURL'] = posixpath.join(main_siteurl, lang)
+        _SITE_DB[lang] = overrides['SITEURL']
+        # default subsite hierarchy
+        if 'OUTPUT_PATH' not in overrides:
+            overrides['OUTPUT_PATH'] = os.path.join(
+                _MAIN_SETTINGS['OUTPUT_PATH'], lang)
+        if 'CACHE_PATH' not in overrides:
+            overrides['CACHE_PATH'] = os.path.join(
+                _MAIN_SETTINGS['CACHE_PATH'], lang)
+        if 'STATIC_PATHS' not in overrides:
+            overrides['STATIC_PATHS'] = []
+        if ('THEME' not in overrides and 'THEME_STATIC_DIR' not in overrides and
+                'THEME_STATIC_PATHS' not in overrides):
+            relpath = relpath_to_site(lang, _MAIN_LANG)
+            overrides['THEME_STATIC_DIR'] = posixpath.join(
+                relpath, _MAIN_SETTINGS['THEME_STATIC_DIR'])
+            overrides['THEME_STATIC_PATHS'] = []
+        # to change what is perceived as translations
+        overrides['DEFAULT_LANG'] = lang
+
+
+def subscribe_filter_to_signals(settings):
+    '''Subscribe content filter to requested signals'''
+    for sig in settings.get('I18N_FILTER_SIGNALS', []):
+        sig.connect(filter_contents_translations)
+
+
+def initialize_plugin(pelican_obj):
+    '''Initialize plugin variables and Pelican settings'''
+    if _MAIN_SETTINGS is None:
+        initialize_dbs(pelican_obj.settings)
+        subscribe_filter_to_signals(pelican_obj.settings)
+
+
+def get_site_path(url):
+    '''Get the path component of an url, excludes siteurl
+
+    also normalizes '' to '/' for relpath to work,
+    otherwise it could be interpreted as a relative filesystem path
+    '''
+    path = urlparse(url).path
+    if path == '':
+        path = '/'
+    return path
+
+
+def relpath_to_site(lang, target_lang):
+    '''Get relative path from siteurl of lang to siteurl of base_lang
+
+    the output is cached in _SITES_RELPATH_DB
+    '''
+    path = _SITES_RELPATH_DB.get((lang, target_lang), None)
+    if path is None:
+        siteurl = _SITE_DB.get(lang, _MAIN_SITEURL)
+        target_siteurl = _SITE_DB.get(target_lang, _MAIN_SITEURL)
+        path = posixpath.relpath(get_site_path(target_siteurl),
+                                 get_site_path(siteurl))
+        _SITES_RELPATH_DB[(lang, target_lang)] = path
+    return path
+
+
+def save_generator(generator):
+    '''Save the generator for later use
+
+    initialize the removed content list
+    '''
+    _GENERATOR_DB[generator] = []
+
+
+def article2draft(article):
+    '''Transform an Article to Draft'''
+    draft = Draft(article._content, article.metadata, article.settings,
+                  article.source_path, article._context)
+    draft.status = 'draft'
+    return draft
+
+
+def page2hidden_page(page):
+    '''Transform a Page to a hidden Page'''
+    page.status = 'hidden'
+    return page
+
+
+class GeneratorInspector(object):
+    '''Inspector of generator instances'''
+
+    generators_info = {
+        ArticlesGenerator: {
+            'translations_lists': ['translations', 'drafts_translations'],
+            'contents_lists': [('articles', 'drafts')],
+            'hiding_func': article2draft,
+            'policy': 'I18N_UNTRANSLATED_ARTICLES',
+        },
+        PagesGenerator: {
+            'translations_lists': ['translations', 'hidden_translations'],
+            'contents_lists': [('pages', 'hidden_pages')],
+            'hiding_func': page2hidden_page,
+            'policy': 'I18N_UNTRANSLATED_PAGES',
+        },
+    }
+
+    def __init__(self, generator):
+        '''Identify the best known class of the generator instance
+
+        The class '''
+        self.generator = generator
+        self.generators_info.update(generator.settings.get(
+            'I18N_GENERATORS_INFO', {}))
+        for cls in generator.__class__.__mro__:
+            if cls in self.generators_info:
+                self.info = self.generators_info[cls]
+                break
+        else:
+            self.info = {}
+
+    def translations_lists(self):
+        '''Iterator over lists of content translations'''
+        return (getattr(self.generator, name) for name in
+                self.info.get('translations_lists', []))
+
+    def contents_list_pairs(self):
+        '''Iterator over pairs of normal and hidden contents'''
+        return (tuple(getattr(self.generator, name) for name in names)
+                for names in self.info.get('contents_lists', []))
+
+    def hiding_function(self):
+        '''Function for transforming content to a hidden version'''
+        hiding_func = self.info.get('hiding_func', lambda x: x)
+        return hiding_func
+
+    def untranslated_policy(self, default):
+        '''Get the policy for untranslated content'''
+        return self.generator.settings.get(self.info.get('policy', None),
+                                           default)
+
+    def all_contents(self):
+        '''Iterator over all contents'''
+        translations_iterator = chain(*self.translations_lists())
+        return chain(translations_iterator,
+                     *(pair[i] for pair in self.contents_list_pairs()
+                       for i in (0, 1)))
+
+
+def filter_contents_translations(generator):
+    '''Filter the content and translations lists of a generator
+
+    Filters out
+        1) translations which will be generated in a different site
+        2) content that is not in the language of the currently
+        generated site but in that of a different site, content in a
+        language which has no site is generated always. The filtering
+        method bay be modified by the respective untranslated policy
+    '''
+    inspector = GeneratorInspector(generator)
+    current_lang = generator.settings['DEFAULT_LANG']
+    langs_with_sites = _SITE_DB.keys()
+    removed_contents = _GENERATOR_DB[generator]
+
+    for translations in inspector.translations_lists():
+        for translation in translations[:]:    # copy to be able to remove
+            if translation.lang in langs_with_sites:
+                translations.remove(translation)
+                removed_contents.append(translation)
+
+    hiding_func = inspector.hiding_function()
+    untrans_policy = inspector.untranslated_policy(default='hide')
+    for (contents, other_contents) in inspector.contents_list_pairs():
+        for content in other_contents: # save any hidden native content first
+            if content.lang == current_lang: # in native lang
+                # save the native URL attr formatted in the current locale
+                _NATIVE_CONTENT_URL_DB[content.source_path] = content.url
+        for content in contents[:]:        # copy for removing in loop
+            if content.lang == current_lang: # in native lang
+                # save the native URL attr formatted in the current locale
+                _NATIVE_CONTENT_URL_DB[content.source_path] = content.url
+            elif content.lang in langs_with_sites and untrans_policy != 'keep':
+                contents.remove(content)
+                if untrans_policy == 'hide':
+                    other_contents.append(hiding_func(content))
+                elif untrans_policy == 'remove':
+                    removed_contents.append(content)
+
+
+def install_templates_translations(generator):
+    '''Install gettext translations in the jinja2.Environment
+
+    Only if the 'jinja2.ext.i18n' jinja2 extension is enabled
+    the translations for the current DEFAULT_LANG are installed.
+    '''
+    if 'JINJA_ENVIRONMENT' in generator.settings: # pelican 3.7+
+        jinja_extensions = generator.settings['JINJA_ENVIRONMENT'].get(
+            'extensions', [])
+    else:
+        jinja_extensions = generator.settings['JINJA_EXTENSIONS']
+
+    if 'jinja2.ext.i18n' in jinja_extensions:
+        domain = generator.settings.get('I18N_GETTEXT_DOMAIN', 'messages')
+        localedir = generator.settings.get('I18N_GETTEXT_LOCALEDIR')
+        if localedir is None:
+            localedir = os.path.join(generator.theme, 'translations')
+        current_lang = generator.settings['DEFAULT_LANG']
+        if current_lang == generator.settings.get('I18N_TEMPLATES_LANG',
+                                                  _MAIN_LANG):
+            translations = gettext.NullTranslations()
+        else:
+            langs = [current_lang]
+            try:
+                translations = gettext.translation(domain, localedir, langs)
+            except (IOError, OSError):
+                _LOGGER.error((
+                    "Cannot find translations for language '{}' in '{}' with "
+                    "domain '{}'. Installing NullTranslations.").format(
+                        langs[0], localedir, domain))
+                translations = gettext.NullTranslations()
+        newstyle = generator.settings.get('I18N_GETTEXT_NEWSTYLE', True)
+        generator.env.install_gettext_translations(translations, newstyle)
+
+
+def add_variables_to_context(generator):
+    '''Adds useful iterable variables to template context'''
+    context = generator.context             # minimize attr lookup
+    context['relpath_to_site'] = relpath_to_site
+    context['main_siteurl'] = _MAIN_SITEURL
+    context['main_lang'] = _MAIN_LANG
+    context['lang_siteurls'] = _SITE_DB
+    current_lang = generator.settings['DEFAULT_LANG']
+    extra_siteurls = _SITE_DB.copy()
+    extra_siteurls.pop(current_lang)
+    context['extra_siteurls'] = extra_siteurls
+
+
+def interlink_translations(content):
+    '''Link content to translations in their main language
+
+    so the URL (including localized month names) of the different subsites
+    will be honored
+    '''
+    lang = content.lang
+    # sort translations by lang
+    content.translations.sort(key=attrgetter('lang'))
+    for translation in content.translations:
+        relpath = relpath_to_site(lang, translation.lang)
+        url = _NATIVE_CONTENT_URL_DB[translation.source_path]
+        translation.override_url = posixpath.join(relpath, url)
+
+
+def interlink_translated_content(generator):
+    '''Make translations link to the native locations
+
+    for generators that may contain translated content
+    '''
+    inspector = GeneratorInspector(generator)
+    for content in inspector.all_contents():
+        interlink_translations(content)
+
+
+def interlink_removed_content(generator):
+    '''For all contents removed from generation queue update interlinks
+
+    link to the native location
+    '''
+    current_lang = generator.settings['DEFAULT_LANG']
+    for content in _GENERATOR_DB[generator]:
+        url = _NATIVE_CONTENT_URL_DB[content.source_path]
+        relpath = relpath_to_site(current_lang, content.lang)
+        content.override_url = posixpath.join(relpath, url)
+
+
+def interlink_static_files(generator):
+    '''Add links to static files in the main site if necessary'''
+    if generator.settings['STATIC_PATHS'] != []:
+        return                               # customized STATIC_PATHS
+    try: # minimize attr lookup
+        static_content = generator.context['static_content']
+    except KeyError:
+        static_content = generator.context['filenames']
+    relpath = relpath_to_site(generator.settings['DEFAULT_LANG'], _MAIN_LANG)
+    for staticfile in _MAIN_STATIC_FILES:
+        if staticfile.get_relative_source_path() not in static_content:
+            staticfile = copy(staticfile) # prevent override in main site
+            staticfile.override_url = posixpath.join(relpath, staticfile.url)
+            try:
+                generator.add_source_path(staticfile, static=True)
+            except TypeError:
+                generator.add_source_path(staticfile)
+
+
+def save_main_static_files(static_generator):
+    '''Save the static files generated for the main site'''
+    global _MAIN_STATIC_FILES
+    # test just for current lang as settings change in autoreload mode
+    if static_generator.settings['DEFAULT_LANG'] == _MAIN_LANG:
+        _MAIN_STATIC_FILES = static_generator.staticfiles
+
+
+def update_generators():
+    '''Update the context of all generators
+
+    Ads useful variables and translations into the template context
+    and interlink translations
+    '''
+    for generator in _GENERATOR_DB.keys():
+        install_templates_translations(generator)
+        add_variables_to_context(generator)
+        interlink_static_files(generator)
+        interlink_removed_content(generator)
+        interlink_translated_content(generator)
+
+
+def get_pelican_cls(settings):
+    '''Get the Pelican class requested in settings'''
+    cls = settings['PELICAN_CLASS']
+    if isinstance(cls, six.string_types):
+        module, cls_name = cls.rsplit('.', 1)
+        module = __import__(module)
+        cls = getattr(module, cls_name)
+    return cls
+
+
+def create_next_subsite(pelican_obj):
+    '''Create the next subsite using the lang-specific config
+
+    If there are no more subsites in the generation queue, update all
+    the generators (interlink translations and removed content, add
+    variables and translations to template context). Otherwise get the
+    language and overrides for next the subsite in the queue and apply
+    overrides.  Then generate the subsite using a PELICAN_CLASS
+    instance and its run method. Finally, restore the previous locale.
+    '''
+    global _MAIN_SETTINGS
+    if len(_SUBSITE_QUEUE) == 0:
+        _LOGGER.debug(
+            'i18n: Updating cross-site links and context of all generators.')
+        update_generators()
+        _MAIN_SETTINGS = None             # to initialize next time
+    else:
+        with temporary_locale():
+            settings = _MAIN_SETTINGS.copy()
+            lang, overrides = _SUBSITE_QUEUE.popitem()
+            settings.update(overrides)
+            settings = configure_settings(settings)      # to set LOCALE, etc.
+            cls = get_pelican_cls(settings)
+
+            new_pelican_obj = cls(settings)
+            _LOGGER.debug(("Generating i18n subsite for language '{}' "
+                           "using class {}").format(lang, cls))
+            new_pelican_obj.run()
+
+
+# map: signal name -> function name
+_SIGNAL_HANDLERS_DB = {
+    'get_generators': initialize_plugin,
+    'article_generator_pretaxonomy': filter_contents_translations,
+    'page_generator_finalized': filter_contents_translations,
+    'get_writer': create_next_subsite,
+    'static_generator_finalized': save_main_static_files,
+    'generator_init': save_generator,
+}
+
+
+def register():
+    '''Register the plugin only if required signals are available'''
+    for sig_name in _SIGNAL_HANDLERS_DB.keys():
+        if not hasattr(signals, sig_name):
+            _LOGGER.error((
+                'The i18n_subsites plugin requires the {} '
+                'signal available for sure in Pelican 3.4.0 and later, '
+                'plugin will not be used.').format(sig_name))
+            return
+
+    for sig_name, handler in _SIGNAL_HANDLERS_DB.items():
+        sig = getattr(signals, sig_name)
+        sig.connect(handler)
diff --git a/pelican-plugins/i18n_subsites/implementing_language_buttons.rst b/pelican-plugins/i18n_subsites/implementing_language_buttons.rst
new file mode 100644
index 0000000000000000000000000000000000000000..55b7bf3e63aca7089fa63f4b89188ba5a5af6c88
--- /dev/null
+++ b/pelican-plugins/i18n_subsites/implementing_language_buttons.rst
@@ -0,0 +1,128 @@
+-----------------------------
+Implementing language buttons
+-----------------------------
+
+Each article with translations has translations links, but that's the
+only way to switch between language subsites.
+
+For this reason it is convenient to add language buttons to the top
+menu bar to make it simple to switch between the language subsites on
+all pages.
+
+Example designs
+---------------
+
+Language buttons showing other available languages
+..................................................
+
+The ``extra_siteurls`` dictionary is a mapping of all other (not the
+``DEFAULT_LANG`` of the current (sub-)site) languages to the
+``SITEURL`` of the respective (sub-)sites
+
+.. code-block:: jinja
+
+   <!-- SNIP -->
+   <nav><ul>
+   {% if extra_siteurls %}
+   {% for lang, url in extra_siteurls.items() %}
+   <li><a href="{{ url }}/">{{ lang }}</a></li>
+   {% endfor %}
+   <!-- separator -->
+   <li style="background-color: white; padding: 5px;">&nbsp</li>
+   {% endif %}
+   {% for title, link in MENUITEMS %}
+   <!-- SNIP -->
+
+Notice the slash ``/`` after ``{{ url }}``, this makes sure it works
+with local development when ``SITEURL == ''``.
+
+Language buttons showing all available languages, current is active
+...................................................................
+
+The ``lang_subsites`` dictionary is a mapping of all languages to the
+``SITEURL`` of the respective (sub-)sites. This template sets the
+language of the current (sub-)site as active.
+
+.. code-block:: jinja
+
+   <!-- SNIP -->
+   <nav><ul>
+   {% if lang_siteurls %}
+   {% for lang, url in lang_siteurls.items() %}
+   <li{% if lang == DEFAULT_LANG %} class="active"{% endif %}><a href="{{ url }}/">{{ lang }}</a></li>
+   {% endfor %}
+   <!-- separator -->
+   <li style="background-color: white; padding: 5px;">&nbsp</li>
+   {% endif %}
+   {% for title, link in MENUITEMS %}
+   <!-- SNIP -->
+
+
+Tips and tricks
+---------------
+
+Showing more than language codes
+................................
+
+If you want the language buttons to show e.g. the names of the
+languages or flags [#flags]_, not just the language codes, you can use
+a jinja filter to translate the language codes
+
+
+.. code-block:: python
+
+   languages_lookup = {
+		'en': 'English',
+		'cz': 'Čeština',
+		}
+
+   def lookup_lang_name(lang_code):
+       return languages_lookup[lang_code]
+
+   JINJA_FILTERS = {
+		...
+		'lookup_lang_name': lookup_lang_name,
+		}
+
+And then the link content becomes
+
+.. code-block:: jinja
+
+   <!-- SNIP -->
+   {% for lang, siteurl in lang_siteurls.items() %}
+   <li{% if lang == DEFAULT_LANG %} class="active"{% endif %}><a href="{{ siteurl }}/">{{ lang | lookup_lang_name }}</a></li>
+   {% endfor %}
+   <!-- SNIP -->
+
+
+Changing the order of language buttons
+......................................
+
+Because ``lang_siteurls`` and ``extra_siteurls`` are instances of
+``OrderedDict`` with ``main_lang`` being always the first key, you can
+change the order through a jinja filter.
+
+.. code-block:: python
+
+   def my_ordered_items(ordered_dict):
+       items = list(ordered_dict.items())
+       # swap first and last using tuple unpacking
+       items[0], items[-1] = items[-1], items[0]
+       return items
+
+   JINJA_FILTERS = {
+		...
+		'my_ordered_items': my_ordered_items,
+		}
+
+And then the ``for`` loop line in the template becomes
+
+.. code-block:: jinja
+
+   <!-- SNIP -->
+   {% for lang, siteurl in lang_siteurls | my_ordered_items %}
+   <!-- SNIP -->
+
+
+.. [#flags] Although it may look nice, `w3 discourages it
+            <http://www.w3.org/TR/i18n-html-tech-lang/#ri20040808.173208643>`_.
diff --git a/pelican-plugins/i18n_subsites/localizing_using_jinja2.rst b/pelican-plugins/i18n_subsites/localizing_using_jinja2.rst
new file mode 100644
index 0000000000000000000000000000000000000000..a28beddf8b1d925d490955bb2e4bc58e45a641ca
--- /dev/null
+++ b/pelican-plugins/i18n_subsites/localizing_using_jinja2.rst
@@ -0,0 +1,202 @@
+-----------------------------
+Localizing themes with Jinja2
+-----------------------------
+
+1. Localize templates
+---------------------
+
+To enable the |ext| extension in your templates, you must add it to
+``JINJA_ENVIRONMENT`` in your Pelican configuration
+
+.. code-block:: python
+
+  JINJA_ENVIRONMENT = {
+    'extensions': ['jinja2.ext.i18n', ...]
+  }
+
+Then follow the `Jinja2 templating documentation for the I18N plugin
+<http://jinja.pocoo.org/docs/templates/#i18n>`_ to make your templates
+localizable. This usually means surrounding strings with the ``{%
+trans %}`` directive or using ``gettext()`` in expressions
+
+.. code-block:: jinja
+
+    {% trans %}translatable content{% endtrans %}
+    {{ gettext('a translatable string') }}
+
+For pluralization support, etc. consult the documentation.
+
+To enable `newstyle gettext calls
+<http://jinja.pocoo.org/docs/extensions/#newstyle-gettext>`_ the
+``I18N_GETTEXT_NEWSTYLE`` config variable must be set to ``True``
+(default).
+
+.. |ext| replace:: ``jinja2.ext.i18n``
+
+2. Specify translations location
+--------------------------------
+
+The |ext| extension uses the `Python gettext library
+<http://docs.python.org/library/gettext.html>`_ for translating
+strings.
+
+In your Pelican config you can give the path in which to look for
+translations in the ``I18N_GETTEXT_LOCALEDIR`` variable. If not given,
+it is assumed to be the ``translations`` subfolder in the top folder
+of the theme specified by ``THEME``.
+
+The domain of the translations (the name of each translation file is
+``domain.mo``) is controlled by the ``I18N_GETTEXT_DOMAIN`` config
+variable (defaults to ``messages``).
+
+Example
+.......
+
+With the following in your Pelican settings file
+
+.. code-block:: python
+
+  I18N_GETTEXT_LOCALEDIR = 'some/path/'
+  I18N_GETTEXT_DOMAIN = 'my_domain'
+
+the translation for language 'cz' will be expected to be in
+``some/path/cz/LC_MESSAGES/my_domain.mo``
+
+
+3. Extract translatable strings and translate them
+--------------------------------------------------
+
+There are many ways to extract translatable strings and create
+``gettext`` compatible translations. You can create the ``*.po`` and
+``*.mo`` message catalog files yourself, or you can use some helper
+tool as described in `the Python gettext library tutorial
+<http://docs.python.org/library/gettext.html#internationalizing-your-programs-and-modules>`_.
+
+You of course don't need to provide a translation for the language in
+which the templates are written which is assumed to be the original
+``DEFAULT_LANG``. This can be overridden in the
+``I18N_TEMPLATES_LANG`` variable.
+
+Recommended tool: babel
+.......................
+
+`Babel <http://babel.pocoo.org/>`_ makes it easy to extract
+translatable strings from the localized Jinja2 templates and assists
+with creating translations as documented in this `Jinja2-Babel
+tutorial
+<http://pythonhosted.org/Flask-Babel/#translating-applications>`_
+[#flask]_ on which the following is based.
+
+1. Add babel mapping
+~~~~~~~~~~~~~~~~~~~~
+
+Let's assume that you are localizing a theme in ``themes/my_theme/``
+and that you use the default settings, i.e. the default domain
+``messages`` and will put the translations in the ``translations``
+subdirectory of the theme directory as
+``themes/my_theme/translations/``.
+
+It is up to you where to store babel mappings and translation files
+templates (``*.pot``), but a convenient place is to put them in
+``themes/my_theme/`` and work in that directory. From now on let's
+assume that it will be our current working directory (CWD).
+
+To tell babel to extract translatable strings from the templates
+create a mapping file ``babel.cfg`` with the following line
+
+.. code-block:: cfg
+
+    [jinja2: templates/**.html]
+
+
+2. Extract translatable strings from templates
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Run the following command to create a ``messages.pot`` message catalog
+template file from extracted translatable strings
+
+.. code-block:: bash
+
+    pybabel extract --mapping babel.cfg --output messages.pot ./
+
+
+3. Initialize message catalogs
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If you want to translate the template to language ``lang``, run the
+following command to create a message catalog
+``translations/lang/LC_MESSAGES/messages.po`` using the template
+``messages.pot``
+
+.. code-block:: bash
+
+    pybabel init --input-file messages.pot --output-dir translations/ --locale lang --domain messages
+
+babel expects ``lang`` to be a valid locale identifier, so if e.g. you
+are translating for language ``cz`` but the corresponding locale is
+``cs``, you have to use the locale identifier. Nevertheless, the
+gettext infrastructure should later correctly find the locale for a
+given language.
+
+4. Fill the message catalogs
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The message catalog files format is quite intuitive, it is fully
+documented in the `GNU gettext manual
+<http://www.gnu.org/software/gettext/manual/gettext.html#PO-Files>`_. Essentially,
+you fill in the ``msgstr`` strings
+
+
+.. code-block:: po
+
+    msgid "just a simple string"
+    msgstr "jenom jednoduchý řetězec"
+
+    msgid ""
+    "some multiline string"
+    "looks like this"
+    msgstr ""
+    "nějaký více řádkový řetězec"
+    "vypadá takto"
+
+You might also want to remove ``#,fuzzy`` flags once the translation
+is complete and reviewed to show that it can be compiled.
+
+5. Compile the message catalogs
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The message catalogs must be compiled into binary format using this
+command
+
+.. code-block:: bash
+
+    pybabel compile --directory translations/ --domain messages
+
+This command might complain about "fuzzy" translations, which means
+you should review the translations and once done, remove the fuzzy
+flag line.
+
+(6.) Update the catalogs when templates change
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If you add any translatable patterns into your templates, you have to
+update your message catalogs too.  First you extract a new message
+catalog template as described in the 2. step. Then you run the
+following command [#pybabel_error]_
+
+.. code-block:: bash
+
+   pybabel update --input-file messages.pot --output-dir translations/ --domain messages
+
+This will merge the new patterns with the old ones. Once you review
+and fill them, you have to recompile them as described in the 5. step.
+
+.. [#flask] Although the tutorial is focused on Flask-based web
+            applications, the linked translation tutorial is not
+            Flask-specific.
+.. [#pybabel_error] If you get an error ``TypeError: must be str, not
+                    bytes`` with Python 3.3, it is likely you are
+                    suffering from this `bug
+                    <https://github.com/mitsuhiko/flask-babel/issues/43>`_.
+                    Until the fix is released, you can use babel with
+                    Python 2.7.
diff --git a/pelican-plugins/i18n_subsites/test_data/__init__.py b/pelican-plugins/i18n_subsites/test_data/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/pelican-plugins/i18n_subsites/test_data/content/images/img.png b/pelican-plugins/i18n_subsites/test_data/content/images/img.png
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/pelican-plugins/i18n_subsites/test_data/content/pages/hidden-page-cz.rst b/pelican-plugins/i18n_subsites/test_data/content/pages/hidden-page-cz.rst
new file mode 100644
index 0000000000000000000000000000000000000000..c282faa7899e76ea8ea4ac70ce8a8558ba517199
--- /dev/null
+++ b/pelican-plugins/i18n_subsites/test_data/content/pages/hidden-page-cz.rst
@@ -0,0 +1,7 @@
+404 stránka
+===========
+:slug: 404
+:lang: cz
+:status: hidden
+
+Jednoduchá 404 stránka.
diff --git a/pelican-plugins/i18n_subsites/test_data/content/pages/hidden-page-de.rst b/pelican-plugins/i18n_subsites/test_data/content/pages/hidden-page-de.rst
new file mode 100644
index 0000000000000000000000000000000000000000..d8410a152a08a7599686c7a3b7fda582197557ae
--- /dev/null
+++ b/pelican-plugins/i18n_subsites/test_data/content/pages/hidden-page-de.rst
@@ -0,0 +1,7 @@
+Eine 404 Seite
+==============
+:slug: 404
+:lang: de
+:status: hidden
+
+Eine einfache 404 Seite.
diff --git a/pelican-plugins/i18n_subsites/test_data/content/pages/hidden-page-en.rst b/pelican-plugins/i18n_subsites/test_data/content/pages/hidden-page-en.rst
new file mode 100644
index 0000000000000000000000000000000000000000..74a97d79e1c7a08c5642264753904b7a59c96e5b
--- /dev/null
+++ b/pelican-plugins/i18n_subsites/test_data/content/pages/hidden-page-en.rst
@@ -0,0 +1,7 @@
+A 404 page
+==========
+:slug: 404
+:lang: en
+:status: hidden
+
+A simple 404 page.
diff --git a/pelican-plugins/i18n_subsites/test_data/content/pages/untranslated-page.rst b/pelican-plugins/i18n_subsites/test_data/content/pages/untranslated-page.rst
new file mode 100644
index 0000000000000000000000000000000000000000..ae4c2b867fc62e88132e2d315fdf3973b1bb5c3e
--- /dev/null
+++ b/pelican-plugins/i18n_subsites/test_data/content/pages/untranslated-page.rst
@@ -0,0 +1,5 @@
+Untranslated page
+=================
+:lang: en
+
+This page has no translation.
diff --git a/pelican-plugins/i18n_subsites/test_data/content/translated_article-cz.rst b/pelican-plugins/i18n_subsites/test_data/content/translated_article-cz.rst
new file mode 100644
index 0000000000000000000000000000000000000000..555a69d7d3513e1c2b255885e41350c175fd0dc6
--- /dev/null
+++ b/pelican-plugins/i18n_subsites/test_data/content/translated_article-cz.rst
@@ -0,0 +1,8 @@
+Přeložený článek
+================
+:slug: translated-article
+:lang: cz
+:date: 2014-09-15
+
+Jednoduchý článek s překlady.
+Zde je odkaz na `nějaký obrázek <{filename}/images/img.png>`_.
diff --git a/pelican-plugins/i18n_subsites/test_data/content/translated_article-de.rst b/pelican-plugins/i18n_subsites/test_data/content/translated_article-de.rst
new file mode 100644
index 0000000000000000000000000000000000000000..01bf565f73a07f2e45323fd0c13e96635ba2442c
--- /dev/null
+++ b/pelican-plugins/i18n_subsites/test_data/content/translated_article-de.rst
@@ -0,0 +1,8 @@
+Ein übersetzter Artikel
+=======================
+:slug: translated-article
+:lang: de
+:date: 2014-09-14
+
+Ein einfacher Artikel mit einer Ãœbersetzung.
+Hier ist ein Link zur `einigem Bild <{filename}/images/img.png>`_.
diff --git a/pelican-plugins/i18n_subsites/test_data/content/translated_article-en.rst b/pelican-plugins/i18n_subsites/test_data/content/translated_article-en.rst
new file mode 100644
index 0000000000000000000000000000000000000000..d7f5dad274e9c8db04e252e5b87885afbed12e7e
--- /dev/null
+++ b/pelican-plugins/i18n_subsites/test_data/content/translated_article-en.rst
@@ -0,0 +1,8 @@
+A translated article
+====================
+:slug: translated-article
+:lang: en
+:date: 2014-09-13
+
+A simple article with a translation.
+Here is a link to `some image <{filename}/images/img.png>`_.
diff --git a/pelican-plugins/i18n_subsites/test_data/content/untranslated_article-en.rst b/pelican-plugins/i18n_subsites/test_data/content/untranslated_article-en.rst
new file mode 100644
index 0000000000000000000000000000000000000000..867ae5d86fad8d6d70c40c509f92a0897ecd4293
--- /dev/null
+++ b/pelican-plugins/i18n_subsites/test_data/content/untranslated_article-en.rst
@@ -0,0 +1,9 @@
+An untranslated article
+=======================
+:date: 2014-07-14
+:lang: en
+
+An article without a translation.
+Here is a link to an `untranslated page`_
+
+.. _`untranslated page`: {filename}/pages/untranslated-page.rst
diff --git a/pelican-plugins/i18n_subsites/test_data/localized_theme/babel.cfg b/pelican-plugins/i18n_subsites/test_data/localized_theme/babel.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..440673245823c4b5195689fef85e3ac0a6ab6684
--- /dev/null
+++ b/pelican-plugins/i18n_subsites/test_data/localized_theme/babel.cfg
@@ -0,0 +1,2 @@
+[jinja2: templates/**.html]
+
diff --git a/pelican-plugins/i18n_subsites/test_data/localized_theme/messages.pot b/pelican-plugins/i18n_subsites/test_data/localized_theme/messages.pot
new file mode 100644
index 0000000000000000000000000000000000000000..578917f449f698444e37129086f2d3bd4063158c
--- /dev/null
+++ b/pelican-plugins/i18n_subsites/test_data/localized_theme/messages.pot
@@ -0,0 +1,23 @@
+# Translations template for PROJECT.
+# Copyright (C) 2014 ORGANIZATION
+# This file is distributed under the same license as the PROJECT project.
+# FIRST AUTHOR <EMAIL@ADDRESS>, 2014.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PROJECT VERSION\n"
+"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
+"POT-Creation-Date: 2014-07-13 12:25+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 1.3\n"
+
+#: templates/base.html:3
+msgid "Welcome to our"
+msgstr ""
+
diff --git a/pelican-plugins/i18n_subsites/test_data/localized_theme/static/style.css b/pelican-plugins/i18n_subsites/test_data/localized_theme/static/style.css
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/pelican-plugins/i18n_subsites/test_data/localized_theme/templates/base.html b/pelican-plugins/i18n_subsites/test_data/localized_theme/templates/base.html
new file mode 100644
index 0000000000000000000000000000000000000000..a24eb1db8d0ed296b656a3aed62cf13599dbc6cb
--- /dev/null
+++ b/pelican-plugins/i18n_subsites/test_data/localized_theme/templates/base.html
@@ -0,0 +1,7 @@
+{% extends "!simple/base.html" %}
+
+{% block title %}{% trans %}Welcome to our{% endtrans %} {{ SITENAME }}{% endblock %}
+{% block head %}
+{{ super() }}
+<link rel="stylesheet" href="{{ SITEURL }}/{{ THEME_STATIC_DIR }}/style.css" />
+{% endblock %}
diff --git a/pelican-plugins/i18n_subsites/test_data/localized_theme/translations/de/LC_MESSAGES/messages.mo b/pelican-plugins/i18n_subsites/test_data/localized_theme/translations/de/LC_MESSAGES/messages.mo
new file mode 100644
index 0000000000000000000000000000000000000000..239010221fe789ec007296b700578fd5114ab279
Binary files /dev/null and b/pelican-plugins/i18n_subsites/test_data/localized_theme/translations/de/LC_MESSAGES/messages.mo differ
diff --git a/pelican-plugins/i18n_subsites/test_data/localized_theme/translations/de/LC_MESSAGES/messages.po b/pelican-plugins/i18n_subsites/test_data/localized_theme/translations/de/LC_MESSAGES/messages.po
new file mode 100644
index 0000000000000000000000000000000000000000..2eb4efbb6b21787423781c59cccffe0830618d1a
--- /dev/null
+++ b/pelican-plugins/i18n_subsites/test_data/localized_theme/translations/de/LC_MESSAGES/messages.po
@@ -0,0 +1,23 @@
+# German translations for PROJECT.
+# Copyright (C) 2014 ORGANIZATION
+# This file is distributed under the same license as the PROJECT project.
+# FIRST AUTHOR <EMAIL@ADDRESS>, 2014.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: PROJECT VERSION\n"
+"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
+"POT-Creation-Date: 2014-07-13 12:25+0200\n"
+"PO-Revision-Date: 2014-07-13 12:26+0200\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: de <LL@li.org>\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 1.3\n"
+
+#: templates/base.html:3
+msgid "Welcome to our"
+msgstr "Willkommen Sie zur unserer"
+
diff --git a/pelican-plugins/i18n_subsites/test_data/pelicanconf.py b/pelican-plugins/i18n_subsites/test_data/pelicanconf.py
new file mode 100644
index 0000000000000000000000000000000000000000..55018f27101df879c31f57903dad4e6c9e88201f
--- /dev/null
+++ b/pelican-plugins/i18n_subsites/test_data/pelicanconf.py
@@ -0,0 +1,53 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*- #
+from __future__ import unicode_literals
+
+AUTHOR = 'The Tester'
+SITENAME = 'Testing site'
+SITEURL = 'http://example.com/test'
+
+# to make the test suite portable
+TIMEZONE = 'UTC'
+
+DEFAULT_LANG = 'en'
+LOCALE = 'en_US.UTF-8'
+
+# Generate only one feed
+FEED_ALL_ATOM = 'feeds_all.atom.xml'
+CATEGORY_FEED_ATOM = None
+TRANSLATION_FEED_ATOM = None
+AUTHOR_FEED_ATOM = None
+AUTHOR_FEED_RSS = None
+
+# Disable unnecessary pages
+CATEGORY_SAVE_AS = ''
+TAG_SAVE_AS = ''
+AUTHOR_SAVE_AS = ''
+ARCHIVES_SAVE_AS = ''
+AUTHORS_SAVE_AS = ''
+CATEGORIES_SAVE_AS = ''
+TAGS_SAVE_AS = ''
+
+PLUGIN_PATHS = ['../../']
+PLUGINS = ['i18n_subsites']
+
+THEME = 'localized_theme'
+JINJA_ENVIRONMENT = {'extensions': ['jinja2.ext.i18n']}
+
+from blinker import signal
+tmpsig = signal('tmpsig')
+I18N_FILTER_SIGNALS = [tmpsig]
+
+I18N_SUBSITES = {
+    'de': {
+        'SITENAME': 'Testseite',
+        'AUTHOR': 'Der Tester',
+        'LOCALE': 'de_DE.UTF-8',
+        },
+    'cz': {
+        'SITENAME': 'Testovací stránka',
+        'AUTHOR': 'Test Testovič',
+        'I18N_UNTRANSLATED_PAGES': 'remove',
+        'I18N_UNTRANSLATED_ARTICLES': 'keep',
+        },
+    }
diff --git a/pelican-plugins/i18n_subsites/test_i18n_subsites.py b/pelican-plugins/i18n_subsites/test_i18n_subsites.py
new file mode 100644
index 0000000000000000000000000000000000000000..83d0cb986275e1f1ef18278b554bf0c41be0d2bb
--- /dev/null
+++ b/pelican-plugins/i18n_subsites/test_i18n_subsites.py
@@ -0,0 +1,139 @@
+'''Unit tests for the i18n_subsites plugin'''
+
+import os
+import locale
+import unittest
+import subprocess
+from tempfile import mkdtemp
+from shutil import rmtree
+
+from . import i18n_subsites as i18ns
+from pelican import Pelican
+from pelican.tests.support import get_settings
+from pelican.settings import read_settings
+
+
+class TestTemporaryLocale(unittest.TestCase):
+    '''Test the temporary locale context manager'''
+
+    def test_locale_restored(self):
+        '''Test that the locale is restored after exiting context'''
+        orig_locale = locale.setlocale(locale.LC_ALL)
+        with i18ns.temporary_locale():
+            locale.setlocale(locale.LC_ALL, 'C')
+            self.assertEqual(locale.setlocale(locale.LC_ALL), 'C')
+        self.assertEqual(locale.setlocale(locale.LC_ALL), orig_locale)
+
+    def test_temp_locale_set(self):
+        '''Test that the temporary locale is set'''
+        with i18ns.temporary_locale('C'):
+            self.assertEqual(locale.setlocale(locale.LC_ALL), 'C')
+
+
+class TestSettingsManipulation(unittest.TestCase):
+    '''Test operations on settings dict'''
+
+    def setUp(self):
+        '''Prepare default settings'''
+        self.settings = get_settings()
+
+    def test_get_pelican_cls_class(self):
+        '''Test that we get class given as an object'''
+        self.settings['PELICAN_CLASS'] = object
+        cls = i18ns.get_pelican_cls(self.settings)
+        self.assertIs(cls, object)
+        
+    def test_get_pelican_cls_str(self):
+        '''Test that we get correct class given by string'''
+        cls = i18ns.get_pelican_cls(self.settings)
+        self.assertIs(cls, Pelican)
+        
+
+class TestSitesRelpath(unittest.TestCase):
+    '''Test relative path between sites generation'''
+
+    def setUp(self):
+        '''Generate some sample siteurls'''
+        self.siteurl = 'http://example.com'
+        i18ns._SITE_DB['en'] = self.siteurl
+        i18ns._SITE_DB['de'] = self.siteurl + '/de'
+
+    def tearDown(self):
+        '''Remove sites from db'''
+        i18ns._SITE_DB.clear()
+
+    def test_get_site_path(self):
+        '''Test getting the path within a site'''
+        self.assertEqual(i18ns.get_site_path(self.siteurl), '/')
+        self.assertEqual(i18ns.get_site_path(self.siteurl + '/de'), '/de')
+
+    def test_relpath_to_site(self):
+        '''Test getting relative paths between sites'''
+        self.assertEqual(i18ns.relpath_to_site('en', 'de'), 'de')
+        self.assertEqual(i18ns.relpath_to_site('de', 'en'), '..')
+
+        
+class TestRegistration(unittest.TestCase):
+    '''Test plugin registration'''
+
+    def test_return_on_missing_signal(self):
+        '''Test return on missing required signal'''
+        i18ns._SIGNAL_HANDLERS_DB['tmp_sig'] = None
+        i18ns.register()
+        self.assertNotIn(id(i18ns.save_generator),
+                         i18ns.signals.generator_init.receivers)
+
+    def test_registration(self):
+        '''Test registration of all signal handlers'''
+        i18ns.register()
+        for sig_name, handler in i18ns._SIGNAL_HANDLERS_DB.items():
+            sig = getattr(i18ns.signals, sig_name)
+            self.assertIn(id(handler), sig.receivers)
+            # clean up
+            sig.disconnect(handler)
+        
+
+class TestFullRun(unittest.TestCase):
+    '''Test running Pelican with the Plugin'''
+
+    def setUp(self):
+        '''Create temporary output and cache folders'''
+        self.temp_path = mkdtemp(prefix='pelicantests.')
+        self.temp_cache = mkdtemp(prefix='pelican_cache.')
+
+    def tearDown(self):
+        '''Remove output and cache folders'''
+        rmtree(self.temp_path)
+        rmtree(self.temp_cache)
+
+    def test_sites_generation(self):
+        '''Test generation of sites with the plugin
+
+        Compare with recorded output via ``git diff``.
+        To generate output for comparison run the command
+        ``pelican -o test_data/output -s test_data/pelicanconf.py \
+        test_data/content``
+        Remember to remove the output/ folder before that.
+        '''
+        base_path = os.path.dirname(os.path.abspath(__file__))
+        base_path = os.path.join(base_path, 'test_data')
+        content_path = os.path.join(base_path, 'content')
+        output_path = os.path.join(base_path, 'output')
+        settings_path = os.path.join(base_path, 'pelicanconf.py')
+        settings = read_settings(path=settings_path, override={
+            'PATH': content_path,
+            'OUTPUT_PATH': self.temp_path,
+            'CACHE_PATH': self.temp_cache,
+            'PLUGINS': [i18ns],
+            }
+        )
+        pelican = Pelican(settings)
+        pelican.run()
+
+        # compare output
+        out, err = subprocess.Popen(
+            ['git', 'diff', '--no-ext-diff', '--exit-code', '-w', output_path,
+             self.temp_path], env={'PAGER': ''},
+            stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
+        self.assertFalse(out, 'non-empty `diff` stdout:\n{}'.format(out))
+        self.assertFalse(err, 'non-empty `diff` stderr:\n{}'.format(err))
diff --git a/pelican-plugins/ical/Readme.rst b/pelican-plugins/ical/Readme.rst
new file mode 100644
index 0000000000000000000000000000000000000000..e8897d1d07e8b3615194802017e2677136a6600a
--- /dev/null
+++ b/pelican-plugins/ical/Readme.rst
@@ -0,0 +1,39 @@
+ical
+----
+
+This plugin looks for and parses an ``.ics`` file if it is defined in a given
+page's ``calendar`` metadata. One calendar can be defined per page.
+
+Dependencies
+------------
+
+This plugin depends on the ``icalendar`` package, which can be installed via
+pip::
+
+	pip install icalendar
+
+Usage
+-----
+
+For a reST-formatted page, include the following line in the metadata::
+
+    :calendar: /path/to/your/ics/file
+
+For Markdown, include the following line in the page metadata::
+
+    Calendar: /path/to/your/ics/file
+
+Following is some example code that can be added to your theme's ``page.html``
+template in order to display the calendar::
+
+    {% if page.calendar %}
+    <dl>
+        {% for vevent in events[page.slug] %}
+            <dt>{{ vevent.summary }}</dt>
+            <dd>{{ vevent.description|replace('\n\n', '<br>') }}</dd>
+            <dd>{{ vevent.dtstart }}</dd>
+            <dd>{{ vevent.dtend }}</dd>
+            <dd class="footer"><a href="{{ vevent.url }}">See more</a></dd>
+        {% endfor %}
+    </dl>
+    {% endif %}
diff --git a/pelican-plugins/ical/__init__.py b/pelican-plugins/ical/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..8a1352a317bc425d494da962d34db25e8e197652
--- /dev/null
+++ b/pelican-plugins/ical/__init__.py
@@ -0,0 +1 @@
+from .ical import *
diff --git a/pelican-plugins/ical/ical.py b/pelican-plugins/ical/ical.py
new file mode 100644
index 0000000000000000000000000000000000000000..b6034a96f9c9fa81344c2c8f4f161ce4f1ad63db
--- /dev/null
+++ b/pelican-plugins/ical/ical.py
@@ -0,0 +1,52 @@
+# -*- coding: utf-8 -*-
+"""
+ical plugin for Pelican
+=======================
+
+This plugin looks for and parses an .ics file if it is defined in a given
+page's :calendar: metadata. One calendar can be defined per page.
+
+"""
+
+from icalendar import Calendar, Event
+from pelican import signals , utils
+import pytz
+import datetime
+import os.path
+
+def init_cal(generator):
+    # initialisation of the calendar dictionary
+    # you can add one calendar per page
+    calDict = {}
+    generator.context['events'] = calDict
+
+def add_ical(generator, metadata):
+    # check if a calendar is here
+    if 'calendar' in metadata.keys():
+        summ = []
+        path = metadata['calendar']
+        if not os.path.isabs(path):
+            path = os.path.abspath(metadata['calendar'])
+        cal = Calendar.from_ical(open(path,'rb').read())
+        for element in cal.walk():
+            eventdict = {}
+            if element.name == "VEVENT":
+                if element.get('summary') != None:
+                    eventdict['summary'] = element.get('summary')
+                if element.get('description') != None:
+                    eventdict['description'] = element.get('description')
+                if element.get('url') != None:
+                    eventdict['url'] = element.get('url')
+                if element.get('dtstart') != None:
+                    eventdict['dtstart'] = element.get('dtstart').dt
+                if element.get('dtend') != None:
+                    eventdict['dtend'] = element.get('dtend').dt
+                summ.append(eventdict)
+        # the id of the calendar is the slugified name of the page
+        calId = utils.slugify(metadata['title'])
+        generator.context['events'][calId] = summ
+
+
+def register():
+    signals.page_generator_init.connect(init_cal)
+    signals.page_generator_context.connect(add_ical)
diff --git a/pelican-plugins/ical/requirements.txt b/pelican-plugins/ical/requirements.txt
new file mode 100755
index 0000000000000000000000000000000000000000..44466d5c771da8bc9bdc55314155c7edd7cf266a
--- /dev/null
+++ b/pelican-plugins/ical/requirements.txt
@@ -0,0 +1 @@
+icalendar
\ No newline at end of file
diff --git a/pelican-plugins/interlinks/__init__.py b/pelican-plugins/interlinks/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..397894ebbba877ff87d02c211b02bf6ad1417786
--- /dev/null
+++ b/pelican-plugins/interlinks/__init__.py
@@ -0,0 +1 @@
+from .interlinks import *
diff --git a/pelican-plugins/interlinks/interlinks.py b/pelican-plugins/interlinks/interlinks.py
new file mode 100644
index 0000000000000000000000000000000000000000..82d7f0a2448d660f21632182128b0ff6aa66ca70
--- /dev/null
+++ b/pelican-plugins/interlinks/interlinks.py
@@ -0,0 +1,73 @@
+# -*- coding: utf-8 -*-
+"""
+Interlinks
+=========================
+This plugin allows you to include "interwiki" or shortcuts links into the blog,
+as keyword>rest_of_url
+"""
+import re
+
+from pelican import signals
+
+from bs4 import BeautifulSoup
+from bs4 import SoupStrainer
+
+interlinks = {}
+
+
+def getSettings(generator):
+
+    global interlinks
+
+    interlinks = {'this': generator.settings['SITEURL']+"/"}
+    if 'INTERLINKS' in generator.settings:
+        for key, value in generator.settings['INTERLINKS'].items():
+            interlinks[key] = value
+
+
+def parse_links(instance):
+
+    if instance._content is not None:
+        content = instance._content
+
+        if '<a' in content:
+            text = BeautifulSoup(
+                content, "html.parser", parse_only=SoupStrainer("a"))
+            for link in text.find_all("a", href=re.compile("(.+?)>")):
+                old_tag = link.decode()
+                url = link.get('href')
+                m = re.search(r"(.+?)>", url).groups()
+                name = m[0]
+                if name in interlinks:
+                    hi = url.replace(name + ">", interlinks[name])
+                    link['href'] = hi
+
+                content = content.replace(old_tag, link.decode())
+
+        if '<img' in content:
+            text = BeautifulSoup(
+                content, "html.parser", parse_only=SoupStrainer("img"))
+            for img in text.find_all('img', src=re.compile("(.+?)>")):
+                old_tag = img.decode()
+                url = img.get('src')
+                m = re.search(r"(.+?)>", url).groups()
+                name = m[0]
+                if name in interlinks:
+                    hi = url.replace(name+">", interlinks[name])
+                    img['src'] = hi
+
+                # generated output has no trailing slash; match for replacement
+                repaired_old_tag = old_tag.replace("/>", ">")
+
+                content = content.replace(
+                    repaired_old_tag,
+                    img.decode()
+                )
+
+
+        instance._content = content
+
+
+def register():
+    signals.generator_init.connect(getSettings)
+    signals.content_object_init.connect(parse_links)
diff --git a/pelican-plugins/interlinks/readme.md b/pelican-plugins/interlinks/readme.md
new file mode 100644
index 0000000000000000000000000000000000000000..1ee2d2e481a1b6e0d83bd2b5d00252540987e9be
--- /dev/null
+++ b/pelican-plugins/interlinks/readme.md
@@ -0,0 +1,50 @@
+Interlinks
+==========
+
+This plugin lets you add frequently-used URLs to your markup using short keywords.
+Short URL format is `keyword>rest-of-url` where `keyword` is defined in your Pelican
+settings file. This is subsequently replaced with the actual URL in the generated
+HTML output.
+
+
+Requirements
+------------
+
+This plugin requires BeautifulSoup:
+
+	pip install beautifulsoup4
+
+Installation
+------------
+
+Put the plugin into your plugins folder, then add Interlinks in your settings file:
+
+	PLUGINS = ["interlinks"]
+
+Usage
+-----
+
+Interlinks are specified in your settings file. Here is an example:
+
+	INTERLINKS = {
+	    'wikipedia_en': 'http://en.wikipedia.org/wiki/',
+	    'wikipedia_es': 'http://es.wikipedia.org/wiki/',
+	    'ddg': 'https://duckduckgo.com/?q='
+	}
+
+There's also a default key, `this`, that is mapped to the `SITEURL` variable.
+
+Then, in your content, you just create a normal link but add the `keyword>` syntax as the URL scheme, followed by the rest of the URL.
+
+Example (Markdown syntax)
+-------------------------
+
+	[Normal boring link](http://www.example.com). But this is a [cool link](this>) that links to this site.
+
+	Search in [Wikipedia](wikipedia_en>python), ([here](wikipedia_es>python) in Spanish). You can also [search](ddg>python) it.
+
+All the above will be rendered as:
+
+	<p><a href="http://www.example.com">Normal boring link</a>. But this is a <a href="http://[yoursite]/index.html">cool link</a> that links to this site.</p>
+
+	<p>Search in <a href="http://en.wikipedia.org/wiki/python">Wikipedia</a>, (<a href="http://es.wikipedia.org/wiki/python">here</a> in Spanish). You can also <a href="https://duckduckgo.com/?q=python">search</a> it.</p>
diff --git a/pelican-plugins/interlinks/test_data/testme.md b/pelican-plugins/interlinks/test_data/testme.md
new file mode 100644
index 0000000000000000000000000000000000000000..332deb6f9c99de8e0bb0e7f2a62f5176adb0f580
--- /dev/null
+++ b/pelican-plugins/interlinks/test_data/testme.md
@@ -0,0 +1,9 @@
+Title: Testing
+Date: 3000-07-09
+Slug: plugin-test
+
+Testeando un poco la cosa
+
+[Normal boring link](http://www.example.com). But this is a [cool link](this>) that links to this site.
+
+Search in [Wikipedia](wikipedia_en>python), ([here](wikipedia_es>python) in spanish). Also can [search](ddg>python) it.
diff --git a/pelican-plugins/jinja2content/README.md b/pelican-plugins/jinja2content/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..aa49f7126c17fff7e7664bcb17c3a89ed1d7a8f8
--- /dev/null
+++ b/pelican-plugins/jinja2content/README.md
@@ -0,0 +1,116 @@
+# Jinja2 Content
+
+**NOTE: [This plugin has been moved to its own repository](https://github.com/pelican-plugins/jinja2content). Please file any issues/PRs there. Once all plugins have been migrated to the [new Pelican Plugins organization](https://github.com/pelican-plugins), this monolithic repository will be archived.**
+
+This plugin allows the use of Jinja2 directives inside your pelican articles and pages.
+
+In this approach, your content is *first* rendered by the Jinja template engine. The result
+is then passed to the normal pelican reader as usual. There are two consequences for usage.
+First, this means the Pelican context and jinja variables [usually
+visible](http://docs.getpelican.com/en/stable/themes.html#templates-and-variables) to your
+article or page template are _not_ available at rendering time. Second, it means that if any
+of your input content could be parsed as Jinja directives, they will be rendered as such.
+This is unlikely to happen accidentally, but it's good to be aware of.
+
+All input that needs Pelican variables such as `article`, `category`, etc., should be put
+inside your *theme's* templating.  As such, the main use of this plugin is to automatically
+generate parts of your articles or pages.
+
+Markdown, reStructured Text, and HTML input are all supported. Note that by enabling this
+plugin, all input files of these file types will be preprocessed with the Jinja renderer.
+It is not currently supported to selectively enable or disable `jinja2content` for only some of
+these input sources.
+
+
+## Example
+
+One usage is to embed repetitive HTML in Markdown articles. Since Markdown doesn't allow
+customization of layout, if anything more sophisticated than just displaying an image is
+necessary, one is forced to embed HTML in Markdown articles (i.e. hardcode `<div>` tags and
+then select them from the theme's CSS).  However, with `jinja2content`, one can do the
+following.
+
+File `my-cool-article.md`
+```
+# My cool title
+
+My cool content.
+
+{% from 'img_desc.html' import img_desc %}
+{{ img_desc("/images/my-cool-image.png",
+    "This is a cool tooltip",
+    "This is a very cool image.") }}
+```
+
+Where file `img_desc.html` contains:
+```
+{% macro img_desc(src, title='', desc='') -%}
+<div class="img-desc">
+  <p><img src="{{ src }}" title="{{ title }}"></p>
+  {% if desc %}
+  <p><em>{{ desc }}</em></p>
+  {% endif %}
+</div>
+{%- endmacro %}
+```
+
+The result will be:
+```
+# My cool title
+
+My cool content.
+
+<div class="img-desc">
+  <p><img src="/images/my-cool-image.png" title="This is a cool tooltip"></p>
+  <p><em>This is a very cool image.</em></p>
+</div>
+```
+
+After this, the Markdown will be rendered into HTML and only then the
+theme's templates will be applied.
+
+In this way, Markdown articles have more control over the content that is
+passed to the theme's `article.html` template, without the need to pollute
+the Markdown with HTML.  Another added benefit is that now `img_desc` is
+reusable across articles.
+
+Note that templates rendered with `jinja2content` can contain Markdown as
+well as HTML, since they are added before the Markdown content is converted
+to html.
+
+
+## Usage
+
+Enable the `jinja2content` plugin in your project in [the usual
+manner](http://docs.getpelican.com/en/stable/plugins.html#how-to-use-plugins).
+
+```
+PLUGINS = [
+    # ...
+    "jinja2content",
+]
+```
+
+
+## Configuration
+
+This plugin accepts the setting `JINJA2CONTENT_TEMPLATES` which should be set to a list of
+paths relative to `PATH` (the main content directory, usually `"content"`).  `jinja2content`
+will look for templates inside these directories, in order.  If they are not found in any,
+the theme's templates folder is used.
+
+
+## Extension
+
+This plugin is structured such that it should be quite easy to extend readers for other file
+types to also render Jinja template logic. It should be sufficient to create a new reader
+class that inherents from the `JinjaContentMixin` and then your desired reader class. See
+class definitions in the source for examples.
+
+
+## Acknowledgements
+
+- Created by @Leonardo.
+- Updated to support rst and HTML input by @micahjsmith.
+- Replaces [pelican-jinj2content](https://github.com/joachimneu/pelican-jinja2content/tree/f73ef9b1ef1ee1f56c80757b4190b53f8cd607d1),
+which had become unmaintained.
diff --git a/pelican-plugins/jinja2content/__init__.py b/pelican-plugins/jinja2content/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..de025c4b340cb55000bf3bafcb10a20043da87e2
--- /dev/null
+++ b/pelican-plugins/jinja2content/__init__.py
@@ -0,0 +1 @@
+from .jinja2content import *
diff --git a/pelican-plugins/jinja2content/jinja2content.py b/pelican-plugins/jinja2content/jinja2content.py
new file mode 100644
index 0000000000000000000000000000000000000000..802b49fba99c4876ccd8f07bec00af1ef3c15ff5
--- /dev/null
+++ b/pelican-plugins/jinja2content/jinja2content.py
@@ -0,0 +1,69 @@
+"""
+jinja2content.py
+----------------
+
+Pelican plugin that processes Markdown files as jinja templates.
+
+"""
+
+from jinja2 import Environment, FileSystemLoader, ChoiceLoader
+import os
+from pelican import signals
+from pelican.readers import MarkdownReader, HTMLReader, RstReader
+from pelican.utils import pelican_open
+from tempfile import NamedTemporaryFile
+
+class JinjaContentMixin:
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+
+        # will look first in 'JINJA2CONTENT_TEMPLATES', by default the
+        # content root path, then in the theme's templates
+        local_dirs = self.settings.get('JINJA2CONTENT_TEMPLATES', ['.'])
+        local_dirs = [os.path.join(self.settings['PATH'], folder)
+                      for folder in local_dirs]
+        theme_dir = os.path.join(self.settings['THEME'], 'templates')
+
+        loaders = [FileSystemLoader(_dir) for _dir
+                   in local_dirs + [theme_dir]]
+        if 'JINJA_ENVIRONMENT' in self.settings: # pelican 3.7
+            jinja_environment = self.settings['JINJA_ENVIRONMENT']
+        else:
+            jinja_environment = {
+                'trim_blocks': True,
+                'lstrip_blocks': True,
+                'extensions': self.settings['JINJA_EXTENSIONS']
+            }
+        self.env = Environment(
+            loader=ChoiceLoader(loaders),
+            **jinja_environment)
+
+
+    def read(self, source_path):
+        with pelican_open(source_path) as text:
+            text = self.env.from_string(text).render()
+
+        with NamedTemporaryFile(delete=False) as f:
+            f.write(text.encode())
+            f.close()
+            content, metadata = super().read(f.name)
+            os.unlink(f.name)
+            return content, metadata
+
+
+class JinjaMarkdownReader(JinjaContentMixin, MarkdownReader):
+    pass
+
+class JinjaRstReader(JinjaContentMixin, RstReader):
+    pass
+
+class JinjaHTMLReader(JinjaContentMixin, HTMLReader):
+    pass
+
+def add_reader(readers):
+    for Reader in [JinjaMarkdownReader, JinjaRstReader, JinjaHTMLReader]:
+        for ext in Reader.file_extensions:
+            readers.reader_classes[ext] = Reader
+
+def register():
+    signals.readers_init.connect(add_reader)
diff --git a/pelican-plugins/jpeg_reader/.gitignore b/pelican-plugins/jpeg_reader/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..16d3c4dbbfec555a7690fe6a0f9f812de215fab2
--- /dev/null
+++ b/pelican-plugins/jpeg_reader/.gitignore
@@ -0,0 +1 @@
+.cache
diff --git a/pelican-plugins/jpeg_reader/README.md b/pelican-plugins/jpeg_reader/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..419f300cf4547189782c488a8bad66af04ea9e8e
--- /dev/null
+++ b/pelican-plugins/jpeg_reader/README.md
@@ -0,0 +1,45 @@
+### Pelican JPEG Reader.
+
+Original author: [Mitchell Currie](https://github.com/mitchins)
+
+##### Requirements:
+
+* Python3
+* Pelican
+* Pillow library (PIL for python3)
+* Exiv2 binary accessible by $PATH
+
+To avoid undesired creation of content, the specific extension must be `jpeg_article`, i.e. "myPhoto.jpeg_article", it's a regular JPEG image, but this avoids your other JPEG images getting picked up. It can work for pages or blogs, and determines based on whether it's `content/blog` or `content/pages` (or whatever you use for content).
+
+#### Most relevant EXIF/IPTC flags from Exiv2 that are used
+
+|  Page/Article Field | Exiv2 Key  |  Description |
+|---|---|---|
+| title  | `Exif.Image.ImageDescription`  |  Defaults to 'Untitled' |
+| author  | `Exif.Image.Artist`  |  Default to Unknown. Currently Scalar |
+| date  |  `Exif.Photo.DateTimeOriginal` |  Undefined behaviour if not present as required |
+|  slug |  `Iptc.Application2.Headline` |  Defaults to title's value |
+|  body |  `Exif.Photo.UserComment` |  This goes under image in page/article, blank default |
+|  summary |  `Iptc.Application2.Caption` |  Used for article index, defaults to first 140 characters of the body |
+|  category |  `Iptc.Application2.SuppCategory` |  Specifies the category of page/article if `USE_FOLDER_AS_CATEGORY` not set  |
+|  template |  `Iptc.Application2.ObjectName` |  If specified will set the template metadata property to tell pelican where to look  |
+|  tags |  `Iptc.Application2.Keywords` |  For each entry found with this key, a tag is created with the value of the entry |
+|  `metadata['exiv2']` | ***Everything***|  All exiv2 fields from the image are shoved into the metadata dictionary of the item, under `exiv2` key for template usage |
+
+
+
+#### Pelican Settings Added or Honoured:
+
+|  Key in pelicanconf.py |  Description |
+|---|---|
+| `PATH`  |  **Content Path** |
+| `OUTPUT_PATH` |  **Output Path** |
+| `USE_FOLDER_AS_CATEGORY` | **Category from folder name** If enabled, takes the category from the name of the folder the file is in. Otherwise the category will attempt to be read from `Iptc.Application2.SuppCategory` |
+|  `SITEURL` | **Site Url** The optional absolute Url for the site, defaults to '' usually. |
+|  `PAGE_URL` | **Page Url** The format string to specify where page html files are saved to |
+|  `PAGE_SAVE_AS` | **Page Save Path** The format string to specify where page html files are physically written to disk |
+|  `ARTICLE_URL` | ** Article Url** The format string to specify where page html files are saved to |
+|  `ARTICLE_SAVE_AS` | **Article Save Path** The format string to specify where page html files are physically written to disk |
+
+
+
diff --git a/pelican-plugins/jpeg_reader/__init__.py b/pelican-plugins/jpeg_reader/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..51704cf00afa0c2d85f0034067e3e541d3a130b9
--- /dev/null
+++ b/pelican-plugins/jpeg_reader/__init__.py
@@ -0,0 +1 @@
+from .jpeg_reader import *
diff --git a/pelican-plugins/jpeg_reader/constants.py b/pelican-plugins/jpeg_reader/constants.py
new file mode 100644
index 0000000000000000000000000000000000000000..03866fa8bc8c8345d4977a2cb60f5de12937b033
--- /dev/null
+++ b/pelican-plugins/jpeg_reader/constants.py
@@ -0,0 +1,42 @@
+from enum import Enum
+
+
+class Exiv(Enum):
+    DESCRIPTION = 'Exif.Image.ImageDescription'
+    ARTIST = 'Exif.Image.Artist'
+    DATETIME = 'Exif.Photo.DateTimeOriginal'
+    HEADLINE = 'Iptc.Application2.Headline'
+    COMMENT = 'Exif.Photo.UserComment'
+    CAPTION = 'Iptc.Application2.Caption'
+    KEYWORDS = 'Iptc.Application2.Keywords'
+    CATEGORY = 'Iptc.Application2.SuppCategory'
+    OBJECT_NAME = 'Iptc.Application2.ObjectName'
+
+
+class PelicanConfig(Enum):
+    PATH = 'PATH'
+    OUTPUT_PATH = 'OUTPUT_PATH'
+    USE_FOLDER_AS_CATEGORY = 'USE_FOLDER_AS_CATEGORY'
+    SITE_URL = 'SITEURL'
+    PAGE_URL = 'PAGE_URL'
+    PAGE_SAVE_AS = 'PAGE_SAVE_AS'
+    ARTICLE_URL = 'ARTICLE_URL'
+    ARTICLE_SAVE_AS = 'ARTICLE_SAVE_AS'
+
+
+class PelicanMetadata(Enum):
+    TITLE = 'title'
+    AUTHORS = 'authors'
+    DATE = 'date'
+    SLUG = 'slug'
+    TAGS = 'tags'
+    CATEGORY = 'category'
+    SUMMARY = 'summary'
+    FEATURED_IMAGE = 'featured_image'  # Acts as a thumbnail
+    TEMPLATE = 'template'
+    CUSTOM_ALL = 'exiv2'  # Not officially part of metadata, but we add it ourselves
+
+
+class PelicanClass(Enum):
+    BLOG = 'blog'
+    PAGES = 'pages'
diff --git a/pelican-plugins/jpeg_reader/exiv2_parser.py b/pelican-plugins/jpeg_reader/exiv2_parser.py
new file mode 100644
index 0000000000000000000000000000000000000000..0bf77bff97f6a2ccd046475be179e1043d3ac685
--- /dev/null
+++ b/pelican-plugins/jpeg_reader/exiv2_parser.py
@@ -0,0 +1,61 @@
+import re
+import subprocess
+from typing import List, Tuple
+
+from . import util
+
+
+class Keyword:
+    def __init__(self, *, keyword:str, kind: str, count: int):
+        self.keyword = keyword
+        self.kind = kind
+        self.count = count
+
+
+class Exiv2Parser:
+    @classmethod
+    def get_exiv2_version(cls) -> Tuple[str, str]:
+        commands = ['exiv2', '--version']
+        process = subprocess.Popen(commands, stdout=subprocess.PIPE)
+        output = util.to_str(process.communicate()[0])
+        match = re.search(r'exiv2 ([\d.]+) (\w+)', output)
+        if match is not None:
+            return match.groups()
+        return None
+
+    @classmethod
+    def get_values(cls, file_path: str) -> dict:
+        keywords = cls.__get_keys(file_path)
+        result = dict()
+        for key in keywords:
+            commands = ['exiv2', '-K', key.keyword, '-P', 't', 'print', file_path]
+            process = subprocess.Popen(commands, stdout=subprocess.PIPE)
+            output = util.to_str(process.communicate()[0]).rstrip('\n')
+            # Check if the key is a list or scalar
+            if key.count > 1:
+                result[key.keyword] = output.split('\n')  # Assume the output is like keywords, one per line
+            else:
+                result[key.keyword] = output  # Assume the whole input is the value
+        return result
+
+    @classmethod
+    def __get_keys(cls, file_path: str) -> List[Keyword]:
+        found_keywords = dict()
+        commands = ['exiv2', '-P', 'ky', 'print', file_path]
+        process = subprocess.Popen(commands, stdout=subprocess.PIPE)
+        output = util.to_str(process.communicate()[0])
+        for match in re.finditer(r'([\w.]+)\W+(\w+)\W*\n?', output):
+            code, kind = match.groups()
+            keyword = found_keywords.get(code, Keyword(keyword=code, kind=kind, count=0))
+            keyword.count += 1
+            found_keywords[code] = keyword
+
+        return list(found_keywords.values())
+
+if __name__ == '__main__':
+    #data = Exiv2Parser.get_values('content/blog/terms2.jpeg')
+    #print(data)
+    version_info = Exiv2Parser.get_exiv2_version()
+    print(version_info)
+
+
diff --git a/pelican-plugins/jpeg_reader/jpeg_reader.py b/pelican-plugins/jpeg_reader/jpeg_reader.py
new file mode 100644
index 0000000000000000000000000000000000000000..fd16a3ecc65f958283248a660d7587f58af8feca
--- /dev/null
+++ b/pelican-plugins/jpeg_reader/jpeg_reader.py
@@ -0,0 +1,128 @@
+"""
+This plugin uses the metadata from JPEG images (EXIF and IPTC) to construct a meaningful page or gallery.
+Possible uses are gallery pages or a blog article that's mainly about an image.
+With this tool, it's posible to just dump an image without any extra data/linkage to create coherent output.
+The note here is that the extension is `jpeg_article` so it doesn't pick up {attach} or other static resources.
+"""
+
+import logging
+from datetime import datetime
+from os import makedirs, sep
+from os.path import join, dirname, isdir, splitext
+from typing import Tuple
+
+from PIL import Image
+from pelican import signals
+from pelican.readers import BaseReader
+from pelican.urlwrappers import URLWrapper, Category, Author, Tag
+
+from .constants import Exiv, PelicanConfig, PelicanMetadata, PelicanClass
+from .exiv2_parser import Exiv2Parser
+
+
+class JpegReader(BaseReader):
+    logger = logging.getLogger('JpegReader')
+    enabled = True
+    file_extensions = ('jpeg_article')
+    thumb_size = 250, 250
+
+    def __init__(self, settings):
+        super(JpegReader, self).__init__(settings)
+
+    def read(self, source_path):
+        try:
+            if Exiv2Parser.get_exiv2_version() is not None:
+                content, metadata = self.parse_jpeg(source_path=source_path)
+
+        except ValueError:      # if file can't be parsed, ignore it
+            pass
+        else:
+            return content, metadata
+
+    def parse_jpeg(self, *, source_path: str) -> Tuple[str, dict]:
+        JpegReader.logger.info(source_path)
+
+        img = Image.open(source_path)
+
+        image_data = Exiv2Parser.get_values(source_path)
+
+        title = image_data.get(Exiv.DESCRIPTION.value, 'Untitled')
+        author = image_data.get(Exiv.ARTIST.value, 'Unknown')
+        date_string = image_data.get(Exiv.DATETIME.value, '')
+
+        date = datetime.strptime(date_string, "%Y:%m:%d %H:%M:%S")
+        slug = URLWrapper(image_data.get(Exiv.HEADLINE.value, title), self.settings).slug
+        description_long = image_data.get(Exiv.COMMENT.value, '')
+        summary = image_data.get(Exiv.CAPTION.value, description_long[:140])
+
+        tags = [Tag(tag, self.settings) for tag in image_data.get(Exiv.KEYWORDS.value, list())]
+
+        content_root = self.settings[PelicanConfig.PATH.value]
+        path_output = self.settings[PelicanConfig.OUTPUT_PATH.value]
+        relative_source = dirname(source_path[len(content_root):]).lstrip(sep)
+        if self.settings[PelicanConfig.USE_FOLDER_AS_CATEGORY.value]:
+            category = relative_source.split(sep)[-1]
+        else:
+            category = image_data.get(Exiv.CATEGORY.value, None)
+
+        type_of_content = relative_source.split(sep)[0]  # either 'blog' or 'pages' as far as I know.
+        url_site = self.settings[PelicanConfig.SITE_URL.value]
+
+        if type_of_content.lower() == PelicanClass.PAGES.value:
+            url_document = self.settings[PelicanConfig.PAGE_URL.value]
+            document_save_as = self.settings[PelicanConfig.PAGE_SAVE_AS.value]
+        else:  # Assume PelicanClass.BLOG
+            url_document = self.settings[PelicanConfig.ARTICLE_URL.value]
+            document_save_as = self.settings[PelicanConfig.ARTICLE_SAVE_AS.value]
+
+        page_url_complete = join(url_site, url_document)
+
+        author_wrapper = Author(author, self.settings)
+
+        # Move image in place:
+        metadata = {PelicanMetadata.TITLE.value: title, PelicanMetadata.AUTHORS.value: [author_wrapper],
+                    PelicanMetadata.DATE.value: date, PelicanMetadata.SLUG.value: slug,
+                    PelicanMetadata.TAGS.value: tags,
+                    PelicanMetadata.CUSTOM_ALL.value: image_data}
+        if category is not None:
+            metadata[PelicanMetadata.CATEGORY.value] = Category(category, self.settings)
+
+        thumb_name = '{0}_thumb.jpg'.format(slug)
+        original_name = '{0}.jpg'.format(slug)
+
+        path_output_html = join(path_output, document_save_as).format(**metadata)
+        path_output_dir = dirname(path_output_html)
+        path_output_original = join(path_output_dir, original_name)
+        path_output_thumb = join(path_output_dir, thumb_name)
+
+        # Here we generate the summary info incase this is used for articles we get nice thumbnails and summary
+        metadata[PelicanMetadata.SUMMARY.value] = summary
+        metadata[PelicanMetadata.FEATURED_IMAGE.value] = join(url_site, path_output_thumb[len(path_output):])
+        if Exiv.OBJECT_NAME.value in image_data:
+            metadata[PelicanMetadata.TEMPLATE.value] = image_data[Exiv.OBJECT_NAME.value]
+
+        # Write the size/HTML out before we reduce the image to a thumb
+        content = "<img src='{src}' alt='{alt}' style='width: {width}px; height: auto; max-width: 100%;'></img><p>{body}</p>" \
+            .format(src=original_name, alt=title, width=img.width, height=img.height, body=description_long)
+
+        # Ensure the directory levels exist
+        if not isdir(path_output_dir):
+            makedirs(path_output_dir)
+        img.save(path_output_original)
+        img.thumbnail(self.thumb_size)
+        img.save(path_output_thumb)
+
+        # Debug info if we need it
+        JpegReader.logger.debug(content)
+        JpegReader.logger.debug(str(metadata))
+        JpegReader.logger.debug(path_output_html)
+
+        return content, metadata
+
+
+def add_reader(readers):
+    readers.reader_classes['jpeg_article'] = JpegReader
+
+
+def register():
+    signals.readers_init.connect(add_reader)
\ No newline at end of file
diff --git a/pelican-plugins/jpeg_reader/test_exiv2_parser.py b/pelican-plugins/jpeg_reader/test_exiv2_parser.py
new file mode 100644
index 0000000000000000000000000000000000000000..4f14c073232598bf43b9217b1df86ec63ff1284a
--- /dev/null
+++ b/pelican-plugins/jpeg_reader/test_exiv2_parser.py
@@ -0,0 +1,36 @@
+from unittest.mock import patch
+import subprocess
+
+from .exiv2_parser import Exiv2Parser
+
+
+class MockPopen(object):
+    """Mock Popen method"""
+    def __init__(self, cmd, *, stdout):
+        pass
+
+    def communicate(self):
+        """Mock communicate method of Popen"""
+        return b'bash: command not found: exiv2', b''
+
+
+class MockPopenSuccess(MockPopen):
+    def __init__(self, cmd, *, stdout):
+        MockPopen.__init__(self, cmd, stdout=stdout)
+
+    def communicate(self):
+        """Mock communicate method of Popen"""
+        return b'exiv2 0.26 001a00 (64 bit build)', b''
+
+
+@patch('subprocess.Popen', MockPopen)
+def test_get_version_fail():
+    version_info = Exiv2Parser.get_exiv2_version()
+    assert version_info is None
+
+
+@patch('subprocess.Popen', MockPopenSuccess)
+def test_get_version_success():
+    version, commit = Exiv2Parser.get_exiv2_version()
+    assert version == '0.26'
+    assert commit == '001a00'
diff --git a/pelican-plugins/jpeg_reader/util.py b/pelican-plugins/jpeg_reader/util.py
new file mode 100644
index 0000000000000000000000000000000000000000..c4906bbefcc9a67a030cbcf66743ee82b0ac5f34
--- /dev/null
+++ b/pelican-plugins/jpeg_reader/util.py
@@ -0,0 +1,14 @@
+def to_str(bytes_or_str):
+    if isinstance(bytes_or_str, bytes):
+        value = bytes_or_str.decode('utf-8')
+    else:
+        value = bytes_or_str
+    return value  # Instance of Str
+
+
+def to_bytes(bytes_or_str):
+    if isinstance(bytes_or_str, str):
+        value = bytes_or_str.encode('utf-8')
+    else:
+        value = bytes_or_str
+    return value  # Instance of Bytes
diff --git a/pelican-plugins/latex b/pelican-plugins/latex
new file mode 120000
index 0000000000000000000000000000000000000000..4a2d98c3e270ff2985e25e5d7f120cb16f247a8a
--- /dev/null
+++ b/pelican-plugins/latex
@@ -0,0 +1 @@
+render_math/
\ No newline at end of file
diff --git a/pelican-plugins/libravatar/.gitignore b/pelican-plugins/libravatar/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..a74b07aee47bc7f175e5d4b01a7d52cf1c833f0e
--- /dev/null
+++ b/pelican-plugins/libravatar/.gitignore
@@ -0,0 +1 @@
+/*.pyc
diff --git a/pelican-plugins/libravatar/Readme.md b/pelican-plugins/libravatar/Readme.md
new file mode 100644
index 0000000000000000000000000000000000000000..d1f5bb94ffd01f0b57a42c9738722ad91ccbba59
--- /dev/null
+++ b/pelican-plugins/libravatar/Readme.md
@@ -0,0 +1,77 @@
+# Libravatar plugin for Pelican
+
+**NOTE:** [This plugin has been moved to its own repository](https://github.com/pelican-plugins/avatar). Please file any issues/PRs there. Once all plugins have been migrated to the [new Pelican Plugins organization](https://github.com/pelican-plugins), this monolithic repository will be archived.
+
+## Description
+
+This plugin allows the inclusion of [Libravatar](http://www.libravatar.org)
+user profile pictures, according to the email address of the article's
+author.
+
+## Usage
+
+### Specifying the author's email address
+
+The default email address is taken from the `LIBRAVATAR_AUTHOR_EMAIL`
+variable in the Pelican configuration file.  This default value can be
+overridden in a per-article basis, according to the email address found in
+the article's metadata.
+
+In ReSTructuredText:
+
+```rst
+:email: bart.simpson@example.com
+```
+
+In Markdown:
+
+```markdown
+Email: bart.simpson@example.com
+```
+
+If the avatar for the specified email address is not found at Libravatar,
+it is searched at [Gravatar](http://www.gravatar.com).  If it is not found
+there neither, a default picture is shown.  The default for the "missing
+picture" can be defined in the configuration variable `LIBRAVATAR_MISSING`.
+
+### Adjusting the template
+
+This plugin assigns the `author_libravatar` variable to the Libravatar URL
+and makes the variable available within the article's context.  For
+instance, you can add the following to a template file (for example, to the
+`article_infos.html` template file of the notmyidea theme), just before the
+infomation about the author:
+
+```html
+{% if article.author_libravatar %}
+<div align="center">
+        <img src="{{ article.author_libravatar }}">
+</div>
+{% endif %}
+```
+
+## Settings
+
+The following variables can be set in the Pelican configuration file:
+
+- `LIBRAVATAR_AUTHOR_EMAIL`: site-wide default for the author's email address.
+
+- `LIBRAVATAR_MISSING`: The default for the missing picture.  This can be
+either a url (e.g. `'http://example.com/nobody.png'`) or the name of a
+library of logos (e.g. `'wavatar'`; for the full set of alternativas, see
+the [Libravatar API](https://wiki.libravatar.org/api/)).
+
+- `LIBRAVATAR_SIZE`: The size, in pixels, of the profile picture (it is
+always square, so the height is equal to the width).  If not specified, the
+default size (80×80) is returned by Libravatar.
+
+## Credits
+
+Inspiration for this plugin came from the
+[gravatar plugin](https://github.com/getpelican/pelican-plugins/tree/master/gravatar).
+
+## Author
+
+Copyright (C) 2015  Rafael Laboissiere (<rafael@laboissiere.net>)
+
+Released under the GNU Affero Public License, version 3 or later.  No warranties.
diff --git a/pelican-plugins/libravatar/__init__.py b/pelican-plugins/libravatar/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..b73035aa55417c7bb8e081d0ff59303b990b3fc9
--- /dev/null
+++ b/pelican-plugins/libravatar/__init__.py
@@ -0,0 +1 @@
+from . libravatar import *
diff --git a/pelican-plugins/libravatar/libravatar.py b/pelican-plugins/libravatar/libravatar.py
new file mode 100644
index 0000000000000000000000000000000000000000..ed783a4db82f6285b984ec9d91ffb7e15b092e44
--- /dev/null
+++ b/pelican-plugins/libravatar/libravatar.py
@@ -0,0 +1,67 @@
+"""Libravatar plugin for Pelican"""
+
+## Copyright (C) 2015  Rafael Laboissiere <rafael@laboissiere.net>
+##
+## This program is free software: you can redistribute it and/or modify it
+## under the terms of the GNU General Affero Public License as published by
+## the Free Software Foundation, either version 3 of the License, or (at
+## your option) any later version.
+##
+## This program is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+## Affero General Public License for more details.
+##
+## You should have received a copy of the GNU Affero General Public License
+## along with this program.  If not, see http://www.gnu.org/licenses/.
+
+
+import hashlib
+from pelican import signals
+
+
+def initialize (pelicanobj):
+    """Initialize the Libravatar plugin"""
+    pelicanobj.settings.setdefault ('LIBRAVATAR_MISSING', None)
+    pelicanobj.settings.setdefault ('LIBRAVATAR_SIZE', None)
+
+
+def add_libravatar (generator, metadata):
+    """Article generator connector for the Libravatar plugin"""
+    missing = generator.settings.get ('LIBRAVATAR_MISSING')
+    size = generator.settings.get ('LIBRAVATAR_SIZE')
+
+    ## Check the presence of the Email header
+    if 'email' not in metadata.keys ():
+        try:
+            metadata ['email'] = generator.settings.get ('AUTHOR_EMAIL')
+        except:
+            pass
+
+    ## Add the Libravatar URL
+    if metadata ['email']:
+
+        ## Compose URL using the MD5 hash
+        ## (the ascii encoding is necessary for Python3)
+        email = metadata ['email'].lower ().encode ('ascii')
+        md5 = hashlib.md5 (email).hexdigest ()
+        url = 'http://cdn.libravatar.org/avatar/' + md5
+
+        ## Add eventual "missing picture" option
+        if missing or size:
+            url = url + '?'
+            if missing:
+                url = url + 'd=' + missing
+                if size:
+                    url = url + '&'
+            if size:
+                url = url + 's=' + str (size)
+
+        ## Add URL to the article's metadata
+        metadata ['author_libravatar'] = url
+
+
+def register ():
+    """Register the Libravatar plugin with Pelican"""
+    signals.initialized.connect (initialize)
+    signals.article_generator_context.connect (add_libravatar)
diff --git a/pelican-plugins/libravatar/test_data/theme/templates/article.html b/pelican-plugins/libravatar/test_data/theme/templates/article.html
new file mode 100644
index 0000000000000000000000000000000000000000..ea6cfbcd7f2d812798a625c6648778560371ec2f
--- /dev/null
+++ b/pelican-plugins/libravatar/test_data/theme/templates/article.html
@@ -0,0 +1,12 @@
+{% extends "base.html" %}
+{% block content %}
+<section id="content" class="body">
+    <footer class="post-info">
+    {% if article.author_libravatar %}
+        <div align="center">
+            <img src="{{ article.author_libravatar }}">
+        </div>
+    {% endif %}
+    </footer>
+</section>
+{% endblock %}
diff --git a/pelican-plugins/libravatar/test_libravatar.py b/pelican-plugins/libravatar/test_libravatar.py
new file mode 100644
index 0000000000000000000000000000000000000000..639b460d2af9499af3deb5c876b04aba69049df3
--- /dev/null
+++ b/pelican-plugins/libravatar/test_libravatar.py
@@ -0,0 +1,97 @@
+"""Unit testing suite for the Libravatar Plugin"""
+from __future__ import print_function
+
+## Copyright (C) 2015  Rafael Laboissiere <rafael@laboissiere.net>
+##
+## This program is free software: you can redistribute it and/or modify it
+## under the terms of the GNU General Affero Public License as published by
+## the Free Software Foundation, either version 3 of the License, or (at
+## your option) any later version.
+##
+## This program is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+## Affero General Public License for more details.
+##
+## You should have received a copy of the GNU Affero General Public License
+## along with this program.  If not, see http://www.gnu.org/licenses/.
+
+import os
+import re
+import unittest
+import hashlib
+from tempfile import mkdtemp
+from shutil import rmtree
+
+from . import libravatar
+from pelican import Pelican
+from pelican.settings import read_settings
+
+
+AUTHOR_EMAIL = 'bart.simpson@example.com'
+MD5_HASH = hashlib.md5 (AUTHOR_EMAIL.encode()).hexdigest ()
+LIBRAVATAR_BASE_URL = 'http://cdn.libravatar.org/avatar/'
+
+
+class TestLibravatarURL (unittest.TestCase):
+    """Class for testing the URL output of the Libravatar plugin"""
+
+    def setUp (self, override = None):
+        self.output_path = mkdtemp (prefix = 'pelicantests.')
+        self.content_path = mkdtemp (prefix = 'pelicantests.')
+        theme_path = os.path.join (os.path.dirname (os.path.abspath (__file__)),
+                                   'test_data', 'theme')
+        settings = {
+            'PATH': self.content_path,
+            'THEME': theme_path,
+            'OUTPUT_PATH': self.output_path,
+            'PLUGINS': [libravatar],
+            'CACHE_CONTENT': False
+        }
+        if override:
+            settings.update (override)
+
+        with open (os.path.join (self.content_path, 'test.md'), 'w') as test_md_file:
+            test_md_file.write ('Title: Test\nDate: 2019-09-05\nEmail: ' + AUTHOR_EMAIL + '\n\n')
+
+        self.settings = read_settings (override = settings)
+        pelican = Pelican (settings = self.settings)
+        pelican.run ()
+
+    def tearDown (self):
+        rmtree (self.output_path)
+        rmtree (self.content_path)
+
+    def test_url (self, options = ''):
+        with open (os.path.join (self.output_path, 'test.html'), 'r') as test_html_file:
+            found = False
+            for line in test_html_file.readlines ():
+                if re.search (LIBRAVATAR_BASE_URL + MD5_HASH + options, line):
+                    found = True
+                    break
+            assert found
+
+class TestLibravatarMissing (TestLibravatarURL):
+    """Class for testing the Libravatar "missing picture" option"""
+
+    def setUp (self, override = None):
+        self.library = 'wavatar'
+        TestLibravatarURL.setUp (self,
+                                  override = {'LIBRAVATAR_MISSING':
+                                              self.library})
+
+    def test_url (self):
+        TestLibravatarURL.test_url (self, r'\?d=' + self.library)
+
+
+class TestLibravatarSize (TestLibravatarURL):
+    """Class for testing the Libravatar size option"""
+
+    def setUp (self, override = None):
+        self.size = 100
+        TestLibravatarURL.setUp (self,
+                                  override = {'LIBRAVATAR_SIZE': self.size})
+
+    def test_url (self):
+        TestLibravatarURL.test_url (self, r'\?s=' + str (self.size))
+
diff --git a/pelican-plugins/linker/Readme.md b/pelican-plugins/linker/Readme.md
new file mode 100644
index 0000000000000000000000000000000000000000..7529245d673dd07dff1ea212b9a6d282a6b24e53
--- /dev/null
+++ b/pelican-plugins/linker/Readme.md
@@ -0,0 +1,75 @@
+# Linker
+
+This plugin allows to define custom linker commands in analogy to the builtin
+`{attach}`, `{author}`, `{category}`, `{filename}`, `{index}`, `{static}` and
+`{tag}` syntax.
+
+## Provided commands (each of which in its own submodule)
+
+### `{mailto}`
+
+**Purpose:** Helps to create `mailto:` links with javascript (JS) on top of a
+non-JS fallback.
+
+* **How the HTML code is replaced step by step**
+
+  * Your code in a content file (page or article):
+
+    ```
+    `Send me a mail <{mailtor}webmaster>`_
+    ```
+
+  * Pelican generated output:
+
+    ```
+    <a href="{mailto}webmaster">Send me a mail</a>
+    ```
+
+  * Plugin replacement (after computing `'jroznfgre' = rot_13('webmaster')`):
+
+    ```
+    <a href="/mailto/jroznfgre/">Send me a mail</a>
+    ```
+
+  * Result of a JS-powered transform (which you must add):
+
+    ```
+    <a href="mailto:webmaster@example.com">Send me a mail</a>
+    ```
+
+  * As a fallback for users without JS, the static page
+  `mailto/jroznfgre/index.html` is generated using the template
+  `mailto_fallback`.
+
+* **Usage instruction**
+
+  * Activate nested `{mailto}` plugin using
+
+    ``
+    PLUGINS = ['linker.mailto']
+    ``
+
+  * Provide the `mailto_fallback` template (accessing `mailto` which is injected
+  into the template)
+
+  * Add JS to the theme to improve the user experience as sketched above
+
+    ```
+    <script>
+        var pattern = new RegExp("mailto\/([a-z_\.\-]+)\/")
+        var a = document.querySelectorAll('a[href^="/mailto/"]');
+        for (var i = 0, len = a.length; i < len; i++) {
+            var match = pattern.exec(a[i])
+            if (match.length == 2) {
+                a[i].setAttribute('href', 'mailto:'+match[1].replace(/[a-zA-Z]/g,function(c){return String.fromCharCode((c<="Z"?90:122)>=(c=c.charCodeAt(0)+13)?c:c-26);})+'@example.com')
+            }
+        }
+    </script>
+    ```
+
+## Other included submodules
+
+### `content_objects`
+
+This plugin collects all `pelican.contents.Content` instances in a `set` which
+can be accessed using `context['content_objects']`.
diff --git a/pelican-plugins/linker/__init__.py b/pelican-plugins/linker/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..68cc268c176c94bcc5dea2eed370d8be4e166677
--- /dev/null
+++ b/pelican-plugins/linker/__init__.py
@@ -0,0 +1 @@
+from .linker import *
diff --git a/pelican-plugins/linker/content_objects.py b/pelican-plugins/linker/content_objects.py
new file mode 100644
index 0000000000000000000000000000000000000000..d6e9e9f9eddd0c10f7db76a24a2d87b54a1eec58
--- /dev/null
+++ b/pelican-plugins/linker/content_objects.py
@@ -0,0 +1,12 @@
+# -*- coding: utf-8 -*-
+from pelican import signals
+
+def initialize_content_object_set(app):
+    app.settings['content_objects'] = set()
+
+def collect_content_objects(co):
+    context = co._context['content_objects'].add(co)
+
+def register():
+    signals.initialized.connect(initialize_content_object_set)
+    signals.content_object_init.connect(collect_content_objects)
diff --git a/pelican-plugins/linker/linker.py b/pelican-plugins/linker/linker.py
new file mode 100644
index 0000000000000000000000000000000000000000..18a350f8bc9bff285c13ef89b32399d2c075487f
--- /dev/null
+++ b/pelican-plugins/linker/linker.py
@@ -0,0 +1,136 @@
+# -*- coding: utf-8 -*-
+from __future__ import absolute_import
+
+import logging
+import re
+
+from six.moves.urllib.parse import urlparse, urlunparse
+
+from pelican import signals, contents
+
+from linker import content_objects
+
+logger = logging.getLogger("linker")
+
+class Link(object):
+    """Represents an HTML link including a linker command.
+
+    Typically, the Link is constructed from an SRE_Match after applying the
+    provided Link.regex pattern to the HTML content of a content object.
+
+    """
+    # regex based on the one used in contents.py from pelican version 3.6.3
+    regex = re.compile(
+        r""" # EXAMPLE: <a rel="nofollow" href="{mailto}webmaster"
+
+        (?P<markup><\s*[^\>]*   # <a rel="nofollow" href=   --> markup
+            (?:href|src|poster|data|cite|formaction|action)\s*=)
+
+        (?P<quote>["\'])        # "                         --> quote
+        \{(?P<cmd>.*?)\}        # {mailto}                  --> cmd
+        (?P<url>.*?)            # webmaster                 --> __url (see path)
+        \2                      # "                         <-- quote
+
+        """, re.X)
+
+    def __init__(self, context, content_object, match):
+        """Construct a Link from an SRE_Match.
+
+        :param context: The shared context between generators.
+        :param content_object: The associated pelican.contents.Content.
+        :param match: An SRE_Match obtained by applying the regex to my content.
+
+        """
+        self.context = context
+        self.content_object = content_object
+
+        self.markup = match.group('markup')
+        self.quote = match.group('quote')
+        self.cmd = match.group('cmd')
+        self.__url = urlparse(match.group('url'))
+        self.path = self.__url.path
+
+    def href(self): # rebuild matched URL using (possibly updated) self.path
+        return urlunparse( self.__url._replace(path=self.path) )
+
+    def html_code(self): # rebuild matched pattern from (possibly updated) self
+        return ''.join((self.markup, self.quote, self.href(), self.quote))
+
+
+class LinkerBase(object):
+    """Base class for performing the linker command magic.
+
+    In order to provide the linker command 'foo' as in '<a href="{foo}contact',
+    a responsible Linker class (e.g., FooLinker) should derive from LinkerBase
+    and set FooLinker.commands to ['foo']. The linker command is processed when
+    the overridden Linker.link(Link) is called.
+
+    """
+    commands = [] # link commands handled by the Linker. EXAMPLE: ['mailto']
+    builtins = ['attach', 'author', 'category', 'filename', 'index', 'static', 'tag']
+
+
+    def __init__(self, settings):
+        self.settings = settings
+
+    def link(self, link):
+        raise NotImplementedError
+
+
+class Linkers(object):
+    """Interface for all Linkers.
+
+    This class contains a mapping of {cmd1: linker1, cmd2: linker2} to apply any
+    registered linker command by passing the Link to the responsible Linker.
+
+    (Idea based on pelican.readers.Readers, but with less customization options.)
+
+    """
+    def __init__(self, settings):
+        self.settings = settings
+        self.linkers = {}
+
+        for linker_class in [LinkerBase] + LinkerBase.__subclasses__():
+            for cmd in linker_class.commands:
+                self.register_linker(cmd, linker_class)
+
+    def register_linker(self, cmd, linker_class):
+        if cmd in self.linkers: # check for existing registration of that cmd
+            current_linker_class = self.linkers[cmd].__class__
+            logger.warning(
+                "%s is stealing the linker command %s from %s.",
+                linker_class.__name__, cmd, current_linker_class.__name__
+            )
+        self.linkers[cmd] = linker_class(self.settings)
+
+    def handle_links_in_content_object(self, context, content_object):
+        # replace Link matches (with side effects on content and content_object)
+        def replace_link_match(match):
+            link = Link(context, content_object, match)
+
+            if link.cmd in LinkerBase.builtins:
+                return match.group(0)  # builtin commands not handled here
+            elif link.cmd in self.linkers:
+                self.linkers[link.cmd].link(link) # let Linker process the Link
+            else:
+                logger.warning("Ignoring unknown linker command %s", link.cmd)
+
+            return link.html_code() # return HTML to replace the matched link
+
+        content_object._content = Link.regex.sub( # match, process and replace
+            replace_link_match, content_object._content)
+
+
+def feed_context_to_linkers(generators):
+    settings = generators[0].settings
+    linkers = Linkers(settings)
+
+    context = generators[0].context
+    for co in context['content_objects']: # provided by plugin 'content_objects'
+        if isinstance(co, contents.Static): continue
+        if not co._content: continue
+        linkers.handle_links_in_content_object(context, co)
+
+def register():
+    content_objects.register()
+    signals.all_generators_finalized.connect(feed_context_to_linkers)
diff --git a/pelican-plugins/linker/mailto.py b/pelican-plugins/linker/mailto.py
new file mode 100644
index 0000000000000000000000000000000000000000..581e57c6287f42398a71ccea76dbbee6c55b85b5
--- /dev/null
+++ b/pelican-plugins/linker/mailto.py
@@ -0,0 +1,41 @@
+# -*- coding: utf-8 -*-
+from __future__ import absolute_import
+
+import codecs
+
+from pelican import signals
+from pelican.generators import Generator
+
+from linker import linker
+
+def encode_mailto_link(mailto):
+    return 'mailto/' + codecs.encode(mailto, 'rot_13') + '/'
+
+class MailtoLinker(linker.LinkerBase):
+    commands = ['mailto']
+
+    def link(self, link):
+        mailto = link.path
+
+        link.path = '/' + encode_mailto_link(mailto) # a.href for JS parsing
+        link.context['mailtos'].add(mailto) # remember mail address for fallback
+
+
+class MailtoFallbackGenerator(Generator):
+    def generate_context(self):
+        self.context['mailtos'] = set() # populated on {mailto} link processing
+
+    def generate_output(self, writer):
+        for mailto in self.context['mailtos']:
+            save_as = encode_mailto_link(mailto) + 'index.html'
+
+            writer.write_file(save_as, self.get_template('mailto_fallback'),
+                              self.context, mailto=mailto)
+
+
+def return_mailto_fallback_generator(generators):
+    return MailtoFallbackGenerator
+
+def register():
+    linker.register()
+    signals.get_generators.connect(return_mailto_fallback_generator)
diff --git a/pelican-plugins/liquid_tags/.gitignore b/pelican-plugins/liquid_tags/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..08d9ce7ef26e15e801fee091e1d645ea2a05681a
--- /dev/null
+++ b/pelican-plugins/liquid_tags/.gitignore
@@ -0,0 +1,4 @@
+.tox
+test_data/cache/
+test_data/output/theme/
+_nb_header.html
diff --git a/pelican-plugins/liquid_tags/Readme.md b/pelican-plugins/liquid_tags/Readme.md
new file mode 100644
index 0000000000000000000000000000000000000000..247c714ebd800dafbe4c3f95b34dcdee08e0b8ce
--- /dev/null
+++ b/pelican-plugins/liquid_tags/Readme.md
@@ -0,0 +1,284 @@
+# Liquid-style Tags
+
+**NOTE:** [This plugin has been moved to its own repository](https://github.com/pelican-plugins/liquid-tags). Please file any issues/PRs there. Once all plugins have been migrated to the [new Pelican Plugins organization](https://github.com/pelican-plugins), this monolithic repository will be archived.
+
+ -------------------------------------------------------------------------------
+
+*Author: Jake Vanderplas <jakevdp@cs.washington.edu>*
+
+This plugin allows liquid-style tags to be inserted into Markdown within
+Pelican documents via tags bounded by `{% ... %}`, a convention also used
+to extend Markdown in other publishing platforms such as Octopress.
+
+This set of extensions does not actually interface with liquid, but allows
+users to define their own liquid-style tags which will be inserted into
+the Markdown preprocessor stream. There are several built-in tags, which
+can be added as follows.
+
+First, in your pelicanconf.py file, add the plugins you want to use:
+
+    PLUGIN_PATH = '/path/to/pelican-plugins'
+    PLUGINS = ['liquid_tags.img', 'liquid_tags.video',
+               'liquid_tags.youtube', 'liquid_tags.vimeo',
+               'liquid_tags.include_code', 'liquid_tags.notebook']
+
+Following below is more information about these and other tags.
+
+## Image Tag
+To insert a sized and labeled image in your document, enable the
+`liquid_tags.img` plugin and use the following:
+
+    {% img [class name(s)] path/to/image [width [height]] [title text | "title text" ["alt text"]] %}
+
+### Base64 Image (inline image) tag
+
+`b64img` is based on the`img` tag, but instead of inserting a link to the image it encodes it as base64 text and inserts it into a`<img src=` attribute.
+
+To use it:
+
+1. Enable `liquid_tags.b64img`
+1. Insert a tag as follows: `{% b64img [class name(s)] path/to/image [width [height]] [title text | "title text" ["alt text"]] %}`
+
+Images are encoded at generation time, so you can use any local path (just be sure that the image will remain in the same location for subsequent site generations).
+
+## Instagram Tag
+To insert a sized and labeled Instagram image in your document by its shortcode (such as `pFI0CAIZna`), enable the `liquid_tags.gram` plugin and use the following:
+
+    {% gram shortcode [size] [width] [class name(s)] [title text | "title text" ["alt text"]] %}
+
+You can specify a size with `t`, `m`, or `l`.
+
+## Flickr Tag
+To insert a Flickr image to a post, follow these steps:
+
+1. Enable `liquid_tags.flickr`
+2. [Get an API key from Flickr](https://www.flickr.com/services/apps/create/apply)
+3. Add FLICKR_API_KEY to your settings file
+4. Add this to your source document:
+
+    {% flickr image_id [small|medium|large] ["alt text"|'alt text'] %}
+
+## Giphy Tag
+To insert a GIF from Giphy in your document by its ID (such as `aMSJFS6oFX0fC`), enable the `liquid_tags.giphy` plugin and use the following:
+
+    {% giphy gif_id ["alt text"|'alt text'] %}
+
+**Important:** You must [request a production API key](https://api.giphy.com/submit) from Giphy.
+If you just want to try it out, you can use the public beta key contained within the [GiphyAPI](https://github.com/giphy/GiphyAPI) README file.
+
+## Soundcloud Tag
+To insert a Soundcloud widget in your content, follow these steps:
+
+1. Enable `liquid_tags.soundcloud`
+2. Add this to your source document:
+
+    {% soundcloud track_url %}
+
+## YouTube Tag
+To insert a YouTube video into your content, enable the
+`liquid_tags.youtube` plugin and add the following to your source document:
+
+    {% youtube youtube_id [width] [height] %}
+
+The width and height are in pixels and are optional. If they
+are not specified, then the dimensions will be 640 (wide) by 390 (tall).
+
+If you experience issues with code generation (e.g., missing closing tags),
+add `SUMMARY_MAX_LENGTH = None` to your settings file.
+
+### Embedding just thumbnail
+
+If you do not want to add over megabyte of JS code to page you can embed linked
+thumbnail instead. To use that feature set `YOUTUBE_THUMB_ONLY` variable in your
+settings file. `YOUTUBE_THUMB_SIZE` variable controls dimensions of thumbnail
+with 4 sizes available:
+
+name  | xres | yres
+------|------|-----
+maxres| 1280 | 720
+sd    |  640 | 480
+hq    |  480 | 360
+mq    |  320 | 180
+
+Embedded thumbnails have CSS class 'youtube_video' which can be used to add
+'play' button above.
+
+## Vimeo Tag
+To insert a Vimeo video into your content, enable the `liquid_tags.vimeo`
+plugin and add the following to your source document:
+
+    {% vimeo vimeo_id [width] [height] %}
+
+The width and height are in pixels and are optional. If they
+are not specified, then the dimensions will be 640 (wide) by 390 (tall).
+
+If you experience issues with code generation (e.g., missing closing tags),
+add `SUMMARY_MAX_LENGTH = None` to your settings file.
+
+## Speakerdeck Tag
+
+To insert a Speakerdeck viewer into your content, follow these steps:
+
+1. Enable the `liquid_tags.soundcloud` plugin
+2. Add the following to your source document:
+
+  ```html
+  {% speakerdeck speakerdeck_id [ratio] %}
+  ```
+
+### Note
+
+- The ratio is a decimal number and is optional.
+- Ratio accept decimal number and digit after decimal is optional.
+- If ratio is not specified, then it will be `1.33333333333333` (4/3).
+- An example value for the ration can be `1.77777777777777` (16/9).
+
+## Video Tag
+To insert HTML5-friendly video into your content, enable the `liquid_tags.video`
+plugin and add the following to your source document:
+
+    {% video /url/to/video.mp4 [width] [height] [/path/to/poster.png] %}
+
+The width and height are in pixels and are optional. If they are not specified,
+then the native video size will be used. The poster image is a preview image
+that is shown prior to initiating video playback.
+To link to a video file, make sure it is in a static directory, transmitted
+to your server, and available at the specified URL.
+
+## Audio Tag
+To insert HTML5 audio into a post, enable the `liquid_tags.audio` plugin
+and add the following to your source document:
+
+    {% audio url/to/audio [url/to/audio] [url/to/audio] %}
+
+This tag supports up to three audio URL arguments so you can add different
+audio file versions, as different browsers support different file formats.
+
+To link to a audio file, make sure it is in a static directory, transmitted
+to your server, and available at the specified URL.
+
+## Include Code
+To include code from a file in your document with a link to the original
+file, enable the `liquid_tags.include_code` plugin, and add the following to
+your source document:
+
+    {% include_code /path/to/code.py [lang:python] [lines:X-Y] [:hidefilename:] [title] %}
+
+All arguments are optional but must be specified in the order shown above.
+
+    {% include_code /path/to/code.py lines:1-10 Test Example %}
+
+This example will show the first ten lines of the file.
+
+To hide filename, use `:hidefilename:`. If using `:hidefilename:`, a title must
+be provided.
+
+You can hide download link with `:hidelink:`. 
+
+If you would like to hide all three, i.e. title, filename and download link, use `:hideall:`.
+
+Following examples hides the filename.
+
+    {% include_code /path/to/code.py lines:1-10 :hidefilename: Test Example %}
+
+The script must be in the `code` subdirectory of your content folder;
+the default location can be changed by specifying the directory in your
+settings file thusly:
+
+    CODE_DIR = 'code'
+
+Additionally, in order for the resulting hyperlink to work, this directory must
+be listed in the STATIC_PATHS setting. For example:
+
+    STATIC_PATHS = ['images', 'code']
+
+## IPython notebooks
+
+To insert an [IPython][] notebook into your post, enable the
+``liquid_tags.notebook`` plugin and add the following to your source document:
+
+    {% notebook filename.ipynb %}
+
+The file should be specified relative to the `notebooks` subdirectory of the
+content directory. Optionally, this subdirectory can be specified in your
+settings file:
+
+    NOTEBOOK_DIR = 'notebooks'
+
+Because the conversion and rendering of notebooks is rather involved, there
+are a few extra steps required for this plugin. First, you must install IPython:
+
+      pip install ipython==2.4.1
+
+After running Pelican on content containing an IPython notebook tag, a file
+called `_nb_header.html` will be generated in the main directory. The content
+of this file should be included in the header of your theme. An easy way to
+accomplish this is to add the following to your theme’s header template…
+
+      {% if EXTRA_HEADER %}
+      {{ EXTRA_HEADER }}
+      {% endif %}
+
+… and in your settings file, include the line:
+
+      from io import open
+      EXTRA_HEADER = open('_nb_header.html', encoding='utf-8').read()
+
+This will insert the proper CSS formatting into your generated document.
+
+### Optional Arguments for Notebook Tags
+
+The notebook tag also has two optional arguments: `cells` and `language`.
+
+- You can specify a slice of cells to include:
+
+  `{% notebook filename.ipynb cells[2:8] %}`
+
+- You can also specify the name of the language that Pygments should use for
+  highlighting code cells. For a list of the language short names that Pygments
+  can highlight, refer to the [Pygments lexer list](http://www.pygments.org/docs/lexers/).
+
+  `{% notebook filename.ipynb language[julia] %}`
+
+  This may be helpful for those using [IJulia](https://github.com/JuliaLang/IJulia.jl)
+  or notebooks in other languages, especially as the IPython project [broadens its
+  scope](https://github.com/ipython/ipython/wiki/Roadmap:-IPython) to [support
+  other languages](http://jupyter.org/). The default language for highlighting
+  is `ipython`.
+
+- These options can be used separately, together, or not at all. However,
+  if both tags are used then `cells` must come before `language`:
+
+  `{% notebook filename.ipynb cells[2:8] language[julia] %}`
+
+### Collapsible Code in IPython Notebooks
+
+The IPython plugin also enables collapsible code input boxes. For this to work
+you must first copy the file `pelicanhtml_3.tpl` (for IPython 3.x) or
+`pelicanhtml_2.tpl` (for IPython 2.x) to the top level of your content
+directory. Notebook input cells containing the comment line `#
+<!-- collapse=True -->` will be collapsed when the HTML page is
+loaded and can be expanded by tapping on them. Cells containing the
+comment line `# <!-- collapse=False -->` will be expanded on load but
+can be collapsed by tapping on their header. Cells without collapsed
+comments are rendered as standard code input cells.
+
+## Configuration settings in custom tags
+
+Tags do not have access to the full Pelicans settings, and instead arrange for 
+the variables to be passed to the tag.  For tag authors who plan to add their 
+tag as in-tree tags, they can just add the variables they need to an array in 
+`mdx_liquid_tags.py`, but out-of-tree tags can specify which variables they 
+need by including a tuple of (variable, default value, helptext) in the 
+user's `pelicanconf.py` settings:
+
+    LIQUID_CONFIGS = (('PATH', '.', "The default path"), ('SITENAME', 'Default Sitename', 'The name of the site'))
+
+## Testing
+
+To test the plugin in multiple environments we use [tox](http://tox.readthedocs.org/en/latest/). To run the entire test suite:
+
+    cd path/to/liquid_tags
+    tox
+
+[IPython]: http://ipython.org/
diff --git a/pelican-plugins/liquid_tags/__init__.py b/pelican-plugins/liquid_tags/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..eabcd63a131dac4299ff245d985cbece9b394e77
--- /dev/null
+++ b/pelican-plugins/liquid_tags/__init__.py
@@ -0,0 +1 @@
+from .liquid_tags import *
diff --git a/pelican-plugins/liquid_tags/audio.py b/pelican-plugins/liquid_tags/audio.py
new file mode 100644
index 0000000000000000000000000000000000000000..a500ce6aac273ee21bd77bca715c44aea4f86018
--- /dev/null
+++ b/pelican-plugins/liquid_tags/audio.py
@@ -0,0 +1,75 @@
+"""
+Audio Tag
+---------
+This implements a Liquid-style audio tag for Pelican,
+based on the pelican video plugin [1]_
+
+Syntax
+------
+{% audio url/to/audio [url/to/audio] [/url/to/audio] %}
+
+Example
+-------
+{% audio http://example.tld/foo.mp3 http://example.tld/foo.ogg %}
+
+Output
+------
+<audio controls><source src="http://example.tld/foo.mp3" type="audio/mpeg"><source src="http://example.tld/foo.ogg" type="audio/ogg">Your browser does not support the audio element.</audio>
+
+[1] https://github.com/getpelican/pelican-plugins/blob/master/liquid_tags/video.py
+"""
+import os
+import re
+from .mdx_liquid_tags import LiquidTags
+
+SYNTAX = "{% audio url/to/audio [url/to/audio] [/url/to/audio] %}"
+AUDIO = re.compile(r'(/\S+|https?:\S+)(?:\s+(/\S+|https?:\S+))?(?:\s+(/\S+|https?:\S+))?')
+
+AUDIO_TYPEDICT = {'.mp3': 'audio/mpeg',
+                  '.ogg': 'audio/ogg',
+                  '.oga': 'audio/ogg',
+                  '.opus': 'audio/ogg',
+                  '.wav': 'audio/wav',
+                  '.mp4': 'audio/mp4'}
+
+
+def create_html(markup):
+    match = AUDIO.search(markup)
+    if match:
+        groups = match.groups()
+        audio_files = [g for g in groups if g]
+
+    if any(audio_files):
+        audio_out = '<audio controls>'
+
+        for audio_file in audio_files:
+
+            base, ext = os.path.splitext(audio_file)
+
+            if ext not in AUDIO_TYPEDICT:
+                raise ValueError("Unrecognized audio extension: "
+                                 "{0}".format(ext))
+
+            # add audio source
+            audio_out += '<source src="{}" type="{}">'.format(
+                audio_file, AUDIO_TYPEDICT[ext])
+
+        # close audio tag
+        audio_out += 'Your browser does not support the audio element.'
+        audio_out += '</audio>'
+
+    else:
+        raise ValueError("Error processing input, "
+                         "expected syntax: {0}".format(SYNTAX))
+
+    return audio_out
+
+
+@LiquidTags.register('audio')
+def audio(preprocessor, tag, markup):
+    return create_html(markup)
+
+
+# ---------------------------------------------------
+# This import allows image tag to be a Pelican plugin
+from liquid_tags import register
diff --git a/pelican-plugins/liquid_tags/b64img.py b/pelican-plugins/liquid_tags/b64img.py
new file mode 100644
index 0000000000000000000000000000000000000000..8c70bc94222f6c9bfa24fb5f754ca335c62fa026
--- /dev/null
+++ b/pelican-plugins/liquid_tags/b64img.py
@@ -0,0 +1,91 @@
+"""
+Image Tag
+---------
+This implements a Liquid-style image tag for Pelican,
+based on the liquid img tag which is based on the octopress image tag [1]_
+
+Syntax
+------
+{% b64img [class name(s)] [http[s]:/]/path/to/image [width [height]] [title text | "title text" ["alt text"]] %}
+
+Examples
+--------
+{% b64img /images/ninja.png Ninja Attack! %}
+{% b64img left half http://site.com/images/ninja.png Ninja Attack! %}
+{% b64img left half http://site.com/images/ninja.png 150 150 "Ninja Attack!" "Ninja in attack posture" %}
+
+Output
+------
+<img src="data:;base64,....">
+<img class="left half" src="data:;base64,..." title="Ninja Attack!" alt="Ninja Attack!">
+<img class="left half" src="data:;base64,..." width="150" height="150" title="Ninja Attack!" alt="Ninja in attack posture">
+
+[1] https://github.com/imathis/octopress/blob/master/plugins/image_tag.rb
+"""
+import re
+import base64
+try:
+    import urllib.request as urllib2
+except ImportError:
+    import urllib2
+from .mdx_liquid_tags import LiquidTags
+import six
+
+SYNTAX = '{% b64img [class name(s)] [http[s]:/]/path/to/image [width [height]] [title text | "title text" ["alt text"]] %}'
+
+# Regular expression to match the entire syntax
+ReImg = re.compile("""(?P<class>\S.*\s+)?(?P<src>(?:https?:\/\/|\/|\S+\/)\S+)(?:\s+(?P<width>\d+))?(?:\s+(?P<height>\d+))?(?P<title>\s+.+)?""")
+
+# Regular expression to split the title and alt text
+ReTitleAlt = re.compile("""(?:"|')(?P<title>[^"']+)?(?:"|')\s+(?:"|')(?P<alt>[^"']+)?(?:"|')""")
+
+
+def _get_file(src):
+    """ Return content from local or remote file. """
+    try:
+        if '://' in src or src[0:2] == '//':  # Most likely this is remote file
+            response = urllib2.urlopen(src)
+            return response.read()
+        else:
+            with open(src, 'rb') as fh:
+                return fh.read()
+    except Exception as e:
+        raise RuntimeError('Error generating base64image: {}'.format(e))
+
+
+def base64image(src):
+    """ Generate base64 encoded image from srouce file. """
+    return base64.b64encode(_get_file(src))
+
+
+@LiquidTags.register('b64img')
+def b64img(preprocessor, tag, markup):
+    attrs = None
+
+    # Parse the markup string
+    match = ReImg.search(markup)
+    if match:
+        attrs = dict([(key, val.strip())
+                      for (key, val) in six.iteritems(match.groupdict()) if val])
+    else:
+        raise ValueError('Error processing input. '
+                         'Expected syntax: {0}'.format(SYNTAX))
+
+    # Check if alt text is present -- if so, split it from title
+    if 'title' in attrs:
+        match = ReTitleAlt.search(attrs['title'])
+        if match:
+            attrs.update(match.groupdict())
+        if not attrs.get('alt'):
+            attrs['alt'] = attrs['title']
+
+    attrs['src'] = 'data:;base64,{}'.format(base64image(attrs['src']))
+
+    # Return the formatted text
+    return "<img {0}>".format(' '.join('{0}="{1}"'.format(key, val)
+                                       for (key, val) in six.iteritems(attrs)))
+
+
+#----------------------------------------------------------------------
+# This import allows image tag to be a Pelican plugin
+from .liquid_tags import register
diff --git a/pelican-plugins/liquid_tags/content/test_data/main.c b/pelican-plugins/liquid_tags/content/test_data/main.c
new file mode 100644
index 0000000000000000000000000000000000000000..cbc04c3698affb5d3bceaf9a22166e8a9ee366c9
--- /dev/null
+++ b/pelican-plugins/liquid_tags/content/test_data/main.c
@@ -0,0 +1,7 @@
+#include <stdio.h>
+
+int main(int argc, char** argv){
+    printf("Hello world!");
+
+    return 0;
+}
diff --git a/pelican-plugins/liquid_tags/content/test_data/main_cz.c b/pelican-plugins/liquid_tags/content/test_data/main_cz.c
new file mode 100644
index 0000000000000000000000000000000000000000..ee912b4bbb25dc05e68deeb294ee377813734ef0
--- /dev/null
+++ b/pelican-plugins/liquid_tags/content/test_data/main_cz.c
@@ -0,0 +1,7 @@
+#include <stdio.h>
+
+int main(int argc, char** argv){
+    printf("Dobrý");
+
+    return 0;
+}
diff --git a/pelican-plugins/liquid_tags/diag.py b/pelican-plugins/liquid_tags/diag.py
new file mode 100644
index 0000000000000000000000000000000000000000..e10bfd592d037d42c08f262918d30071c84b8fbd
--- /dev/null
+++ b/pelican-plugins/liquid_tags/diag.py
@@ -0,0 +1,177 @@
+"""
+Blockdiag Tag
+---------
+This tag implements a liquid style tag for blockdiag [1].  You can use different
+diagram types like blockdiag, seqdiag, packetdiag etc. [1]
+
+
+[1] http://blockdiag.com/en/blockdiag/
+
+Syntax
+------
+{% blockdiag {
+        <diagramm type> {
+            <CODE>
+        }
+    }
+%}
+
+Examples
+--------
+{% blockdiag {
+        blockdiag {
+          A -> B -> C;
+          B -> D;
+        }
+    }
+%}
+
+
+{% blockdiag {
+        actdiag {
+          A -> B -> C -> D -> E;
+
+          lane {
+            A; C; E;
+          }
+          lane {
+            B; D;
+          }
+        }
+    }
+%}
+
+
+{% blockdiag {
+        packetdiag {
+           0-7: Source Port
+           8-15: Destination Port
+           16-31: Sequence Number
+           32-47: Acknowledgment Number
+        }
+    }
+%}
+
+...
+
+
+Output
+------
+<span class="blockdiag" style="align: center;"><img src="data:image/png;base64,_BASE64_IMAGE DATA_/></span>
+
+"""
+
+import io
+import os
+import sys
+
+import base64
+import re
+from .mdx_liquid_tags import LiquidTags
+
+
+SYNTAX = '{% blockdiag [diagram type] [code] %}'
+DOT_BLOCK_RE = re.compile(r'^\s*(?P<diagram>\w+).*$', re.MULTILINE | re.DOTALL)
+
+_draw_mode = 'PNG'
+_publish_mode = 'PNG'
+
+
+def get_diag(code, command):
+    """ Generate diagramm and return data """
+    import tempfile
+    import shutil
+    code = code + u'\n'
+
+    try:
+        tmpdir = tempfile.mkdtemp()
+        fd, diag_name = tempfile.mkstemp(dir=tmpdir)
+
+        f = os.fdopen(fd, "w")
+        f.write(code.encode('utf-8'))
+        f.close()
+
+        format = _draw_mode.lower()
+        draw_name = diag_name + '.' + format
+
+        saved_argv = sys.argv
+        argv = [diag_name, '-T', format, '-o', draw_name]
+
+        if _draw_mode == 'SVG':
+            argv += ['--ignore-pil']
+
+        # Run command
+        command.main(argv)
+
+        # Read image data from file
+        file_name = diag_name + '.' + _publish_mode.lower()
+
+        with io.open(file_name, 'rb') as f:
+            data = f.read()
+            f.close()
+
+    finally:
+        for file in os.listdir(tmpdir):
+            os.unlink(tmpdir + "/" + file)
+
+        # os.rmdir will fail -> use shutil
+        shutil.rmtree(tmpdir)
+
+    return data
+
+
+def diag(code, command):
+    if command == "blockdiag":                      # blockdiag
+        import blockdiag.command
+        return get_diag(code, blockdiag.command)
+
+    elif command == "diagram":                      # diagram
+        import blockdiag.command
+        return get_diag(code, blockdiag.command)
+
+    elif command == "seqdiag":                      # seqdiag
+        import seqdiag.command
+        return get_diag(code, seqdiag.command)
+
+    elif command == "actdiag":                      # actdiag
+        import actdiag.command
+        return get_diag(code, actdiag.command)
+
+    elif command == "nwdiag":                       # nwdiag
+        import nwdiag.command
+        return get_diag(code, nwdiag.command)
+
+    elif command == "packetdiag":                   # packetdiag
+        import packetdiag.command
+        return get_diag(code, packetdiag.command)
+
+    elif command == "rackdiag":                     # racketdiag
+        import rackdiag.command
+        return get_diag(code, rackdiag.command)
+
+    else:                                           # not found
+        print("No such command %s" % command)
+        return None
+
+
+@LiquidTags.register("blockdiag")
+def blockdiag_parser(preprocessor, tag, markup):
+    """ Blockdiag parser """
+    m = DOT_BLOCK_RE.search(markup)
+    if m:
+        # Get diagram type and code
+        diagram = m.group('diagram').strip()
+        code = markup
+
+        # Run command
+        output = diag(code, diagram)
+
+        if output:
+            # Return Base64 encoded image
+            return '<span class="blockdiag" style="align: center;"><img src="data:image/png;base64,%s"></span>' % base64.b64encode(output)
+    else:
+        raise ValueError('Error processing input. '
+                         'Expected syntax: {0}'.format(SYNTAX))
+
+# This import allows image tag to be a Pelican plugin
+from .liquid_tags import register
diff --git a/pelican-plugins/liquid_tags/flickr.py b/pelican-plugins/liquid_tags/flickr.py
new file mode 100644
index 0000000000000000000000000000000000000000..9e74db6a6fe37a5fac1b79c94799fa01a9b39b37
--- /dev/null
+++ b/pelican-plugins/liquid_tags/flickr.py
@@ -0,0 +1,116 @@
+"""
+Flickr Tag
+----------
+This implements a Liquid-style flickr tag for Pelican.
+
+IMPORTANT: You have to create a API key to access the flickr api.
+You can do this `here <https://www.flickr.com/services/apps/create/apply>`_.
+Add the created key to your config under FLICKR_API_KEY.
+
+Syntax
+------
+{% flickr image_id [small|medium|large] ["alt text"|'alt text'] %}
+
+Example
+--------
+{% flickr 18841055371 large "Fichte"}
+
+Output
+------
+<a href="https://www.flickr.com/photos/marvinxsteadfast/18841055371/"><img src="https://farm6.staticflickr.com/5552/18841055371_17ac287217_b.jpg" alt="Fichte"></a>
+"""
+import json
+import re
+try:
+    from urllib.request import urlopen
+    from urllib.parse import urlencode
+except ImportError:
+    from urllib import urlopen, urlencode
+from .mdx_liquid_tags import LiquidTags
+
+
+SYNTAX = '''{% flickr image_id [small|medium|large] ["alt text"|'alt text'] %}'''
+PARSE_SYNTAX = re.compile((r'''(?P<photo_id>\S+)'''
+                           r'''(?:\s+(?P<size>large|medium|small))?'''
+                           r'''(?:\s+(['"]{0,1})(?P<alt>.+)(\3))?'''))
+
+
+def get_info(photo_id, api_key):
+    ''' Get photo informations from flickr api. '''
+    query = urlencode({
+        'method': 'flickr.photos.getInfo',
+        'api_key': api_key,
+        'photo_id': photo_id,
+        'format': 'json',
+        'nojsoncallback': '1'
+    })
+
+    r = urlopen('https://api.flickr.com/services/rest/?' + query)
+    info = json.loads(r.read().decode('utf-8'))
+
+    if info['stat'] == 'fail':
+        raise ValueError(info['message'])
+
+    return info
+
+
+def source_url(farm, server, id, secret, size):
+    ''' Url for direct jpg use. '''
+    if size == 'small':
+        img_size = 'n'
+    elif size == 'medium':
+        img_size = 'c'
+    elif size == 'large':
+        img_size = 'b'
+
+    return 'https://farm{}.staticflickr.com/{}/{}_{}_{}.jpg'.format(
+        farm, server, id, secret, img_size)
+
+
+def generate_html(attrs, api_key):
+    ''' Returns html code. '''
+    # getting flickr api data
+    flickr_data = get_info(attrs['photo_id'], api_key)
+
+    # if size is not defined it will use large as image size
+    if 'size' not in attrs.keys():
+        attrs['size'] = 'large'
+
+    # if no alt is defined it will use the flickr image title
+    if 'alt' not in attrs.keys():
+        attrs['alt'] = flickr_data['photo']['title']['_content']
+
+    # return final html code
+    return '<a href="{}"><img src="{}" alt="{}"></a>'.format(
+        flickr_data['photo']['urls']['url'][0]['_content'],
+        source_url(flickr_data['photo']['farm'],
+                   flickr_data['photo']['server'],
+                   attrs['photo_id'],
+                   flickr_data['photo']['secret'],
+                   attrs['size']),
+        attrs['alt'])
+
+
+@LiquidTags.register('flickr')
+def flickr(preprocessor, tag, markup):
+    # getting flickr api key out of config
+    api_key = preprocessor.configs.getConfig('FLICKR_API_KEY')
+
+    # parse markup and extract data
+    attrs = None
+
+    match = PARSE_SYNTAX.search(markup)
+    if match:
+        attrs = dict(
+            [(key, value.strip())
+             for (key, value) in match.groupdict().items() if value])
+    else:
+        raise ValueError('Error processing input. '
+                         'Expected syntax: {}'.format(SYNTAX))
+
+    return generate_html(attrs, api_key)
+
+
+# ---------------------------------------------------
+# This import allows image tag to be a Pelican plugin
+from liquid_tags import register
diff --git a/pelican-plugins/liquid_tags/generic.py b/pelican-plugins/liquid_tags/generic.py
new file mode 100644
index 0000000000000000000000000000000000000000..e52cd42404cd8d3eefc8e4780c7dd302563c039a
--- /dev/null
+++ b/pelican-plugins/liquid_tags/generic.py
@@ -0,0 +1,36 @@
+"""
+Generic Tag
+-----------
+This implements a tag that that is mostly useful for testing.
+
+This tag does not implement anything useful, but is a place that can be
+used for testing liquid_tags infrastructure, in situations that need
+a full lifecycle test.
+
+The first use case is a test of a tag that will pull out data from
+the configuration file and make it available during the test.
+
+A tag of
+{% generic config <config file variable> %>
+will be replaced with the value of that config file in html
+
+Not all config file variables are exposed - the set
+of variables are from the LIQUID_CONFIGS setting, which is a list of 
+variables to pass to the liquid tags.
+"""
+from .mdx_liquid_tags import LiquidTags
+
+@LiquidTags.register('generic')
+def generic(preprocessor, tag, markup):
+    (cmd, args) = markup.split(' ', 1)
+    if cmd.lower() == 'config':
+        config_param = args.split()[0].upper()
+        config_val = preprocessor.configs.getConfig(config_param)	
+        return(config_val)
+    else:
+        return 'generic: %s ' % markup
+
+#----------------------------------------------------------------------
+# This import allows image tag to be a Pelican plugin
+from liquid_tags import register
+
diff --git a/pelican-plugins/liquid_tags/giphy.py b/pelican-plugins/liquid_tags/giphy.py
new file mode 100644
index 0000000000000000000000000000000000000000..9decff32504f2d55e2661150b723b026d11b7697
--- /dev/null
+++ b/pelican-plugins/liquid_tags/giphy.py
@@ -0,0 +1,89 @@
+"""
+Giphy Tag
+---------
+
+This implements a Liquid-style Giphy tag for Pelican.
+
+IMPORTANT: You have to request a production API key from giphy `here <https://api.giphy.com/submit>`.
+For the first runs you could also use the public beta key you can get `here <https://github.com/giphy/GiphyAPI>`.
+
+Syntax
+------
+{% giphy gif_id ["alt text"|'alt text'] %}
+
+Example
+-------
+{% giphy aMSJFS6oFX0fC 'ive had some free time' %}
+
+Output
+------
+<a href="http://giphy.com/gifs/veronica-mars-aMSJFS6oFX0fC"><img src="http://media4.giphy.com/media/aMSJFS6oFX0fC/giphy.gif" alt="ive had some free time"></a>
+"""
+import json
+import re
+try:
+    from urllib.request import urlopen
+except ImportError:
+    from urllib import urlopen
+from .mdx_liquid_tags import LiquidTags
+
+
+SYNTAX = '''{% giphy gif_id ["alt text"|'alt text'] %}'''
+GIPHY = re.compile(r'''(?P<gif_id>[\S+]+)(?:\s+(['"]{0,1})(?P<alt>.+)(\\2))?''')
+
+
+def get_gif(api_key, gif_id):
+    '''Returns dict with gif informations from the API.'''
+    url = 'http://api.giphy.com/v1/gifs/{}?api_key={}'.format(gif_id, api_key)
+    r = urlopen(url)
+
+    return json.loads(r.read().decode('utf-8'))
+
+
+def create_html(api_key, attrs):
+    '''Returns complete html tag string.'''
+    gif = get_gif(api_key, attrs['gif_id'])
+
+    if 'alt' not in attrs.keys():
+        attrs['alt'] = 'source: {}'.format(gif['data']['source'])
+
+    html_out = '<a href="{}">'.format(gif['data']['url'])
+    html_out += '<img src="{}" alt="{}">'.format(
+        gif['data']['images']['original']['url'],
+        attrs['alt'])
+    html_out += '</a>'
+
+    return html_out
+
+
+def main(api_key, markup):
+    '''Doing the regex parsing and running the create_html function.'''
+    match = GIPHY.search(markup)
+
+    attrs = None
+
+    if match:
+        attrs = dict(
+            [(key, value.strip())
+             for (key, value) in match.groupdict().items() if value])
+
+    else:
+        raise ValueError('Error processing input. '
+                         'Expected syntax: {}'.format(SYNTAX))
+
+    return create_html(api_key, attrs)
+
+
+@LiquidTags.register('giphy')
+def giphy(preprocessor, tag, markup):
+    api_key = preprocessor.configs.getConfig('GIPHY_API_KEY')
+
+    if api_key is None:
+        raise ValueError('Please set GIPHY_API_KEY.')
+
+    return main(api_key, markup)
+
+
+# ---------------------------------------------------
+# This import allows image tag to be a Pelican plugin
+from liquid_tags import register
diff --git a/pelican-plugins/liquid_tags/gram.py b/pelican-plugins/liquid_tags/gram.py
new file mode 100644
index 0000000000000000000000000000000000000000..8fe18dde6f5608f71ad5078bb8e95cccd465abfe
--- /dev/null
+++ b/pelican-plugins/liquid_tags/gram.py
@@ -0,0 +1,103 @@
+"""
+Instagram Image Tag
+-------------------
+
+By `Tom Spalding <https://github.com/digitalvapor>`_
+
+You can see a working example at `antivapor.net/instagram-tag.html <http://antivapor.net/instagram-tag.html>`_.
+
+Based on `Liquid Image Tag <https://github.com/getpelican/pelican-plugins/blob/master/liquid_tags/img.py>`_ by `Jake Vanderplas <https://github.com/jakevdp>`_.
+
+Optional Todo:
+* Query JSON to automatically include descriptions.
+  http://api.instagram.com/oembed?url=http://instagr.am/p/olw8jXiz1_/
+  and option to add wrapping anchor link to original http://instagram.com/p/olw8jXiz1_
+* Default to size m
+  http://instagr.am/p/olw8jXiz1_/media/?size=t
+  http://instagr.am/p/olw8jXiz1_/media
+* Provide examples using with [Better Figures and Images](https://github.com/getpelican/pelican-plugins/tree/master/better_figures_and_images).
+
+Syntax
+------
+
+    {% gram shortcode [size] [width] [class name(s)] [title text | "title text" ["alt text"]] %}
+
+where size is t, m, or l, and it defaults to m. see http://instagram.com/developer/embedding.
+
+Examples
+--------
+
+    {% gram pFG7naIZkr t %}
+    {% gram pFJE11IZnx %}
+    {% gram pFI0CAIZna l 400 figure 'pretty turkey tail fungus' %}
+    {% gram rOru21oZpe l 450 test_class instagram 'warehouse window title' 'alt text' %}
+
+Output
+------
+
+    <img src="http://photos-c.ak.instagram.com/hphotos-ak-xaf1/t51.2885-15/917172_604907902963826_254280879_n.jpg" width="450" title="warehouse window title" alt="alt text" class="test_class instagram">
+"""
+import re
+try:
+    from urllib.request import urlopen
+except ImportError:
+    from urllib import urlopen
+from .mdx_liquid_tags import LiquidTags
+
+SYNTAX = '{% gram shortcode [size] [width] [class name(s)] [title text | "title text" ["alt text"]] %}'
+
+# Regular expression for full syntax
+# ReGram = re.compile("""(?P<shortcode>\S+)(?:\s+(?P<size>[tml]?))?(?:\s+(?P<width>\d*))?(?:\s+(?P<class>\S*))?(?P<title>\s+.+)?""")
+ReGram = re.compile("""(?P<shortcode>\S+)(?:\s+(?P<size>[tml]?))?(?:\s+(?P<width>\d*))?(?:\s+(?P<class>[^']*))?(?P<title>.+)?""")
+
+# Regular expression to split the title and alt text
+ReTitleAlt = re.compile("""(?:"|')(?P<title>[^"']+)?(?:"|')\s+(?:"|')(?P<alt>[^"']+)?(?:"|')""")
+
+@LiquidTags.register('gram')
+def gram(preprocessor, tag, markup):
+
+    attrs = None
+
+    # Parse the markup string
+    match = ReGram.search(markup)
+    if match:
+        attrs = dict([(key, val.strip())
+                      for (key, val) in match.groupdict().items() if val])
+    else:
+        raise ValueError('Error processing input. '
+                         'Expected syntax: {0}'.format(SYNTAX))
+
+    # Construct URI
+    #print(attrs)
+    shortcode = attrs['shortcode']
+    url = 'http://instagr.am/p/'+shortcode+'/media/'
+    del attrs['shortcode']
+
+    if 'size' in attrs:
+        size = '?size={0}'.format(attrs['size'])
+        url = url+size
+        del attrs['size']
+
+    r = urlopen(url)
+
+    if(r.getcode()==404):
+        raise ValueError('%s isnt a photo.'%shortcode)
+
+    gram_url = r.geturl()
+
+    # Check if alt text is present -- if so, split it from title
+    if 'title' in attrs:
+        match = ReTitleAlt.search(attrs['title'])
+        if match:
+            attrs.update(match.groupdict())
+        if not attrs.get('alt'):
+            attrs['alt'] = attrs['title']
+
+    #print('updated dict: '+repr(attrs))
+
+    # Return the formatted text
+    return '<img src="{0}"{1}>'.format(gram_url,' '.join(' {0}="{1}"'.format(key,val) for (key,val) in attrs.items()))
+
+#----------------------------------------------------------------------
+# This import allows image tag to be a Pelican plugin
+from liquid_tags import register
diff --git a/pelican-plugins/liquid_tags/graphviz.py b/pelican-plugins/liquid_tags/graphviz.py
new file mode 100644
index 0000000000000000000000000000000000000000..ba7764bddbd37b04b12db45720cd226a891caac6
--- /dev/null
+++ b/pelican-plugins/liquid_tags/graphviz.py
@@ -0,0 +1,128 @@
+"""
+GraphViz Tag
+---------
+This implements a Liquid-style graphviz tag for Pelican. You can use different
+Graphviz programs like dot, neato, twopi etc. [1]
+
+
+[1] http://www.graphviz.org/
+
+Syntax
+------
+{% graphviz
+    <program> {
+        <DOT code>
+    }
+%}
+
+Examples
+--------
+{% graphviz
+    dot {
+        digraph graphname {
+            a -> b -> c;
+            b -> d;
+        }
+    }
+%}
+
+
+{% graphviz
+    twopi {
+        <code goes here>
+    }
+%}
+
+
+{% graphviz
+    neato {
+        <code goes here>
+    }
+%}
+
+...
+
+
+Output
+------
+<span class="graphviz" style="text-align: center;"><img src="data:image/png;base64,_BASE64_IMAGE DATA_/></span>
+
+"""
+
+import base64
+import re
+from errno import EINVAL, EPIPE
+from .mdx_liquid_tags import LiquidTags
+
+SYNTAX = '{% dot graphviz [program] [dot code] %}'
+DOT_BLOCK_RE = re.compile(r'^\s*(?P<program>\w+)\s*\{\s*(?P<code>.*\})\s*\}$', re.MULTILINE | re.DOTALL)
+
+
+def run_graphviz(program, code, options=[], format='png'):
+    """ Runs graphviz programs and returns image data
+
+        Copied from https://github.com/tkf/ipython-hierarchymagic/blob/master/hierarchymagic.py
+    """
+    import os
+    from subprocess import Popen, PIPE
+
+    dot_args = [program] + options + ['-T', format]
+
+    if os.name == 'nt':
+        # Avoid opening shell window.
+        # * https://github.com/tkf/ipython-hierarchymagic/issues/1
+        # * http://stackoverflow.com/a/2935727/727827
+        p = Popen(dot_args, stdout=PIPE, stdin=PIPE, stderr=PIPE, creationflags=0x08000000)
+    else:
+        p = Popen(dot_args, stdout=PIPE, stdin=PIPE, stderr=PIPE)
+        wentwrong = False
+
+    try:
+        # Graphviz may close standard input when an error occurs,
+        # resulting in a broken pipe on communicate()
+        stdout, stderr = p.communicate(code.encode('utf-8'))
+    except (OSError, IOError) as err:
+        if err.errno != EPIPE:
+            raise
+        wentwrong = True
+    except IOError as err:
+        if err.errno != EINVAL:
+            raise
+        wentwrong = True
+
+    if wentwrong:
+    # in this case, read the standard output and standard error streams
+    # directly, to get the error message(s)
+        stdout, stderr = p.stdout.read(), p.stderr.read()
+        p.wait()
+
+    if p.returncode != 0:
+        raise RuntimeError('dot exited with error:\n[stderr]\n{0}'.format(stderr.decode('utf-8')))
+
+    return stdout
+
+
+@LiquidTags.register('graphviz')
+def graphviz_parser(preprocessor, tag, markup):
+    """ Simple Graphviz parser """
+
+    # Parse the markup string
+    m = DOT_BLOCK_RE.search(markup)
+    if m:
+        # Get program and DOT code
+        code = m.group('code')
+        program = m.group('program').strip()
+
+        # Run specified program with our markup
+        output = run_graphviz(program, code)
+
+        # Return Base64 encoded image
+        return '<span class="graphviz" style="text-align: center;"><img src="data:image/png;base64,%s"></span>' % base64.b64encode(output).decode('utf-8')
+    else:
+        raise ValueError('Error processing input. '
+                         'Expected syntax: {0}'.format(SYNTAX))
+
+#----------------------------------------------------------------------
+# This import allows image tag to be a Pelican plugin
+from .liquid_tags import register
+
diff --git a/pelican-plugins/liquid_tags/img.py b/pelican-plugins/liquid_tags/img.py
new file mode 100644
index 0000000000000000000000000000000000000000..22d83e436b2d480442dd4195852f5ccabefab9d3
--- /dev/null
+++ b/pelican-plugins/liquid_tags/img.py
@@ -0,0 +1,66 @@
+"""
+Image Tag
+---------
+This implements a Liquid-style image tag for Pelican,
+based on the octopress image tag [1]_
+
+Syntax
+------
+{% img [class name(s)] [http[s]:/]/path/to/image [width [height]] [title text | "title text" ["alt text"]] %}
+
+Examples
+--------
+{% img /images/ninja.png Ninja Attack! %}
+{% img left half http://site.com/images/ninja.png Ninja Attack! %}
+{% img left half http://site.com/images/ninja.png 150 150 "Ninja Attack!" "Ninja in attack posture" %}
+
+Output
+------
+<img src="/images/ninja.png">
+<img class="left half" src="http://site.com/images/ninja.png" title="Ninja Attack!" alt="Ninja Attack!">
+<img class="left half" src="http://site.com/images/ninja.png" width="150" height="150" title="Ninja Attack!" alt="Ninja in attack posture">
+
+[1] https://github.com/imathis/octopress/blob/master/plugins/image_tag.rb
+"""
+import re
+from .mdx_liquid_tags import LiquidTags
+import six
+
+SYNTAX = '{% img [class name(s)] [http[s]:/]/path/to/image [width [height]] [title text | "title text" ["alt text"]] %}'
+
+# Regular expression to match the entire syntax
+ReImg = re.compile("""(?P<class>\S.*\s+)?(?P<src>(?:https?:\/\/|\/|\S+\/)\S+)(?:\s+(?P<width>\d+))?(?:\s+(?P<height>\d+))?(?P<title>\s+.+)?""")
+
+# Regular expression to split the title and alt text
+ReTitleAlt = re.compile("""(?:"|')(?P<title>[^"']+)?(?:"|')\s+(?:"|')(?P<alt>[^"']+)?(?:"|')""")
+
+
+@LiquidTags.register('img')
+def img(preprocessor, tag, markup):
+    attrs = None
+
+    # Parse the markup string
+    match = ReImg.search(markup)
+    if match:
+        attrs = dict([(key, val.strip())
+                      for (key, val) in six.iteritems(match.groupdict()) if val])
+    else:
+        raise ValueError('Error processing input. '
+                         'Expected syntax: {0}'.format(SYNTAX))
+
+    # Check if alt text is present -- if so, split it from title
+    if 'title' in attrs:
+        match = ReTitleAlt.search(attrs['title'])
+        if match:
+            attrs.update(match.groupdict())
+        if not attrs.get('alt'):
+            attrs['alt'] = attrs['title']
+
+    # Return the formatted text
+    return "<img {0}>".format(' '.join('{0}="{1}"'.format(key, val)
+                                       for (key, val) in six.iteritems(attrs)))
+
+#----------------------------------------------------------------------
+# This import allows image tag to be a Pelican plugin
+from .liquid_tags import register
+
diff --git a/pelican-plugins/liquid_tags/include_code.py b/pelican-plugins/liquid_tags/include_code.py
new file mode 100644
index 0000000000000000000000000000000000000000..185a62ee089243d6c076c864ce3017c7add8538d
--- /dev/null
+++ b/pelican-plugins/liquid_tags/include_code.py
@@ -0,0 +1,166 @@
+"""
+Include Code Tag
+----------------
+This implements a Liquid-style video tag for Pelican,
+based on the octopress video tag [1]_
+
+Syntax
+------
+{% include_code path/to/code [lang:python] [Title text] [codec:utf8] %}
+
+The "path to code" is specified relative to the ``code`` subdirectory of
+the content directory  Optionally, this subdirectory can be specified in the
+config file:
+
+    CODE_DIR = 'code'
+
+If your input file is not ASCII/UTF-8 encoded, you need to specify the
+appropriate input codec by using the ``codec`` option.
+Example ``codec:iso-8859-1``
+Using this option does not affect the output encoding.
+
+For a list of valid codec identifiers, see
+https://docs.python.org/2/library/codecs.html#standard-encodings
+
+Example
+-------
+{% include_code myscript.py %}
+
+This will import myscript.py from content/code/myscript.py
+and output the contents in a syntax highlighted code block inside a figure,
+with a figcaption listing the file name and download link.
+
+The file link will be valid only if the 'code' directory is listed
+in the STATIC_PATHS setting, e.g.:
+
+    STATIC_PATHS = ['images', 'code']
+
+[1] https://github.com/imathis/octopress/blob/master/plugins/include_code.rb
+"""
+import re
+import os
+import sys
+from .mdx_liquid_tags import LiquidTags
+
+
+SYNTAX = "{% include_code /path/to/code.py [lang:python] [lines:X-Y] "\
+         "[:hidefilename:] [:hidelink:] [:hideall:] [title] %}"
+FORMAT = re.compile(r"""
+^(?:\s+)?                          # Allow whitespace at beginning
+(?P<src>\S+)                       # Find the path
+(?:\s+)?                           # Whitespace
+(?:(?:lang:)(?P<lang>\S+))?        # Optional language
+(?:\s+)?                           # Whitespace
+(?:(?:lines:)(?P<lines>\d+-\d+))?  # Optional lines
+(?:\s+)?                           # Whitespace
+(?P<hidefilename>:hidefilename:)?  # Hidefilename flag
+(?:\s+)?                           # Whitespace
+(?P<hidelink>:hidelink:)?          # Hide download link
+(?:\s+)?                           # Whitespace
+(?P<hideall>:hideall:)?            # Hide title and download link
+(?:\s+)?                           # Whitespace
+(?:(?:codec:)(?P<codec>\S+))?      # Optional language
+(?:\s+)?                           # Whitespace
+(?P<title>.+)?$                    # Optional title
+""", re.VERBOSE)
+
+
+@LiquidTags.register('include_code')
+def include_code(preprocessor, tag, markup):
+
+    title = None
+    lang = None
+    src = None
+
+    match = FORMAT.search(markup)
+    if match:
+        argdict = match.groupdict()
+        title = argdict['title'] or ""
+        lang = argdict['lang']
+        codec = argdict['codec'] or "utf8"
+        lines = argdict['lines']
+        hide_filename = bool(argdict['hidefilename'])
+        hide_link = bool(argdict['hidelink'])
+        hide_all = bool(argdict['hideall'])
+        if lines:
+            first_line, last_line = map(int, lines.split("-"))
+        src = argdict['src']
+
+    if not src:
+        raise ValueError("Error processing input, "
+                         "expected syntax: {0}".format(SYNTAX))
+
+    code_dir = preprocessor.configs.getConfig('CODE_DIR')
+    code_path = os.path.join('content', code_dir, src)
+
+    if not os.path.exists(code_path):
+        raise ValueError("File {0} could not be found".format(code_path))
+
+    if not codec:
+        codec = 'utf-8'
+
+    with open(code_path, encoding=codec) as fh:
+        if lines:
+            code = fh.readlines()[first_line - 1: last_line]
+            code[-1] = code[-1].rstrip()
+            code = "".join(code)
+        else:
+            code = fh.read()
+
+    if (not title and hide_filename) and not hide_all:
+        raise ValueError("Either title must be specified or filename must "
+                         "be available")
+
+    open_tag = "<figure class='code'>\n"
+    close_tag = "</figure>"
+
+    if not hide_all:
+        open_tag+= "<figcaption>"
+
+        if title:
+            open_tag += "<span class=\"liquid-tags-code-title\">{title}</span>".format(title=title.strip())
+
+        if not hide_filename:
+            filename = "%s" % os.path.basename(src)
+            open_tag += "<span class=\"liquid-tags-code-filename\">{filename}</span>".format(filename=filename.strip())
+
+        if lines:
+            lines = " [Lines %s]" % lines
+            open_tag += "<span class=\"liquid-tags-code-lines\">{lines}</span>".format(lines=lines.strip())
+
+        if not hide_link:
+            url = '/{0}/{1}'.format(code_dir, src)
+            url = re.sub('/+', '/', url)
+            open_tag += "<a href='{url}'>download</a>".format(url=url)
+
+        open_tag += "</figcaption>"
+
+    # store HTML tags in the stash.  This prevents them from being
+    # modified by markdown.
+    open_tag = preprocessor.configs.htmlStash.store(open_tag)
+    close_tag = preprocessor.configs.htmlStash.store(close_tag)
+
+    if lang:
+        lang_include = ':::' + lang + '\n    '
+    else:
+        lang_include = ''
+
+    if sys.version_info[0] < 3:
+        source = (open_tag
+                  + '\n\n    '
+                  + lang_include
+                  + '\n    '.join(code.decode(codec).split('\n')) + '\n\n'
+                  + close_tag + '\n')
+    else:
+        source = (open_tag
+                  + '\n\n    '
+                  + lang_include
+                  + '\n    '.join(code.split('\n')) + '\n\n'
+                  + close_tag + '\n')
+
+    return source
+
+
+#----------------------------------------------------------------------
+# This import allows image tag to be a Pelican plugin
+from liquid_tags import register
diff --git a/pelican-plugins/liquid_tags/liquid_tags.py b/pelican-plugins/liquid_tags/liquid_tags.py
new file mode 100644
index 0000000000000000000000000000000000000000..69bb9ddf166add6ac70687c7bac5ce8b7ba30173
--- /dev/null
+++ b/pelican-plugins/liquid_tags/liquid_tags.py
@@ -0,0 +1,30 @@
+from pelican import signals
+from .mdx_liquid_tags import LiquidTags, LT_CONFIG, LT_HELP
+
+
+def addLiquidTags(gen):
+    if not gen.settings.get('MARKDOWN'):
+        from pelican.settings import DEFAULT_CONFIG
+        gen.settings['MARKDOWN'] = DEFAULT_CONFIG['MARKDOWN']
+
+    if gen.settings.get('LIQUID_CONFIGS'):
+        for param,default,helptext in gen.settings.get('LIQUID_CONFIGS'):
+            LT_CONFIG[param] = default
+            LT_HELP[param] = helptext
+
+    if LiquidTags not in gen.settings['MARKDOWN']:
+        configs = dict()
+        for key,value in LT_CONFIG.items():
+            configs[key]=value
+        for key,value in gen.settings.items():
+            if key in LT_CONFIG:
+                configs[key]=value
+        gen.settings['MARKDOWN'].setdefault(
+            'extensions', []
+        ).append(
+            LiquidTags(configs)
+        )
+
+
+def register():
+    signals.initialized.connect(addLiquidTags)
diff --git a/pelican-plugins/liquid_tags/literal.py b/pelican-plugins/liquid_tags/literal.py
new file mode 100644
index 0000000000000000000000000000000000000000..7c0460221a802866fad207b394f247a038bc9684
--- /dev/null
+++ b/pelican-plugins/liquid_tags/literal.py
@@ -0,0 +1,27 @@
+"""
+Literal Tag
+-----------
+This implements a tag that allows explicitly showing commands which would
+otherwise be interpreted as a liquid tag.
+
+For example, the line
+
+    {% literal video arg1 arg2 %}
+
+would result in the following line:
+
+    {% video arg1 arg2 %}
+
+This is useful when the resulting line would be interpreted as another
+liquid-style tag.
+"""
+from .mdx_liquid_tags import LiquidTags
+
+@LiquidTags.register('literal')
+def literal(preprocessor, tag, markup):
+    return '{%% %s %%}' % markup
+
+#----------------------------------------------------------------------
+# This import allows image tag to be a Pelican plugin
+from liquid_tags import register
+
diff --git a/pelican-plugins/liquid_tags/mdx_liquid_tags.py b/pelican-plugins/liquid_tags/mdx_liquid_tags.py
new file mode 100644
index 0000000000000000000000000000000000000000..60edf5e216cc51fc8e0aa522a0a899df44f65a14
--- /dev/null
+++ b/pelican-plugins/liquid_tags/mdx_liquid_tags.py
@@ -0,0 +1,98 @@
+"""
+Markdown Extension for Liquid-style Tags
+----------------------------------------
+A markdown extension to allow user-defined tags of the form::
+
+    {% tag arg1 arg2 ... argn %}
+
+Where "tag" is associated with some user-defined extension.
+These result in a preprocess step within markdown that produces
+either markdown or html.
+"""
+import warnings
+import markdown
+import itertools
+import re
+import os
+from functools import wraps
+
+# Define some regular expressions
+LIQUID_TAG = re.compile(r'\{%.*?%\}', re.MULTILINE | re.DOTALL)
+EXTRACT_TAG = re.compile(r'(?:\s*)(\S+)(?:\s*)')
+LT_CONFIG = { 'CODE_DIR': 'code',
+              'NOTEBOOK_DIR': 'notebooks',
+              'FLICKR_API_KEY': 'flickr',
+              'GIPHY_API_KEY': 'giphy'
+}
+LT_HELP = { 'CODE_DIR' : 'Code directory for include_code subplugin',
+            'NOTEBOOK_DIR' : 'Notebook directory for notebook subplugin',
+            'FLICKR_API_KEY': 'Flickr key for accessing the API',
+            'GIPHY_API_KEY': 'Giphy key for accessing the API'
+}
+
+class _LiquidTagsPreprocessor(markdown.preprocessors.Preprocessor):
+    _tags = {}
+    def __init__(self, configs):
+        self.configs = configs
+
+    def run(self, lines):
+        page = '\n'.join(lines)
+        liquid_tags = LIQUID_TAG.findall(page)
+
+        for i, markup in enumerate(liquid_tags):
+            # remove {% %}
+            markup = markup[2:-2]
+            tag = EXTRACT_TAG.match(markup).groups()[0]
+            markup = EXTRACT_TAG.sub('', markup, 1)
+            if tag in self._tags:
+                liquid_tags[i] = self._tags[tag](self, tag, markup.strip())
+
+        # add an empty string to liquid_tags so that chaining works
+        liquid_tags.append('')
+
+        # reconstruct string
+        page = ''.join(itertools.chain(*zip(LIQUID_TAG.split(page),
+                                            liquid_tags)))
+
+        # resplit the lines
+        return page.split("\n")
+
+
+class LiquidTags(markdown.Extension):
+    """Wrapper for MDPreprocessor"""
+    def __init__(self, config):
+        try:
+            # Needed for markdown versions >= 2.5
+            for key,value in LT_CONFIG.items():
+                self.config[key] = [value,LT_HELP[key]]
+            super(LiquidTags,self).__init__(**config)
+        except AttributeError:
+            # Markdown versions < 2.5
+            for key,value in LT_CONFIG.items():
+                config[key] = [config[key],LT_HELP[key]]
+            super(LiquidTags,self).__init__(config)
+
+    @classmethod
+    def register(cls, tag):
+        """Decorator to register a new include tag"""
+        def dec(func):
+            if tag in _LiquidTagsPreprocessor._tags:
+                warnings.warn("Enhanced Markdown: overriding tag '%s'" % tag)
+            _LiquidTagsPreprocessor._tags[tag] = func
+            return func
+        return dec
+
+    def extendMarkdown(self, md, md_globals):
+        self.htmlStash = md.htmlStash
+        md.registerExtension(self)
+        # for the include_code preprocessor, we need to re-run the
+        # fenced code block preprocessor after substituting the code.
+        # Because the fenced code processor is run before, {% %} tags
+        # within equations will not be parsed as an include.
+        md.preprocessors.add('mdincludes',
+                             _LiquidTagsPreprocessor(self), ">html_block")
+
+
+def makeExtension(configs=None):
+    """Wrapper for a MarkDown extension"""
+    return LiquidTags(configs=configs)
diff --git a/pelican-plugins/liquid_tags/notebook.py b/pelican-plugins/liquid_tags/notebook.py
new file mode 100644
index 0000000000000000000000000000000000000000..c310e6d5ed0182ff756abec8025b47b45928bf5a
--- /dev/null
+++ b/pelican-plugins/liquid_tags/notebook.py
@@ -0,0 +1,355 @@
+"""
+Notebook Tag
+------------
+This is a liquid-style tag to include a static html rendering of an IPython
+notebook in a blog post.
+
+Syntax
+------
+{% notebook filename.ipynb [ cells[start:end] language[language] ]%}
+
+The file should be specified relative to the ``notebooks`` subdirectory of the
+content directory.  Optionally, this subdirectory can be specified in the
+config file:
+
+    NOTEBOOK_DIR = 'notebooks'
+
+The cells[start:end] statement is optional, and can be used to specify which
+block of cells from the notebook to include.
+
+The language statement is obvious and can be used to specify whether ipython2
+or ipython3 syntax highlighting should be used.
+
+Requirements
+------------
+- The plugin requires IPython version 1.0 or above.  It no longer supports the
+  standalone nbconvert package, which has been deprecated.
+
+Details
+-------
+Because the notebook relies on some rather extensive custom CSS, the use of
+this plugin requires additional CSS to be inserted into the blog theme.
+After typing "make html" when using the notebook tag, a file called
+``_nb_header.html`` will be produced in the main directory.  The content
+of the file should be included in the header of the theme.  An easy way
+to accomplish this is to add the following lines within the header template
+of the theme you use:
+
+    {% if EXTRA_HEADER %}
+      {{ EXTRA_HEADER }}
+    {% endif %}
+
+and in your ``pelicanconf.py`` file, include the line:
+
+    EXTRA_HEADER = open('_nb_header.html').read().decode('utf-8')
+
+this will insert the appropriate CSS.  All efforts have been made to ensure
+that this CSS will not override formats within the blog theme, but there may
+still be some conflicts.
+"""
+import warnings
+import re
+import os
+from functools import partial
+from io import open
+
+from .mdx_liquid_tags import LiquidTags
+
+import IPython
+IPYTHON_VERSION = IPython.version_info[0]
+
+try:
+    import nbformat
+except:
+    pass
+
+if not IPYTHON_VERSION >= 1:
+    raise ValueError("IPython version 1.0+ required for notebook tag")
+
+if IPYTHON_VERSION > 1:
+    warnings.warn("Pelican plugin is not designed to work with IPython "
+                  "versions greater than 1.x. CSS styles have changed in "
+                  "later releases.")
+
+try:
+    from nbconvert.filters.highlight import _pygments_highlight
+except ImportError:
+    try:
+        from IPython.nbconvert.filters.highlight import _pygments_highlight
+    except ImportError:
+        # IPython < 2.0
+        from IPython.nbconvert.filters.highlight import _pygment_highlight as _pygments_highlight
+
+from pygments.formatters import HtmlFormatter
+
+try:
+    from nbconvert.exporters import HTMLExporter
+except ImportError:
+        from IPython.nbconvert.exporters import HTMLExporter
+
+try:
+    from traitlets.config import Config
+except ImportError:
+    from IPython.config import Config
+
+try:
+    from nbconvert.preprocessors import Preprocessor
+except ImportError:
+    try:
+        from IPython.nbconvert.preprocessors import Preprocessor
+    except ImportError:
+        # IPython < 2.0
+        from IPython.nbconvert.transformers import Transformer as Preprocessor
+
+try:
+    from traitlets import Integer
+except ImportError:
+    from IPython.utils.traitlets import Integer
+
+from copy import deepcopy
+
+#----------------------------------------------------------------------
+# Some code that will be added to the header:
+#  Some of the following javascript/css include is adapted from
+#  IPython/nbconvert/templates/fullhtml.tpl, while some are custom tags
+#  specifically designed to make the results look good within the
+#  pelican-octopress theme.
+JS_INCLUDE = r"""
+<style type="text/css">
+/* Overrides of notebook CSS for static HTML export */
+div.entry-content {
+  overflow: visible;
+  padding: 8px;
+}
+.input_area {
+  padding: 0.2em;
+}
+
+a.heading-anchor {
+ white-space: normal;
+}
+
+.rendered_html
+code {
+ font-size: .8em;
+}
+
+pre.ipynb {
+  color: black;
+  background: #f7f7f7;
+  border: none;
+  box-shadow: none;
+  margin-bottom: 0;
+  padding: 0;
+  margin: 0px;
+  font-size: 13px;
+}
+
+/* remove the prompt div from text cells */
+div.text_cell .prompt {
+    display: none;
+}
+
+/* remove horizontal padding from text cells, */
+/* so it aligns with outer body text */
+div.text_cell_render {
+    padding: 0.5em 0em;
+}
+
+img.anim_icon{padding:0; border:0; vertical-align:middle; -webkit-box-shadow:none; -box-shadow:none}
+
+div.collapseheader {
+    width=100%;
+    background-color:#d3d3d3;
+    padding: 2px;
+    cursor: pointer;
+    font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;
+}
+</style>
+
+<script type="text/x-mathjax-config">
+MathJax.Hub.Config({
+    tex2jax: {
+        inlineMath: [['$','$'], ['\\(','\\)']],
+        processEscapes: true,
+        displayMath: [['$$','$$'], ["\\[","\\]"]]
+    }
+});
+</script>
+<script type="text/javascript" async src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.2/MathJax.js?config=TeX-MML-AM_CHTML">
+</script>
+
+<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
+
+<script type="text/javascript">
+jQuery(document).ready(function($) {
+    $("div.collapseheader").click(function () {
+    $header = $(this).children("span").first();
+    $codearea = $(this).children(".input_area");
+    console.log($(this).children());
+    $codearea.slideToggle(500, function () {
+        $header.text(function () {
+            return $codearea.is(":visible") ? "Collapse Code" : "Expand Code";
+        });
+    });
+});
+});
+</script>
+
+"""
+
+CSS_WRAPPER = """
+<style type="text/css">
+{0}
+</style>
+"""
+
+
+#----------------------------------------------------------------------
+# Create a custom preprocessor
+class SliceIndex(Integer):
+    """An integer trait that accepts None"""
+    default_value = None
+
+    def validate(self, obj, value):
+        if value is None:
+            return value
+        else:
+            return super(SliceIndex, self).validate(obj, value)
+
+
+class SubCell(Preprocessor):
+    """A transformer to select a slice of the cells of a notebook"""
+    start = SliceIndex(0, config=True,
+                       help="first cell of notebook to be converted")
+    end = SliceIndex(None, config=True,
+                     help="last cell of notebook to be converted")
+
+    def preprocess(self, nb, resources):
+        nbc = deepcopy(nb)
+        if IPYTHON_VERSION < 3:
+            for worksheet in nbc.worksheets:
+                cells = worksheet.cells[:]
+                worksheet.cells = cells[self.start:self.end]
+        else:
+            nbc.cells = nbc.cells[self.start:self.end]
+
+        return nbc, resources
+
+    call = preprocess # IPython < 2.0
+
+
+
+#----------------------------------------------------------------------
+# Custom highlighter:
+#  instead of using class='highlight', use class='highlight-ipynb'
+def custom_highlighter(source, language='ipython', metadata=None):
+    formatter = HtmlFormatter(cssclass='highlight-ipynb')
+    if not language:
+        language = 'ipython'
+    output = _pygments_highlight(source, formatter, language)
+    return output.replace('<pre>', '<pre class="ipynb">')
+
+
+#----------------------------------------------------------------------
+# Below is the pelican plugin code.
+#
+SYNTAX = "{% notebook /path/to/notebook.ipynb [ cells[start:end] ] [ language[language] ] %}"
+FORMAT = re.compile(r"""^(\s+)?(?P<src>\S+)(\s+)?((cells\[)(?P<start>-?[0-9]*):(?P<end>-?[0-9]*)(\]))?(\s+)?((language\[)(?P<language>-?[a-z0-9\+\-]*)(\]))?(\s+)?$""")
+
+
+@LiquidTags.register('notebook')
+def notebook(preprocessor, tag, markup):
+    match = FORMAT.search(markup)
+    if match:
+        argdict = match.groupdict()
+        src = argdict['src']
+        start = argdict['start']
+        end = argdict['end']
+        language = argdict['language']
+    else:
+        raise ValueError("Error processing input, "
+                         "expected syntax: {0}".format(SYNTAX))
+
+    if start:
+        start = int(start)
+    else:
+        start = 0
+
+    if end:
+        end = int(end)
+    else:
+        end = None
+
+    language_applied_highlighter = partial(custom_highlighter, language=language)
+
+    nb_dir =  preprocessor.configs.getConfig('NOTEBOOK_DIR')
+    nb_path = os.path.join(settings.get('PATH', 'content'), nb_dir, src)
+
+    if not os.path.exists(nb_path):
+        raise ValueError("File {0} could not be found".format(nb_path))
+
+    # Create the custom notebook converter
+    c = Config({'CSSHTMLHeaderTransformer':
+                    {'enabled':True, 'highlight_class':'.highlight-ipynb'},
+                'SubCell':
+                    {'enabled':True, 'start':start, 'end':end}})
+
+    template_file = 'basic'
+    if IPYTHON_VERSION >= 3:
+        if os.path.exists('pelicanhtml_3.tpl'):
+            template_file = 'pelicanhtml_3'
+    elif IPYTHON_VERSION == 2:
+        if os.path.exists('pelicanhtml_2.tpl'):
+            template_file = 'pelicanhtml_2'
+    else:
+        if os.path.exists('pelicanhtml_1.tpl'):
+            template_file = 'pelicanhtml_1'
+
+    if IPYTHON_VERSION >= 2:
+        subcell_kwarg = dict(preprocessors=[SubCell])
+    else:
+        subcell_kwarg = dict(transformers=[SubCell])
+
+    exporter = HTMLExporter(config=c,
+                            template_file=template_file,
+                            filters={'highlight2html': language_applied_highlighter},
+                            **subcell_kwarg)
+
+    # read and parse the notebook
+    with open(nb_path, encoding='utf-8') as f:
+        nb_text = f.read()
+        if IPYTHON_VERSION < 3:
+            nb_json = IPython.nbformat.current.reads_json(nb_text)
+        else:
+            try:
+                nb_json = nbformat.reads(nb_text, as_version=4)
+            except:
+                nb_json = IPython.nbformat.reads(nb_text, as_version=4)
+
+    (body, resources) = exporter.from_notebook_node(nb_json)
+
+    # if we haven't already saved the header, save it here.
+    if not notebook.header_saved:
+        print ("\n ** Writing styles to _nb_header.html: "
+               "this should be included in the theme. **\n")
+
+        header = '\n'.join(CSS_WRAPPER.format(css_line)
+                           for css_line in resources['inlining']['css'])
+        header += JS_INCLUDE
+
+        with open('_nb_header.html', 'w') as f:
+            f.write(header)
+        notebook.header_saved = True
+
+    # this will stash special characters so that they won't be transformed
+    # by subsequent processes.
+    body = preprocessor.configs.htmlStash.store(body)
+    return body
+
+notebook.header_saved = False
+
+
+#----------------------------------------------------------------------
+# This import allows notebook to be a Pelican plugin
+from liquid_tags import register
diff --git a/pelican-plugins/liquid_tags/pelicanhtml_1.tpl b/pelican-plugins/liquid_tags/pelicanhtml_1.tpl
new file mode 100644
index 0000000000000000000000000000000000000000..e0094f30ec7f9164924ee6d4a0d77c92ea2ae2fe
--- /dev/null
+++ b/pelican-plugins/liquid_tags/pelicanhtml_1.tpl
@@ -0,0 +1,44 @@
+{%- extends 'html_basic.tpl' -%}
+
+{% block stream_stdout -%}
+<div class="box-flex1 output_subarea output_stream output_stdout">
+<pre class="ipynb">{{output.text |ansi2html}}</pre>
+</div>
+{%- endblock stream_stdout %}
+
+{% block stream_stderr -%}
+<div class="box-flex1 output_subarea output_stream output_stderr">
+<pre class="ipynb">{{output.text |ansi2html}}</pre>
+</div>
+{%- endblock stream_stderr %}
+
+{% block pyerr -%}
+<div class="box-flex1 output_subarea output_pyerr">
+<pre class="ipynb">{{super()}}</pre>
+</div>
+{%- endblock pyerr %}
+
+{%- block data_text %}
+<pre class="ipynb">{{output.text | ansi2html}}</pre>
+{%- endblock -%}
+
+{% block input %}
+{% if "# <!-- collapse=True -->" in cell.input %}
+<div class="collapseheader box-flex1"><span style="font-weight: bold;">Expand Code</span>
+<div class="input_area box-flex1" style="display:none">
+{{ cell.input.replace("# <!-- collapse=True -->\n", "") | highlight2html(metadata=cell.metadata) }}
+</div>
+</div>
+{% elif "# <!-- collapse=False -->" in cell.input %}
+<div class="collapseheader box-flex1"><span style="font-weight: bold;">Collapse Code</span>
+<div class="input_area box-flex1">
+{{ cell.input.replace("# <!-- collapse=False -->\n", "") | highlight2html(metadata=cell.metadata) }}
+</div>
+</div>
+{% else %}
+<div class="input_area box-flex1">
+{{ cell.input | highlight2html(metadata=cell.metadata) }}
+</div>
+{% endif %}
+{%- endblock input %}
+
diff --git a/pelican-plugins/liquid_tags/pelicanhtml_2.tpl b/pelican-plugins/liquid_tags/pelicanhtml_2.tpl
new file mode 100644
index 0000000000000000000000000000000000000000..937c38349fb7be6cc80120427d8c483968202b66
--- /dev/null
+++ b/pelican-plugins/liquid_tags/pelicanhtml_2.tpl
@@ -0,0 +1,44 @@
+{%- extends 'basic.tpl' -%}
+
+{% block stream_stdout -%}
+<div class="box-flex1 output_subarea output_stream output_stdout">
+<pre class="ipynb">{{output.text |ansi2html}}</pre>
+</div>
+{%- endblock stream_stdout %}
+
+{% block stream_stderr -%}
+<div class="box-flex1 output_subarea output_stream output_stderr">
+<pre class="ipynb">{{output.text |ansi2html}}</pre>
+</div>
+{%- endblock stream_stderr %}
+
+{% block pyerr -%}
+<div class="box-flex1 output_subarea output_pyerr">
+<pre class="ipynb">{{super()}}</pre>
+</div>
+{%- endblock pyerr %}
+
+{%- block data_text %}
+<pre class="ipynb">{{output.text | ansi2html}}</pre>
+{%- endblock -%}
+
+{% block input %}
+{% if "# <!-- collapse=True -->" in cell.input %}
+<div class="collapseheader box-flex1"><span style="font-weight: bold;">Expand Code</span>
+<div class="input_area box-flex1" style="display:none">
+{{ cell.input.replace("# <!-- collapse=True -->\n", "") | highlight2html(metadata=cell.metadata) }}
+</div>
+</div>
+{% elif "# <!-- collapse=False -->" in cell.input %}
+<div class="collapseheader box-flex1"><span style="font-weight: bold;">Collapse Code</span>
+<div class="input_area box-flex1">
+{{ cell.input.replace("# <!-- collapse=False -->\n", "") | highlight2html(metadata=cell.metadata) }}
+</div>
+</div>
+{% else %}
+<div class="input_area box-flex1">
+{{ cell.input | highlight2html(metadata=cell.metadata) }}
+</div>
+{% endif %}
+{%- endblock input %}
+
diff --git a/pelican-plugins/liquid_tags/pelicanhtml_3.tpl b/pelican-plugins/liquid_tags/pelicanhtml_3.tpl
new file mode 100644
index 0000000000000000000000000000000000000000..277aeb05f1cd0292f9731b58bbfb2709b022ba6f
--- /dev/null
+++ b/pelican-plugins/liquid_tags/pelicanhtml_3.tpl
@@ -0,0 +1,55 @@
+{%- extends 'basic.tpl' -%}
+
+{% block stream_stdout -%}
+<div class="output_subarea output_stream output_stdout output_text">
+<pre class="ipynb">
+{{- output.text | ansi2html -}}
+</pre>
+</div>
+{%- endblock stream_stdout %}
+
+{% block stream_stderr -%}
+<div class="output_subarea output_stream output_stderr output_text">
+<pre class="ipynb">
+{{- output.text | ansi2html -}}
+</pre>
+</div>
+{%- endblock stream_stderr %}
+
+{% block error -%}
+<div class="output_subarea output_text output_error">
+<pre class="ipynb">
+{{- super() -}}
+</pre>
+</div>
+{%- endblock error %}
+
+{%- block data_text scoped %}
+<div class="output_text output_subarea {{extra_class}}">
+<pre class="ipynb">
+{{- output.data['text/plain'] | ansi2html -}}
+</pre>
+</div>
+{%- endblock -%}
+
+{% block input %}
+{% if "# <!-- collapse=True -->" in cell.source %}
+<div class="collapseheader inner_cell"><span style="font-weight: bold;">Expand Code</span>
+<div class="input_area" style="display:none">
+{{ cell.source.replace("# <!-- collapse=True -->\n", "") | highlight_code(metadata=cell.metadata) }}
+</div>
+</div>
+{% elif "# <!-- collapse=False -->" in cell.source %}
+<div class="collapseheader inner_cell"><span style="font-weight: bold;">Collapse Code</span>
+<div class="input_area">
+{{ cell.source.replace("# <!-- collapse=False -->\n", "") | highlight_code(metadata=cell.metadata) }}
+</div>
+</div>
+{% else %}
+<div class="inner_cell">
+    <div class="input_area">
+        {{ cell.source | highlight_code(metadata=cell.metadata) }}
+    </div>
+</div>
+{% endif %}
+{%- endblock input %}
diff --git a/pelican-plugins/liquid_tags/pygalcharts.py b/pelican-plugins/liquid_tags/pygalcharts.py
new file mode 100644
index 0000000000000000000000000000000000000000..1c31325282d4dfe6f0247bcb440be73457176259
--- /dev/null
+++ b/pelican-plugins/liquid_tags/pygalcharts.py
@@ -0,0 +1,164 @@
+"""
+pygal Tag
+---------
+This implements a Liquid-style pygal tag for Pelican. JSON is used for the data,
+and you can pass a bunch of pygal's 'config' items through as-is
+
+[1] http://www.pygal.org/
+
+Syntax
+------
+{% pygal
+    {
+        <graph data>
+    }
+%}
+
+Examples
+--------
+{%
+	pygal {
+		"type": "bar",
+		"title": "Test Chart",
+		"x-labels" : {"from": 2002, "to": 2013},
+		"data" : [
+		{"title": "Firefox",
+		 "values": [null, null, 0, 16.6,   25,   31, 36.4, 45.5, 46.3, 42.8, 37.1]},
+		 {"title": "Chrome",
+		 "values": [null, null, null, null, null, null,    0,  3.9, 10.8, 23.8, 35.3]},
+		 {"title": "IE",
+		 "values": [85.8, 84.6, 84.7, 74.5,   66, 58.6, 54.7, 44.8, 36.2, 26.6, 20.1]},
+		 {"title": "Others",
+		 "values": [14.2, 15.4, 15.3,  8.9,    9, 10.4,  8.9,  5.8,  6.7,  6.8,  7.5]}
+		]
+	}
+%}
+
+{%
+	pygal {
+		"type": "pie",
+		"half_pie": true,
+		"title": "Browser usage in February 2012 (in %)",
+		"data" : [
+		{"title": "IE",
+		 "values": 19.5},
+		 {"title": "Firefox",
+		 "values": 36.6},
+		 {"title": "Chrome",
+		 "values": 36.3},
+		 {"title": "Safari",
+		 "values": 4.5},
+		 {"title": "Opera",
+		 "values": 2.3}
+		]
+	}
+%}
+
+{%
+	pygal {
+		"type": "pie",
+			"config": {
+			"show_legend": false,
+			"print_values": true,
+			"show_y_labels": true
+		},
+		"title": "Browser usage in February 2012 (in %)",
+		"data" : [
+		{"title": "IE",
+		 "values": 19.5},
+		 {"title": "Firefox",
+		 "values": 36.6},
+		 {"title": "Chrome",
+		 "values": 36.3},
+		 {"title": "Safari",
+		 "values": 4.5},
+		 {"title": "Opera",
+		 "values": 2.3}
+		]
+	}
+%}
+
+
+
+...
+
+
+Output
+------
+<<div class="pygal" style="text-align: center;"><embed type="image/svg+xml" src=SVG_MARKUP_EMBEDDED style="max-width:1000px"/></div>
+
+"""
+
+import base64
+import re
+from json import loads
+from .mdx_liquid_tags import LiquidTags
+
+SYNTAX = '{% pygal (data) %}'
+DOT_BLOCK_RE = re.compile(r'^\s*\{\s*(?P<code>.*\})\s*\}$', re.MULTILINE | re.DOTALL)
+
+
+def run_pygal(data, options=[], format='svg'):
+    """ Runs pygal programs and returns image data
+    """
+    import pygal
+
+    chart_title = data.get('title', None)
+    chart_type = data.get('type', '').lower()
+    # Config options are pretty much proxied straight through from the JSON dict into the object
+    config = pygal.Config()
+    config_dict = data.get('config', {})
+    for key in config_dict.keys():
+        setattr(config, key, config_dict[key])
+
+    if chart_type == 'bar':
+        chart = pygal.HorizontalBar(config) if data.get('horizontal', False) else pygal.Bar(config)
+    elif chart_type == 'line':
+        chart = pygal.Line(config)
+    elif chart_type == 'pie':
+        ir=data.get('inner_radius', 0.0)
+        hp=data.get('half_pie', False)
+        chart = pygal.Pie(config, inner_radius=ir, half_pie=hp)
+    else:
+        print('undefined or unknown chart type')
+
+    if chart is not None:
+        chart.title = data.get('title', None)
+        # Do labels (if present)
+        label_data = data.get('x-labels', None)
+        if isinstance(label_data, list):
+            # use list
+            chart.x_labels = label_data
+        elif isinstance(label_data, dict):
+            # use a range
+            range_from = label_data.get('from', 0)
+            range_to = label_data.get('to', 0)
+            chart.x_labels = map(str, range(range_from, range_to))
+        # insert data
+        for data_set in data.get('data', []):
+            title = data_set.get('title', None)
+            values = data_set.get('values', None)
+            chart.add(title, values)
+        # now render
+        result = chart.render_data_uri()
+    else:
+        result = None
+    return result
+
+@LiquidTags.register('pygal')
+def pygal_parser(preprocessor, tag, markup):
+    """ Simple pygal parser """
+    # Find JSON payload
+    data = loads(markup)
+    if tag == 'pygal' and data is not None:
+        # Run generation of chart
+        output = run_pygal(data)
+        # Return embedded SVG image
+        return '<div class="pygal" style="text-align: center;"><embed type="image/svg+xml" src=%s style="max-width:1000px"/></div>' % output
+
+    else:
+        raise ValueError('Error processing input. \nExpected syntax: {0}'.format(SYNTAX))
+
+#----------------------------------------------------------------------
+# This import allows image tag to be a Pelican plugin
+from .liquid_tags import register
diff --git a/pelican-plugins/liquid_tags/requirements.txt b/pelican-plugins/liquid_tags/requirements.txt
new file mode 100755
index 0000000000000000000000000000000000000000..55b033e901cdda93a26ac64b418f260224260a39
--- /dev/null
+++ b/pelican-plugins/liquid_tags/requirements.txt
@@ -0,0 +1 @@
+pytest
\ No newline at end of file
diff --git a/pelican-plugins/liquid_tags/soundcloud.py b/pelican-plugins/liquid_tags/soundcloud.py
new file mode 100644
index 0000000000000000000000000000000000000000..3f089958169c2753e1e5d65d803605b9de04ba61
--- /dev/null
+++ b/pelican-plugins/liquid_tags/soundcloud.py
@@ -0,0 +1,70 @@
+"""
+Soundcloud Tag
+--------------
+This implements a Liquid-style soundcloud tag for Pelican.
+
+It asks the official Soundcloud-API for the widget html code.
+
+Syntax
+------
+`{% soundcloud track_url %}`
+
+Example
+-------
+`{% soundcloud https://soundcloud.com/luftmentsh/hakotel %}`
+
+Output
+------
+```
+<iframe width="100%"
+         height="400"
+         scrolling="no"
+         frameborder="no"
+         src="https://w.soundcloud.com/player/?visual=true&url=http%3A%2F%2F\
+             api.soundcloud.com%2Ftracks%2F33875102&show_artwork=true">
+</iframe>
+```
+"""
+import json
+import re
+
+# ---------------------------------------------------
+# This import allows image tag to be a Pelican plugin
+from liquid_tags import register
+
+from .mdx_liquid_tags import LiquidTags
+
+try:
+    from urllib.request import urlopen
+except ImportError:
+    from urllib import urlopen
+
+
+SYNTAX = "{% soundcloud track_url %}"
+PARSE_SYNTAX = re.compile(r"(?P<track_url>https?://soundcloud.com/[\S]+)")
+SOUNDCLOUD_API_URL = "https://soundcloud.com/oembed"
+
+
+def get_widget(track_url):
+    r = urlopen(
+        SOUNDCLOUD_API_URL, data="format=json&url={}".format(track_url).encode("utf-8")
+    )
+
+    return json.loads(r.read().decode("utf-8"))["html"]
+
+
+def match_it(markup):
+    match = PARSE_SYNTAX.search(markup)
+    if match:
+        return match.groupdict()
+    else:
+        raise ValueError(
+            "Error processing input. " "Expected syntax: {}".format(SYNTAX)
+        )
+
+
+@LiquidTags.register("soundcloud")
+def soundcloud(preprocessor, tag, markup):
+    track_url = match_it(markup)["track_url"]
+
+    return get_widget(track_url)
diff --git a/pelican-plugins/liquid_tags/speakerdeck.py b/pelican-plugins/liquid_tags/speakerdeck.py
new file mode 100644
index 0000000000000000000000000000000000000000..54a13e6dccd4ee82e20154f9fbad4ee97b063d03
--- /dev/null
+++ b/pelican-plugins/liquid_tags/speakerdeck.py
@@ -0,0 +1,56 @@
+"""
+Speakerdeck Tag
+---------------
+This implements a Liquid-style speakerdeck tag for Pelican.
+
+Syntax
+------
+{% speakerdeck id %}
+
+Example
+-------
+{% speakerdeck 82b209c0f181013106da6eb14261a8ef %}
+
+Output
+------
+<script
+    async
+    class="speakerdeck-embed"
+    data-id="82b209c0f181013106da6eb14261a8ef"
+    data-ratio="1.33333333333333"
+    src="//speakerdeck.com/assets/embed.js">
+</script>
+"""
+import re
+
+from .mdx_liquid_tags import LiquidTags
+
+SYNTAX = "{% speakerdeck id [ratio] %}"
+
+REGEX = re.compile(r'([\S]+)(\s+(\d*\.?\d*))?')
+
+
+@LiquidTags.register('speakerdeck')
+def speakerdeck(preprocessor, tag, markup):
+    ratio = 1.33333333333333
+    id = None
+    match = REGEX.search(markup)
+    if match:
+        groups = match.groups()
+        id = groups[0]
+        ratio = groups[2] or ratio
+    if id:
+        speakerdeck_out = """
+<script async class="speakerdeck-embed" data-id="{id}"
+data-ratio="{ratio}" src="//speakerdeck.com/assets/embed.js"></script>
+        """.format(id=id, ratio=ratio)
+    else:
+        raise ValueError(
+            "Error processing input, expected syntax: {0}".format(SYNTAX)
+        )
+    return speakerdeck_out
+
+
+# ---------------------------------------------------
+# This import allows speakerdeck tag to be a Pelican plugin
+from liquid_tags import register  # noqa # isort:skip
diff --git a/pelican-plugins/liquid_tags/spotify.py b/pelican-plugins/liquid_tags/spotify.py
new file mode 100644
index 0000000000000000000000000000000000000000..f3eb8752f6f7676adb454ce200d3af14b5057d6b
--- /dev/null
+++ b/pelican-plugins/liquid_tags/spotify.py
@@ -0,0 +1,55 @@
+"""
+Spotify Tag
+---------
+This implements a Liquid-style spotify tag for Pelican,
+based on the jekyll / octopress youtube tag [1]_
+
+Syntax
+------
+{% spotify id %}
+
+Example
+-------
+{% spotify 1HNZcRFlIKwHAJD3LxvX4d %}
+
+Output
+------
+<iframe
+    src='https://embed.spotify.com/?uri=spotify:track:1HNZcRFlIKwHAJD3LxvX4d'
+    width='300' height='380' frameborder='0' allowtransparency='true'>
+</iframe>
+"""
+import re
+from .mdx_liquid_tags import LiquidTags
+
+SYNTAX = "{% spotify id %}"
+
+SPOTIFY = re.compile(r'(\w+)(\s+(\d+)\s(\d+))?')
+
+
+@LiquidTags.register('spotify')
+def spotify(preprocessor, tag, markup):
+    spotify_id = None
+
+    match = SPOTIFY.search(markup)
+    if match:
+        groups = match.groups()
+        spotify_id = groups[0]
+
+    if spotify_id:
+        spotify_out = """
+        <iframe src='https://embed.spotify.com/?uri=spotify:track:{}'
+          width='300'
+          height='380'
+          frameborder='0'
+          allowtransparency='true'></iframe>""".format(spotify_id).strip()
+    else:
+        raise ValueError("Error processing input, "
+                         "expected syntax: {0}".format(SYNTAX))
+
+    return spotify_out
+
+
+# ---------------------------------------------------
+# This import allows image tag to be a Pelican plugin
+from liquid_tags import register  # noqa
diff --git a/pelican-plugins/liquid_tags/test_audio.py b/pelican-plugins/liquid_tags/test_audio.py
new file mode 100644
index 0000000000000000000000000000000000000000..4e1fa921aa70104c5763b59afa93ed8b1c34efc7
--- /dev/null
+++ b/pelican-plugins/liquid_tags/test_audio.py
@@ -0,0 +1,44 @@
+import re
+import sys
+import unittest
+import pytest
+from . import audio
+
+
+if 'nosetests' in sys.argv[0]:
+    raise unittest.SkipTest('Those tests are pytest-compatible only')
+
+
+@pytest.mark.parametrize('input,expected', [
+    ('http://foo.bar https://bar.foo',
+     ('http://foo.bar', 'https://bar.foo', None)),
+    ('http://test.foo',
+     ('http://test.foo', None, None)),
+    ('https://test.foo',
+     ('https://test.foo', None, None)),
+    ('http://foo.foo https://bar.bar http://zonk.zonk',
+     ('http://foo.foo', 'https://bar.bar', 'http://zonk.zonk'))
+])
+def test_regex(input, expected):
+    assert re.match(audio.AUDIO, input).groups() == expected
+
+
+@pytest.mark.parametrize('input,expected', [
+    ('http://foo.foo/foo.mp3',
+     ('<audio controls>'
+      '<source src="http://foo.foo/foo.mp3" type="audio/mpeg">'
+      'Your browser does not support the audio element.</audio>')),
+    ('https://foo.foo/foo.ogg http://bar.bar/bar.opus',
+     ('<audio controls>'
+      '<source src="https://foo.foo/foo.ogg" type="audio/ogg">'
+      '<source src="http://bar.bar/bar.opus" type="audio/ogg">'
+      'Your browser does not support the audio element.</audio>')),
+    ('http://1.de/1.wav http://2.de/2.mp4 http://3.de/3.ogg',
+     ('<audio controls>'
+      '<source src="http://1.de/1.wav" type="audio/wav">'
+      '<source src="http://2.de/2.mp4" type="audio/mp4">'
+      '<source src="http://3.de/3.ogg" type="audio/ogg">'
+      'Your browser does not support the audio element.</audio>'))
+])
+def test_create_html(input, expected):
+    assert audio.create_html(input) == expected
diff --git a/pelican-plugins/liquid_tags/test_data/__init__.py b/pelican-plugins/liquid_tags/test_data/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/pelican-plugins/liquid_tags/test_data/content/notebooks/test_nbformat3.ipynb b/pelican-plugins/liquid_tags/test_data/content/notebooks/test_nbformat3.ipynb
new file mode 100644
index 0000000000000000000000000000000000000000..4327ae5b8c18a32c5edea3afbdfe284fbbf4a6f9
--- /dev/null
+++ b/pelican-plugins/liquid_tags/test_data/content/notebooks/test_nbformat3.ipynb
@@ -0,0 +1,810 @@
+{
+ "metadata": {
+  "name": "",
+  "signature": "sha256:1630a227c54e735b7c11051ce8769271ee46f9bca6c1916bb26cfdba7c01b546"
+ },
+ "nbformat": 3,
+ "nbformat_minor": 0,
+ "worksheets": [
+  {
+   "cells": [
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "Il s\u2019agit de rep\u00e9rer les donn\u00e9es n\u00e9cessaires voire indispensables \u00e0 la\n",
+      "r\u00e9solution. Ces donn\u00e9es peuvent \u00eatre:\n",
+      "\n",
+      "-   num\u00e9riques,\n",
+      "-   ou sous forme de textes (on dit souvent cha\u00eenes de caract\u00e8res),\n",
+      "-   ou de type logique (\u00e0 deux valeurs possibles, vrai ou faux).\n",
+      "\n",
+      "Pour affecter une valeur \u00e0 une variable en python, la syntaxe est de la forme:\n",
+      "\n",
+      "```\n",
+      "nom = valeur\n",
+      "```\n",
+      "\n",
+      "<dl>\n",
+      "<dt>Affectation</dt>\n",
+      "<dd>Dans un programme, une affectation donne une valeur \u00e0 une variable, elle est de la forme <code>v = e</code> avec `v` une variable et `e` une expression.\n",
+      "</dd>\n",
+      "</dl>\n",
+      "\n",
+      "Valeurs num\u00e9riques\n",
+      "==================\n",
+      "\n",
+      "Python distingue les entiers(integers), des nombres \u00e0\n",
+      "virgule(floating-point) par l'ajout d'une virgule:\n",
+      "\n"
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "a = 1\n",
+      "b = 1.0\n",
+      "\n",
+      "print(a, type(a), b, type(b))"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "1 <class 'int'> 1.0 <class 'float'>\n"
+       ]
+      }
+     ],
+     "prompt_number": 2
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "Mais il impl\u00e9mente aussi des nombres plus exotiques tels que les\n",
+      "d\u00e9cimaux, les fractions ou encore les nombres complexes\n",
+      "\n",
+      "Cha\u00eenes de caract\u00e8res\n",
+      "=====================\n",
+      "\n",
+      "En python3, il existe deux grands types de cha\u00eenes de caract\u00e8res, les\n",
+      "strings, et les bytes. Les strings sont simplement entour\u00e9es par des\n",
+      "guillemets simples ou doubles.\n",
+      "\n"
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "a = 'Mon texte'\n",
+      "b = \"Mon deuxi\u00e8me texte\"\n",
+      "\n",
+      "print(a, type(a), b, type(b))"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "Mon texte <class 'str'> Mon deuxi\u00e8me texte <class 'str'>\n"
+       ]
+      }
+     ],
+     "prompt_number": 3
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "Les bytes sont pr\u00e9c\u00e9d\u00e9es de la lettre b, il s'agit de texte brut utilis\u00e9\n",
+      "par la machine mais pas toujours lisible par un humain.\n",
+      "\n"
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "a = b'Mon texte'\n",
+      "print(a, type(a))\n"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "b'Mon texte' <class 'bytes'>\n"
+       ]
+      }
+     ],
+     "prompt_number": 4
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "On ne peut repr\u00e9senter simplement que certains caract\u00e8res(les caract\u00e8res\n",
+      "ASCII), les caract\u00e8res accentu\u00e9s ne peuvent \u00eatre \u00e9crits directement.\n",
+      "\n"
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "b = b'Mon deuxi\u00e8me texte'"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "ename": "SyntaxError",
+       "evalue": "bytes can only contain ASCII literal characters. (<ipython-input-5-b0bf0ef2715d>, line 1)",
+       "output_type": "pyerr",
+       "traceback": [
+        "\u001b[1;36m  File \u001b[1;32m\"<ipython-input-5-b0bf0ef2715d>\"\u001b[1;36m, line \u001b[1;32m1\u001b[0m\n\u001b[1;33m    b = b'Mon deuxi\u00e8me texte'\u001b[0m\n\u001b[1;37m       ^\u001b[0m\n\u001b[1;31mSyntaxError\u001b[0m\u001b[1;31m:\u001b[0m bytes can only contain ASCII literal characters.\n"
+       ]
+      }
+     ],
+     "prompt_number": 5
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "Un encodage autre que le ASCII doit \u00eatre utilis\u00e9 pour ces caract\u00e8res,\n",
+      "comme par exemple le UTF-8 qui est un codage sur huit bits tr\u00e8s r\u00e9pandu.\n"
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "b = b'Mon deuxi\\xc3\\xa8me texte'\n",
+      "print(b)\n",
+      "print(b.decode('utf-8'))"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "b'Mon deuxi\\xc3\\xa8me texte'\n",
+        "Mon deuxi\u00e8me texte\n"
+       ]
+      }
+     ],
+     "prompt_number": 6
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "Valeurs logiques\n",
+      "================\n",
+      "\n",
+      "On les appelle les bool\u00e9ens, il ne peuvent avoir que deux valeurs:\n",
+      "\n",
+      "-   VRAI ou FAUX: True ou False en anglais;\n",
+      "-   1 ou 0.\n",
+      "\n",
+      "On les utilise pour r\u00e9aliser des tests.\n"
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "1 < 2, 1 > 2, 2 == 10, 2 >= 1.9, 2 == 2, 2 != 2"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 7,
+       "text": [
+        "(True, False, False, True, True, False)"
+       ]
+      }
+     ],
+     "prompt_number": 7
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "On peut aussi les utiliser pour savoir si une variable existe.\n",
+      "\n"
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "bool(a)"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 8,
+       "text": [
+        "True"
+       ]
+      }
+     ],
+     "prompt_number": 8
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "On peut aussi combiner plusieurs tests avec les op\u00e9rateurs bool\u00e9ens `and`, `or` et `not`."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "1 < 2 or 2 < 1"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 9,
+       "text": [
+        "True"
+       ]
+      }
+     ],
+     "prompt_number": 9
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "1 < 2 and 2 < 1"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 10,
+       "text": [
+        "False"
+       ]
+      }
+     ],
+     "prompt_number": 10
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "not(1 < 2 and 2 < 1)"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 11,
+       "text": [
+        "True"
+       ]
+      }
+     ],
+     "prompt_number": 11
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "#Listes et dictionnaires\n",
+      "\n",
+      "Souvent les donn\u00e9es pertinentes doivent \u00eatre agenc\u00e9es sous une forme\n",
+      "plus vaste, comme par exemple des listes o\u00f9 on peut ranger de fa\u00e7on ordonn\u00e9e *plusieurs valeurs*.\n",
+      "\n",
+      "##Listes\n",
+      "\n",
+      "\n",
+      "Les listes sont des collections *ordonn\u00e9es de valeurs*, elles sont\n",
+      "entour\u00e9es par des crochets `[]`, leurs \u00e9l\u00e9ments sont s\u00e9par\u00e9s par des\n",
+      "virgules.\n",
+      "\n"
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "a = [ 1, 'deux' , 3]\n",
+      "type(a)"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 12,
+       "text": [
+        "list"
+       ]
+      }
+     ],
+     "prompt_number": 12
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "On peut facilement acc\u00e9der \u00e0 la longueur de la liste grace \u00e0 la fonction\n",
+      "`len` et \u00e0 chacun de ces \u00e9l\u00e9ments gr\u00e2ce \u00e0 leur index *(Attention le\n",
+      "premier \u00e9lement \u00e0 l'index 0)*\n"
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "len(a)\n"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 13,
+       "text": [
+        "3"
+       ]
+      }
+     ],
+     "prompt_number": 13
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "a[0], a[2]"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 14,
+       "text": [
+        "(1, 3)"
+       ]
+      }
+     ],
+     "prompt_number": 14
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "On peut inversement conna\u00eetre l'indice correspondant \u00e0 une valeur gr\u00e2ce \u00e0 l'attribut `index`."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "a.index(1), a.index('deux'), a.index(3)"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 15,
+       "text": [
+        "(0, 1, 2)"
+       ]
+      }
+     ],
+     "prompt_number": 15
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "##Dictionnaires\n",
+      "\n",
+      "Dans un dictionnaire les valeurs de la collection ne sont pas rep\u00e9r\u00e9 par\n",
+      "un index, mais par une *cl\u00e9*. Les dictionnaires sont entour\u00e9s d'accolades `{}`.\n"
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "a = { 'nom': 'Doe' , 'prenom': 'John', 'age': 77 }\n",
+      "type(a)"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 16,
+       "text": [
+        "dict"
+       ]
+      }
+     ],
+     "prompt_number": 16
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "Pour acc\u00e9der aux \u00e9l\u00e9ments du dictionnaire, il suffit d'appeler la cl\u00e9\n",
+      "correspondante, d'autres part la fonction `len` est \u00e9galemnt disponible.\n"
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "len(a)"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 17,
+       "text": [
+        "3"
+       ]
+      }
+     ],
+     "prompt_number": 17
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "a['nom'], a['age']"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 18,
+       "text": [
+        "('Doe', 77)"
+       ]
+      }
+     ],
+     "prompt_number": 18
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "##Modification des listes et dictionnaires\n",
+      "\n",
+      "Les listes et dictionnaires sont des objets **mutables**, c'est \u00e0 dire que l'on peut modifier leur contenu sans cr\u00e9er un nouvel objet. On dit qu'il s'agit de donn\u00e9es [non-persistantes](http://fr.wikipedia.org/wiki/Persistance_%28informatique%29).\n",
+      "\n"
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "# Valeurs initiales\n",
+      "liste = [ 1, 'deux' , 3]\n",
+      "dict = { 'nom': 'Doe' , 'prenom': 'John', 'age': 77 }\n",
+      "print(\"Valeurs initiales:\\n\", liste, dict)\n",
+      "\n",
+      "# Modification des valeurs par assignation\n",
+      "liste[1] = 2\n",
+      "dict['age'] = 17\n",
+      "\n",
+      "print(\"Modification des valeurs par assignation:\\n\", liste, dict)\n",
+      "\n",
+      "# Ajout d'\u00e9l\u00e9ments\n",
+      "liste.append(4)\n",
+      "dict['nationalit\u00e9'] = 'fran\u00e7aise'\n",
+      "\n",
+      "print(\"Ajout d'\u00e9l\u00e9ments:\\n\", liste, dict)\n",
+      "\n",
+      "# Suppression d'\u00e9l\u00e9ments\n",
+      "liste.pop(0)\n",
+      "dict.pop('age')\n",
+      "\n",
+      "print(\"Suppression d'\u00e9l\u00e9ments:\\n\", liste, dict)"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "Valeurs initiales:\n",
+        " [1, 'deux', 3] {'age': 77, 'nom': 'Doe', 'prenom': 'John'}\n",
+        "Modification des valeurs par assignation:\n",
+        " [1, 2, 3] {'age': 17, 'nom': 'Doe', 'prenom': 'John'}\n",
+        "Ajout d'\u00e9l\u00e9ments:\n",
+        " [1, 2, 3, 4] {'age': 17, 'nom': 'Doe', 'nationalit\u00e9': 'fran\u00e7aise', 'prenom': 'John'}\n",
+        "Suppression d'\u00e9l\u00e9ments:\n",
+        " [2, 3, 4] {'nom': 'Doe', 'nationalit\u00e9': 'fran\u00e7aise', 'prenom': 'John'}\n"
+       ]
+      }
+     ],
+     "prompt_number": 19
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "Si on a besoin de modifier une liste ou un dictionnaire, mais que l'on veut garder une trace des objets initiaux, il faut commencer par en cr\u00e9er une **copie**, il ne suffit pas de cr\u00e9er une variable supl\u00e9mentaire sans quoi cette variable serait elle aussi modifi\u00e9e si l'objet initial changeait: l'assignation est dite par **r\u00e9f\u00e9rence** dans ce cas."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "# Valeurs initiales\n",
+      "L = [ 1, 'deux' , 3]\n",
+      "print(\"Valeurs initiales:\\n\", L)\n",
+      "\n",
+      "# Cr\u00e9ation d'une r\u00e9f\u00e9rencxe \u00e0 la liste par simple assignation\n",
+      "L_ref = L\n",
+      "\n",
+      "# Cr\u00e9ation d'une copie de la liste\n",
+      "L_copie = list(L)\n",
+      "\n",
+      "# Modification de la liste initiale\n",
+      "L[1] = 2\n",
+      "\n",
+      "print(\"Modification de la liste L:\")\n",
+      "print(\"La liste L a bien, \u00e9t\u00e9 modifi\u00e9e:\", L)\n",
+      "print(\"La liste L_ref a aussi \u00e9t\u00e9 modifi\u00e9e car il s'agit juste d'une r\u00e9f\u00e9rence vers la liste L:\", L_ref)\n",
+      "print(\"La copie L_copie n'a pas \u00e9t\u00e9 modifi\u00e9e:\", L_copie)\n",
+      "\n"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "Valeurs initiales:\n",
+        " [1, 'deux', 3]\n",
+        "Modification de la liste L:\n",
+        "La liste L a bien, \u00e9t\u00e9 modifi\u00e9e: [1, 2, 3]\n",
+        "La liste L_ref a aussi \u00e9t\u00e9 modifi\u00e9e car il s'agit juste d'une r\u00e9f\u00e9rence vers la liste L: [1, 2, 3]\n",
+        "La copie L_copie n'a pas \u00e9t\u00e9 modifi\u00e9e: [1, 'deux', 3]\n"
+       ]
+      }
+     ],
+     "prompt_number": 20
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "Entr\u00e9e des donn\u00e9es\n",
+      "==================\n",
+      "\n",
+      "Dans cette phase peut aussi figurer ce qu\u2019on appelle l\u2019entr\u00e9e des\n",
+      "donn\u00e9es, qui peut se manifester par la saisie de caract\u00e8res ou de\n",
+      "nombres sur le clavier, ou la lecture de la position du pointeur de la\n",
+      "souris, ou encore par la lecture d\u2019un fichier contenant ces nombres ou\n",
+      "caract\u00e8res.\n",
+      "\n",
+      "Il s\u2019agit aussi de rep\u00e9rer les r\u00e9sultats interm\u00e9diaires qu\u2019il est bon de\n",
+      "m\u00e9moriser pour la suite car indispensables au traitement.\n",
+      "\n",
+      "Il est parfois utile d\u2019utiliser des variables auxiliaires pour ne pas\n",
+      "perturber les donn\u00e9es initiales.\n",
+      "\n",
+      "Entr\u00e9e des donn\u00e9es au clavier\n",
+      "-----------------------------\n"
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "chiffre = input(\"Entrer la valeur du chiffre souhait\u00e9: \")\n"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "name": "stdout",
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "Entrer la valeur du chiffre souhait\u00e9: 7\n"
+       ]
+      }
+     ],
+     "prompt_number": 21
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "print(chiffre)\n"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "7\n"
+       ]
+      }
+     ],
+     "prompt_number": 22
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "Attention cependant, cette valeur est une cha\u00eene de caract\u00e8re, et les op\u00e9rations sont celles des `string`s."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "print(type(chiffre))\n",
+      "chiffre*10"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "<class 'str'>\n"
+       ]
+      },
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 23,
+       "text": [
+        "'7777777777'"
+       ]
+      }
+     ],
+     "prompt_number": 23
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "Il convient de la convertir un nombre entier ou flottant avant d'utiliser la valeur."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "chiffre = int(chiffre)\n",
+      "10*chiffre"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 24,
+       "text": [
+        "70"
+       ]
+      }
+     ],
+     "prompt_number": 24
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "Lecture d'un fichier\n",
+      "--------------------\n",
+      "\n",
+      "Voici par exemple comment ouvrir et lire un fichier appel\u00e9 `lorem.txt`\n",
+      "contenu dans le r\u00e9p\u00e9rtoire `data` du dossier courant.\n",
+      "\n"
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "fichier = open('data/lorem.txt')\n",
+      "fichier.read()"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 27,
+       "text": [
+        "'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod\\ntempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At\\nvero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren,\\nno sea takimata sanctus est Lorem ipsum dolor sit amet.\\n'"
+       ]
+      }
+     ],
+     "prompt_number": 27
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "Dans la sortie pr\u00e9c\u00e9dente, les caract\u00e8res `\\n` repr\u00e9sentent les sauts de\n",
+      "ligne, on peut aussi lire le fichier ligne par ligne."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "fichier = open('data/lorem.txt')\n",
+      "fichier.readline()"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 29,
+       "text": [
+        "'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod\\n'"
+       ]
+      }
+     ],
+     "prompt_number": 29
+    }
+   ],
+   "metadata": {}
+  }
+ ]
+}
diff --git a/pelican-plugins/liquid_tags/test_data/content/notebooks/test_nbformat4.ipynb b/pelican-plugins/liquid_tags/test_data/content/notebooks/test_nbformat4.ipynb
new file mode 100644
index 0000000000000000000000000000000000000000..e8cf0bfb6e70245ace6ba814c3f7a9a77b03a06c
--- /dev/null
+++ b/pelican-plugins/liquid_tags/test_data/content/notebooks/test_nbformat4.ipynb
@@ -0,0 +1,853 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Il s’agit de repérer les données nécessaires voire indispensables à la\n",
+    "résolution. Ces données peuvent être:\n",
+    "\n",
+    "-   numériques,\n",
+    "-   ou sous forme de textes (on dit souvent chaînes de caractères),\n",
+    "-   ou de type logique (à deux valeurs possibles, vrai ou faux).\n",
+    "\n",
+    "Pour affecter une valeur à une variable en python, la syntaxe est de la forme:\n",
+    "\n",
+    "```\n",
+    "nom = valeur\n",
+    "```\n",
+    "\n",
+    "<dl>\n",
+    "<dt>Affectation</dt>\n",
+    "<dd>Dans un programme, une affectation donne une valeur à une variable, elle est de la forme <code>v = e</code> avec `v` une variable et `e` une expression.\n",
+    "</dd>\n",
+    "</dl>\n",
+    "\n",
+    "Valeurs numériques\n",
+    "==================\n",
+    "\n",
+    "Python distingue les entiers(integers), des nombres à\n",
+    "virgule(floating-point) par l'ajout d'une virgule:\n",
+    "\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "1 <class 'int'> 1.0 <class 'float'>\n"
+     ]
+    }
+   ],
+   "source": [
+    "# <!-- collapse=True -->\n",
+    "a = 1\n",
+    "b = 1.0\n",
+    "\n",
+    "print(a, type(a), b, type(b))"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Mais il implémente aussi des nombres plus exotiques tels que les\n",
+    "décimaux, les fractions ou encore les nombres complexes\n",
+    "\n",
+    "Chaînes de caractères\n",
+    "=====================\n",
+    "\n",
+    "En python3, il existe deux grands types de chaînes de caractères, les\n",
+    "strings, et les bytes. Les strings sont simplement entourées par des\n",
+    "guillemets simples ou doubles.\n",
+    "\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 3,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Mon texte <class 'str'> Mon deuxième texte <class 'str'>\n"
+     ]
+    }
+   ],
+   "source": [
+    "# <!-- collapse=False -->\n",
+    "a = 'Mon texte'\n",
+    "b = \"Mon deuxième texte\"\n",
+    "\n",
+    "print(a, type(a), b, type(b))"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Les bytes sont précédées de la lettre b, il s'agit de texte brut utilisé\n",
+    "par la machine mais pas toujours lisible par un humain.\n",
+    "\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 4,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "b'Mon texte' <class 'bytes'>\n"
+     ]
+    }
+   ],
+   "source": [
+    "a = b'Mon texte'\n",
+    "print(a, type(a))\n"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "On ne peut représenter simplement que certains caractères(les caractères\n",
+    "ASCII), les caractères accentués ne peuvent être écrits directement.\n",
+    "\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 5,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
+    {
+     "ename": "SyntaxError",
+     "evalue": "bytes can only contain ASCII literal characters. (<ipython-input-5-b0bf0ef2715d>, line 1)",
+     "output_type": "error",
+     "traceback": [
+      "\u001b[1;36m  File \u001b[1;32m\"<ipython-input-5-b0bf0ef2715d>\"\u001b[1;36m, line \u001b[1;32m1\u001b[0m\n\u001b[1;33m    b = b'Mon deuxième texte'\u001b[0m\n\u001b[1;37m       ^\u001b[0m\n\u001b[1;31mSyntaxError\u001b[0m\u001b[1;31m:\u001b[0m bytes can only contain ASCII literal characters.\n"
+     ]
+    }
+   ],
+   "source": [
+    "b = b'Mon deuxième texte'"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Un encodage autre que le ASCII doit être utilisé pour ces caractères,\n",
+    "comme par exemple le UTF-8 qui est un codage sur huit bits très répandu.\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 6,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "b'Mon deuxi\\xc3\\xa8me texte'\n",
+      "Mon deuxième texte\n"
+     ]
+    }
+   ],
+   "source": [
+    "b = b'Mon deuxi\\xc3\\xa8me texte'\n",
+    "print(b)\n",
+    "print(b.decode('utf-8'))"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Valeurs logiques\n",
+    "================\n",
+    "\n",
+    "On les appelle les booléens, il ne peuvent avoir que deux valeurs:\n",
+    "\n",
+    "-   VRAI ou FAUX: True ou False en anglais;\n",
+    "-   1 ou 0.\n",
+    "\n",
+    "On les utilise pour réaliser des tests.\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 7,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "(True, False, False, True, True, False)"
+      ]
+     },
+     "execution_count": 7,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "1 < 2, 1 > 2, 2 == 10, 2 >= 1.9, 2 == 2, 2 != 2"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "On peut aussi les utiliser pour savoir si une variable existe.\n",
+    "\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 8,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "True"
+      ]
+     },
+     "execution_count": 8,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "bool(a)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "On peut aussi combiner plusieurs tests avec les opérateurs booléens `and`, `or` et `not`."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 9,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "True"
+      ]
+     },
+     "execution_count": 9,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "1 < 2 or 2 < 1"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 10,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "False"
+      ]
+     },
+     "execution_count": 10,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "1 < 2 and 2 < 1"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 11,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "True"
+      ]
+     },
+     "execution_count": 11,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "not(1 < 2 and 2 < 1)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#Listes et dictionnaires\n",
+    "\n",
+    "Souvent les données pertinentes doivent être agencées sous une forme\n",
+    "plus vaste, comme par exemple des listes où on peut ranger de façon ordonnée *plusieurs valeurs*.\n",
+    "\n",
+    "##Listes\n",
+    "\n",
+    "\n",
+    "Les listes sont des collections *ordonnées de valeurs*, elles sont\n",
+    "entourées par des crochets `[]`, leurs éléments sont séparés par des\n",
+    "virgules.\n",
+    "\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 12,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "list"
+      ]
+     },
+     "execution_count": 12,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "a = [ 1, 'deux' , 3]\n",
+    "type(a)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "On peut facilement accéder à la longueur de la liste grace à la fonction\n",
+    "`len` et à chacun de ces éléments grâce à leur index *(Attention le\n",
+    "premier élement à l'index 0)*\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 13,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "3"
+      ]
+     },
+     "execution_count": 13,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "len(a)\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 14,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "(1, 3)"
+      ]
+     },
+     "execution_count": 14,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "a[0], a[2]"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "On peut inversement connaître l'indice correspondant à une valeur grâce à l'attribut `index`."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 15,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "(0, 1, 2)"
+      ]
+     },
+     "execution_count": 15,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "a.index(1), a.index('deux'), a.index(3)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "##Dictionnaires\n",
+    "\n",
+    "Dans un dictionnaire les valeurs de la collection ne sont pas repéré par\n",
+    "un index, mais par une *clé*. Les dictionnaires sont entourés d'accolades `{}`.\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 16,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "dict"
+      ]
+     },
+     "execution_count": 16,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "a = { 'nom': 'Doe' , 'prenom': 'John', 'age': 77 }\n",
+    "type(a)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Pour accéder aux éléments du dictionnaire, il suffit d'appeler la clé\n",
+    "correspondante, d'autres part la fonction `len` est égalemnt disponible.\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 17,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "3"
+      ]
+     },
+     "execution_count": 17,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "len(a)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 18,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "('Doe', 77)"
+      ]
+     },
+     "execution_count": 18,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "a['nom'], a['age']"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "##Modification des listes et dictionnaires\n",
+    "\n",
+    "Les listes et dictionnaires sont des objets **mutables**, c'est à dire que l'on peut modifier leur contenu sans créer un nouvel objet. On dit qu'il s'agit de données [non-persistantes](http://fr.wikipedia.org/wiki/Persistance_%28informatique%29).\n",
+    "\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 19,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Valeurs initiales:\n",
+      " [1, 'deux', 3] {'age': 77, 'nom': 'Doe', 'prenom': 'John'}\n",
+      "Modification des valeurs par assignation:\n",
+      " [1, 2, 3] {'age': 17, 'nom': 'Doe', 'prenom': 'John'}\n",
+      "Ajout d'éléments:\n",
+      " [1, 2, 3, 4] {'age': 17, 'nom': 'Doe', 'nationalité': 'française', 'prenom': 'John'}\n",
+      "Suppression d'éléments:\n",
+      " [2, 3, 4] {'nom': 'Doe', 'nationalité': 'française', 'prenom': 'John'}\n"
+     ]
+    }
+   ],
+   "source": [
+    "# Valeurs initiales\n",
+    "liste = [ 1, 'deux' , 3]\n",
+    "dict = { 'nom': 'Doe' , 'prenom': 'John', 'age': 77 }\n",
+    "print(\"Valeurs initiales:\\n\", liste, dict)\n",
+    "\n",
+    "# Modification des valeurs par assignation\n",
+    "liste[1] = 2\n",
+    "dict['age'] = 17\n",
+    "\n",
+    "print(\"Modification des valeurs par assignation:\\n\", liste, dict)\n",
+    "\n",
+    "# Ajout d'éléments\n",
+    "liste.append(4)\n",
+    "dict['nationalité'] = 'française'\n",
+    "\n",
+    "print(\"Ajout d'éléments:\\n\", liste, dict)\n",
+    "\n",
+    "# Suppression d'éléments\n",
+    "liste.pop(0)\n",
+    "dict.pop('age')\n",
+    "\n",
+    "print(\"Suppression d'éléments:\\n\", liste, dict)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Si on a besoin de modifier une liste ou un dictionnaire, mais que l'on veut garder une trace des objets initiaux, il faut commencer par en créer une **copie**, il ne suffit pas de créer une variable suplémentaire sans quoi cette variable serait elle aussi modifiée si l'objet initial changeait: l'assignation est dite par **référence** dans ce cas."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 20,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Valeurs initiales:\n",
+      " [1, 'deux', 3]\n",
+      "Modification de la liste L:\n",
+      "La liste L a bien, été modifiée: [1, 2, 3]\n",
+      "La liste L_ref a aussi été modifiée car il s'agit juste d'une référence vers la liste L: [1, 2, 3]\n",
+      "La copie L_copie n'a pas été modifiée: [1, 'deux', 3]\n"
+     ]
+    }
+   ],
+   "source": [
+    "# Valeurs initiales\n",
+    "L = [ 1, 'deux' , 3]\n",
+    "print(\"Valeurs initiales:\\n\", L)\n",
+    "\n",
+    "# Création d'une référencxe à la liste par simple assignation\n",
+    "L_ref = L\n",
+    "\n",
+    "# Création d'une copie de la liste\n",
+    "L_copie = list(L)\n",
+    "\n",
+    "# Modification de la liste initiale\n",
+    "L[1] = 2\n",
+    "\n",
+    "print(\"Modification de la liste L:\")\n",
+    "print(\"La liste L a bien, été modifiée:\", L)\n",
+    "print(\"La liste L_ref a aussi été modifiée car il s'agit juste d'une référence vers la liste L:\", L_ref)\n",
+    "print(\"La copie L_copie n'a pas été modifiée:\", L_copie)\n",
+    "\n"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Entrée des données\n",
+    "==================\n",
+    "\n",
+    "Dans cette phase peut aussi figurer ce qu’on appelle l’entrée des\n",
+    "données, qui peut se manifester par la saisie de caractères ou de\n",
+    "nombres sur le clavier, ou la lecture de la position du pointeur de la\n",
+    "souris, ou encore par la lecture d’un fichier contenant ces nombres ou\n",
+    "caractères.\n",
+    "\n",
+    "Il s’agit aussi de repérer les résultats intermédiaires qu’il est bon de\n",
+    "mémoriser pour la suite car indispensables au traitement.\n",
+    "\n",
+    "Il est parfois utile d’utiliser des variables auxiliaires pour ne pas\n",
+    "perturber les données initiales.\n",
+    "\n",
+    "Entrée des données au clavier\n",
+    "-----------------------------\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 21,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Entrer la valeur du chiffre souhaité: 7\n"
+     ]
+    }
+   ],
+   "source": [
+    "chiffre = input(\"Entrer la valeur du chiffre souhaité: \")\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 22,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "7\n"
+     ]
+    }
+   ],
+   "source": [
+    "print(chiffre)\n"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Attention cependant, cette valeur est une chaîne de caractère, et les opérations sont celles des `string`s."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 23,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "<class 'str'>\n"
+     ]
+    },
+    {
+     "data": {
+      "text/plain": [
+       "'7777777777'"
+      ]
+     },
+     "execution_count": 23,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "print(type(chiffre))\n",
+    "chiffre*10"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Il convient de la convertir un nombre entier ou flottant avant d'utiliser la valeur."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 24,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "70"
+      ]
+     },
+     "execution_count": 24,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "chiffre = int(chiffre)\n",
+    "10*chiffre"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Lecture d'un fichier\n",
+    "--------------------\n",
+    "\n",
+    "Voici par exemple comment ouvrir et lire un fichier appelé `lorem.txt`\n",
+    "contenu dans le répértoire `data` du dossier courant.\n",
+    "\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 27,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod\\ntempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At\\nvero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren,\\nno sea takimata sanctus est Lorem ipsum dolor sit amet.\\n'"
+      ]
+     },
+     "execution_count": 27,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "fichier = open('data/lorem.txt')\n",
+    "fichier.read()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Dans la sortie précédente, les caractères `\\n` représentent les sauts de\n",
+    "ligne, on peut aussi lire le fichier ligne par ligne."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 29,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod\\n'"
+      ]
+     },
+     "execution_count": 29,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "fichier = open('data/lorem.txt')\n",
+    "fichier.readline()"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.4.0"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/pelican-plugins/liquid_tags/test_data/content/test-generic-config-tag.md b/pelican-plugins/liquid_tags/test_data/content/test-generic-config-tag.md
new file mode 100644
index 0000000000000000000000000000000000000000..867b8d7ad7ad3cf419f48bc61c1fcbceecb65a4b
--- /dev/null
+++ b/pelican-plugins/liquid_tags/test_data/content/test-generic-config-tag.md
@@ -0,0 +1,7 @@
+Title: test generic config tag
+Date: 2017-12-03
+Authors: A. Person
+
+This post is written by 
+{% generic config author %}
+if all goes well the blog post author (from settings, not from the post) shows up one line above
diff --git a/pelican-plugins/liquid_tags/test_data/content/test-ipython-notebook-nbformat3.md b/pelican-plugins/liquid_tags/test_data/content/test-ipython-notebook-nbformat3.md
new file mode 100644
index 0000000000000000000000000000000000000000..0f5d2d88598f91f874e40a7b41ef0ab05c5f3268
--- /dev/null
+++ b/pelican-plugins/liquid_tags/test_data/content/test-ipython-notebook-nbformat3.md
@@ -0,0 +1,17 @@
+Title: test ipython notebook nb format 3
+Date: 2015-03-03
+Authors: Testing Man
+
+
+Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod
+tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At
+vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren,
+no sea takimata sanctus est Lorem ipsum dolor sit amet.
+
+#Loading an entire notebook nbformat = 3.0
+
+{% notebook test_nbformat3.ipynb %}
+
+#Loading selected cells from a notebook nbformat = 3.0
+
+{% notebook test_nbformat3.ipynb cells[1:5] %}
diff --git a/pelican-plugins/liquid_tags/test_data/content/test-ipython-notebook-nbformat4.md b/pelican-plugins/liquid_tags/test_data/content/test-ipython-notebook-nbformat4.md
new file mode 100644
index 0000000000000000000000000000000000000000..680817af8b056ab98a59326e6b376f0d2276d163
--- /dev/null
+++ b/pelican-plugins/liquid_tags/test_data/content/test-ipython-notebook-nbformat4.md
@@ -0,0 +1,17 @@
+Title: test ipython notebook nb format 4
+Date: 2015-03-03
+Authors: Testing Man
+
+
+Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod
+tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At
+vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren,
+no sea takimata sanctus est Lorem ipsum dolor sit amet.
+
+#Loading an entire notebook nbformat = 4.0
+
+{% notebook test_nbformat4.ipynb %}
+
+#Loading selected cells from a notebook nbformat = 4.0
+
+{% notebook test_nbformat4.ipynb cells[1:5] %}
diff --git a/pelican-plugins/liquid_tags/test_data/flickr.json b/pelican-plugins/liquid_tags/test_data/flickr.json
new file mode 100644
index 0000000000000000000000000000000000000000..dbc7078d419d166919b71736d12557666c2fec5b
--- /dev/null
+++ b/pelican-plugins/liquid_tags/test_data/flickr.json
@@ -0,0 +1 @@
+{"photo":{"id":"18841055371","secret":"17ac287217","server":"5552","farm":6,"dateuploaded":"1434394882","isfavorite":0,"license":"4","safety_level":"0","rotation":0,"originalsecret":"ee4b63c8d8","originalformat":"jpg","owner":{"nsid":"8810721@N07","username":"marvinxsteadfast","realname":"","location":"","iconserver":"0","iconfarm":0,"path_alias":"marvinxsteadfast"},"title":{"_content":"fichte"},"description":{"_content":"Processed with VSCOcam with k1 preset"},"visibility":{"ispublic":1,"isfriend":0,"isfamily":0},"dates":{"posted":"1434394882","taken":"2015-06-13 12:51:14","takengranularity":"0","takenunknown":"0","lastupdate":"1434445581"},"views":"23","editability":{"cancomment":0,"canaddmeta":0},"publiceditability":{"cancomment":1,"canaddmeta":0},"usage":{"candownload":1,"canblog":0,"canprint":0,"canshare":1},"comments":{"_content":"0"},"notes":{"note":[]},"people":{"haspeople":0},"tags":{"tag":[]},"location":{"latitude":"52.418917","longitude":"10.785100","accuracy":"16","context":"0","neighbourhood":{"_content":"Stadtteil Wolfsburg","place_id":"WhlihUxTVLJhqUkmNQ","woeid":"26823353"},"locality":{"_content":"Wolfsburg","place_id":"E0zyhhBWUr1dyKs","woeid":"707678"},"county":{"_content":"Stadtkreis Wolfsburg","place_id":"3NbeUiNQUL9_BB.8dg","woeid":"12596975"},"region":{"_content":"Lower Saxony","place_id":"ul2d17NTUb4lkTA_","woeid":"2345486"},"country":{"_content":"Germany","place_id":"h7eZVDlTUb50Btij9Q","woeid":"23424829"},"place_id":"WhlihUxTVLJhqUkmNQ","woeid":"26823353"},"geoperms":{"ispublic":1,"iscontact":0,"isfriend":0,"isfamily":0},"urls":{"url":[{"type":"photopage","_content":"https:\/\/www.flickr.com\/photos\/marvinxsteadfast\/18841055371\/"}]},"media":"photo"},"stat":"ok"}
\ No newline at end of file
diff --git a/pelican-plugins/liquid_tags/test_data/giphy.json b/pelican-plugins/liquid_tags/test_data/giphy.json
new file mode 100644
index 0000000000000000000000000000000000000000..8a2d5e11fc69a6db2f38f53be208158076330ad1
--- /dev/null
+++ b/pelican-plugins/liquid_tags/test_data/giphy.json
@@ -0,0 +1,2 @@
+
+{"data":{"type":"gif","id":"aMSJFS6oFX0fC","url":"http:\/\/giphy.com\/gifs\/veronica-mars-aMSJFS6oFX0fC","bitly_gif_url":"http:\/\/gph.is\/1hcFOSo","bitly_url":"http:\/\/gph.is\/1hcFOSo","embed_url":"http:\/\/giphy.com\/embed\/aMSJFS6oFX0fC","username":"","source":"http:\/\/www.tumblr.com","rating":"pg","caption":"","content_url":"","import_datetime":"2014-02-21 04:43:11","trending_datetime":"1970-01-01 00:00:00","images":{"fixed_height":{"url":"http:\/\/media2.giphy.com\/media\/aMSJFS6oFX0fC\/200.gif","width":"350","height":"200","size":"296611","mp4":"http:\/\/media2.giphy.com\/media\/aMSJFS6oFX0fC\/200.mp4","mp4_size":"64850","webp":"http:\/\/media2.giphy.com\/media\/aMSJFS6oFX0fC\/200.webp","webp_size":"529942"},"fixed_height_still":{"url":"http:\/\/media2.giphy.com\/media\/aMSJFS6oFX0fC\/200_s.gif","width":"350","height":"200"},"fixed_height_downsampled":{"url":"http:\/\/media2.giphy.com\/media\/aMSJFS6oFX0fC\/200_d.gif","width":"350","height":"200","size":"243264","webp":"http:\/\/media2.giphy.com\/media\/aMSJFS6oFX0fC\/200_d.webp","webp_size":"138300"},"fixed_width":{"url":"http:\/\/media2.giphy.com\/media\/aMSJFS6oFX0fC\/200w.gif","width":"200","height":"114","size":"120246","mp4":"http:\/\/media2.giphy.com\/media\/aMSJFS6oFX0fC\/200w.mp4","mp4_size":"96166","webp":"http:\/\/media2.giphy.com\/media\/aMSJFS6oFX0fC\/200w.webp","webp_size":"210140"},"fixed_width_still":{"url":"http:\/\/media2.giphy.com\/media\/aMSJFS6oFX0fC\/200w_s.gif","width":"200","height":"114"},"fixed_width_downsampled":{"url":"http:\/\/media2.giphy.com\/media\/aMSJFS6oFX0fC\/200w_d.gif","width":"200","height":"114","size":"113909","webp":"http:\/\/media2.giphy.com\/media\/aMSJFS6oFX0fC\/200w_d.webp","webp_size":"55084"},"fixed_height_small":{"url":"http:\/\/media2.giphy.com\/media\/aMSJFS6oFX0fC\/100.gif","width":"175","height":"100","size":"296611","mp4":"http:\/\/media2.giphy.com\/media\/aMSJFS6oFX0fC\/100.mp4","mp4_size":"243931","webp":"http:\/\/media2.giphy.com\/media\/aMSJFS6oFX0fC\/100.webp","webp_size":"136098"},"fixed_height_small_still":{"url":"http:\/\/media2.giphy.com\/media\/aMSJFS6oFX0fC\/100_s.gif","width":"175","height":"100"},"fixed_width_small":{"url":"http:\/\/media2.giphy.com\/media\/aMSJFS6oFX0fC\/100w.gif","width":"100","height":"57","size":"120246","mp4":"http:\/\/media2.giphy.com\/media\/aMSJFS6oFX0fC\/100w.mp4","mp4_size":"101604","webp":"http:\/\/media2.giphy.com\/media\/aMSJFS6oFX0fC\/100w.webp","webp_size":"55480"},"fixed_width_small_still":{"url":"http:\/\/media2.giphy.com\/media\/aMSJFS6oFX0fC\/100w_s.gif","width":"100","height":"57"},"downsized":{"url":"http:\/\/media2.giphy.com\/media\/aMSJFS6oFX0fC\/giphy.gif","width":"245","height":"140","size":"487767"},"downsized_still":{"url":"http:\/\/media2.giphy.com\/media\/aMSJFS6oFX0fC\/giphy_s.gif","width":"245","height":"140"},"downsized_large":{"url":"http:\/\/media2.giphy.com\/media\/aMSJFS6oFX0fC\/giphy.gif","width":"245","height":"140","size":"487767"},"original":{"url":"http:\/\/media2.giphy.com\/media\/aMSJFS6oFX0fC\/giphy.gif","width":"245","height":"140","size":"487767","frames":"23","mp4":"http:\/\/media2.giphy.com\/media\/aMSJFS6oFX0fC\/giphy.mp4","mp4_size":"219528","webp":"http:\/\/media2.giphy.com\/media\/aMSJFS6oFX0fC\/giphy.webp","webp_size":"272262"},"original_still":{"url":"http:\/\/media2.giphy.com\/media\/aMSJFS6oFX0fC\/giphy_s.gif","width":"245","height":"140"}}},"meta":{"status":200,"msg":"OK"}}
\ No newline at end of file
diff --git a/pelican-plugins/liquid_tags/test_data/pelicanconf.py b/pelican-plugins/liquid_tags/test_data/pelicanconf.py
new file mode 100644
index 0000000000000000000000000000000000000000..e6ebd9a8854a75ec078f94ebc9cad0c73ccdfdee
--- /dev/null
+++ b/pelican-plugins/liquid_tags/test_data/pelicanconf.py
@@ -0,0 +1,35 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*- #
+from __future__ import unicode_literals
+
+AUTHOR = 'The Tester'
+SITENAME = 'Testing site'
+SITEURL = 'http://example.com/test'
+
+# to make the test suite portable
+TIMEZONE = 'UTC'
+PATH = 'content'
+
+READERS = {'html': None}
+
+# Generate only one feed
+FEED_ALL_ATOM = None
+CATEGORY_FEED_ATOM = None
+TRANSLATION_FEED_ATOM = None
+AUTHOR_FEED_ATOM = None
+AUTHOR_FEED_RSS = None
+
+# Disable unnecessary pages
+CATEGORY_SAVE_AS = ''
+TAG_SAVE_AS = ''
+AUTHOR_SAVE_AS = ''
+ARCHIVES_SAVE_AS = ''
+AUTHORS_SAVE_AS = ''
+CATEGORIES_SAVE_AS = ''
+TAGS_SAVE_AS = ''
+
+PLUGIN_PATHS = ['../../']
+PLUGINS = ['liquid_tags.notebook', 'liquid_tags.generic']
+
+NOTEBOOK_DIR = 'notebooks'
+LIQUID_CONFIGS = (('PATH', '.', "The default path"), ('THEME', '', 'The theme in use'), ('SITENAME', 'Default Sitename', 'The name of the site'), ('AUTHOR', '', 'Name of the blog author'))
diff --git a/pelican-plugins/liquid_tags/test_data/pelicanhtml_2.tpl b/pelican-plugins/liquid_tags/test_data/pelicanhtml_2.tpl
new file mode 120000
index 0000000000000000000000000000000000000000..0f51030b56f2ed50988dc26c3707da1678e89c81
--- /dev/null
+++ b/pelican-plugins/liquid_tags/test_data/pelicanhtml_2.tpl
@@ -0,0 +1 @@
+../pelicanhtml_2.tpl
\ No newline at end of file
diff --git a/pelican-plugins/liquid_tags/test_data/pelicanhtml_3.tpl b/pelican-plugins/liquid_tags/test_data/pelicanhtml_3.tpl
new file mode 120000
index 0000000000000000000000000000000000000000..662692d39914a4ea42364ba2785ded4c83601237
--- /dev/null
+++ b/pelican-plugins/liquid_tags/test_data/pelicanhtml_3.tpl
@@ -0,0 +1 @@
+../pelicanhtml_3.tpl
\ No newline at end of file
diff --git a/pelican-plugins/liquid_tags/test_flickr.py b/pelican-plugins/liquid_tags/test_flickr.py
new file mode 100644
index 0000000000000000000000000000000000000000..556dad01b8ac11206ecc73b2d76b03fda09331da
--- /dev/null
+++ b/pelican-plugins/liquid_tags/test_flickr.py
@@ -0,0 +1,84 @@
+import os
+import re
+import sys
+import unittest
+try:
+    from unittest.mock import patch
+except ImportError:
+    from mock import patch
+import pytest
+from . import flickr
+
+
+if 'nosetests' in sys.argv[0]:
+    raise unittest.SkipTest('Those tests are pytest-compatible only')
+
+PLUGIN_DIR = os.path.dirname(__file__)
+TEST_DATA_DIR = os.path.join(PLUGIN_DIR, 'test_data')
+
+
+@pytest.mark.parametrize('input,expected', [
+    ('18873146680 large "test 1"',
+     dict(photo_id='18873146680',
+          size='large',
+          alt='test 1')),
+    ('18873146680 large \'test 1\'',
+     dict(photo_id='18873146680',
+          size='large',
+          alt='test 1')),
+    ('18873143536360 medium "test number two"',
+     dict(photo_id='18873143536360',
+          size='medium',
+          alt='test number two')),
+    ('18873143536360 small "test number 3"',
+     dict(photo_id='18873143536360',
+          size='small',
+          alt='test number 3')),
+    ('18873143536360 "test 4"',
+     dict(photo_id='18873143536360',
+          size=None,
+          alt='test 4')),
+    ('18873143536360',
+     dict(photo_id='18873143536360',
+          size=None,
+          alt=None)),
+    ('123456 small',
+     dict(photo_id='123456',
+          size='small',
+          alt=None))
+])
+def test_regex(input, expected):
+    assert re.match(flickr.PARSE_SYNTAX, input).groupdict() == expected
+
+
+@pytest.mark.parametrize('input,expected', [
+    (['1', 'server1', '1', 'secret1', 'small'],
+     'https://farm1.staticflickr.com/server1/1_secret1_n.jpg'),
+    (['2', 'server2', '2', 'secret2', 'medium'],
+     'https://farm2.staticflickr.com/server2/2_secret2_c.jpg'),
+    (['3', 'server3', '3', 'secret3', 'large'],
+     'https://farm3.staticflickr.com/server3/3_secret3_b.jpg')
+])
+def test_source_url(input, expected):
+    assert flickr.source_url(
+        input[0], input[1], input[2], input[3], input[4]) == expected
+
+
+@patch('liquid_tags.flickr.urlopen')
+def test_generage_html(mock_urlopen):
+    # mock the return to deliver the flickr.json file instead
+    with open(TEST_DATA_DIR + '/flickr.json', 'rb') as f:
+        mock_urlopen.return_value.read.return_value = f.read()
+
+        attrs = dict(
+            photo_id='1234567',
+            size='large',
+            alt='this is a test'
+        )
+
+        expected = ('<a href="https://www.flickr.com/photos/'
+                    'marvinxsteadfast/18841055371/">'
+                    '<img src="https://farm6.staticflickr.com/5552/1234567_'
+                    '17ac287217_b.jpg" alt="this is a test"></a>')
+
+        assert flickr.generate_html(attrs, 'abcdef') == expected
diff --git a/pelican-plugins/liquid_tags/test_generation.py b/pelican-plugins/liquid_tags/test_generation.py
new file mode 100644
index 0000000000000000000000000000000000000000..169034ad2b64dc623e182820e7040440b949d9ec
--- /dev/null
+++ b/pelican-plugins/liquid_tags/test_generation.py
@@ -0,0 +1,96 @@
+# -*- coding: utf-8 -*-
+from __future__ import print_function
+
+import filecmp
+import os
+import unittest
+from shutil import rmtree
+from tempfile import mkdtemp
+
+import pytest
+from pelican import Pelican
+from pelican.settings import read_settings
+
+from .notebook import IPYTHON_VERSION
+
+PLUGIN_DIR = os.path.dirname(__file__)
+TEST_DATA_DIR = os.path.join(PLUGIN_DIR, 'test_data')
+
+
+class TestFullRun(unittest.TestCase):
+    '''Test running Pelican with the Plugin'''
+
+    def setUp(self):
+        '''Create temporary output and cache folders'''
+        self.temp_path = mkdtemp(prefix='pelicantests.')
+        self.temp_cache = mkdtemp(prefix='pelican_cache.')
+        os.chdir(TEST_DATA_DIR)
+
+    def tearDown(self):
+        '''Remove output and cache folders'''
+        rmtree(self.temp_path)
+        rmtree(self.temp_cache)
+        os.chdir(PLUGIN_DIR)
+
+    @unittest.skipIf(IPYTHON_VERSION != 3,
+                     reason="output must be created with ipython version 3")
+    def test_generate_with_ipython3(self):
+        '''Test generation of site with the plugin.'''
+
+        base_path = os.path.dirname(os.path.abspath(__file__))
+        base_path = os.path.join(base_path, 'test_data')
+        content_path = os.path.join(base_path, 'content')
+        output_path = os.path.join(base_path, 'output')
+        settings_path = os.path.join(base_path, 'pelicanconf.py')
+        settings = read_settings(path=settings_path,
+                                 override={'PATH': content_path,
+                                           'OUTPUT_PATH': self.temp_path,
+                                           'CACHE_PATH': self.temp_cache,
+                                           }
+                                 )
+
+        pelican = Pelican(settings)
+        pelican.run()
+
+        # test existence
+        assert os.path.exists(os.path.join(self.temp_path,
+                                           'test-ipython-notebook-nb-format-3.html'))
+        assert os.path.exists(os.path.join(self.temp_path,
+                                           'test-ipython-notebook-nb-format-4.html'))
+
+        # test differences
+        #assert filecmp.cmp(os.path.join(output_path,
+        #                                'test-ipython-notebook-v2.html'),
+        #                   os.path.join(self.temp_path,
+        #                                'test-ipython-notebook.html'))
+
+    @unittest.skipIf(IPYTHON_VERSION != 2,
+                     reason="output must be created with ipython version 2")
+    def test_generate_with_ipython2(self):
+        '''Test generation of site with the plugin.'''
+
+        base_path = os.path.dirname(os.path.abspath(__file__))
+        base_path = os.path.join(base_path, 'test_data')
+        content_path = os.path.join(base_path, 'content')
+        settings_path = os.path.join(base_path, 'pelicanconf.py')
+        settings = read_settings(path=settings_path,
+                                 override={'PATH': content_path,
+                                           'OUTPUT_PATH': self.temp_path,
+                                           'CACHE_PATH': self.temp_cache,
+                                           }
+                                 )
+
+        pelican = Pelican(settings)
+        pelican.run()
+
+        # test existence
+        assert os.path.exists(os.path.join(self.temp_path,
+                                           'test-ipython-notebook-nb-format-3.html'))
+        assert os.path.exists(os.path.join(self.temp_path,
+                                           'test-ipython-notebook-nb-format-4.html'))
+
+        # test differences
+        #assert filecmp.cmp(os.path.join(output_path,
+        #                                'test-ipython-notebook-v3.html'),
+        #                   os.path.join(self.temp_path,
+        #                                'test-ipython-notebook.html'))
diff --git a/pelican-plugins/liquid_tags/test_generic.py b/pelican-plugins/liquid_tags/test_generic.py
new file mode 100644
index 0000000000000000000000000000000000000000..4960219d7498602b162c82eba5d8a24a93b3d2a4
--- /dev/null
+++ b/pelican-plugins/liquid_tags/test_generic.py
@@ -0,0 +1,61 @@
+# -*- coding: utf-8 -*-
+from __future__ import print_function
+
+import filecmp
+import os
+import unittest
+from shutil import rmtree
+from tempfile import mkdtemp
+
+import pytest
+from pelican import Pelican
+from pelican.settings import read_settings
+
+PLUGIN_DIR = os.path.dirname(__file__)
+TEST_DATA_DIR = os.path.join(PLUGIN_DIR, 'test_data')
+
+
+class TestFullRun(unittest.TestCase):
+    '''Test running Pelican with the Plugin'''
+
+    def setUp(self):
+        '''Create temporary output and cache folders'''
+        self.temp_path = mkdtemp(prefix='pelicantests.')
+        self.temp_cache = mkdtemp(prefix='pelican_cache.')
+        os.chdir(TEST_DATA_DIR)
+
+    def tearDown(self):
+        '''Remove output and cache folders'''
+        rmtree(self.temp_path)
+        rmtree(self.temp_cache)
+        os.chdir(PLUGIN_DIR)
+
+
+    def test_generic_tag_with_config(self):
+        '''Test generation of site with a generic tag that reads in a config file.'''
+
+        base_path = os.path.dirname(os.path.abspath(__file__))
+        base_path = os.path.join(base_path, 'test_data')
+        content_path = os.path.join(base_path, 'content')
+        output_path = os.path.join(base_path, 'output')
+        settings_path = os.path.join(base_path, 'pelicanconf.py')
+        settings = read_settings(path=settings_path,
+                                 override={'PATH': content_path,
+                                           'OUTPUT_PATH': self.temp_path,
+                                           'CACHE_PATH': self.temp_cache,
+                                           }
+                                 )
+
+        pelican = Pelican(settings)
+        pelican.run()
+
+        assert os.path.exists(os.path.join(self.temp_path,
+                                           'test-generic-config-tag.html'))
+
+        assert "Tester" in open(os.path.join(self.temp_path,
+                                           'test-generic-config-tag.html')).read()
+        # test differences
+        #assert filecmp.cmp(os.path.join(output_path,
+        #                                'test-ipython-notebook-v3.html'),
+        #                   os.path.join(self.temp_path,
+        #                                'test-ipython-notebook.html'))
diff --git a/pelican-plugins/liquid_tags/test_giphy.py b/pelican-plugins/liquid_tags/test_giphy.py
new file mode 100644
index 0000000000000000000000000000000000000000..a6d915da3c78ddfe1d34a17a67e7c646918badec
--- /dev/null
+++ b/pelican-plugins/liquid_tags/test_giphy.py
@@ -0,0 +1,34 @@
+import os
+import sys
+import unittest
+try:
+    from unittest.mock import patch
+except ImportError:
+    from mock import patch
+import pytest
+from . import giphy
+
+
+if 'nosetests' in sys.argv[0]:
+    raise unittest.SkipTest('Those tests are pytest-compatible only')
+
+PLUGIN_DIR = os.path.dirname(__file__)
+TEST_DATA_DIR = os.path.join(PLUGIN_DIR, 'test_data')
+
+
+@pytest.mark.parametrize('input,expected', [
+    (dict(gif_id='abc123'),
+     ('<a href="http://giphy.com/gifs/veronica-mars-aMSJFS6oFX0fC">'
+      '<img src="http://media2.giphy.com/media/'
+      'aMSJFS6oFX0fC/giphy.gif" alt="source: http://www.tumblr.com"></a>')),
+    (dict(gif_id='abc123', alt='ive had some free time'),
+     ('<a href="http://giphy.com/gifs/veronica-mars-aMSJFS6oFX0fC">'
+      '<img src="http://media2.giphy.com/media/'
+      'aMSJFS6oFX0fC/giphy.gif" alt="ive had some free time"></a>'))
+])
+@patch('liquid_tags.giphy.urlopen')
+def test_create_html(mock_urlopen, input, expected):
+    with open(TEST_DATA_DIR + '/giphy.json', 'rb') as f:
+        mock_urlopen.return_value.read.return_value = f.read()
+
+        assert giphy.create_html('test_api_key', input) == expected
diff --git a/pelican-plugins/liquid_tags/test_include_code.py b/pelican-plugins/liquid_tags/test_include_code.py
new file mode 100644
index 0000000000000000000000000000000000000000..a5321c67f018a24cec606c01c9baf30a472b68d0
--- /dev/null
+++ b/pelican-plugins/liquid_tags/test_include_code.py
@@ -0,0 +1,247 @@
+import re
+import sys
+import unittest
+
+import pytest
+
+from . import include_code
+
+if 'nosetests' in sys.argv[0]:
+    raise unittest.SkipTest('Those tests are pytest-compatible only')
+
+@pytest.mark.parametrize(
+        'input,expected', [
+            (
+                'test_data/main.c',
+                ('test_data/main.c', None, None, None, None, None, None, None)
+            ),
+            (
+                'test_data/main.c lang:c',
+                ('test_data/main.c', 'c', None, None, None, None, None, None)
+            ),
+            (
+                'test_data/main.c lang:c lines:1-10',
+                ('test_data/main.c', 'c', '1-10', None, None, None, None, None)
+            ),
+            (
+                'test_data/main.c lang:c lines:1-10 :hidefilename:',
+                ('test_data/main.c', 'c', '1-10', ':hidefilename:', None, None,
+                 None, None)
+            ),
+            (
+                'test_data/main.c lang:c lines:1-10 :hidefilename: :hidelink:',
+                ('test_data/main.c', 'c', '1-10', ':hidefilename:', ':hidelink:',
+                 None, None, None)
+            ),
+            (
+                'test_data/main.c lang:c lines:1-10 :hidefilename: :hidelink: '\
+                ':hideall:',
+                ('test_data/main.c', 'c', '1-10', ':hidefilename:', ':hidelink:',
+                 ":hideall:", None, None)
+            ),
+            (
+                'test_data/main.c lang:c lines:1-10 :hidefilename: :hidelink: '\
+                ':hideall: codec:iso-8859-1',
+                ('test_data/main.c', 'c', '1-10', ':hidefilename:', ':hidelink:',
+                 ':hideall:', 'iso-8859-1', None)
+            ),
+            (
+                'test_data/main.c lang:c lines:1-10 :hidefilename: :hidelink: '\
+                ':hideall: codec:iso-8859-1 Hello It\'s me - Title',
+                ('test_data/main.c', 'c', '1-10', ':hidefilename:', ':hidelink:',
+                 ':hideall:', 'iso-8859-1', 'Hello It\'s me - Title')
+            ),
+        ]
+)
+def test_regex(input, expected):
+    print(re.match(include_code.FORMAT, input).groups())
+    assert re.match(include_code.FORMAT, input).groups() == expected
+
+class Object:
+    pass
+
+class preprocessor:
+    @classmethod
+    def func(*x, safe=False):
+        return ''.join([str(s) for s in x])
+
+    def __init__(self):
+        self.configs = Object()
+        self.configs.getConfig = lambda x: ''
+        self.configs.htmlStash = Object()
+        self.configs.htmlStash.store = self.func
+
+@pytest.mark.parametrize(
+        'input, expected', [
+            (
+                'test_data/main.c',
+                (
+                    '<class \'liquid_tags.test_include_code.preprocessor\'>'
+                    '<figure class=\'code\'>\n<figcaption>'
+                    '<span>main.c</span> '
+                    '<a href=\'/test_data/main.c\'>download</a>'
+                    '\n'
+                    '\n'
+                    '    #include <stdio.h>\n'
+                    '    \n'
+                    '    int main(int argc, char** argv){\n'
+                    '        printf("Hello world!");\n'
+                    '    \n'
+                    '        return 0;\n'
+                    '    }\n'
+                    '    \n'
+                    '\n'
+                    '<class \'liquid_tags.test_include_code.preprocessor\'>'
+                    '</figure>\n'
+                )
+            ),
+            (
+                'test_data/main.c :hideall:',
+                (
+                    '<class \'liquid_tags.test_include_code.preprocessor\'>'
+                    '<figure class=\'code\'>\n<figcaption>'
+                    '\n'
+                    '\n'
+                    '    #include <stdio.h>\n'
+                    '    \n'
+                    '    int main(int argc, char** argv){\n'
+                    '        printf("Hello world!");\n'
+                    '    \n'
+                    '        return 0;\n'
+                    '    }\n'
+                    '    \n'
+                    '\n'
+                    '<class \'liquid_tags.test_include_code.preprocessor\'>'
+                    '</figure>\n'
+                )
+            ),
+            (
+                'test_data/main.c :hidefilename: C application',
+                (
+                    '<class \'liquid_tags.test_include_code.preprocessor\'>'
+                    '<figure class=\'code\'>\n<figcaption>'
+                    '<a href=\'/test_data/main.c\'>download</a>'
+                    '\n'
+                    '\n'
+                    '    #include <stdio.h>\n'
+                    '    \n'
+                    '    int main(int argc, char** argv){\n'
+                    '        printf("Hello world!");\n'
+                    '    \n'
+                    '        return 0;\n'
+                    '    }\n'
+                    '    \n'
+                    '\n'
+                    '<class \'liquid_tags.test_include_code.preprocessor\'>'
+                    '</figure>\n'
+                )
+            ),
+            (
+                'test_data/main.c :hidelink:',
+                (
+                    '<class \'liquid_tags.test_include_code.preprocessor\'>'
+                    '<figure class=\'code\'>\n<figcaption>'
+                    '<span>main.c</span> '
+                    '\n'
+                    '\n'
+                    '    #include <stdio.h>\n'
+                    '    \n'
+                    '    int main(int argc, char** argv){\n'
+                    '        printf("Hello world!");\n'
+                    '    \n'
+                    '        return 0;\n'
+                    '    }\n'
+                    '    \n'
+                    '\n'
+                    '<class \'liquid_tags.test_include_code.preprocessor\'>'
+                    '</figure>\n'
+                )
+            ),
+            (
+                'test_data/main.c lang:c',
+                (
+                    '<class \'liquid_tags.test_include_code.preprocessor\'>'
+                    '<figure class=\'code\'>\n<figcaption>'
+                    '<span>main.c</span> '
+                    '<a href=\'/test_data/main.c\'>download</a>'
+                    '\n'
+                    '\n'
+                    '    :::c\n'
+                    '    #include <stdio.h>\n'
+                    '    \n'
+                    '    int main(int argc, char** argv){\n'
+                    '        printf("Hello world!");\n'
+                    '    \n'
+                    '        return 0;\n'
+                    '    }\n'
+                    '    \n'
+                    '\n'
+                    '<class \'liquid_tags.test_include_code.preprocessor\'>'
+                    '</figure>\n'
+                )
+            ),
+            (
+                'test_data/main.c lines:4-6',
+                (
+                    '<class \'liquid_tags.test_include_code.preprocessor\'>'
+                    '<figure class=\'code\'>\n<figcaption>'
+                    '<span>main.c [Lines 4-6]</span> '
+                    '<a href=\'/test_data/main.c\'>download</a>'
+                    '\n'
+                    '\n'
+                    '        printf("Hello world!");\n'
+                    '    \n'
+                    '        return 0;\n'
+                    '\n'
+                    '<class \'liquid_tags.test_include_code.preprocessor\'>'
+                    '</figure>\n'
+                )
+            ),
+            (
+                'test_data/main_cz.c codec:iso-8859-1',
+                (
+                    '<class \'liquid_tags.test_include_code.preprocessor\'>'
+                    '<figure class=\'code\'>\n<figcaption>'
+                    '<span>main_cz.c</span> '
+                    '<a href=\'/test_data/main_cz.c\'>download</a>'
+                    '\n'
+                    '\n'
+                    '    #include <stdio.h>\n'
+                    '    \n'
+                    '    int main(int argc, char** argv){\n'
+                    '        printf("Dobrý");\n'
+                    '    \n'
+                    '        return 0;\n'
+                    '    }\n'
+                    '    \n'
+                    '\n'
+                    '<class \'liquid_tags.test_include_code.preprocessor\'>'
+                    '</figure>\n'
+                )
+            ),
+            (
+                'test_data/main.c C Application',
+                (
+                    '<class \'liquid_tags.test_include_code.preprocessor\'>'
+                    '<figure class=\'code\'>\n<figcaption>'
+                    '<span>C Application main.c</span> '
+                    '<a href=\'/test_data/main.c\'>download</a>'
+                    '\n'
+                    '\n'
+                    '    #include <stdio.h>\n'
+                    '    \n'
+                    '    int main(int argc, char** argv){\n'
+                    '        printf("Hello world!");\n'
+                    '    \n'
+                    '        return 0;\n'
+                    '    }\n'
+                    '    \n'
+                    '\n'
+                    '<class \'liquid_tags.test_include_code.preprocessor\'>'
+                    '</figure>\n'
+                )
+            ),
+        ]
+)
+def test_create_html(input, expected):
+    assert include_code.include_code(preprocessor(), 'include_code', input) == expected
diff --git a/pelican-plugins/liquid_tags/test_notebook.py b/pelican-plugins/liquid_tags/test_notebook.py
new file mode 100644
index 0000000000000000000000000000000000000000..d699cdcb600f54c838a087204790a6c4940c7c76
--- /dev/null
+++ b/pelican-plugins/liquid_tags/test_notebook.py
@@ -0,0 +1,100 @@
+import re
+import sys
+import unittest
+
+if 'nosetests' in sys.argv[0]:
+    raise unittest.SkipTest('Those tests are pytest-compatible only')
+
+import pytest
+pytest.skip("Test is currently broken, see pelican pr #1618", allow_module_level=True)
+
+from pelican.tests.support import unittest
+
+from . import notebook
+
+
+class TestNotebookTagRegex(unittest.TestCase):
+
+    def get_argdict(self, markup):
+
+        match = notebook.FORMAT.search(markup)
+
+        if match:
+            argdict = match.groupdict()
+
+            src = argdict['src']
+            start = argdict['start']
+            end = argdict['end']
+            language = argdict['language']
+
+            return src, start, end, language
+
+        return None
+
+    def test_basic_notebook_tag(self):
+        markup = u'path/to/thing.ipynb'
+        src, start, end, language = self.get_argdict(markup)
+
+        self.assertEqual(src, u'path/to/thing.ipynb')
+        self.assertIsNone(start)
+        self.assertIsNone(end)
+        self.assertIsNone(language)
+
+    def test_basic_notebook_tag_insensitive_to_whitespace(self):
+        markup = u'   path/to/thing.ipynb '
+        src, start, end, language = self.get_argdict(markup)
+
+        self.assertEqual(src, u'path/to/thing.ipynb')
+        self.assertIsNone(start)
+        self.assertIsNone(end)
+        self.assertIsNone(language)
+
+    def test_notebook_tag_with_cells(self):
+        markup = u'path/to/thing.ipynb cells[1:5]'
+        src, start, end, language = self.get_argdict(markup)
+
+        self.assertEqual(src, u'path/to/thing.ipynb')
+        self.assertEqual(start, u'1')
+        self.assertEqual(end, u'5')
+        self.assertIsNone(language)
+
+    def test_notebook_tag_with_alphanumeric_language(self):
+        markup = u'path/to/thing.ipynb language[python3]'
+        src, start, end, language = self.get_argdict(markup)
+
+        self.assertEqual(src, u'path/to/thing.ipynb')
+        self.assertIsNone(start)
+        self.assertIsNone(end)
+        self.assertEqual(language, u'python3')
+
+    def test_notebook_tag_with_symbol_in_name_language(self):
+        for short_name in [u'c++', u'cpp-objdump', u'c++-objdumb', u'cxx-objdump']:
+            markup = u'path/to/thing.ipynb language[{}]'.format(short_name)
+            src, start, end, language = self.get_argdict(markup)
+
+            self.assertEqual(src, u'path/to/thing.ipynb')
+            self.assertIsNone(start)
+            self.assertIsNone(end)
+            self.assertEqual(language, short_name)
+
+    def test_notebook_tag_with_language_and_cells(self):
+        markup = u'path/to/thing.ipynb cells[1:5] language[julia]'
+        src, start, end, language = self.get_argdict(markup)
+
+        self.assertEqual(src, u'path/to/thing.ipynb')
+        self.assertEqual(start, u'1')
+        self.assertEqual(end, u'5')
+        self.assertEqual(language, u'julia')
+
+    def test_notebook_tag_with_language_and_cells_and_weird_spaces(self):
+        markup = u'   path/to/thing.ipynb   cells[1:5]  language[julia]       '
+        src, start, end, language = self.get_argdict(markup)
+
+        self.assertEqual(src, u'path/to/thing.ipynb')
+        self.assertEqual(start, u'1')
+        self.assertEqual(end, u'5')
+        self.assertEqual(language, u'julia')
+
+
+if __name__ == '__main__':
+    unittest.main()
\ No newline at end of file
diff --git a/pelican-plugins/liquid_tags/test_soundcloud.py b/pelican-plugins/liquid_tags/test_soundcloud.py
new file mode 100644
index 0000000000000000000000000000000000000000..9809435546d63009ccfdd129ea26b6c2b9ed4091
--- /dev/null
+++ b/pelican-plugins/liquid_tags/test_soundcloud.py
@@ -0,0 +1,45 @@
+import sys
+import unittest
+
+import pytest
+
+from . import soundcloud
+
+if "nosetests" in sys.argv[0]:
+    raise unittest.SkipTest("Those tests are pytest-compatible only")
+
+
+@pytest.mark.parametrize(
+    "input,expected",
+    [
+        (
+            "https://soundcloud.com/forss/in-paradisum",
+            dict(track_url="https://soundcloud.com/forss/in-paradisum"),
+        ),
+        (
+            "https://soundcloud.com/forss/in-paradisum",
+            dict(track_url="https://soundcloud.com/forss/in-paradisum"),
+        ),
+        (
+            "https://soundcloud.com/toroymoi/real-love-ft-kool-ad",
+            dict(track_url="https://soundcloud.com/toroymoi/real-love-ft-kool-ad"),
+        ),
+        (
+            "https://soundcloud.com/capturedtracks/sets/wild-nothing-nocturne",
+            dict(
+                track_url=(
+                    "https://soundcloud.com/capturedtracks/"
+                    "sets/wild-nothing-nocturne"
+                )
+            ),
+        ),
+    ],
+)
+def test_match_it(input, expected):
+    assert soundcloud.match_it(input) == expected
+
+
+@pytest.mark.parametrize("input", ["http://foobar.com", "foobar", "https://google.com"])
+def test_match_it_exception(input):
+    with pytest.raises(ValueError):
+        soundcloud.match_it(input)
diff --git a/pelican-plugins/liquid_tags/test_video.py b/pelican-plugins/liquid_tags/test_video.py
new file mode 100644
index 0000000000000000000000000000000000000000..501c68455c040ca1d24c4f3cee89f44ceaf2ab3a
--- /dev/null
+++ b/pelican-plugins/liquid_tags/test_video.py
@@ -0,0 +1,42 @@
+import unittest
+from .video import video
+
+
+class TestVideoTag(unittest.TestCase):
+
+    maxDiff = None
+
+    def setUp(self):
+        self.preprocessor = None
+        self.tag = 'video'
+
+    def test_normal_example(self):
+        markup = 'http://site.com/video.mp4 720 480 http://site.com/poster-frame.jpg'
+        expected = (
+            '<video width="720" height="480" preload="none" controls poster="http://site.com/poster-frame.jpg">'
+            "<source src='http://site.com/video.mp4' type='video/mp4; codecs=\"avc1.42E01E, mp4a.40.2\"'>"
+            "</video>")
+        actual = video(self.preprocessor, self.tag, markup)
+        self.assertIn(expected, actual)
+
+    def test_relative_path(self):
+        markup = 'files/video.mp4 720 480 images/poster-frame.jpg'
+        expected = (
+            '<video width="720" height="480" preload="none" controls poster="images/poster-frame.jpg">'
+            "<source src='files/video.mp4' type='video/mp4; codecs=\"avc1.42E01E, mp4a.40.2\"'>"
+            "</video>")
+        actual = video(self.preprocessor, self.tag, markup)
+        self.assertIn(expected, actual)
+
+    def test_internal_content(self):
+        markup = '{filename}../files/video.mp4 720 480 {filename}../images/poster-frame.jpg'
+        expected = (
+            '<video width="720" height="480" preload="none" controls poster="{filename}../images/poster-frame.jpg">'
+            "<source src='{filename}../files/video.mp4' type='video/mp4; codecs=\"avc1.42E01E, mp4a.40.2\"'>"
+            "</video>")
+        actual = video(self.preprocessor, self.tag, markup)
+        self.assertIn(expected, actual)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/pelican-plugins/liquid_tags/test_youtube.py b/pelican-plugins/liquid_tags/test_youtube.py
new file mode 100644
index 0000000000000000000000000000000000000000..92cb59f3bc209f2075bae2bc3b66cef865480a00
--- /dev/null
+++ b/pelican-plugins/liquid_tags/test_youtube.py
@@ -0,0 +1,52 @@
+import sys
+import unittest
+
+import pytest
+
+from . import youtube
+
+
+if "nosetests" in sys.argv[0]:
+    raise unittest.SkipTest("Those tests are pytest-compatible only")
+
+
+class configs:
+    def __init__(self):
+        self.config = {}
+
+    def setConfig(self, name, value):
+        self.config[name] = value
+
+    def getConfig(self, name):
+        try:
+            out = self.config[name]
+        except KeyError:
+            out = ''
+
+        return out
+
+
+class fake_proc:
+    def __init__(self):
+        self.configs = configs()
+
+
+@pytest.mark.parametrize('input,expected', [
+    ('v78_WujMnVk', """<a
+                    href="https://www.youtube.com/watch?v=v78_WujMnVk"
+                class="youtube_video" alt="YouTube Video"
+                title="Click to view on YouTube">
+                    <img width="1280" height="720"
+                        src="https://img.youtube.com/vi/v78_WujMnVk/maxresdefault.jpg">
+                </a>""")])
+def test_youtube(input, expected):
+    fake_preproc = fake_proc()
+
+    fake_preproc.configs.setConfig('YOUTUBE_THUMB_ONLY', True)
+    fake_preproc.configs.setConfig('YOUTUBE_THUMB_SIZE', 'maxres')
+
+    print(fake_preproc.configs.config)
+
+    out = youtube.youtube(fake_preproc, '', input)
+
+    assert out == expected
diff --git a/pelican-plugins/liquid_tags/tox.ini b/pelican-plugins/liquid_tags/tox.ini
new file mode 100644
index 0000000000000000000000000000000000000000..e2e22bdc38d39ff01ba857da3c9b9a036425aee1
--- /dev/null
+++ b/pelican-plugins/liquid_tags/tox.ini
@@ -0,0 +1,17 @@
+[tox]
+skipsdist = True
+minversion = 1.8
+envlist =
+       py{27,34}-ipython2,
+       py{27,34}-ipython3,
+
+[testenv]
+commands = py.test
+
+deps =
+	pytest
+	pelican
+	markdown
+	mock
+	ipython2: ipython[notebook]>=2,<3
+	ipython3: ipython[notebook]
diff --git a/pelican-plugins/liquid_tags/video.py b/pelican-plugins/liquid_tags/video.py
new file mode 100644
index 0000000000000000000000000000000000000000..2ef8baf3c381f404e87e08e4f544a2f563a10492
--- /dev/null
+++ b/pelican-plugins/liquid_tags/video.py
@@ -0,0 +1,80 @@
+"""
+Video Tag
+---------
+This implements a Liquid-style video tag for Pelican,
+based on the octopress video tag [1]_
+
+Syntax
+------
+{% video url/to/video [width height] [url/to/poster] %}
+
+Example
+-------
+{% video http://site.com/video.mp4 100% 480 http://site.com/poster-frame.jpg %}
+
+Output
+------
+<span class="videobox">
+	<video width='100%' height='480' preload='none' controls poster='http://site.com/poster-frame.jpg'>
+	   <source src='http://site.com/video.mp4' type='video/mp4; codecs=\"avc1.42E01E, mp4a.40.2\"'/>
+	</video>
+</span>
+
+[1] https://github.com/imathis/octopress/blob/master/plugins/video_tag.rb
+"""
+import os
+import re
+from .mdx_liquid_tags import LiquidTags
+
+SYNTAX = "{% video [http[s]://]path/to/video [[http[s]://]path/to/video] [[http[s]://]path/to/video] [width height] [[http[s]://]path/to/poster] %}"
+
+VIDEO = re.compile(
+    r'((?:https?://|/|\S+/)\S+)(\s+((?:https?://|/|\S+/)\S+))?(\s+((?:https?://|/|\S+/)\S+))?'  # Up to 3 videos
+    r'(\s+(\d+\%?)\s(\d+\%?))?'  # width and height
+    r'(\s+((?:https?://|/|\S+/)\S+))?'  # poster
+)
+
+VID_TYPEDICT = {'.mp4':"type='video/mp4; codecs=\"avc1.42E01E, mp4a.40.2\"'",
+                '.ogv':"type='video/ogg; codecs=theora, vorbis'",
+                '.webm':"type='video/webm; codecs=vp8, vorbis'"}
+
+
+@LiquidTags.register('video')
+def video(preprocessor, tag, markup):
+    videos = []
+    width = None
+    height = None
+    poster = None
+
+    match = VIDEO.search(markup)
+    if match:
+        groups = match.groups()
+        videos = [g for g in groups[0:6:2] if g]
+        width = groups[6]
+        height = groups[7]
+        poster = groups[9]
+
+    if any(videos):
+        video_out = """
+        <span class="videobox">
+            <video width="{width}" height="{height}" preload="none" controls poster="{poster}">
+        """.format(width=width, height=height, poster=poster).strip()
+
+        for vid in videos:
+            base, ext = os.path.splitext(vid)
+            if ext not in VID_TYPEDICT:
+                raise ValueError("Unrecognized video extension: "
+                                 "{0}".format(ext))
+            video_out += ("<source src='{0}' "
+                          "{1}>".format(vid, VID_TYPEDICT[ext]))
+        video_out += "</video></span>"
+    else:
+        raise ValueError("Error processing input, "
+                         "expected syntax: {0}".format(SYNTAX))
+
+    return video_out
+
+
+#----------------------------------------------------------------------
+# This import allows video tag to be a Pelican plugin
+from liquid_tags import register
diff --git a/pelican-plugins/liquid_tags/vimeo.py b/pelican-plugins/liquid_tags/vimeo.py
new file mode 100644
index 0000000000000000000000000000000000000000..e60f0b1f394c58221b96ba6f8d0010041db6ea57
--- /dev/null
+++ b/pelican-plugins/liquid_tags/vimeo.py
@@ -0,0 +1,68 @@
+"""
+Vimeo Tag
+---------
+This implements a Liquid-style vimeo tag for Pelican,
+based on the youtube tag which is in turn based on
+the jekyll / octopress youtube tag [1]_
+
+Syntax
+------
+{% vimeo id [width height] %}
+
+Example
+-------
+{% vimeo 10739054 640 480 %}
+
+Output
+------
+<span style="width:640px; height:480px;">
+    <iframe
+        src="//player.vimeo.com/video/10739054?title=0&amp;byline=0&amp;portrait=0"
+        width="640" height="480" frameborder="0"
+        webkitallowfullscreen mozallowfullscreen allowfullscreen>
+    </iframe>
+</span>
+
+[1] https://gist.github.com/jamieowen/2063748
+"""
+import re
+from .mdx_liquid_tags import LiquidTags
+
+SYNTAX = "{% vimeo id [width height] %}"
+
+VIMEO = re.compile(r'(\S+)(\s+(\d+)\s(\d+))?')
+
+
+@LiquidTags.register('vimeo')
+def vimeo(preprocessor, tag, markup):
+    width = 640
+    height = 390
+    vimeo_id = None
+
+    match = VIMEO.search(markup)
+    if match:
+        groups = match.groups()
+        vimeo_id = groups[0]
+        width = groups[2] or width
+        height = groups[3] or height
+
+    if vimeo_id:
+        vimeo_out = """
+            <span class="videobox">
+                <iframe
+                    src="//player.vimeo.com/video/{vimeo_id}?title=0&amp;byline=0&amp;portrait=0"
+                    width="{width}" height="{height}" frameborder="0"
+                    webkitAllowFullScreen mozallowfullscreen allowFullScreen>
+                </iframe>
+            </span>
+        """.format(width=width, height=height, vimeo_id=vimeo_id).strip()
+    else:
+        raise ValueError("Error processing input, "
+                         "expected syntax: {0}".format(SYNTAX))
+
+    return vimeo_out
+
+
+# ---------------------------------------------------
+# This import allows vimeo tag to be a Pelican plugin
+from liquid_tags import register  # noqa
diff --git a/pelican-plugins/liquid_tags/youtube.py b/pelican-plugins/liquid_tags/youtube.py
new file mode 100644
index 0000000000000000000000000000000000000000..26fc0591d6bad58cd3ec76d47783c0b839ce31c5
--- /dev/null
+++ b/pelican-plugins/liquid_tags/youtube.py
@@ -0,0 +1,104 @@
+"""
+Youtube Tag
+---------
+This implements a Liquid-style youtube tag for Pelican,
+based on the jekyll / octopress youtube tag [1]_
+
+Syntax
+------
+{% youtube id [width height] %}
+
+Example
+-------
+{% youtube dQw4w9WgXcQ 640 480 %}
+
+Output
+------
+
+<span class="videobox">
+    <iframe
+        width="640" height="480"
+        src="https://www.youtube.com/embed/dQw4w9WgXcQ" frameborder="0"
+        webkitAllowFullScreen mozallowfullscreen allowFullScreen>
+    </iframe>
+</span>
+
+[1] https://gist.github.com/jamieowen/2063748
+"""
+import re
+from .mdx_liquid_tags import LiquidTags
+
+SYNTAX = "{% youtube id [width height] %}"
+
+YOUTUBE = re.compile(r'([\S]+)(\s+([\d%]+)\s([\d%]+))?')
+
+
+@LiquidTags.register('youtube')
+def youtube(preprocessor, tag, markup):
+    width = 640
+    height = 390
+    youtube_id = None
+
+    config_thumb_only = preprocessor.configs.getConfig('YOUTUBE_THUMB_ONLY')
+    config_thumb_size = preprocessor.configs.getConfig('YOUTUBE_THUMB_SIZE')
+
+    thumb_sizes = {
+        'maxres': [1280, 720],
+        'sd': [640, 480],
+        'hq': [480, 360],
+        'mq': [320, 180]
+    }
+
+    if config_thumb_only:
+        if not config_thumb_size:
+            config_thumb_size = 'sd'
+
+        try:
+            width = thumb_sizes[config_thumb_size][0]
+            height = thumb_sizes[config_thumb_size][1]
+        except KeyError:
+            pass
+
+    match = YOUTUBE.search(markup)
+    if match:
+        groups = match.groups()
+        youtube_id = groups[0]
+        width = groups[2] or width
+        height = groups[3] or height
+
+    if youtube_id:
+        if config_thumb_only:
+            thumb_url = 'https://img.youtube.com/vi/{youtube_id}'.format(
+                youtube_id=youtube_id)
+
+            youtube_out = """<a
+                    href="https://www.youtube.com/watch?v={youtube_id}"
+                class="youtube_video" alt="YouTube Video"
+                title="Click to view on YouTube">
+                    <img width="{width}" height="{height}"
+                        src="{thumb_url}/{size}default.jpg">
+                </a>""".format(width=width, height=height,
+                               youtube_id=youtube_id,
+                               size=config_thumb_size,
+                               thumb_url=thumb_url)
+        else:
+            youtube_out = """
+                <span class="videobox">
+                    <iframe width="{width}" height="{height}"
+                        src='https://www.youtube.com/embed/{youtube_id}'
+                        frameborder='0' webkitAllowFullScreen
+                        mozallowfullscreen allowFullScreen>
+                    </iframe>
+                </span>
+            """.format(width=width, height=height,
+                       youtube_id=youtube_id).strip()
+    else:
+        raise ValueError("Error processing input, "
+                         "expected syntax: {0}".format(SYNTAX))
+
+    return youtube_out
+
+
+# ---------------------------------------------------
+# This import allows youtube tag to be a Pelican plugin
+from liquid_tags import register  # noqa
diff --git a/pelican-plugins/md_inline_extension/Readme.md b/pelican-plugins/md_inline_extension/Readme.md
new file mode 100644
index 0000000000000000000000000000000000000000..6d13db14c6e3733790d591254eb7e827844990eb
--- /dev/null
+++ b/pelican-plugins/md_inline_extension/Readme.md
@@ -0,0 +1,48 @@
+Markdown Inline Extension For Pelican
+=====================================
+This plugin lets you customize inline HTML
+within Markdown by extending Python's Markdown module.
+
+Installation
+------------
+To enable, ensure that the `md_inline_extension` plugin is accessible.
+Then add the following to settings.py:
+
+    PLUGINS = ["md_inline_extension"]
+
+Usage
+-----
+By default, any Markdown text inside `[]...[]` will get wrapped in
+`span` tags with a class of `pelican-inline`. For example:
+
+`[]Lorem ipsum dolor sit amet, consectetur adipiscing elit[]` will
+become `<span class="pelican-inline">Lorem ipsum dolor sit amet, consectetur adipiscing elit</span>`
+
+You can create your own inline patterns and associate them with
+arbitrary classes and styles by using the `MD_INLINE` dictionary in settings.
+The dictionary takes a pattern as key and expects either a string or a tuple
+as a value. If a string is provided, then that will be the CSS class. If
+a tuple is provided, then the first value will be the style, and the second
+value (if present) will be the class. For example:
+
+```
+MD_INLINE = {
+    '+=+': ('color:red;', 'my-test-class'),
+    '|-|': ('color:blue;',),
+    '&^': 'my-other-text-class',
+}
+```
+
+The above defines three new inline patterns:
+
+ 1. **+=+**: Text within `+=+` will be wrapped in `span` tags like so
+: `<span style="color:red;" class="my-test-class">...</span>`
+ 2. **|-|**: Text within `|-|` will be wrapped in
+`<span style="color:blue;">...</span>`. Note - no class is present.
+ 3. **&^**: Text within `&^` will be wrapped in
+`<span class="my-other-text-class">...</span>`. Note - no style present.
+
+In order to work seamlessly with default inline patterns such as `*` and
+`**`, it is important that your pattern not contain these characters. So
+do not create patterns that are already part of a default Markdown
+[span element](http://daringfireball.net/projects/markdown/syntax#span).
diff --git a/pelican-plugins/md_inline_extension/__init__.py b/pelican-plugins/md_inline_extension/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..2453fe9d1750f98eb34d71a8832e5468bb9308e2
--- /dev/null
+++ b/pelican-plugins/md_inline_extension/__init__.py
@@ -0,0 +1 @@
+from .inline import *
diff --git a/pelican-plugins/md_inline_extension/inline.py b/pelican-plugins/md_inline_extension/inline.py
new file mode 100644
index 0000000000000000000000000000000000000000..db4b6271546a869517452e3ee0636b2fcd40f245
--- /dev/null
+++ b/pelican-plugins/md_inline_extension/inline.py
@@ -0,0 +1,68 @@
+# -*- coding: utf-8 -*-
+"""
+Markdown Inline Extension For Pelican
+=====================================
+Extends Pelican's Markdown module
+and allows for customized inline HTML
+"""
+
+import os
+import sys
+
+from pelican import signals
+
+try:
+    from . pelican_inline_markdown_extension import PelicanInlineMarkdownExtension
+except ImportError as e:
+    PelicanInlineMarkdownExtension = None
+    print("\nMarkdown is not installed - inline Markdown extension disabled\n")
+
+def process_settings(pelicanobj):
+    """Sets user specified settings (see README for more details)"""
+
+    # Default settings
+    inline_settings = {}
+    inline_settings['config'] = {'[]':('', 'pelican-inline')}
+
+    # Get the user specified settings
+    try:
+        settings = pelicanobj.settings['MD_INLINE']
+    except:
+        settings = None
+
+    # If settings have been specified, add them to the config
+    if isinstance(settings, dict):
+        inline_settings['config'].update(settings)
+
+    return inline_settings
+
+def inline_markdown_extension(pelicanobj, config):
+    """Instantiates a customized Markdown extension"""
+
+    # Instantiate Markdown extension and append it to the current extensions
+    try:
+        if isinstance(pelicanobj.settings.get('MD_EXTENSIONS'), list):  # pelican 3.6.3 and earlier
+            pelicanobj.settings['MD_EXTENSIONS'].append(PelicanInlineMarkdownExtension(config))
+        else:
+            pelicanobj.settings['MARKDOWN'].setdefault('extensions', []).append(PelicanInlineMarkdownExtension(config))
+    except:
+        sys.excepthook(*sys.exc_info())
+        sys.stderr.write("\nError - the pelican Markdown extension failed to configure. Inline Markdown extension is non-functional.\n")
+        sys.stderr.flush()
+
+def pelican_init(pelicanobj):
+    """Loads settings and instantiates the Python Markdown extension"""
+
+    # If there was an error loading Markdown, then do not process any further 
+    if not PelicanInlineMarkdownExtension:
+        return
+
+    # Process settings
+    config = process_settings(pelicanobj)
+
+    # Configure Markdown Extension
+    inline_markdown_extension(pelicanobj, config)
+
+def register():
+    """Plugin registration"""
+    signals.initialized.connect(pelican_init)
diff --git a/pelican-plugins/md_inline_extension/pelican_inline_markdown_extension.py b/pelican-plugins/md_inline_extension/pelican_inline_markdown_extension.py
new file mode 100644
index 0000000000000000000000000000000000000000..a3a3eb3cf0cc081248403bf806a14eaffbc77226
--- /dev/null
+++ b/pelican-plugins/md_inline_extension/pelican_inline_markdown_extension.py
@@ -0,0 +1,69 @@
+# -*- coding: utf-8 -*-
+"""
+Pelican Inline Markdown Extension
+==================================
+An extension for the Python Markdown module that enables
+the Pelican Python static site generator to add inline patterns.
+"""
+
+import markdown
+import re
+
+from markdown.util import etree
+from markdown.util import AtomicString
+
+class PelicanInlineMarkdownExtensionPattern(markdown.inlinepatterns.Pattern):
+    """Inline Markdown processing"""
+
+    def __init__(self, pelican_markdown_extension, tag, pattern):
+        super(PelicanInlineMarkdownExtensionPattern,self).__init__(pattern)
+        self.tag = tag
+        self.config = pelican_markdown_extension.getConfig('config')
+
+    def handleMatch(self, m):
+        node = markdown.util.etree.Element(self.tag)
+        tag_attributes = self.config.get(m.group('prefix'), ('', 'pelican-inline'))
+        tag_class = 'pelican-inline'  # default class
+        tag_style = ''  # default is for no styling
+
+        if isinstance(tag_attributes, tuple):
+            tag_style = tag_attributes[0]
+            tag_class = tag_attributes[1] if len(tag_attributes) > 1 else ''
+        elif isinstance(tag_attributes, str):
+            tag_class = tag_attributes
+
+        if tag_class != '':
+            node.set('class', tag_class)
+        if tag_style!= '':
+            node.set('style', tag_style)
+
+        node.text = markdown.util.AtomicString(m.group('text'))
+
+        return node
+
+class PelicanInlineMarkdownExtension(markdown.Extension):
+    """A Markdown extension enabling processing in Markdown for Pelican"""
+    def __init__(self, config):
+
+        try:
+            # Needed for Markdown versions >= 2.5
+            self.config['config'] = ['{}', 'config for markdown extension']
+            super(PelicanInlineMarkdownExtension,self).__init__(**config)
+        except AttributeError:
+            # Markdown versions < 2.5
+            config['config'] = [config['config'], 'config for markdown extension']
+            super(PelicanInlineMarkdownExtension, self).__init__(config)
+
+    def extendMarkdown(self, md, md_globals):
+        # Regex to detect mathjax
+        config = self.getConfig('config')
+        patterns = []
+
+        # The following mathjax settings can be set via the settings dictionary
+        for key in config:
+            patterns.append(re.escape(key))
+
+        inline_regex = r'(?P<prefix>%s)(?P<text>.+?)\2' % ('|'.join(patterns))
+
+        # Process after escapes
+        md.inlinePatterns.add('texthighlight_inlined', PelicanInlineMarkdownExtensionPattern(self, 'span', inline_regex), '>emphasis2')
diff --git a/pelican-plugins/members/Readme.rst b/pelican-plugins/members/Readme.rst
new file mode 100644
index 0000000000000000000000000000000000000000..ce3494dc09773ad93a43e7e678c2ec564f6f97d1
--- /dev/null
+++ b/pelican-plugins/members/Readme.rst
@@ -0,0 +1,22 @@
+Members
+-------
+
+This plugin looks for a ``members`` metadata header containing key/value pairs
+and makes them available for use in templates.
+
+The first line of this metadata defines each key, and the following line should
+contain corresponding values for each member.
+
+The keys must be in the same first line as the ``members`` metadata header,
+and the next line containing the corresponding values must have an identation
+before it.
+
+Example for reStructuredText::
+
+    :members: name, email, twitter, github, site_nome, site_href
+        Danilo Shiga, daniloshiga@gmail.com, @daneoshiga, daneoshiga, Danilo Shiga, http://daniloshiga.com
+
+Example for Markdown::
+
+    members: name, email, twitter, github, site_nome, site_href
+        Danilo Shiga, daniloshiga@gmail.com, @daneoshiga, daneoshiga, Danilo Shiga, http://daniloshiga.com
diff --git a/pelican-plugins/members/__init__.py b/pelican-plugins/members/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..3c0b42fb9fb4c323f6a0c67ea52814bbbc4af2a3
--- /dev/null
+++ b/pelican-plugins/members/__init__.py
@@ -0,0 +1 @@
+from members import *  # noqa
diff --git a/pelican-plugins/members/members.py b/pelican-plugins/members/members.py
new file mode 100644
index 0000000000000000000000000000000000000000..d0845191fc72b221cc69100b91f21686790a778c
--- /dev/null
+++ b/pelican-plugins/members/members.py
@@ -0,0 +1,38 @@
+"""
+Members info plugin for Pelican
+===============================
+
+This plugin looks for a ``members`` metadata header containing key/value pairs
+and makes them available for use in templates
+
+The first line of the members metadata defines each key, and the following
+lines contain corresponding values for each member.
+
+:members: nome, email, twitter, github, site_nome, site_href
+    Danilo Shiga, daniloshiga@gmail.com, @daneoshiga, daneoshiga, Danilo Shiga, http://daniloshiga.com
+"""
+
+from collections import OrderedDict
+
+from pelican import signals
+
+
+def add_members(generator, metadata):
+
+    if 'members' in metadata.keys():
+        # Dealing with differences on metadata for md and rst content
+        if type(metadata['members']) == list:
+            members = metadata['members']
+        else:
+            members = metadata['members'].splitlines()
+
+        metadata['members'] = OrderedDict()
+        keys = map(unicode.strip, members[0].split(','))
+        for member in members[1:]:
+            values = map(unicode.strip, member.split(','))
+            member_dict = dict(zip(keys, values))
+            metadata['members'][member_dict['nome']] = member_dict
+
+
+def register():
+    signals.page_generator_context.connect(add_members)
diff --git a/pelican-plugins/more_categories/README.md b/pelican-plugins/more_categories/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..85e996684ff6fd66b609b3cd609c4a3848184324
--- /dev/null
+++ b/pelican-plugins/more_categories/README.md
@@ -0,0 +1,79 @@
+# Subcategory Plugin
+
+**NOTE: [This plugin has been moved to its own repository](https://github.com/pelican-plugins/more-categories). Please file any issues/PRs there. Once all plugins have been migrated to the [new Pelican Plugins organization](https://github.com/pelican-plugins), this monolithic repository will be archived.**
+
+This plugin adds support for multiple categories per article, and for nested
+categories. It requires Pelican 4.0.0 or newer.
+
+## Multiple categories
+To indicate that an article belongs to multiple categories, use a
+comma-separated string:
+
+    Category: foo, bar, bazz
+
+This will add the article to the categories `foo`, `bar` and `bazz`.
+
+### Templates
+Existing themes that use `article.category` will display only the first of
+these categories, `foo`. This plugin adds `article.categories` that you can
+loop over instead. To accomodate this plugin in a theme whether it is present
+or not, use:
+
+    {% for cat in article.categories or [article.category] %}
+        <a href="{{ SITEURL }}/{{ cat.url }}">{{ cat }}</a>{{ ', ' if not loop.last }}
+    {% endfor %}
+
+## Nested categories
+(This is a reimplementation of the `subcategory` plugin.)
+
+To indicate that a category is a child of another category, use a
+slash-separated string:
+
+    Category: foo/bar/bazz
+
+This will add the article to the categories `foo/bar/bazz`, `foo/bar` and
+`foo`.
+
+### Templates
+Existing themes that use `article.category` will display the full path to the
+most specific of these categories, `foo/bar/bazz`. For any category `cat`, this
+plugin adds `cat.shortname`, which in this case is `bazz`, `cat.parent`, which
+in this case is the category `foo/bar`, and `cat.ancestors`, which is a list of
+the category's ancestors, ending with the category itself. For instance, to
+also include a link to each of the ancestor categories on an article page, in
+case this plugin in present, use:
+
+    {% for cat in article.category.ancestors or [article.category] %}
+        <a href="{{ SITEURL }}/{{ cat.url }}">{{ cat.shortname or cat }}</a>{{ '/' if not loop.last }}
+    {% endfor %}
+
+Likewise, `category.shortname`, `category.parent` and `category.ancestors` can
+also be used on the category template.
+
+### Slug
+The slug of a category is generated recursively by slugifying the shortname of
+the category and its ancestors, and preserving slashes:
+
+    slug-of-(foo/bar/baz) := slug-of-foo/slug-of-bar/slug-of-baz
+
+### Category folders
+To specify categories using the directory structure, you can configure
+`PATH_METADATA` to extract the article path into the `category` metadata. The
+following settings would use the entire structure:
+
+    PATH_METADATA = '(?P<category>.*)/.*'
+
+If you store all articles in a single `articles/` folder that you want to
+ignore for this purpose, use:
+
+    PATH_METADATA = 'articles/(?P<category>.*)/.*'
+
+### Categories in templates
+The list `categories` of all pairs of categories with their corresponding
+articles, which is available in the context and can be used in templates (e.g.
+to make a menu of available categories), is ordered lexicographically, so
+categories always follow their parent:
+
+    aba
+    aba/dat
+    abaala
diff --git a/pelican-plugins/more_categories/__init__.py b/pelican-plugins/more_categories/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..85fa31b046e82c861d8f4c4687bebb3417773886
--- /dev/null
+++ b/pelican-plugins/more_categories/__init__.py
@@ -0,0 +1 @@
+from .more_categories import *
\ No newline at end of file
diff --git a/pelican-plugins/more_categories/more_categories.py b/pelican-plugins/more_categories/more_categories.py
new file mode 100644
index 0000000000000000000000000000000000000000..945a1bc076aa091323d3df8ff58aeb4eb233c715
--- /dev/null
+++ b/pelican-plugins/more_categories/more_categories.py
@@ -0,0 +1,82 @@
+# -*- coding: utf-8 -*-
+"""
+Title: More Categories
+Description: adds support for multiple categories per article and nested
+categories
+Requirements: Pelican 3.8 or higher
+"""
+
+from pelican import signals
+from pelican.urlwrappers import URLWrapper
+from pelican.utils import slugify
+
+from collections import defaultdict
+from six import text_type
+
+class Category(URLWrapper):
+    @property
+    def _name(self):
+        if self.parent:
+            return self.parent._name + '/' + self.shortname
+        return self.shortname
+
+    @_name.setter
+    def _name(self, val):
+        if '/' in val:
+            parentname, val = val.rsplit('/', 1)
+            self.parent = self.__class__(parentname, self.settings)
+        else:
+            self.parent = None
+        self.shortname = val.strip()
+
+    @URLWrapper.name.setter
+    def name(self, val):
+        self._name = val
+
+    @property
+    def slug(self):
+        if self._slug is None:
+            if 'CATEGORY_REGEX_SUBSTITUTIONS' in self.settings:
+                subs = self.settings['CATEGORY_REGEX_SUBSTITUTIONS']
+            else:
+                subs = self.settings.get('SLUG_REGEX_SUBSTITUTIONS', [])
+            self._slug = slugify(self.shortname, regex_subs=subs)
+            if self.parent:
+                self._slug = self.parent.slug + '/' + self._slug
+        return self._slug
+
+    @property
+    def ancestors(self):
+        if self.parent:
+            return self.parent.ancestors + [self]
+        return [self]
+
+    def as_dict(self):
+        d = super(Category, self).as_dict()
+        d['shortname'] = self.shortname
+        return d
+
+
+def get_categories(generator, metadata):
+    categories = text_type(metadata.get('category')).split(',')
+    metadata['categories'] = [
+        Category(name, generator.settings) for name in categories]
+    metadata['category'] = metadata['categories'][0]
+
+
+def create_categories(generator):
+    generator.categories = []
+    cat_dct = defaultdict(list)
+    for article in generator.articles:
+        for cat in {a for c in article.categories for a in c.ancestors}:
+            cat_dct[cat].append(article)
+
+    generator.categories = sorted(
+        list(cat_dct.items()),
+        reverse=generator.settings.get('REVERSE_CATEGORY_ORDER') or False)
+    generator._update_context(['categories'])
+
+
+def register():
+    signals.article_generator_context.connect(get_categories)
+    signals.article_generator_finalized.connect(create_categories)
diff --git a/pelican-plugins/more_categories/test_data/article_with_multiple_categories.rst b/pelican-plugins/more_categories/test_data/article_with_multiple_categories.rst
new file mode 100644
index 0000000000000000000000000000000000000000..aa7ea80684e2e84d8b212bf5273ef00f5ed7bf41
--- /dev/null
+++ b/pelican-plugins/more_categories/test_data/article_with_multiple_categories.rst
@@ -0,0 +1,6 @@
+Article with multiple and nested categories
+===========================================
+:date: 2018-11-04
+:category: foo/bar, foo/b#az
+
+This is an article with multiple categories
diff --git a/pelican-plugins/more_categories/test_data/article_with_no_category.rst b/pelican-plugins/more_categories/test_data/article_with_no_category.rst
new file mode 100644
index 0000000000000000000000000000000000000000..756c54b693cd9b36c620b8707f97e22483aa0ef3
--- /dev/null
+++ b/pelican-plugins/more_categories/test_data/article_with_no_category.rst
@@ -0,0 +1,5 @@
+Article with no category
+========================
+:date: 2018-11-04
+
+This is an article with no category
diff --git a/pelican-plugins/more_categories/test_more_categories.py b/pelican-plugins/more_categories/test_more_categories.py
new file mode 100644
index 0000000000000000000000000000000000000000..45e7966fc123a46b154930b58d485da9e3c427f3
--- /dev/null
+++ b/pelican-plugins/more_categories/test_more_categories.py
@@ -0,0 +1,57 @@
+"""Unit tests for the more_categories plugin"""
+
+import os
+import unittest
+from shutil import rmtree
+from tempfile import mkdtemp
+
+from . import more_categories
+from pelican.generators import ArticlesGenerator
+from pelican.tests.support import get_context, get_settings
+
+
+class TestArticlesGenerator(unittest.TestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        cls.temp_path = mkdtemp(prefix='pelicantests.')
+        more_categories.register()
+        settings = get_settings()
+        settings['DEFAULT_CATEGORY'] = 'default'
+        settings['CACHE_CONTENT'] = False
+        settings['PLUGINS'] = more_categories
+        context = get_context(settings)
+
+        base_path = os.path.dirname(os.path.abspath(__file__))
+        test_data_path = os.path.join(base_path, 'test_data')
+        cls.generator = ArticlesGenerator(
+            context=context, settings=settings,
+            path=test_data_path, theme=settings['THEME'], output_path=cls.temp_path)
+        cls.generator.generate_context()
+
+    @classmethod
+    def tearDownClass(cls):
+        rmtree(cls.temp_path)
+
+    def test_generate_categories(self):
+        """Test whether multiple categories are generated correctly,
+        including ancestor categories"""
+
+        cats_generated = [cat.name for cat, _ in self.generator.categories]
+        cats_expected = ['default', 'foo', 'foo/bar', 'foo/b#az',]
+        self.assertEqual(sorted(cats_generated), sorted(cats_expected))
+
+    def test_categories_slug(self):
+        """Test whether category slug substitutions are used"""
+
+        slugs_generated = [cat.slug for cat, _ in self.generator.categories]
+        slugs_expected = ['default', 'foo', 'foo/bar', 'foo/baz',]
+        self.assertEqual(sorted(slugs_generated), sorted(slugs_expected))
+
+    def test_assign_articles_to_categories(self):
+        """Test whether articles are correctly assigned to categories,
+        including whether articles are not assigned multiple times to the same
+        ancestor category"""
+
+        for cat, articles in self.generator.categories:
+            self.assertEqual(len(articles), 1)
\ No newline at end of file
diff --git a/pelican-plugins/multi_part/Readme.md b/pelican-plugins/multi_part/Readme.md
new file mode 100644
index 0000000000000000000000000000000000000000..47b720f56670371ab8257189dacea27bf16a9bca
--- /dev/null
+++ b/pelican-plugins/multi_part/Readme.md
@@ -0,0 +1,26 @@
+Multi parts posts
+-----------------
+
+**This plugin has been deprecated. See the series plugin for a better support of multi part articles.**
+
+The multi-part posts plugin allow you to write multi-part posts.
+
+In order to mark posts as part of a multi-part post, use the `:parts:` metadata:
+
+    :parts:  MY_AWESOME_MULTI_PART_POST
+
+You can then use the `article.metadata.parts_articles` variable in your templates 
+to display other parts of current post.
+
+For example:
+
+    {% if article.metadata.parts_articles %}
+        <p>This post is part of a series:</p>
+        <ol class="parts">
+            {% for part_article in article.metadata.parts_articles %}
+                <li {% if part_article == article %}class="active"{% endif %}>
+                    <a href='{{ SITEURL }}/{{ part_article.url }}'>{{ part_article.title }}</a>
+                </li>
+            {% endfor %}
+        </ol>
+    {% endif %}
diff --git a/pelican-plugins/multi_part/__init__.py b/pelican-plugins/multi_part/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..6e00046bf6f588a54e8743ed2be1930b71cc4f8a
--- /dev/null
+++ b/pelican-plugins/multi_part/__init__.py
@@ -0,0 +1 @@
+from .multi_part import *
diff --git a/pelican-plugins/multi_part/multi_part.py b/pelican-plugins/multi_part/multi_part.py
new file mode 100644
index 0000000000000000000000000000000000000000..6f4a859ba9432251b2faabe8e3faba89919c5fe9
--- /dev/null
+++ b/pelican-plugins/multi_part/multi_part.py
@@ -0,0 +1,42 @@
+# -*- coding: utf-8 -*-
+"""
+Copyright (c) FELD Boris <lothiraldan@gmail.com>
+
+Multiple part support
+=====================
+
+Create a navigation menu for multi-part related_posts
+"""
+
+from collections import defaultdict
+
+from pelican import signals
+
+import logging
+
+log = logging.getLogger(__name__)
+
+warning_message = """multi_part plugin: this plugin has been deprecated.
+The 'series' plugin provides better support for multi part articles.
+"""
+
+def aggregate_multi_part(generator):
+    log.warning(warning_message)
+    multi_part = defaultdict(list)
+
+    for article in generator.articles:
+        if 'parts' in article.metadata:
+            multi_part[article.metadata['parts']].append(article)
+
+    for part_id in multi_part:
+        parts = multi_part[part_id]
+
+        # Sort by date
+        parts.sort(key=lambda x: x.metadata['date'])
+
+        for article in parts:
+            article.metadata['parts_articles'] = parts
+
+
+def register():
+    signals.article_generator_finalized.connect(aggregate_multi_part)
diff --git a/pelican-plugins/neighbors/Readme.rst b/pelican-plugins/neighbors/Readme.rst
new file mode 100644
index 0000000000000000000000000000000000000000..e9277fc88ac5cced8ff7176f86e455741a536405
--- /dev/null
+++ b/pelican-plugins/neighbors/Readme.rst
@@ -0,0 +1,101 @@
+Neighbor Articles Plugin for Pelican
+====================================
+
+**NOTE:** `This plugin has been moved to its own repository <https://github.com/pelican-plugins/neighbors>`_. Please file any issues/PRs there. Once all plugins have been migrated to the `new Pelican Plugins organization <https://github.com/pelican-plugins>`_, this monolithic repository will be archived.
+
+-------------------------------------------------------------------------------
+
+This plugin adds ``next_article`` (newer) and ``prev_article`` (older)
+variables to the article's context.
+
+Also adds ``next_article_in_category`` and ``prev_article_in_category``.
+
+
+Usage
+-----
+
+.. code-block:: html+jinja
+
+    <ul>
+    {% if article.prev_article %}
+        <li>
+            <a href="{{ SITEURL }}/{{ article.prev_article.url}}">
+                {{ article.prev_article.title }}
+            </a>
+        </li>
+    {% endif %}
+    {% if article.next_article %}
+        <li>
+            <a href="{{ SITEURL }}/{{ article.next_article.url}}">
+                {{ article.next_article.title }}
+            </a>
+        </li>
+    {% endif %}
+   </ul>
+   <ul>
+    {% if article.prev_article_in_category %}
+        <li>
+            <a href="{{ SITEURL }}/{{ article.prev_article_in_category.url}}">
+                {{ article.prev_article_in_category.title }}
+            </a>
+        </li>
+    {% endif %}
+    {% if article.next_article_in_category %}
+        <li>
+            <a href="{{ SITEURL }}/{{ article.next_article_in_category.url}}">
+                {{ article.next_article_in_category.title }}
+            </a>
+        </li>
+    {% endif %}
+    </ul>
+
+Usage with the Subcategory plugin
+---------------------------------
+
+If you want to get the neigbors within a subcategory it's a little different.
+Since an article can belong to more than one subcategory, subcategories are
+stored in a list. If you have an article with subcategories like
+
+``Category/Foo/Bar``
+
+it will belong to both subcategory Foo, and Foo/Bar. Subcategory neighbors are
+added to an article as ``next_article_in_subcategory#`` and
+``prev_article_in_subcategory#`` where ``#`` is the level of subcategory. So using
+the example from above, subcategory1 will be Foo, and subcategory2 Foo/Bar.
+Therefor the usage with subcategories is:
+
+.. code-block:: html+jinja
+
+    <ul>
+    {% if article.prev_article_in_subcategory1 %}
+        <li>
+            <a href="{{ SITEURL }}/{{ article.prev_article_in_subcategory1.url}}">
+                {{ article.prev_article_in_subcategory1.title }}
+            </a>
+        </li>
+    {% endif %}
+    {% if article.next_article_in_subcategory1 %}
+        <li>
+            <a href="{{ SITEURL }}/{{ article.next_article_in_subcategory1.url}}">
+                {{ article.next_article_in_subcategory1.title }}
+            </a>
+        </li>
+    {% endif %}
+   </ul>
+   <ul>
+    {% if article.prev_article_in_subcategory2 %}
+        <li>
+            <a href="{{ SITEURL }}/{{ article.prev_article_in_subcategory2.url}}">
+                {{ article.prev_article_in_subcategory2.title }}
+            </a>
+        </li>
+    {% endif %}
+    {% if article.next_article_in_subcategory2 %}
+        <li>
+            <a href="{{ SITEURL }}/{{ article.next_article_in_subcategory2.url}}">
+                {{ article.next_article_in_subcategory2.title }}
+            </a>
+        </li>
+    {% endif %}
+    </ul>
+
diff --git a/pelican-plugins/neighbors/__init__.py b/pelican-plugins/neighbors/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..9038d7e52455356ff29e8940febc8bb6f75dec22
--- /dev/null
+++ b/pelican-plugins/neighbors/__init__.py
@@ -0,0 +1 @@
+from .neighbors import *
diff --git a/pelican-plugins/neighbors/neighbors.py b/pelican-plugins/neighbors/neighbors.py
new file mode 100644
index 0000000000000000000000000000000000000000..71ea9ce52c1dcdf2d391503e6c7f2f8c1a995269
--- /dev/null
+++ b/pelican-plugins/neighbors/neighbors.py
@@ -0,0 +1,63 @@
+# -*- coding: utf-8 -*-
+"""
+Neighbor Articles Plugin for Pelican
+====================================
+
+This plugin adds ``next_article`` (newer) and ``prev_article`` (older)
+variables to the article's context
+"""
+from pelican import signals
+
+
+def iter3(seq):
+    """Generate one triplet per element in 'seq' following PEP-479."""
+    nxt, cur = None, None
+    for prv in seq:
+        if cur:
+            yield nxt, cur, prv
+        nxt, cur = cur, prv
+    # Don't yield anything if empty seq
+    if cur:
+        # Yield last element in seq (also if len(seq) == 1)
+        yield nxt, cur, None
+
+
+def get_translation(article, prefered_language):
+    if not article:
+        return None
+    for translation in article.translations:
+        if translation.lang == prefered_language:
+            return translation
+    return article
+
+
+def set_neighbors(articles, next_name, prev_name):
+    for nxt, cur, prv in iter3(articles):
+        setattr(cur, next_name, nxt)
+        setattr(cur, prev_name, prv)
+
+        for translation in cur.translations:
+            setattr(translation, next_name,
+                    get_translation(nxt, translation.lang))
+            setattr(translation, prev_name,
+                    get_translation(prv, translation.lang))
+
+def neighbors(generator):
+    set_neighbors(generator.articles, 'next_article', 'prev_article')
+
+    for category, articles in generator.categories:
+        articles.sort(key=lambda x: x.date, reverse=True)
+        set_neighbors(
+            articles, 'next_article_in_category', 'prev_article_in_category')
+
+    if hasattr(generator, 'subcategories'):
+        for subcategory, articles in generator.subcategories:
+            articles.sort(key=lambda x: x.date, reverse=True)
+            index = subcategory.name.count('/')
+            next_name = 'next_article_in_subcategory{}'.format(index)
+            prev_name = 'prev_article_in_subcategory{}'.format(index)
+            set_neighbors(articles, next_name, prev_name)
+
+
+def register():
+    signals.article_generator_finalized.connect(neighbors)
diff --git a/pelican-plugins/neighbors/test_data/article.md b/pelican-plugins/neighbors/test_data/article.md
new file mode 100644
index 0000000000000000000000000000000000000000..89b6980c3206e8cc737fdb8edc35fce59d48a76b
--- /dev/null
+++ b/pelican-plugins/neighbors/test_data/article.md
@@ -0,0 +1,14 @@
+Title: Test md File
+Category: test
+Tags: foo, bar, foobar
+Date: 2010-12-02 10:14
+Modified: 2010-12-02 10:20
+Summary: I have a lot to test
+
+Test Markdown File Header
+=========================
+
+Used for pelican test
+---------------------
+
+The quick brown fox jumped over the lazy dog's back.
diff --git a/pelican-plugins/neighbors/test_neighbors.py b/pelican-plugins/neighbors/test_neighbors.py
new file mode 100644
index 0000000000000000000000000000000000000000..f9865faab9b326db020e4f3e630fb06c0584e062
--- /dev/null
+++ b/pelican-plugins/neighbors/test_neighbors.py
@@ -0,0 +1,35 @@
+# -*- coding: utf-8 -*-
+from os.path import dirname, join
+from tempfile import TemporaryDirectory
+
+from pelican.generators import ArticlesGenerator
+from pelican.tests.support import get_settings, unittest
+
+from .neighbors import neighbors
+
+
+CUR_DIR = dirname(__file__)
+
+
+class NeighborsTest(unittest.TestCase):
+    def test_neighbors_basic(self):
+        with TemporaryDirectory() as tmpdirname:
+            generator = _build_article_generator(join(CUR_DIR, '..', 'test_data'), tmpdirname)
+            neighbors(generator)
+    def test_neighbors_with_single_article(self):
+        with TemporaryDirectory() as tmpdirname:
+            generator = _build_article_generator(join(CUR_DIR, 'test_data'), tmpdirname)
+            neighbors(generator)
+
+
+def _build_article_generator(content_path, output_path):
+    settings = get_settings(filenames={})
+    settings['PATH'] = content_path
+    context = settings.copy()
+    context['generated_content'] = dict()
+    context['static_links'] = set()
+    article_generator = ArticlesGenerator(
+        context=context, settings=settings,
+        path=settings['PATH'], theme=settings['THEME'], output_path=output_path)
+    article_generator.generate_context()
+    return article_generator
diff --git a/pelican-plugins/optimize_images/Readme.md b/pelican-plugins/optimize_images/Readme.md
new file mode 100644
index 0000000000000000000000000000000000000000..8efc9261791df981b7854d8976fb0c198e98659c
--- /dev/null
+++ b/pelican-plugins/optimize_images/Readme.md
@@ -0,0 +1,28 @@
+Optimize Images Plugin For Pelican
+==================================
+
+This plugin applies lossless compression on JPEG, PNG and SVG images, with no
+effect on image quality via [jpegtran][], [OptiPNG][] and [svgo][] respectively. 
+The plugin assumes that all of these tools are installed, with associated
+executables available on the system path.
+
+[jpegtran]: http://jpegclub.org/jpegtran/
+[OptiPNG]: http://optipng.sourceforge.net/
+[SVGO]: https://github.com/svg/svgo
+
+
+Installation
+------------
+
+To enable, ensure that `optimize_images.py` is put somewhere that is accessible.
+Then use as follows by adding the following to your settings.py:
+
+    PLUGIN_PATH = 'path/to/pelican-plugins'
+    PLUGINS = ["optimize_images"]
+
+`PLUGIN_PATH` can be a path relative to your settings file or an absolute path.
+
+Usage
+-----
+The plugin will activate and optimize images upon `finalized` signal of
+Pelican.
diff --git a/pelican-plugins/optimize_images/__init__.py b/pelican-plugins/optimize_images/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..cbb056ab4fc62e7e213687f24b13f34ac84ffd39
--- /dev/null
+++ b/pelican-plugins/optimize_images/__init__.py
@@ -0,0 +1 @@
+from .optimize_images import *
diff --git a/pelican-plugins/optimize_images/optimize_images.py b/pelican-plugins/optimize_images/optimize_images.py
new file mode 100644
index 0000000000000000000000000000000000000000..af252bdcc77c449a6b2008f6bceb64363c0e5d31
--- /dev/null
+++ b/pelican-plugins/optimize_images/optimize_images.py
@@ -0,0 +1,61 @@
+# -*- coding: utf-8 -*-
+
+"""
+Optimized images (jpg and png)
+Assumes that jpegtran and optipng are isntalled on path.
+http://jpegclub.org/jpegtran/
+http://optipng.sourceforge.net/
+Copyright (c) 2012 Irfan Ahmad (http://i.com.pk)
+"""
+
+import logging
+import os
+from subprocess import call
+
+from pelican import signals
+
+logger = logging.getLogger(__name__)
+
+# Display command output on DEBUG and TRACE
+SHOW_OUTPUT = logger.getEffectiveLevel() <= logging.DEBUG
+
+# A list of file types with their respective commands
+COMMANDS = {
+    # '.ext': ('command {flags} {filename', 'silent_flag', 'verbose_flag')
+    '.svg': ('svgo {flags} --input="{filename}" --output="{filename}"', '--quiet', ''),
+    '.jpg': ('jpegtran {flags} -copy none -optimize -progressive -outfile "{filename}" "{filename}"', '', '-v'),
+    '.png': ('optipng {flags} "{filename}"', '--quiet', ''),
+}
+
+
+def optimize_images(pelican):
+    """
+    Optimized jpg and png images
+
+    :param pelican: The Pelican instance
+    """
+    for dirpath, _, filenames in os.walk(pelican.settings['OUTPUT_PATH']):
+        for name in filenames:
+            if os.path.splitext(name)[1] in COMMANDS.keys():
+                optimize(dirpath, name)
+
+def optimize(dirpath, filename):
+    """
+    Check if the name is a type of file that should be optimized.
+    And optimizes it if required.
+
+    :param dirpath: Path of the file to be optimzed
+    :param name: A file name to be optimized
+    """
+    filepath = os.path.join(dirpath, filename)
+    logger.info('optimizing %s', filepath)
+
+    ext = os.path.splitext(filename)[1]
+    command, silent, verbose = COMMANDS[ext]
+    flags = verbose if SHOW_OUTPUT else silent
+    command = command.format(filename=filepath, flags=flags)
+    call(command, shell=True)
+
+
+def register():
+    signals.finalized.connect(optimize_images)
diff --git a/pelican-plugins/org_python_reader/LICENSE b/pelican-plugins/org_python_reader/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..f288702d2fa16d3cdf0035b15a9fcbc552cd88e7
--- /dev/null
+++ b/pelican-plugins/org_python_reader/LICENSE
@@ -0,0 +1,674 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) <year>  <name of author>
+    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<https://www.gnu.org/licenses/>.
+
+  The GNU General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.  But first, please read
+<https://www.gnu.org/licenses/why-not-lgpl.html>.
diff --git a/pelican-plugins/org_python_reader/README.org b/pelican-plugins/org_python_reader/README.org
new file mode 100644
index 0000000000000000000000000000000000000000..b025be76dbbc710515e001f489a45201f621f963
--- /dev/null
+++ b/pelican-plugins/org_python_reader/README.org
@@ -0,0 +1,74 @@
+* Org Python Reader plugin
+
+This plugin add the possibility to write your pages and blog posts in
+Org format from Emacs Org-mode.
+
+To generate your website, you don't need Emacs, Pandoc or any other
+software.
+
+
+** Dependency
+
+   This plugin only need =orgco= python package.
+   
+   You can install it with =pip=:
+   #+BEGIN_SRC sh
+   pip install orgco
+   #+END_SRC
+
+** Install
+
+   First, install this plugin in path know by pelican. Set pelican
+   variable =PLUGIN_PATHS= in =pelicanconf.py= if necessary.
+
+   Second, enable this plugin by adding his name to the pelican
+   variable =PLUGINS= in =pelicanconf.py=
+   
+   Exemple:
+   #+BEGIN_SRC python
+   PLUGINS = ['org_python_reader']
+   #+END_SRC
+
+** Settings
+
+   With this plugin, you define settings with the =ORGMODE= variable,
+   a dictionary.
+
+   Available settings:
+   - =code_highlight=: =True= or =False=, enable code highlight with
+     =orgco=. Default is =True=.
+
+   Exemple of settings:
+   #+BEGIN_SRC python
+     ORGMODE = {
+	 'code_highlight': False,
+     }
+   #+END_SRC
+
+** Usage
+
+   Simply write content in Org format.
+
+** More informations
+
+*** Org-mode
+    
+     Org-mode is an Emacs mode for keeping notes, maintaining TODO
+     lists, planning projects, and authoring documents with a fast and
+     effective plain-text system.
+
+     - [[http://orgmode.org][Org-mode website]]
+     - [[https://www.gnu.org/software/emacs/][Emacs website]]
+     - [[https://www.youtube.com/watch?v=SzA2YODtgK4][Getting Started With Org Mode]]
+
+*** Orgco
+
+    #+BEGIN_QUOTE
+    With orgco you can convert Emacs’ orgmode to other formats.
+    #+END_QUOTE
+
+    - [[https://github.com/paetzke/orgco][orgco website]]
+
+*** Author
+    Sébastien Gendre <seb@k-7.ch>
+    [[https://k-7.ch/][Website]]
diff --git a/pelican-plugins/org_python_reader/__init__.py b/pelican-plugins/org_python_reader/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..6c549ac5ac8114a1698366b5c527523926ea8274
--- /dev/null
+++ b/pelican-plugins/org_python_reader/__init__.py
@@ -0,0 +1,15 @@
+# Copyright (C) 2017  Sébastien Gendre
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+from .org_python_reader import *
diff --git a/pelican-plugins/org_python_reader/org_python_reader.py b/pelican-plugins/org_python_reader/org_python_reader.py
new file mode 100644
index 0000000000000000000000000000000000000000..d0efe746613b15add94b35760932de4b3ce2c70f
--- /dev/null
+++ b/pelican-plugins/org_python_reader/org_python_reader.py
@@ -0,0 +1,102 @@
+# Copyright (C) 2017  Sébastien Gendre
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+import re
+from orgco import convert_html
+from pelican import signals
+from pelican.readers import BaseReader
+from pelican.utils import pelican_open
+
+class OrgReader(BaseReader):
+    """Reader for Org files"""
+    enabled = True
+    file_extensions = ['org']
+
+    def __init__(self, *args, **kargs):
+        """Init object construct with this class"""
+        super(OrgReader, self).__init__(*args, **kargs)
+        settings = self.settings['ORGMODE']
+        settings.setdefault('code_highlight', True)
+        self.code_highlight = settings['code_highlight']
+    
+    def _separate_header_and_content(self, text_lines):
+        """
+        From a given Org text, return the header separate from the content.
+        The given text must be separate line by line and be a list.
+        The return is a list of two items: header and content.
+        Theses two items are text separate line by line in format of a list
+        Keyword Arguments:
+        text_lines -- A list, each item is a line of the texte
+        Return:
+        [
+          header   -- A list, each item is a line of the texte
+          content  -- A list, each item is a line of the texte
+        ]
+        """
+        no_more_header = False
+        expr_metadata = re.compile(r'^#\+[a-zA-Z]+:.*')
+        header = []
+        content = []
+        for line in text_lines:
+            metadata = expr_metadata.match(line)
+            if metadata and not no_more_header:
+                header.append(line)
+            else:
+                no_more_header = True
+                content.append(line)
+        return header, content 
+
+    def _parse_metadatas(self, text_lines):
+        """
+        From a given Org text, return the metadatas 
+        Keyword Arguments:
+        text_lines -- A list, each item is a line of the texte
+        Return:
+        A dict containing metadatas
+        """
+        if not text_lines:
+            return {}
+        expr_metadata = re.compile(r'^#\+([a-zA-Z]+):(.*)')
+        return {
+            expr_metadata.match(line).group(1).lower()
+            : expr_metadata.match(line).group(2).strip()
+            for line in text_lines
+        }
+
+    def read(self, source_path):
+        """
+        Parse content and metadata of Org files
+        Keyword Arguments:
+        source_path -- Path to the Org file to parse
+        """
+        with pelican_open(source_path) as text:
+            text_lines = list(text.splitlines())
+
+        header, content = self._separate_header_and_content(text_lines)
+        metadatas = self._parse_metadatas(header)
+        metadatas_processed = {
+            key
+            : self.process_metadata(key, value)
+            for key, value in metadatas.items()
+        }
+        content_html = convert_html("\n".join(content),
+                                    highlight=self.code_highlight)
+        return content_html, metadatas_processed
+    
+def add_reader(readers):
+    for ext in OrgReader.file_extensions:
+        readers.reader_classes[ext] = OrgReader
+
+def register():
+    signals.readers_init.connect(add_reader)
diff --git a/pelican-plugins/org_python_reader/requirements.txt b/pelican-plugins/org_python_reader/requirements.txt
new file mode 100755
index 0000000000000000000000000000000000000000..9781cf3cee3b6d2a74b01a4a5371f37e5fa7ffd7
--- /dev/null
+++ b/pelican-plugins/org_python_reader/requirements.txt
@@ -0,0 +1 @@
+orgco
\ No newline at end of file
diff --git a/pelican-plugins/org_reader/LICENSE b/pelican-plugins/org_reader/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..4d98b176fa6b4151bb0521beb2709ced61095cfd
--- /dev/null
+++ b/pelican-plugins/org_reader/LICENSE
@@ -0,0 +1,12 @@
+Copyright 2015 Matthew Snyder, http://msnyder.info
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/pelican-plugins/org_reader/README.md b/pelican-plugins/org_reader/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..c4a18a85cb5898587f34f7df15f76df60783b356
--- /dev/null
+++ b/pelican-plugins/org_reader/README.md
@@ -0,0 +1,44 @@
+# Org Emacs Reader
+
+Publish Emacs Org files alongside the rest of your website or blog.
+
+- `ORG_READER_EMACS_LOCATION`: Required. Location of Emacs binary.
+  If you use `Emacs for Mac OS X`,
+  the location should be `/Applications/Emacs.app/Contents/MacOS/Emacs`,
+  rather than `/usr/bin/emacs`.
+
+- `ORG_READER_EMACS_SETTINGS`: Optional. An absolute path to an Elisp file, to
+  run per invocation. Useful for initializing the `package` Emacs library if
+  that's where your Org mode comes from, or any modifications to Org Export-
+  related variables. If you want to use your standard emacs init file, you
+  can ignore this variable.
+
+- `ORG_READER_BACKEND`: Optional. A custom backend to provide to Org. Defaults
+  to `'html`.
+
+To provide metadata to Pelican, the following properties can be defined in
+the org file's header:
+
+    #+TITLE: The Title Of This BlogPost
+    #+DATE: 2001-01-01
+    #+CATEGORY: blog-category
+    #+AUTHOR: My Name
+    #+PROPERTY: LANGUAGE en
+    #+PROPERTY: SUMMARY hello, this is the description
+    #+PROPERTY: STATUS disable or enable document
+    #+PROPERTY: SLUG test_slug
+    #+PROPERTY: MODIFIED [2015-12-29 Di]
+    #+PROPERTY: TAGS my, first, tags
+    #+PROPERTY: SAVE_AS alternative_filename.html
+
+
+- The `TITLE` is the only mandatory header property
+- Timestamps (`DATE` and `MODIFIED`) are optional and can be either a string
+  of `%Y-%m-%d` or an org timestamp
+- The property names (`SUMMARY`, `SLUG`, `MODIFIED`, `TAGS`, `SAVE_AS`) can
+  be either lower-case or upper-case
+- The slug is automatically the filename of the Org file, if not explicitly
+  specified
+- It is not possible to pass an empty property to Pelican.  For this plugin,
+  it makes no difference if a property is present in the Org file and left
+  empty, or if it is not defined at all.
diff --git a/pelican-plugins/org_reader/__init__.py b/pelican-plugins/org_reader/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..a42b13be9f420b713e6a85aa27c764d00c9a51b0
--- /dev/null
+++ b/pelican-plugins/org_reader/__init__.py
@@ -0,0 +1 @@
+from .org_reader import *
diff --git a/pelican-plugins/org_reader/org_reader.el b/pelican-plugins/org_reader/org_reader.el
new file mode 100644
index 0000000000000000000000000000000000000000..abc9e80622331702fc43172bf911a14702b53950
--- /dev/null
+++ b/pelican-plugins/org_reader/org_reader.el
@@ -0,0 +1,59 @@
+(require 'json)
+(require 'org)
+(require 'ox)
+(require 'package)
+
+;; htmlize is needed for SRC blocks
+(setq package-load-list '((htmlize t)))
+(package-initialize)
+
+(defun org->pelican (filename backend)
+  (progn
+    (save-excursion
+      ; open org file
+      (find-file filename)
+
+      ; pre-process some metadata
+      (let (; extract org export properties
+            (org-export-env (org-export-get-environment))
+            ; convert MODIFIED prop to string
+            (modifiedstr (cdr (assoc-string "MODIFIED" org-keyword-properties t)))
+            ; prepare date property
+            (dateobj (car (plist-get (org-export-get-environment) ':date))))
+
+        ; check if #+TITLE: is given and give sensible error message if not
+        (if (symbolp (car (plist-get org-export-env :title)))
+            (error "Each page/article must have a #+TITLE: property"))
+
+        ; construct the JSON object
+        (princ (json-encode
+                (list
+                 ; org export environment
+                 :title (substring-no-properties
+                         (car (plist-get org-export-env :title)))
+                 ; if #+DATE is not given, dateobj is nil
+                 ; if #+DATE is a %Y-%m-%d string, dateobj is a string,
+                 ; and otherwise we assume #+DATE is a org timestamp
+                 :date (if (symbolp dateobj)
+                           ""
+                         (if (stringp dateobj)
+                             (org-read-date nil nil dateobj nil)
+                           (org-timestamp-format dateobj "%Y-%m-%d")))
+
+                 :author (substring-no-properties
+                          (car (plist-get org-export-env ':author)))
+
+                 ; org file properties
+                 :category (cdr (assoc-string "CATEGORY" org-keyword-properties t))
+
+                 ; custom org file properties, defined as #+PROPERTY: NAME ARG
+                 :language (cdr (assoc-string "LANGUAGE" org-keyword-properties t))
+                 :save_as (cdr (assoc-string "SAVE_AS" org-keyword-properties t))
+                 :tags (cdr (assoc-string "TAGS" org-keyword-properties t))
+                 :summary (cdr (assoc-string "SUMMARY" org-keyword-properties t))
+                 :status (cdr (assoc-string "STATUS" org-keyword-properties t))
+                 :slug (cdr (assoc-string "SLUG" org-keyword-properties t))
+                 :modified (if (stringp modifiedstr)
+                               (org-read-date nil nil modifiedstr nil)
+                             "")
+                 :post (org-export-as backend nil nil t))))))))
diff --git a/pelican-plugins/org_reader/org_reader.py b/pelican-plugins/org_reader/org_reader.py
new file mode 100644
index 0000000000000000000000000000000000000000..bc04657f3e583f68be49c2adb55a6d086fa8a9f5
--- /dev/null
+++ b/pelican-plugins/org_reader/org_reader.py
@@ -0,0 +1,122 @@
+"""
+Org Reader
+==========
+
+Version 1.1.
+
+Relevant Pelican settings:
+
+- ORG_READER_EMACS_LOCATION: Required. Location of Emacs binary.
+
+- ORG_READER_EMACS_SETTINGS: Optional. An absolute path to an Elisp file, to
+  run per invocation. Useful for initializing the `package` Emacs library if
+  that's where your Org mode comes from, or any modifications to Org Export-
+  related variables.
+
+- ORG_READER_BACKEND: Optional. A custom backend to provide to Org. Defaults
+  to 'html.
+
+To provide metadata to Pelican, the following properties can be defined in
+the org file's header:
+
+#+TITLE: The Title Of This BlogPost
+#+DATE: 2001-01-01
+#+CATEGORY: blog-category
+#+AUTHOR: My Name
+#+PROPERTY: LANGUAGE en
+#+PROPERTY: SUMMARY hello, this is the description
+#+PROPERTY: SLUG test_slug
+#+PROPERTY: MODIFIED [2015-12-29 Di]
+#+PROPERTY: TAGS my, first, tags
+#+PROPERTY: SAVE_AS alternative_filename.html
+
+- The TITLE is the only mandatory header property
+- Timestamps (DATE and MODIFIED) are optional and can be either a string of
+  %Y-%m-%d or an org timestamp
+- The property names (SUMMARY, SLUG, MODIFIED, TAGS, SAVE_AS) can be either
+  lower-case or upper-case
+- The slug is automatically the filename of the Org file, if not explicitly
+  specified
+- It is not possible to pass an empty property to Pelican.  For this plugin,
+  it makes no difference if a property is present in the Org file and left
+  empty, or if it is not defined at all.
+
+"""
+import os
+import json
+import logging
+import subprocess
+from pelican import readers
+from pelican import signals
+
+
+ELISP = os.path.join(os.path.dirname(__file__), 'org_reader.el')
+LOG = logging.getLogger(__name__)
+
+
+class OrgEmacsReader(readers.BaseReader):
+    enabled = True
+
+    EMACS_ARGS = ["-Q", "--batch"]
+    ELISP_EXEC = "(org->pelican \"{0}\" {1})"
+
+    file_extensions = ['org']
+
+    def read(self, filename):
+        assert 'ORG_READER_EMACS_LOCATION' in self.settings, \
+            "No ORG_READER_EMACS_LOCATION specified in settings"
+        LOG.info("Reading Org file {0}".format(filename))
+        cmd = [self.settings['ORG_READER_EMACS_LOCATION']]
+        cmd.extend(self.EMACS_ARGS)
+
+        if 'ORG_READER_EMACS_SETTINGS' in self.settings:
+            cmd.append('-l')
+            cmd.append(self.settings['ORG_READER_EMACS_SETTINGS'])
+
+        backend = self.settings.get('ORG_READER_BACKEND', "'html")
+
+        cmd.append('-l')
+        cmd.append(ELISP)
+
+        cmd.append('--eval')
+        cmd.append(self.ELISP_EXEC.format(filename, backend))
+
+        LOG.debug("OrgEmacsReader: running command `{0}`".format(cmd))
+
+        json_result = subprocess.check_output(cmd, universal_newlines=True)
+        json_output = json.loads(json_result)
+
+        # get default slug from .org filename
+        default_slug, _ = os.path.splitext(os.path.basename(filename))
+
+        metadata = {'title': json_output['title'] or '',
+                    'date': json_output['date'] or '',
+                    'author': json_output['author'] or '',
+                    'lang': json_output['language'] or '',
+                    'category': json_output['category'] or '',
+                    'slug': json_output['slug'] or default_slug,
+                    'modified': json_output['modified'] or '',
+                    'tags': json_output['tags'] or '',
+                    'save_as': json_output['save_as'] or '',
+                    'summary': json_output['summary'] or '',
+                    'status': json_output['status'] or ''}
+        # remove empty strings when necessary
+        for key in ['save_as', 'modified', 'lang', 'summary']:
+            if not metadata[key]:
+                metadata.pop(key)
+
+        parsed = {}
+        for key, value in metadata.items():
+            parsed[key] = self.process_metadata(key, value)
+
+        content = json_output['post']
+
+        return content, parsed
+
+
+def add_reader(readers):
+    readers.reader_classes['org'] = OrgEmacsReader
+
+
+def register():
+    signals.readers_init.connect(add_reader)
diff --git a/pelican-plugins/pdf/Readme.rst b/pelican-plugins/pdf/Readme.rst
new file mode 100644
index 0000000000000000000000000000000000000000..18bf8ee9ecee9cba54ae3d22ca1078d182cd0c4c
--- /dev/null
+++ b/pelican-plugins/pdf/Readme.rst
@@ -0,0 +1,43 @@
+-------------
+PDF Generator
+-------------
+
+**NOTE:** `This plugin has been moved to its own repository <https://github.com/pelican-plugins/pdf>`_. Please file any issues/PRs there. Once all plugins have been migrated to the `new Pelican Plugins organization <https://github.com/pelican-plugins>`_, this monolithic repository will be archived.
+
+The PDF Generator plugin automatically exports articles and pages as PDF files
+as part of the site generation process. PDFs are saved to:
+``output/pdf/``
+
+Requirements
+------------
+
+You should ensure you have the ``rst2pdf`` module installed, which can be
+accomplished via::
+
+	pip install rst2pdf
+
+If you are converting Markdown sources to PDF, you also need the ``xhtml2pdf``
+module, which can be installed with::
+
+	pip install xhtml2pdf
+
+Usage
+-----
+
+To customize the PDF output, you can use the following settings in your
+Pelican configuration file::
+
+	PDF_STYLE = ''
+	PDF_STYLE_PATH = ''
+
+``PDF_STYLE_PATH`` defines a new path where ``rst2pdf`` will look for style
+sheets, while ``PDF_STYLE`` specifies the style you want to use. For a
+description of the available styles, please read the `rst2pdf documentation`_.
+
+.. _rst2pdf documentation: http://rst2pdf.ralsina.me/handbook.html#styles
+
+Known Issues
+------------
+
+Relative links in the form of ``|filename|images/test.png`` are not yet handled
+by the PDF generator.
diff --git a/pelican-plugins/pdf/__init__.py b/pelican-plugins/pdf/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..3920e0360e3b74def5d232e00fe9361fc45e55f7
--- /dev/null
+++ b/pelican-plugins/pdf/__init__.py
@@ -0,0 +1 @@
+from .pdf import *
diff --git a/pelican-plugins/pdf/pdf.py b/pelican-plugins/pdf/pdf.py
new file mode 100644
index 0000000000000000000000000000000000000000..a981d636ac081a998ed47002345c8e13b3007611
--- /dev/null
+++ b/pelican-plugins/pdf/pdf.py
@@ -0,0 +1,125 @@
+# -*- coding: utf-8 -*-
+'''
+PDF Generator
+-------
+
+The pdf plugin generates PDF files from reStructuredText and Markdown sources.
+'''
+
+from __future__ import unicode_literals, print_function
+
+from io import open
+from pelican import signals
+from pelican.generators import Generator
+from pelican.readers import MarkdownReader
+
+import os
+import logging
+
+logger = logging.getLogger(__name__)
+
+import xhtml2pdf.util
+if 'pyPdf' not in dir(xhtml2pdf.util):
+    try:
+        from xhtml2pdf.util import PyPDF2
+        xhtml2pdf.util.pyPdf = PyPDF2
+    except ImportError:
+        logger.error('Failed to monkeypatch xhtml2pdf. ' +
+                     'You have missing dependencies')
+        raise
+
+from rst2pdf.createpdf import RstToPdf
+
+
+class PdfGenerator(Generator):
+    "Generate PDFs on the output dir, for all articles and pages"
+
+    supported_md_fields = ['date']
+
+    def __init__(self, *args, **kwargs):
+        super(PdfGenerator, self).__init__(*args, **kwargs)
+
+        if 'PDF_STYLE_PATH' in self.settings:
+            pdf_style_path = [self.settings['PDF_STYLE_PATH']]
+        else:
+            pdf_style_path = []
+
+        if 'PDF_STYLE' in self.settings:
+            pdf_style = [self.settings['PDF_STYLE']]
+        else:
+            pdf_style = []
+
+        self.pdfcreator = RstToPdf(breakside=0,
+                                   stylesheets=pdf_style,
+                                   style_path=pdf_style_path,
+                                   raw_html=True)
+
+    def _create_pdf(self, obj, output_path):
+        filename = obj.slug + '.pdf'
+        output_pdf = os.path.join(output_path, filename)
+        mdreader = MarkdownReader(self.settings)
+        _, ext = os.path.splitext(obj.source_path)
+        if ext == '.rst':
+            with open(obj.source_path, encoding='utf-8') as f:
+                text = f.read()
+            header = ''
+        elif ext[1:] in mdreader.file_extensions and mdreader.enabled:
+            text, meta = mdreader.read(obj.source_path)
+            header = ''
+
+            if 'title' in meta:
+                title = meta['title']
+                header = title + '\n' + '#' * len(title) + '\n\n'
+                del meta['title']
+
+            for k in meta.keys():
+                # We can't support all fields, so we strip the ones that won't
+                # look good
+                if k not in self.supported_md_fields:
+                    del meta[k]
+
+            header += '\n'.join([':%s: %s' % (k, meta[k]) for k in meta])
+            header += '\n\n.. raw:: html\n\n\t'
+            text = text.replace('\n', '\n\t')
+
+            # rst2pdf casts the text to str and will break if it finds
+            # non-escaped characters. Here we nicely escape them to XML/HTML
+            # entities before proceeding
+            text = text.encode('ascii', 'xmlcharrefreplace').decode()
+        else:
+            # We don't support this format
+            logger.warn('Ignoring unsupported file ' + obj.source_path)
+            return
+
+        logger.info(' [ok] writing %s' % output_pdf)
+        self.pdfcreator.createPdf(text=(header+text),
+                                  output=output_pdf)
+
+    def generate_context(self):
+        pass
+
+    def generate_output(self, writer=None):
+        # we don't use the writer passed as argument here
+        # since we write our own files
+        logger.info(' Generating PDF files...')
+        pdf_path = os.path.join(self.output_path, 'pdf')
+        if not os.path.exists(pdf_path):
+            try:
+                os.mkdir(pdf_path)
+            except OSError:
+                logger.error("Couldn't create the pdf output folder in " +
+                             pdf_path)
+
+        for article in self.context['articles']:
+            self._create_pdf(article, pdf_path)
+
+        for page in self.context['pages']:
+            self._create_pdf(page, pdf_path)
+
+
+def get_generators(generators):
+    return PdfGenerator
+
+
+def register():
+    signals.get_generators.connect(get_generators)
diff --git a/pelican-plugins/pdf/requirements.txt b/pelican-plugins/pdf/requirements.txt
new file mode 100755
index 0000000000000000000000000000000000000000..576b757585a00e3aeae456cac8c5f59ddb25dcd6
--- /dev/null
+++ b/pelican-plugins/pdf/requirements.txt
@@ -0,0 +1,3 @@
+# Using git version as no Python3-compatible exists on Pypi: https://github.com/rst2pdf/rst2pdf/issues/799
+git+https://github.com/rst2pdf/rst2pdf.git
+xhtml2pdf
\ No newline at end of file
diff --git a/pelican-plugins/pdf/test_pdf.py b/pelican-plugins/pdf/test_pdf.py
new file mode 100644
index 0000000000000000000000000000000000000000..daa61b1a8b1d76d7ca8f36330f90966f75ad6294
--- /dev/null
+++ b/pelican-plugins/pdf/test_pdf.py
@@ -0,0 +1,47 @@
+import unittest
+import os
+import locale
+import logging
+import pdf
+
+from tempfile import mkdtemp
+from pelican import Pelican
+from pelican.readers import MarkdownReader
+from pelican.settings import read_settings
+from shutil import rmtree
+
+CUR_DIR = os.path.dirname(__file__)
+
+
+class TestPdfGeneration(unittest.TestCase):
+    def setUp(self, override=None):
+        self.temp_path = mkdtemp(prefix='pelicantests.')
+        settings = {
+            'PATH': os.path.join(CUR_DIR, '..', 'test_data', 'content'),
+            'OUTPUT_PATH': self.temp_path,
+            'PLUGINS': [pdf],
+            'LOCALE': locale.normalize('en_US'),
+        }
+        if override:
+            settings.update(override)
+
+        self.settings = read_settings(override=settings)
+        pelican = Pelican(settings=self.settings)
+
+        try:
+            pelican.run()
+        except ValueError:
+            logging.warn('Relative links in the form of ' +
+                         '|filename|images/test.png are not yet handled by ' +
+                         ' the pdf generator')
+            pass
+
+    def tearDown(self):
+        rmtree(self.temp_path)
+
+    def test_existence(self):
+        assert os.path.exists(os.path.join(self.temp_path, 'pdf',
+                                           'this-is-a-super-article.pdf'))
+        if MarkdownReader.enabled:
+            assert os.path.exists(os.path.join(self.temp_path, 'pdf',
+                                  'a-markdown-powered-article.pdf'))
diff --git a/pelican-plugins/pelican-cite/LICENSE b/pelican-plugins/pelican-cite/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..733c072369ca77331f392c40da7404c85c36542c
--- /dev/null
+++ b/pelican-plugins/pelican-cite/LICENSE
@@ -0,0 +1,675 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    {one line to give the program's name and a brief idea of what it does.}
+    Copyright (C) {year}  {name of author}
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    {project}  Copyright (C) {year}  {fullname}
+    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+  The GNU General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.  But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
+
diff --git a/pelican-plugins/pelican-cite/README.md b/pelican-plugins/pelican-cite/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..2c67a40fb2454f8d5812ac07ed17cbb064ad5821
--- /dev/null
+++ b/pelican-plugins/pelican-cite/README.md
@@ -0,0 +1,57 @@
+pelican-cite
+==============
+
+Allows the use of BibTeX citations within a Pelican site. 
+
+## Installation
+
+Clone the git repository and put it in a directory listed in ``PLUGIN_PATHS`` in your ``pelicanconf.py``:
+
+```bash
+git clone https://github.com/VorpalBlade/pelican-cite.git
+```
+
+Then install the dependency `pybtex`:
+
+```bash
+pip install pybtex
+```
+
+How to Use
+==========
+
+This plugin reads a user-specified BibTeX file and generates bibliographic
+information within your articles and pages.
+
+Configuration is simply:
+
+```python
+PUBLICATIONS_SRC = 'content/pubs.bib'
+```
+
+If the file is present and readable, then content will be scanned for references
+to citation keys. These take the format `[@Bai2011]` or `[@@Bai2011]`. These
+will be replaced by incline citations which provide links to the full
+bibliographic information at the end of the article. The former reference would
+be replaced by a citation of the form "Bai & Stone (2011)", while the latter
+would be replaced by "(Bai & Stone, 2011)". 
+
+If a citation key is used which does not exist within the BibTeX file then
+a warning will be displayed.
+
+The BibTeX file may, optionally, be provided or overridden on a per-article
+basis by supplying the meta-data `publications_src`.
+
+The HTML code for the start and end of the bibliography section can be replaced via
+setting `BIBLIOGRAPHY_START` and `BIBLIOGRAPHY_END`. For example: 
+
+```python
+BIBLIOGRAPHY_START = '<section id="bib"><h1>My awesome bibliography</h1>'
+BIBLIOGRAPHY_END = '</section>' 
+``` 
+
+Attribution
+===========
+`pelican-cite` is based on the
+[pelican-bibtex](https://github.com/vene/pelican-bibtex) plugin written by
+[Vlad Niculae](https://github.com/vene).
diff --git a/pelican-plugins/pelican-cite/__init__.py b/pelican-plugins/pelican-cite/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..1b365702b537fa38151ce5bd937bb6dead6a4b71
--- /dev/null
+++ b/pelican-plugins/pelican-cite/__init__.py
@@ -0,0 +1,3 @@
+# This is a compatibility shim to support using this package directly with
+# Pelican's import system (instead of installing from PyPI).
+from .src.pelican_cite import *
diff --git a/pelican-plugins/pelican-cite/pelican-cite-master/.gitignore b/pelican-plugins/pelican-cite/pelican-cite-master/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..768b3cd874f4eff301c7aedfd40fb57a88e7902f
--- /dev/null
+++ b/pelican-plugins/pelican-cite/pelican-cite-master/.gitignore
@@ -0,0 +1,13 @@
+*.pyc
+*.pyo
+*.swp
+*\.egg-info/
+*~
+.coverage*
+\.eggs/
+__pycache__
+build/
+dist/
+papers.bib
+pip-wheel-metadata/
+test-blog
diff --git a/pelican-plugins/pelican-cite/pyproject.toml b/pelican-plugins/pelican-cite/pyproject.toml
new file mode 100644
index 0000000000000000000000000000000000000000..121a39f5f2396a29f551d56ecbb2214d900120b6
--- /dev/null
+++ b/pelican-plugins/pelican-cite/pyproject.toml
@@ -0,0 +1,3 @@
+[build-system]
+requires = ["setuptools >= 40.6.0", "wheel"]
+build-backend = "setuptools.build_meta"
diff --git a/pelican-plugins/pelican-cite/setup.py b/pelican-plugins/pelican-cite/setup.py
new file mode 100755
index 0000000000000000000000000000000000000000..0f1881b9b1a5fe728029ab4ba6cfcb6cfbdb13b1
--- /dev/null
+++ b/pelican-plugins/pelican-cite/setup.py
@@ -0,0 +1,35 @@
+from os.path import abspath, dirname, join, normpath
+
+import setuptools
+
+setuptools.setup(
+    # Basic package information:
+    name='pelican_cite',
+    version='1.0.0',
+    packages=setuptools.find_packages('src'),
+    package_dir={'': 'src'},
+
+
+    # Packaging options:
+    include_package_data=True,
+
+    # Package dependencies:
+    install_requires=['pelican>=4.0', 'pybtex'],
+
+    # Metadata for PyPI:
+    author='Arvid Norlander',
+    author_email='VorpalBlade@users.noreply.github.com',
+    license='GPL-3.0',
+    url='https://github.com/VorpalBlade/pelican-cite',
+    keywords='pelican blog static bibtex citation',
+    description='Allows the use of BibTeX citations within a Pelican site.',
+    long_description=open(
+        normpath(join(dirname(abspath(__file__)), 'README.md'))).read(),
+    long_description_content_type="text/markdown",
+    classifiers=[
+        "Programming Language :: Python :: 3",
+        "License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
+        "Operating System :: OS Independent",
+        "Framework :: Pelican :: Plugins",
+    ],
+)
diff --git a/pelican-plugins/pelican-cite/src/__init__.py b/pelican-plugins/pelican-cite/src/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/pelican-plugins/pelican-cite/src/pelican_cite/__init__.py b/pelican-plugins/pelican-cite/src/pelican_cite/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..3653c5e79afcf424535d23a1ab30337be8757613
--- /dev/null
+++ b/pelican-plugins/pelican-cite/src/pelican_cite/__init__.py
@@ -0,0 +1 @@
+from .pelican_cite import *
diff --git a/pelican-plugins/pelican-cite/src/pelican_cite/author_year.py b/pelican-plugins/pelican-cite/src/pelican_cite/author_year.py
new file mode 100644
index 0000000000000000000000000000000000000000..15b5e19dec3301f40b0a8371df12bd00c65e30bf
--- /dev/null
+++ b/pelican-plugins/pelican-cite/src/pelican_cite/author_year.py
@@ -0,0 +1,193 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2015 Chris MacMackin
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+"""
+Defines an author-year inline citation style for Pybtex. This is 
+modified from the alpha style built into Pybtex, written by
+Andrey Golovizin.
+"""
+
+import re
+import sys
+import unicodedata
+from collections import Counter
+from pybtex.style.labels import BaseLabelStyle
+from pybtex.textutils import abbreviate
+
+_nonalnum_pattern = re.compile(r'[^\w]+', re.UNICODE)
+
+
+def _strip_accents(s):
+    return u''.join(
+        (c for c in unicodedata.normalize('NFD', s)
+         if not unicodedata.combining(c)))
+
+
+def _strip_nonalnum(parts):
+    """Strip all non-alphanumerical characters from a list of strings.
+
+    >>> _strip_nonalnum([u"Ã…A. B. Testing 12+}[.@~_", u" 3%"])
+    u'AABTesting123'
+    """
+    s = u''.join(parts)
+    return _nonalnum_pattern.sub(u'', s)
+
+
+class LabelStyle(BaseLabelStyle):
+    name = 'alpha'
+
+    def format_labels(self, sorted_entries):
+        labels = [self.format_label(entry) for entry in sorted_entries]
+        for i in range(len(labels)):
+            labels[i] = labels[i].replace('\\{', '&#123;')
+            labels[i] = labels[i].replace('\\}', '&#125;')
+            labels[i] = labels[i].replace('{', '')
+            labels[i] = labels[i].replace('}', '')
+        count = Counter(labels)
+        counted = Counter()
+        for label in labels:
+            if count[label]:
+                yield '(' + label + ')'
+            else:
+                yield '(' + label + chr(ord('a') + counted[label]) + ')'
+                counted.update([label])
+
+    # note: this currently closely follows the alpha.bst code
+    # we should eventually refactor it
+
+    def format_label(self, entry):
+        # see alpha.bst calc.label
+        if entry.type == "book" or entry.type == "inbook":
+            label = self.author_editor_key_label(entry)
+        elif entry.type == "proceedings":
+            label = self.editor_key_organization_label(entry)
+        elif entry.type == "manual":
+            label = self.author_key_organization_label(entry)
+        else:
+            label = self.author_key_label(entry)
+        if "year" in entry.fields:
+            return label.strip() + ', ' + entry.fields["year"]
+        else:
+            return label.strip()
+        # bst additionally sets sort.label
+
+    def author_key_label(self, entry):
+        # see alpha.bst author.key.label
+        if "author" not in entry.persons:
+            if "key" not in entry.fields:
+                return entry.key[:]  # entry.key is bst cite$
+            else:
+                # for entry.key, bst actually uses text.prefix$
+                return entry.fields["key"][:]
+        else:
+            return self.format_lab_names(entry.persons["author"])
+
+    def author_editor_key_label(self, entry):
+        # see alpha.bst author.editor.key.label
+        if "author" not in entry.persons:
+            if "editor" not in entry.persons:
+                if "key" not in entry.fields:
+                    return entry.key[:]  # entry.key is bst cite$
+                else:
+                    # for entry.key, bst actually uses text.prefix$
+                    return entry.fields["key"][:]
+            else:
+                return self.format_lab_names(entry.persons["editor"])
+        else:
+            return self.format_lab_names(entry.persons["author"])
+
+    def author_key_organization_label(self, entry):
+        if "author" not in entry.persons:
+            if "key" not in entry.fields:
+                if "organization" not in entry.fields:
+                    return entry.key[:]  # entry.key is bst cite$
+                else:
+                    result = entry.fields["organization"]
+                    if result.startswith("The "):
+                        result = result[4:]
+                    return result
+            else:
+                return entry.fields["key"][:]
+        else:
+            return self.format_lab_names(entry.persons["author"])
+
+    def editor_key_organization_label(self, entry):
+        if "editor" not in entry.persons:
+            if "key" not in entry.fields:
+                if "organization" not in entry.fields:
+                    return entry.key[:]  # entry.key is bst cite$
+                else:
+                    result = entry.fields["organization"]
+                    if result.startswith("The "):
+                        result = result[4:]
+                    return result
+            else:
+                return entry.fields["key"][:]
+        else:
+            return self.format_lab_names(entry.persons["editor"])
+
+    @staticmethod
+    def format_lab_names(persons):
+        # see alpha.bst format.lab.names
+        # s = persons
+        numnames = len(persons)
+        if numnames > 1:
+            if numnames > 2:
+                namesleft = 1
+            else:
+                namesleft = numnames
+            result = ""
+            nameptr = 1
+            while namesleft:
+                person = persons[nameptr - 1]
+                if nameptr == numnames:
+                    if str(person) == "others":
+                        result += "et al. "
+                    else:
+                        result += _strip_nonalnum(
+                            [abbreviate(name) for name in person.prelast()]
+                            + [' ']
+                            + person.last()
+                        )
+                else:
+                    result += _strip_nonalnum(
+                        [abbreviate(name) for name in person.prelast()]
+                        + [' ']
+                        + person.last()
+                    )
+                if numnames == 2 and nameptr == 1:
+                    result += ' and '
+                else:
+                    result += ' '
+                nameptr += 1
+                namesleft -= 1
+            if numnames > 2:
+                result += "et al."
+        else:
+            person = persons[0]
+            result = _strip_nonalnum(
+                [abbreviate(name) for name in person.prelast()]
+                + [' ']
+                + person.last()
+            )
+        return result
diff --git a/pelican-plugins/pelican-cite/src/pelican_cite/pelican_cite.py b/pelican-plugins/pelican-cite/src/pelican_cite/pelican_cite.py
new file mode 100644
index 0000000000000000000000000000000000000000..c4a4ff3f102f3ebd26dd70c4411a263941511333
--- /dev/null
+++ b/pelican-plugins/pelican-cite/src/pelican_cite/pelican_cite.py
@@ -0,0 +1,254 @@
+# -*- coding: utf-8 -*-
+"""
+pelican-cite
+==============
+
+A Pelican plugin that provices a BibTeX-style reference system within
+pelican sites.
+
+Based on teh Pelican BibTeX plugin written by Vlad Niculae <vlad@vene.ro>
+"""
+
+import logging
+import re
+import sys
+
+try:
+    from pybtex.database.input.bibtex import Parser
+    from pybtex.database.output.bibtex import Writer
+    from pybtex.database import BibliographyData, PybtexError, Entry
+    from pybtex.backends import html
+    from pybtex.style.formatting import toplevel
+    from pybtex.style.formatting.unsrt import dashify, Style as UnsrtStyle
+    from pybtex.style.template import (
+        join, words, field, optional, first_of, sentence, tag, optional_field,
+    )
+    from pybtex.plugin import find_plugin
+
+    pyb_imported = True
+except ImportError:
+    pyb_imported = False
+
+from pelican import signals
+from pelican.contents import Static
+from .author_year import LabelStyle
+
+__version__ = '1.0.0'
+
+JUMP_BACK = '<a class="cite-backref" href="#ref-{0}-{1}" title="Jump back to reference {1}">{2}</a>'
+CITE_RE = re.compile(r"\[&#64;(&#64;)?\s*(\w.*?)\s*\]")
+DATE_RE = re.compile(r"(?P<y>\d{4})(?:-(?P<m>\d{1,2})(?:-(?P<d>\d{1,2}))?)?")
+CITE_2_RE = re.compile(r">\s*\(\s*(.*?),\s*(.*?)\s*\)\s*<")
+
+
+class Style(UnsrtStyle):
+    name = 'inline'
+    default_sorting_style = 'author_year_title'
+    default_label_style = 'author_year'
+
+    def __init__(self, label_style=None, name_style=None, sorting_style=None, abbreviate_names=False, **kwargs):
+        self.name_style = find_plugin('pybtex.style.names', name_style or self.default_name_style)()
+        self.label_style = LabelStyle()
+        self.sorting_style = find_plugin('pybtex.style.sorting', sorting_style or self.default_sorting_style)()
+        self.format_name = self.name_style.format
+        self.format_labels = self.label_style.format_labels
+        self.sort = self.sorting_style.sort
+        self.abbreviate_names = abbreviate_names
+
+    def get_article_template(self, e):
+        pages = field('pages', apply_func=dashify)
+        date = words[optional_field('month'), field('year')]
+        volume_and_pages = first_of[
+            # volume and pages, with optional issue number
+            optional[
+                join[
+                    field('volume'),
+                    optional['(', field('number'), ')'],
+                    ':', pages
+                ],
+            ],
+            # pages only
+            words['pages', pages],
+        ]
+        template = toplevel[
+            self.format_names('author'),
+            self.format_title(e, 'title'),
+            sentence[
+                tag('em')[first_of[
+                              optional[field('journal')],
+                              optional[field('journaltitle')],
+                          ],
+                ],
+                optional[volume_and_pages],
+                date],
+            sentence[optional_field('note')],
+            self.format_web_refs(e),
+        ]
+        return template
+
+
+logger = logging.getLogger(__name__)
+global_bib = None
+bibliography_start = '<hr>\n<h2>Bibliography</h2>\n'
+bibliography_end = ''
+if pyb_imported:
+    style = Style()
+    backend = html.Backend()
+else:
+    style = None
+    backend = None
+
+
+def get_bib_file(article):
+    """
+    If a bibliography file is specified for this article/page, parse
+    it and return the parsed object.
+    """
+    if 'publications_src' in article.metadata:
+        refs_file = article.metadata['publications_src']
+        try:
+            local_bib = Parser().parse_file(refs_file)
+            return local_bib
+        except PybtexError as e:
+            logger.warning('`pelican_bibtex` failed to parse file %s: %s' % (
+                refs_file,
+                str(e)))
+            return global_bib
+    else:
+        return global_bib
+
+
+def process_content(article):
+    """
+    Substitute the citations and add a bibliography for an article or
+    page, using the local bib file if specified or the global one otherwise.
+    """
+    data = get_bib_file(article)
+    if not data:
+        return
+    content = article._content
+    content = content.replace("@", "&#64;")
+
+    # Scan post to figure out what citations are needed
+    cite_count = {}
+    replace_count = {}
+    for citation in CITE_RE.findall(content):
+        if citation[1] not in cite_count:
+            cite_count[citation[1]] = 1
+            replace_count[citation[1]] = 1
+        else:
+            cite_count[citation[1]] += 1
+
+    # Get formatted entries for the appropriate bibliographic entries
+    cited = []
+    for key in data.entries.keys():
+        if key in cite_count:
+            cited.append(data.entries[key])
+    if len(cited) == 0:
+        return
+
+    # Patch entries, adding missing things to workaround style expecting fields
+    # that are not there
+    for entry in cited:  # type: Entry
+        # Zotero at least exports with "date" instead of separate "year" etc.
+        if 'year' not in entry.fields and 'date' in entry.fields:
+            date_parse = DATE_RE.match(entry.fields['date'])
+            if date_parse:
+                groups = date_parse.groupdict()
+                if groups['y']:
+                    entry.fields['year'] = groups['y']
+                if groups['m']:
+                    entry.fields['month'] = groups['m']
+                if groups['d']:
+                    entry.fields['day'] = groups['d']
+    formatted_entries = style.format_entries(cited)
+
+    # Get the data for the required citations and append to content
+    labels = {}
+    content += bibliography_start
+    for formatted_entry in formatted_entries:
+        key = formatted_entry.key
+        ref_id = key.replace(' ', '')
+        label = ("<a href='#" + ref_id + "' id='ref-" + ref_id + "-{0}'>"
+                 + formatted_entry.label + "</a>")
+        t = formatted_entry.text.render(backend)
+        t = t.replace('\\{', '&#123;')
+        t = t.replace('\\}', '&#125;')
+        t = t.replace('{', '')
+        t = t.replace('}', '')
+        text = ("<p id='" + ref_id + "'>" + t)
+        for i in range(cite_count[key]):
+            if i == 0:
+                text += ' ' + JUMP_BACK.format(ref_id, 1, '↩')
+                if cite_count[key] > 1:
+                    text += JUMP_BACK.format(ref_id, 1, ' <sup>1</sup> ')
+            else:
+                text += JUMP_BACK.format(ref_id, i + 1, '<sup>' + str(i + 1) + '</sup> ')
+        text += '</p>'
+        content += text + '\n'
+        labels[key] = label
+
+    content += bibliography_end
+
+    # Replace citations in article/page
+    cite_count = {}
+
+    def replace_cites(match):
+        label = match.group(2)
+        if label in labels:
+            if label not in cite_count:
+                cite_count[label] = 1
+                replace_count[label] = 1
+            else:
+                cite_count[label] += 1
+            lab = labels[label].format(cite_count[label])
+            if '&#64;&#64;' in match.group():
+                return lab
+            else:
+                m = CITE_2_RE.search(lab)
+                lab = lab[0:m.start()] + '>' + m.group(1) + ' (' + m.group(2) + ')<' + lab[m.end():]
+                return lab
+        else:
+            logger.warning('No BibTeX entry found for key "{}"'.format(label))
+            return match.group(0)
+
+    content = CITE_RE.sub(replace_cites, content)
+    article._content = content
+
+
+def add_citations(content):
+    if isinstance(content, Static):
+        return
+
+    global global_bib
+    if not pyb_imported:
+        logger.warning('`pelican-cite` failed to load dependency `pybtex`')
+        return
+
+    process_content(content)
+
+
+def init(pelican_instance):
+    global global_bib, bibliography_start, bibliography_end
+    if not pyb_imported:
+        logger.warning('`pelican-cite` failed to load dependency `pybtex`')
+        return
+
+    if 'BIBLIOGRAPHY_START' in pelican_instance.settings:
+        bibliography_start = pelican_instance.settings['BIBLIOGRAPHY_START']
+    if 'BIBLIOGRAPHY_END' in pelican_instance.settings:
+        bibliography_end = pelican_instance.settings['BIBLIOGRAPHY_END']
+
+    if 'PUBLICATIONS_SRC' in pelican_instance.settings:
+        refs_file = pelican_instance.settings['PUBLICATIONS_SRC']
+        try:
+            global_bib = Parser().parse_file(refs_file)
+        except PybtexError as e:
+            logger.warning('`pelican_bibtex` failed to parse file %s: %s' % (
+                refs_file,
+                str(e)))
+
+
+def register():
+    signals.initialized.connect(init)
+    signals.content_object_init.connect(add_citations)
diff --git a/pelican-plugins/pelican-rdf/ReadMe.md b/pelican-plugins/pelican-rdf/ReadMe.md
new file mode 100644
index 0000000000000000000000000000000000000000..85908370ceda9f1aca9b0ec72d9d5b705834fb9d
--- /dev/null
+++ b/pelican-plugins/pelican-rdf/ReadMe.md
@@ -0,0 +1,107 @@
+pelican-rdf plugin
+==================
+
+A plugin for rdf vocabularies providers
+---------------------------------------
+
+# Overview
+ 
+This plugin is intended at easing the lightwheight description of vocabularies online, in the fashion of http://vocab.linkeddata.es/. It offers a new media type, the Vocabulary, and a flexible mechanism to gather metadata about said vocabulary based on sparql queries.
+
+# How it works
+
+## Required configuration
+
+### Description of the variables
+Your pelicanconf.py should include new options : 
+- **VOC_PATHS**: A list of paths to a local folders containing vocabularies. If all your vocabularies are remote, set its value to an empty list.
+- **VOC_EXCLUDES**: A list of paths to folders where you don't want vocabularies to be processed. 
+- **VOC_URIS** = A list of URLs pointing to dereferencable vocabularies. Content is negociated to retrieve RDF/XML.
+- **VOC_QUERIES_PATH** = Path th the folder containing the sparql queries to collect metadata about the vocabulary.
+- **VOCABULARY_URL**= How the generated document URL should look like
+- **VOCABULARY_SAVE_AS**= How the generated document should be named
+
+### Default configuration
+```
+VOC_PATHS=['ontologies']
+VOC_EXCLUDES=[]
+VOC_URIS = ["https://www.irit.fr/recherches/MELODI/ontologies/IoT-O",]
+VOC_QUERIES_PATH = "plugins/pelican-rdf/sparql-queries"
+VOCABULARY_URL= '{slug}.html'
+VOCABULARY_SAVE_AS= '{slug}.html'
+```
+
+## Accessing the vocabulary metadata
+
+### First, a simple example...
+The following snippet of code outputs a description of the vocabularies that have been processed : 
+```
+<h1 class="page-header">
+    Ontology repository
+</h1>
+{% if vocabularies %}
+    <table class="table table-striped">
+        <thead>
+          <tr>
+            <th>Title</th>
+            <th>Description</th>
+            <th>License (if any)</th>
+          </tr>
+        </thead>
+        <tbody>
+            {% for voc in vocabularies %}
+                <tr>
+                    <td><a href="{{ voc.iri }}">{{ voc.title }}</a></td>
+                    <td>
+                        <button class="btn btn-primary" type="button" data-toggle="collapse" data-target="#{{ voc.title }}description" aria-expanded="false" aria-controls="{{ voc.title }}description">
+                            {{ voc.title }} description
+                        </button>
+                        <div class="collapse" id="{{ voc.title }}description">
+                            <div class="card card-block">
+                                {{ voc.description }}
+                          </div>
+                        </div>
+                    </td>
+                    <td>{{ voc.lov_metadata.license }}</td>
+                <tr>
+            {% endfor %}
+        </tbody>
+      </table>
+{% endif %}
+```
+
+### ... but how does it work ?
+The required properties (iri, title, version and description) are directly available in the vocabulary metadata. Notice that the license is accessed through a compound notation, explained in the next paragraph.
+
+### Another example...
+The following snippet outputs a list of the classes defined by the ontology, as well as its superclass (limited to one for the time being) and potential description (the comment).
+```
+{% for class in voc.classes %}
+    <div>
+        <h2>{{ class.class }}</h2>
+        <h3>{{ class.superclass }}</h3>
+        <p>{{ class.comment }}</p>
+    </div>
+{% endfor %}
+```
+
+### ... with custom metadata
+To understand the example, one must look at the classes.sparql (the name is important) query : 
+```
+PREFIX owl: <http://www.w3.org/2002/07/owl#>
+PREFIX dc:  <http://purl.org/dc/elements/1.1/>
+PREFIX cc:  <http://creativecommons.org/ns#>
+PREFIX rdfs:<http://www.w3.org/2000/01/rdf-schema#>
+
+SELECT ?class ?comment ?label ?superclass
+WHERE {
+    ?class rdf:type owl:Class.
+    OPTIONAL { ?class rdfs:comment ?comment.}
+    OPTIONAL { ?label rdfs:label ?label.}
+    OPTIONAL { ?class rdfs:subClassOf ?superclass.}
+} GROUP BY ?class
+```
+
+Each graph binding matching this sparql query is returned as a dictionnary in the vocabulary context, with the sparql projection attributes (here, class, comment, label and superclass) as keys. Then, this list of results is stored in the vocabulary metadata under the name of the query, here "classes". 
+
+**NOTE**: The management of multiple values such as the multiple superclasses for a class is not yet handled correctly, it is a work in progress.
\ No newline at end of file
diff --git a/pelican-plugins/pelican-rdf/__init__.py b/pelican-plugins/pelican-rdf/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..1040e555d3424fb59a68f96ba26a2fa13353e518
--- /dev/null
+++ b/pelican-plugins/pelican-rdf/__init__.py
@@ -0,0 +1 @@
+from .pelican_rdf import *
diff --git a/pelican-plugins/pelican-rdf/pelican_rdf.py b/pelican-plugins/pelican-rdf/pelican_rdf.py
new file mode 100644
index 0000000000000000000000000000000000000000..8bc6220a61b831ccde5763c358e425925703da84
--- /dev/null
+++ b/pelican-plugins/pelican-rdf/pelican_rdf.py
@@ -0,0 +1,175 @@
+from pelican.readers import BaseReader
+from pelican.generators import CachingGenerator
+from pelican.contents import Page, is_valid_content
+from pelican import signals
+import logging
+from blinker import signal
+import requests
+from os import listdir
+from os.path import isfile, join
+
+"""
+pelican-rdf
+===============
+
+This plugin integrates to pelican a new type of media, the vocabulary.
+Vocabularies are .rdf or .owl files, and metadata about them is collected
+through sparql queries.
+"""
+
+
+try:
+    import rdflib
+    from rdflib.query import Processor
+    rdflib_loaded=True
+except ImportError:
+    rdflib_loaded=False
+
+logger = logging.getLogger(__name__)
+
+voc_generator_init = signal('voc_generator_init')
+voc_generator_finalized = signal('voc_generator_finalized')
+voc_writer_finalized = signal('voc_writer_finalized')
+voc_generator_preread = signal('voc_generator_preread')
+voc_generator_context = signal('voc_generator_context')
+
+class VocabularyGenerator(CachingGenerator):
+    """Generate vocabulary descriptions"""
+
+    # temporary file where the vocabulary is dereferenced to
+    # when collected online
+    _local_vocabulary_path = "/tmp/"
+    
+    def __init__(self, *args, **kwargs):
+        logger.debug("Vocabulary generator called")
+        self.vocabularies =[]
+        super(VocabularyGenerator, self).__init__(*args, **kwargs)
+    
+    # Called both for local and remote vocabulary context creation.
+    # Performs the actual Vocabulary generation.
+    def generate_vocabulary_context(
+            self, vocabulary_file_name, path_to_vocabulary):
+        logger.debug("Generating__ vocabulary context for "+
+            path_to_vocabulary+"/"+vocabulary_file_name)
+        voc = self.get_cached_data(vocabulary_file_name, None)
+        if voc is None:
+            try:
+                voc = self.readers.read_file(
+                    base_path=path_to_vocabulary,
+                    path=vocabulary_file_name,
+                    content_class=Vocabulary,
+                    context=self.context,
+                    preread_signal=voc_generator_preread,
+                    preread_sender=self,
+                    context_signal=voc_generator_context,
+                    context_sender=self)
+            except Exception as e:
+                logger.error(
+                    'Could not process %s\n%s', vocabulary_file_name, e,
+                    exc_info=self.settings.get('DEBUG', False))
+                self._add_failed_source_path(vocabulary_file_name)
+            
+            if not is_valid_content(voc, vocabulary_file_name):
+                self._add_failed_source_path(vocabulary_file_name)
+    
+            self.cache_data(vocabulary_file_name, voc)
+        self.vocabularies.append(voc)
+        self.add_source_path(voc)
+    
+    
+    def generate_local_context(self):
+        for f in self.get_files(
+                self.settings['VOC_PATHS'],
+                exclude=self.settings['VOC_EXCLUDES']):
+            self.generate_vocabulary_context(f, self.path)
+    
+    def dereference(self, uri, local_file):
+        logger.debug("Dereferencing "+uri+" into "+local_file)
+        headers={"Accept":"application/rdf+xml"}
+        r = requests.get(uri, headers=headers)
+        with open(self._local_vocabulary_path+local_file, 'w') as f:
+            f.write(r.text)
+    
+    def generate_remote_context(self):
+        for uri in self.settings["VOC_URIS"]:
+            logger.debug("Generating context for remote "+uri)
+            local_name = uri.split("/")[-1]+".rdf"
+            self.dereference(uri, local_name)
+            self.generate_vocabulary_context(
+                local_name,
+                self._local_vocabulary_path)
+    
+    def generate_context(self):
+        self.generate_local_context()
+        self.generate_remote_context()
+        self._update_context(('vocabularies',))
+        self.save_cache()
+        self.readers.save_cache()
+    
+    def generate_output(self, writer):
+        for voc in self.vocabularies:
+            writer.write_file(
+                voc.save_as, self.get_template(voc.template),
+                self.context, voc=voc,
+                relative_urls=self.settings['RELATIVE_URLS'],
+                override_output=hasattr(voc, 'override_save_as'))
+        voc_writer_finalized.send(self, writer=writer)
+
+class RdfReader(BaseReader):
+    
+    file_extensions = ['rdf', 'owl']
+    enabled = bool(rdflib_loaded)
+    
+    def __init__(self, *args, **kwargs):
+        super(RdfReader, self).__init__(*args, **kwargs)
+
+    def read(self, source_path):
+        """Parse content and metadata of an rdf file"""
+        logger.debug("Loading graph described in "+source_path)
+        graph = rdflib.Graph()
+        graph.load(source_path)
+        meta = {}
+        queries = [
+            f for f in listdir(self.settings["VOC_QUERIES_PATH"])
+            if (isfile(join(self.settings["VOC_QUERIES_PATH"], f)) 
+                and f.endswith(".sparql"))]
+        for query_path in queries:
+            query_file_path = self.settings["VOC_QUERIES_PATH"]+"/"+query_path
+            with open(query_file_path, "r") as query_file:
+                query = query_file.read()
+
+                # The name of the query identifies the elements in the context
+                query_key=query_path.split(".")[0]
+                result_set = graph.query(query)
+                # Each query result will be stored as a dictionnary in the
+                # vocabulary context, referenced by the query name as its key.
+                # Multiple results are stored in a list.
+                for result in result_set:
+                    if not query_key in meta.keys():
+                        meta[query_key]=result.asdict()
+                    elif type(meta[query_key]) == list:
+                        meta[query_key].append(result.asdict())
+                    else:
+                        meta[query_key]=[meta[query_key], result.asdict()]
+        meta["iri"] = meta["lov_metadata"]["iri"]
+        meta["description"] = meta["lov_metadata"]["description"]
+        meta["version"] = meta["lov_metadata"]["version"]
+        meta["title"] = meta["lov_metadata"]["title"]
+        return "", meta
+
+class Vocabulary(Page):
+    mandatory_properties = ('iri','description','version', 'title')
+    default_template = 'vocabulary'
+
+def add_reader(readers):
+    for ext in RdfReader.file_extensions:
+        readers.reader_classes[ext] = RdfReader
+
+def add_generator(pelican_object):
+    print("Adding the generator")
+    return VocabularyGenerator
+
+
+def register():
+    signals.get_generators.connect(add_generator)
+    signals.readers_init.connect(add_reader)
diff --git a/pelican-plugins/pelican-rdf/sparql-queries/classes.sparql b/pelican-plugins/pelican-rdf/sparql-queries/classes.sparql
new file mode 100644
index 0000000000000000000000000000000000000000..269cf2fc801d6f8134ed9b979b07932dfcf5362f
--- /dev/null
+++ b/pelican-plugins/pelican-rdf/sparql-queries/classes.sparql
@@ -0,0 +1,12 @@
+PREFIX owl:	<http://www.w3.org/2002/07/owl#>
+PREFIX dc:	<http://purl.org/dc/elements/1.1/>
+PREFIX cc:	<http://creativecommons.org/ns#>
+PREFIX rdfs:<http://www.w3.org/2000/01/rdf-schema#>
+
+SELECT ?class ?comment ?label
+WHERE {
+	?class rdf:type owl:Class.
+	OPTIONAL { ?class rdfs:comment ?comment.}
+	OPTIONAL { ?label rdfs:label ?label.}
+	OPTIONAL { ?class rdfs:subClassOf ?superclass.}
+} GROUP BY ?class
\ No newline at end of file
diff --git a/pelican-plugins/pelican-rdf/sparql-queries/lov_metadata.sparql b/pelican-plugins/pelican-rdf/sparql-queries/lov_metadata.sparql
new file mode 100644
index 0000000000000000000000000000000000000000..87556a1eb73f03ed6cf830c358f5abb3e826cd5e
--- /dev/null
+++ b/pelican-plugins/pelican-rdf/sparql-queries/lov_metadata.sparql
@@ -0,0 +1,11 @@
+PREFIX owl:	<http://www.w3.org/2002/07/owl#>
+PREFIX dc:	<http://purl.org/dc/elements/1.1/>
+PREFIX cc:	<http://creativecommons.org/ns#>
+SELECT ?iri ?license ?description ?version ?title
+WHERE {
+	?iri rdf:type owl:Ontology;
+		cc:license ?license;
+		dc:description ?description;
+		dc:title ?title;
+		owl:versionInfo ?version.
+} LIMIT 1
\ No newline at end of file
diff --git a/pelican-plugins/pelican_comment_system/CHANGELOG.md b/pelican-plugins/pelican_comment_system/CHANGELOG.md
new file mode 100644
index 0000000000000000000000000000000000000000..d6720e401aa59a80f299feb1ee5427d64d314f70
--- /dev/null
+++ b/pelican-plugins/pelican_comment_system/CHANGELOG.md
@@ -0,0 +1,54 @@
+# Change Log #
+All notable changes to this project will be documented in this file.
+This project adheres to [Semantic Versioning](http://semver.org/).
+
+## 1.3.0 - 2017-01-10
+### Added
+- add [blogger_comment_export.py](import/blogger_comment_export.py) script to export comments from Blogger XML export and [associated documentation](docs/import.md) [PR #835](https://github.com/getpelican/pelican-plugins/pull/835)
+
+## 1.2.2 – 2016-12-19
+### Fixed
+- Correct jQuery expression in cancelReply method  [PR #820](https://github.com/getpelican/pelican-plugins/pull/820)
+
+## 1.2.1 – 2016-09-22
+### Fixed
+- Add support for the autoreload mode of pelican [PR #782](https://github.com/getpelican/pelican-plugins/pull/782) [Fixes pelican#1949](https://github.com/getpelican/pelican/issues/1949)
+
+## 1.2.0 – 2016-05-23
+### Fixed - Documentation
+- Correct template path [PR #713](https://github.com/getpelican/pelican-plugins/pull/713)
+
+### Added - Documentation
+- Adds Quickstart guide + default theme [PR #686](https://github.com/getpelican/pelican-plugins/pull/686)
+
+### Fixed
+- Fix mailto link: use '\r\n' instead of '\n' [PR #720](https://github.com/getpelican/pelican-plugins/pull/720)
+- Fix comparison of offset-naive and offset-aware datetimes [PR #722](https://github.com/getpelican/pelican-plugins/pull/722)
+
+### Added
+- Logs a warning if the parent of a comment can not be found [PR #715](https://github.com/getpelican/pelican-plugins/pull/715)
+
+## 1.1.0 – 2016-02-18
+### Fixed – Documentation
+- Updated old URLs [PR #677](https://github.com/getpelican/pelican-plugins/pull/677)
+
+### Changed
+- Main logic runs a bit earlier (allows other plugins to access comments earlier)  [PR #677](https://github.com/getpelican/pelican-plugins/pull/677)
+- The writer to generate the feeds can now be exchanged (via a normal pelican writer plugin) [PR #677](https://github.com/getpelican/pelican-plugins/pull/677)
+
+
+## 1.0.1 – 2015-10-04
+### Fixed – Documentation
+- Add commas indicating tuple (`PELICAN_COMMENT_SYSTEM_AUTHORS`) [PR #579](https://github.com/getpelican/pelican-plugins/pull/579)
+
+
+## 1.0.0 – 2014-11-05
+### Added
+- Basic static comments
+- Atom Feeds
+- Replies to comments
+- Avatars and identicons
+
+
+This change log uses [Keep a CHANGELOG](http://keepachangelog.com/) as a template.
+
diff --git a/pelican-plugins/pelican_comment_system/Readme.md b/pelican-plugins/pelican_comment_system/Readme.md
new file mode 100644
index 0000000000000000000000000000000000000000..668118776630f2754b1b340431067482eeccbb39
--- /dev/null
+++ b/pelican-plugins/pelican_comment_system/Readme.md
@@ -0,0 +1,45 @@
+# Pelican Comment System
+
+Pelican Comment System allows you to add static comments to your articles.
+
+Comments are stored in files in formats that can be processed by Pelican (e.g., Markdown, reStructuredText). Each comment resides in its own file.
+
+### Features
+
+ - Static comments for each article
+ - Replies to comments
+ - Avatars and [Identicons](https://en.wikipedia.org/wiki/Identicon)
+ - Comment Atom feed for each article
+ - Easy styleable via themes
+ - Python 2 and 3 support
+
+
+See it in action here: [bernhard.scheirle.de](http://bernhard.scheirle.de/posts/2014/March/29/static-comments-via-email/)
+
+Author             | Website                       | Github
+-------------------|-------------------------------|------------------------------
+Bernhard Scheirle  | <http://bernhard.scheirle.de> | <https://github.com/Scheirle>
+
+## Instructions
+
+ - [Quickstart Guide](doc/quickstart.md)
+ - [Installation and basic usage](doc/installation.md)
+ - [Import existing comments](doc/import.md)
+ - [Avatars and identicons](doc/avatars.md)
+ - [Comment Atom feed](doc/feed.md)
+ 
+## Requirements
+
+Pelican 3.4 or newer is required.
+
+To create identicons, the Python Image Library is needed. Therefore you either need PIL **or** Pillow (recommended).
+
+**Install Pillow via:**
+
+    pip install Pillow
+
+If you don't want avatars or identicons, this plugin works fine without PIL/Pillow. You will, however, see a warning that identicons are deactivated (as expected).
+
+## Change Log
+
+The change log can be found in the [CHANGELOG.md](CHANGELOG.md) file.
diff --git a/pelican-plugins/pelican_comment_system/__init__.py b/pelican-plugins/pelican_comment_system/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..4c8a3ca98b6f7f60ee9e2b019d8c1dc56ceaafec
--- /dev/null
+++ b/pelican-plugins/pelican_comment_system/__init__.py
@@ -0,0 +1 @@
+from .pelican_comment_system import *
diff --git a/pelican-plugins/pelican_comment_system/avatars.py b/pelican-plugins/pelican_comment_system/avatars.py
new file mode 100644
index 0000000000000000000000000000000000000000..faa3ce3eaf900c928f6a6409308bba65a5da5fd0
--- /dev/null
+++ b/pelican-plugins/pelican_comment_system/avatars.py
@@ -0,0 +1,105 @@
+# -*- coding: utf-8 -*-
+"""
+Author: Bernhard Scheirle
+"""
+
+from __future__ import unicode_literals
+
+import logging
+import os
+
+import hashlib
+
+
+logger = logging.getLogger(__name__)
+_log = "pelican_comment_system: avatars: "
+try:
+    from . identicon import identicon
+    _identiconImported = True
+except ImportError as e:
+    logger.warning(_log + "identicon deactivated: " + str(e))
+    _identiconImported = False
+
+# Global Variables
+_identicon_save_path = None
+_identicon_output_path = None
+_identicon_data = None
+_identicon_size = None
+_initialized = False
+_authors = None
+_missingAvatars = []
+
+
+def _ready():
+    if not _initialized:
+        logger.warning(_log + "Module not initialized. use init")
+    if not _identicon_data:
+        logger.debug(_log + "No identicon data set")
+    return _identiconImported and _initialized and _identicon_data
+
+
+def init(pelican_output_path, identicon_output_path, identicon_data,
+         identicon_size, authors):
+    global _identicon_save_path
+    global _identicon_output_path
+    global _identicon_data
+    global _identicon_size
+    global _initialized
+    global _authors
+    global _missingAvatars
+
+    _identicon_save_path = os.path.join(pelican_output_path,
+                                        identicon_output_path)
+    _identicon_output_path = identicon_output_path
+    _identicon_data = identicon_data
+    _identicon_size = identicon_size
+    _authors = authors
+    _missingAvatars = []
+    _initialized = True
+
+
+def _createIdenticonOutputFolder():
+    if not _ready():
+        return
+
+    if not os.path.exists(_identicon_save_path):
+        os.makedirs(_identicon_save_path)
+
+
+def getAvatarPath(comment_id, metadata):
+    if not _ready():
+        return ''
+
+    md5 = hashlib.md5()
+    author = tuple()
+    for data in _identicon_data:
+        if data in metadata:
+            string = "{}".format(metadata[data])
+            md5.update(string.encode('utf-8'))
+            author += tuple([string])
+        else:
+            logger.warning(_log + data +
+                           " is missing in comment: " + comment_id)
+
+    if author in _authors:
+        return _authors[author]
+
+    global _missingAvatars
+
+    code = md5.hexdigest()
+
+    if not code in _missingAvatars:
+        _missingAvatars.append(code)
+
+    return os.path.join(_identicon_output_path, '%s.png' % code)
+
+
+def generateAndSaveMissingAvatars():
+    _createIdenticonOutputFolder()
+    global _missingAvatars
+    for code in _missingAvatars:
+        avatar_path = '%s.png' % code
+        avatar = identicon.render_identicon(int(code, 16), _identicon_size)
+        avatar_save_path = os.path.join(_identicon_save_path, avatar_path)
+        avatar.save(avatar_save_path, 'PNG')
+    _missingAvatars = []
diff --git a/pelican-plugins/pelican_comment_system/comment.py b/pelican-plugins/pelican_comment_system/comment.py
new file mode 100644
index 0000000000000000000000000000000000000000..66c2131d8783ebe47426b7bd8b93a62aa444106a
--- /dev/null
+++ b/pelican-plugins/pelican_comment_system/comment.py
@@ -0,0 +1,66 @@
+# -*- coding: utf-8 -*-
+"""
+Author: Bernhard Scheirle
+"""
+from __future__ import unicode_literals
+import os
+
+from pelican.contents import Content
+from pelican.utils import slugify
+
+from . import avatars
+
+
+class Comment(Content):
+    mandatory_properties = ('author', 'date')
+    default_template = 'None'
+
+    article = None
+
+    def __init__(self, content, metadata, settings, source_path, context):
+        # Strip the path off the full filename.
+        name = os.path.split(source_path)[1]
+
+        if not hasattr(self, 'slug'):
+            # compute the slug before initializing the base Content object, so
+            # it doesn't get set there
+            # This is required because we need a slug containing the file
+            # extension.
+            self.slug = slugify(name, settings.get('SLUG_SUBSTITUTIONS', ()))
+
+        super(Comment, self).__init__(content, metadata, settings, source_path,
+                                      context)
+
+        self.replies = []
+
+        # Strip the extension from the filename.
+        name = os.path.splitext(name)[0]
+        self.avatar = avatars.getAvatarPath(name, metadata)
+        self.title = "Posted by:  {}".format(metadata['author'])
+
+    def addReply(self, comment):
+        self.replies.append(comment)
+
+    def getReply(self, slug):
+        for reply in self.replies:
+            if reply.slug == slug:
+                return reply
+            else:
+                deepReply = reply.getReply(slug)
+                if deepReply is not None:
+                    return deepReply
+        return None
+
+    def __lt__(self, other):
+        return self.date < other.date
+
+    def sortReplies(self):
+        for r in self.replies:
+            r.sortReplies()
+        self.replies = sorted(self.replies)
+
+    def countReplies(self):
+        amount = 0
+        for r in self.replies:
+            amount += r.countReplies()
+        return amount + len(self.replies)
diff --git a/pelican-plugins/pelican_comment_system/doc/avatars.md b/pelican-plugins/pelican_comment_system/doc/avatars.md
new file mode 100644
index 0000000000000000000000000000000000000000..73441141e3fcc0a3d112a6e8785205dce0453662
--- /dev/null
+++ b/pelican-plugins/pelican_comment_system/doc/avatars.md
@@ -0,0 +1,35 @@
+# Avatars and Identicons
+To activate the avatars and [identicons](https://en.wikipedia.org/wiki/Identicon) you have to set `PELICAN_COMMENT_SYSTEM_IDENTICON_DATA`.
+
+##### Example
+```python
+PELICAN_COMMENT_SYSTEM_IDENTICON_DATA = ('author',)
+```
+Now every comment with the same author tag will be treated as if written from the same person. And therefore have the same avatar/identicon. Of cause you can modify this tuple so other metadata are checked.
+
+## Specific Avatars
+To set a specific avatar for a author you have to add them to the `PELICAN_COMMENT_SYSTEM_AUTHORS` dictionary.
+
+The `key` of the dictionary has to be a tuple of the form of `PELICAN_COMMENT_SYSTEM_IDENTICON_DATA`, so in our case only the author's name.
+
+The `value` of the dictionary is the path to the specific avatar.
+
+##### Example
+```python
+PELICAN_COMMENT_SYSTEM_AUTHORS = {
+	('John',): "images/authors/john.png",
+	('Tom',): "images/authors/tom.png",
+}
+```
+
+## Theme
+To display the avatars and identicons simply add the following in the "comment for loop" in your theme:
+
+```html
+<img src="{{ SITEURL }}/{{ comment.avatar }}"
+		alt="Avatar"
+		height="{{ PELICAN_COMMENT_SYSTEM_IDENTICON_SIZE }}"
+		width="{{ PELICAN_COMMENT_SYSTEM_IDENTICON_SIZE }}">
+```
+
+Of cause the `height` and `width` are optional, but they make sure that everything has the same size (in particular  specific avatars).
diff --git a/pelican-plugins/pelican_comment_system/doc/feed.md b/pelican-plugins/pelican_comment_system/doc/feed.md
new file mode 100644
index 0000000000000000000000000000000000000000..88939632abf743474df8fdc8b5714c72efe2ae97
--- /dev/null
+++ b/pelican-plugins/pelican_comment_system/doc/feed.md
@@ -0,0 +1,33 @@
+# Comment Atom Feed
+## Custom comment url
+Be sure that the id of the html tag containing the comment matches `COMMENT_URL`.
+
+##### pelicanconf.py
+```python
+COMMENT_URL = "#my_own_comment_id_{slug}"
+```
+
+##### Theme
+```html
+{% for comment in article.comments recursive %}
+	...
+	<article id="my_own_comment_id_{{comment.slug}}">{{ comment.content }}</article>
+	...
+{% endfor %}
+```
+## Theme
+#### Link
+To display a link to the article feed simply add the following to your theme:
+
+```html
+{% if article %}
+	<a href="{{ FEED_DOMAIN }}/{{ PELICAN_COMMENT_SYSTEM_FEED|format(article.slug) }}">Comment Atom Feed</a>
+{% endif %}
+```
+
+Link to the all comment feed:
+
+```html
+<a href="{{ FEED_DOMAIN }}/{{ PELICAN_COMMENT_SYSTEM_FEED_ALL }}">Comment All Atom Feed</a>
+```
+
diff --git a/pelican-plugins/pelican_comment_system/doc/import.md b/pelican-plugins/pelican_comment_system/doc/import.md
new file mode 100644
index 0000000000000000000000000000000000000000..0263fda0255107fe027a134146fd5add5ee21036
--- /dev/null
+++ b/pelican-plugins/pelican_comment_system/doc/import.md
@@ -0,0 +1,47 @@
+# Importing Comments
+
+**Note**: Contributions to this section are welcomed!
+
+When moving to Pelican and the Pelican Comment System, it may be desirable to move over your comments as well.
+
+The scripts to support this are found in the `import` directory.
+
+## Blogger
+
+Blogger is good in that it will give you an export of everything, but the bad news is it's one giant XML file. XML is great if you're a computer, but a bit of a pain if you're a human. 
+
+The code I used to export my comments from Blogger is found at [blogger_comment_export.py](../import/blogger_comment_export.py).
+
+To use it
+yourself, you will need to first adjust the constants at the beginning of the 
+script (lines 26-33) to point to your Blogger XML export and where you want
+the comments to be exported to. You will also need to install `untangle`
+(available through pip -- `pip install untangle`).
+
+Comments will be exported into folders matching
+the Blogger slug of the post. The email for all authors will be `noreply@blogger.com`. The other file created will be `authors.txt`
+which lists the various comment authors, and a link to the profile
+picture used on Blogger. These pictures will need to be manually downloaded
+and then configured using the `PELICAN_COMMENT_SYSTEM_AUTHORS` setting.
+In my case, that looked like this:
+
+```python
+# in pelicanconf.py
+PELICAN_COMMENT_SYSTEM_AUTHORS = {
+    ('PROTIK KHAN', 'noreply@blogger.com'): "images/authors/rabiul_karim.webp",
+    ('Matthew Hartzell', 'noreply@blogger.com'): "images/authors/matthew_hartzell.webp",
+    ('Jens-Peter Labus', 'noreply@blogger.com'): "images/authors/jens-peter_labus.png",
+    ('Bridget', 'noreply@blogger.com'): "images/authors/bridget.jpg",
+    ('melissaclee', 'noreply@blogger.com'): "images/authors/melissa_lee.jpg",
+    ('Melissa', 'noreply@blogger.com'): "images/authors/melissa_lee.jpg"
+}
+```
+
+The script was developed for Python 3.6, but should work on Python 3.4+
+without modification.
+
+For more information on this script on, you can read my
+[blog post](http://blog.minchin.ca/2016/12/blogger-comments-exported.html)
+where I introduced it.
+
+-- Wm. Minchin (@MinchinWeb), January 10, 2017
diff --git a/pelican-plugins/pelican_comment_system/doc/installation.md b/pelican-plugins/pelican_comment_system/doc/installation.md
new file mode 100644
index 0000000000000000000000000000000000000000..56869b4a1186401de69fbde0b2dc08d9e13c201e
--- /dev/null
+++ b/pelican-plugins/pelican_comment_system/doc/installation.md
@@ -0,0 +1,117 @@
+# Installation
+
+Activate the plugin by adding it to your `pelicanconf.py`: (See also [How to use plugins](https://github.com/getpelican/pelican-plugins/tree/master/#how-to-use-plugins))
+
+	PELICAN_COMMENT_SYSTEM = True
+
+Then, modify your `article.html` theme as follows below.
+
+## Settings
+
+Name                                           | Type      | Default                      | Description
+-----------------------------------------------|-----------|------------------------------|-------
+`PELICAN_COMMENT_SYSTEM`                       | `boolean` | `False`                      | Activates or deactivates the comment system
+`PELICAN_COMMENT_SYSTEM_DIR`                   | `string`  | `comments`                   | Folder where the comments are stored, relative to `PATH`
+`PELICAN_COMMENT_SYSTEM_IDENTICON_OUTPUT_PATH` | `string`  | `images/identicon`           | Relative URL to the output folder where the identicons are stored
+`PELICAN_COMMENT_SYSTEM_IDENTICON_DATA`        | `tuple`   | `()`                         | Contains all Metadata tags, which in combination identifies a comment author (like `('author', 'email')`)
+`PELICAN_COMMENT_SYSTEM_IDENTICON_SIZE`        | `int`     | `72`                         | Width and height of the identicons. Has to be a multiple of 3.
+`PELICAN_COMMENT_SYSTEM_AUTHORS`               | `dict`    | `{}`                         | Comment authors, which should have a specific avatar. More info [here](avatars.md)
+`PELICAN_COMMENT_SYSTEM_FEED`                  | `string`  |`feeds/comment.%s.atom.xml`   | Relative URL to output the Atom feed for each article.`%s` gets replaced with the slug of the article. More info [here](http://docs.getpelican.com/en/latest/settings.html#feed-settings)
+`PELICAN_COMMENT_SYSTEM_FEED_ALL`              | `string`  |`feeds/comments.all.atom.xml` | Relative URL to output the Atom feed which contains all comments of all articles. More info [here](http://docs.getpelican.com/en/latest/settings.html#feed-settings)
+`COMMENT_URL`                                  | `string`  | `#comment-{slug}`            | `{slug}` gets replaced with the slug of the comment. More info [here](feed.md)
+
+## Folder structure
+
+Every comment file has to be stored in a sub-folder of `PELICAN_COMMENT_SYSTEM_DIR`.
+
+Sub-folders are named after the `slug` of the articles.
+
+So the comments to your `foo-bar` article are stored in `comments/foo-bar/`
+
+The filenames of the comment files are up to you. But the filename is the identifier of the comment (**with** extension).
+
+##### Example folder structure
+
+	.
+	└── comments
+		└── foo-bar
+		│   ├── 1.md
+		│   └── 0.md
+		└── some-other-slug
+			├── random-Name.md
+			├── 1.md
+			└── 0.md
+
+
+## Comment file
+
+### Meta information
+
+Tag           | Required  | Description
+--------------|-----------|----------------
+`date`        | yes       | Date when the comment was posted
+`author`      | yes       | Name of the comment author
+`slug`        | no        | Slug of the comment. If not present it will be computed from the file name (including the extension)
+`replyto`     | no        | Slug of the parent comment
+
+Every other (custom) tag gets parsed as well and will be available through the theme.
+
+##### Example of a comment file
+
+	date: 2014-3-21 15:02
+	author: Author of the comment
+	website: http://authors.website.com
+	replyto: 1md
+	anothermetatag: some random tag
+
+	Content of the comment.
+
+## Theme
+
+In the `article.html` template file, there are now two additional variables available.
+
+Variables                | Description
+-------------------------|--------------------------
+`article.comments_count` | Number of total comments for this article (including replies to comments)
+`article.comments`       | Array containing the top-level comments for this article (no replies to comments)
+
+### Comment object
+
+The comment object is a [content](https://github.com/getpelican/pelican/blob/master/pelican/contents.py#L34) object, so all common attributes are available (author, content, date, local_date, slug, metadata, etc.).
+
+The additional following attributes are also available:
+
+Attribute  | Description
+-----------|--------------------------
+`replies`  | Array containing the top level replies for this comment
+`avatar`   | Path to the avatar or identicon of the comment author
+
+##### Example article.html template
+
+(only the comment section is shown here)
+
+```html
+{% if article.comments %}
+	{% for comment in article.comments recursive %}
+		{% if loop.depth0 == 0 %}
+			{% set marginLeft = 0 %}
+		{% else %}
+			{% set marginLeft = 50 %}
+		{% endif %}
+			<article id="comment-{{comment.slug}}" style="border: 1px solid #DDDDDD; padding: 5px 0px 0px 5px; margin: 0px -1px 5px {{marginLeft}}px;">
+				<a href="{{ SITEURL }}/{{ article.url }}#comment-{{comment.slug}}" rel="bookmark" title="Permalink to this comment">Permalink</a>
+				<h4>{{ comment.author }}</h4>
+				<p>Posted on <abbr class="published" title="{{ comment.date.isoformat() }}">{{ comment.locale_date }}</abbr></p>
+				{{ comment.metadata['my_custom_metadata'] }}
+				{{ comment.content }}
+				{% if comment.replies %}
+					{{ loop(comment.replies) }}
+				{% endif %}
+			</article>
+	{% endfor %}
+{% else %}
+	<p>There are no comments yet.<p>
+{% endif %}
+```
+
+For a more complex / extensive example have a look at [theme/templates/pcs/comments.html](../theme/templates/pcs/comments.html)
\ No newline at end of file
diff --git a/pelican-plugins/pelican_comment_system/doc/quickstart.md b/pelican-plugins/pelican_comment_system/doc/quickstart.md
new file mode 100644
index 0000000000000000000000000000000000000000..3ef675bbe1841d787439e0163744c66ef07e287b
--- /dev/null
+++ b/pelican-plugins/pelican_comment_system/doc/quickstart.md
@@ -0,0 +1,32 @@
+# Quickstart Guide (with Comment form)
+
+This guide shows you how to setup the plugin for basic usage.
+The default theme has an comment form included.
+This form allows your visitors to easily write comments and send them to you via email [1].
+
+1. Merge the `./theme` folder with your own theme folder, or copy the files manually
+	```
+	mkdir -p [yourtheme]/templates/pcs
+	mkdir -p [yourtheme]/static/js
+	cp ./theme/templates/pcs/comments.html [yourtheme]/templates/pcs/comments.html
+	cp ./theme/static/js/comments.js      [yourtheme]/static/js/comments.html
+	```
+
+2. Modify your `article.html` template:
+	1. Add `{% import 'pcs/comments.html' as pcs with context %}` to the top
+	2. Add `{{ pcs.comments_quickstart("emailuser", "example.com") }}` where you want your comments (e.g. below `{{ article.content }}`)  
+	"emailuser@example.com" will be the e-mail address used for the `mailto:` link [1]
+
+3. Enable the plugin: `pelicanconf.py` (See also [How to use plugins](https://github.com/getpelican/pelican-plugins/tree/master/#how-to-use-plugins))
+	```
+	PELICAN_COMMENT_SYSTEM = True
+	PELICAN_COMMENT_SYSTEM_IDENTICON_DATA = ('author',)
+	```
+
+## Notes
+ * Instead of using `pcs.comments_quickstart` you can also use the other macros available in `comments.html`.
+   They are a bit more flexible and may generate "better" html output e.g.:
+    * Don't force load jQuery
+    * No inline css
+
+[1] The comment form generates a `mailto:` link on submisson. The resulting email contains a valid Markdown block. Now you only have to copy this block to a new file, obviating the need to gather the metadata (such as date, author, replyto) yourself.
\ No newline at end of file
diff --git a/pelican-plugins/pelican_comment_system/identicon/LICENSE b/pelican-plugins/pelican_comment_system/identicon/LICENSE
new file mode 100755
index 0000000000000000000000000000000000000000..e6e964fb9143dce8863bd317035ce31de10d4f82
--- /dev/null
+++ b/pelican-plugins/pelican_comment_system/identicon/LICENSE
@@ -0,0 +1,11 @@
+identicon.py is Licesensed under FreeBSD License.
+(http://www.freebsd.org/copyright/freebsd-license.html)
+
+Copyright 1994-2009 Shin Adachi. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+   1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+   2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/pelican-plugins/pelican_comment_system/identicon/README.md b/pelican-plugins/pelican_comment_system/identicon/README.md
new file mode 100755
index 0000000000000000000000000000000000000000..1aa768c367b0dea15e88292ddb18761e13f3c9f2
--- /dev/null
+++ b/pelican-plugins/pelican_comment_system/identicon/README.md
@@ -0,0 +1,17 @@
+identicon.py: identicon python implementation.
+==============================================
+:Author:Shin Adachi <shn@glucose.jp>
+
+## usage
+
+### commandline
+
+    python identicon.py [code]
+
+### python
+
+    import identicon
+    identicon.render_identicon(code, size)
+
+Return a PIL Image class instance which have generated identicon image.
+`size` specifies patch size. Generated image size is 3 * `size`.
\ No newline at end of file
diff --git a/pelican-plugins/pelican_comment_system/identicon/__init__.py b/pelican-plugins/pelican_comment_system/identicon/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/pelican-plugins/pelican_comment_system/identicon/identicon.py b/pelican-plugins/pelican_comment_system/identicon/identicon.py
new file mode 100755
index 0000000000000000000000000000000000000000..a8f9c5c8358a80753aaac134c6954c2f38c64103
--- /dev/null
+++ b/pelican-plugins/pelican_comment_system/identicon/identicon.py
@@ -0,0 +1,259 @@
+#!/usr/bin/env python
+# -*- coding:utf-8 -*-
+"""
+identicon.py
+identicon python implementation.
+by Shin Adachi <shn@glucose.jp>
+
+= usage =
+
+== commandline ==
+>>> python identicon.py [code]
+
+== python ==
+>>> import identicon
+>>> identicon.render_identicon(code, size)
+
+Return a PIL Image class instance which have generated identicon image.
+```size``` specifies `patch size`. Generated image size is 3 * ```size```.
+"""
+# g
+# PIL Modules
+from PIL import Image, ImageDraw, ImagePath, ImageColor
+
+
+__all__ = ['render_identicon', 'IdenticonRendererBase']
+
+
+class Matrix2D(list):
+
+    """Matrix for Patch rotation"""
+
+    def __init__(self, initial=[0.] * 9):
+        assert isinstance(initial, list) and len(initial) == 9
+        list.__init__(self, initial)
+
+    def clear(self):
+        for i in range(9):
+            self[i] = 0.
+
+    def set_identity(self):
+        self.clear()
+        for i in range(3):
+            self[i] = 1.
+
+    def __str__(self):
+        return '[%s]' % ', '.join('%3.2f' % v for v in self)
+
+    def __mul__(self, other):
+        r = []
+        if isinstance(other, Matrix2D):
+            for y in range(3):
+                for x in range(3):
+                    v = 0.0
+                    for i in range(3):
+                        v += (self[i * 3 + x] * other[y * 3 + i])
+                    r.append(v)
+        else:
+            raise NotImplementedError
+        return Matrix2D(r)
+
+    def for_PIL(self):
+        return self[0:6]
+
+    @classmethod
+    def translate(kls, x, y):
+        return kls([1.0, 0.0, float(x),
+                    0.0, 1.0, float(y),
+                    0.0, 0.0, 1.0])
+
+    @classmethod
+    def scale(kls, x, y):
+        return kls([float(x), 0.0, 0.0,
+                    0.0, float(y), 0.0,
+                    0.0, 0.0, 1.0])
+
+    """
+    # need `import math`
+    @classmethod
+    def rotate(kls, theta, pivot=None):
+        c = math.cos(theta)
+        s = math.sin(theta)
+
+        matR = kls([c, -s, 0., s, c, 0., 0., 0., 1.])
+        if not pivot:
+            return matR
+        return kls.translate(-pivot[0], -pivot[1]) * matR *
+            kls.translate(*pivot)
+    """
+
+    @classmethod
+    def rotateSquare(kls, theta, pivot=None):
+        theta = theta % 4
+        c = [1., 0., -1., 0.][theta]
+        s = [0., 1., 0., -1.][theta]
+
+        matR = kls([c, -s, 0., s, c, 0., 0., 0., 1.])
+        if not pivot:
+            return matR
+        return kls.translate(-pivot[0], -pivot[1]) * matR * \
+            kls.translate(*pivot)
+
+
+class IdenticonRendererBase(object):
+    PATH_SET = []
+
+    def __init__(self, code):
+        """
+        @param code code for icon
+        """
+        if not isinstance(code, int):
+            code = int(code)
+        self.code = code
+
+    def render(self, size):
+        """
+        render identicon to PIL.Image
+
+        @param size identicon patchsize. (image size is 3 * [size])
+        @return PIL.Image
+        """
+
+        # decode the code
+        middle, corner, side, foreColor, backColor = self.decode(self.code)
+        size = int(size)
+        # make image
+        image = Image.new("RGB", (size * 3, size * 3))
+        draw = ImageDraw.Draw(image)
+
+        # fill background
+        draw.rectangle((0, 0, image.size[0], image.size[1]), fill=0)
+
+        kwds = {
+            'draw': draw,
+            'size': size,
+            'foreColor': foreColor,
+            'backColor': backColor}
+        # middle patch
+        self.drawPatch((1, 1), middle[2], middle[1], middle[0], **kwds)
+
+        # side patch
+        kwds['type'] = side[0]
+        for i in range(4):
+            pos = [(1, 0), (2, 1), (1, 2), (0, 1)][i]
+            self.drawPatch(pos, side[2] + 1 + i, side[1], **kwds)
+
+        # corner patch
+        kwds['type'] = corner[0]
+        for i in range(4):
+            pos = [(0, 0), (2, 0), (2, 2), (0, 2)][i]
+            self.drawPatch(pos, corner[2] + 1 + i, corner[1], **kwds)
+
+        return image
+
+    def drawPatch(self, pos, turn, invert, type, draw, size, foreColor,
+                  backColor):
+        """
+        @param size patch size
+        """
+        path = self.PATH_SET[type]
+        if not path:
+            # blank patch
+            invert = not invert
+            path = [(0., 0.), (1., 0.), (1., 1.), (0., 1.), (0., 0.)]
+        patch = ImagePath.Path(path)
+        if invert:
+            foreColor, backColor = backColor, foreColor
+
+        mat = Matrix2D.rotateSquare(turn, pivot=(0.5, 0.5)) *\
+            Matrix2D.translate(*pos) *\
+            Matrix2D.scale(size, size)
+
+        patch.transform(mat.for_PIL())
+        draw.rectangle((pos[0] * size, pos[1] * size, (pos[0] + 1) * size,
+                        (pos[1] + 1) * size), fill=backColor)
+        draw.polygon(patch, fill=foreColor, outline=foreColor)
+
+    # virtual functions
+    def decode(self, code):
+        raise NotImplementedError
+
+
+class DonRenderer(IdenticonRendererBase):
+
+    """
+    Don Park's implementation of identicon
+    see : http://www.docuverse.com/blog/donpark/2007/01/19/identicon-updated-and-source-released
+    """
+
+    PATH_SET = [
+        [(0, 0), (4, 0), (4, 4), (0, 4)],   # 0
+        [(0, 0), (4, 0), (0, 4)],
+        [(2, 0), (4, 4), (0, 4)],
+        [(0, 0), (2, 0), (2, 4), (0, 4)],
+        [(2, 0), (4, 2), (2, 4), (0, 2)],   # 4
+        [(0, 0), (4, 2), (4, 4), (2, 4)],
+        [(2, 0), (4, 4), (2, 4), (3, 2), (1, 2), (2, 4), (0, 4)],
+        [(0, 0), (4, 2), (2, 4)],
+        [(1, 1), (3, 1), (3, 3), (1, 3)],   # 8
+        [(2, 0), (4, 0), (0, 4), (0, 2), (2, 2)],
+        [(0, 0), (2, 0), (2, 2), (0, 2)],
+        [(0, 2), (4, 2), (2, 4)],
+        [(2, 2), (4, 4), (0, 4)],
+        [(2, 0), (2, 2), (0, 2)],
+        [(0, 0), (2, 0), (0, 2)],
+        []]                                 # 15
+    MIDDLE_PATCH_SET = [0, 4, 8, 15]
+
+    # modify path set
+    for idx in range(len(PATH_SET)):
+        if PATH_SET[idx]:
+            p = map(lambda vec: (vec[0] / 4.0, vec[1] / 4.0), PATH_SET[idx])
+            p = list(p)
+            PATH_SET[idx] = p + p[:1]
+
+    def decode(self, code):
+        # decode the code
+        middleType = self.MIDDLE_PATCH_SET[code & 0x03]
+        middleInvert = (code >> 2) & 0x01
+        cornerType = (code >> 3) & 0x0F
+        cornerInvert = (code >> 7) & 0x01
+        cornerTurn = (code >> 8) & 0x03
+        sideType = (code >> 10) & 0x0F
+        sideInvert = (code >> 14) & 0x01
+        sideTurn = (code >> 15) & 0x03
+        blue = (code >> 16) & 0x1F
+        green = (code >> 21) & 0x1F
+        red = (code >> 27) & 0x1F
+
+        foreColor = (red << 3, green << 3, blue << 3)
+
+        return (middleType, middleInvert, 0),\
+               (cornerType, cornerInvert, cornerTurn),\
+               (sideType, sideInvert, sideTurn),\
+            foreColor, ImageColor.getrgb('white')
+
+
+def render_identicon(code, size, renderer=None):
+    if not renderer:
+        renderer = DonRenderer
+    return renderer(code).render(size)
+
+
+if __name__ == '__main__':
+    import sys
+
+    if len(sys.argv) < 2:
+        print('usage: python identicon.py [CODE]....')
+        raise SystemExit
+
+    for code in sys.argv[1:]:
+        if code.startswith('0x') or code.startswith('0X'):
+            code = int(code[2:], 16)
+        elif code.startswith('0'):
+            code = int(code[1:], 8)
+        else:
+            code = int(code)
+
+        icon = render_identicon(code, 24)
+        icon.save('%08x.png' % code, 'PNG')
diff --git a/pelican-plugins/pelican_comment_system/import/__init__.py b/pelican-plugins/pelican_comment_system/import/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/pelican-plugins/pelican_comment_system/import/blogger_comment_export.py b/pelican-plugins/pelican_comment_system/import/blogger_comment_export.py
new file mode 100644
index 0000000000000000000000000000000000000000..fa4bb0859d2ffe67e0e14d4f60f6f3e59987d139
--- /dev/null
+++ b/pelican-plugins/pelican_comment_system/import/blogger_comment_export.py
@@ -0,0 +1,165 @@
+#! python3.6
+"""
+Export Comments from BLogger XML
+
+Takes in a Blogger export XML file and spits out each comment in a seperate
+file, such that can be used with the [Pelican Comment System]
+(https://bernhard.scheirle.de/posts/2014/March/29/static-comments-via-email/).
+
+May be simple to extend to export posts as well.
+
+For a more detailed desciption, read my blog post at
+    http://blog.minchin.ca/2016/12/blogger-comments-exported.html
+
+Author: Wm. Minchin -- minchinweb@gmail.com
+License: MIT
+Changes:
+
+ - 2016.12.29 -- initial release
+ - 2017.01.10 -- clean-up for addition in Pelican Comment System repo
+"""
+
+from pathlib import Path
+
+import untangle
+
+###############################################################################
+# Constants                                                                   #
+###############################################################################
+
+BLOGGER_EXPORT = r'c:\tmp\blog.xml'
+COMMENTS_DIR = 'comments'
+COMMENT_EXT = '.md'
+AUTHORS_FILENAME = 'authors.txt'
+
+###############################################################################
+# Main Code Body                                                              #
+###############################################################################
+
+authors_and_pics = []
+
+
+def main():
+    obj = untangle.parse(BLOGGER_EXPORT)
+
+    templates = 0
+    posts = 0
+    comments = 0
+    settings = 0
+    others = 0
+
+    for entry in obj.feed.entry:
+        try:
+            full_type = entry.category['term']
+        except TypeError:
+            # if a post is under multiple categories
+            for my_category in entry.category:
+                full_type = my_category['term']
+                # str.find() uses a return of `-1` to denote failure
+                if full_type.find('#') != -1:
+                    break
+            else:
+                others += 1
+
+        simple_type = full_type[full_type.find('#')+1:]
+
+        if 'settings' == simple_type:
+            settings += 1
+        elif 'post' == simple_type:
+            posts += 1
+            # process posts here
+        elif 'comment' == simple_type:
+            comments += 1
+            process_comment(entry, obj)
+        elif 'template' == simple_type:
+            templates += 1
+        else:
+            others += 1
+
+    export_authors()
+
+    print('''
+            {} template
+            {} posts (including drafts)
+            {} comments
+            {} settings
+            {} other entries'''.format(templates,
+                                       posts,
+                                       comments,
+                                       settings,
+                                       others))
+
+
+def process_comment(entry, obj):
+    # e.g. "tag:blogger.com,1999:blog-26967745.post-4115122471434984978"
+    comment_id = entry.id.cdata
+    # in ISO 8601 format, usable as is
+    comment_published = entry.published.cdata
+    comment_body = entry.content.cdata
+    comment_post_id = entry.thr_in_reply_to['ref']
+    comment_author = entry.author.name.cdata
+    comment_author_pic = entry.author.gd_image['src']
+    comment_author_email = entry.author.email.cdata
+
+    # add author and pic to global list
+    global authors_and_pics
+    authors_and_pics.append((comment_author, comment_author_pic))
+
+    # use this for a filename for the comment
+    # e.g. "4115122471434984978"
+    comment_short_id = comment_id[comment_id.find('post-')+5:]
+
+    comment_text = "date: {}\nauthor: {}\nemail: {}\n\n{}\n"\
+                        .format(comment_published,
+                                comment_author,
+                                comment_author_email,
+                                comment_body)
+
+    # article
+    for entry in obj.feed.entry:
+        entry_id = entry.id.cdata
+        if entry_id == comment_post_id:
+            article_entry = entry
+            break
+    else:
+        print("No matching article for comment", comment_id, comment_post_id)
+        # don't process comment further
+        return
+
+    # article slug
+    for link in article_entry.link:
+        if link['rel'] == 'alternate':
+            article_link = link['href']
+            break
+    else:
+        article_title = article_entry.title.cdata
+        print('Could not find slug for', article_title)
+        article_link = article_title.lower().replace(' ', '-')
+
+    article_slug = article_link[article_link.rfind('/')+1:
+                                                    article_link.find('.html')]
+
+    comment_filename = Path(COMMENTS_DIR).resolve()
+    # folder; if it doesn't exist, create it
+    comment_filename = comment_filename / article_slug
+    comment_filename.mkdir(parents=True, exist_ok=True)
+    # write the comment file
+    comment_filename = comment_filename / (comment_short_id + COMMENT_EXT)
+    comment_filename.write_text(comment_text)
+
+
+def export_authors():
+    to_export = set(authors_and_pics)
+    to_export = list(to_export)
+    to_export.sort()
+
+    str_export = ''
+    for i in to_export:
+        str_export += (i[0] + '\t\t' + i[1] + '\n')
+
+    authors_filename = Path(COMMENTS_DIR).resolve() / AUTHORS_FILENAME
+    authors_filename.write_text(str_export)
+
+
+if __name__ == "__main__":
+    main()
diff --git a/pelican-plugins/pelican_comment_system/pelican_comment_system.py b/pelican-plugins/pelican_comment_system/pelican_comment_system.py
new file mode 100644
index 0000000000000000000000000000000000000000..9d7f3b225355b683ae50f0d48585e241cc2f5537
--- /dev/null
+++ b/pelican-plugins/pelican_comment_system/pelican_comment_system.py
@@ -0,0 +1,240 @@
+# -*- coding: utf-8 -*-
+"""
+Pelican Comment System
+======================
+
+A Pelican plugin, which allows you to add comments to your articles.
+
+Author: Bernhard Scheirle
+"""
+from __future__ import unicode_literals
+import logging
+import os
+import copy
+
+logger = logging.getLogger(__name__)
+
+from itertools import chain
+from pelican import signals
+from pelican.readers import Readers
+from pelican.writers import Writer
+
+from . comment import Comment
+from . import avatars
+
+
+__version__ = "1.3.0"
+
+
+_all_comments = []
+_pelican_writer = None
+_pelican_obj = None
+
+def setdefault(pelican, settings):
+    from pelican.settings import DEFAULT_CONFIG
+    for key, value in settings:
+        DEFAULT_CONFIG.setdefault(key, value)
+
+    if not pelican:
+        return
+
+    for key, value in settings:
+        pelican.settings.setdefault(key, value)
+
+
+def pelican_initialized(pelican):
+    from pelican.settings import DEFAULT_CONFIG
+    settings = [
+        ('PELICAN_COMMENT_SYSTEM', False),
+        ('PELICAN_COMMENT_SYSTEM_DIR', 'comments'),
+        ('PELICAN_COMMENT_SYSTEM_IDENTICON_OUTPUT_PATH', 'images/identicon'),
+        ('PELICAN_COMMENT_SYSTEM_IDENTICON_DATA', ()),
+        ('PELICAN_COMMENT_SYSTEM_IDENTICON_SIZE', 72),
+        ('PELICAN_COMMENT_SYSTEM_AUTHORS', {}),
+        ('PELICAN_COMMENT_SYSTEM_FEED', os.path.join('feeds', 'comment.%s.atom.xml')),
+        ('PELICAN_COMMENT_SYSTEM_FEED_ALL', os.path.join('feeds', 'comments.all.atom.xml')),
+        ('COMMENT_URL', '#comment-{slug}')
+    ]
+
+    setdefault(pelican, settings)
+
+    DEFAULT_CONFIG['PAGE_EXCLUDES'].append(
+        DEFAULT_CONFIG['PELICAN_COMMENT_SYSTEM_DIR'])
+    DEFAULT_CONFIG['ARTICLE_EXCLUDES'].append(
+        DEFAULT_CONFIG['PELICAN_COMMENT_SYSTEM_DIR'])
+    pelican.settings['PAGE_EXCLUDES'].append(
+        pelican.settings['PELICAN_COMMENT_SYSTEM_DIR'])
+    pelican.settings['ARTICLE_EXCLUDES'].append(
+        pelican.settings['PELICAN_COMMENT_SYSTEM_DIR'])
+
+    global _pelican_obj
+    _pelican_obj = pelican
+
+
+def initialize(article_generator):
+    avatars.init(
+        article_generator.settings['OUTPUT_PATH'],
+        article_generator.settings[
+            'PELICAN_COMMENT_SYSTEM_IDENTICON_OUTPUT_PATH'],
+        article_generator.settings['PELICAN_COMMENT_SYSTEM_IDENTICON_DATA'],
+        article_generator.settings[
+            'PELICAN_COMMENT_SYSTEM_IDENTICON_SIZE'] / 3,
+        article_generator.settings['PELICAN_COMMENT_SYSTEM_AUTHORS'],
+    )
+
+    # Reset old states (autoreload mode)
+    global _all_comments
+    global _pelican_writer
+    _pelican_writer = _pelican_obj._get_writer()
+    _all_comments = []
+
+def warn_on_slug_collision(items):
+    slugs = {}
+    for comment in items:
+        if not comment.slug in slugs:
+            slugs[comment.slug] = [comment]
+        else:
+            slugs[comment.slug].append(comment)
+
+    for slug, itemList in slugs.items():
+        len_ = len(itemList)
+        if len_ > 1:
+            logger.warning('There are %s comments with the same slug: %s', len_, slug)
+            for x in itemList:
+                logger.warning('    %s', x.source_path)
+
+
+def write_feed_all(gen, writer):
+    if gen.settings['PELICAN_COMMENT_SYSTEM'] is not True:
+        return
+    if gen.settings['PELICAN_COMMENT_SYSTEM_FEED_ALL'] is None:
+        return
+
+    context = copy.copy(gen.context)
+    context['SITENAME'] += " - All Comments"
+    context['SITESUBTITLE'] = ""
+    path = gen.settings['PELICAN_COMMENT_SYSTEM_FEED_ALL']
+
+    global _all_comments
+    _all_comments = sorted(_all_comments)
+    _all_comments.reverse()
+
+    for com in _all_comments:
+        com.title = com.article.title + " - " + com.title
+        com.override_url = com.article.url + com.url
+
+    writer.write_feed(_all_comments, context, path)
+
+
+def write_feed(gen, items, context, slug):
+    if gen.settings['PELICAN_COMMENT_SYSTEM_FEED'] is None:
+        return
+
+    path = gen.settings['PELICAN_COMMENT_SYSTEM_FEED'] % slug
+    _pelican_writer.write_feed(items, context, path)
+
+
+def process_comments(article_generator):
+    for article in article_generator.articles:
+        add_static_comments(article_generator, article)
+
+def mirror_to_translations(article):
+    for translation in article.translations:
+        translation.comments_count = article.comments_count
+        translation.comments = article.comments
+
+def add_static_comments(gen, content):
+    if gen.settings['PELICAN_COMMENT_SYSTEM'] is not True:
+        return
+
+    global _all_comments
+
+    content.comments_count = 0
+    content.comments = []
+    mirror_to_translations(content)
+
+    # Modify the local context, so we get proper values for the feed
+    context = copy.copy(gen.context)
+    context['SITEURL'] += "/" + content.url
+    context['SITENAME'] += " - Comments: " + content.title
+    context['SITESUBTITLE'] = ""
+
+    folder = os.path.join(
+        gen.settings['PATH'],
+        gen.settings['PELICAN_COMMENT_SYSTEM_DIR'],
+        content.slug
+    )
+
+    if not os.path.isdir(folder):
+        logger.debug("No comments found for: %s", content.slug)
+        write_feed(gen, [], context, content.slug)
+        return
+
+    reader = Readers(gen.settings)
+    comments = []
+    replies = []
+
+    for file in os.listdir(folder):
+        name, extension = os.path.splitext(file)
+        if extension[1:].lower() in reader.extensions:
+            com = reader.read_file(
+                base_path=folder, path=file,
+                content_class=Comment, context=context)
+
+            com.article = content
+            _all_comments.append(com)
+
+            if hasattr(com, 'replyto'):
+                replies.append(com)
+            else:
+                comments.append(com)
+
+    feed_items = sorted(comments + replies)
+    feed_items.reverse()
+    warn_on_slug_collision(feed_items)
+
+    write_feed(gen, feed_items, context, content.slug)
+
+    # TODO: Fix this O(n²) loop
+    for reply in replies:
+        found_parent = False
+        for comment in chain(comments, replies):
+            if comment.slug == reply.replyto:
+                comment.addReply(reply)
+                found_parent = True
+                break
+        if not found_parent:
+            logger.warning('Comment "%s/%s" is a reply to non-existent comment "%s". '
+                'Make sure the replyto attribute is set correctly.',
+                content.slug, reply.slug, reply.replyto)
+
+    count = 0
+    for comment in comments:
+        comment.sortReplies()
+        count += comment.countReplies()
+
+    comments = sorted(comments)
+
+    content.comments_count = len(comments) + count
+    content.comments = comments
+    mirror_to_translations(content)
+
+
+def writeIdenticonsToDisk(gen, writer):
+    avatars.generateAndSaveMissingAvatars()
+
+
+def pelican_finalized(pelican):
+    if pelican.settings['PELICAN_COMMENT_SYSTEM'] is not True:
+        return
+    global _all_comments
+    print('Processed %s comment(s)' % len(_all_comments))
+
+
+def register():
+    signals.initialized.connect(pelican_initialized)
+    signals.article_generator_init.connect(initialize)
+    signals.article_generator_finalized.connect(process_comments)
+    signals.article_writer_finalized.connect(writeIdenticonsToDisk)
+    signals.article_writer_finalized.connect(write_feed_all)
+    signals.finalized.connect(pelican_finalized)
diff --git a/pelican-plugins/pelican_comment_system/theme/static/js/comments.js b/pelican-plugins/pelican_comment_system/theme/static/js/comments.js
new file mode 100644
index 0000000000000000000000000000000000000000..2625428b7b8d2fc30a280c3893db3e5049e429b9
--- /dev/null
+++ b/pelican-plugins/pelican_comment_system/theme/static/js/comments.js
@@ -0,0 +1,76 @@
+//requires jquery
+var CommentSystem = {
+	email_user:   "not set",
+	email_domain: "not set",
+	display_replyto_html: function(comment_content, article_slug, author) {return ''},
+
+	cancelReply: function() {
+		$('#pcs-comment-form-input-replyto').val("");
+		$('#pcs-comment-form-display-replyto').hide();
+	},
+
+	setReply: function(slug, author) {
+		slug   = decodeURIComponent(slug);
+		author = decodeURIComponent(author);
+
+		$('html, body').animate({ scrollTop: $("#pcs-comment-form").offset().top }, 1000);
+
+		$('#pcs-comment-form-input-replyto').val(slug);
+
+		var jquery_escaped_id = slug.replace('.', '\\.')
+		var commentContent = $('#comment-' + jquery_escaped_id + ' .pcs-comment-content:first').text().trim()
+
+		$('#pcs-comment-form-display-replyto').html(this.display_replyto_html(commentContent, slug, author));
+		$('#pcs-comment-form-display-replyto').show();
+	},
+
+	getMailtoLink: function(slug) {
+		var subject = 'Comment for \'' + slug + '\'' ;
+
+		var now = new Date();
+		tzo = -now.getTimezoneOffset(),
+		dif = tzo >= 0 ? '+' : '-',
+		pad = function(num) {
+			norm = Math.abs(Math.floor(num));
+			return (norm < 10 ? '0' : '') + norm;
+		};
+		var body = ''
+			+ 'Hey,\nI posted a new comment on ' + document.URL + '\n\nGreetings ' + $("#pcs-comment-form-input-name").val() + '\n\n\n'
+			+ 'Raw comment data:\n'
+			+ '----------------------------------------\n'
+			+ 'email: \n' // just that I don't forget to write it down
+			+ 'date: ' + now.getFullYear()
+					+ '-' + pad(now.getMonth()+1)
+					+ '-' + pad(now.getDate())
+					+ 'T' + pad(now.getHours())
+					+ ':' + pad(now.getMinutes())
+					+ dif + pad(tzo / 60)
+					+ ':' + pad(tzo % 60) +'\n'
+			+ 'author: ' + $("#pcs-comment-form-input-name").val() + '\n';
+
+		var replyto = $('#pcs-comment-form-input-replyto').val();
+		if (replyto.length != 0)
+		{
+			body += 'replyto: ' + replyto + '\n'
+		}
+
+		var url = $("#pcs-comment-form-input-website").val();
+		if (url.length != 0)
+		{
+			if(url.substr(0,7) != 'http://' && url.substr(0,8) != 'https://'){
+				url = 'http://' + url;
+			}
+			body += 'website: ' + url + '\n';
+		}
+		body += '\n'
+			+ $("#pcs-comment-form-input-textarea").val() + '\n'
+			+ '----------------------------------------\n';
+
+		var link = 'mailto:' + this.email_user + '@' + this.email_domain + '?subject='
+			+ encodeURIComponent(subject)
+			+ "&body="
+			+ encodeURIComponent(body.replace(/\r?\n/g, "\r\n"));
+		console.log(link)
+		return link;
+	}
+}
diff --git a/pelican-plugins/pelican_comment_system/theme/templates/pcs/comments.html b/pelican-plugins/pelican_comment_system/theme/templates/pcs/comments.html
new file mode 100644
index 0000000000000000000000000000000000000000..7dcb2b0737737f20f43ce1bb03051985116c51ce
--- /dev/null
+++ b/pelican-plugins/pelican_comment_system/theme/templates/pcs/comments.html
@@ -0,0 +1,162 @@
+{% macro comments_styles() %}
+{% if PELICAN_COMMENT_SYSTEM %}
+{# NOTE:
+ # Instead of using this macro copy these styles in your main css file
+ # This marco is only here to allow a quickstart with nice styles
+ #}
+<style>
+#pcs-comment-form input,
+#pcs-comment-form textarea {
+	width: 100%;
+}
+#pcs-comment-form-display-replyto {
+	border: solid 1px black;
+	padding: 2px;
+}
+#pcs-comment-form-display-replyto p {
+	margin-top: 0.5em;
+	margin-bottom: 0.5em;
+}
+#pcs-comments ul {
+	list-style: none;
+}
+#pcs-comments .comment-left {
+	display: table-cell;
+	padding-right: 10px;
+}
+#pcs-comments .comment-body {
+	display: table-cell;
+	vertical-align: top;
+	width: 100%;
+}
+</style>
+{% endif %}
+{% endmacro %}
+
+{% macro comments_form() %}
+{% if PELICAN_COMMENT_SYSTEM %}
+<section>
+	<form id="pcs-comment-form" action="#">
+		<legend>Add a Comment</legend>
+		<input type="hidden" id="pcs-comment-form-input-replyto">
+		<fieldset>
+			<label for="pcs-comment-form-input-name">Name</label>
+			<input  id="pcs-comment-form-input-name" type="text" placeholder="Enter your name or nickname" />
+		</fieldset>
+		<fieldset>
+			<label for="pcs-comment-form-input-website">Website</label>
+			<input  id="pcs-comment-form-input-website" type="text" placeholder="Enter your website (optional)" />
+		</fieldset>
+		<fieldset>
+			<label   for="pcs-comment-form-input-textarea">Your Comment</label>
+			<textarea id="pcs-comment-form-input-textarea" rows="5" style="resize:vertical;" placeholder="Your comment"></textarea>
+			<p>You can use the <a href="https://en.wikipedia.org/wiki/Markdown">Markdown</a> syntax to format your comment.</p>
+			<div style="display: none; " id="pcs-comment-form-display-replyto"></div>
+		</fieldset>
+
+		<button type="submit"
+				id="pcs-comment-form-button-submit"
+				{# Piwik Track click on comment button
+				onclick="javascript:_paq.push(['trackEvent', 'comment', '{{ article.title }}', document.getElementById('pcs-comment-form-input-textarea').value]);" #}
+				>Post via email</button>
+
+		{% if PELICAN_COMMENT_SYSTEM_FEED and article %}
+			<a href="{{ SITEURL }}/{{ PELICAN_COMMENT_SYSTEM_FEED|format(article.slug) }}">
+				Comment Atom Feed
+			</a>
+		{% endif %}
+	</form>
+</section>
+{% endif %}
+{% endmacro %}
+
+{% macro comments_with_form() %}
+{% if PELICAN_COMMENT_SYSTEM %}
+<section id="pcs-comments">
+	<header>
+		<h2>Comments</h2>
+		<hr/>
+	</header>
+	{% if article.comments %}
+		<ul>
+		{% for comment in article.comments recursive %}
+			<li id="comment-{{comment.slug}}">
+				<div class="comment-left">
+					<img    src="{{ SITEURL }}/{{ comment.avatar }}"
+							alt="Avatar"
+							height="{{ PELICAN_COMMENT_SYSTEM_IDENTICON_SIZE }}"
+							width="{{ PELICAN_COMMENT_SYSTEM_IDENTICON_SIZE }}">
+				</div>
+				<div class="comment-body">
+					<div style="float:right;">
+						<a role="button" href="{{ SITEURL }}/{{ article.url }}#comment-{{comment.slug}}" rel="bookmark" title="Permalink to this comment">Permalink</a>
+						<button onclick="CommentSystem.setReply('{{comment.slug | urlencode}}', '{{comment.author | urlencode}}');">Reply</button>
+					</div>
+					<h4>
+						{% if comment.metadata['website'] %}
+							<a href="{{comment.metadata['website']}}">{{ comment.author }}</a>
+						{% else %}
+							{{ comment.author }}
+						{% endif %}
+					</h4>
+					<p>
+						Posted on
+						<time datetime="{{ comment.date.isoformat() }}" title="{{ comment.date.isoformat() }}">{{ comment.locale_date }}</time>
+					</p>
+					<div class="pcs-comment-content" {# class used as id in comments.js#}>
+						{{ comment.content }}
+					</div>
+					{% if comment.replies %}
+						<hr>
+						<ul>
+							{{ loop(comment.replies) }}
+						</ul>
+					{% endif %}
+				</div>
+			</li>
+		{% endfor %}
+		</ul>
+	{% else %}
+		<p>There are no comments yet.</p>
+	{% endif %}
+	{{ comments_form() }}
+</section>
+{% endif %}
+{% endmacro %}
+
+
+{% macro comments_js(user, domain, includeJquery=True) %}
+{% if PELICAN_COMMENT_SYSTEM %}
+	{% if includeJquery %}
+		<script type="text/javascript" src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
+	{% endif %}
+	<script type="text/javascript" src="{{ SITEURL }}/{{ THEME_STATIC_DIR }}/js/comments.js"></script>
+	<script type="text/javascript">
+		$(document).ready(function() {
+			CommentSystem.email_user   = "{{ user }}";
+			CommentSystem.email_domain = "{{ domain }}";
+			CommentSystem.display_replyto_html = function(comment_content, article_slug, author) { 
+				return ''
+					+ '<button style="float:right;" onclick="CommentSystem.cancelReply(); return false;" title="Cancel the reply">'
+					+ 	'×'
+					+ '</button>'
+					+ '<p>This comment will be posted as a reply to \'<a title="'+comment_content+'" href="#comment-'+article_slug+'">'+author+'</a>\'</p>';
+			};
+
+			$('#pcs-comment-form').on("submit",
+				function( event )
+				{
+					event.preventDefault();
+					$(location).attr('href', CommentSystem.getMailtoLink("{{ article.slug }}"));
+				}
+			);
+		});
+	</script>
+{% endif %}
+{% endmacro %}
+
+{% macro comments_quickstart(user, domain) %}
+	{{ comments_styles() }}
+	{{ comments_with_form() }}
+	{{ comments_js(user, domain) }}
+{% endmacro %}
diff --git a/pelican-plugins/pelican_unity_webgl/LICENSE.txt b/pelican-plugins/pelican_unity_webgl/LICENSE.txt
new file mode 100644
index 0000000000000000000000000000000000000000..63a216a3ba6acc391b76cc869acecc40a72a8986
--- /dev/null
+++ b/pelican-plugins/pelican_unity_webgl/LICENSE.txt
@@ -0,0 +1,18 @@
+Copyright (c) 2017 Mr.Page
+Permission is hereby granted, free of charge, to any person obtaininga copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
\ No newline at end of file
diff --git a/pelican-plugins/pelican_unity_webgl/README.rst b/pelican-plugins/pelican_unity_webgl/README.rst
new file mode 100644
index 0000000000000000000000000000000000000000..2d7e1dcfce71e1a2698202e1938a0938f54b28a0
--- /dev/null
+++ b/pelican-plugins/pelican_unity_webgl/README.rst
@@ -0,0 +1,81 @@
+===================
+Pelican Unity WebGL
+===================
+
+Compatibility
+=============
+
+Unity >= 5.6 (Tested on 5.6)
+
+Usage
+=====
+
+Basic directive
+---------------
+
+.. code-block:: rst
+
+	.. unitywebgl GAME_ID
+
+**GAME_ID** is the required parameter. This is name of the directory, that contains your webgl build. This directory should be place in path, specified in config.py file, or with :gameroot: parameter.
+
+Optional parameters
+-------------------
+
+.. code-block:: rst
+
+	.. unitywebgl GAME_ID
+		:gameroot: /games/new/
+		:template: /games/templates/newtemplate
+		:width: 640
+		:height: 480
+
++-------------------+------------------+---------------------------------------------------------+
+| Parameter         | default value    |                                                         |
++===================+==================+=========================================================+
+| gameroot          | /games           | path to directory with games                            |
++-------------------+------------------+---------------------------------------------------------+
+| templatepath      | /games/utemplate | path to template                                        |
++-------------------+------------------+---------------------------------------------------------+
+| width             |                                                                            |
++-------------------+ Player resolution                                                          |
+| height            |                                                                            |
++-------------------+------------------+---------------------------------------------------------+
+
+.. note::
+	Test pelican project can be found in *pelican_demo.zip* archive `here <https://github.com/mrpeidz/Unity-WebGL-RST-directive>`_
+
+Configuration
+=============
+
+You can change default root directory, template path and player resolution in the *config.py* file.
+
+Modifying html output template
+------------------------------
+
+You can modify the *template.txt* file to change html output.
+
+Template parameters explanation:
+
++-------------------+-------------------------------+
+| Parameter         | default value                 |
++===================+===============================+
+| 0                 | game directory name           |
++-------------------+-------------------------------+
+| 1                 | directory, where games placed |
++-------------------+-------------------------------+
+| 2                 | template directory            |
++-------------------+-------------------------------+
+| 3                 | width                         |
++-------------------+-------------------------------+
+| 4                 | height                        |
++-------------------+-------------------------------+
+
+About
+=======
+
+License: `MIT <https://opensource.org/licenses/MIT>`_
+
+`Unity WebGL RST Directive github page <https://github.com/mrpeidz/Unity-WebGL-RST-directive>`_
+
+**Mr.Page (c) 2017**
\ No newline at end of file
diff --git a/pelican-plugins/pelican_unity_webgl/UnityGameDirective.py b/pelican-plugins/pelican_unity_webgl/UnityGameDirective.py
new file mode 100644
index 0000000000000000000000000000000000000000..dac0c9412d40e07b6c0733c1f446f6520a681db3
--- /dev/null
+++ b/pelican-plugins/pelican_unity_webgl/UnityGameDirective.py
@@ -0,0 +1,86 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2017 Mr.Page
+# Permission is hereby granted, free of charge, to any person obtaininga copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+from __future__ import unicode_literals
+
+from docutils import nodes
+from docutils.parsers.rst import directives, Directive
+
+from . import config
+
+import os
+
+
+class UnityWebgl(Directive):
+    required_arguments = 1
+    optional_arguments = 4
+
+    option_spec = {
+        'width': directives.positive_int,
+        'height': directives.positive_int,
+        'gameroot': directives.unchanged_required,
+        'template': directives.unchanged_required,
+    }
+
+    final_argument_whitespace = False
+    has_content = False
+
+    def load_template(self, game, gamesroot, templatepath, width, height):
+        basepath = os.path.dirname(__file__)
+        filepath = os.path.abspath(os.path.join(basepath, 'template.txt'))
+        with open(filepath, 'r') as template:
+            data = template.read()
+            return data.format(game, gamesroot, templatepath, width, height)
+
+    def run(self):
+
+        # load config
+
+        game = self.arguments[0].strip()
+        games_path = config.GAMES_ROOT_DIR
+        template_path = config.TEMPLATE_PATH
+        width = config.DEFAULT_WIDTH
+        height = config.DEFAULT_HEIGHT
+
+        # get params
+
+        if 'width' in self.options:
+            width = self.options['width']
+        if 'height' in self.options:
+            height = self.options['height']
+        if 'gameroot' in self.options:
+            games_path = self.options['gameroot']
+        if 'template' in self.options:
+            template_path = self.options['template']
+
+        # remove slashes
+
+        games_path = games_path.rstrip('/')
+        template_path = template_path.rstrip('/')
+
+        html = self.load_template(game, games_path, template_path, width, height)
+
+        return [
+            nodes.raw('', html, format='html')]
+
+
+def register():
+    directives.register_directive('unitywebgl', UnityWebgl)
diff --git a/pelican-plugins/pelican_unity_webgl/__init__.py b/pelican-plugins/pelican_unity_webgl/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..716856d8588e3695e76f9a1edbae97b08f801641
--- /dev/null
+++ b/pelican-plugins/pelican_unity_webgl/__init__.py
@@ -0,0 +1,3 @@
+# -*- coding: utf-8 -*-
+
+from .UnityGameDirective import register
diff --git a/pelican-plugins/pelican_unity_webgl/config.py b/pelican-plugins/pelican_unity_webgl/config.py
new file mode 100644
index 0000000000000000000000000000000000000000..d593bd4ec47c9a629c24960a713d3e3d42230735
--- /dev/null
+++ b/pelican-plugins/pelican_unity_webgl/config.py
@@ -0,0 +1,10 @@
+# unity webgl options
+
+DEFAULT_WIDTH = 960
+DEFAULT_HEIGHT = 600
+DEFAULT_ALIGN = 'center'
+
+# paths
+
+GAMES_ROOT_DIR = '/games'  # directory with games
+TEMPLATE_PATH = '/games/utemplate'  # template path
diff --git a/pelican-plugins/pelican_unity_webgl/template.txt b/pelican-plugins/pelican_unity_webgl/template.txt
new file mode 100644
index 0000000000000000000000000000000000000000..de31d77d9d488873624fb403a1d48f2a490dcab8
--- /dev/null
+++ b/pelican-plugins/pelican_unity_webgl/template.txt
@@ -0,0 +1,9 @@
+<!-- Begin Unity webgl app -->
+    <link rel="stylesheet" href="{2}/style.css">
+    <script src="{2}/UnityProgress.js"></script>
+    <script src="{1}/{0}/Build/UnityLoader.js"></script>
+    <script>
+        var gameInstance = UnityLoader.instantiate("gameContainer", "{1}/{0}/Build/{0}.json", {{onProgress: UnityProgress}});
+    </script>
+    <div id="gameContainer" style="width: {3}px; height: {4}px; left: 50%; transform: translateX(-50%);"></div>
+<!-- End Unity webgl app -->
\ No newline at end of file
diff --git a/pelican-plugins/pelican_unity_webgl/test_unitywebgl.py b/pelican-plugins/pelican_unity_webgl/test_unitywebgl.py
new file mode 100644
index 0000000000000000000000000000000000000000..739fd75b3c7dfdfdcd9dd1abc58d344aa64b7c06
--- /dev/null
+++ b/pelican-plugins/pelican_unity_webgl/test_unitywebgl.py
@@ -0,0 +1,70 @@
+from unittest import TestCase
+from docutils.core import publish_string
+from docutils.parsers.rst import directives
+
+from pelican_unity_webgl import config
+from pelican_unity_webgl.UnityGameDirective import UnityWebgl
+
+
+class TestUnityWebgl(TestCase):
+
+    def test_directive_basic(self):
+
+        # test directive html output with default template
+        # and settings
+
+        directives.register_directive('unitywebgl', UnityWebgl)
+
+        config.GAMES_ROOT_DIR = '/test_games_root'
+        config.TEMPLATE_PATH = '/test_template_path/template'
+        config.DEFAULT_WIDTH = 960
+        config.DEFAULT_HEIGHT = 600
+
+        html =['<link rel="stylesheet" href="/test_template_path/template/style.css">',
+        '<script src="/test_template_path/template/UnityProgress.js">',
+        '<script src="/test_games_root/testgame/Build/UnityLoader.js">',
+        '<script>\n        var gameInstance = UnityLoader.instantiate("gameContainer", "/test_games_root/testgame/Build/testgame.json", {onProgress: UnityProgress});\n    </script>',
+        '<div id="gameContainer" style="width: 960px; height: 600px; left: 50%; transform: translateX(-50%);"></div>']
+
+        res = publish_string('.. unitywebgl:: testgame', writer_name='html', settings_overrides={'output_encoding': 'unicode'})
+
+        passed = True
+
+        for line in html:
+            if line not in res:
+                passed = False
+                break
+
+        assert passed
+
+
+    def test_directive_with_params(self):
+
+        # test directive html output with all optional parameters,
+        # default template and settings
+
+        directives.register_directive('unitywebgl', UnityWebgl)
+
+        config.GAMES_ROOT_DIR = 'test_games_root'
+        config.TEMPLATE_PATH = 'test_template_path'
+        config.DEFAULT_WIDTH = 960
+        config.DEFAULT_HEIGHT = 600
+
+        html =['<link rel="stylesheet" href="/games2/template2/style.css">',
+        '<script src="/games2/template2/UnityProgress.js">',
+        '<script src="/games2/testgame/Build/UnityLoader.js">',
+        '<script>\n        var gameInstance = UnityLoader.instantiate("gameContainer", "/games2/testgame/Build/testgame.json", {onProgress: UnityProgress});\n    </script>',
+        '<div id="gameContainer" style="width: 640px; height: 480px; left: 50%; transform: translateX(-50%);"></div>']
+
+        d = '.. unitywebgl:: testgame\n\t:gameroot: /games2\n\t:template: /games2/template2\n\t:width: 640\n\t:height: 480'
+
+        res = publish_string(d, writer_name='html', settings_overrides={'output_encoding': 'unicode'})
+
+        passed = True
+
+        for line in html:
+            if line not in res:
+                passed = False
+                break
+
+        assert passed
\ No newline at end of file
diff --git a/pelican-plugins/permalinks/README.md b/pelican-plugins/permalinks/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..ae00154767f7411ff1223e4e8f919d7c56ff2f43
--- /dev/null
+++ b/pelican-plugins/permalinks/README.md
@@ -0,0 +1,25 @@
+permalink
+=========
+
+This plugin enables a kind of permalink which can be used to refer to a piece
+of content which is resistant to the file being moved or renamed.
+
+It does this by creating additional output html in `PERMALINK_PATH`
+(default permalinks/) which include redirect code to point user at original
+page.
+
+To work each page has to have an additional piece of metadata with the key
+`permalink_id` (configurable with `PERMALINK_ID_METADATA_KEY`
+which should remain static even through renames and should also
+be unique on the site.
+
+This can be generated automatically with the filetime_from_git module and
+the `GIT_FILETIME_GENERATE_PERMALINK` option. 
+This should always be used with `GIT_FILETIME_FOLLOW` to ensure this
+persists across renames.
+
+
+Hacky redirects
+---------------
+To make this work with things like github.io I'm forced to use HTML and
+Javascript redirects rather than HTTP redirects which is obviously suboptimal.
diff --git a/pelican-plugins/permalinks/__init__.py b/pelican-plugins/permalinks/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..f4ede4e144135850df4c651eab844f7634f7bec2
--- /dev/null
+++ b/pelican-plugins/permalinks/__init__.py
@@ -0,0 +1 @@
+from .permalinks import register
diff --git a/pelican-plugins/permalinks/permalinks.py b/pelican-plugins/permalinks/permalinks.py
new file mode 100644
index 0000000000000000000000000000000000000000..fec838db36bdb86f3d633dbe54905015e75bdd45
--- /dev/null
+++ b/pelican-plugins/permalinks/permalinks.py
@@ -0,0 +1,156 @@
+# -*- coding: utf-8 -*-
+"""
+This plugin enables a kind of permalink which can be used to refer to a piece
+of content which is resistant to the file being moved or renamed.
+"""
+import itertools
+import logging
+import os
+import os.path
+
+from pelican import signals
+from pelican.generators import Generator
+from pelican.utils import clean_output_dir
+from pelican.utils import mkdir_p
+
+logger = logging.getLogger(__name__)
+
+
+def article_url(content):
+    '''
+    Get the URL for an item of content
+    '''
+    return '{content.settings[SITEURL]}/{content.url}'.format(
+        content=content)
+
+
+REDIRECT_STRING = '''
+<!DOCTYPE HTML>
+<html lang="en-US">
+    <head>
+        <meta charset="UTF-8">
+        <meta http-equiv="refresh" content="0;url={url}">
+        <script type="text/javascript">
+            window.location.href = "{url}"
+        </script>
+        <title>Page Redirection to {title}</title>
+    </head>
+    <body>
+        If you are not redirected automatically, follow the
+        <a href='{url}'>link to {title}</a>
+    </body>
+</html>
+'''
+
+
+class PermalinkGenerator(Generator):
+    '''
+    Generate a redirect page for every item of content with a
+    permalink_id metadata
+    '''
+    def generate_context(self):
+        '''
+        Setup context
+        '''
+        self.permalink_output_path = os.path.join(
+            self.output_path, self.settings['PERMALINK_PATH'])
+        self.permalink_id_metadata_key = (
+            self.settings['PERMALINK_ID_METADATA_KEY'])
+
+    def generate_output(self, writer=None):
+        '''
+        Generate redirect files
+        '''
+        logger.info(
+            'Generating permalink files in %r', self.permalink_output_path)
+
+        clean_output_dir(self.permalink_output_path, [])
+        mkdir_p(self.permalink_output_path)
+        for content in itertools.chain(
+                self.context['articles'], self.context['pages']):
+
+            for permalink_id in content.get_permalink_ids_iter():
+                permalink_path = os.path.join(
+                    self.permalink_output_path, permalink_id) + '.html'
+
+                redirect_string = REDIRECT_STRING.format(
+                    url=article_url(content),
+                    title=content.title)
+                open(permalink_path, 'w').write(redirect_string)
+
+
+def get_permalink_ids_iter(self):
+    '''
+    Method to get permalink ids from content. To be bound to the class last
+    thing.
+    '''
+    permalink_id_key = self.settings['PERMALINK_ID_METADATA_KEY']
+    permalink_ids = self.metadata.get(permalink_id_key, '')
+
+    for permalink_id in permalink_ids.split(','):
+        if permalink_id:
+            yield permalink_id.strip()
+
+
+def get_permalink_ids(self):
+    '''
+    Method to get permalink ids from content. To be bound to the class last
+    thing.
+    '''
+    return list(self.get_permalink_ids_iter())
+
+
+def get_permalink_path(self):
+    """Get just path component of permalink."""
+    try:
+        first_permalink_id = next(self.get_permalink_ids_iter())
+    except StopIteration:
+        return None
+
+    return '/{settings[PERMALINK_PATH]}/{first_permalink}.html'.format(
+        settings=self.settings, first_permalink=first_permalink_id)
+
+
+def get_permalink_url(self):
+    '''
+    Get a permalink URL
+    '''
+    return "/".join((self.settings['SITEURL'], self.get_permalink_path()))
+
+
+PERMALINK_METHODS = (
+    get_permalink_ids_iter,
+    get_permalink_ids,
+    get_permalink_url,
+    get_permalink_path,
+)
+
+
+def add_permalink_methods(content_inst):
+    '''
+    Add permalink methods to object
+    '''
+    for permalink_method in PERMALINK_METHODS:
+        setattr(
+            content_inst,
+            permalink_method.__name__,
+            permalink_method.__get__(content_inst, content_inst.__class__))
+
+
+def add_permalink_option_defaults(pelicon_inst):
+    '''
+    Add perlican defaults
+    '''
+    pelicon_inst.settings.setdefault('PERMALINK_PATH', 'permalinks')
+    pelicon_inst.settings.setdefault(
+        'PERMALINK_ID_METADATA_KEY', 'permalink_id')
+
+
+def get_generators(_pelican_object):
+    return PermalinkGenerator
+
+
+def register():
+    signals.get_generators.connect(get_generators)
+    signals.content_object_init.connect(add_permalink_methods)
+    signals.initialized.connect(add_permalink_option_defaults)
diff --git a/pelican-plugins/photos/README.md b/pelican-plugins/photos/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..323dd6ce467766a39b0f3e55ed9e0d9e4134f38e
--- /dev/null
+++ b/pelican-plugins/photos/README.md
@@ -0,0 +1,296 @@
+# Photos
+
+**NOTE:** [This plugin has been moved to its own repository](https://github.com/pelican-plugins/photos).
+Please file any issues/PRs there. Once all plugins have been migrated to the
+[new Pelican Plugins organization](https://github.com/pelican-plugins>),
+this monolithic repository will be archived.
+
+Use Photos to add a photo or a gallery of photos to an article, or to include photos in the body text. Photos are kept separately, as an organized library of high resolution photos, and resized as needed.
+
+## How to install and configure
+
+The plug-in requires `Pillow`: the Python Imaging Library and optionally `Piexif`, whose installation are outside the scope of this document.
+
+The plug-in resizes the referred photos, and generates thumbnails for galleries and associated photos, based on the following configuration and default values:
+
+`PHOTO_LIBRARY = "~/Pictures"`
+:	Absolute path to the folder where the original photos are kept, organized in sub-folders.
+
+`PHOTO_GALLERY = (1024, 768, 80)`
+:	For photos in galleries, maximum width and height, plus JPEG quality as a percentage. This would typically be the size of the photo displayed when the reader clicks a thumbnail.
+
+`PHOTO_ARTICLE = (760, 506, 80)`
+:	For photos associated with articles, maximum width, height, and quality. The maximum size would typically depend on the needs of the theme. 760px is suitable for the theme `notmyidea`.
+
+`PHOTO_THUMB = (192, 144, 60)`
+:	For thumbnails, maximum width, height, and quality.
+
+`PHOTO_SQUARE_THUMB = False`
+:	Crops thumbnails to make them square.
+
+`PHOTO_RESIZE_JOBS = 5`
+: Number of parallel resize jobs to be run. Defaults to 1.
+
+`PHOTO_WATERMARK = True`
+: Adds a watermark to all photos in articles and pages. Defaults to using your site name.
+
+`PHOTO_WATERMARK_TEXT' = SITENAME`
+: Allow the user to change the watermark text or remove it completely. By default it uses [SourceCodePro-Bold](http://www.adobe.com/products/type/font-information/source-code-pro-readme.html) as the font.
+
+`PHOTO_WATERMARK_IMG = ''`
+: Allows the user to add an image in addition to or as the only watermark. Set the variable to the location.
+
+**The following features require the piexif library**
+`PHOTO_EXIF_KEEP = True`
+: Keeps the exif of the input photo.
+
+`PHOTO_EXIF_REMOVE_GPS = True`
+: Removes any GPS information from the files exif data.
+
+`PHOTO_EXIF_COPYRIGHT = 'COPYRIGHT'`
+: Attaches an author and a license to the file. Choices include:
+	- `COPYRIGHT`: Copyright
+	- `CC0`: Public Domain
+	- `CC-BY-NC-ND`: Creative Commons Attribution-NonCommercial-NoDerivatives
+	- `CC-BY-NC-SA`: Creative Commons Attribution-NonCommercial-ShareAlike
+	- `CC-BY`: Creative Commons Attribution
+	- `CC-BY-SA`: Creative Commons Attribution-ShareAlike
+	- `CC-BY-NC`: Creative Commons Attribution-NonCommercial
+	- `CC-BY-ND`: Creative Commons Attribution-NoDerivatives
+
+`PHOTO_EXIF_COPYRIGHT_AUTHOR = 'Your Name Here'`
+: Adds an author name to the photo's exif and copyright statement. Defaults to `AUTHOR` value from the `pelicanconf.py`
+
+The plug-in automatically resizes the photos and publishes them to the following output folder:
+
+    ./output/photos
+
+**WARNING:** The plug-in can take hours to resize 40,000 photos, therefore, photos and thumbnails are only generated once. Clean the output folders to regenerate the resized photos again.
+
+## How to use
+
+Maintain an organized library of high resolution photos somewhere on disk, using folders to group related images. The default path `~/Pictures` is convenient for Mac OS X users.
+
+* To create a gallery of photos, add the metadata field `gallery: {photo}folder` to an article. To simplify the transition from the plug-in Gallery, the syntax `gallery: {filename}folder` is also accepted.
+* You can now have multiple galleries. The galleries need to be seperated by a comma in the metadata field. The syntax is gallery: `{photo}folder, {photo}folder2`. You can also add titles to your galleries. The syntax is: `{photo}folder, {photo}folder2{This is a title}`. Using the following example the first gallery would have the title of the folder location and the second would have the title `This is a tile.`
+* To use an image in the body of the text, just use the syntax `{photo}folder/image.jpg` instead of the usual `{filename}/images/image.jpg`.
+* To use an image in the body of the text, which can be used with [Lightbox](http://lokeshdhakar.com/projects/lightbox2/) just use the syntax `{lightbox}folder/image.jpg`. For use with other implementations, the gallery and caption attribute names can be set with `PHOTO_LIGHTBOX_GALLERY_ATTR` and `PHOTO_LIGHTBOX_CAPTION_ATTR`.
+* To associate an image with an article, add the metadata field `image: {photo}folder/image.jpg` to an article. Use associated images to improve navigation. For compatibility, the syntax `image: {filename}/images/image.jpg` is also accepted.
+
+### Exif, Captions, and Blacklists
+Folders of photos may optionally have three text files, where each line describes one photo. You can use the `#` to comment out a line. Generating these optional files is left as an exercise for the reader (but consider using Phil Harvey's [exiftool](http://www.sno.phy.queensu.ca/~phil/exiftool/)). See below for one method of extracting exif data.
+
+`exif.txt`
+:	Associates compact technical information with photos, typically the camera settings. For example:
+
+	best.jpg: Canon EOS 5D Mark II - 20mm f/8 1/250s ISO 100
+	night.jpg: Canon EOS 5D Mark II - 47mm f/8 5s ISO 100
+	# new.jpg: Canon EOS 5D Mark II - 47mm f/8 5s ISO 100
+
+`captions.txt`
+:	Associates comments with photos. For example:
+
+	best.jpg: My best photo ever! How lucky of me!
+	night.jpg: Twilight over the dam.
+	# new.jpg: My new photo blog entry is not quite ready.
+
+`blacklist.txt`
+: Skips photos the user does not want to include. For example:
+
+	this-file-will-be-skipped.jpg
+	this-one-will-be-skipped-too.jpg
+	# but-this-file-will-NOT-be-skipped.jpg
+	this-one-will-be-also-skipped.jpg
+
+
+Here is an example Markdown article that shows the four use cases:
+
+	title: My Article
+	gallery: {photo}favorite
+	image: {photo}favorite/best.jpg
+
+	Here are my best photos, taken with my favorite camera:
+	![]({photo}mybag/camera.jpg).
+	![]({lightbox}mybag/flash.jpg).
+
+The default behavior of the Photos plugin removes the exif information from the file. If you would like to keep the exif information, you can install the `piexif` library for python and add the following settings to keep some or all of the exif information. This feature is not a replacement for the `exif.txt` feature but in addition to that feature. This feature currently only works with jpeg input files.
+
+## How to change the Jinja templates
+
+The plugin provides the following variables to your templates:
+
+`article.photo_image`
+:	For articles with an associated photo, a tuple with the following information:
+
+* The filename of the original photo.
+* The output path to the generated photo.
+* The output path to the generated thumbnail.
+
+For example, modify the template `article.html` as shown below to display the associated image before the article content:
+
+```html
+<div class="entry-content">
+	{% if article.photo_image %}<img src="{{ SITEURL }}/{{ article.photo_image[1] }}" />{% endif %}
+	{% include 'article_infos.html' %}
+	{{ article.content }}
+</div><!-- /.entry-content -->
+```
+
+`article.photo_gallery`
+:	For articles with a gallery, a list of the photos in the gallery. Each item in the list is a tuple with five elements:
+
+* The title of the gallery
+* The filename of the original photo.
+* The output path to the generated photo.
+* The output path to the generated thumbnail.
+* The EXIF information of the photo, as read from the file `exif.txt`.
+* The caption of the photo, as read from `captions.txt`.
+
+For example, add the following to the template `article.html` to add the gallery as the end of the article:
+
+```html
+{% if article.photo_gallery %}
+<div class="gallery">
+		{% for title, gallery in article.photo_gallery %}
+			<h1>{{ title }}</h1>
+				{% for name, photo, thumb, exif, caption in gallery %}
+						<a href="{{ SITEURL }}/{{ photo }}" title="{{ name }}" exif="{{ exif }}" caption="{{ caption }}"><img src="{{ SITEURL }}/{{ thumb }}"></a>
+				{% endfor %}
+		{% endfor %}
+</div>
+{% endif %}
+```
+
+For example, add the following to the template `index.html`, inside the `entry-content`, to display the thumbnail with a link to the article:
+
+```html
+{% if article.photo_image %}<a href="{{ SITEURL }}/{{ article.url }}"><img src="{{ SITEURL }}/{{ article.photo_image[2] }}"
+	style="display: inline; float: right; margin: 2px 0 2ex 4ex;" /></a>
+{% endif %}
+```
+
+## How to make the gallery lightbox
+
+There are several JavaScript libraries that display a list of images as a lightbox. The example below uses [Magnific Popup](http://dimsemenov.com/plugins/magnific-popup/), which allows the more complex initialization needed to display both the filename, the compact technical information, and the caption. The solution would be simpler if photos did not show any extra information.
+
+Copy the files `magnific-popup.css` and `magnific-popup.js` to the root of your Pelican template.
+
+Add the following to the template `base.html`, inside the HTML `head` tags:
+
+```html
+{% if (article and article.photo_gallery) or (articles_page and articles_page.object_list[0].photo_gallery) %}
+	<link rel="stylesheet" href="{{ SITEURL }}/{{ THEME_STATIC_DIR }}/magnific-popup.css">
+{% endif %}
+```
+
+Add the following to the template `base.html`, before the closing HTML `</body>` tag:
+
+```JavaScript
+{% if (article and article.photo_gallery) or (articles_page and articles_page.object_list[0].photo_gallery) %}
+<!-- jQuery 1.7.2+ or Zepto.js 1.0+ -->
+<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
+
+<!-- Magnific Popup core JS file -->
+<script src="{{ SITEURL }}/{{ THEME_STATIC_DIR }}/magnific-popup.js"></script>
+<script>
+$('.gallery').magnificPopup({
+	delegate: 'a',
+	type: 'image',
+	gallery: {
+		enabled: true,
+		navigateByImgClick: true,
+		preload: [1,2]
+	},
+	image: {
+		titleSrc: function(item) {
+			if (item.el.attr('caption') && item.el.attr('exif')) {
+				return (item.el.attr('caption').replace(/\\n/g, '<br />') +
+					'<small>' + item.el.attr('title') + ' - ' + item.el.attr('exif') + '</small>');
+			}
+		return item.el.attr('title') + '<small>' + item.el.attr('exif') + '</small>';
+	} }
+});
+</script>
+{% endif %}
+```
+
+## How to make a Bootstrap Carousel
+
+If you are using bootstrap, the following code is an example of how one could create a carousel.
+
+```html
+{% if article.photo_gallery %}
+  {% for title, gallery in article.photo_gallery %}
+    <h1>{{ title }}</h1>
+    <div id="carousel-{{ loop.index }}" class="carousel slide">
+      <ol class="carousel-indicators">
+          {% for i in range(0, gallery|length) %}
+          <li data-target="#carousel-{{ loop.index }}" data-slide-to="{{ i }}" {% if i==0 %} class="active" {% endif %}></li>
+          {% endfor %}
+      </ol>
+      <div class="carousel-inner">
+        {% for name, photo, thumb, exif, caption in gallery %}
+          {% if loop.first %}
+            <div class="item active">
+          {% else %}
+            <div class="item">
+          {% endif %}
+          <img src="{{ SITEURL }}/{{ photo }}" exif="{{ exif }}" alt="{{ caption }}">
+          <div class="carousel-caption">
+              <h5>{{ caption }}</h5>
+          </div> <!-- carousel-caption -->
+        </div> <!-- item -->
+        {% endfor %}
+      </div> <!-- carousel-inner -->
+      <a class="left carousel-control" href="#carousel-{{ loop.index }}" data-slide="prev">
+        <span class="glyphicon glyphicon-chevron-left"></span>
+      </a>
+      <a class="right carousel-control" href="#carousel-{{ loop.index }}" data-slide="next">
+        <span class="glyphicon glyphicon-chevron-right"></span>
+      </a>
+    </div> <!-- closes carousel-{{ loop.index }} -->
+    {% endfor %}
+{% endif %}
+```
+
+## Exiftool example
+
+You can add the following stanza to your fab file if you are using `fabric` to generate the appropriate text files for your galleries. You need to set the location of `Exiftool` control files.
+
+```Python
+def photo_gallery_gen(location):
+    """Create gallery metadata files."""
+    local_path = os.getcwd() + 'LOCATION OF YOUR EXIF CONTROL FILES'
+    with lcd(location):
+        local("exiftool -p {fmt_path}/exif.fmt . > exif.txt".format(
+            fmt_path=local_path))
+        local("exiftool -p {fmt_path}/captions.fmt . > captions.txt".format(
+            fmt_path=local_path))
+
+```
+
+`captions.fmt` example file
+
+```
+$FileName: $Description
+```
+
+`exif.fmt` example file
+
+```
+$FileName: $CreateDate - $Make $Model Stats:(f/$Aperture, ${ShutterSpeed}s, ISO $ISO Flash: $Flash) GPS:($GPSPosition $GPSAltitude)
+```
+
+## Known use cases
+
+[pxquim.pt](http://pxquim.pt/) uses Photos and the plug-in Sub-parts to publish 600 photo galleries with 40,000 photos. Photos keeps the high-resolution photos separate from the site articles.
+
+[pxquim.com](http://pxquim.com/) uses sub-parts to cover conferences, where it makes sense to have a sub-part for each speaker.
+
+## Alternatives
+
+Gallery
+:	Galleries are distinct entities, without the organizational capabilities of articles. Photos must be resized separately, and must be kept with the source of the blog. Gallery was the initial inspiration for Photos.
+
+Image_process
+:	Resize and process images in the article body in a more flexible way (based on the CSS class of the image), but without the ability to create galleries. The source photos must be kept with the source of the blog.
diff --git a/pelican-plugins/photos/SourceCodePro-Bold.otf b/pelican-plugins/photos/SourceCodePro-Bold.otf
new file mode 100644
index 0000000000000000000000000000000000000000..f4e576cecf78b07861673cb351f9fdeb1cf9865e
Binary files /dev/null and b/pelican-plugins/photos/SourceCodePro-Bold.otf differ
diff --git a/pelican-plugins/photos/SourceCodePro-Regular.otf b/pelican-plugins/photos/SourceCodePro-Regular.otf
new file mode 100644
index 0000000000000000000000000000000000000000..4e3b9d0bcd92851958b3919e84565d9cf0cc91d1
Binary files /dev/null and b/pelican-plugins/photos/SourceCodePro-Regular.otf differ
diff --git a/pelican-plugins/photos/__init__.py b/pelican-plugins/photos/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..9832420e9b1efa37189cef6984e6d4e746c07bab
--- /dev/null
+++ b/pelican-plugins/photos/__init__.py
@@ -0,0 +1 @@
+from .photos import *
diff --git a/pelican-plugins/photos/licenses.json b/pelican-plugins/photos/licenses.json
new file mode 100644
index 0000000000000000000000000000000000000000..c727e212a61d6e20faa0517f8d6d798f6ea493bf
--- /dev/null
+++ b/pelican-plugins/photos/licenses.json
@@ -0,0 +1,30 @@
+{
+	"CC-BY-NC-ND": {
+		"URL": "https://creativecommons.org/licenses/by-nc-nd/4.0/legalcode",
+		"Text": "Copyleft license, {Author} {Year}. Content licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License, except where indicated otherwise. See {URL} for more information."
+	},
+	"CC-BY-NC-SA": {
+		"URL": "https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode",
+		"Text": "Copyleft license, {Author} {Year}. Content licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License, except where indicated otherwise. See {URL} for more information."
+	},
+	"CC-BY": {
+		"URL": "https://creativecommons.org/licenses/by/4.0/legalcode",
+		"Text": "Copyleft license, {Author} {Year}. Content licensed under a Creative Commons Attribution 4.0 International License, except where indicated otherwise. See {URL} for more information."
+	},
+	"CC-BY-SA": {
+		"URL": "https://creativecommons.org/licenses/by-sa/4.0/legalcode",
+		"Text": "Copyleft license, {Author} {Year}. Content licensed under a Creative Commons Attribution-ShareAlike 4.0 International License, except where indicated otherwise. See {URL} for more information."
+	},
+	"CC0": {
+		"URL": "https://creativecommons.org/publicdomain/zero/1.0/",
+		"Text": "CC0 Copyleft license, {Author} {Year}. To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring rights to this software to the public domain worldwide. This software is distributed without any warranty. See {URL} for more information."
+	},
+	"CC-BY-NC": {
+		"URL": "https://creativecommons.org/licenses/by-nc/4.0/legalcode",
+		"Text": "Copyleft license, {Author} {Year}. Content licensed under a Creative Commons Attribution-NonCommercial 4.0 International License, except where indicated otherwise. See {URL} for more information."
+	},
+	"CC-BY-ND": {
+		"URL": "https://creativecommons.org/licenses/by-nd/4.0/legalcode",
+		"Text": "Copyleft license, {Author} {Year}. Content licensed under a Creative Commons Attribution-NoDerivatives 4.0 International License, except where indicated otherwise. See {URL} for more information."
+	}
+}
diff --git a/pelican-plugins/photos/photos.py b/pelican-plugins/photos/photos.py
new file mode 100644
index 0000000000000000000000000000000000000000..d0c1af17a3bd1f38f605199eaa36c2477f5d4f35
--- /dev/null
+++ b/pelican-plugins/photos/photos.py
@@ -0,0 +1,589 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+import datetime
+import itertools
+import json
+import logging
+import multiprocessing
+import os
+import pprint
+import re
+import sys
+
+from pelican.generators import ArticlesGenerator
+from pelican.generators import PagesGenerator
+from pelican.settings import DEFAULT_CONFIG
+from pelican import signals
+from pelican.utils import pelican_open
+
+logger = logging.getLogger(__name__)
+
+try:
+    from PIL import Image
+    from PIL import ImageDraw
+    from PIL import ImageEnhance
+    from PIL import ImageFont
+    from PIL import ImageOps
+except ImportError:
+    logger.error('PIL/Pillow not found')
+
+try:
+    import piexif
+except ImportError:
+    ispiexif = False
+    logger.warning('piexif not found! Cannot use exif manipulation features')
+else:
+    ispiexif = True
+    logger.debug('piexif found.')
+
+
+def initialized(pelican):
+    p = os.path.expanduser('~/Pictures')
+
+    DEFAULT_CONFIG.setdefault('PHOTO_LIBRARY', p)
+    DEFAULT_CONFIG.setdefault('PHOTO_GALLERY', (1024, 768, 80))
+    DEFAULT_CONFIG.setdefault('PHOTO_ARTICLE', (760, 506, 80))
+    DEFAULT_CONFIG.setdefault('PHOTO_THUMB', (192, 144, 60))
+    DEFAULT_CONFIG.setdefault('PHOTO_SQUARE_THUMB', False)
+    DEFAULT_CONFIG.setdefault('PHOTO_GALLERY_TITLE', '')
+    DEFAULT_CONFIG.setdefault('PHOTO_ALPHA_BACKGROUND_COLOR', (255, 255, 255))
+    DEFAULT_CONFIG.setdefault('PHOTO_WATERMARK', False)
+    DEFAULT_CONFIG.setdefault('PHOTO_WATERMARK_THUMB', False)
+    DEFAULT_CONFIG.setdefault('PHOTO_WATERMARK_TEXT', DEFAULT_CONFIG['SITENAME'])
+    DEFAULT_CONFIG.setdefault('PHOTO_WATERMARK_TEXT_COLOR', (255, 255, 255))
+    DEFAULT_CONFIG.setdefault('PHOTO_WATERMARK_IMG', '')
+    DEFAULT_CONFIG.setdefault('PHOTO_WATERMARK_IMG_SIZE', False)
+    DEFAULT_CONFIG.setdefault('PHOTO_RESIZE_JOBS', 1)
+    DEFAULT_CONFIG.setdefault('PHOTO_EXIF_KEEP', False)
+    DEFAULT_CONFIG.setdefault('PHOTO_EXIF_REMOVE_GPS', False)
+    DEFAULT_CONFIG.setdefault('PHOTO_EXIF_AUTOROTATE', True)
+    DEFAULT_CONFIG.setdefault('PHOTO_EXIF_COPYRIGHT', False)
+    DEFAULT_CONFIG.setdefault('PHOTO_EXIF_COPYRIGHT_AUTHOR', DEFAULT_CONFIG['SITENAME'])
+    DEFAULT_CONFIG.setdefault('PHOTO_LIGHTBOX_GALLERY_ATTR', 'data-lightbox')
+    DEFAULT_CONFIG.setdefault('PHOTO_LIGHTBOX_CAPTION_ATTR', 'data-title')
+
+    DEFAULT_CONFIG['queue_resize'] = {}
+    DEFAULT_CONFIG['created_galleries'] = {}
+    DEFAULT_CONFIG['plugin_dir'] = os.path.dirname(os.path.realpath(__file__))
+
+    if pelican:
+        pelican.settings.setdefault('PHOTO_LIBRARY', p)
+        pelican.settings.setdefault('PHOTO_GALLERY', (1024, 768, 80))
+        pelican.settings.setdefault('PHOTO_ARTICLE', (760, 506, 80))
+        pelican.settings.setdefault('PHOTO_THUMB', (192, 144, 60))
+        pelican.settings.setdefault('PHOTO_SQUARE_THUMB', False)
+        pelican.settings.setdefault('PHOTO_GALLERY_TITLE', '')
+        pelican.settings.setdefault('PHOTO_ALPHA_BACKGROUND_COLOR', (255, 255, 255))
+        pelican.settings.setdefault('PHOTO_WATERMARK', False)
+        pelican.settings.setdefault('PHOTO_WATERMARK_THUMB', False)
+        pelican.settings.setdefault('PHOTO_WATERMARK_TEXT', pelican.settings['SITENAME'])
+        pelican.settings.setdefault('PHOTO_WATERMARK_TEXT_COLOR', (255, 255, 255))
+        pelican.settings.setdefault('PHOTO_WATERMARK_IMG', '')
+        pelican.settings.setdefault('PHOTO_WATERMARK_IMG_SIZE', False)
+        pelican.settings.setdefault('PHOTO_RESIZE_JOBS', 1)
+        pelican.settings.setdefault('PHOTO_EXIF_KEEP', False)
+        pelican.settings.setdefault('PHOTO_EXIF_REMOVE_GPS', False)
+        pelican.settings.setdefault('PHOTO_EXIF_AUTOROTATE', True)
+        pelican.settings.setdefault('PHOTO_EXIF_COPYRIGHT', False)
+        pelican.settings.setdefault('PHOTO_EXIF_COPYRIGHT_AUTHOR', pelican.settings['AUTHOR'])
+        pelican.settings.setdefault('PHOTO_LIGHTBOX_GALLERY_ATTR', 'data-lightbox')
+        pelican.settings.setdefault('PHOTO_LIGHTBOX_CAPTION_ATTR', 'data-title')
+
+
+def read_notes(filename, msg=None):
+    notes = {}
+    try:
+        with pelican_open(filename) as text:
+            for line in text.splitlines():
+                if line.startswith('#'):
+                    continue
+                m = line.split(':', 1)
+                if len(m) > 1:
+                    pic = m[0].strip()
+                    note = m[1].strip()
+                    if pic and note:
+                        notes[pic] = note
+                else:
+                    notes[line] = ''
+    except Exception as e:
+        if msg:
+            logger.info('{} at file {}'.format(msg, filename))
+        logger.debug('read_notes issue: {} at file {}. Debug message:{}'.format(msg, filename, e))
+    return notes
+
+
+def enqueue_resize(orig, resized, spec=(640, 480, 80)):
+    if resized not in DEFAULT_CONFIG['queue_resize']:
+        DEFAULT_CONFIG['queue_resize'][resized] = (orig, spec)
+    elif DEFAULT_CONFIG['queue_resize'][resized] != (orig, spec):
+        logger.error('photos: resize conflict for {}, {}-{} is not {}-{}'.format(resized, DEFAULT_CONFIG['queue_resize'][resized][0], DEFAULT_CONFIG['queue_resize'][resized][1], orig, spec))
+
+
+def isalpha(img):
+    return True if img.mode in ('RGBA', 'LA') or (img.mode == 'P' and 'transparency' in img.info) else False
+
+
+def remove_alpha(img, bg_color):
+    background = Image.new("RGB", img.size, bg_color)
+    background.paste(img, mask=img.split()[3])  # 3 is the alpha channel
+    return background
+
+
+def ReduceOpacity(im, opacity):
+    """Reduces Opacity.
+
+    Returns an image with reduced opacity.
+    Taken from http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/362879
+    """
+    assert opacity >= 0 and opacity <= 1
+    if isalpha(im):
+        im = im.copy()
+    else:
+        im = im.convert('RGBA')
+
+    alpha = im.split()[3]
+    alpha = ImageEnhance.Brightness(alpha).enhance(opacity)
+    im.putalpha(alpha)
+    return im
+
+
+def watermark_photo(image, settings):
+    margin = [10, 10]
+    opacity = 0.6
+
+    watermark_layer = Image.new("RGBA", image.size, (0, 0, 0, 0))
+    draw_watermark = ImageDraw.Draw(watermark_layer)
+    text_reducer = 32
+    image_reducer = 8
+    text_size = [0, 0]
+    mark_size = [0, 0]
+    text_position = [0, 0]
+
+    if settings['PHOTO_WATERMARK_TEXT']:
+        font_name = 'SourceCodePro-Bold.otf'
+        default_font = os.path.join(DEFAULT_CONFIG['plugin_dir'], font_name)
+        font = ImageFont.FreeTypeFont(default_font, watermark_layer.size[0] // text_reducer)
+        text_size = draw_watermark.textsize(settings['PHOTO_WATERMARK_TEXT'], font)
+        text_position = [image.size[i] - text_size[i] - margin[i] for i in [0, 1]]
+        draw_watermark.text(text_position, settings['PHOTO_WATERMARK_TEXT'], settings['PHOTO_WATERMARK_TEXT_COLOR'], font=font)
+
+    if settings['PHOTO_WATERMARK_IMG']:
+        mark_image = Image.open(settings['PHOTO_WATERMARK_IMG'])
+        mark_image_size = [watermark_layer.size[0] // image_reducer for size in mark_size]
+        mark_image_size = settings['PHOTO_WATERMARK_IMG_SIZE'] if settings['PHOTO_WATERMARK_IMG_SIZE'] else mark_image_size
+        mark_image.thumbnail(mark_image_size, Image.ANTIALIAS)
+        mark_position = [watermark_layer.size[i] - mark_image.size[i] - margin[i] for i in [0, 1]]
+        mark_position = tuple([mark_position[0] - (text_size[0] // 2) + (mark_image_size[0] // 2), mark_position[1] - text_size[1]])
+
+        if not isalpha(mark_image):
+            mark_image = mark_image.convert('RGBA')
+
+        watermark_layer.paste(mark_image, mark_position, mark_image)
+
+    watermark_layer = ReduceOpacity(watermark_layer, opacity)
+    image.paste(watermark_layer, (0, 0), watermark_layer)
+
+    return image
+
+
+def rotate_image(img, exif_dict):
+    if "exif" in img.info and piexif.ImageIFD.Orientation in exif_dict["0th"]:
+        orientation = exif_dict["0th"].pop(piexif.ImageIFD.Orientation)
+        if orientation == 2:
+            img = img.transpose(Image.FLIP_LEFT_RIGHT)
+        elif orientation == 3:
+            img = img.rotate(180)
+        elif orientation == 4:
+            img = img.rotate(180).transpose(Image.FLIP_LEFT_RIGHT)
+        elif orientation == 5:
+            img = img.rotate(-90).transpose(Image.FLIP_LEFT_RIGHT)
+        elif orientation == 6:
+            img = img.rotate(-90, expand=True)
+        elif orientation == 7:
+            img = img.rotate(90).transpose(Image.FLIP_LEFT_RIGHT)
+        elif orientation == 8:
+            img = img.rotate(90)
+
+    return (img, exif_dict)
+
+
+def build_license(license, author):
+    year = datetime.datetime.now().year
+    license_file = os.path.join(DEFAULT_CONFIG['plugin_dir'], 'licenses.json')
+
+    with open(license_file) as data_file:
+        licenses = json.load(data_file)
+
+    if any(license in k for k in licenses):
+        return licenses[license]['Text'].format(Author=author, Year=year, URL=licenses[license]['URL'])
+    else:
+        return 'Copyright {Year} {Author}, All Rights Reserved'.format(Author=author, Year=year)
+
+
+def manipulate_exif(img, settings):
+    try:
+        exif = piexif.load(img.info['exif'])
+    except Exception:
+        logger.debug('EXIF information not found')
+        exif = {}
+
+    if settings['PHOTO_EXIF_AUTOROTATE']:
+        img, exif = rotate_image(img, exif)
+
+    if settings['PHOTO_EXIF_REMOVE_GPS']:
+        exif.pop('GPS')
+
+    if settings['PHOTO_EXIF_COPYRIGHT']:
+
+        # We want to be minimally destructive to any preset exif author or copyright information.
+        # If there is copyright or author information prefer that over everything else.
+        if not exif['0th'].get(piexif.ImageIFD.Artist):
+            exif['0th'][piexif.ImageIFD.Artist] = settings['PHOTO_EXIF_COPYRIGHT_AUTHOR']
+            author = settings['PHOTO_EXIF_COPYRIGHT_AUTHOR']
+
+        if not exif['0th'].get(piexif.ImageIFD.Copyright):
+            license = build_license(settings['PHOTO_EXIF_COPYRIGHT'], author)
+            exif['0th'][piexif.ImageIFD.Copyright] = license
+
+    return (img, piexif.dump(exif))
+
+
+def resize_worker(orig, resized, spec, settings):
+    logger.info('photos: make photo {} -> {}'.format(orig, resized))
+    im = Image.open(orig)
+
+    if ispiexif and settings['PHOTO_EXIF_KEEP'] and im.format == 'JPEG':  # Only works with JPEG exif for sure.
+        try:
+            im, exif_copy = manipulate_exif(im, settings)
+        except:
+            logger.info('photos: no EXIF or EXIF error in {}'.format(orig))
+            exif_copy = b''
+    else:
+        exif_copy = b''
+
+    icc_profile = im.info.get("icc_profile", None)
+
+    if settings['PHOTO_SQUARE_THUMB'] and spec == settings['PHOTO_THUMB']:
+        im = ImageOps.fit(im, (spec[0], spec[1]), Image.ANTIALIAS)
+
+    im.thumbnail((spec[0], spec[1]), Image.ANTIALIAS)
+    directory = os.path.split(resized)[0]
+
+    if isalpha(im):
+        im = remove_alpha(im, settings['PHOTO_ALPHA_BACKGROUND_COLOR'])
+
+    if not os.path.exists(directory):
+        try:
+            os.makedirs(directory)
+        except Exception:
+            logger.exception('Could not create {}'.format(directory))
+    else:
+        logger.debug('Directory already exists at {}'.format(os.path.split(resized)[0]))
+
+    if settings['PHOTO_WATERMARK']:
+        isthumb = True if spec == settings['PHOTO_THUMB'] else False
+        if not isthumb or (isthumb and settings['PHOTO_WATERMARK_THUMB']):
+            im = watermark_photo(im, settings)
+
+    im.save(resized, 'JPEG', quality=spec[2], icc_profile=icc_profile, exif=exif_copy)
+
+
+def resize_photos(generator, writer):
+    if generator.settings['PHOTO_RESIZE_JOBS'] == -1:
+        debug = True
+        generator.settings['PHOTO_RESIZE_JOBS'] = 1
+    else:
+        debug = False
+
+    pool = multiprocessing.Pool(generator.settings['PHOTO_RESIZE_JOBS'])
+    logger.debug('Debug Status: {}'.format(debug))
+    for resized, what in DEFAULT_CONFIG['queue_resize'].items():
+        resized = os.path.join(generator.output_path, resized)
+        orig, spec = what
+        if (not os.path.isfile(resized) or os.path.getmtime(orig) > os.path.getmtime(resized)):
+            if debug:
+                resize_worker(orig, resized, spec, generator.settings)
+            else:
+                pool.apply_async(resize_worker, (orig, resized, spec, generator.settings))
+
+    pool.close()
+    pool.join()
+
+
+def detect_content(content):
+    hrefs = None
+
+    def replacer(m):
+        what = m.group('what')
+        value = m.group('value')
+        tag = m.group('tag')
+        output = m.group(0)
+
+        if what in ('photo', 'lightbox'):
+            if value.startswith('/'):
+                value = value[1:]
+
+            path = os.path.join(
+                os.path.expanduser(settings['PHOTO_LIBRARY']),
+                value
+            )
+
+            if os.path.isfile(path):
+                photo_prefix = os.path.splitext(value)[0].lower()
+
+                if what == 'photo':
+                    photo_article = photo_prefix + 'a.jpg'
+                    enqueue_resize(
+                        path,
+                        os.path.join('photos', photo_article),
+                        settings['PHOTO_ARTICLE']
+                    )
+
+                    output = ''.join((
+                        '<',
+                        m.group('tag'),
+                        m.group('attrs_before'),
+                        m.group('src'),
+                        '=',
+                        m.group('quote'),
+                        os.path.join(settings['SITEURL'], 'photos', photo_article),
+                        m.group('quote'),
+                        m.group('attrs_after'),
+                    ))
+
+                elif what == 'lightbox' and tag == 'img':
+                    photo_gallery = photo_prefix + '.jpg'
+                    enqueue_resize(
+                        path,
+                        os.path.join('photos', photo_gallery),
+                        settings['PHOTO_GALLERY']
+                    )
+
+                    photo_thumb = photo_prefix + 't.jpg'
+                    enqueue_resize(
+                        path,
+                        os.path.join('photos', photo_thumb),
+                        settings['PHOTO_THUMB']
+                    )
+
+                    lightbox_attr_list = ['']
+
+                    gallery_name = value.split('/')[0]
+                    lightbox_attr_list.append('{}="{}"'.format(
+                        settings['PHOTO_LIGHTBOX_GALLERY_ATTR'],
+                        gallery_name
+                    ))
+
+                    captions = read_notes(
+                        os.path.join(os.path.dirname(path), 'captions.txt'),
+                        msg = 'photos: No captions for gallery'
+                    )
+                    caption = captions.get(os.path.basename(path)) if captions else None
+                    if caption:
+                        lightbox_attr_list.append('{}="{}"'.format(
+                            settings['PHOTO_LIGHTBOX_CAPTION_ATTR'],
+                            caption
+                        ))
+
+                    lightbox_attrs = ' '.join(lightbox_attr_list)
+
+                    output = ''.join((
+                        '<a href=',
+                        m.group('quote'),
+                        os.path.join(settings['SITEURL'], 'photos', photo_gallery),
+                        m.group('quote'),
+                        lightbox_attrs,
+                        '><img',
+                        m.group('attrs_before'),
+                        'src=',
+                        m.group('quote'),
+                        os.path.join(settings['SITEURL'], 'photos', photo_thumb),
+                        m.group('quote'),
+                        m.group('attrs_after'),
+                        '</a>'
+                    ))
+
+            else:
+                logger.error('photos: No photo %s', path)
+
+        return output
+
+    if hrefs is None:
+        regex = r"""
+            <\s*
+            (?P<tag>[^\s\>]+)  # detect the tag
+            (?P<attrs_before>[^\>]*)
+            (?P<src>href|src)  # match tag with src and href attr
+            \s*=
+            (?P<quote>["\'])  # require value to be quoted
+            (?P<path>{0}(?P<value>.*?))  # the url value
+            (?P=quote)
+            (?P<attrs_after>[^\>]*>)
+        """.format(
+            content.settings['INTRASITE_LINK_REGEX']
+        )
+        hrefs = re.compile(regex, re.X)
+
+    if content._content and ('{photo}' in content._content or '{lightbox}' in content._content):
+        settings = content.settings
+        content._content = hrefs.sub(replacer, content._content)
+
+
+def galleries_string_decompose(gallery_string):
+    splitter_regex = re.compile(r'[\s,]*?({photo}|{filename})')
+    title_regex = re.compile(r'{(.+)}')
+    galleries = map(unicode.strip if sys.version_info.major == 2 else str.strip, filter(None, splitter_regex.split(gallery_string)))
+    galleries = [gallery[1:] if gallery.startswith('/') else gallery for gallery in galleries]
+    if len(galleries) % 2 == 0 and ' ' not in galleries:
+        galleries = zip(zip(['type'] * len(galleries[0::2]), galleries[0::2]), zip(['location'] * len(galleries[0::2]), galleries[1::2]))
+        galleries = [dict(gallery) for gallery in galleries]
+        for gallery in galleries:
+            title = re.search(title_regex, gallery['location'])
+            if title:
+                gallery['title'] = title.group(1)
+                gallery['location'] = re.sub(title_regex, '', gallery['location']).strip()
+            else:
+                gallery['title'] = DEFAULT_CONFIG['PHOTO_GALLERY_TITLE']
+        return galleries
+    else:
+        logger.error('Unexpected gallery location format! \n{}'.format(pprint.pformat(galleries)))
+
+
+def process_gallery(generator, content, location):
+    content.photo_gallery = []
+
+    galleries = galleries_string_decompose(location)
+
+    for gallery in galleries:
+
+        if gallery['location'] in DEFAULT_CONFIG['created_galleries']:
+            content.photo_gallery.append((gallery['location'], DEFAULT_CONFIG['created_galleries'][gallery]))
+            continue
+
+        if gallery['type'] == '{photo}':
+            dir_gallery = os.path.join(os.path.expanduser(generator.settings['PHOTO_LIBRARY']), gallery['location'])
+            rel_gallery = gallery['location']
+        elif gallery['type'] == '{filename}':
+            base_path = os.path.join(generator.path, content.relative_dir)
+            dir_gallery = os.path.join(base_path, gallery['location'])
+            rel_gallery = os.path.join(content.relative_dir, gallery['location'])
+
+        if os.path.isdir(dir_gallery):
+            logger.info('photos: Gallery detected: {}'.format(rel_gallery))
+            dir_photo = os.path.join('photos', rel_gallery.lower())
+            dir_thumb = os.path.join('photos', rel_gallery.lower())
+            exifs = read_notes(os.path.join(dir_gallery, 'exif.txt'),
+                               msg='photos: No EXIF for gallery')
+            captions = read_notes(os.path.join(dir_gallery, 'captions.txt'), msg='photos: No captions for gallery')
+            blacklist = read_notes(os.path.join(dir_gallery, 'blacklist.txt'), msg='photos: No blacklist for gallery')
+            content_gallery = []
+
+            title = gallery['title']
+            for pic in sorted(os.listdir(dir_gallery)):
+                if pic.startswith('.'):
+                    continue
+                if pic.endswith('.txt'):
+                    continue
+                if pic in blacklist:
+                    continue
+                photo = os.path.splitext(pic)[0].lower() + '.jpg'
+                thumb = os.path.splitext(pic)[0].lower() + 't.jpg'
+                content_gallery.append((
+                    pic,
+                    os.path.join(dir_photo, photo),
+                    os.path.join(dir_thumb, thumb),
+                    exifs.get(pic, ''),
+                    captions.get(pic, '')))
+
+                enqueue_resize(
+                    os.path.join(dir_gallery, pic),
+                    os.path.join(dir_photo, photo),
+                    generator.settings['PHOTO_GALLERY'])
+                enqueue_resize(
+                    os.path.join(dir_gallery, pic),
+                    os.path.join(dir_thumb, thumb),
+                    generator.settings['PHOTO_THUMB'])
+
+            content.photo_gallery.append((title, content_gallery))
+            logger.debug('Gallery Data: '.format(pprint.pformat(content.photo_gallery)))
+            DEFAULT_CONFIG['created_galleries']['gallery'] = content_gallery
+        else:
+            logger.error('photos: Gallery does not exist: {} at {}'.format(gallery['location'], dir_gallery))
+
+
+def detect_gallery(generator, content):
+    if 'gallery' in content.metadata:
+        gallery = content.metadata.get('gallery')
+        if gallery.startswith('{photo}') or gallery.startswith('{filename}'):
+            process_gallery(generator, content, gallery)
+        elif gallery:
+            logger.error('photos: Gallery tag not recognized: {}'.format(gallery))
+
+
+def image_clipper(x):
+    return x[8:] if x[8] == '/' else x[7:]
+
+
+def file_clipper(x):
+    return x[11:] if x[10] == '/' else x[10:]
+
+
+def process_image(generator, content, image):
+    if image.startswith('{photo}'):
+        path = os.path.join(os.path.expanduser(generator.settings['PHOTO_LIBRARY']), image_clipper(image))
+        image = image_clipper(image)
+    elif image.startswith('{filename}'):
+        path = os.path.join(generator.path, content.relative_dir, file_clipper(image))
+        image = file_clipper(image)
+
+    if os.path.isfile(path):
+        photo = os.path.splitext(image)[0].lower() + 'a.jpg'
+        thumb = os.path.splitext(image)[0].lower() + 't.jpg'
+        content.photo_image = (
+            os.path.basename(image).lower(),
+            os.path.join('photos', photo),
+            os.path.join('photos', thumb))
+        enqueue_resize(
+            path,
+            os.path.join('photos', photo),
+            generator.settings['PHOTO_ARTICLE'])
+        enqueue_resize(
+            path,
+            os.path.join('photos', thumb),
+            generator.settings['PHOTO_THUMB'])
+    else:
+        logger.error('photo: No photo for {} at {}'.format(content.source_path, path))
+
+
+def detect_image(generator, content):
+    image = content.metadata.get('image', None)
+    if image:
+        if image.startswith('{photo}') or image.startswith('{filename}'):
+            process_image(generator, content, image)
+        else:
+            logger.error('photos: Image tag not recognized: {}'.format(image))
+
+
+def detect_images_and_galleries(generators):
+    """Runs generator on both pages and articles."""
+    for generator in generators:
+        if isinstance(generator, ArticlesGenerator):
+            for article in itertools.chain(generator.articles, generator.translations, generator.drafts):
+                detect_image(generator, article)
+                detect_gallery(generator, article)
+        elif isinstance(generator, PagesGenerator):
+            for page in itertools.chain(generator.pages, generator.translations, generator.hidden_pages):
+                detect_image(generator, page)
+                detect_gallery(generator, page)
+
+
+def register():
+    """Uses the new style of registration based on GitHub Pelican issue #314."""
+    signals.initialized.connect(initialized)
+    try:
+        signals.content_object_init.connect(detect_content)
+        signals.all_generators_finalized.connect(detect_images_and_galleries)
+        signals.article_writer_finalized.connect(resize_photos)
+    except Exception as e:
+        logger.exception('Plugin failed to execute: {}'.format(pprint.pformat(e)))
diff --git a/pelican-plugins/photos/requirements.txt b/pelican-plugins/photos/requirements.txt
new file mode 100644
index 0000000000000000000000000000000000000000..294cfab69c8eb2b527f83811f85b49520174583e
--- /dev/null
+++ b/pelican-plugins/photos/requirements.txt
@@ -0,0 +1,2 @@
+Pillow
+piexif>=1.0.5
diff --git a/pelican-plugins/photos/test_data/agallery/best.jpg b/pelican-plugins/photos/test_data/agallery/best.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..2a8d2bfd540bb3bacbaeef27fc4348b574364a3d
Binary files /dev/null and b/pelican-plugins/photos/test_data/agallery/best.jpg differ
diff --git a/pelican-plugins/photos/test_data/agallery/captions.txt b/pelican-plugins/photos/test_data/agallery/captions.txt
new file mode 100644
index 0000000000000000000000000000000000000000..70012b44a7bfdb8118a1b75c51c82ead0e3a8c90
--- /dev/null
+++ b/pelican-plugins/photos/test_data/agallery/captions.txt
@@ -0,0 +1 @@
+best.jpg: Caption-best
diff --git a/pelican-plugins/photos/test_data/agallery/exif.txt b/pelican-plugins/photos/test_data/agallery/exif.txt
new file mode 100644
index 0000000000000000000000000000000000000000..fe6da980999160cb105474ef0ffab36d7280bbaf
--- /dev/null
+++ b/pelican-plugins/photos/test_data/agallery/exif.txt
@@ -0,0 +1,2 @@
+best.jpg: EXIF-best
+night.png: EXIF-night
diff --git a/pelican-plugins/photos/test_data/agallery/night.png b/pelican-plugins/photos/test_data/agallery/night.png
new file mode 100644
index 0000000000000000000000000000000000000000..c6e770bcb464a2d454de275fc403d1c0217c413b
Binary files /dev/null and b/pelican-plugins/photos/test_data/agallery/night.png differ
diff --git a/pelican-plugins/photos/test_data/filename.md b/pelican-plugins/photos/test_data/filename.md
new file mode 100644
index 0000000000000000000000000000000000000000..23e19d4d2f04868fe31d3f7746bf2969809d2b46
--- /dev/null
+++ b/pelican-plugins/photos/test_data/filename.md
@@ -0,0 +1,7 @@
+title: Test filename
+gallery: {filename}agallery
+image: {filename}agallery/best.jpg
+
+Here is my best photo, again.
+
+![]({filename}agallery/best.jpg).
diff --git a/pelican-plugins/photos/test_data/photo.md b/pelican-plugins/photos/test_data/photo.md
new file mode 100644
index 0000000000000000000000000000000000000000..10f132c90a9e58b33d97cbbc42ed47f63c57cdd9
--- /dev/null
+++ b/pelican-plugins/photos/test_data/photo.md
@@ -0,0 +1,7 @@
+title: Test photo
+gallery: {photo}agallery
+image: {photo}agallery/best.jpg
+
+Here is my best photo, again.
+
+![]({photo}agallery/best.jpg).
diff --git a/pelican-plugins/photos/test_photos.py b/pelican-plugins/photos/test_photos.py
new file mode 100644
index 0000000000000000000000000000000000000000..b5ca73d36cba81b7c01f7cd7c2417b9a72ec6622
--- /dev/null
+++ b/pelican-plugins/photos/test_photos.py
@@ -0,0 +1,129 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+import os
+from pelican.generators import ArticlesGenerator
+from pelican.tests.support import unittest, get_settings
+from tempfile import mkdtemp
+from shutil import rmtree
+import photos
+
+CUR_DIR = os.path.dirname(__file__)
+
+
+class TestPhotos(unittest.TestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        cls.temp_path = mkdtemp(prefix='pelicantests.')
+        cls.settings = get_settings(filenames={})
+        cls.settings['PATH'] = os.path.join(CUR_DIR, 'test_data')
+        cls.settings['PHOTO_LIBRARY'] = os.path.join(CUR_DIR, 'test_data')
+        cls.settings['DEFAULT_DATE'] = (1970, 1, 1)
+        cls.settings['FILENAME_METADATA'] = '(?P<slug>[^.]+)'
+        cls.settings['PLUGINS'] = [photos]
+        cls.settings['CACHE_CONTENT'] = False
+        cls.settings['OUTPUT_PATH'] = cls.temp_path
+        cls.settings['SITEURL'] = 'http://getpelican.com/sub'
+        cls.settings['AUTHOR'] = 'Bob Anonymous'
+        photos.initialized(cls)
+        context = cls.settings.copy()
+        context['generated_content'] = dict()
+        context['static_links'] = set()
+        cls.generator = ArticlesGenerator(
+            context=context, settings=cls.settings,
+            path=cls.settings['PATH'], theme=cls.settings['THEME'],
+            output_path=cls.settings['OUTPUT_PATH'])
+        photos.register()
+        cls.generator.generate_context()
+        for article in cls.generator.articles:
+            photos.detect_image(cls.generator, article)
+            photos.detect_gallery(cls.generator, article)
+
+    @classmethod
+    def tearDownClass(cls):
+        rmtree(cls.temp_path)
+
+    def test_image(self):
+        for a in self.generator.articles:
+            if 'image' in a.metadata:
+                self.assertTrue(
+                    hasattr(a, 'photo_image'),
+                    msg="{} not recognized.".format(a.metadata['image']))
+
+    def test_gallery(self):
+        for a in self.generator.articles:
+            if 'gallety' in a.metadata:
+                self.assertTrue(
+                    hasattr(a, 'photo_gallery'),
+                    msg="{} not recognized.".format(a.metadata['gallery']))
+
+    def get_article(self, slug):
+        for a in self.generator.articles:
+            if slug == a.slug:
+                return a
+        return None
+
+    def test_photo_article_image(self):
+        self.assertEqual(self.get_article('photo').photo_image,
+                         ('best.jpg',
+                          'photos/agallery/besta.jpg',
+                          'photos/agallery/bestt.jpg'))
+
+    def test_photo_article_gallery(self):
+        photo_gallery = self.get_article('filename').photo_gallery[0][1]
+        self.assertEqual(photo_gallery[0],
+                         ('best.jpg',
+                          'photos/agallery/best.jpg',
+                          'photos/agallery/bestt.jpg',
+                          'EXIF-best', 'Caption-best'))
+        self.assertEqual(photo_gallery[1],
+                         ('night.png',
+                          'photos/agallery/night.jpg',
+                          'photos/agallery/nightt.jpg',
+                          'EXIF-night', ''))
+
+    def test_photo_article_body(self):
+        expected = ('<p>Here is my best photo, again.</p>\n'
+                    '<p><img alt="" src="http://getpelican.com/sub/photos/agallery/besta.jpg">.</p>')
+        self.assertEqual(expected, self.get_article('photo').content)
+
+    def test_filename_article_image(self):
+        self.assertEqual(
+            ('best.jpg', 'photos/agallery/besta.jpg', 'photos/agallery/bestt.jpg'),
+            self.get_article('filename').photo_image)
+
+    def test_filename_article_gallery(self):
+        photo_gallery = self.get_article('filename').photo_gallery[0][1]
+        self.assertEqual(photo_gallery[0],
+                         ('best.jpg',
+                          'photos/agallery/best.jpg',
+                          'photos/agallery/bestt.jpg',
+                          'EXIF-best', 'Caption-best'))
+        self.assertEqual(photo_gallery[1],
+                         ('night.png',
+                          'photos/agallery/night.jpg',
+                          'photos/agallery/nightt.jpg',
+                          'EXIF-night', ''))
+
+    def test_filename_article_body(self):
+        expected = ('<p>Here is my best photo, again.</p>\n'
+                    '<p><img alt="" src="{filename}agallery/best.jpg">.</p>')
+        self.assertEqual(expected, self.get_article('filename').content)
+
+    def test_queue_resize(self):
+        expected = [
+            ('photos/agallery/best.jpg',
+                (self.settings['PATH'] + '/agallery/best.jpg', (1024, 768, 80))),
+            ('photos/agallery/besta.jpg',
+                (self.settings['PATH'] + '/agallery/best.jpg', (760, 506, 80))),
+            ('photos/agallery/bestt.jpg',
+                (self.settings['PATH'] + '/agallery/best.jpg', (192, 144, 60))),
+            ('photos/agallery/night.jpg',
+                (self.settings['PATH'] + '/agallery/night.png', (1024, 768, 80))),
+            ('photos/agallery/nightt.jpg',
+                (self.settings['PATH'] + '/agallery/night.png', (192, 144, 60)))]
+        self.assertEqual(sorted(expected), sorted(photos.DEFAULT_CONFIG['queue_resize'].items()))
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/pelican-plugins/plantuml/Readme.rst b/pelican-plugins/plantuml/Readme.rst
new file mode 100644
index 0000000000000000000000000000000000000000..a94e2bd742198cf38f78551a2b7754b7013fec52
--- /dev/null
+++ b/pelican-plugins/plantuml/Readme.rst
@@ -0,0 +1,216 @@
+================================================
+PlantUML plugin for Pelican rst and md documents
+================================================
+
+This plugin allows you to define UML diagrams directly into rst or md documents using the great PlantUML_ tool.
+
+It gets the content of ``uml`` directive, passes it to the external program PlantUML_ and then links the generated image to the document.
+
+.. contents::
+
+Installation
+============
+
+You need to install PlantUML_ (see the site for details) and Graphviz_ 2.26.3 or later. The plugin expects a program ``plantuml`` in the classpath. If not installed by your package manager, you can create a shell script and place it somewhere in the classpath. For example, save te following into ``/usr/local/bin/plantuml`` (supposing PlantUML_ installed into ``/opt/plantuml``):
+
+.. code-block:: bash
+
+    #!/bin/bash
+    java -jar /opt/plantuml/plantuml.jar ${@}
+
+For Gentoo_ there is an ebuild at http://gpo.zugaina.org/dev-util/plantuml/RDep: you can download the ebuild and the ``files`` subfolder or you can add the ``zugaina`` repository with _layman (reccomended).
+
+Usage
+=====
+
+Add ``plantuml`` to plugin list in ``pelicanconf.py``. For example:
+
+.. code-block:: ptyhon
+
+    PLUGINS = [ "sitemap", "plantuml" ]
+
+One loaded the plugin register also the Pyhton-Markdown_ extension. 
+
+RST usage
+---------
+Use the ``uml`` directive to start UML diagram description. It is not necessary to enclose diagram body between ``@startuml`` and ``@enduml`` directives: they are added automatically  before calling ``plantuml``.
+
+In addition to ``class`` and ``alt`` options common to all images, you can use the ``format`` option to select what kind of image must be produced. At the moment only ``png`` and ``svg`` are supported; the default is ``png``.
+
+Please note that the ``format`` option in not recognized by the ``plantuml`` extension of ``rst2pdf`` utility (call it with ``-e plantuml.py``) so if you use it you can get errors from that program.
+
+MD usage
+--------
+For use with the Pyhton-Markdown_ syntax, the UML block must be enclose with ``::uml::``:
+
+.. code-block:: markdown
+
+    ::uml:: [format=...] [classes=...] [alt=...]
+       PlantUML script
+    ::end-uml::
+
+Please keep a blank line before ``::uml::`` and after ``::end-uml::`` to be sure that the UML code will be correctly recognized. See Examples_ for more details.
+
+With MD syntax options must be specified in the same line as the opening ``:uml::``, with the order ``format``, ``classes`` anmd ``alt``. The general syntax for option is
+
+.. code-block:: text
+
+    option="value"
+
+Option can be enclosed with single or double quotes, as you like. Options defaults are the same as for the rst plugin.
+
+For pandoc_reader plugin users
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+The plugin ``pandoc_reader`` sends the Markdown body to the pandoc_ tool which has it's own Markdown parser, written in Haskell_ language: Python_ plugins manipulating Markdown posts (such this) are not used because the entire body id passed to pandoc_ without any iteraction by Pelican_.
+
+For those who are using the ``pandoc_reader`` plugin and wants to include PlantUML_ diagrams, use the ``pandoc-plantuml`` script (only *nix, sorry): it is a wrapper for filtering the code blocks parsed by pandoc_ before
+writing out the converted file. It is an adaption of the great work by Kurt Bonne for his `pandoc-plantuml-filter <https://github.com/kbonne/pandoc-plantuml-filter.git>`_.
+
+To use it, copy the ``pandoc-plantuml`` file in a subdirectory of your pelican project (for example `pandoc_extensions`) and make sure it is executable (``chmod +x pandoc-plantuml``).
+
+In the ``pelicanconf.py`` configure the needed plugins:
+
+.. code-block:: python
+
+    PLUGINS = ['pandoc_reader'] // Yes, plantuml plugin non necessary
+    PANDOC_ARGS = ['--filter=pandoc_extensions/pandoc-plantuml']
+
+In Markdown posts use the following syntax to include PlantUML_ diagrams:
+
+.. code-block:: markdown
+
+    ```plantuml
+    @startuml
+      Alice -> Bob: Authentication Request
+      Bob --> Alice: Authentication Response
+
+      Alice -> Bob: Another authentication Request
+      Alice <-- Bob: another authentication Response
+    @enduml
+    ```
+
+Rendered images will bu put in the output/images folder.
+
+**NOTE:** ``pandoc-plantuml`` is broken from pandoc 1.16 cause an API change in pandoc ``Image`` function. I'm working on a fix but in the meanwhile use a version of pandoc prior to ``1.16`` .
+
+Debugging
+---------
+The plugin can produce debugging informations to help to locate errors. To enable debugging execute ``pelican`` in debug mode:
+
+ .. code-block:: console
+
+     make DEBUG=1 html
+
+  
+Examples
+========
+
+Sequence diagram (from PlantUML_ site):
+
+.. code-block:: rst
+
+  .. uml::
+    :alt: Sample sequence diagram
+
+    participant User
+
+    User -> A: DoWork
+    activate A #FFBBBB
+
+    A -> A: Internal call
+    activate A #DarkSalmon
+
+    A -> B: << createRequest >>
+    activate B
+
+    B --> A: RequestCreated
+    deactivate B
+    deactivate A
+    A -> User: Done
+    deactivate A
+
+Output:
+
+.. image:: http://plantuml.sourceforge.net/imgp/sequence_022.png
+   :alt: Sample sequence diagram
+
+
+Same diagram with Python-Markdown_ syntax:
+
+.. code-block:: markdown
+
+    ::uml:: format="png" alt="Sample sequence diagram"
+      participant User
+
+      User -> A: DoWork
+      activate A #FFBBBB
+
+      A -> A: Internal call
+      activate A #DarkSalmon
+
+      A -> B: << createRequest >>
+      activate B
+
+      B --> A: RequestCreated
+      deactivate B
+      deactivate A
+      A -> User: Done
+      deactivate A
+    ::end-uml::
+
+Another example from PlantUML_ site (activity diagram):
+
+.. code-block:: rst
+
+  .. uml::
+
+    start
+    :ClickServlet.handleRequest();
+    :new page;
+    if (Page.onSecurityCheck) then (true)
+      :Page.onInit();
+      if (isForward?) then (no)
+	:Process controls;
+	if (continue processing?) then (no)
+	  stop
+	endif
+	
+	if (isPost?) then (yes)
+	  :Page.onPost();
+	else (no)
+	  :Page.onGet();
+	endif
+	:Page.onRender();
+      endif
+    else (false)
+    endif
+
+    if (do redirect?) then (yes)
+      :redirect process;
+    else
+      if (do forward?) then (yes)
+	:Forward request;
+      else (no)
+	:Render page template;
+      endif
+    endif
+
+    stop
+
+Generated image:
+
+.. image:: http://plantuml.sourceforge.net/imgp/activity2_009.png
+   :alt: Sample activity diagram
+
+
+
+.. _PlantUML: http://plantuml.sourceforge.net
+.. _Sabayon: http://www.sabayon.org
+.. _Gentoo: http://www.gentoo.org
+.. _layman: http://wiki.gentoo.org/wiki/Layman
+.. _Graphviz: http://www.graphviz.org
+.. _Pyhton-Markdown: http://pythonhosted.org/Markdown
+.. _pandoc: http://johnmacfarlane.net/pandoc
+.. _Haskell: http://www.haskell.org/haskellwiki/Haskell
+.. _Python:: http://www.python.org
+.. _Pelican: http://docs.getpelican.com/en
diff --git a/pelican-plugins/plantuml/__init__.py b/pelican-plugins/plantuml/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..f883239477bd29daf727317f7283d7f83d4f8079
--- /dev/null
+++ b/pelican-plugins/plantuml/__init__.py
@@ -0,0 +1 @@
+from .plantuml_rst import *
diff --git a/pelican-plugins/plantuml/generateUmlDiagram.py b/pelican-plugins/plantuml/generateUmlDiagram.py
new file mode 100644
index 0000000000000000000000000000000000000000..65c2679833aec4f16d7396d28f4c64c82b4a7fa2
--- /dev/null
+++ b/pelican-plugins/plantuml/generateUmlDiagram.py
@@ -0,0 +1,58 @@
+#!/usr/bin/env python
+import logging
+import os
+import tempfile
+from zlib import adler32
+from subprocess import Popen, PIPE
+from pelican import logger
+
+
+def generate_uml_image(path, plantuml_code, imgformat):
+    tf = tempfile.NamedTemporaryFile(delete=False)
+    tf.write('@startuml\n'.encode('utf8'))
+    tf.write(plantuml_code.encode('utf8'))
+    tf.write('\n@enduml'.encode('utf8'))
+    tf.flush()
+
+    logger.debug("[plantuml] Temporary PlantUML source at "+(tf.name))
+
+    if imgformat == 'png':
+        imgext = ".png"
+        outopt = "-tpng"
+    elif imgformat == 'svg':
+        imgext = ".svg"
+        outopt = "-tsvg"
+    else:
+        logger.error("Bad uml image format '"+imgformat+"', using png")
+        imgext = ".png"
+        outopt = "-tpng"
+
+    # make a name
+    name = tf.name+imgext
+    # build cmd line
+    cmdline = ['plantuml', '-o', path, outopt, tf.name]
+
+    try:
+        logger.debug("[plantuml] About to execute "+" ".join(cmdline))
+        p = Popen(cmdline, stdout=PIPE, stderr=PIPE)
+        out, err = p.communicate()
+    except Exception as exc:
+        raise Exception('Failed to run plantuml: %s' % exc)
+    else:
+        if p.returncode == 0:
+            # diagram was correctly generated, we can remove the temporary file (if not debugging)
+            if not logger.isEnabledFor(logging.DEBUG):
+                os.remove(tf.name)
+            # renaming output image using an hash code, just to not pollute
+            # output directory with a growing number of images
+            name = os.path.join(path, os.path.basename(name))
+            newname = os.path.join(path, "%08x" % (adler32(plantuml_code.encode()) & 0xffffffff))+imgext
+
+            if os.path.exists(newname):
+                os.remove(newname)
+
+            os.rename(name, newname)
+            return os.path.basename(newname)
+        else:
+            # the temporary file is still available as aid understanding errors
+            raise RuntimeError('Error calling plantuml: %s' % err)
diff --git a/pelican-plugins/plantuml/pandoc-plantuml b/pelican-plugins/plantuml/pandoc-plantuml
new file mode 100755
index 0000000000000000000000000000000000000000..abc5a30e804690845a316ab68db6abea54e7dca1
--- /dev/null
+++ b/pelican-plugins/plantuml/pandoc-plantuml
@@ -0,0 +1,89 @@
+#!/usr/bin/runhaskell
+{-|
+
+  Script adapted from pandoc-plantuml-filter by Kurt Bonne
+  Original source at https://github.com/kbonne/pandoc-plantuml-filter.git
+
+  This script is meant to be run by pandoc executed by the pandoc_reader pelican plugin.
+  I've changed output paths to be compatibile with pelican output structure.
+
+  If using the pandoc_reader pelican plugin with this script, the plantuml plugin is not necessary.
+
+  Installation:
+  -------------
+    This script requires Haskell, but if you are using pandoc, it's already installed :-)
+    Copy this file in your pelican project, in the same directory of pelicanconf.py, and make sure it is executable.
+
+    In the pelicanconf.py configure the need plugins:
+
+      PLUGINS = ['pandoc_reader']
+      PANDOC_ARGS = ['--filter=pandoc-plantuml']
+
+    If this script will be putted in a different location, adapt the PANDOC_ARGS value.
+
+  Usage:
+  ------
+    In Markdown posts use the following syntax to include PlantUML diagrams:
+
+    ```plantuml
+    @startuml
+       Alice -> Bob: Authentication Request
+       Bob --> Alice: Authentication Response
+
+       Alice -> Bob: Another authentication Request
+       Alice <-- Bob: another authentication Response
+    @enduml
+    ```
+
+    Rendered images will bu put in the output/images folder.
+-}
+
+import Text.Pandoc.JSON
+import Data.ByteString.Lazy (hGetContents, hPut)
+import Data.ByteString.Lazy.UTF8 (fromString)
+import Data.Digest.Pure.SHA (sha1, showDigest)
+import System.IO (hClose, hPutStr, IOMode(..), openBinaryFile, hPutStrLn, stderr)
+import System.Process
+import System.Directory
+
+processBlocks :: Block -> IO Block
+processBlocks b =
+  case b of
+    CodeBlock (_ , ["plantuml"], _) content -> plantUMLToImg content
+    _ -> return b
+
+plantUMLToImg :: String -> IO Block
+plantUMLToImg content =  do
+  path <- renderImage content
+  
+  --hPutStrLn stderr "dopo renderImage"
+
+  return $ Para [Image [] (path, "")]
+
+renderImage :: String -> IO String
+renderImage content = do
+  createDirectoryIfMissing (True) "output/images"
+  let path = uniqueName content ++ ".png"
+  (Just hIn, Just hOut, _, _) <-
+    createProcess (proc "plantuml" ["-pipe", "-tepg"]){ std_in = CreatePipe,
+                                                        std_out = CreatePipe }
+  hPutStr hIn content
+  hClose hIn
+
+  let outPath = "output/images/" ++ path
+  hFile <- openBinaryFile outPath WriteMode
+  img <- hGetContents hOut
+  hPut hFile img
+
+  hClose hFile
+  hClose hOut
+
+  let imgPath = "images/" ++ path
+
+  return imgPath
+
+uniqueName :: String -> String
+uniqueName = showDigest . sha1 . fromString
+
+main :: IO ()
+main = toJSONFilter processBlocks
diff --git a/pelican-plugins/plantuml/plantuml_md.py b/pelican-plugins/plantuml/plantuml_md.py
new file mode 100644
index 0000000000000000000000000000000000000000..0fddd8bf57381f3566064957a7d32d26863dde67
--- /dev/null
+++ b/pelican-plugins/plantuml/plantuml_md.py
@@ -0,0 +1,95 @@
+#!/usr/bin/env python
+"""
+   PlantUML_ Extension for Python-Markdown_
+   ========================================
+
+   Syntax:
+
+       ::uml:: [format="png|svg"] [classes="class1 class2 ..."] [alt="text for alt"]
+          PlantUML script diagram
+       ::end-uml::
+
+   Example:
+
+       ::uml:: format="png" classes="uml myDiagram" alt="My super diagram"
+          Goofy ->  MickeyMouse: calls
+          Goofy <-- MickeyMouse: responds
+       ::end-uml::
+
+   Options are optional, but if present must be specified in the order format, classes, alt.
+   The option value may be enclosed in single or double quotes.
+
+.. _Python-Markdown: http://pythonhosted.org/Markdown/
+.. _PlantUML: http://plantuml.sourceforge.net/
+"""
+import os
+import re
+import markdown
+from markdown.util import etree
+from .generateUmlDiagram import generate_uml_image
+
+# For details see https://pythonhosted.org/Markdown/extensions/api.html#blockparser
+class PlantUMLBlockProcessor(markdown.blockprocessors.BlockProcessor):
+    # Regular expression inspired by the codehilite Markdown plugin
+    RE = re.compile(r'''::uml::
+                        \s*(format=(?P<quot>"|')(?P<format>\w+)(?P=quot))?
+                        \s*(classes=(?P<quot1>"|')(?P<classes>[\w\s]+)(?P=quot1))?
+                        \s*(alt=(?P<quot2>"|')(?P<alt>[\w\s"']+)(?P=quot2))?
+                    ''', re.VERBOSE)
+    # Regular expression for identify end of UML script
+    RE_END = re.compile(r'::end-uml::\s*$')
+
+    def test(self, parent, block):
+        return self.RE.search(block)
+
+    def run(self, parent, blocks):
+        block = blocks.pop(0)
+        text = block
+
+        # Parse configuration params
+        m = self.RE.search(block)
+        format  = m.group('format')  if m.group('format')  else self.config['format']
+        classes = m.group('classes') if m.group('classes') else self.config['classes']
+        alt     = m.group('alt')     if m.group('alt')     else self.config['alt']
+
+        # Read blocks until end marker found
+        while blocks and not self.RE_END.search(block):
+            block = blocks.pop(0)
+            text = text + '\n' + block
+        else:
+            if not blocks:
+                raise RuntimeError("[plantuml] UML block not closed, text is:\n"+text)
+
+        # Remove block header and footer
+        text = re.sub(self.RE, "", re.sub(self.RE_END, "", text))
+
+        path = os.path.abspath(os.path.join('output', 'images'))
+        if not os.path.exists(path):
+            os.makedirs(path)
+
+        # Generate image from PlantUML script
+        imageurl = self.config['siteurl']+'/images/'+generate_uml_image(path, text, format)
+        # Create image tag and append to the document
+        etree.SubElement(parent, "img", src=imageurl, alt=alt, attrib={'class':classes})
+
+
+# For details see https://pythonhosted.org/Markdown/extensions/api.html#extendmarkdown
+class PlantUMLMarkdownExtension(markdown.Extension):
+    # For details see https://pythonhosted.org/Markdown/extensions/api.html#configsettings
+    def __init__(self, *args, **kwargs):
+        self.config = {
+            'classes': ["uml","Space separated list of classes for the generated image. Default uml."],
+            'alt'    : ["uml diagram", "Text to show when image is not available."],
+            'format' : ["png", "Format of image to generate (png or svg). Default png."],
+            'siteurl': ["", "URL of document, used as a prefix for the image diagram."]
+        }
+
+        super(PlantUMLMarkdownExtension, self).__init__(*args, **kwargs)
+
+    def extendMarkdown(self, md, md_globals):
+        blockprocessor = PlantUMLBlockProcessor(md.parser)
+        blockprocessor.config = self.getConfigs()
+        md.parser.blockprocessors.add('plantuml', blockprocessor, '>code')
+
+def makeExtension(**kwargs):
+    return PlantUMLMarkdownExtension(**kwargs)
diff --git a/pelican-plugins/plantuml/plantuml_rst.py b/pelican-plugins/plantuml/plantuml_rst.py
new file mode 100644
index 0000000000000000000000000000000000000000..d753f346bc7315d98e714ff1dcc0086162f0ba6a
--- /dev/null
+++ b/pelican-plugins/plantuml/plantuml_rst.py
@@ -0,0 +1,91 @@
+#!/usr/bin/env python
+"""Custom reST_ directive for plantuml_ integration.
+   Adapted from ditaa_rst plugin.
+
+.. _reST: http://docutils.sourceforge.net/rst.html
+.. _plantuml: http://plantuml.sourceforge.net/
+"""
+
+import sys
+import os
+
+from docutils.nodes import image, literal_block
+from docutils.parsers.rst import Directive, directives
+from pelican import signals, logger
+
+from .generateUmlDiagram import generate_uml_image
+
+
+global_siteurl = "" # URL of the site, filled on plugin initialization
+
+
+class PlantUML_rst(Directive):
+    """ reST directive for PlantUML """
+    required_arguments = 0
+    optional_arguments = 0
+    has_content = True
+
+    global global_siteurl
+
+    option_spec = {
+        'class' : directives.class_option,
+        'alt'   : directives.unchanged,
+        'format': directives.unchanged,
+    }
+
+    def run(self):
+        path = os.path.abspath(os.path.join('output', 'images'))
+        if not os.path.exists(path):
+            os.makedirs(path)
+
+        nodes = []
+        body = '\n'.join(self.content)
+
+        try:
+            uml_format = self.options.get('format', 'png')
+            url = global_siteurl+'/images/'+generate_uml_image(path, body, uml_format)
+        except Exception as exc:
+            error = self.state_machine.reporter.error(
+                'Failed to run plantuml: %s' % exc,
+                literal_block(self.block_text, self.block_text),
+                line=self.lineno)
+            nodes.append(error)
+        else:
+            alt = self.options.get('alt', 'uml diagram')
+            classes = self.options.pop('class', ['uml'])
+            imgnode = image(uri=url, classes=classes, alt=alt)
+            nodes.append(imgnode)
+
+        return nodes
+
+def pelican_init(pelicanobj):
+
+    global global_siteurl
+    global_siteurl = pelicanobj.settings['SITEURL']
+
+    """ Prepare configurations for the MD plugin """
+    try:
+        import markdown
+        from .plantuml_md import PlantUMLMarkdownExtension
+    except:
+        # Markdown not available
+        logger.debug("[plantuml] Markdown support not available")
+        return
+
+    # Register the Markdown plugin
+    config = { 'siteurl': pelicanobj.settings['SITEURL'] }
+
+    try:
+        if 'MD_EXTENSIONS' in pelicanobj.settings.keys(): # pre pelican 3.7.0
+            pelicanobj.settings['MD_EXTENSIONS'].append(PlantUMLMarkdownExtension(config))
+        elif 'MARKDOWN' in pelicanobj.settings.keys() and \
+             not ('extension_configs' in pelicanobj.settings['MARKDOWN']['extension_configs']):  # from pelican 3.7.0
+            pelicanobj.settings['MARKDOWN']['extension_configs']['plantuml.plantuml_md'] = config
+    except:
+        logger.error("[plantuml] Unable to configure plantuml markdown extension")
+
+
+def register():
+    """Plugin registration."""
+    signals.initialized.connect(pelican_init)
+    directives.register_directive('uml', PlantUML_rst)
diff --git a/pelican-plugins/plantuml/test_data/sequence.uml b/pelican-plugins/plantuml/test_data/sequence.uml
new file mode 100755
index 0000000000000000000000000000000000000000..d0f7d7981d6c40a28d74ccab644035d708943d19
--- /dev/null
+++ b/pelican-plugins/plantuml/test_data/sequence.uml
@@ -0,0 +1,7 @@
+@startuml
+Alice -> Bob: Authentication Request
+Bob --> Alice: Authentication Response
+
+Alice -> Bob: Another authentication Request
+Alice <-- Bob: another authentication Response
+@enduml
\ No newline at end of file
diff --git a/pelican-plugins/plantuml/test_plantuml.py b/pelican-plugins/plantuml/test_plantuml.py
new file mode 100644
index 0000000000000000000000000000000000000000..26aa8ab1d09b9af921b3ca7722719c39c5866a61
--- /dev/null
+++ b/pelican-plugins/plantuml/test_plantuml.py
@@ -0,0 +1,18 @@
+import os
+from os.path import dirname, join
+import unittest
+
+from .generateUmlDiagram import generate_uml_image
+
+
+PARENT_DIR = dirname(__file__)
+
+
+class GenerateUmlDiagramTest(unittest.TestCase):
+    maxDiff = None
+    def test_sequence_diagram(self):
+        with open(join(PARENT_DIR, 'test_data/sequence.uml')) as sequence_file:
+            generated_img_path = join(PARENT_DIR, generate_uml_image(PARENT_DIR, sequence_file.read(), 'svg'))
+        with open(generated_img_path) as actual_svg_file:
+            self.assertIn('<svg ', actual_svg_file.read())
+        os.remove(generated_img_path)
diff --git a/pelican-plugins/post_stats/__init__.py b/pelican-plugins/post_stats/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..2313bd5b1b8ba0ea12b9122c7e6fa8f1b922811a
--- /dev/null
+++ b/pelican-plugins/post_stats/__init__.py
@@ -0,0 +1 @@
+from .post_stats import *
diff --git a/pelican-plugins/post_stats/post_stats.py b/pelican-plugins/post_stats/post_stats.py
new file mode 100644
index 0000000000000000000000000000000000000000..80cfdcd49e3cbe10f06dd9e7cdb65962c5176a16
--- /dev/null
+++ b/pelican-plugins/post_stats/post_stats.py
@@ -0,0 +1,71 @@
+# -*- coding: utf-8 -*-
+"""
+Post Statistics
+========================
+
+This plugin calculates various statistics about a post and stores them in an article.stats dictionary:
+
+wc: how many words
+read_mins: how many minutes to read this article, based on 250 wpm (http://en.wikipedia.org/wiki/Words_per_minute#Reading_and_comprehension)
+word_counts: frquency count of all the words in the article; can be used for tag/word clouds/
+fi: Flesch-kincaid Index/ Reading Ease
+fk: Flesch-kincaid Grade Level
+
+"""
+
+from pelican import signals
+from bs4 import BeautifulSoup
+import re
+from collections import Counter
+
+from .readability import *
+
+
+def calculate_stats(instance):
+
+    if instance._content is not None:
+        stats = {}
+        content = instance._content
+
+        # How fast do average people read?
+        WPM = 250
+
+        # Use BeautifulSoup to get readable/visible text
+        raw_text = BeautifulSoup(content, 'html.parser').getText()
+
+        # Process the text to remove entities
+        entities = r'\&\#?.+?;'
+        raw_text = raw_text.replace('&nbsp;', ' ')
+        raw_text = re.sub(entities, '', raw_text)
+
+        # Flesch-kincaid readbility stats counts sentances,
+        # so save before removing punctuation
+        tmp = raw_text
+
+        # Process the text to remove punctuation
+        drop = u'.,?!@#$%^&*()_+-=\|/[]{}`~:;\'\"‘’—…“”'
+        raw_text = raw_text.translate(dict((ord(c), u'') for c in drop))
+
+        # Count the words in the text
+        words = raw_text.lower().split()
+        word_count = Counter(words)
+
+        # Return the stats
+        stats['word_counts'] = word_count
+        stats['wc'] = sum(word_count.values())
+
+        # Calulate how long it'll take to read, rounding up
+        stats['read_mins'] = (stats['wc'] + WPM - 1) // WPM
+        if stats['read_mins'] == 0:
+            stats['read_mins'] = 1
+
+        # Calculate Flesch-kincaid readbility stats
+        readability_stats = stcs, words, sbls = text_stats(tmp, stats['wc'])
+        stats['fi'] = "{:.2f}".format(flesch_index(readability_stats))
+        stats['fk'] = "{:.2f}".format(flesch_kincaid_level(readability_stats))
+
+        instance.stats = stats
+
+
+def register():
+    signals.content_object_init.connect(calculate_stats)
diff --git a/pelican-plugins/post_stats/readability.py b/pelican-plugins/post_stats/readability.py
new file mode 100644
index 0000000000000000000000000000000000000000..b26709822224de145a6b8bdf606573ee1c34cd25
--- /dev/null
+++ b/pelican-plugins/post_stats/readability.py
@@ -0,0 +1,56 @@
+# -*- coding: utf-8 -*-
+
+# Adadpted from here: http://acdx.net/calculating-the-flesch-kincaid-level-in-python/
+# See here for details: http://en.wikipedia.org/wiki/Flesch%E2%80%93Kincaid_readability_test
+
+from __future__ import division
+import re
+
+
+def mean(seq):
+    return sum(seq) / len(seq)
+
+
+def syllables(word):
+    if len(word) <= 3:
+        return 1
+
+    word = re.sub(r"(es|ed|(?<!l)e)$", "", word)
+    return len(re.findall(r"[aeiouy]+", word))
+
+
+def normalize(text):
+    terminators = ".!?:;"
+    term = re.escape(terminators)
+    text = re.sub(r"[^%s\sA-Za-z]+" % term, "", text)
+    text = re.sub(r"\s*([%s]+\s*)+" % term, ". ", text)
+    return re.sub(r"\s+", " ", text)
+
+
+def text_stats(text, wc):
+    text = normalize(text)
+    stcs = [s.split(" ") for s in text.split(". ")]
+    stcs = [s for s in stcs if len(s) >= 2]
+
+    if wc:
+        words = wc
+    else:
+        words = sum(len(s) for s in stcs)
+
+    sbls = sum(syllables(w) for s in stcs for w in s)
+
+    return len(stcs), words, sbls
+
+
+def flesch_index(stats):
+    stcs, words, sbls = stats
+    if stcs == 0 or words == 0:
+        return 0
+    return 206.835 - 1.015 * (words / stcs) - 84.6 * (sbls / words)
+
+
+def flesch_kincaid_level(stats):
+    stcs, words, sbls = stats
+    if stcs == 0 or words == 0:
+        return 0
+    return 0.39 * (words / stcs) + 11.8 * (sbls / words) - 15.59
diff --git a/pelican-plugins/post_stats/readme.rst b/pelican-plugins/post_stats/readme.rst
new file mode 100644
index 0000000000000000000000000000000000000000..89fc13b8054e7d036e8f4b7c48831043dd2a28a3
--- /dev/null
+++ b/pelican-plugins/post_stats/readme.rst
@@ -0,0 +1,49 @@
+Post Statistics
+==================
+
+A Pelican plugin to calculate various statistics about a post and store them in an article.stats dictionary:
+
+- ``wc``: how many words
+- ``read_mins``: how many minutes would it take to read this article, based on 250 wpm (http://en.wikipedia.org/wiki/Words_per_minute#Reading_and_comprehension)
+- ``word_counts``: frquency count of all the words in the article; can be used for tag/word clouds
+- ``fi``: Flesch-kincaid Index/ Reading Ease (see: http://en.wikipedia.org/wiki/Flesch%E2%80%93Kincaid_readability_tests)
+- ``fk``: Flesch-kincaid Grade Level
+
+Example:
+
+.. code-block:: python
+
+    {
+        'wc': 2760,
+        'fi': '65.94',
+        'fk': '7.65',
+        'word_counts': Counter({u'to': 98, u'a': 90, u'the': 83, u'of': 50, ...}),
+        'read_mins': 12
+    }
+
+This allows you to output these values in your templates, like this, for example:
+
+.. code-block:: html+jinja
+
+	<p title="~{{ article.stats['wc'] }} words">~{{ article.stats['read_mins'] }} min read</p>
+	<ul>
+	    <li>Flesch-kincaid Index/ Reading Ease: {{ article.stats['fi'] }}</li>
+	    <li>Flesch-kincaid Grade Level: {{ article.stats['fk'] }}</li>
+	</ul>
+
+The ``word_counts`` variable is a python ``Counter`` dictionary and looks something like this, with each unique word and it's frequency:
+
+.. code-block:: python
+
+	Counter({u'to': 98, u'a': 90, u'the': 83, u'of': 50, u'karma': 50, .....
+
+and can be used to create a tag/word cloud for a post.
+
+Requirements
+----------------
+
+`post_stats` requires BeautifulSoup.
+
+.. code-block:: console
+
+    $ pip install beautifulsoup4
diff --git a/pelican-plugins/random_article/Readme.md b/pelican-plugins/random_article/Readme.md
new file mode 100644
index 0000000000000000000000000000000000000000..fcf00a19ed1ba3fa43ab17d62df42561ef652788
--- /dev/null
+++ b/pelican-plugins/random_article/Readme.md
@@ -0,0 +1,19 @@
+Random Article Plugin For Pelican
+========================
+
+This plugin generates a html file which redirect to a random article
+using javascript's `window.location`. The generated html file is 
+saved at `SITEURL`.
+
+Only published articles are listed to redirect.
+
+Usage
+-----
+
+To use it you have to add in your config file the name of the file to use:
+
+    RANDOM = 'random.html'
+
+Then in some template you add:
+
+    <a href="{{ SITEURL }}/{{ RANDOM }}">random article</a>
diff --git a/pelican-plugins/random_article/__init__.py b/pelican-plugins/random_article/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..b59a6a03a193acf519225f4f9771ddd002d994fb
--- /dev/null
+++ b/pelican-plugins/random_article/__init__.py
@@ -0,0 +1 @@
+from .random_article import *
diff --git a/pelican-plugins/random_article/random_article.py b/pelican-plugins/random_article/random_article.py
new file mode 100644
index 0000000000000000000000000000000000000000..61e06d964c09209d993bd9c1d52495c8ceb15bc0
--- /dev/null
+++ b/pelican-plugins/random_article/random_article.py
@@ -0,0 +1,91 @@
+# -*- coding: utf-8 -*-
+"""
+Random Article Plugin For Pelican
+========================
+
+This plugin generates a html file which redirect to a random article
+using javascript's window.location. The generated html file is 
+saved at SITEURL.
+"""
+
+from __future__ import unicode_literals
+
+import os.path
+import logging
+
+from codecs import open
+
+from pelican import signals
+
+log = logging.getLogger(__name__)
+
+HTML_TOP = """
+<!DOCTYPE html>
+<head>
+    <title>random</title>
+    <script type="text/javascript">
+        function redirect(){
+            var urls = [
+"""
+
+HTML_BOTTOM = """
+        ""];
+
+        var index = Math.floor(Math.random() * (urls.length-1));
+        window.location = urls[index];
+    }
+</script>
+<body onload="redirect()">
+</body>
+</html>
+"""
+
+ARTICLE_URL = """ "{0}/{1}",
+"""
+
+
+class RandomArticleGenerator(object):
+    """
+        The structure is derived from sitemap plugin
+    """
+
+    def __init__(self, context, settings, path, theme, output_path, *null):
+
+        self.output_path = output_path
+        self.context = context
+        self.siteurl = settings.get('SITEURL')
+        self.randomurl = settings.get('RANDOM')
+
+    def write_url(self, article, fd):
+        if getattr(article, 'status', 'published') != 'published':
+            return
+
+        page_path = os.path.join(self.output_path, article.url)
+        if not os.path.exists(page_path):
+            return
+
+        fd.write(ARTICLE_URL.format(self.siteurl, article.url))
+
+
+    def generate_output(self, writer):
+        path = os.path.join(self.output_path, self.randomurl)
+        articles = self.context['articles']
+        log.info('writing %r', path)
+
+        if len(articles) == 0:
+            return
+
+        with open(path, 'w', encoding='utf-8') as fd:
+            fd.write(HTML_TOP)
+
+            for art in articles:
+                self.write_url(art, fd)
+
+            fd.write(HTML_BOTTOM)
+
+def get_generators(generators):
+    return RandomArticleGenerator
+
+
+def register():
+    signals.get_generators.connect(get_generators)
diff --git a/pelican-plugins/read_more_link/Readme.md b/pelican-plugins/read_more_link/Readme.md
new file mode 100644
index 0000000000000000000000000000000000000000..12baeec5ddb610be14a1c6c867c1097fc0f9c341
--- /dev/null
+++ b/pelican-plugins/read_more_link/Readme.md
@@ -0,0 +1,31 @@
+Read More Link
+===
+
+**NOTE:** [This plugin has been moved to its own repository](https://github.com/pelican-plugins/read-more). 
+Please file any issues/PRs there. Once all plugins have been migrated to the 
+[new Pelican Plugins organization](https://github.com/pelican-plugins>), 
+this monolithic repository will be archived.
+
+**Author**: Vuong Nguyen (http://vuongnguyen.com)
+
+This plugin inserts an inline "read more" or "continue" link into the last html element of the object summary.
+
+For more information, please visit: http://vuongnguyen.com/creating-inline-read-more-link-python-pelican-lxml.html
+
+Requirements
+---
+
+    lxml - for parsing html elements
+
+Settings
+---
+    # This settings indicates that you want to create summary at a certain length
+    SUMMARY_MAX_LENGTH = 50
+
+    # This indicates what goes inside the read more link
+    READ_MORE_LINK = None (ex: '<span>continue</span>')
+
+    # This is the format of the read more link
+    READ_MORE_LINK_FORMAT = '<a class="read-more" href="/{url}">{text}</a>'
+
+
diff --git a/pelican-plugins/read_more_link/__init__.py b/pelican-plugins/read_more_link/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..6b3673b0a4e3cfe622fe7be95c00ba164191074d
--- /dev/null
+++ b/pelican-plugins/read_more_link/__init__.py
@@ -0,0 +1 @@
+from .read_more_link import *
\ No newline at end of file
diff --git a/pelican-plugins/read_more_link/read_more_link.py b/pelican-plugins/read_more_link/read_more_link.py
new file mode 100644
index 0000000000000000000000000000000000000000..bcb5c6ecf636eec0bd9e4cc27426d716ee3427f1
--- /dev/null
+++ b/pelican-plugins/read_more_link/read_more_link.py
@@ -0,0 +1,85 @@
+# -*- coding: utf-8 -*-
+"""
+Read More Link
+===========================
+
+This plugin inserts an inline "read more" or "continue" link into the last html element of the object summary.
+
+For more information, please visit: http://vuongnguyen.com/creating-inline-read-more-link-python-pelican-lxml.html
+
+"""
+
+from pelican import signals, contents
+from pelican.utils import truncate_html_words
+from pelican.generators import ArticlesGenerator
+
+try:
+    from lxml.html import fragment_fromstring, fragments_fromstring, tostring
+    from lxml.etree import ParserError
+except ImportError:
+    raise Exception("Unable to find lxml. To use READ_MORE_LINK, you need lxml")
+
+
+def insert_into_last_element(html, element):
+    """
+    function to insert an html element into another html fragment
+    example:
+        html = '<p>paragraph1</p><p>paragraph2...</p>'
+        element = '<a href="/read-more/">read more</a>'
+        ---> '<p>paragraph1</p><p>paragraph2...<a href="/read-more/">read more</a></p>'
+    """
+    try:
+        item = fragment_fromstring(element)
+    except (ParserError, TypeError) as e:
+        item = fragment_fromstring('<span></span>')
+
+    try:
+        doc = fragments_fromstring(html)
+        doc[-1].append(item)
+
+        return ''.join(tostring(e) for e in doc)
+    except (ParserError, TypeError) as e:
+        return ''
+
+def insert_read_more_link(instance):
+    """
+    Insert an inline "read more" link into the last element of the summary
+    :param instance:
+    :return:
+    """
+
+    # only deals with Article type
+    if type(instance) != contents.Article: return
+
+
+    SUMMARY_MAX_LENGTH = instance.settings.get('SUMMARY_MAX_LENGTH')
+    READ_MORE_LINK = instance.settings.get('READ_MORE_LINK', None)
+    READ_MORE_LINK_FORMAT = instance.settings.get('READ_MORE_LINK_FORMAT',
+                                                  '<a class="read-more" href="/{url}">{text}</a>')
+
+    if not (SUMMARY_MAX_LENGTH and READ_MORE_LINK and READ_MORE_LINK_FORMAT): return
+
+    if hasattr(instance, '_summary') and instance._summary:
+        summary = instance._summary
+    else:
+        summary = truncate_html_words(instance.content, SUMMARY_MAX_LENGTH)
+
+    if summary != instance.content:
+        read_more_link = READ_MORE_LINK_FORMAT.format(url=instance.url, text=READ_MORE_LINK)
+        instance._summary = insert_into_last_element(summary, read_more_link)
+
+
+def run_plugin(generators):
+    for generator in generators:
+        if isinstance(generator, ArticlesGenerator):
+            for article in generator.articles:
+                insert_read_more_link(article)
+
+
+def register():
+    try:
+        signals.all_generators_finalized.connect(run_plugin)
+    except AttributeError:
+        # NOTE: This may result in #314 so shouldn't really be relied on
+        # https://github.com/getpelican/pelican-plugins/issues/314
+        signals.content_object_init.connect(insert_read_more_link)
diff --git a/pelican-plugins/read_more_link/requirements.txt b/pelican-plugins/read_more_link/requirements.txt
new file mode 100644
index 0000000000000000000000000000000000000000..41aa5f11740920283abe284436a2f1f96b54feb6
--- /dev/null
+++ b/pelican-plugins/read_more_link/requirements.txt
@@ -0,0 +1 @@
+lxml>=3.2.1
\ No newline at end of file
diff --git a/pelican-plugins/readtime/README.md b/pelican-plugins/readtime/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..e7123435187f9570cf76e05bebc738f0ed44eeb2
--- /dev/null
+++ b/pelican-plugins/readtime/README.md
@@ -0,0 +1,41 @@
+# 'pelican-readtime' - A Read Time Plugin for Pelican Static Site Generator
+
+An article estimated read time plugin for Pelican static site generator. After Pelican generated the content of each page, the plugin read through the generated HTML content and strip all the tags, count all the word, then utilize average human read speed to calculate the read time of each article. The read time is passed over to the 'content' object of the article so Jinja template can use it to display the read time on wherever appropriate.
+
+
+Demo
+-----
+The plugin can be embedded into your site's template and look like this:<br>
+![Pelican Read Time Demo](demo.png )<br>
+An example can be found in my own blog [here](https://wayofnumbers.github.io/).<br>
+
+Usage
+-----
+
+This plugin only uses standard modules(re, html, math, etc), so no extra module installation like BeautifuSoup. To use it, just add the plugin name to the **pelicanconf.py** file.
+```python
+    PLUGINS=[ ... , 'pelican-readtime']
+```
+Then you can put the following code in whichever template you what, like *article.html*. 
+```html
+    {% if article.readtime %}
+    <div><b>Read in {{article.readtime.minutes}} min.</b></div>
+    {% endif %}
+```
+
+You can also add some styling to it like so:
+```html
+    {% if article.readtime %}
+    <span><p style="text-align:right; color:#aaaaaa; ">&nbsp Estimated read time: {{article.readtime.minutes}} min.</p></span>
+    {% endif %}
+```
+
+Credits
+-----
+This is a revised version of [jmaister's readtime plugin](https://github.com/jmaister/readtime). I added some more comments and tweaked the code so it runs smoothly on Python 3.6
+
+
+Reference
+-----
+[1] Wikipedia - [Words per minute](https://en.wikipedia.org/wiki/Words_per_minute) <br>
+[2] Medium - [Read Time](https://help.medium.com/hc/en-us/articles/214991667-Read-time) <br>
diff --git a/pelican-plugins/readtime/__init__.py b/pelican-plugins/readtime/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..8f75495952e0ccc650843efd5c2de985af1aa8e7
--- /dev/null
+++ b/pelican-plugins/readtime/__init__.py
@@ -0,0 +1 @@
+from .readtime import *
diff --git a/pelican-plugins/readtime/demo.png b/pelican-plugins/readtime/demo.png
new file mode 100644
index 0000000000000000000000000000000000000000..a600e4028a137fe2c14d41b83779d7cd91ffd4eb
Binary files /dev/null and b/pelican-plugins/readtime/demo.png differ
diff --git a/pelican-plugins/readtime/readtime.py b/pelican-plugins/readtime/readtime.py
new file mode 100644
index 0000000000000000000000000000000000000000..9cd20993ba18d45397598c5bc1cceac9ae5f7794
--- /dev/null
+++ b/pelican-plugins/readtime/readtime.py
@@ -0,0 +1,55 @@
+import re
+import math
+
+from pelican import signals
+from html.parser import HTMLParser  #use html.parser for Python 3.6
+
+
+# http://en.wikipedia.org/wiki/Words_per_minute
+WPM = 230.0
+
+
+class MLStripper(HTMLParser):
+    def __init__(self):
+        super().__init__()    	# subclassing HTMLParser, also need to calling
+				# super class's '__init__' method
+        self.reset()
+        self.fed = []
+
+    #this method is called whenever a 'data' is encountered.
+    def handle_data(self, d):
+        self.fed.append(d)
+
+    # join all content word into one long sentence for further processing
+    def get_data(self):
+        return ''.join(self.fed)
+
+
+def strip_tags(html):
+    s = MLStripper()
+    s.feed(html)   		# Feed the class with html content, get the fed list
+    return s.get_data()
+
+
+def calculate_readtime(content_object):
+    if content_object._content is not None:
+        content = content_object._content	# get the content html from Pelican
+
+        text = strip_tags(content)		#strip tags and get long sentence
+        words = re.split(r'[^0-9A-Za-z]+', text) # split the long sentence into list of words
+
+        num_words = len(words)  	# count the words
+        minutes = int(math.ceil(num_words / WPM))  #calculate the minutes
+
+	    #set minimum read time to 1 minutes.
+        if minutes == 0:
+            minutes = 1
+
+        content_object.readtime = {
+            "minutes": minutes,
+        }
+
+
+def register():
+    signals.content_object_init.connect(calculate_readtime)   # connect with 'content_object_init' signal.
+
diff --git a/pelican-plugins/reddit_poster/.gitignore b/pelican-plugins/reddit_poster/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..1d17dae13b53adda563547053eb79233b236f797
--- /dev/null
+++ b/pelican-plugins/reddit_poster/.gitignore
@@ -0,0 +1 @@
+.venv
diff --git a/pelican-plugins/reddit_poster/Readme.md b/pelican-plugins/reddit_poster/Readme.md
new file mode 100644
index 0000000000000000000000000000000000000000..bfb63b4a6620f459efd38a982b9b5f2e66cf439d
--- /dev/null
+++ b/pelican-plugins/reddit_poster/Readme.md
@@ -0,0 +1,51 @@
+Reddit poster posts articles to reddit
+===================================
+
+You can use the 'subreddit' attribute in you articles to specify which 
+subbreddit the article should be post in aside of your default sub.
+
+We look for the subreddit in the metadata.
+Then try to find a post that has the same title as this article.
+If we can't find a post we create it.
+
+## Usage
+I followed these steps for praw: https://praw.readthedocs.io/en/latest/getting_started/authentication.html#script-application
+Summeraized:
++ Go to https://www.reddit.com/prefs/apps/ 
++ create a script for example named 'reddit-poster'.
++ redirect uri is unused so can be http://localhost:8080
++ about uri is probably some page on your website, not important either I think
+
+This will create an 'application', ie it let's reddit know you have a script by 
+using the secret generated.
+Interesting from this screen is the secret and the app id. copy those over 
+somewhere.
+
+We need 4 pieces of information from you which you must *not* commit to source 
+code. These should all be considered private.
+This script will expect these values to be set in the settings.
+You should store them for real in either an ignored file, environment variables,
+or pass them each time as command line options.
+Also make sure to set your SITEURL, for proper backlinks
+
+https://praw.readthedocs.io/en/latest/code_overview/models/subreddit.html#praw.models.Subreddit.search
+https://praw.readthedocs.io/en/latest/code_overview/models/subreddit.html#praw.models.Subreddit.submit
+
+### Config example
+
+```python
+REDDIT_POSTER_AUTH = {
+    'username': 'blah',
+    'password': 'pass',
+    'client_id': 'client',
+    'client_secret': 'secret',
+    'user_agent': 'python:pelican_redditposter:1 by /u/jappieofficial'
+}
+REDDIT_POSTER_COLLECT_SUB = "jappie"
+```
+## Dependencies
++ praw 5.4
+
+On fedora the praw in yum is too old, however the pip requests doesn't 
+work for some reason, so I installed praw with pip and requests with dnf 
+which seems to work (requests probably needed some sys lib or something)
diff --git a/pelican-plugins/reddit_poster/__init__.py b/pelican-plugins/reddit_poster/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..1f5334e13b1a06f1aea2df6517b85422d24f0495
--- /dev/null
+++ b/pelican-plugins/reddit_poster/__init__.py
@@ -0,0 +1,83 @@
+"""
+Reddit poster posts articles to reddit
+
+You can use the 'subreddit' attribute in you articles to specify which 
+subbreddit the article should be post in aside of your default sub.
+"""
+
+from collections import OrderedDict
+
+from pelican import signals
+from pelican.generators import Generator
+from functools import partial
+import logging
+import praw
+import lxml.html
+
+log = logging.getLogger(__name__)
+
+def cross_post(reddit, submission, subs):
+    subreddits = [] if subs is None else subs.split(' ')
+    log.debug("Posting in marked subs: %s", subreddits)
+    for sub in subreddits:
+        if sub == '':
+            continue
+        log.debug("Posting in %s" % sub)
+        subreddit = reddit.subreddit(sub)
+        subreddit.subscribe() # must be subscribed to crosspost
+        submission.crosspost(subreddit)
+
+def make_posts(generator, metadata, url):
+    """
+    Make posts on reddit if it's not a draft, on whatever subs are specified
+    """
+    reddit = generator.get_reddit()
+    title =  lxml.html.fromstring(metadata['title']).text_content()
+    if reddit is None:
+        log.info("Reddit plugin not enabled")
+        return
+    if metadata.get('status') == "draft": # people don't want to post drafts
+        log.debug("ignoring draft %s" % title)
+        return
+
+    collection = generator.settings['REDDIT_POSTER_COLLECT_SUB']
+    sub = reddit.subreddit(collection)
+    results = sub.search(title)
+    if len([result for result in results]) > 0:
+        log.debug("ignoring %s because it is already on sub %s " % (title, collection))
+        # post already was made to this sub
+        return
+    try:
+        submission = sub.submit(title, url=url, resubmit=False)
+        cross_post(reddit, submission, metadata.get('subreddit'))
+    except praw.exceptions.APIException as e:
+        log.error("got an api exception: %s", e)
+    except AssertionError as e:
+        log.error("Received an assertion error %s", e)
+
+
+def init_reddit(generator):
+    """
+    this is a hack to make sure the reddit object keeps track of a session
+    trough article scanning, speeding up networking as the connection can be 
+    kept alive.
+    """
+    auth_dict = generator.settings.get('REDDIT_POSTER_AUTH')
+    if auth_dict is None:
+        log.info("Could not find REDDIT_POSTER_AUTH key in settings, reddit plugin won't function")
+        generator.get_reddit = lambda: None
+        return
+
+    reddit = praw.Reddit(**auth_dict)
+    generator.get_reddit = lambda: reddit # .. openworld has it's merrits
+
+def content_written(generator, content):
+    """
+    create a url and call make posts (which has less information)
+    """
+    url = "%s/%s" % (generator.settings.get('SITEURL', 'http://localhost:8000'), content.url)
+    make_posts(generator, content.metadata, url)
+
+def register():
+    signals.article_generator_write_article.connect(content_written)
+    signals.article_generator_init.connect(init_reddit)
diff --git a/pelican-plugins/reddit_poster/redditconf.py.example b/pelican-plugins/reddit_poster/redditconf.py.example
new file mode 100644
index 0000000000000000000000000000000000000000..fe28500bc1c5d36329785a924fdfdc8fdb36ee8d
--- /dev/null
+++ b/pelican-plugins/reddit_poster/redditconf.py.example
@@ -0,0 +1,9 @@
+REDDIT_POSTER_AUTH = {
+    'username': 'blah',
+    'password': 'pass',
+    'client_id': 'client',
+    'client_secret': 'secret',
+    'user_agent': 'python:pelican_redditposter:1 by /u/jappieofficial'
+}
+# a sub where all the posts get collected
+REDDIT_POSTER_COLLECT_SUB = "jappie"
\ No newline at end of file
diff --git a/pelican-plugins/reddit_poster/requirements.txt b/pelican-plugins/reddit_poster/requirements.txt
new file mode 100755
index 0000000000000000000000000000000000000000..9d9d90a490bfc071c0eaab9a8e1dd2bd491e9ac4
--- /dev/null
+++ b/pelican-plugins/reddit_poster/requirements.txt
@@ -0,0 +1 @@
+praw
\ No newline at end of file
diff --git a/pelican-plugins/related_posts/Readme.rst b/pelican-plugins/related_posts/Readme.rst
new file mode 100644
index 0000000000000000000000000000000000000000..0edf4bd8b71af02895adc3dc03a199e32feafa07
--- /dev/null
+++ b/pelican-plugins/related_posts/Readme.rst
@@ -0,0 +1,38 @@
+Related posts
+-------------
+
+**NOTE:** `This plugin has been moved to its own repository <https://github.com/pelican-plugins/related-posts>`_. Please file any issues/PRs there. Once all plugins have been migrated to the `new Pelican Plugins organization <https://github.com/pelican-plugins>`_, this monolithic repository will be archived.
+
+-------------------------------------------------------------------------------
+
+This plugin adds the ``related_posts`` variable to the article's context.
+By default, up to 5 articles are listed. You can customize this value by 
+defining ``RELATED_POSTS_MAX`` in your settings file::
+
+    RELATED_POSTS_MAX = 10
+
+You can then use the ``article.related_posts`` variable in your templates.
+For example::
+
+    {% if article.related_posts %}
+        <ul>
+        {% for related_post in article.related_posts %}
+            <li><a href="{{ SITEURL }}/{{ related_post.url }}">{{ related_post.title }}</a></li>
+        {% endfor %}
+        </ul>
+    {% endif %}
+
+
+Your related posts should share a common tag. You can also use ``related_posts:`` in your post's meta data.
+The 'related_posts:' meta data works together with your existing slugs::
+
+    related_posts: slug1, slug2, slug3, ... slugN
+
+``N`` represents the ``RELATED_POSTS_MAX``.
+
+Additionally, you can specify::
+
+    RELATED_POSTS_SKIP_SAME_CATEGORY = True
+
+in your settings file. With this setting, ``article.related_posts`` will
+contain only related posts from categories other than the original article's.
diff --git a/pelican-plugins/related_posts/__init__.py b/pelican-plugins/related_posts/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..057540e34abe72f63fe51ef4f0c47ebef6549270
--- /dev/null
+++ b/pelican-plugins/related_posts/__init__.py
@@ -0,0 +1 @@
+from .related_posts import *
diff --git a/pelican-plugins/related_posts/related_posts.py b/pelican-plugins/related_posts/related_posts.py
new file mode 100644
index 0000000000000000000000000000000000000000..fbb24260eb9bb4dd9f46066079b814e381682878
--- /dev/null
+++ b/pelican-plugins/related_posts/related_posts.py
@@ -0,0 +1,56 @@
+"""
+Related posts plugin for Pelican
+================================
+
+Adds related_posts variable to article's context
+"""
+
+from pelican import signals
+from collections import Counter
+from itertools import chain
+
+
+def add_related_posts(generator):
+    # get the max number of entries from settings
+    # or fall back to default (5)
+    numentries = generator.settings.get('RELATED_POSTS_MAX', 5)
+    # Skip all posts in the same category as the article
+    skipcategory = generator.settings.get('RELATED_POSTS_SKIP_SAME_CATEGORY', False)
+    for article in chain(generator.articles, generator.drafts):
+        # set priority in case of forced related posts
+        if hasattr(article,'related_posts'):
+            # split slugs 
+            related_posts = article.related_posts.split(',')
+            posts = [] 
+            # get related articles
+            for slug in related_posts:
+                i = 0
+                slug = slug.strip()
+                for a in generator.articles:
+                    if i >= numentries: # break in case there are max related psots
+                        break
+                    if a.slug == slug:
+                        posts.append(a)
+                        i += 1
+
+            article.related_posts = posts
+        else:
+            # no tag, no relation
+            if not hasattr(article, 'tags'):
+                continue
+
+            # score = number of common tags
+            related = chain(*(generator.tags[tag] for tag in article.tags))
+            if skipcategory:
+                related = (other for other in related
+                                 if other.category != article.category)
+            scores = Counter(related)
+
+            # remove itself
+            scores.pop(article, None)
+
+            article.related_posts = [other for other, count 
+                in scores.most_common(numentries)]
+
+def register():
+    signals.article_generator_finalized.connect(add_related_posts)
diff --git a/pelican-plugins/render_math/Readme.md b/pelican-plugins/render_math/Readme.md
new file mode 100644
index 0000000000000000000000000000000000000000..7d541aa6ad8e01ba0f8aa232ad95fe81784cd8d3
--- /dev/null
+++ b/pelican-plugins/render_math/Readme.md
@@ -0,0 +1,167 @@
+Math Render Plugin For Pelican
+==============================
+
+**NOTE: [This plugin has been moved to its own repository](https://github.com/pelican-plugins/render-math). Please file any issues/PRs there. Once all plugins have been migrated to the [new Pelican Plugins organization](https://github.com/pelican-plugins), this monolithic repository will be archived.**
+
+This plugin gives pelican the ability to render mathematics. It accomplishes
+this by using the [MathJax](http://www.mathjax.org/) javascript engine.
+
+The plugin also ensures that Typogrify and recognized math "play" nicely together, by
+ensuring [Typogrify](https://github.com/mintchaos/typogrify) does not alter math content.
+
+Both Markdown and reStructuredText is supported.
+
+Requirements
+------------
+
+  * Pelican version *3.6* or above is required.
+  * Typogrify version *2.0.7* or higher is needed for Typogrify to play
+    "nicely" with this plugin. If this version is not available, Typogrify
+    will be disabled for the entire site.
+  * BeautifulSoup4 is required to correct summaries. If BeautifulSoup4 is
+    not installed, summary processing will be ignored, even if specified
+    in user settings.
+
+Installation
+------------
+To enable, ensure that `render_math` plugin is accessible.
+Then add the following to settings.py:
+
+    PLUGINS = ["render_math"]
+
+Your site is now capable of rendering math math using the mathjax JavaScript
+engine. No alterations to the template is needed, just use and enjoy!
+
+However, if you wish, you can set the `auto_insert` setting to `False` which
+will disable the mathjax script from being automatically inserted into the
+content. You would only want to do this if you had control over the template
+and wanted to insert the script manually.
+
+### Typogrify
+In the past, using [Typgogrify](https://github.com/mintchaos/typogrify) would
+alter the math contents resulting in math that could not be rendered by MathJax.
+The only option was to ensure that Typogrify was disabled in the settings.
+
+The problem has been rectified in this plugin, but it requires at a minimum
+[Typogrify version 2.0.7](https://pypi.python.org/pypi/typogrify) (or higher).
+If this version is not present, the plugin will disable Typogrify for the entire
+site.
+
+### BeautifulSoup4
+Pelican creates summaries by truncating the contents to a specified user length.
+The truncation process is oblivious to any math and can therefore destroy
+the math output in the summary.
+
+To restore math, [BeautifulSoup4](https://pypi.python.org/pypi/beautifulsoup4/4.4.0)
+is used. If it is not installed, no summary processing will happen.
+
+Usage
+-----
+### Templates
+No alteration is needed to a template for this plugin to work. Just install
+the plugin and start writing your Math. 
+
+### Settings
+Certain MathJax rendering options can be set. These options 
+are in a dictionary variable called `MATH_JAX` in the pelican
+settings file.
+
+The dictionary can be set with the following keys:
+
+ * `align`: [string] controls how displayed math will be aligned. Can be set to either
+`'left'`, `'right'` or `'center'`. **Default Value**: `'center'`.
+ * `auto_insert`: [boolean] will insert the mathjax script into content that it is
+detected to have math in it. Setting it to false is not recommended.
+**Default Value**: `True`
+ * `indent`: [string] if `align` not set to `'center'`, then this controls the indent
+level. **Default Value**: `'0em'`.
+ * `show_menu`: [boolean] controls whether the mathjax contextual menu is shown.
+**Default Value**: `True`
+ * `process_escapes`: [boolean] controls whether mathjax processes escape sequences.
+**Default Value**: `True`
+ * `mathjax_font`: [string] will force mathjax to use the chosen font. Current choices
+for the font is `sanserif`, `typewriter` or `fraktur`. If this is not set, it will
+use the default font settings. **Default Value**: `default`
+ * `latex_preview`: [string] controls the preview message users are shown while mathjax is
+rendering LaTex. If set to `'Tex'`, then the TeX code is used as the preview 
+(which will be visible until it is processed by MathJax). **Default Value**: `'Tex'`
+ * `color`: [string] controls the color of the mathjax rendered font. **Default Value**: `'inherit'`
+ * `linebreak_automatic`: [boolean] If set, Mathjax will try to *intelligently* break up displayed math
+(Note: It will not work for inline math). This is very useful for a responsive site. It
+is turned off by default due to it potentially being CPU expensive. **Default Value**: `False`
+ * `tex_extensions`: [list] a list of [latex extensions](http://docs.mathjax.org/en/latest/tex.html#tex-and-latex-extensions)
+accepted by mathjax. **Default Value**: `[]` (empty list)
+ * `responsive`: [boolean] tries to make displayed math render responsively. It does by determining if the width
+is less than `responsive_break` (see below) and if so, sets `align` to `left`, `indent` to `0em` and `linebreak_automatic` to `True`.
+**Default Value**: `False` (defaults to `False` for backward compatibility)
+ * `responsive_break`: [integer] a number (in pixels) representing the width breakpoint that is used
+when setting `responsive_align` to `True`. **Default Value**: 768
+ * `process_summary`: [boolean] ensures math will render in summaries and fixes math in that were cut off.
+Requires [BeautifulSoup4](http://www.crummy.com/software/BeautifulSoup/bs4/doc/) be installed. **Default Value**: `True`
+ * `message_style`: [string] This value controls the verbosity of the messages in the lower left-hand corner. Set it to `None` to eliminate all messages.
+**Default Value**: normal
+
+#### Settings Examples
+Make math render in blue and displaymath align to the left:
+
+    MATH_JAX = {'color':'blue','align':left}
+
+Use the [color](http://docs.mathjax.org/en/latest/tex.html#color) and
+[mhchem](http://docs.mathjax.org/en/latest/tex.html#mhchem) extensions:
+    
+    MATH_JAX = {'tex_extensions': ['color.js','mhchem.js']}
+
+#### Resulting HTML
+Inlined math is wrapped in `span` tags, while displayed math is wrapped in `div` tags.
+These tags will have a class attribute that is set to `math` which 
+can be used by template designers to alter the display of the math.
+
+Markdown
+--------
+This plugin implements a custom extension for markdown resulting in math
+being a "first class citizen" for Pelican. 
+
+### Inlined Math
+Math between `$`..`$`, for example, `$`x^2`$`, will be rendered inline
+with respect to the current html block. Note: To use inline math, there
+must *not* be any whitespace before the ending `$`. So for example:
+
+ * **Relevant inline math**: `$e=mc^2$`
+ * **Will not render as inline math**: `$40 vs $50`
+
+### Displayed Math
+Math between `$$`..`$$` will be rendered "block style", for example, `$$`x^2`$$`, will be rendered centered in a
+new paragraph.
+
+#### Other Latex  Display Math commands
+The other LaTeX commands which usually invoke display math mode from text mode
+are supported,
+and are automatically treated like `$$`-style displayed math 
+in that they are rendered "block" style on their own lines.
+For example, `\begin{equation}` x^2 `\end{equation}`,
+will be rendered in its own block with a right justified equation number
+at the top of the block. This equation number can be referenced in the document.
+To do this, use a `label` inside of the equation format and then refer to that label
+using `ref`. For example: `\begin{equation}` `\label{eq}` X^2 `\end{equation}`. 
+Now refer to that equation number by `$`\ref{eq}`$`.
+
+reStructuredText
+----------------
+If there is math detected in reStructuredText document, the plugin will automatically
+set the [math_output](http://docutils.sourceforge.net/docs/user/config.html#math-output) configuration setting to `MathJax`.
+
+### Inlined Math
+Inlined math needs to use the [math role](http://docutils.sourceforge.net/docs/ref/rst/roles.html#math):
+
+```
+The area of a circle is :math:`A_\text{c} = (\pi/4) d^2`.
+```
+
+### Displayed Math
+Displayed math uses the [math block](http://docutils.sourceforge.net/docs/ref/rst/directives.html#math):
+
+```
+.. math::
+
+  α_t(i) = P(O_1, O_2, … O_t, q_t = S_i λ)
+```
diff --git a/pelican-plugins/render_math/__init__.py b/pelican-plugins/render_math/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..2ac15ddaf2f0e3381db91714529ac2f335e8c985
--- /dev/null
+++ b/pelican-plugins/render_math/__init__.py
@@ -0,0 +1 @@
+from .math import *
diff --git a/pelican-plugins/render_math/math.py b/pelican-plugins/render_math/math.py
new file mode 100644
index 0000000000000000000000000000000000000000..165d59e2032de1145ea95c3ea201963158adeaff
--- /dev/null
+++ b/pelican-plugins/render_math/math.py
@@ -0,0 +1,367 @@
+# -*- coding: utf-8 -*-
+"""
+Math Render Plugin for Pelican
+==============================
+This plugin allows your site to render Math. It uses
+the MathJax JavaScript engine.
+
+For markdown, the plugin works by creating a Markdown
+extension which is used during the markdown compilation
+stage.  Math therefore gets treated like a "first class
+citizen" in Pelican
+
+For reStructuredText, the plugin instructs the rst engine
+to output Mathjax for all math.
+
+The mathjax script is by default automatically inserted
+into the HTML.
+
+Typogrify Compatibility
+-----------------------
+This plugin now plays nicely with Typogrify, but it
+requires Typogrify version 2.07 or above.
+
+User Settings
+-------------
+Users are also able to pass a dictionary of settings
+in the settings file which will control how the MathJax
+library renders things. This could be very useful for
+template builders that want to adjust the look and feel of
+the math.  See README for more details.
+"""
+
+import os
+import sys
+
+from pelican import signals, generators
+
+try:
+    from bs4 import BeautifulSoup
+except ImportError as e:
+    BeautifulSoup = None
+
+try:
+    from . pelican_mathjax_markdown_extension import PelicanMathJaxExtension
+except ImportError as e:
+    PelicanMathJaxExtension = None
+
+try:
+    string_type = basestring
+except NameError:
+    string_type = str
+
+
+def process_settings(pelicanobj):
+    """Sets user specified MathJax settings (see README for more details)"""
+
+    mathjax_settings = {}
+
+    # NOTE TO FUTURE DEVELOPERS: Look at the README and what is happening in
+    # this function if any additional changes to the mathjax settings need to
+    # be incorporated. Also, please inline comment what the variables
+    # will be used for
+
+    # Default settings
+    mathjax_settings['auto_insert'] = True  # if set to true, it will insert mathjax script automatically into content without needing to alter the template.
+    mathjax_settings['align'] = 'center'  # controls alignment of of displayed equations (values can be: left, right, center)
+    mathjax_settings['indent'] = '0em'  # if above is not set to 'center', then this setting acts as an indent
+    mathjax_settings['show_menu'] = 'true'  # controls whether to attach mathjax contextual menu
+    mathjax_settings['process_escapes'] = 'true'  # controls whether escapes are processed
+    mathjax_settings['latex_preview'] = 'TeX'  # controls what user sees while waiting for LaTex to render
+    mathjax_settings['color'] = 'inherit'  # controls color math is rendered in
+    mathjax_settings['linebreak_automatic'] = 'false'  # Set to false by default for performance reasons (see http://docs.mathjax.org/en/latest/output.html#automatic-line-breaking)
+    mathjax_settings['tex_extensions'] = ''  # latex extensions that can be embedded inside mathjax (see http://docs.mathjax.org/en/latest/tex.html#tex-and-latex-extensions)
+    mathjax_settings['responsive'] = 'false'  # Tries to make displayed math responsive
+    mathjax_settings['responsive_break'] = '768'  # The break point at which it math is responsively aligned (in pixels)
+    mathjax_settings['mathjax_font'] = 'default'  # forces mathjax to use the specified font.
+    mathjax_settings['process_summary'] = BeautifulSoup is not None  # will fix up summaries if math is cut off. Requires beautiful soup
+    mathjax_settings['message_style'] = 'normal'  # This value controls the verbosity of the messages in the lower left-hand corner. Set it to "none" to eliminate all messages
+    mathjax_settings['font_list'] = ['STIX', 'TeX'] # Include in order of preference among TeX, STIX-Web, Asana-Math, Neo-Euler, Gyre-Pagella, Gyre-Termes and Latin-Modern
+    mathjax_settings['equation_numbering'] = 'none' # AMS, auto, none
+
+    # Source for MathJax
+    mathjax_settings['source'] = "'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.3/latest.js?config=TeX-AMS-MML_HTMLorMML'"
+
+    # Get the user specified settings
+    try:
+        settings = pelicanobj.settings['MATH_JAX']
+    except:
+        settings = None
+
+    # If no settings have been specified, then return the defaults
+    if not isinstance(settings, dict):
+        return mathjax_settings
+
+    # The following mathjax settings can be set via the settings dictionary
+    for key, value in ((key, settings[key]) for key in settings):
+        # Iterate over dictionary in a way that is compatible with both version 2
+        # and 3 of python
+
+        if key == 'align':
+            typeVal = isinstance(value, string_type)
+
+            if not typeVal:
+                continue
+
+            if value == 'left' or value == 'right' or value == 'center':
+                mathjax_settings[key] = value
+            else:
+                mathjax_settings[key] = 'center'
+
+        if key == 'indent':
+            mathjax_settings[key] = value
+
+        if key == 'source':
+            mathjax_settings[key] = value
+
+        if key == 'show_menu' and isinstance(value, bool):
+            mathjax_settings[key] = 'true' if value else 'false'
+
+        if key == 'message_style':
+            mathjax_settings[key] = value if value is not None else 'none'
+
+        if key == 'auto_insert' and isinstance(value, bool):
+            mathjax_settings[key] = value
+
+        if key == 'process_escapes' and isinstance(value, bool):
+            mathjax_settings[key] = 'true' if value else 'false'
+
+        if key == 'latex_preview':
+            typeVal = isinstance(value, string_type)
+
+            if not typeVal:
+                continue
+
+            mathjax_settings[key] = value
+
+        if key == 'color':
+            typeVal = isinstance(value, string_type)
+
+            if not typeVal:
+                continue
+
+            mathjax_settings[key] = value
+
+        if key == 'linebreak_automatic' and isinstance(value, bool):
+            mathjax_settings[key] = 'true' if value else 'false'
+
+        if key == 'process_summary' and isinstance(value, bool):
+            if value and BeautifulSoup is None:
+                print("BeautifulSoup4 is needed for summaries to be processed by render_math\nPlease install it")
+                value = False
+
+            mathjax_settings[key] = value
+
+        if key == 'responsive' and isinstance(value, bool):
+            mathjax_settings[key] = 'true' if value else 'false'
+
+        if key == 'responsive_break' and isinstance(value, int):
+            mathjax_settings[key] = str(value)
+
+        if key == 'tex_extensions' and isinstance(value, list):
+            # filter string values, then add '' to them
+            value = filter(lambda string: isinstance(string, string_type), value)
+            value = map(lambda string: "'%s'" % string, value)
+            mathjax_settings[key] = ',' + ','.join(value)
+
+        if key == 'mathjax_font':
+            typeVal = isinstance(value, string_type)
+
+            if not typeVal:
+                continue
+
+            value = value.lower()
+
+            if value == 'sanserif':
+                value = 'SansSerif'
+            elif value == 'fraktur':
+                value = 'Fraktur'
+            elif value == 'typewriter':
+                value = 'Typewriter'
+            else:
+                value = 'default'
+
+            mathjax_settings[key] = value
+
+        if key == 'font_list' and isinstance(value, list):
+            # make an array string from the list
+            value = filter(lambda string: isinstance(string, string_type), value)
+            value = map(lambda string: ",'%s'" % string, value)
+            mathjax_settings[key] = ''.join(value)[1:]
+
+        if key == 'equation_numbering':
+            mathjax_settings[key] = value if value is not None else 'none'
+
+    return mathjax_settings
+
+def process_summary(article):
+    """Ensures summaries are not cut off. Also inserts
+    mathjax script so that math will be rendered"""
+
+    summary = article.summary
+    summary_parsed = BeautifulSoup(summary, 'html.parser')
+    math = summary_parsed.find_all(class_='math')
+
+    if len(math) > 0:
+        last_math_text = math[-1].get_text()
+        if len(last_math_text) > 3 and last_math_text[-3:] == '...':
+            content_parsed = BeautifulSoup(article._content, 'html.parser')
+            full_text = content_parsed.find_all(class_='math')[len(math)-1].get_text()
+            math[-1].string = "%s ..." % full_text
+            summary = summary_parsed.decode()
+
+        # clear memoization cache
+        import functools
+        if isinstance(article.get_summary, functools.partial):
+            memoize_instance = article.get_summary.func.__self__
+            memoize_instance.cache.clear()
+
+        article._summary = "%s<script type='text/javascript'>%s</script>" % (summary, process_summary.mathjax_script)
+
+def configure_typogrify(pelicanobj, mathjax_settings):
+    """Instructs Typogrify to ignore math tags - which allows Typogrify
+    to play nicely with math related content"""
+
+    # If Typogrify is not being used, then just exit
+    if not pelicanobj.settings.get('TYPOGRIFY', False):
+        return
+
+    try:
+        import typogrify
+        from distutils.version import LooseVersion
+
+        if LooseVersion(typogrify.__version__) < LooseVersion('2.0.7'):
+            raise TypeError('Incorrect version of Typogrify')
+
+        from typogrify.filters import typogrify
+
+        # At this point, we are happy to use Typogrify, meaning
+        # it is installed and it is a recent enough version
+        # that can be used to ignore all math
+        # Instantiate markdown extension and append it to the current extensions
+        pelicanobj.settings['TYPOGRIFY_IGNORE_TAGS'].extend(['.math', 'script'])  # ignore math class and script
+
+    except (ImportError, TypeError) as e:
+        pelicanobj.settings['TYPOGRIFY'] = False  # disable Typogrify
+
+        if isinstance(e, ImportError):
+            print("\nTypogrify is not installed, so it is being ignored.\nIf you want to use it, please install via: pip install typogrify\n")
+
+        if isinstance(e, TypeError):
+            print("\nA more recent version of Typogrify is needed for the render_math module.\nPlease upgrade Typogrify to the latest version (anything equal or above version 2.0.7 is okay).\nTypogrify will be turned off due to this reason.\n")
+
+def process_mathjax_script(mathjax_settings):
+    """Load the mathjax script template from file, and render with the settings"""
+
+    # Read the mathjax javascript template from file
+    with open (os.path.dirname(os.path.realpath(__file__))
+            + '/mathjax_script_template', 'r') as mathjax_script_template:
+        mathjax_template = mathjax_script_template.read()
+
+    return mathjax_template.format(**mathjax_settings)
+
+def mathjax_for_markdown(pelicanobj, mathjax_script, mathjax_settings):
+    """Instantiates a customized markdown extension for handling mathjax
+    related content"""
+
+    # Create the configuration for the markdown template
+    config = {}
+    config['mathjax_script'] = mathjax_script
+    config['math_tag_class'] = 'math'
+    config['auto_insert'] = mathjax_settings['auto_insert']
+
+    # Instantiate markdown extension and append it to the current extensions
+    try:
+        if isinstance(pelicanobj.settings.get('MD_EXTENSIONS'), list): # pelican 3.6.3 and earlier
+            pelicanobj.settings['MD_EXTENSIONS'].append(PelicanMathJaxExtension(config))
+        else:
+            pelicanobj.settings['MARKDOWN'].setdefault('extensions', []).append(PelicanMathJaxExtension(config))
+    except:
+        sys.excepthook(*sys.exc_info())
+        sys.stderr.write("\nError - the pelican mathjax markdown extension failed to configure. MathJax is non-functional.\n")
+        sys.stderr.flush()
+
+def mathjax_for_rst(pelicanobj, mathjax_script, mathjax_settings):
+    """Setup math for RST"""
+    docutils_settings = pelicanobj.settings.get('DOCUTILS_SETTINGS', {})
+    docutils_settings.setdefault('math_output', 'MathJax %s' % mathjax_settings['source'])
+    pelicanobj.settings['DOCUTILS_SETTINGS'] = docutils_settings
+    rst_add_mathjax.mathjax_script = mathjax_script
+
+def pelican_init(pelicanobj):
+    """
+    Loads the mathjax script according to the settings.
+    Instantiate the Python markdown extension, passing in the mathjax
+    script as config parameter.
+    """
+
+    # Process settings, and set global var
+    mathjax_settings = process_settings(pelicanobj)
+
+    # Generate mathjax script
+    mathjax_script = process_mathjax_script(mathjax_settings)
+
+    # Configure Typogrify
+    configure_typogrify(pelicanobj, mathjax_settings)
+
+    # Configure Mathjax For Markdown
+    if PelicanMathJaxExtension:
+        mathjax_for_markdown(pelicanobj, mathjax_script, mathjax_settings)
+
+    # Configure Mathjax For RST
+    mathjax_for_rst(pelicanobj, mathjax_script, mathjax_settings)
+
+    # Set process_summary's mathjax_script variable
+    process_summary.mathjax_script = None
+    if mathjax_settings['process_summary']:
+        process_summary.mathjax_script = mathjax_script
+
+def rst_add_mathjax(content):
+    """Adds mathjax script for reStructuredText"""
+
+    # .rst is the only valid extension for reStructuredText files
+    _, ext = os.path.splitext(os.path.basename(content.source_path))
+    if ext != '.rst':
+        return
+
+    # If math class is present in text, add the javascript
+    # note that RST hardwires mathjax to be class "math"
+    if 'class="math"' in content._content:
+        content._content += "<script type='text/javascript'>%s</script>" % rst_add_mathjax.mathjax_script
+
+def process_rst_and_summaries(content_generators):
+    """
+    Ensure mathjax script is applied to RST and summaries are
+    corrected if specified in user settings.
+
+    Handles content attached to ArticleGenerator and PageGenerator objects,
+    since the plugin doesn't know how to handle other Generator types.
+
+    For reStructuredText content, examine both articles and pages.
+    If article or page is reStructuredText and there is math present,
+    append the mathjax script.
+
+    Also process summaries if present (only applies to articles)
+    and user wants summaries processed (via user settings)
+    """
+
+    for generator in content_generators:
+        if isinstance(generator, generators.ArticlesGenerator):
+            for article in (
+                    generator.articles +
+                    generator.translations +
+                    generator.drafts):
+                rst_add_mathjax(article)
+                #optionally fix truncated formulae in summaries.
+                if process_summary.mathjax_script is not None:
+                    process_summary(article)
+        elif isinstance(generator, generators.PagesGenerator):
+            for page in generator.pages:
+                rst_add_mathjax(page)
+            for page in generator.hidden_pages:
+                rst_add_mathjax(page)
+
+def register():
+    """Plugin registration"""
+    signals.initialized.connect(pelican_init)
+    signals.all_generators_finalized.connect(process_rst_and_summaries)
diff --git a/pelican-plugins/render_math/mathjax_script_template b/pelican-plugins/render_math/mathjax_script_template
new file mode 100644
index 0000000000000000000000000000000000000000..db8aeba08a6a052272c172608bfcf813ed9aac5f
--- /dev/null
+++ b/pelican-plugins/render_math/mathjax_script_template
@@ -0,0 +1,61 @@
+if (!document.getElementById('mathjaxscript_pelican_#%@#$@#')) {{
+    var align = "{align}",
+        indent = "{indent}",
+        linebreak = "{linebreak_automatic}";
+
+    if ({responsive}) {{
+        align = (screen.width < {responsive_break}) ? "left" : align;
+        indent = (screen.width < {responsive_break}) ? "0em" : indent;
+        linebreak = (screen.width < {responsive_break}) ? 'true' : linebreak;
+    }}
+
+    var mathjaxscript = document.createElement('script');
+    mathjaxscript.id = 'mathjaxscript_pelican_#%@#$@#';
+    mathjaxscript.type = 'text/javascript';
+    mathjaxscript.src = {source};
+
+    var configscript = document.createElement('script');
+    configscript.type = 'text/x-mathjax-config';
+    configscript[(window.opera ? "innerHTML" : "text")] =
+        "MathJax.Hub.Config({{" +
+        "    config: ['MMLorHTML.js']," +
+        "    TeX: {{ extensions: ['AMSmath.js','AMSsymbols.js','noErrors.js','noUndefined.js'{tex_extensions}], equationNumbers: {{ autoNumber: '{equation_numbering}' }} }}," +
+        "    jax: ['input/TeX','input/MathML','output/HTML-CSS']," +
+        "    extensions: ['tex2jax.js','mml2jax.js','MathMenu.js','MathZoom.js']," +
+        "    displayAlign: '"+ align +"'," +
+        "    displayIndent: '"+ indent +"'," +
+        "    showMathMenu: {show_menu}," +
+        "    messageStyle: '{message_style}'," +
+        "    tex2jax: {{ " +
+        "        inlineMath: [ ['\\\\(','\\\\)'] ], " +
+        "        displayMath: [ ['$$','$$'] ]," +
+        "        processEscapes: {process_escapes}," +
+        "        preview: '{latex_preview}'," +
+        "    }}, " +
+        "    'HTML-CSS': {{ " +
+        "        availableFonts: {font_list}," +
+        "        preferredFont: 'STIX'," +
+        "        styles: {{ '.MathJax_Display, .MathJax .mo, .MathJax .mi, .MathJax .mn': {{color: '{color} ! important'}} }}," +
+        "        linebreaks: {{ automatic: "+ linebreak +", width: '90% container' }}," +
+        "    }}, " +
+        "}}); " +
+        "if ('{mathjax_font}' !== 'default') {{" +
+            "MathJax.Hub.Register.StartupHook('HTML-CSS Jax Ready',function () {{" +
+                "var VARIANT = MathJax.OutputJax['HTML-CSS'].FONTDATA.VARIANT;" +
+                "VARIANT['normal'].fonts.unshift('MathJax_{mathjax_font}');" +
+                "VARIANT['bold'].fonts.unshift('MathJax_{mathjax_font}-bold');" +
+                "VARIANT['italic'].fonts.unshift('MathJax_{mathjax_font}-italic');" +
+                "VARIANT['-tex-mathit'].fonts.unshift('MathJax_{mathjax_font}-italic');" +
+            "}});" +
+            "MathJax.Hub.Register.StartupHook('SVG Jax Ready',function () {{" +
+                "var VARIANT = MathJax.OutputJax.SVG.FONTDATA.VARIANT;" +
+                "VARIANT['normal'].fonts.unshift('MathJax_{mathjax_font}');" +
+                "VARIANT['bold'].fonts.unshift('MathJax_{mathjax_font}-bold');" +
+                "VARIANT['italic'].fonts.unshift('MathJax_{mathjax_font}-italic');" +
+                "VARIANT['-tex-mathit'].fonts.unshift('MathJax_{mathjax_font}-italic');" +
+            "}});" +
+        "}}";
+
+    (document.body || document.getElementsByTagName('head')[0]).appendChild(configscript);
+    (document.body || document.getElementsByTagName('head')[0]).appendChild(mathjaxscript);
+}}
diff --git a/pelican-plugins/render_math/pelican_mathjax_markdown_extension.py b/pelican-plugins/render_math/pelican_mathjax_markdown_extension.py
new file mode 100644
index 0000000000000000000000000000000000000000..e739363f8069a567ab6857176bfae8344cf926d0
--- /dev/null
+++ b/pelican-plugins/render_math/pelican_mathjax_markdown_extension.py
@@ -0,0 +1,158 @@
+# -*- coding: utf-8 -*-
+"""
+Pelican Mathjax Markdown Extension
+==================================
+An extension for the Python Markdown module that enables
+the Pelican python blog to process mathjax. This extension
+gives Pelican the ability to use Mathjax as a "first class
+citizen" of the blog
+"""
+
+import markdown
+
+from markdown.util import etree
+from markdown.util import AtomicString
+
+class PelicanMathJaxPattern(markdown.inlinepatterns.Pattern):
+    """Inline markdown processing that matches mathjax"""
+
+    def __init__(self, pelican_mathjax_extension, tag, pattern):
+        super(PelicanMathJaxPattern,self).__init__(pattern)
+        self.math_tag_class = pelican_mathjax_extension.getConfig('math_tag_class')
+        self.pelican_mathjax_extension = pelican_mathjax_extension
+        self.tag = tag
+
+    def handleMatch(self, m):
+        node = markdown.util.etree.Element(self.tag)
+        node.set('class', self.math_tag_class)
+
+        prefix = '\\(' if m.group('prefix') == '$' else m.group('prefix')
+        suffix = '\\)' if m.group('suffix') == '$' else m.group('suffix')
+        node.text = markdown.util.AtomicString(prefix + m.group('math') + suffix)
+
+        # If mathjax was successfully matched, then JavaScript needs to be added
+        # for rendering. The boolean below indicates this
+        self.pelican_mathjax_extension.mathjax_needed = True
+        return node
+
+class PelicanMathJaxCorrectDisplayMath(markdown.treeprocessors.Treeprocessor):
+    """Corrects invalid html that results from a <div> being put inside
+    a <p> for displayed math"""
+
+    def __init__(self, pelican_mathjax_extension):
+        self.pelican_mathjax_extension = pelican_mathjax_extension
+
+    def correct_html(self, root, children, div_math, insert_idx, text):
+        """Separates out <div class="math"> from the parent tag <p>. Anything
+        in between is put into its own parent tag of <p>"""
+
+        current_idx = 0
+
+        for idx in div_math:
+            el = markdown.util.etree.Element('p')
+            el.text = text
+            el.extend(children[current_idx:idx])
+
+            # Test to ensure that empty <p> is not inserted  
+            if len(el) != 0 or (el.text and not el.text.isspace()):
+               root.insert(insert_idx, el)
+               insert_idx += 1
+
+            text = children[idx].tail
+            children[idx].tail = None
+            root.insert(insert_idx, children[idx])
+            insert_idx += 1
+            current_idx = idx+1
+
+        el = markdown.util.etree.Element('p')
+        el.text = text
+        el.extend(children[current_idx:])
+
+        if len(el) != 0 or (el.text and not el.text.isspace()):
+            root.insert(insert_idx, el)
+
+    def run(self, root):
+        """Searches for <div class="math"> that are children in <p> tags and corrects
+        the invalid HTML that results"""
+
+        math_tag_class = self.pelican_mathjax_extension.getConfig('math_tag_class')
+
+        for parent in root:
+            div_math = []
+            children = list(parent)
+
+            for div in parent.findall('div'):
+                if div.get('class') == math_tag_class:
+                    div_math.append(children.index(div))
+
+            # Do not process further if no displayed math has been found
+            if not div_math:
+                continue
+
+            insert_idx = list(root).index(parent)
+            self.correct_html(root, children, div_math, insert_idx, parent.text) 
+            root.remove(parent)  # Parent must be removed last for correct insertion index
+
+        return root
+
+class PelicanMathJaxAddJavaScript(markdown.treeprocessors.Treeprocessor):
+    """Tree Processor for adding Mathjax JavaScript to the blog"""
+
+    def __init__(self, pelican_mathjax_extension):
+        self.pelican_mathjax_extension = pelican_mathjax_extension
+
+    def run(self, root):
+        # If no mathjax was present, then exit
+        if (not self.pelican_mathjax_extension.mathjax_needed):
+            return root
+
+        # Add the mathjax script to the html document
+        mathjax_script = etree.Element('script')
+        mathjax_script.set('type','text/javascript')
+        mathjax_script.text = AtomicString(self.pelican_mathjax_extension.getConfig('mathjax_script'))
+        root.append(mathjax_script)
+
+        # Reset the boolean switch to false so that script is only added
+        # to other pages if needed
+        self.pelican_mathjax_extension.mathjax_needed = False
+        return root
+
+class PelicanMathJaxExtension(markdown.Extension):
+    """A markdown extension enabling mathjax processing in Markdown for Pelican"""
+    def __init__(self, config):
+
+        try:
+            # Needed for markdown versions >= 2.5
+            self.config['mathjax_script'] = ['', 'Mathjax JavaScript script']
+            self.config['math_tag_class'] = ['math', 'The class of the tag in which mathematics is wrapped']
+            self.config['auto_insert'] = [True, 'Determines if mathjax script is automatically inserted into content']
+            super(PelicanMathJaxExtension,self).__init__(**config)
+        except AttributeError:
+            # Markdown versions < 2.5
+            config['mathjax_script'] = [config['mathjax_script'], 'Mathjax JavaScript script']
+            config['math_tag_class'] = [config['math_tag_class'], 'The class of the tag in which mathematic is wrapped']
+            config['auto_insert'] = [config['auto_insert'], 'Determines if mathjax script is automatically inserted into content']
+            super(PelicanMathJaxExtension,self).__init__(config)
+
+        # Used as a flag to determine if javascript
+        # needs to be injected into a document
+        self.mathjax_needed = False
+
+    def extendMarkdown(self, md):
+        # Regex to detect mathjax
+        mathjax_inline_regex = r'(?P<prefix>\$)(?P<math>.+?)(?P<suffix>(?<!\s)\2)'
+        mathjax_display_regex = r'(?P<prefix>\$\$|\\begin\{(.+?)\})(?P<math>.+?)(?P<suffix>\2|\\end\{\3\})'
+
+        # Process mathjax before escapes are processed since escape processing will
+        # intefer with mathjax. The order in which the displayed and inlined math
+        # is registered below matters: we should have higher priority than 'escape' which has 180
+        md.inlinePatterns.register(PelicanMathJaxPattern(self, 'div', mathjax_display_regex), 'mathjax_displayed', 186)
+        md.inlinePatterns.register(PelicanMathJaxPattern(self, 'span', mathjax_inline_regex), 'mathjax_inlined', 185)
+
+        # Correct the invalid HTML that results from teh displayed math (<div> tag within a <p> tag) 
+        md.treeprocessors.register(PelicanMathJaxCorrectDisplayMath(self), 'mathjax_correctdisplayedmath', 15)
+
+        # If necessary, add the JavaScript Mathjax library to the document. This must
+        # be last in the ordered dict (hence it is given the position '_end')
+        if self.getConfig('auto_insert'):
+            md.treeprocessors.register(PelicanMathJaxAddJavaScript(self), 'mathjax_addjavascript', 0)
diff --git a/pelican-plugins/render_math/requirements.txt b/pelican-plugins/render_math/requirements.txt
new file mode 100644
index 0000000000000000000000000000000000000000..be64ec96251a46960e5d2ffdd93c28abceaf5d4c
--- /dev/null
+++ b/pelican-plugins/render_math/requirements.txt
@@ -0,0 +1 @@
+typogrify
diff --git a/pelican-plugins/render_math/test_data/article.ipynb b/pelican-plugins/render_math/test_data/article.ipynb
new file mode 100755
index 0000000000000000000000000000000000000000..890f8be5150fe206e1afd691e1d55b020e12e882
--- /dev/null
+++ b/pelican-plugins/render_math/test_data/article.ipynb
@@ -0,0 +1,42 @@
+{
+  "cells": [
+    {
+      "cell_type": "markdown",
+      "source": [
+        "The formula is:\n",
+        "\n",
+        "\\begin{align*}A =\n",
+        "LL^{T}\n",
+        "\\end{align*}\n"
+      ],
+      "metadata": {}
+    }
+  ],
+  "metadata": {
+    "kernelspec": {
+      "name": "python3",
+      "language": "python",
+      "display_name": "Python 3"
+    },
+    "language_info": {
+      "name": "python",
+      "version": "3.7.3",
+      "mimetype": "text/x-python",
+      "codemirror_mode": {
+        "name": "ipython",
+        "version": 3
+      },
+      "pygments_lexer": "ipython3",
+      "nbconvert_exporter": "python",
+      "file_extension": ".py"
+    },
+    "kernel_info": {
+      "name": "python3"
+    },
+    "nteract": {
+      "version": "0.14.5"
+    }
+  },
+  "nbformat": 4,
+  "nbformat_minor": 2
+}
\ No newline at end of file
diff --git a/pelican-plugins/render_math/test_data/article.nbdata b/pelican-plugins/render_math/test_data/article.nbdata
new file mode 100755
index 0000000000000000000000000000000000000000..bf1353804ac37e3f55531d69e205b2b6c78c0d9d
--- /dev/null
+++ b/pelican-plugins/render_math/test_data/article.nbdata
@@ -0,0 +1,5 @@
+Title: An article from a Jupyter notebook
+Date: 2019-03-05 12:14
+Category: Mathematics
+Tags: Linear Algebra, Python, Numpy, Scipy
+Summary: This is a advance part of Linear Algebra with Python
\ No newline at end of file
diff --git a/pelican-plugins/render_math/test_data/article_with_math_formulas.rst b/pelican-plugins/render_math/test_data/article_with_math_formulas.rst
new file mode 100644
index 0000000000000000000000000000000000000000..87dcc450e546ff3fdd0aa2dfd55a359fb7de3551
--- /dev/null
+++ b/pelican-plugins/render_math/test_data/article_with_math_formulas.rst
@@ -0,0 +1,20 @@
+Math formulas
+#############
+
+:date: 2019-09-10
+:yeah: oh yeah !
+:summary: :math:`A_\text{c} = (\pi/4) d^2`
+
+The area of a circle is :math:`A_\text{c} = (\pi/4) d^2`.
+
+.. math::
+
+  α_t(i) = P(O_1, O_2, … O_t, q_t = S_i λ)
+
+    A =
+    \begin{bmatrix}
+    a_{11} & a_{12} & a_{13} \
+    a_{21} & a_{22} & a_{23} \
+    a_{31} & a_{32} & a_{33}
+    \end{bmatrix}
+    
\ No newline at end of file
diff --git a/pelican-plugins/render_math/test_render_math.py b/pelican-plugins/render_math/test_render_math.py
new file mode 100644
index 0000000000000000000000000000000000000000..b71f4e718d120053be634d31765b44eceb98d51a
--- /dev/null
+++ b/pelican-plugins/render_math/test_render_math.py
@@ -0,0 +1,56 @@
+# -*- coding: utf-8 -*-
+from os.path import dirname, join
+from tempfile import TemporaryDirectory
+
+from pelican import Pelican
+from pelican.generators import ArticlesGenerator
+from pelican.settings import configure_settings
+from pelican.tests.support import get_settings, unittest
+from pelican.writers import Writer
+
+from .math import pelican_init, process_rst_and_summaries
+
+
+CUR_DIR = dirname(__file__)
+
+
+class RenderMathTest(unittest.TestCase):
+    def test_ok_on_shared_test_data(self):
+        settings = get_settings(filenames={})
+        settings['PATH'] = join(CUR_DIR, '..', 'test_data')
+        pelican_init(PelicanMock(settings))
+        with TemporaryDirectory() as tmpdirname:
+            generator = _build_article_generator(settings, tmpdirname)
+            process_rst_and_summaries([generator])
+    def test_ok_on_custom_data(self):
+        settings = get_settings(filenames={})
+        settings['PATH'] = join(CUR_DIR, 'test_data')
+        settings['PLUGINS'] = ['pelican-ipynb.markup']  # to also parse .ipynb files
+        configure_settings(settings)
+        pelican_mock = PelicanMock(settings)
+        pelican_init(pelican_mock)
+        Pelican.init_plugins(pelican_mock)
+        with TemporaryDirectory() as tmpdirname:
+            generator = _build_article_generator(settings, tmpdirname)
+            process_rst_and_summaries([generator])
+            for article in generator.articles:
+                if article.source_path.endswith('.rst'):
+                    self.assertIn('mathjaxscript_pelican', article.content)
+            generator.generate_output(Writer(tmpdirname, settings=settings))
+
+
+def _build_article_generator(settings, output_path):
+    context = settings.copy()
+    context['generated_content'] = dict()
+    context['static_links'] = set()
+    article_generator = ArticlesGenerator(
+        context=context, settings=settings,
+        path=settings['PATH'], theme=settings['THEME'], output_path=output_path)
+    article_generator.generate_context()
+    return article_generator
+
+class PelicanMock:
+    'A dummy class exposing the only attributes needed'
+    def __init__(self, settings):
+        self.plugins = []
+        self.settings = settings
diff --git a/pelican-plugins/representative_image/Readme.md b/pelican-plugins/representative_image/Readme.md
new file mode 100644
index 0000000000000000000000000000000000000000..a6abbb4f00eaf9422a09363813f38172c423eeb3
--- /dev/null
+++ b/pelican-plugins/representative_image/Readme.md
@@ -0,0 +1,61 @@
+# Summary
+
+This plugin extracts a representative image (i.e, featured image) from the summary or content of an article or a page, if not specified in the metadata.
+
+The plugin also removes any images from the summary after extraction to avoid duplication.
+
+It allows the flexibility on where and how to display the featured image of an article together with its summary in a template page. For example, the article metadata can be displayed in thumbnail format, in which there is a short summary and an image. The layout of the summary and the image can be varied for aesthetical purpose. It doesn't have to depend on article's content format.
+
+## Installation
+
+This plugin requires `BeautifulSoup`:
+
+```bash
+pip install beautifulsoup4
+```
+
+To enable, add the following to your `settings.py`:
+
+```python
+PLUGIN_PATH = 'path/to/pelican-plugins'
+PLUGINS = ["representative_image"]
+```
+
+`PLUGIN_PATH` can be a path relative to your settings file or an absolute path.
+
+## Usage
+
+To override the default behavior of selecting the first image in the article's summary or content, set the image property the article's metadata to the URL of the image to display, e.g:
+
+```markdown
+Title: My super title
+Date: 2010-12-03 10:20
+Category: Python
+Tags: pelican, publishing
+Slug: my-super-post
+Author: Alexis Metaireau
+Summary: Short version for index and feeds
+Image: /images/my-super-image.png
+
+Article content...
+```
+
+### Page
+
+To include a representative image in a page add the following to the template:
+
+```html
+{% if page.featured_image %}
+    <img src="{{ page.featured_image }}">
+{% endif %}
+```
+
+### Article
+
+To include a representative image in an article add the following to the template:
+
+```html
+{% if article.featured_image %}
+    <img src="{{ article.featured_image }}">
+{% endif %}
+```
diff --git a/pelican-plugins/representative_image/__init__.py b/pelican-plugins/representative_image/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..88c52b4522a20a087ff9ba4010777717752f5d3f
--- /dev/null
+++ b/pelican-plugins/representative_image/__init__.py
@@ -0,0 +1 @@
+from .representative_image import *  # noqa
diff --git a/pelican-plugins/representative_image/representative_image.py b/pelican-plugins/representative_image/representative_image.py
new file mode 100644
index 0000000000000000000000000000000000000000..2cec1a2c9d9b2ea8ead7cba9ee81a80cebce8262
--- /dev/null
+++ b/pelican-plugins/representative_image/representative_image.py
@@ -0,0 +1,63 @@
+import six
+from bs4 import BeautifulSoup
+
+from pelican import signals
+from pelican.contents import Article, Page
+from pelican.generators import ArticlesGenerator, PagesGenerator
+
+
+def images_extraction(instance):
+    representativeImage = None
+    if type(instance) in (Article, Page):
+        if 'image' in instance.metadata:
+            representativeImage = instance.metadata['image']
+
+        # Process Summary:
+        # If summary contains images, extract one to be the representativeImage
+        # and remove images from summary
+        soup = BeautifulSoup(instance.summary, 'html.parser')
+        images = soup.find_all('img')
+        for i in images:
+            if not representativeImage:
+                representativeImage = i['src']
+            i.extract()
+        if len(images) > 0:
+            # set _summary field which is based on metadata. summary field is
+            # only based on article's content and not settable
+            instance._summary = six.text_type(soup)
+
+        # If there are no image in summary, look for it in the content body
+        if not representativeImage:
+            soup = BeautifulSoup(instance._content, 'html.parser')
+            imageTag = soup.find('img')
+            if imageTag:
+                representativeImage = imageTag['src']
+
+        # Set the attribute to content instance
+        instance.featured_image = representativeImage
+        instance.featured_alt = instance.metadata.get('alt', None)
+        instance.featured_link = instance.metadata.get('link', None)
+        instance.featured_caption = instance.metadata.get('caption', None)
+
+
+def run_plugin(generators):
+    for generator in generators:
+        if isinstance(generator, ArticlesGenerator):
+            for article in generator.articles:
+                images_extraction(article)
+                for translation in article.translations:
+                    images_extraction(translation)
+        elif isinstance(generator, PagesGenerator):
+            for page in generator.pages:
+                images_extraction(page)
+                for translation in page.translations:
+                    images_extraction(translation)
+
+
+def register():
+    try:
+        signals.all_generators_finalized.connect(run_plugin)
+    except AttributeError:
+        # NOTE: This results in #314 so shouldn't really be relied on
+        # https://github.com/getpelican/pelican-plugins/issues/314
+        signals.content_object_init.connect(images_extraction)
diff --git a/pelican-plugins/representative_image/requirements.txt b/pelican-plugins/representative_image/requirements.txt
new file mode 100644
index 0000000000000000000000000000000000000000..c1f5f713cdafc4d0a904e0c158ccb14a0da6a445
--- /dev/null
+++ b/pelican-plugins/representative_image/requirements.txt
@@ -0,0 +1 @@
+beautifulsoup4
diff --git a/pelican-plugins/representative_image/test_representative_image.py b/pelican-plugins/representative_image/test_representative_image.py
new file mode 100755
index 0000000000000000000000000000000000000000..949c35e320da45e10b3b4fcf7a0a0ae1b29c5cef
--- /dev/null
+++ b/pelican-plugins/representative_image/test_representative_image.py
@@ -0,0 +1,73 @@
+#!/bin/sh
+import unittest
+
+import representative_image
+from jinja2.utils import generate_lorem_ipsum
+from pelican.contents import Article, Page
+
+# Generate content with image
+TEST_CONTENT_IMAGE_URL = 'https://testimage.com/test.jpg'
+TEST_CONTENT = str(generate_lorem_ipsum(n=3, html=True)) + '<img src="' + TEST_CONTENT_IMAGE_URL + '"/>'+ str(generate_lorem_ipsum(n=2,html=True))  # noqa
+TEST_SUMMARY_IMAGE_URL = 'https://testimage.com/summary.jpg'
+TEST_SUMMARY_WITHOUTIMAGE = str(generate_lorem_ipsum(n=1, html=True))
+TEST_SUMMARY_WITHIMAGE = TEST_SUMMARY_WITHOUTIMAGE + '<img src="' + TEST_SUMMARY_IMAGE_URL + '"/>'  # noqa
+TEST_CUSTOM_IMAGE_URL = 'https://testimage.com/custom.jpg'
+
+
+class TestRepresentativeImage(unittest.TestCase):
+
+    def setUp(self):
+        super(TestRepresentativeImage, self).setUp()
+        representative_image.register()
+
+    def test_extract_image_from_content(self):
+        args = {
+            'content': TEST_CONTENT,
+            'metadata': {
+                'summary': TEST_SUMMARY_WITHOUTIMAGE,
+            },
+        }
+
+        article = Article(**args)
+        self.assertEqual(article.featured_image, TEST_CONTENT_IMAGE_URL)
+
+    def test_extract_image_from_summary(self):
+        args = {
+            'content': TEST_CONTENT,
+            'metadata': {
+                'summary': TEST_SUMMARY_WITHIMAGE,
+            },
+        }
+
+        article = Article(**args)
+        self.assertEqual(article.featured_image, TEST_SUMMARY_IMAGE_URL)
+        self.assertEqual(article.summary, TEST_SUMMARY_WITHOUTIMAGE)
+
+    def test_extract_image_from_summary_with_custom_image(self):
+        args = {
+            'content': TEST_CONTENT,
+            'metadata': {
+                'summary': TEST_SUMMARY_WITHIMAGE,
+                'image': TEST_CUSTOM_IMAGE_URL,
+            },
+        }
+
+        article = Article(**args)
+        self.assertEqual(article.featured_image, TEST_CUSTOM_IMAGE_URL)
+        self.assertEqual(article.summary, TEST_SUMMARY_WITHOUTIMAGE)
+
+    def test_extract_image_from_page_summary_with_custom_image(self):
+        args = {
+            'content': TEST_CONTENT,
+            'metadata': {
+                'summary': TEST_SUMMARY_WITHIMAGE,
+                'image': TEST_CUSTOM_IMAGE_URL,
+            },
+        }
+        page = Page(**args)
+        self.assertEqual(page.featured_image, TEST_CUSTOM_IMAGE_URL)
+        self.assertEqual(page.summary, TEST_SUMMARY_WITHOUTIMAGE)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/pelican-plugins/requirements.txt b/pelican-plugins/requirements.txt
new file mode 100644
index 0000000000000000000000000000000000000000..85fdd2ab0e5e3acc6f2aea69795b95b3aa0b2b4e
--- /dev/null
+++ b/pelican-plugins/requirements.txt
@@ -0,0 +1,4 @@
+Markdown
+nose
+nose-exclude
+nose-summary-report
diff --git a/pelican-plugins/rmd_reader/Readme.md b/pelican-plugins/rmd_reader/Readme.md
new file mode 100644
index 0000000000000000000000000000000000000000..dd3d13693294ba07c72c8a94f7e504a4ded66fa2
--- /dev/null
+++ b/pelican-plugins/rmd_reader/Readme.md
@@ -0,0 +1,68 @@
+# RMD Reader
+
+This plugin helps you creating posts with knitr's RMarkdown files.
+[knitr](http://yihui.name/knitr/) is a template engine which executes and displays embedded R code.
+So, being short you can write an executable paper with codes, formulas and graphics.
+
+## Loading
+
+It is a good idea to load `rmd_reader` at last.
+It uses settings loaded at runtime and it is important to be sure that all settings have been loaded.
+
+```
+PLUGINS = ['sitemap',
+           'summary',
+           ...
+           'render_math',
+           'rmd_reader']  # put it here!
+```
+
+## Dependency
+
+This plugin requires [rpy2](https://pypi.python.org/pypi/rpy2).
+Install it with:
+
+```
+pip install rpy2
+```
+
+Of course, R must be installed and also the knitr package.
+Execute the command below in R to get knitr installed.
+
+```
+R> install.packages('knitr')
+```
+
+## Usage
+
+The plugin detects RMD files ending with `.Rmd` or `.rmd` so you only have to write a RMarkdown files inside `content` directory.
+
+This plugin calls R to process these files and generates markdown files that are processed by Pelican's `MarkdownReader` in order to generate html files from ordinary `.md` files.
+
+### Configuration
+
+`rmd_reader` has these variables that can be set in `pelicanconf`.
+
+- `RMD_READER_CLEANUP` (`True`): The RMarkdown file is converted into a Markdown file with the extension `.aux` (to avoid conflicts while pelican is processing). This file is processed by pelican's MarkdownReader and is removed after that (the cleanup step). So if you want to check this file set `RMD_READER_CLEANUP=True`.
+- `RMD_READER_RENAME_PLOT` (`chunklabel`): the figures generated for plots are named with a default prefix (usually `unnamed-chunk`) followed by a sequential number. That sequence starts on 1 for every processed file, which causes naming conflicts among files. In order to avoid these conflicts `RMD_READER_RENAME_PLOT` can be set `chunklabel` and that prefix is replaced with the name of the markdown source, without extension. Alternatively, `RMD_READER_RENAME_PLOT` can be set `directory` in which case the `fig.path` (defaults to `figure/`) is augmented with the path to markdown source including the name of the source file without extension.  Another way to avoid conflicts is naming the chuncks and in that case this variable can be set to any other string.
+- `RMD_READER_KNITR_QUIET` (`True`): sets `knitr`'s quiet argument.
+- `RMD_READER_KNITR_ENCODING` (`UTF-8`): sets `knitr`'s encoding argument.
+- `RMD_READER_KNITR_OPTS_CHUNK` (`None`): sets `knitr`'s `opts_chunk`.
+- `RMD_READER_KNITR_OPTS_KNIT` (`None`): sets `knitr`'s `opts_knit`.
+
+
+### Plotting
+
+I strongly suggest using the variable `RMD_READER_RENAME_PLOT='chunklabel'`.
+That helps with avoiding naming conflits among different posts.
+`rmd_reader` sets knitr's `unnamed.chunk.label` option to the Rmd file name (without extension) in runtime.
+
+Alternatively, Rebecca Weiss (@rjweiss) suggested using `opts_chunk` to set knitr's `fig.path` ([link](http://rjweiss.github.io/articles/2014_08_25/testing-rmarkdown-integration/)).
+Now that can be done directly in `pelicanconf` thougth `RMD_READER_KNITR_OPTS_CHUNK`, that variable receives a `dict` with options to be passed to knitr's `opts_chunk`. With this scheme you can set `RMD_READER_RENAME_PLOT='directory'` and add the generated figures directory to `STATIC_PATHS`.
+
+```
+RMD_READER_RENAME_PLOT = 'directory'
+RMD_READER_KNITR_OPTS_CHUNK = {'fig.path': 'figure/'}
+STATIC_PATHS = ['figure']
+```
+
diff --git a/pelican-plugins/rmd_reader/__init__.py b/pelican-plugins/rmd_reader/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..ca633b6cda047ecfd97fb5f5d23ab6131b019b8e
--- /dev/null
+++ b/pelican-plugins/rmd_reader/__init__.py
@@ -0,0 +1 @@
+from .rmd_reader import *
diff --git a/pelican-plugins/rmd_reader/requirements.txt b/pelican-plugins/rmd_reader/requirements.txt
new file mode 100644
index 0000000000000000000000000000000000000000..8f3898626880b2f79a21a2ed9aa32ca1e3327850
--- /dev/null
+++ b/pelican-plugins/rmd_reader/requirements.txt
@@ -0,0 +1 @@
+rpy2
diff --git a/pelican-plugins/rmd_reader/rmd_reader.py b/pelican-plugins/rmd_reader/rmd_reader.py
new file mode 100644
index 0000000000000000000000000000000000000000..5c2b4fe7352d1d56bbc2252285fb512254bd92fe
--- /dev/null
+++ b/pelican-plugins/rmd_reader/rmd_reader.py
@@ -0,0 +1,126 @@
+#-*- conding: utf-8 -*-
+
+import os
+import warnings
+import logging
+
+logger = logging.getLogger('RMD_READER')
+
+from pelican import readers
+from pelican import signals
+from pelican import settings
+
+KNITR = None
+RMD = False
+FIG_PATH = None
+R_STARTED = False
+
+def startr():
+    global KNITR, R_OBJECTS, R_STARTED
+    if R_STARTED:
+        return
+    logger.debug('STARTING R')
+    with warnings.catch_warnings():
+        warnings.simplefilter("ignore")
+        try:
+            import rpy2.rinterface
+            rpy2.rinterface.set_initoptions((b'rpy2', b'--no-save', b'--vanilla', b'--quiet'))
+        except AttributeError:
+            from rpy2.rinterface_lib import embedded
+            embedded.set_initoptions(("rpy2", "--no-save", "--vanilla", "--quiet"))
+        import rpy2.robjects as R_OBJECTS
+        from rpy2.robjects.packages import importr
+    KNITR = importr('knitr')
+    logger.debug('R STARTED')
+    R_STARTED = True
+
+def initsignal(pelicanobj):
+    global RMD, FIG_PATH
+    try:
+        startr()
+        
+        idx = KNITR.opts_knit.names.index('set')
+        path = pelicanobj.settings.get('PATH','%s/content' % settings.DEFAULT_CONFIG.get('PATH'))
+        logger.debug("RMD_READER PATH = %s", path)
+        KNITR.opts_knit[idx](**{'base.dir': path})
+        
+        knitroptsknit = pelicanobj.settings.get('RMD_READER_KNITR_OPTS_KNIT', None)
+        if knitroptsknit:
+            KNITR.opts_knit[idx](**{str(k): v for k,v in knitroptsknit.items()})
+        
+        idx = KNITR.opts_chunk.names.index('set')
+        knitroptschunk = pelicanobj.settings.get('RMD_READER_KNITR_OPTS_CHUNK', None)
+        if knitroptschunk:
+            FIG_PATH = knitroptschunk['fig.path'] if 'fig.path' in knitroptschunk else 'figure/'
+            KNITR.opts_chunk[idx](**{str(k): v for k,v in knitroptschunk.items()})
+        
+        RMD = True
+    except ImportError as ex:
+        RMD = False
+
+class RmdReader(readers.BaseReader):
+    file_extensions = ['Rmd', 'rmd']
+
+    @property
+    def enabled(self):
+        return RMD
+
+    # You need to have a read method, which takes a filename and returns
+    # some content and the associated metadata.
+    def read(self, filename):
+        """Parse content and metadata of markdown files"""
+        QUIET = self.settings.get('RMD_READER_KNITR_QUIET', True)
+        ENCODING = self.settings.get('RMD_READER_KNITR_ENCODING', 'UTF-8')
+        CLEANUP = self.settings.get('RMD_READER_CLEANUP', True)
+        RENAME_PLOT = self.settings.get('RMD_READER_RENAME_PLOT', 'chunklabel')
+        if type(RENAME_PLOT) is bool:
+            logger.error("RMD_READER_RENAME_PLOT takes a string value (either chunklabel or directory), please see the readme.")
+            if RENAME_PLOT:
+                RENAME_PLOT = 'chunklabel'
+                logger.error("Defaulting to chunklabel")
+            else:
+                RENAME_PLOT = 'disabled'
+                logger.error("Disabling plot renaming")
+        logger.debug("RMD_READER_KNITR_QUIET = %s", QUIET)
+        logger.debug("RMD_READER_KNITR_ENCODING = %s", ENCODING)
+        logger.debug("RMD_READER_CLEANUP = %s", CLEANUP)
+        logger.debug("RMD_READER_RENAME_PLOT = %s", RENAME_PLOT)
+        # replace single backslashes with double backslashes
+        filename = filename.replace('\\', '\\\\')
+        # parse Rmd file - generate md file
+        md_filename = filename.replace('.Rmd', '.aux').replace('.rmd', '.aux')
+        if RENAME_PLOT == 'chunklabel' or RENAME_PLOT == 'directory':
+            if RENAME_PLOT == 'chunklabel':
+                chunk_label = os.path.splitext(os.path.basename(filename))[0]
+                logger.debug('Chunk label: %s', chunk_label)
+            elif RENAME_PLOT == 'directory':
+                chunk_label = 'unnamed-chunk'
+                PATH = self.settings.get('PATH','%s/content' % settings.DEFAULT_CONFIG.get('PATH'))
+                src_name = os.path.splitext(os.path.relpath(filename, PATH))[0]
+                idx = KNITR.opts_chunk.names.index('set')
+                knitroptschunk = { 'fig.path': '%s-' % os.path.join(FIG_PATH, src_name) }
+                KNITR.opts_chunk[idx](**{str(k): v for k,v in knitroptschunk.items()})
+                logger.debug('Figures path: %s, chunk label: %s', knitroptschunk['fig.path'], chunk_label)
+            R_OBJECTS.r('''
+opts_knit$set(unnamed.chunk.label="{unnamed_chunk_label}")
+render_markdown()
+hook_plot <- knit_hooks$get('plot')
+knit_hooks$set(plot=function(x, options) hook_plot(paste0("{{static}}/", x), options))
+            '''.format(unnamed_chunk_label=chunk_label))
+        with warnings.catch_warnings():
+            warnings.simplefilter("ignore")
+            KNITR.knit(filename, md_filename, quiet=QUIET, encoding=ENCODING)
+        # read md file - create a MarkdownReader
+        md_reader = readers.MarkdownReader(self.settings)
+        content, metadata = md_reader.read(md_filename)
+        # remove md file
+        if CLEANUP:
+            os.remove(md_filename)
+        return content, metadata
+
+def add_reader(readers):
+    readers.reader_classes['rmd'] = RmdReader
+
+def register():
+    signals.readers_init.connect(add_reader)
+    signals.initialized.connect(initsignal)
diff --git a/pelican-plugins/rmd_reader/test_rmd_reader.py b/pelican-plugins/rmd_reader/test_rmd_reader.py
new file mode 100644
index 0000000000000000000000000000000000000000..608ade43bc719e785dc5ffefa8ea16892106b3fe
--- /dev/null
+++ b/pelican-plugins/rmd_reader/test_rmd_reader.py
@@ -0,0 +1,162 @@
+'''
+Created on Jan 25, 2016
+
+@author: Aaron Kitzmiller <aaron_kitzmiller@harvard.edu?
+'''
+import unittest, os, sys
+import shutil
+import logging
+import glob
+from pelican import Pelican
+from pelican.settings import read_settings
+
+logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)
+
+class Test(unittest.TestCase):
+
+
+    def setUp(self):
+        try:
+            import rpy2
+            import rmd_reader
+        except Exception:
+            raise unittest.SkipTest("rpy not installed.  Will not test rmd_reader.")
+        
+        self.testtitle = 'rtest'
+        self.cwd = os.path.dirname(os.path.abspath(__file__))
+        logging.debug(self.cwd)
+        
+        # Setup content dir and test rmd file
+        self.contentdir = os.path.join(self.cwd,'test-content')
+        logging.debug(self.contentdir)
+        try:
+            os.mkdir(self.contentdir)
+        except Exception:
+            pass
+        self.contentfile = os.path.join(self.contentdir,'test.rmd')
+        logging.debug(self.contentfile)
+        
+        self.testrmd = '''Title: %s
+Date: 2014-06-23
+
+Let's make a simple plot about cars.
+```{r}
+cars <- c(1, 3, 6, 4, 9)
+plot(cars)
+```
+''' % self.testtitle
+        with open(self.contentfile,'w') as f:
+            f.write(self.testrmd)
+            
+        # Setup output dir
+        self.outputdir = os.path.join(self.cwd,'test-output')
+        logging.debug(self.outputdir)
+        
+        try:
+            os.mkdir(self.outputdir)
+        except Exception:
+            pass
+        
+        self.figpath = 'images'
+        
+
+
+    def tearDown(self):
+        logging.debug('CLEAN')
+        if os.path.isdir(self.outputdir):
+            shutil.rmtree(self.outputdir)
+        if os.path.isdir(self.contentdir):
+            shutil.rmtree(self.contentdir)
+
+    def testKnitrSettings(self):
+        settings = read_settings(path=None, override={
+            'LOAD_CONTENT_CACHE': False,
+            'PATH': self.contentdir,
+            'OUTPUT_PATH': self.outputdir,
+            'RMD_READER_KNITR_OPTS_CHUNK': {'fig.path' : '%s/' % self.figpath},
+            'RMD_READER_KNITR_OPTS_KNIT': {'progress' : True, 'verbose': True},
+            'RMD_READER_RENAME_PLOT': 'disable',
+            'PLUGIN_PATHS': ['../'],
+            'PLUGINS': ['rmd_reader'],
+            'AUTHOR': 'Bob Anonymous',
+        })
+        pelican = Pelican(settings=settings)
+        pelican.run()
+        
+        outputfilename = os.path.join(self.outputdir,'%s.html' % self.testtitle)
+        self.assertTrue(os.path.exists(outputfilename),'File %s was not created.' % outputfilename)
+        
+        imagesdir = os.path.join(self.outputdir, self.figpath)
+        self.assertTrue(os.path.exists(imagesdir), 'figpath not created.')
+        
+        imagefile = os.path.join(imagesdir, 'unnamed-chunk') + '-1-1.png'
+        logging.debug(imagefile)
+        images = glob.glob('%s/*' % imagesdir)
+        logging.debug(images)
+        self.assertTrue(os.path.exists(imagefile), 'image correctly named.')
+        
+        self.assertTrue(len(images) == 1,'Contents of images dir is not correct: %s' % ','.join(images))
+        
+
+    def testKnitrSettings2(self):
+        settings = read_settings(path=None, override={
+            'LOAD_CONTENT_CACHE': False,
+            'PATH': self.contentdir,
+            'OUTPUT_PATH': self.outputdir,
+            'RMD_READER_KNITR_OPTS_CHUNK': {'fig.path' : '%s/' % self.figpath},
+            'RMD_READER_KNITR_OPTS_KNIT': {'progress' : True, 'verbose': True},
+            'RMD_READER_RENAME_PLOT': 'chunklabel',
+            'PLUGIN_PATHS': ['../'],
+            'PLUGINS': ['rmd_reader'],
+            'AUTHOR': 'Bob Anonymous',
+        })
+        pelican = Pelican(settings=settings)
+        pelican.run()
+        
+        outputfilename = os.path.join(self.outputdir,'%s.html' % self.testtitle)
+        self.assertTrue(os.path.exists(outputfilename),'File %s was not created.' % outputfilename)
+        
+        imagesdir = os.path.join(self.outputdir, self.figpath)
+        self.assertTrue(os.path.exists(imagesdir), 'figpath not created.')
+        
+        imagefile = os.path.join(imagesdir, os.path.splitext(os.path.split(self.contentfile)[1])[0]) + '-1-1.png'
+        logging.debug(imagefile)
+        self.assertTrue(os.path.exists(imagefile), 'image correctly named.')
+        
+        images = glob.glob('%s/*' % imagesdir)
+        logging.debug(images)
+        self.assertTrue(len(images) == 1,'Contents of images dir is not correct: %s' % ','.join(images))
+
+    def testKnitrSettings3(self):
+        settings = read_settings(path=None, override={
+            'LOAD_CONTENT_CACHE': False,
+            'PATH': self.contentdir,
+            'OUTPUT_PATH': self.outputdir,
+            'RMD_READER_KNITR_OPTS_CHUNK': {'fig.path' : '%s/' % self.figpath},
+            'RMD_READER_KNITR_OPTS_KNIT': {'progress' : True, 'verbose': True},
+            'RMD_READER_RENAME_PLOT': 'directory',
+            'PLUGIN_PATHS': ['../'],
+            'PLUGINS': ['rmd_reader'],
+            'AUTHOR': 'Bob Anonymous',
+        })
+        pelican = Pelican(settings=settings)
+        pelican.run()
+
+        outputfilename = os.path.join(self.outputdir,'%s.html' % self.testtitle)
+        self.assertTrue(os.path.exists(outputfilename),'File %s was not created.' % outputfilename)
+
+        imagesdir = os.path.join(self.outputdir, self.figpath)
+        self.assertTrue(os.path.exists(imagesdir), 'figpath not created.')
+
+        imagefile = os.path.join(imagesdir, os.path.splitext(os.path.split(self.contentfile)[1])[0]) + '-unnamed-chunk-1-1.png'
+        logging.debug(imagefile)
+        self.assertTrue(os.path.exists(imagefile), 'image correctly named.')
+
+        images = glob.glob('%s/*' % imagesdir)
+        logging.debug(images)
+        self.assertTrue(len(images) == 1,'Contents of images dir is not correct: %s' % ','.join(images))
+
+
+if __name__ == "__main__":
+    #import sys;sys.argv = ['', 'Test.testName']
+    unittest.main()
diff --git a/pelican-plugins/section_number/Readme.md b/pelican-plugins/section_number/Readme.md
new file mode 100644
index 0000000000000000000000000000000000000000..4f219077f54036d11acace064dcf41069237f187
--- /dev/null
+++ b/pelican-plugins/section_number/Readme.md
@@ -0,0 +1,41 @@
+Section Number
+--------------
+
+This plugin adds section numbers to an article's context, in the form of `X.X.X`. Sections are indicated via Markdown headers, which appear as `<h1> – <h6>` in the generated HTML.
+
+
+# Settings
+
+By default, up to three section levels will be prefixed with numbers. You can customize this value by defining `SECTION_NUMBER_MAX` in your settings file:
+
+    SECTION_NUMBER_MAX = 5
+
+
+# Caveat
+
+The first section in the article will be marked as the top section level. Namely, if `<h3>` is the first section encountered, the plugin assumes that no `<h1>` or `<h2>` sections will be present. Otherwise an exception may result.
+
+
+# Example
+
+The following Markdown content...
+
+    # section
+    blabla
+    ## subsection
+    blabla
+    ## subsection
+    blabla
+    # section
+    blabla
+
+... will be rendered as:
+
+>#1 section
+>blabla
+>##1.1 subsection
+>blabla
+>##1.2 subsection
+>blabla
+>#2 section
+>blabla
diff --git a/pelican-plugins/section_number/__init__.py b/pelican-plugins/section_number/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..19a92cfa12cd73f11339e327ce0f596fd5b26440
--- /dev/null
+++ b/pelican-plugins/section_number/__init__.py
@@ -0,0 +1 @@
+from .section_number import *
\ No newline at end of file
diff --git a/pelican-plugins/section_number/section_number.py b/pelican-plugins/section_number/section_number.py
new file mode 100644
index 0000000000000000000000000000000000000000..1ac435e1315b4f1dfb19c361050daaee718059ea
--- /dev/null
+++ b/pelican-plugins/section_number/section_number.py
@@ -0,0 +1,90 @@
+# -*- coding: utf-8 -*-
+"""
+Section number plugin for Pelican
+================================
+Adds section numbers to section titles of the article
+"""
+
+from pelican import signals
+
+
+def _extract_level(text, idx):
+    end = text.find(">", idx)
+
+    if end == -1:
+        return (idx, -1)
+
+    try:
+        level = int(text[idx: end])
+        return (end, level)
+
+    except:
+        return (idx, -1)
+
+
+def _level_str(level_nums, level_max):
+    ret = u''
+
+    if len(level_nums) > level_max:
+        return ret
+
+    for n in level_nums:
+        ret += str(n) + '.'
+
+    return ret[:-1]
+
+
+def _insert_title_number(text, level_max):
+    idx = 0
+    levels = []
+    level_nums = []
+
+    while True:
+        idx = text.find("<h", idx)
+        if idx == -1:
+            break
+
+        (idx, level) = _extract_level(text, idx + 2)
+
+        if level == -1:
+            continue
+
+        if not levels:
+            levels += [level]
+            level_nums += [1]
+
+        elif level == levels[-1]:
+            level_nums[-1] += 1
+
+        elif level < levels[-1]:
+            while level < levels[-1]:
+                levels.pop()
+                level_nums.pop()
+            level_nums[-1] += 1
+
+        else:
+            while level > levels[-1]:
+                levels += [levels[-1] + 1]
+                level_nums += [1]
+
+        text = text[:idx + 1] + \
+            _level_str(level_nums, level_max) + '. ' + text[idx + 1:]
+
+    # print text.encode('gb2312')
+    return text
+
+
+def process_content(content):
+    if content._content is None:
+        return
+
+    level_max = content.settings.get('SECTION_NUMBER_MAX', 3)
+
+    if level_max <= 0:
+        return
+
+    content._content = _insert_title_number(content._content, level_max)
+
+
+def register():
+    signals.content_object_init.connect(process_content)
diff --git a/pelican-plugins/series/Readme.md b/pelican-plugins/series/Readme.md
new file mode 100644
index 0000000000000000000000000000000000000000..fecb36737868c8bff9c5f3593550a4124b6b6ccc
--- /dev/null
+++ b/pelican-plugins/series/Readme.md
@@ -0,0 +1,48 @@
+Series plugin
+-------------
+
+**NOTE:** [This plugin has been moved to its own repository](https://github.com/pelican-plugins/series). Please file any issues/PRs there. Once all plugins have been migrated to the [new Pelican Plugins organization](https://github.com/pelican-plugins), this monolithic repository will be archived.
+
+The series plugin allows you to join different posts into a series.
+
+In order to mark posts as part of a series, use the `:series:` metadata:
+
+    :series:  NAME_OF_THIS_SERIES
+
+or, in Markdown syntax
+
+    Series: NAME_OF_THIS_SERIES
+
+The plugin collects all articles belonging to the same series and provides
+series-related variables that you can use in your template.
+
+#### Indexing
+By default articles in a series are ordered by date and then automatically numbered.
+
+If you want to force a given order just specify the `:series_index:` metadata or in Markdown `series_index:`,
+starting from 1. All articles with this enforced index are put at the beginning of
+the series and ordered according to the index itself. All the remaining articles
+come after them, ordered by date.
+
+The plugin provides the following variables to your templates
+
+    * `article.series.name` is the name of the series as specified in the article metadata
+    * `article.series.index` is the index of the current article inside the series
+    * `article.series.all` is an ordered list of all articles in the series (including the current one)
+    * `article.series.all_previous` is an ordered list of the articles published before the current one
+    * `article.series.all_next` is an ordered list of the articles published after the current one
+    * `article.series.previous` is the previous article in the series (a shortcut to `article.series.all_previous[-1]`)
+    * `article.series.next` is the next article in the series (a shortcut to `article.series.all_next[0]`)
+
+For example:
+
+    {% if article.series %}
+        <p>This post is part {{ article.series.index }} of the "{{ article.series.name }}" series:</p>
+        <ol class="parts">
+            {% for part_article in article.series.all %}
+                <li {% if part_article == article %}class="active"{% endif %}>
+                    <a href='{{ SITEURL }}/{{ part_article.url }}'>{{ part_article.title }}</a>
+                </li>
+            {% endfor %}
+        </ol>
+    {% endif %}
diff --git a/pelican-plugins/series/__init__.py b/pelican-plugins/series/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..451ef23a6ccb082ac385c87418b865edf1bec6ac
--- /dev/null
+++ b/pelican-plugins/series/__init__.py
@@ -0,0 +1 @@
+from .series import *
diff --git a/pelican-plugins/series/series.py b/pelican-plugins/series/series.py
new file mode 100644
index 0000000000000000000000000000000000000000..c183de6ccfa30cfa888b451c1380500834d3ba5a
--- /dev/null
+++ b/pelican-plugins/series/series.py
@@ -0,0 +1,72 @@
+# -*- coding: utf-8 -*-
+"""
+This plugin extends the original series plugin
+by FELD Boris <lothiraldan@gmail.com>
+
+Copyright (c) Leonardo Giordani <giordani.leonardo@gmail.com>
+
+Joins articles in a series and provides variables to
+manage the series in the template.
+"""
+
+from collections import defaultdict
+from operator import itemgetter
+
+from pelican import signals
+
+
+def aggregate_series(generator):
+    series = defaultdict(list)
+
+    # This cycles through all articles in the given generator
+    # and collects the 'series' metadata, if present.
+    # The 'series_index' metadata is also stored, if specified
+    for article in generator.articles:
+        if 'series' in article.metadata:
+            article_entry = (
+                article.metadata.get('series_index', None),
+                article.metadata['date'],
+                article
+            )
+
+            series[article.metadata['series']].append(article_entry)
+
+    # This uses items() which on Python2 is not a generator
+    # but we are dealing with a small amount of data so
+    # there shouldn't be performance issues =)
+    for series_name, series_articles in series.items():
+        # This is not DRY but very simple to understand
+        forced_order_articles = [
+            art_tup for art_tup in series_articles if art_tup[0] is not None]
+
+        date_order_articles = [
+            art_tup for art_tup in series_articles if art_tup[0] is None]
+
+        forced_order_articles.sort(key=itemgetter(0))
+        date_order_articles.sort(key=itemgetter(1))
+
+        all_articles = forced_order_articles + date_order_articles
+        ordered_articles = [art_tup[2] for art_tup in all_articles]
+        enumerated_articles = enumerate(ordered_articles)
+
+        for index, article in enumerated_articles:
+            article.series = dict()
+            article.series['name'] = series_name
+            article.series['index'] = index + 1
+            article.series['all'] = ordered_articles
+            article.series['all_previous'] = ordered_articles[0: index]
+            article.series['all_next'] = ordered_articles[index + 1:]
+
+            if index > 0:
+                article.series['previous'] = ordered_articles[index - 1]
+            else:
+                article.series['previous'] = None
+
+            try:
+                article.series['next'] = ordered_articles[index + 1]
+            except IndexError:
+                article.series['next'] = None
+
+
+def register():
+    signals.article_generator_finalized.connect(aggregate_series)
diff --git a/pelican-plugins/shaarli_poster/README.md b/pelican-plugins/shaarli_poster/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..d47bd721c093b7cc0a56e2346e6093bd2f0c14fe
--- /dev/null
+++ b/pelican-plugins/shaarli_poster/README.md
@@ -0,0 +1,55 @@
+# Summary
+
+> [Shaarli](https://github.com/shaarli/Shaarli) is a minimalist link sharing service that you can install on your own server.
+> It is designed to be personal (single-user), fast and handy.
+
+This plugin upload newly redacted articles onto a specified Shaarli instance.
+
+First, it detects which articles are new by querying the list of links from the configured Shaarli server
+with the tag "FromPelican" (configurable through the `SHAARLI_POSTER_TAG` variable).
+
+Then, it creates a link on the Shaarli instance for every missing article.
+
+
+## Installation
+
+This plugin relies on the [shaarli-client](https://python-shaarli-client.readthedocs.io/en/latest/user/configuration.html) Python package,
+that must be installed and configured beforehand.
+
+To enable this plugin, add the following to your `publishconf.py`:
+
+    PLUGIN_PATH = 'path/to/pelican-plugins'
+    PLUGINS = ['shaarli_poster']
+
+`PLUGIN_PATH` can be a path relative to your settings file or an absolute path.
+
+
+## Configuration
+
+Available options:
+
+- `SHAARLI_POSTER_TAG` (optional, default: `'FromPelican'`) : defines tag to add on all Shaarli links created by this plugin
+- `SHAARLI_POSTER_CONFIG_FILE_PATH` (optional) : where to look for a [python-shaarli-client configuration file](https://python-shaarli-client.readthedocs.io/en/latest/user/configuration.html)
+- `SHAARLI_POSTER_INSTANCE` : name of the instance to use in this configuration file, instead of the default one
+- `SHAARLI_POSTER_INSECURE` (optional) : set it to `True` to disable certs verification
+
+
+## CLI usage
+
+Due to the way the Shaarli HTTP API work, newly created links are associated with the date of the plugin execution,
+and not the actual date of the blog article.
+
+This is usually not an issue but when enabling this plugin on an existing blog,
+you may want to generate a Shaarli `datastore.php` with links dates matching the articles ones.
+
+You can invoke this plugin in order to do exactly this:
+
+    python shaarli_poster.py --all --starting-id 42 pelicanconf.py > datastore.json
+    php -r '$dt = json_decode(file_get_contents("datastore.json"), true); foreach($dt as &$l) { $l["created"] = new DateTime($l["created"]["date"], new DateTimeZone($l["created"]["timezone"])); if (array_key_exists("updated", $l) && $l["updated"]) { $l["updated"] = new DateTime($l["updated"]["date"], new DateTimeZone($l["updated"]["timezone"])); } } print("<?php /* ".base64_encode(gzdeflate(serialize($dt)))." */ ?>");' > datastore.php
+
+
+## Tests
+
+To execute them:
+
+    nosetests -w shaarli_poster
diff --git a/pelican-plugins/shaarli_poster/__init__.py b/pelican-plugins/shaarli_poster/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..99e470b4bb2d07aeae5f9c22c4c92430ce040f16
--- /dev/null
+++ b/pelican-plugins/shaarli_poster/__init__.py
@@ -0,0 +1 @@
+from .shaarli_poster import *
diff --git a/pelican-plugins/shaarli_poster/requirements.txt b/pelican-plugins/shaarli_poster/requirements.txt
new file mode 100644
index 0000000000000000000000000000000000000000..8d027766f73e650f43ad86d50e2b02cbf5179ce4
--- /dev/null
+++ b/pelican-plugins/shaarli_poster/requirements.txt
@@ -0,0 +1 @@
+shaarli-client
diff --git a/pelican-plugins/shaarli_poster/shaarli_poster.py b/pelican-plugins/shaarli_poster/shaarli_poster.py
new file mode 100644
index 0000000000000000000000000000000000000000..fb7674ea0efd9e81358021c28bea64dd3875c51f
--- /dev/null
+++ b/pelican-plugins/shaarli_poster/shaarli_poster.py
@@ -0,0 +1,139 @@
+# -*- coding: utf-8 -*-
+import json
+import logging
+import os
+import re
+from argparse import ArgumentParser, Namespace
+from base64 import b64encode
+from collections import defaultdict
+
+from pelican import signals
+from pelican.generators import ArticlesGenerator
+from pelican.settings import read_settings
+from shaarli_client.client import ShaarliV1Client
+from shaarli_client.config import get_credentials
+
+
+DEFAULT_TAG = 'FromPelican'
+LOGGER = logging.getLogger(__name__)
+
+
+def upload_new_articles(generators):
+    article_generator = next(g for g in generators if isinstance(g, ArticlesGenerator))
+    client = build_shaarli_client(article_generator.settings)
+    articles_urls_on_shaarli = get_published_articles_urls(client, article_generator.settings)
+    for article in article_generator.articles:
+        if article.status == 'published' and article.url not in articles_urls_on_shaarli:
+            LOGGER.info('Publishing article %s', article.url)
+            publish_new_article(client, article_generator.settings, article)
+
+def build_shaarli_client(settings):
+    args = Namespace(
+        config=settings.get('SHAARLI_POSTER_CONFIG_FILE_PATH'),
+        instance=settings.get('SHAARLI_POSTER_INSTANCE'),
+        secret=None,
+        url=None,
+    )
+    return ShaarliV1Client(*get_credentials(args))
+
+def get_published_articles_urls(client, pelican_settings):
+    args = Namespace(
+        endpoint_name='get-links',
+        insecure=pelican_settings.get('SHAARLI_POSTER_INSECURE'),
+        limit='all',
+        searchtags=pelican_settings.get('SHAARLI_POSTER_TAG', DEFAULT_TAG)
+    )
+    response = client.request(args)
+    response.raise_for_status()
+    return set(link['url'].rsplit('/', 1)[1] for link in response.json())
+
+def publish_new_article(client, pelican_settings, article):
+    description, tags = get_desc_and_tags(article, pelican_settings)
+    args = Namespace(
+        endpoint_name='post-link',
+        insecure=pelican_settings.get('SHAARLI_POSTER_INSECURE'),
+        url=os.path.join(pelican_settings['SITEURL'], article.url),
+        title=article.title,
+        description=description,
+        tags=tags
+    )
+    response = client.request(args)
+    response.raise_for_status()
+
+def get_desc_and_tags(article, pelican_settings):
+    tags = [tag.name for tag in article.tags] + [pelican_settings.get('SHAARLI_POSTER_TAG', DEFAULT_TAG)]
+    # We transform relative images URLs into absolute ones:
+    description = re.sub(' src="(?!http)', ' src="' + os.path.join(pelican_settings['SITEURL'], ''), article.summary)
+    return description, tags
+
+def main():
+    args_parser = ArgumentParser()
+    args_parser.add_argument('--all', action='store_true', help='Do not filter out articles already published')
+    args_parser.add_argument('--starting-id', type=int, default=1)
+    args_parser.add_argument('--timezone', default='Europe/Berlin')
+    args_parser.add_argument('--timezone-type', type=int, default=3)
+    args_parser.add_argument('pelican_config_file')
+    args = args_parser.parse_args()
+    settings = read_settings(args.pelican_config_file)
+    article_generator = build_article_generator(settings, settings['PATH'])
+    articles_urls_on_shaarli = set() if args.all else get_published_articles_urls(client, settings)
+    links = []
+    for i, article in enumerate(article_generator.articles):
+        if article.status == 'published' and article.url not in articles_urls_on_shaarli:
+            id = args.starting_id + i
+            description, tags = get_desc_and_tags(article, settings)
+            url = os.path.join(settings['SITEURL'], article.url)
+            links.append({
+                'url': url,
+                'real_url': url,
+                'title': article.title,
+                'description': description,
+                'created': {
+                    'date': article.date.strftime('%Y-%m-%d %H:%M:%S.%f'),
+                    'timezone_type': args.timezone_type,
+                    'timezone': args.timezone
+                },
+                'tags': ' '.join(tags),
+                'id': id,
+                'shorturl': small_hash(article.date, id),
+                'private': 0,
+            })
+    print(json.dumps(links, indent=4, sort_keys=True))
+
+def build_article_generator(settings, content_path, output_path=None):
+    context = settings.copy()
+    context['generated_content'] = dict()
+    context['static_links'] = set()
+    article_generator = ArticlesGenerator(
+        context=context, settings=settings,
+        path=content_path, theme=settings['THEME'], output_path=output_path)
+    article_generator.generate_context()
+    return article_generator
+
+def small_hash(date, id):
+    'Reference: https://github.com/shaarli/Shaarli/blob/master/application/Utils.php#L24'
+    text = date.strftime('%Y%m%d_%H%M%S') + str(id)
+    return b64encode(php_crc32(text.encode()).to_bytes(4, 'big')).decode().replace('=', '')
+
+def php_crc32(a):
+    '''
+    References:
+    - https://www.php.net/manual/en/function.hash-file.php#104836
+    - https://stackoverflow.com/a/50843127/636849
+    '''
+    crc = 0xffffffff
+    for x in a:
+        crc ^= x << 24;
+        for k in range(8):
+            crc = (crc << 1) ^ 0x04c11db7 if crc & 0x80000000 else crc << 1
+    crc = ~crc
+    crc &= 0xffffffff
+    # Convert from big endian to little endian:
+    return int.from_bytes(crc.to_bytes(4, 'big'), 'little')
+
+def register():
+    signals.all_generators_finalized.connect(upload_new_articles)
+
+
+if __name__ == '__main__':
+    main()
diff --git a/pelican-plugins/shaarli_poster/test_content/dummy_article.md b/pelican-plugins/shaarli_poster/test_content/dummy_article.md
new file mode 100644
index 0000000000000000000000000000000000000000..c9444236e5405a16f2e55f7750a858188a46b5b4
--- /dev/null
+++ b/pelican-plugins/shaarli_poster/test_content/dummy_article.md
@@ -0,0 +1,14 @@
+Title: Dummy article
+Category: test
+Tags: foo, bar, foobar
+Date: 2010-12-02 10:14
+Modified: 2010-12-02 10:20
+Summary: I have a lot to test
+
+Test Markdown File Header
+=========================
+
+Used for pelican test
+---------------------
+
+The quick brown fox jumped over the lazy dog's back.
diff --git a/pelican-plugins/shaarli_poster/test_content/other_dummy_article.md b/pelican-plugins/shaarli_poster/test_content/other_dummy_article.md
new file mode 100644
index 0000000000000000000000000000000000000000..2af71ac1c422ed763d2ebb5463f1c9b6bc069d77
--- /dev/null
+++ b/pelican-plugins/shaarli_poster/test_content/other_dummy_article.md
@@ -0,0 +1,19 @@
+Title: マックOS X 10.8でパイソンとVirtualenvをインストールと設定
+Slug: other-dummy-article
+Date: 2012-12-20
+Modified: 2012-12-22
+Tags: パイソン, マック
+Category: 指導書
+Summary: パイソンとVirtualenvをまっくでインストールする方法について明確に説明します。
+
+Writing unicode is certainly fun.
+
+パイソンとVirtualenvをまっくでインストールする方法について明確に説明します。
+
+And let's mix languages.
+
+первый пост
+
+Now another.
+
+İlk yazı çok özel değil.
diff --git a/pelican-plugins/shaarli_poster/test_shaarli_poster.py b/pelican-plugins/shaarli_poster/test_shaarli_poster.py
new file mode 100644
index 0000000000000000000000000000000000000000..d87d2984d283f1dc90316e04a89fa326d9484fe7
--- /dev/null
+++ b/pelican-plugins/shaarli_poster/test_shaarli_poster.py
@@ -0,0 +1,34 @@
+# -*- coding: utf-8 -*-
+import os, shutil
+from tempfile import TemporaryDirectory
+from unittest.mock import patch, Mock
+
+from pelican.tests.support import get_settings, unittest
+
+from .shaarli_poster import build_article_generator, upload_new_articles
+
+
+CUR_DIR = os.path.dirname(__file__)
+TEST_CONTENT_DIR = os.path.join(CUR_DIR, 'test_content')
+
+
+class ShaarliPosterTest(unittest.TestCase):
+
+    @patch('shaarli_poster.shaarli_poster.get_credentials')  # avoid ~/.config/shaarli/client.ini parsing, which may not exist in CI
+    @patch('shaarli_poster.shaarli_poster.ShaarliV1Client')
+    def test_upload_new_article(self, ShaarliClientClassMock, _):
+        ShaarliClientClassMock.return_value.request.side_effect = [
+            ResponseMock([{'url': '/other-dummy-article.html'}]),  # get-links
+            ResponseMock()  # post-link
+        ]
+        with TemporaryDirectory() as tmpdirname:
+            upload_new_articles([build_article_generator(get_settings(filenames={}), TEST_CONTENT_DIR, tmpdirname)])
+            self.assertEqual(ShaarliClientClassMock.return_value.request.call_count, 2)  # 1 get-links + 1 post-link
+
+class ResponseMock:
+    def __init__(self, json=None):
+        self._json = json
+    def json(self):
+        return self._json
+    def raise_for_status(self):
+        pass
diff --git a/pelican-plugins/share_post/README.md b/pelican-plugins/share_post/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..ba3ec0c919c70f087082d33d6a7df6fcbde378ce
--- /dev/null
+++ b/pelican-plugins/share_post/README.md
@@ -0,0 +1,77 @@
+# Share Post
+
+**NOTE:** [This plugin has been moved to its own repository](https://github.com/pelican-plugins/share-post). Please file any issues/PRs there. Once all plugins have been migrated to the [new Pelican Plugins organization](https://github.com/pelican-plugins), this monolithic repository will be archived.
+
+A Pelican plugin to create share URLs of article
+
+# Author
+
+Copyright (c) Talha Mansoor
+
+Author          | Talha Mansoor
+----------------|-----
+Author Email    | talha131@gmail.com
+Author Homepage | http://onCrashReboot.com
+Github Account  | https://github.com/talha131
+
+### Contributors
+
+* [Jonathan DEKHTIAR](https://github.com/DEKHTIARJonathan) - contact@jonathandekhtiar.eu
+* [Paolo Melchiorre](https://github.com/pauloxnet) - [www.paulox.net](https://www.paulox.net/)
+
+## Why do you need it?
+
+Almost all website have share widgets to let readers share posts on social
+networks. Most of these widgets are used by vendors for online tracking. These
+widgets are also visual which quite often become a distraction and negatively
+affect readers attention.
+
+`share_post` creates old school URLs for some popular sites which your theme
+can use. These links do not have the ability to track the users. They can also
+be unobtrusive depending on how Pelican theme uses them.
+
+## Requirements
+
+`share_post` requires BeautifulSoup
+
+```bash
+pip install beautifulsoup4
+```
+
+## How to Use
+
+`share_post` adds a dictionary attribute to `article` which can be accessed via
+`article.share_post`. Keys of the dictionary are as follows,
+
+1. `facebook`
+1. `email`
+1. `twitter`
+1. `diaspora`
+1. `linkedin`
+1. `hacker-news`
+1. `reddit`
+
+## Template Example
+
+```html
+{% if article.share_post and article.status != 'draft' %}
+<section>
+  <p id="post-share-links">
+    Share on:
+    <a href="{{article.share_post['diaspora']}}" target="_blank" title="Share on Diaspora">Diaspora*</a>
+    ❄
+    <a href="{{article.share_post['twitter']}}" target="_blank" title="Share on Twitter">Twitter</a>
+    ❄
+    <a href="{{article.share_post['facebook']}}" target="_blank" title="Share on Facebook">Facebook</a>
+    ❄
+    <a href="{{article.share_post['linkedin']}}" target="_blank" title="Share on LinkedIn">LinkedIn</a>
+    ❄
+    <a href="{{article.share_post['hacker-news']}}" target="_blank" title="Share on HackerNews">HackerNews</a>
+    ❄
+    <a href="{{article.share_post['email']}}" target="_blank" title="Share via Email">Email</a>
+    ❄
+    <a href="{{article.share_post['reddit']}}" target="_blank" title="Share via Reddit">Reddit</a>
+  </p>
+</section>
+{% endif %}
+```
diff --git a/pelican-plugins/share_post/__init__.py b/pelican-plugins/share_post/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..4185ce815f8dfa14376e6a225fd603f542e8c076
--- /dev/null
+++ b/pelican-plugins/share_post/__init__.py
@@ -0,0 +1 @@
+from .share_post import *  # noqa
diff --git a/pelican-plugins/share_post/share_post.py b/pelican-plugins/share_post/share_post.py
new file mode 100644
index 0000000000000000000000000000000000000000..d2a3c64d30c1b97e9afa553dea379be7b4392e1c
--- /dev/null
+++ b/pelican-plugins/share_post/share_post.py
@@ -0,0 +1,100 @@
+"""
+Share Post plugin.
+
+This plugin adds share URL to article. These links are textual which means no
+online tracking of your readers.
+"""
+
+from bs4 import BeautifulSoup
+
+from pelican import contents, signals
+from pelican.generators import ArticlesGenerator, PagesGenerator
+
+try:
+    from urllib.parse import quote
+except ImportError:
+    from urllib import quote
+
+
+def article_title(content):
+    main_title = BeautifulSoup(content.title, 'html.parser').get_text().strip()
+    sub_title = ''
+    if hasattr(content, 'subtitle'):
+        sub_title = ' ' + BeautifulSoup(content.subtitle, 'html.parser').get_text().strip()  # noqa
+    return quote(('%s%s' % (main_title, sub_title)).encode('utf-8'))
+
+
+def article_url(content):
+    site_url = content.settings['SITEURL']
+    return quote(('%s/%s' % (site_url, content.url)).encode('utf-8'))
+
+
+def article_summary(content):
+    return quote(BeautifulSoup(content.summary, 'html.parser').get_text().strip().encode('utf-8'))  # noqa
+
+
+def twitter_hastags(content):
+    tags = getattr(content, 'tags', [])
+    hashtags = ','.join((tag.slug for tag in tags))
+    return '' if not hashtags else '&hashtags=%s' % hashtags
+
+
+def twitter_via(content):
+    twitter_username = content.settings.get('TWITTER_USERNAME', '')
+    return '' if not twitter_username else '&via=%s' % twitter_username
+
+
+def share_post(content):
+    if isinstance(content, contents.Static):
+        return
+
+    title = article_title(content)
+    url = article_url(content)
+    summary = article_summary(content)
+    hastags = twitter_hastags(content)
+    via = twitter_via(content)
+
+    mail_link = 'mailto:?subject=%s&amp;body=%s' % (title, url)
+    diaspora_link = 'https://sharetodiaspora.github.io/?title=%s&url=%s' % (
+        title, url)
+    facebook_link = 'https://www.facebook.com/sharer/sharer.php?u=%s' % url
+    twitter_link = 'https://twitter.com/intent/tweet?text=%s&url=%s%s%s' % (
+        title, url, via, hastags)
+    hackernews_link = 'https://news.ycombinator.com/submitlink?t=%s&u=%s' % (
+        title, url)
+    linkedin_link = 'https://www.linkedin.com/shareArticle?mini=true&url=%s&title=%s&summary=%s&source=%s' % (  # noqa
+        url, title, summary, url
+    )
+    reddit_link = 'https://www.reddit.com/submit?url=%s&title=%s' % (
+        url, title)
+
+    content.share_post = {
+        'diaspora': diaspora_link,
+        'twitter': twitter_link,
+        'facebook': facebook_link,
+        'linkedin': linkedin_link,
+        'hacker-news': hackernews_link,
+        'email': mail_link,
+        'reddit': reddit_link,
+    }
+
+
+def run_plugin(generators):
+    for generator in generators:
+        if isinstance(generator, ArticlesGenerator):
+            for article in generator.articles:
+                share_post(article)
+                for translation in article.translations:
+                    share_post(translation)
+        elif isinstance(generator, PagesGenerator):
+            for page in generator.pages:
+                share_post(page)
+
+
+def register():
+    try:
+        signals.all_generators_finalized.connect(run_plugin)
+    except AttributeError:
+        # NOTE: This results in #314 so shouldn't really be relied on
+        # https://github.com/getpelican/pelican-plugins/issues/314
+        signals.content_object_init.connect(share_post)
diff --git a/pelican-plugins/shortcodes/README.md b/pelican-plugins/shortcodes/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..4f9a9fd0c90d6ad5210b49e45918ff41d8bb7056
--- /dev/null
+++ b/pelican-plugins/shortcodes/README.md
@@ -0,0 +1,61 @@
+# pelican-shortcodes
+
+This plugin allows to define macros called shortcodes in content pages that will be expanded as jinja2 templates.
+
+_inspired by [lektor-shortcodes](https://github.com/skorokithakis/lektor-shortcodes)_
+
+The purpose of this plugin is to allow explicit and quick jinja2 templating in your content without compromising markdown/rst.
+
+# Example
+
+    #pelicanconf.py
+
+    SHORTCODES = {
+        'image': "<img src=/images/{{src}}>{{desc|title}}<img>"""
+    }
+
+Then in your content:
+
+    [% image src=foo.png desc="this is a test" %]
+
+will become:
+
+    <img src=/images/foo.png>This Is A Test<img>
+
+# Rules
+
+Shortcode patterns follows:
+
+    [% shortcode_name kwarg=value kwarg="value" kwarg='value' %]
+
+Shortcode pattern rules:
+
+* shortcodes must be single line
+* spaces are important, i.e. there should be space after `[%` and before `%]`
+* shortcode_name cannot contain spaces
+* kwargs are separated by a space
+* kwargs can be surrounded by quotes but not necessary
+
+# Recipes
+
+Some shortcode examples:
+
+    SHORTCODES = {
+        # image with caption
+        'image': "<img src=/images/{{src}} title={{desc}}></img><figcaption>{{desc}}</figcatpion>"
+
+        # embed looping mp4 gifs
+        'mp4gif': " <video width="480" height="240" autoplay loop muted title="{{title}}"><source src="/gifs/{{gif}}" type="video/mp4"></video>
+    }
+
+## Install
+
+    #pelicanconf.py    
+    PLUGINS = ['shortcodes']
+    SHORTCODES = {
+        # your shortcodes go here
+        # shortcode_name: jinja2 template string
+        'image': "<img src=/images/{{src}}>{{desc|title}}<img>"""
+    }
+    
+    
diff --git a/pelican-plugins/shortcodes/__init__.py b/pelican-plugins/shortcodes/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..40630719e1db941ca0244c77a144874e1cdb28f3
--- /dev/null
+++ b/pelican-plugins/shortcodes/__init__.py
@@ -0,0 +1 @@
+from .shortcodes import *
\ No newline at end of file
diff --git a/pelican-plugins/shortcodes/shortcodes.py b/pelican-plugins/shortcodes/shortcodes.py
new file mode 100644
index 0000000000000000000000000000000000000000..f9d7f30e003552a732fdfe36a899ee1edc81bccf
--- /dev/null
+++ b/pelican-plugins/shortcodes/shortcodes.py
@@ -0,0 +1,59 @@
+"""
+shortcodes.py
+=============
+Copyright: AGPLv3 <Bernardas Ališauskas @ bernardas.alisauskas@pm.me>
+
+This plugin allows to define macros called shortcodes in content pages that will be expanded as jinja2 templates.
+The purpose of this plugin is to allow explicit and quick jinja2 templating in your content without compromising markdown/rst.
+
+Example:
+
+    #pelicanconf.py
+
+    SHORTCODES = {
+        'image': "<img src=/images/{{src}}>{{desc|title}}<img>"
+    }
+
+Then in your content:
+
+    [% image src=foo.png desc="this is a test" %]
+
+will become:
+
+    <img src=/images/foo.png>This Is A Test<img>
+"""
+import re
+from typing import Match, Dict
+
+from jinja2 import Template
+from pelican import signals
+from pelican.contents import Content
+
+SETTINGS_NAME = 'SHORTCODES'
+
+
+def expand_shortcodes(text: str, shortcodes: Dict[str, str]) -> str:
+    def repl(group: Match):
+        """replace shortocodes with evaluated templates"""
+        match = group.groups()[0]
+        func, args = match.split(' ', 1)
+        args = re.split('(\w+=)', args)
+        args = [a.strip("""'" """) for a in args if a]
+        kwargs = {args[i].strip('='): args[i + 1] for i in range(0, len(args), 2)}
+        try:
+            return Template(shortcodes[func]).render(**kwargs)
+        except KeyError:
+            raise KeyError('shortcode {} not found'.format(func))
+
+    return re.sub(r'\[% (.+?) %\]', repl, text)
+
+
+def content_object_init(instance: Content):
+    shortcodes = instance.settings.get(SETTINGS_NAME)
+    if not shortcodes or not instance._content:
+        return
+    instance._content = expand_shortcodes(instance._content, shortcodes)
+
+
+def register():
+    signals.content_object_init.connect(content_object_init)
diff --git a/pelican-plugins/shortcodes/test_shortcodes.py b/pelican-plugins/shortcodes/test_shortcodes.py
new file mode 100644
index 0000000000000000000000000000000000000000..ff4d8b85445b881b93bf0960c5bab909dea35076
--- /dev/null
+++ b/pelican-plugins/shortcodes/test_shortcodes.py
@@ -0,0 +1,35 @@
+import pytest
+
+from .shortcodes import expand_shortcodes
+
+
+def test_expand_shortcodes():
+    shortcode_map = {
+        'image': """<img src={{src}} title={{title}}> {{desc}}</img>"""
+    }
+    text = """
+    This is a test foo
+
+    {} 
+    
+    more text
+    """
+
+    expected = {
+        """[% image src="image_nice.png" desc='meow meow' title=woof %]""":
+            "<img src=image_nice.png title=woof> meow meow</img>",
+        """[% image src=src desc=desc title=title %]""":
+            "<img src=src title=title> desc</img>",
+        """[% image src="src " desc=" desc" title=''' title ''' %]""":
+            "<img src=src title=title> desc</img>",
+    }
+    for short_code, exp in expected.items():
+        assert expand_shortcodes(text.format(short_code), shortcode_map) == text.format(exp)
+
+    expected_errors = {
+        """[% short_code_not_there src="src " desc=" desc" title=''' title ''' %]""":
+        KeyError,
+    }
+    with pytest.raises(KeyError):
+        for short_code, exp in expected_errors.items():
+            assert expand_shortcodes(text.format(short_code), shortcode_map)
diff --git a/pelican-plugins/show_source/ReadMe.rst b/pelican-plugins/show_source/ReadMe.rst
new file mode 100644
index 0000000000000000000000000000000000000000..b5990fea5308e4fcb6f27e94a8ef5bbe8469058c
--- /dev/null
+++ b/pelican-plugins/show_source/ReadMe.rst
@@ -0,0 +1,129 @@
+Show Source plugin
+------------------
+
+**NOTE:** `This plugin has been moved to its own repository <https://github.com/pelican-plugins/show-source>`_. Please file any issues/PRs there. Once all plugins have been migrated to the `new Pelican Plugins organization <https://github.com/pelican-plugins>`_, this monolithic repository will be archived.
+
+The plugin allows you to place a link to the source text of your posts in the
+same way that `Sphinx`_ does. It works for both pages and articles.
+
+Plugin Activation
+~~~~~~~~~~~~~~~~~
+
+To activate the plugin ensure that you have ``SHOW_SOURCE_ON_SIDEBAR = True`` or
+``SHOW_SOURCE_IN_SECTION = True`` your settings file.
+
+Making Source Available for Posts
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+In order to mark posts so that their source may be seen use the following
+metadata values (unless overridden)- for reStructuredText documents:
+
+.. code-block:: reStructuredText
+
+    :show_source: True
+
+or, in Markdown syntax
+
+.. code-block:: Markdown
+
+    Show_source: True
+
+The plugin will render your source document URL to a corresponding
+``article.show_source_url`` (or ``page.show_source_url``) attribute which is
+then accessible in the site templates.
+
+Show Source in the Templates
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+To get the show source links on the article or page you will have to modify your
+theme, either as a sidebar display or at the foot of an article.
+
+Article or Page Sidebar Display
+*******************************
+
+How to get the source link to appear in the sidebar using the
+`pelican-bootstrap3`_ theme:
+
+.. code-block:: HTML
+
+    {% if SHOW_SOURCE_ON_SIDEBAR %}
+        {% if (article and article.show_source_url) or (page and page.show_source_url) %}
+            <li class="list-group-item"><h4><i class="fa fa-tags fa-file-text"></i><span class="icon-label">This Page</span></h4>
+                <ul class="list-group">
+                    <li class="list-group-item">
+                        {% if article %}
+                        <a href="{{ SITEURL }}/{{ article.show_source_url }}">Show source</a>
+                        {% elif page %}
+                        <a href="{{ SITEURL }}/{{ page.show_source_url }}">Show source</a>
+                        {% endif %}
+                    </li>
+                </ul>
+            </li>
+        {% endif %}
+    {% endif %}
+
+Article Footer Display
+**********************
+
+Here's some code (yes, `pelican-bootstrap3`_ again) to enable a souce link at
+the bottom of an article:
+
+.. code-block:: HTML
+
+    {% if SHOW_SOURCE_IN_SECTION %}
+        {% if article and article.show_source_url %}
+        <section class="well" id="show-source">
+            <h4>This Page</h4>
+            <ul>
+                <a href="{{ SITEURL }}/{{ article.show_source_url }}">Show source</a>
+            </ul>
+        </section>
+        {% endif %}
+    {% endif %}
+
+
+Overriding Default Plugin Behaviour
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The default behaviour of the plugin is that revealing source is enabled on a
+case by case basis. This can be changed by the use of
+:py:`SHOW_SOURCE_ALL_POSTS = True` in the settings file. This does mean that the
+plugin will publish all source documents no matter whether ``show_source`` is
+set in the metadata or not.
+
+Unless overridden, each document is saved as the article or page slug attribute
+with a ``.txt`` extension.
+
+So for example, if your configuration had ``ARTICLE_SAVE_AS`` configured like
+so:
+
+.. code-block:: python
+
+    ARTICLE_SAVE_AS = 'posts/{date:%Y}/{date:%m}/{slug}/index.html'
+
+Your static HTML post and source text document will be like the following:
+
+.. code-block:: Text
+
+    posts/2016/10/welcome-to-my article/index.html
+    posts/2016/10/welcome-to-my article/welcome-to-my article.txt
+
+You can add the ``SHOW_SOURCE_FILENAME`` variable in your settings file to
+override the source file name, so you could set the following:
+
+.. code-block:: python
+
+    SHOW_SOURCE_FILENAME = 'my_source_file.txt'
+
+So with the ``ARTICLE_SAVE_AS`` configured as above, the files would be saved
+thus:
+
+.. code-block:: Text
+
+    posts/2016/10/welcome-to-my article/index.html
+    posts/2016/10/welcome-to-my article/my_source_file.txt
+
+This is the same behaviour for pages also.
+
+.. _`Sphinx`: http://www.sphinx-doc.org/
+.. _`pelican-bootstrap3`: https://github.com/getpelican/pelican-themes/tree/master/pelican-bootstrap3
diff --git a/pelican-plugins/show_source/__init__.py b/pelican-plugins/show_source/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..bdb4d9c6f396ed4f7f90efe914a9a5ded2cdc36b
--- /dev/null
+++ b/pelican-plugins/show_source/__init__.py
@@ -0,0 +1 @@
+from .show_source import *
diff --git a/pelican-plugins/show_source/show_source.py b/pelican-plugins/show_source/show_source.py
new file mode 100644
index 0000000000000000000000000000000000000000..bc36be8432acf5f43352272cc1d2e487088b123b
--- /dev/null
+++ b/pelican-plugins/show_source/show_source.py
@@ -0,0 +1,86 @@
+import os
+import logging
+from six.moves.urllib.parse import urljoin
+import six
+from pelican import signals
+from pelican.utils import pelican_open
+
+if not six.PY3:
+    from codecs import open
+
+logger = logging.getLogger(__name__)
+source_files = []
+PROCESS = ['articles', 'pages', 'drafts']
+
+def link_source_files(generator):
+    """
+    Processes each article/page object and formulates copy from and copy
+    to destinations, as well as adding a source file URL as an attribute.
+    """
+    # Get all attributes from the generator that are articles or pages
+    posts = [
+        getattr(generator, attr, None) for attr in PROCESS
+        if getattr(generator, attr, None) is not None]
+    # Work on each item
+    for post in posts[0]:
+        if not 'SHOW_SOURCE_ON_SIDEBAR' in generator.settings and \
+            not 'SHOW_SOURCE_IN_SECTION' in generator.settings:
+            return
+        # Only try this when specified in metadata or SHOW_SOURCE_ALL_POSTS
+        # override is present in settings
+        if 'SHOW_SOURCE_ALL_POSTS' in generator.settings or \
+            'show_source' in post.metadata:
+            # Source file name can be optionally set in config
+            show_source_filename = generator.settings.get(
+                'SHOW_SOURCE_FILENAME', '{}.txt'.format(post.slug)
+                )
+            try:
+                # Get the full path to the original source file
+                source_out = os.path.join(
+                    post.settings['OUTPUT_PATH'], post.save_as
+                    )
+                # Get the path to the original source file
+                source_out_path = os.path.split(source_out)[0]
+                # Create 'copy to' destination for writing later
+                copy_to = os.path.join(
+                    source_out_path, show_source_filename
+                    )
+                # Add file to published path
+                source_url = urljoin(
+                    post.save_as, show_source_filename
+                    )
+            except Exception:
+                return
+            # Format post source dict & populate
+            out = dict()
+            out['copy_raw_from'] = post.source_path
+            out['copy_raw_to'] = copy_to
+            logger.debug('Linked %s to %s', post.source_path, copy_to)
+            source_files.append(out)
+            # Also add the source path to the post as an attribute for tpls
+            post.show_source_url = source_url
+
+def _copy_from_to(from_file, to_file):
+    """
+    A very rough and ready copy from / to function.
+    """
+    with pelican_open(from_file) as text_in:
+        encoding = 'utf-8'
+        with open(to_file, 'w', encoding=encoding) as text_out:
+            text_out.write(text_in)
+            logger.info('Writing %s', to_file)
+
+def write_source_files(*args, **kwargs):
+    """
+    Called by the `page_writer_finalized` signal to process source files.
+    """
+    for source in source_files:
+        _copy_from_to(source['copy_raw_from'], source['copy_raw_to'])
+
+def register():
+    """
+    Calls the shots, based on signals
+    """
+    signals.article_generator_finalized.connect(link_source_files)
+    signals.page_generator_finalized.connect(link_source_files)
+    signals.page_writer_finalized.connect(write_source_files)
diff --git a/pelican-plugins/simple_footnotes/README.md b/pelican-plugins/simple_footnotes/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..52550c48c769c3e41beff90f871876f41a6e4555
--- /dev/null
+++ b/pelican-plugins/simple_footnotes/README.md
@@ -0,0 +1,28 @@
+Simple Footnotes
+================
+
+**NOTE: [This plugin has been moved to its own repository](https://github.com/pelican-plugins/simple-footnotes). Please file any issues/PRs there. Once all plugins have been migrated to the [new Pelican Plugins organization](https://github.com/pelican-plugins), this monolithic repository will be archived.**
+
+A Pelican plugin to add footnotes to blog posts.
+
+When writing a post or page, add a footnote like this:
+
+    Here's my written text[ref]and here is a footnote[/ref].
+
+This will appear as, roughly:
+
+Here's my written text<sup>1</sup>
+
+ 1. and here is a footnote ↩
+
+Inspired by Andrew Nacin's [Simple Footnotes WordPress plugin](http://wordpress.org/plugins/simple-footnotes/).
+
+Requirements
+============
+
+Needs html5lib, so you'll want to `pip install html5lib` before running.
+
+Should work with any content format (ReST, Markdown, whatever), because
+it looks for the `[ref]` and `[/ref]` once the conversion to HTML has happened.
+
+Stuart Langridge, http://www.kryogenix.org/, February 2014.
diff --git a/pelican-plugins/simple_footnotes/__init__.py b/pelican-plugins/simple_footnotes/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..2f958a84b3d05a99cf2e11afb9ce21a8fb17c075
--- /dev/null
+++ b/pelican-plugins/simple_footnotes/__init__.py
@@ -0,0 +1 @@
+from .simple_footnotes import *
diff --git a/pelican-plugins/simple_footnotes/requirements.txt b/pelican-plugins/simple_footnotes/requirements.txt
new file mode 100755
index 0000000000000000000000000000000000000000..1e92cdfd80f9cde56d7115e103f8a4b468f71af2
--- /dev/null
+++ b/pelican-plugins/simple_footnotes/requirements.txt
@@ -0,0 +1 @@
+html5lib
\ No newline at end of file
diff --git a/pelican-plugins/simple_footnotes/simple_footnotes.py b/pelican-plugins/simple_footnotes/simple_footnotes.py
new file mode 100644
index 0000000000000000000000000000000000000000..3f6bb46239a8ee9c3c3ba07cd7c19c369b56310b
--- /dev/null
+++ b/pelican-plugins/simple_footnotes/simple_footnotes.py
@@ -0,0 +1,95 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*- #
+
+from pelican import signals
+import html5lib
+import six
+
+RAW_FOOTNOTE_CONTAINERS = ["code"]
+
+
+def getText(node, recursive=False):
+    """Get all the text associated with this node.
+       With recursive == True, all text from child nodes is retrieved."""
+    L = [u'']
+    for n in node.childNodes:
+        if n.nodeType in (node.TEXT_NODE, node.CDATA_SECTION_NODE):
+            L.append(n.data)
+        else:
+            if not recursive:
+                return None
+        L.append(getText(n))
+    return u''.join(L)
+
+
+def sequence_gen(genlist):
+    for gen in genlist:
+        for elem in gen:
+            yield elem
+
+
+def parse_for_footnotes(article_or_page_generator):
+    all_content = [
+        getattr(article_or_page_generator, attr, None) \
+        for attr in [u'articles', u'drafts', u'pages']]
+    all_content = [x for x in all_content if x is not None]
+    for article in sequence_gen(all_content):
+        if u"[ref]" in article._content and u"[/ref]" in article._content:
+            content = article._content.replace(u"[ref]", u"<x-simple-footnote>").replace(u"[/ref]",
+                                                                                         u"</x-simple-footnote>")
+            parser = html5lib.HTMLParser(tree=html5lib.getTreeBuilder(u"dom"))
+            dom = parser.parse(content)
+            endnotes = []
+            count = 0
+            for footnote in dom.getElementsByTagName(u"x-simple-footnote"):
+                pn = footnote
+                leavealone = False
+                while pn:
+                    if pn.nodeName in RAW_FOOTNOTE_CONTAINERS:
+                        leavealone = True
+                        break
+                    pn = pn.parentNode
+                if leavealone:
+                    continue
+                count += 1
+                fnid = u"sf-%s-%s" % (article.slug, count)
+                fnbackid = u"%s-back" % (fnid,)
+                endnotes.append((footnote, fnid, fnbackid))
+                number = dom.createElement(u"sup")
+                number.setAttribute(u"id", fnbackid)
+                numbera = dom.createElement(u"a")
+                numbera.setAttribute(u"href", u"#%s" % fnid)
+                numbera.setAttribute(u"class", u"simple-footnote")
+                numbera.appendChild(dom.createTextNode(six.text_type(count)))
+                txt = getText(footnote, recursive=True).replace(u"\n", u" ")
+                numbera.setAttribute(u"title", txt)
+                number.appendChild(numbera)
+                footnote.parentNode.insertBefore(number, footnote)
+            if endnotes:
+                ol = dom.createElement(u"ol")
+                ol.setAttribute(u"class", u"simple-footnotes")
+                for e, fnid, fnbackid in endnotes:
+                    li = dom.createElement(u"li")
+                    li.setAttribute(u"id", fnid)
+                    while e.firstChild:
+                        li.appendChild(e.firstChild)
+                    backlink = dom.createElement(u"a")
+                    backlink.setAttribute(u"href", u"#%s" % fnbackid)
+                    backlink.setAttribute(u"class", u"simple-footnote-back")
+                    backlink.appendChild(dom.createTextNode(u'\u21a9'))
+                    li.appendChild(dom.createTextNode(u" "))
+                    li.appendChild(backlink)
+                    ol.appendChild(li)
+                    e.parentNode.removeChild(e)
+                dom.getElementsByTagName(u"body")[0].appendChild(ol)
+                s = html5lib.serializer.HTMLSerializer(omit_optional_tags=False, quote_attr_values='legacy')
+                output_generator = s.serialize(
+                    html5lib.treewalkers.getTreeWalker(u"dom")(dom.getElementsByTagName(u"body")[0]))
+                article._content = u"".join(list(output_generator)).replace(
+                    u"<x-simple-footnote>", u"[ref]").replace(u"</x-simple-footnote>", u"[/ref]").replace(
+                    u"<body>", u"").replace(u"</body>", u"")
+
+
+def register():
+    signals.article_generator_finalized.connect(parse_for_footnotes)
+    signals.page_generator_finalized.connect(parse_for_footnotes)
diff --git a/pelican-plugins/simple_footnotes/test_simple_footnotes.py b/pelican-plugins/simple_footnotes/test_simple_footnotes.py
new file mode 100644
index 0000000000000000000000000000000000000000..9a29c213fdf2dce6aa2c71acbc9d804fdc02f82f
--- /dev/null
+++ b/pelican-plugins/simple_footnotes/test_simple_footnotes.py
@@ -0,0 +1,32 @@
+import unittest
+from simple_footnotes import parse_for_footnotes
+
+class PseudoArticleGenerator(object):
+    articles = []
+class PseudoArticle(object):
+    _content = ""
+    slug = "article"
+
+class TestFootnotes(unittest.TestCase):
+
+    def _expect(self, input, expected_output):
+        ag = PseudoArticleGenerator()
+        art = PseudoArticle()
+        art._content = input
+        ag.articles = [art]
+        parse_for_footnotes(ag)
+        self.assertEqual(art._content, expected_output)
+
+    def test_simple(self):
+        self._expect('words[ref]footnote[/ref]end',
+        ('words<sup id=sf-article-1-back><a href=#sf-article-1 class=simple-footnote title=footnote>1</a></sup>end'
+         '<ol class=simple-footnotes>'
+         u'<li id=sf-article-1>footnote <a href=#sf-article-1-back class=simple-footnote-back>\u21a9</a></li>'
+         '</ol>'))
+
+    def test_no_footnote_inside_code(self):
+        self._expect("words<code>this is code[ref]footnote[/ref] end code </code> end",
+            "words<code>this is code[ref]footnote[/ref] end code </code> end")
+
+if __name__ == '__main__':
+    unittest.main()
\ No newline at end of file
diff --git a/pelican-plugins/sitemap/Readme.rst b/pelican-plugins/sitemap/Readme.rst
new file mode 100644
index 0000000000000000000000000000000000000000..bc2641217035a8f1f48b009648be28c57367fa39
--- /dev/null
+++ b/pelican-plugins/sitemap/Readme.rst
@@ -0,0 +1,83 @@
+Sitemap
+-------
+
+**NOTE:** `This plugin has been moved to its own repository <https://github.com/pelican-plugins/sitemap>`_. Please file any issues/PRs there. Once all plugins have been migrated to the `new Pelican Plugins organization <https://github.com/pelican-plugins>`_, this monolithic repository will be archived.
+
+-------------------------------------------------------------------------------
+
+This plugin generates plain-text or XML sitemaps. You can use the ``SITEMAP``
+variable in your settings file to configure the behavior of the plugin.
+
+The ``SITEMAP`` variable must be a Python dictionary and can contain these keys:
+
+- ``format``, which sets the output format of the plugin (``xml`` or ``txt``)
+
+- ``priorities``, which is a dictionary with three keys:
+
+  - ``articles``, the priority for the URLs of the articles and their
+    translations
+
+  - ``pages``, the priority for the URLs of the static pages
+
+  - ``indexes``, the priority for the URLs of the index pages, such as tags,
+     author pages, categories indexes, archives, etc...
+
+  All the values of this dictionary must be decimal numbers between ``0`` and ``1``.
+
+- ``changefreqs``, which is a dictionary with three items:
+
+  - ``articles``, the update frequency of the articles
+
+  - ``pages``, the update frequency of the pages
+
+  - ``indexes``, the update frequency of the index pages
+
+  Valid frequency values are ``always``, ``hourly``, ``daily``, ``weekly``, ``monthly``,
+  ``yearly`` and ``never``.
+
+You can exclude URLs from being included in the sitemap via regular expressions.
+For example, to exclude all URLs containing ``tag/`` or ``category/`` you can
+use the following ``SITEMAP`` setting.
+
+.. code-block:: python
+
+    SITEMAP = {
+        'exclude': ['tag/', 'category/']
+    }
+
+If a key is missing or a value is incorrect, it will be replaced with the
+default value.
+
+You can also exclude an individual URL by adding metadata to it setting ``private``
+to ``True``.
+
+The sitemap is saved in ``<output_path>/sitemap.<format>``.
+
+.. note::
+   ``priorities`` and ``changefreqs`` are information for search engines.
+   They are only used in the XML sitemaps.
+   For more information: <http://www.sitemaps.org/protocol.html#xmlTagDefinitions>
+
+**Example**
+
+Here is an example configuration (it's also the default settings):
+
+.. code-block:: python
+
+    # Where your plug-ins reside
+    PLUGIN_PATHS = ['/where/you/cloned/it/pelican-plugins/', ]
+    PLUGINS=['sitemap',]
+
+    SITEMAP = {
+        'format': 'xml',
+        'priorities': {
+            'articles': 0.5,
+            'indexes': 0.5,
+            'pages': 0.5
+        },
+        'changefreqs': {
+            'articles': 'monthly',
+            'indexes': 'daily',
+            'pages': 'monthly'
+        }
+    }
diff --git a/pelican-plugins/sitemap/__init__.py b/pelican-plugins/sitemap/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..6523d3a1b31811de7b291a13020c8fa567c99bc6
--- /dev/null
+++ b/pelican-plugins/sitemap/__init__.py
@@ -0,0 +1 @@
+from .sitemap import *
\ No newline at end of file
diff --git a/pelican-plugins/sitemap/sitemap.py b/pelican-plugins/sitemap/sitemap.py
new file mode 100644
index 0000000000000000000000000000000000000000..80fca089f322d89393a0e06853d8f090d5dac672
--- /dev/null
+++ b/pelican-plugins/sitemap/sitemap.py
@@ -0,0 +1,271 @@
+# -*- coding: utf-8 -*-
+'''
+Sitemap
+-------
+
+The sitemap plugin generates plain-text or XML sitemaps.
+'''
+
+from __future__ import unicode_literals
+
+import re
+import collections
+import os.path
+import logging
+
+from datetime import datetime
+from codecs import open
+from pytz import timezone
+
+from pelican import signals, contents
+from pelican.utils import get_date
+
+log = logging.getLogger(__name__)
+
+TXT_HEADER = """{0}/index.html
+{0}/archives.html
+{0}/tags.html
+{0}/categories.html
+"""
+
+XML_HEADER = """<?xml version="1.0" encoding="utf-8"?>
+<urlset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd"
+xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
+"""
+
+XML_URL = """
+<url>
+<loc>{0}/{1}</loc>
+<lastmod>{2}</lastmod>
+<changefreq>{3}</changefreq>
+<priority>{4}</priority>
+</url>
+"""
+
+XML_FOOTER = """
+</urlset>
+"""
+
+
+def format_date(date):
+    if date.tzinfo:
+        tz = date.strftime('%z')
+        tz = tz[:-2] + ':' + tz[-2:]
+    else:
+        tz = "-00:00"
+    return date.strftime("%Y-%m-%dT%H:%M:%S") + tz
+
+class SitemapGenerator(object):
+
+    def __init__(self, context, settings, path, theme, output_path, *null):
+
+        self.output_path = output_path
+        self.context = context
+        self.now = datetime.now()
+        self.siteurl = settings.get('SITEURL')
+
+        self.default_timezone = settings.get('TIMEZONE', 'UTC')
+        self.timezone = getattr(self, 'timezone', self.default_timezone)
+        self.timezone = timezone(self.timezone)
+
+        self.format = 'xml'
+
+        self.changefreqs = {
+            'articles': 'monthly',
+            'indexes': 'daily',
+            'pages': 'monthly'
+        }
+
+        self.priorities = {
+            'articles': 0.5,
+            'indexes': 0.5,
+            'pages': 0.5
+        }
+
+        self.sitemapExclude = []
+
+        config = settings.get('SITEMAP', {})
+
+        if not isinstance(config, dict):
+            log.warning("sitemap plugin: the SITEMAP setting must be a dict")
+        else:
+            fmt = config.get('format')
+            pris = config.get('priorities')
+            chfreqs = config.get('changefreqs')
+            self.sitemapExclude = config.get('exclude', [])
+
+            if fmt not in ('xml', 'txt'):
+                log.warning("sitemap plugin: SITEMAP['format'] must be `txt' or `xml'")
+                log.warning("sitemap plugin: Setting SITEMAP['format'] on `xml'")
+            elif fmt == 'txt':
+                self.format = fmt
+                return
+
+            valid_keys = ('articles', 'indexes', 'pages')
+            valid_chfreqs = ('always', 'hourly', 'daily', 'weekly', 'monthly',
+                    'yearly', 'never')
+
+            if isinstance(pris, dict):
+                # We use items for Py3k compat. .iteritems() otherwise
+                for k, v in pris.items():
+                    if k in valid_keys and not isinstance(v, (int, float)):
+                        default = self.priorities[k]
+                        log.warning("sitemap plugin: priorities must be numbers")
+                        log.warning("sitemap plugin: setting SITEMAP['priorities']"
+                                    "['{0}'] on {1}".format(k, default))
+                        pris[k] = default
+                self.priorities.update(pris)
+            elif pris is not None:
+                log.warning("sitemap plugin: SITEMAP['priorities'] must be a dict")
+                log.warning("sitemap plugin: using the default values")
+
+            if isinstance(chfreqs, dict):
+                # .items() for py3k compat.
+                for k, v in chfreqs.items():
+                    if k in valid_keys and v not in valid_chfreqs:
+                        default = self.changefreqs[k]
+                        log.warning("sitemap plugin: invalid changefreq `{0}'".format(v))
+                        log.warning("sitemap plugin: setting SITEMAP['changefreqs']"
+                                    "['{0}'] on '{1}'".format(k, default))
+                        chfreqs[k] = default
+                self.changefreqs.update(chfreqs)
+            elif chfreqs is not None:
+                log.warning("sitemap plugin: SITEMAP['changefreqs'] must be a dict")
+                log.warning("sitemap plugin: using the default values")
+
+    def write_url(self, page, fd):
+
+        if getattr(page, 'status', 'published') != 'published':
+            return
+           
+        if getattr(page, 'private', 'False') == 'True':
+            return
+
+        # We can disable categories/authors/etc by using False instead of ''
+        if not page.save_as:
+            return
+
+        page_path = os.path.join(self.output_path, page.save_as)
+        if not os.path.exists(page_path):
+            return
+
+        lastdate = getattr(page, 'date', self.now)
+        try:
+            lastdate = self.get_date_modified(page, lastdate)
+        except ValueError:
+            log.warning("sitemap plugin: " + page.save_as + " has invalid modification date,")
+            log.warning("sitemap plugin: using date value as lastmod.")
+        lastmod = format_date(lastdate)
+
+        if isinstance(page, contents.Article):
+            pri = self.priorities['articles']
+            chfreq = self.changefreqs['articles']
+        elif isinstance(page, contents.Page):
+            pri = self.priorities['pages']
+            chfreq = self.changefreqs['pages']
+        else:
+            pri = self.priorities['indexes']
+            chfreq = self.changefreqs['indexes']
+
+        pageurl = '' if page.url == 'index.html' else page.url
+
+        #Exclude URLs from the sitemap:
+        if self.format == 'xml':
+            flag = False
+            for regstr in self.sitemapExclude:
+                if re.match(regstr, pageurl):
+                    flag = True
+                    break
+            if not flag:
+                fd.write(XML_URL.format(self.siteurl, pageurl, lastmod, chfreq, pri))
+        else:
+            fd.write(self.siteurl + '/' + pageurl + '\n')
+
+    def get_date_modified(self, page, default):
+        if hasattr(page, 'modified'):
+            if isinstance(page.modified, datetime):
+                return page.modified
+            return get_date(page.modified)
+        else:
+            return default
+
+    def set_url_wrappers_modification_date(self, wrappers):
+        for (wrapper, articles) in wrappers:
+            lastmod = datetime.min.replace(tzinfo=self.timezone)
+            for article in articles:
+                lastmod = max(lastmod, article.date.replace(tzinfo=self.timezone))
+                try:
+                    modified = self.get_date_modified(article, datetime.min).replace(tzinfo=self.timezone)
+                    lastmod = max(lastmod, modified)
+                except ValueError:
+                    # Supressed: user will be notified.
+                    pass
+            setattr(wrapper, 'modified', str(lastmod))
+
+    def generate_output(self, writer):
+        path = os.path.join(self.output_path, 'sitemap.{0}'.format(self.format))
+
+        pages = self.context['pages'] + self.context['articles'] \
+                + [ c for (c, a) in self.context['categories']] \
+                + [ t for (t, a) in self.context['tags']] \
+                + [ a for (a, b) in self.context['authors']]
+
+        self.set_url_wrappers_modification_date(self.context['categories'])
+        self.set_url_wrappers_modification_date(self.context['tags'])
+        self.set_url_wrappers_modification_date(self.context['authors'])
+
+        for article in self.context['articles']:
+            pages += article.translations
+
+        log.info('writing {0}'.format(path))
+
+        with open(path, 'w', encoding='utf-8') as fd:
+
+            if self.format == 'xml':
+                fd.write(XML_HEADER)
+            else:
+                fd.write(TXT_HEADER.format(self.siteurl))
+
+            FakePage = collections.namedtuple('FakePage',
+                                              ['status',
+                                               'date',
+                                               'url',
+                                               'save_as'])
+
+            for standard_page in self.context['DIRECT_TEMPLATES']:
+                standard_page_url = self.context.get('{}_URL'.format(standard_page.upper()))
+                standard_page_save_as = self.context.get('{}_SAVE_AS'.format(standard_page.upper()))
+                fake = FakePage(status='published',
+                                date=self.now,
+                                url=standard_page_url or '{}.html'.format(standard_page),
+                                save_as=standard_page_save_as or '{}.html'.format(standard_page))
+                self.write_url(fake, fd)
+
+            # add template pages
+            # We use items for Py3k compat. .iteritems() otherwise
+            for path, template_page_url in self.context['TEMPLATE_PAGES'].items():
+
+                # don't add duplicate entry for index page
+                if template_page_url == 'index.html':
+                    continue
+
+                fake = FakePage(status='published',
+                                date=self.now,
+                                url=template_page_url,
+                                save_as=template_page_url)
+                self.write_url(fake, fd)
+
+            for page in pages:
+                self.write_url(page, fd)
+
+            if self.format == 'xml':
+                fd.write(XML_FOOTER)
+
+
+def get_generators(generators):
+    return SitemapGenerator
+
+
+def register():
+    signals.get_generators.connect(get_generators)
diff --git a/pelican-plugins/slim/README.md b/pelican-plugins/slim/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..6c93a4102b8b3f5925c69c01431d62735bb90149
--- /dev/null
+++ b/pelican-plugins/slim/README.md
@@ -0,0 +1,36 @@
+# Slim
+
+This plugin uses [Plim](http://plim.readthedocs.org/en/latest/), the Python port of [Slim](http://slim-lang.com), to render your theme's template files, instead of Jinja2. It works best if you have (handcrafted and are using) a Plim based theme. :)
+
+## Installation
+
+This plugin depends on the plim, beautifulsoup4, and htmlmin, which can be installed via pip:
+
+```
+pip install plim
+pip install beautifulsoup4
+pip install htmlmin
+```
+
+If you downloaded this module as part of the pelican-plugins repository, add it to your Pelican configuration as follows:
+
+```
+PLUGIN_PATH = '/path/to/pelican-plugins'
+PLUGINS = ['slim', ]
+```
+
+Otherwise, you can import it into Python as a normal module if you place this repository in your $PYTHONPATH.
+
+## Usage
+
+This plugin will break your Pelican project unless you are using a theme that follows the Plim syntax. As of 2015-04-24 Plim has not yet taken over the world, so if you want to use it, it probably means you need to write your own Plim theme before this plugin will be useful. 
+
+Once you've installed your Plim syntax theme, enabled the plugin and installed the dependencies listed above, you use Pelican normally to generate your site.
+
+## Settings
+
+Add `SLIM_OPTIONS = {'PRETTYIFY': True}` to pelicanconf.py to get prettyified HTML. 
+
+## About
+
+This plugin is a bit of a hack. It copies the builtin Writer and replaces the final rendering step, swapping out Jinja2 with Plim and then minifying or prettifying the HTML output.
diff --git a/pelican-plugins/slim/__init__.py b/pelican-plugins/slim/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..f21e9026eced2e876ac118bb060adc4b30346060
--- /dev/null
+++ b/pelican-plugins/slim/__init__.py
@@ -0,0 +1 @@
+from .slim import *
diff --git a/pelican-plugins/slim/slim.py b/pelican-plugins/slim/slim.py
new file mode 100644
index 0000000000000000000000000000000000000000..3878505c4fd519a69f6f1bb9d52bd86a8d8ede4f
--- /dev/null
+++ b/pelican-plugins/slim/slim.py
@@ -0,0 +1,173 @@
+import os
+import sys
+import logging
+from pkg_resources import EntryPoint
+
+from jinja2 import Template, TemplateNotFound
+
+try:
+    import plim
+except ImportError:
+    plim = None
+
+try:
+    import mako.lookup
+except ImportError:
+    mako = None
+
+try:
+    from bs4 import BeautifulSoup as bs
+except ImportError:
+    bs = None
+
+try:
+    from htmlmin import minify
+except ImportError:
+    minify = None
+
+from pelican.writers import Writer, is_selected_for_writing
+from pelican.paginator import Paginator
+from pelican import signals
+
+logger = logging.getLogger(__name__)
+
+def get_writer(sender):
+
+    class PlimWriter(Writer):
+        def write_file(self, name, template, context, relative_urls=False,
+                       paginated=None, override_output=False, **kwargs):
+            """Render the template and write the file.
+            :param name: name of the file to output
+            :param template: template to use to generate the content
+            :param context: dict to pass to the templates.
+            :param relative_urls: use relative urls or absolutes ones
+            :param paginated: dict of article list to paginate - must have the
+                same length (same list in different orders)
+            :param override_output: boolean telling if we can override previous
+                output with the same name (and if next files written with the same
+                name should be skipped to keep that one)
+            :param **kwargs: additional variables to pass to the templates
+            """
+
+            if name is False or name == "" or\
+               not is_selected_for_writing(self.settings,\
+                   os.path.join(self.output_path, name)):
+                return
+            elif not name:
+                # other stuff, just return for now
+                return
+
+            def _render_using_plim(filename, localcontext):
+                """Render the template using Plim."""
+
+                root_dir = os.path.dirname(os.path.abspath(filename))
+                template_file = os.path.basename(filename)
+                lookup = mako.lookup.TemplateLookup(
+                    directories=[root_dir],
+                    input_encoding='utf-8',
+                    output_encoding='utf-8',
+                    preprocessor=plim.preprocessor,
+                    strict_undefined=True,
+                    default_filters=['trim'])
+
+                output = lookup.get_template(template_file).render_unicode(
+                    **localcontext)
+                if ('SLIM_OPTIONS' in self.settings and
+                        'PRETTYIFY' in self.settings['SLIM_OPTIONS'] and
+                        self.settings['SLIM_OPTIONS']['PRETTYIFY']):
+                    output = bs(output, 'html.parser').prettify() # prettify the html
+                else:
+                    output = minify(output) # minify the html
+                return output
+
+            def _write_file(template, localcontext, output_path, name, override):
+                """Render the template write the file."""
+                # set localsiteurl for context so that Contents can adjust links
+                if localcontext['localsiteurl']:
+                    context['localsiteurl'] = localcontext['localsiteurl']
+
+                output = _render_using_plim(template.filename, localcontext)
+                # output = template.render(localcontext) # render using jinja2
+
+                path = os.path.join(output_path, name)
+                try:
+                    os.makedirs(os.path.dirname(path))
+                except Exception:
+                    pass
+
+                with self._open_w(path, 'utf-8', override=override) as f:
+                    f.write(output)
+                logger.info('Writing %s', path)
+
+                # Send a signal to say we're writing a file with some specific
+                # local context.
+                signals.content_written.send(path, context=localcontext)
+
+            def _get_localcontext(context, name, kwargs, relative_urls):
+                localcontext = context.copy()
+                localcontext['localsiteurl'] = localcontext.get(
+                    'localsiteurl', None)
+                if relative_urls:
+                    relative_url = path_to_url(get_relative_path(name))
+                    localcontext['SITEURL'] = relative_url
+                    localcontext['localsiteurl'] = relative_url
+                localcontext['output_file'] = name
+                localcontext.update(kwargs)
+                return localcontext
+
+            # pagination
+            if paginated:
+
+                # pagination needed, init paginators
+                paginators = {key: Paginator(name, val, self.settings)
+                              for key, val in paginated.items()}
+
+                # generated pages, and write
+                for page_num in range(list(paginators.values())[0].num_pages):
+                    paginated_kwargs = kwargs.copy()
+                    for key in paginators.keys():
+                        paginator = paginators[key]
+                        previous_page = paginator.page(page_num) \
+                            if page_num > 0 else None
+                        page = paginator.page(page_num + 1)
+                        next_page = paginator.page(page_num + 2) \
+                            if page_num + 1 < paginator.num_pages else None
+                        paginated_kwargs.update(
+                            {'%s_paginator' % key: paginator,
+                             '%s_page' % key: page,
+                             '%s_previous_page' % key: previous_page,
+                             '%s_next_page' % key: next_page})
+
+                    localcontext = _get_localcontext(context, page.save_as,
+                        paginated_kwargs, relative_urls)
+                    _write_file(template, localcontext, self.output_path,
+                                page.save_as, override_output)
+            else:
+                # no pagination
+                localcontext = _get_localcontext(context, name, kwargs,
+                    relative_urls)
+                _write_file(template, localcontext, self.output_path, name,
+                            override_output)
+
+    return PlimWriter
+
+def register():
+    """Plugin registration."""
+    if not plim:
+        logger.warning('`slim` failed to load dependency `plim`. '
+                       '`slim` plugin not loaded.')
+        return
+    if not mako:
+        logger.warning('`slim` failed to load dependency `mako`. '
+                       '`slim` plugin not loaded.')
+        return
+    if not bs:
+        logger.warning('`slim` failed to load dependency `BeautifulSoup4`. '
+                       '`slim` plugin not loaded.')
+        return
+    if not minify:
+        logger.warning('`slim` failed to load dependency `htmlmin`. '
+                       '`slim` plugin not loaded.')
+        return
+
+    signals.get_writer.connect(get_writer)
diff --git a/pelican-plugins/static_comments/Readme.md b/pelican-plugins/static_comments/Readme.md
new file mode 100644
index 0000000000000000000000000000000000000000..f95fbc49b2c58df6ef922e233a15598c4d01ab28
--- /dev/null
+++ b/pelican-plugins/static_comments/Readme.md
@@ -0,0 +1,26 @@
+Static comments
+---------------
+
+This plugin allows you to add static comments to an article. By default the
+plugin looks for the comments of each article in a local file named
+``comments/{slug}.md``, where ``{slug}`` is the value of the slug tag for the
+article. The comments file should be formatted using markdown.
+
+Set the ``STATIC_COMMENTS`` parameter to True to enable the plugin. Default is
+False.
+
+Set the ``STATIC_COMMENTS_DIR`` parameter to the directory where the comments
+are located. Default is ``comments``.
+
+On the template side, you just have to add a section for the comments to your
+``article.html``, as in this example::
+
+    {% if STATIC_COMMENTS %}
+    <section id="comments" class="body">
+    <h2>Comments!</h2>
+    {{ article.metadata.static_comments }}
+    </section>
+    {% endif %}
+
+Here is an example of usage:
+<http://jesrui.sdf-eu.org/pelican-static-comments.html>
diff --git a/pelican-plugins/static_comments/__init__.py b/pelican-plugins/static_comments/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..9d131b2d22ff20e4b5e57a9b0b53093bb89728f3
--- /dev/null
+++ b/pelican-plugins/static_comments/__init__.py
@@ -0,0 +1 @@
+from .static_comments import *
diff --git a/pelican-plugins/static_comments/static_comments.py b/pelican-plugins/static_comments/static_comments.py
new file mode 100644
index 0000000000000000000000000000000000000000..65c44916b6411e19a6c14584fe604b1b9d448303
--- /dev/null
+++ b/pelican-plugins/static_comments/static_comments.py
@@ -0,0 +1,46 @@
+# -*- coding: utf-8 -*-
+
+import codecs
+import logging
+import markdown
+import os
+
+logger = logging.getLogger(__name__)
+
+from pelican import signals
+
+
+def initialized(pelican):
+    from pelican.settings import DEFAULT_CONFIG
+    DEFAULT_CONFIG.setdefault('STATIC_COMMENTS', False)
+    DEFAULT_CONFIG.setdefault('STATIC_COMMENTS_DIR' 'comments')
+    if pelican:
+        pelican.settings.setdefault('STATIC_COMMENTS', False)
+        pelican.settings.setdefault('STATIC_COMMENTS_DIR', 'comments')
+
+
+def add_static_comments(gen, metadata):
+    if gen.settings['STATIC_COMMENTS'] != True:
+        return
+
+    if not 'slug' in metadata:
+        logger.warning("static_comments: "
+                "cant't locate comments file without slug tag in the article")
+        return
+
+    fname = os.path.join(gen.settings['STATIC_COMMENTS_DIR'],
+            metadata['slug'] + ".md")
+
+    if not os.path.exists(fname):
+        return
+
+    input_file = codecs.open(fname, mode="r", encoding="utf-8")
+    text = input_file.read()
+    html = markdown.markdown(text)
+
+    metadata['static_comments'] = html
+
+
+def register():
+    signals.initialized.connect(initialized)
+    signals.article_generator_context.connect(add_static_comments)
diff --git a/pelican-plugins/sub_parts/README.md b/pelican-plugins/sub_parts/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..761d16738ad49ba964cab3eb3ce6d3edfc35b2f7
--- /dev/null
+++ b/pelican-plugins/sub_parts/README.md
@@ -0,0 +1,62 @@
+# Sub-parts
+
+Use the Sub-parts plugin to break a very long article in multiple parts, without polluting the timeline with lots of small articles. Sub-parts are removed from timelines and categories but remain in tag and author pages.
+
+## Usage
+
+Article sub-parts have a compound slug with the slug of the parent, two hyphens (`--`), and some identifier. For example, if an article has the slug `karate`, then an article with the slug `karate--medals` would be a sub-part.
+
+This convention is very convenient if the slug is derived from the filename. For example, define the filename metadata as follows:
+
+    FILENAME_METADATA = '(?P<slug>(?P<date>\d{4}-\d{2}-\d{2})-[^.]+)
+
+Then, it is enough to name the files correctly. In the following example, the first Markdown article has two sub-parts:
+
+    ./content/blog/2015-03-21-karate.md
+    ./content/blog/2015-03-21-karate--attendees.md
+    ./content/blog/2015-03-21-karate--medals.md
+
+This plugin provides the following variables to your templates and modifies the titles of sub-part articles:
+
+`article.subparts`: for a parent article with sub-parts, the list of sub-part articles
+
+`article.subpart_of`: for a sub-part article, the parent article
+
+`article.subtitle`: the original title of the sub-part article
+
+`article.title`: compound title with the a comma and the title of the parent
+
+`article.subphotos`: for parent articles with sub-parts that have a gallery generated by the _Photos_ plugin, the total number of gallery photos in all sub-parts
+
+For example, add the following to the template `article.html`:
+
+    {% if article.subparts %}
+    <h2>Parts</h2>
+    <ul>
+    	{% for part in article.subparts %}
+    		<li><a href='{{ SITEURL }}/{{ part.url }}'>{{ part.subtitle }}</a>
+    	{% endfor %}
+    </ul>
+    {% endif %}
+    {% if article.subpart_of %}
+    <h2>Parts</h2>
+    <p>This article is part of <a href='{{ SITEURL }}/{{ article.subpart_of.url }}'>{{ article.subpart_of.title }}</a>:</p>
+    <ul>
+    	{% for part in article.subpart_of.subparts %}
+    		<li><a href='{{ SITEURL }}/{{ part.url }}'>{{ part.subtitle }}</a>
+    	{% endfor %}
+    </ul>
+    {% endif %}
+
+
+## Live sites using this plugin
+
+[pxquim.pt](http://pxquim.pt/) uses sub-parts and the _Photos_ plugin to publish photo galleries with thousands of photos. The Sub-parts plugin breaks the photo galleries into manageable chunks, possibly with different tags and authors.
+
+[pxquim.com](http://pxquim.com/) uses Sub-parts to cover conferences, where it makes sense to have a sub-part for each speaker.
+
+## What is the difference between Sub-part and Series?
+
+Series:	Connects separate articles, but never removes articles from timelines. Series is suited to connecting related articles that are published over time, e.g., a ten-part article written over several months, or a large festival with several independent performances.
+
+Sub-part: Articles that are subordinate to each other, hiding sub-articles from timelines. Sub-parts is suited to relating articles clustered together in time, where the sub-articles need the context of their parent article to be fully understood. For example, a 20-part photo gallery of a karate competition, or coverage of a conference.
diff --git a/pelican-plugins/sub_parts/__init__.py b/pelican-plugins/sub_parts/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..1dc05982abef6e62390fd506c5eca139e1c31db1
--- /dev/null
+++ b/pelican-plugins/sub_parts/__init__.py
@@ -0,0 +1 @@
+from .sub_parts import *
diff --git a/pelican-plugins/sub_parts/sub_parts.py b/pelican-plugins/sub_parts/sub_parts.py
new file mode 100644
index 0000000000000000000000000000000000000000..1e8e7481ed3acedc63f08ae223957489f140a3ea
--- /dev/null
+++ b/pelican-plugins/sub_parts/sub_parts.py
@@ -0,0 +1,66 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+from pelican import signals
+import logging
+
+logger = logging.getLogger(__name__)
+
+
+def patch_subparts(generator):
+    generator.subparts = []
+    slugs = {}
+    for article in generator.articles:
+        slugs[article.slug] = article
+        if '--' in article.slug:
+            generator.subparts.append(article)
+    for article in generator.subparts:
+        logger.info('sub_part: Detected %s', article.slug)
+        (pslug, _) = article.slug.rsplit('--', 1)
+        if pslug in slugs:
+            parent = slugs[pslug]
+            if not hasattr(parent, 'subparts'):
+                parent.subparts = []
+            parent.subparts.append(article)
+            article.subpart_of = parent
+            article.subtitle = article.title
+            article.title = article.title + ", " + parent.title
+            generator.dates.remove(article)
+            generator.articles.remove(article)
+            if article.category:
+                for cat, arts in generator.categories:
+                    if cat.name == article.category.name:
+                        arts.remove(article)
+                        break
+                else:
+                    logger.error(
+                        'sub_part: Cannot remove sub-part from category %s',
+                        article.category)
+            if (hasattr(article, 'subphotos') or
+                    hasattr(article, 'photo_gallery')):
+                parent.subphotos = (
+                    getattr(parent, 'subphotos',
+                            len(getattr(parent, 'photo_gallery', []))) +
+                    getattr(article, 'subphotos', 0) +
+                    len(getattr(article, 'photo_gallery', [])))
+        else:
+            logger.error('sub_part: No parent for %s', pslug)
+        generator._update_context(('articles', 'dates', 'subparts'))
+
+
+def write_subparts(generator, writer):
+    for article in generator.subparts:
+        signals.article_generator_write_article.send(generator,
+                                                     content=article)
+        writer.write_file(
+            article.save_as, generator.get_template(article.template),
+            generator.context, article=article, category=article.category,
+            override_output=hasattr(article, 'override_save_as'),
+            relative_urls=generator.settings['RELATIVE_URLS'])
+    if len(generator.subparts) > 0:
+        print('sub_part: processed {} sub-parts.'.format(
+            len(generator.subparts)))
+
+
+def register():
+    signals.article_generator_finalized.connect(patch_subparts)
+    signals.article_writer_finalized.connect(write_subparts)
diff --git a/pelican-plugins/sub_parts/test_data/noparent.md b/pelican-plugins/sub_parts/test_data/noparent.md
new file mode 100644
index 0000000000000000000000000000000000000000..63cfe43babccac18d188ccac5c79b95f731091e5
--- /dev/null
+++ b/pelican-plugins/sub_parts/test_data/noparent.md
@@ -0,0 +1,4 @@
+title: No parent
+tags: atag
+
+Normal article.
diff --git a/pelican-plugins/sub_parts/test_data/parent--implicit.md b/pelican-plugins/sub_parts/test_data/parent--implicit.md
new file mode 100644
index 0000000000000000000000000000000000000000..60797a996d7da4e0665f2e309ffa99e5c3f7d252
--- /dev/null
+++ b/pelican-plugins/sub_parts/test_data/parent--implicit.md
@@ -0,0 +1,4 @@
+title: Implicit sub-article
+tags: atag
+
+Sub-article based on filename as implicit slug.
diff --git a/pelican-plugins/sub_parts/test_data/parent-explicit.md b/pelican-plugins/sub_parts/test_data/parent-explicit.md
new file mode 100644
index 0000000000000000000000000000000000000000..d9b242bff94b680b5a430cd9e89d7db233911ae4
--- /dev/null
+++ b/pelican-plugins/sub_parts/test_data/parent-explicit.md
@@ -0,0 +1,5 @@
+title: Explicit sub-article
+tags: atag
+slug: parent--explicit
+
+Explicit sub-article, based on explicit slug.
diff --git a/pelican-plugins/sub_parts/test_data/parent.md b/pelican-plugins/sub_parts/test_data/parent.md
new file mode 100644
index 0000000000000000000000000000000000000000..2d0c6f8169d8ee977b3df47cb99b97cef2c18617
--- /dev/null
+++ b/pelican-plugins/sub_parts/test_data/parent.md
@@ -0,0 +1,4 @@
+title: Parent
+tags: atag
+
+Parent article with two sub-articles.
diff --git a/pelican-plugins/sub_parts/test_sub_parts.py b/pelican-plugins/sub_parts/test_sub_parts.py
new file mode 100644
index 0000000000000000000000000000000000000000..1a2f4ba51b1638be24e791d8458fa4801710132b
--- /dev/null
+++ b/pelican-plugins/sub_parts/test_sub_parts.py
@@ -0,0 +1,138 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+import os
+from shutil import rmtree
+from tempfile import mkdtemp
+
+from pelican.generators import ArticlesGenerator
+from pelican.tests.support import unittest, get_settings
+import sub_parts
+
+CUR_DIR = os.path.dirname(__file__)
+
+
+class TestSubParts(unittest.TestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        cls.temp_path = mkdtemp(prefix='pelicantests.')
+        settings = get_settings(filenames={})
+        settings['PATH'] = os.path.join(CUR_DIR, 'test_data')
+        settings['AUTHOR'] = 'Me'
+        settings['DEFAULT_DATE'] = (1970, 1, 1)
+        settings['DEFAULT_CATEGORY'] = 'Default'
+        settings['FILENAME_METADATA'] = '(?P<slug>[^.]+)'
+        settings['PLUGINS'] = [sub_parts]
+        settings['CACHE_CONTENT'] = False
+        context = settings.copy()
+        context['generated_content'] = dict()
+        context['static_links'] = set()
+        cls.generator = ArticlesGenerator(
+            context=context, settings=settings,
+            path=settings['PATH'], theme=settings['THEME'], output_path=cls.temp_path)
+        cls.generator.generate_context()
+        cls.all_articles = list(cls.generator.articles)
+        sub_parts.patch_subparts(cls.generator)
+
+    @classmethod
+    def tearDownClass(cls):
+        rmtree(cls.temp_path)
+
+    def test_all_articles(self):
+        self.assertEqual(
+            sorted(['noparent', 'parent',
+                    'parent--explicit', 'parent--implicit']),
+            sorted([a.slug for a in self.all_articles]))
+
+    def test_articles(self):
+        self.assertEqual(
+            sorted(['noparent', 'parent']),
+            sorted([a.slug for a in self.generator.articles]))
+
+    def test_dates(self):
+        self.assertEqual(
+            sorted(['noparent', 'parent']),
+            sorted([a.slug for a in self.generator.dates]))
+
+    def test_categories(self):
+        self.assertEqual(
+            sorted(['noparent', 'parent']),
+            sorted([a.slug for a in self.generator.categories[0][1]]))
+
+    def test_tags(self):
+        self.assertEqual(
+            sorted([a.slug for a in self.all_articles]),
+            sorted([a.slug for a in self.generator.tags['atag']]))
+
+    def test_authors(self):
+        self.assertEqual(
+            sorted([a.slug for a in self.all_articles]),
+            sorted([a.slug for a in self.generator.authors[0][1]]))
+
+    def test_subparts(self):
+        for a in self.all_articles:
+            if a.slug == 'parent':
+                self.assertTrue(hasattr(a, 'subparts'))
+                self.assertEqual(
+                    sorted(['parent--explicit', 'parent--implicit']),
+                    sorted([a.slug for a in a.subparts]))
+            else:
+                self.assertFalse(hasattr(a, 'subparts'))
+
+    def test_subpart_of(self):
+        for a in self.all_articles:
+            if '--' in a.slug:
+                self.assertTrue(hasattr(a, 'subpart_of'))
+                self.assertEqual('parent', a.subpart_of.slug)
+            else:
+                self.assertFalse(hasattr(a, 'subpart_of'))
+
+    def test_subtitle(self):
+        for a in self.all_articles:
+            if '--' in a.slug:
+                self.assertTrue(hasattr(a, 'subtitle'))
+                self.assertEqual(a.title,
+                                 a.subtitle + ', ' + a.subpart_of.title)
+
+
+class TestSubPartsPhotos(unittest.TestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        cls.temp_path = mkdtemp(prefix='pelicantests.')
+        settings = get_settings(filenames={})
+        settings['PATH'] = os.path.join(CUR_DIR, 'test_data')
+        settings['AUTHOR'] = 'Me'
+        settings['DEFAULT_DATE'] = (1970, 1, 1)
+        settings['DEFAULT_CATEGORY'] = 'Default'
+        settings['FILENAME_METADATA'] = '(?P<slug>[^.]+)'
+        settings['PLUGINS'] = [sub_parts]
+        settings['CACHE_CONTENT'] = False
+        context = settings.copy()
+        context['generated_content'] = dict()
+        context['static_links'] = set()
+        cls.generator = ArticlesGenerator(
+            context=context, settings=settings,
+            path=settings['PATH'], theme=settings['THEME'], output_path=cls.temp_path)
+        cls.generator.generate_context()
+        cls.all_articles = list(cls.generator.articles)
+        for a in cls.all_articles:
+            a.photo_gallery = [('i.jpg', 'i.jpg', 'it.jpg', '', '')]
+        sub_parts.patch_subparts(cls.generator)
+
+    @classmethod
+    def tearDownClass(cls):
+        rmtree(cls.temp_path)
+
+    def test_subphotos(self):
+        for a in self.all_articles:
+            if a.slug == 'parent':
+                self.assertTrue(hasattr(a, 'subphotos'))
+                self.assertEqual(3, a.subphotos)
+            else:
+                self.assertFalse(hasattr(a, 'subphotos'))
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/pelican-plugins/subcategory/README.md b/pelican-plugins/subcategory/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..9ec1f8ec94b471257dd2a04cb1549238abd01c04
--- /dev/null
+++ b/pelican-plugins/subcategory/README.md
@@ -0,0 +1,100 @@
+# Subcategory Plugin
+
+This plugin adds support for subcategories in addition to article categories.
+
+Subcategories are hierarchical. Each subcategory has a parent, which is either a regular category or another subcategory.
+
+Feeds can be generated for each subcategory, just like categories and tags.
+
+## Usage
+
+### Metadata
+
+Subcategories are an extension to categories. Add subcategories to an article's category metadata using a `/` like this:
+
+```
+Category: Regular Category/Sub-Category/Sub-Sub-category
+```
+
+Then create a `subcategory.html` template in your theme, similar to the `category.html` or `tag.html` templates.
+
+In your templates, `article.category` continues to act the same way. Your subcategories are stored in the `articles.subcategories` list. To create breadcrumb-style navigation you might try something like this:
+
+```
+<nav class="breadcrumb">
+<ol>
+    <li>
+        <a href="{{ SITEURL }}/{{ article.category.url }}">{{ article.category}}</a>
+    </li>
+{% for subcategory in article.subcategories %}
+    <li>
+        <a href="{{ SITEURL }}/{{ subcategory.url }}">{{ subcategory.shortname }}</a>
+    </li>
+{% endfor %}
+</ol>
+</nav>
+```
+
+### Subcategory folders
+
+To specify subcategories using folders you can configure `PATH_METADATA`<br>
+to extract the article path (containing all category and subcategory folders) into the `subcategory_path` metadata. The following settings would use all available subcategories for the hierarchy:
+
+```
+PATH_METADATA= '(?P<subcategory_path>.*)/.*'
+```
+
+You can limit the depth of generated subcategories by adjusting the regular expression to only include a specific number of path separators (`/`). For example, the following would generate only a single level of subcategories regardless of the folder tree depth:
+
+```
+PATH_METADATA= '(?P<subcategory_path>[^/]*/[^/]*)/.*'
+```
+
+## Subcategory Names
+
+Each subcategory's full name is a `/`-separated list of it parents and itself. This is necessary to keep each subcategory unique. It means you can have `Category 1/Foo` and `Category 2/Foo` and they won't interfere with each other. Each subcategory has an attribute `shortname` which is just the name without its parents associated. For example if you had...
+
+```
+Category/Sub Category1/Sub Category2
+```
+
+... the full name for Sub Category2 would be `Category/Sub Category1/Sub Category2` and the "short name" would be `Sub Category2`.
+
+If you need to use the slug, it is generated from the short name -- not the full name.
+
+## Settings
+
+Consistent with the default settings for Tags and Categories, the default settings for subcategories are:
+
+```
+'SUBCATEGORY_SAVE_AS' = os.path.join('subcategory', '{savepath}.html')
+'SUBCATEGORY_URL' = 'subcategory/(fullurl).html'
+```
+
+`savepath` and `fullurl` are generated recursively, using slugs. So the full URL would be:
+
+```
+category-slug/sub-category-slug/sub-sub-category-slug
+```
+
+... with `savepath` being similar but joined using `os.path.join`.
+
+Similarly, you can save subcategory feeds by adding one of the following to your Pelican configuration file:
+
+```
+SUBCATEGORY_FEED_ATOM = 'feeds/%s.atom.xml'
+SUBCATEGORY_FEED_RSS = 'feeds/%s.rss.xml'
+```
+
+... and this will create a feed with `fullurl` of the subcategory. For example:
+
+```
+feeds/category/subcategory.atom.xml
+```
+
+Article urls can also use the values of `subpath` and `suburl` in their definitions. These are equivalent to the `fullurl` and `savepath` of the most specific subcategory. If you have articles that don't have subcategories these values are set to the category slug.
+
+```
+ARTICLE_SAVE_AS = os.path.join('{subpath}' 'articles' '{slug}.html')
+ARTICLE_URL = '{suburl}/articles/{slug}.html'
+```
diff --git a/pelican-plugins/subcategory/__init__.py b/pelican-plugins/subcategory/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..9999cf1ddb1972dbe7ed8cbeaa979d0daa8e2fa6
--- /dev/null
+++ b/pelican-plugins/subcategory/__init__.py
@@ -0,0 +1 @@
+from .subcategory import *
diff --git a/pelican-plugins/subcategory/subcategory.py b/pelican-plugins/subcategory/subcategory.py
new file mode 100644
index 0000000000000000000000000000000000000000..31e73ffc167c2839fdf76d5177283881c1625c73
--- /dev/null
+++ b/pelican-plugins/subcategory/subcategory.py
@@ -0,0 +1,135 @@
+# -*- coding: utf-8 -*-
+"""
+@Author: Alistair Magee
+
+Adds support for subcategories on pelican articles
+"""
+import os
+from operator import attrgetter
+from functools import partial
+
+from pelican import signals
+from pelican.urlwrappers import URLWrapper, Category
+from pelican.utils import slugify
+
+from six import text_type
+
+class SubCategory(URLWrapper):
+    def __init__(self, name, parent, settings):
+        super(SubCategory, self).__init__(name, settings)
+        self.parent = parent
+        self.shortname = name.split('/')
+        self.shortname = self.shortname.pop()
+        self.slug = slugify(self.shortname, settings.get('SLUG_SUBSTITUIONS', ()))
+        if isinstance(self.parent, SubCategory):
+            self.savepath = os.path.join(self.parent.savepath, self.slug)
+            self.fullurl = '{}/{}'.format(self.parent.fullurl, self.slug)
+        else: #parent is a category
+            self.savepath = os.path.join(self.parent.slug, self.slug)
+            self.fullurl = '{}/{}'.format(self.parent.slug, self.slug)
+        
+    def as_dict(self):
+        d = self.__dict__
+        d['shortname'] = self.shortname
+        d['savepath'] = self.savepath
+        d['fullurl'] = self.fullurl
+        d['parent'] = self.parent
+        return d
+
+    def __hash__(self):
+        return hash(self.fullurl)
+
+    def _key(self):
+        return self.fullurl
+
+def get_subcategories(generator, metadata):
+    if 'SUBCATEGORY_SAVE_AS' not in generator.settings:
+        generator.settings['SUBCATEGORY_SAVE_AS'] = os.path.join( 
+                'subcategory', '{savepath}.html')
+    if 'SUBCATEGORY_URL' not in generator.settings:
+        generator.settings['SUBCATEGORY_URL'] = 'subcategory/{fullurl}.html'
+    if ('PAGINATED_TEMPLATES' in generator.settings and
+        'subcategory' not in generator.settings['PAGINATED_TEMPLATES']):
+        generator.settings['PAGINATED_TEMPLATES']['subcategory'] = None
+
+    if 'subcategory_path' in metadata:
+        category_list = text_type(metadata.get('subcategory_path')).split('/')
+    else:
+        category_list = text_type(metadata.get('category')).split('/')
+
+    category = (category_list.pop(0)).strip()
+    category = Category(category, generator.settings)
+    metadata['category'] = category
+    #generate a list of subcategories with their parents
+    sub_list = []
+    parent = category.name
+    for subcategory in category_list:
+        subcategory = parent + '/' + subcategory.strip()
+        sub_list.append(subcategory)
+        parent = subcategory
+    metadata['subcategories'] = sub_list
+
+def create_subcategories(generator):
+    generator.subcategories = []
+    for article in generator.articles:
+        parent = article.category
+        actual_subcategories = []
+        for subcategory in article.subcategories:
+            #following line returns a list of items, tuples in this case
+            sub_cat = [item for item in generator.subcategories 
+                    if item[0].name == subcategory]
+            if sub_cat:
+                sub_cat[0][1].append(article)
+                parent = sub_cat[0][0]
+                actual_subcategories.append(parent)
+            else:
+                new_sub = SubCategory(subcategory, parent, generator.settings)
+                generator.subcategories.append((new_sub, [article,]))
+                parent = new_sub
+                actual_subcategories.append(parent)
+        article.subcategories = actual_subcategories
+        """Add subpath and suburl to the article metadata. This allows the
+        the last subcategory's fullurl and savepath to be used when definining
+        Article URL's. If an article has no subcategories, the Category slug
+        is used instead
+        """
+        try:
+            last_subcat = article.subcategories[-1]
+            article.metadata['subpath'] = last_subcat.savepath
+            article.metadata['suburl'] = last_subcat.fullurl
+        except IndexError: #No Subcategory
+            article.metadata['subpath'] = article.category.slug
+            article.metadata['suburl'] = article.category.slug
+
+def generate_subcategories(generator, writer):
+    write = partial(writer.write_file,
+            relative_urls=generator.settings['RELATIVE_URLS'])
+    subcategory_template = generator.get_template('subcategory')
+    for subcat, articles in generator.subcategories:
+        articles.sort(key=attrgetter('date'), reverse=True)
+        dates = [article for article in generator.dates if article in articles]
+        write(subcat.save_as, subcategory_template, generator.context, 
+                subcategory=subcat, articles=articles, dates=dates, 
+                template_name='subcategory', page_name=subcat.page_name,
+                all_articles=generator.articles)
+
+def generate_subcategory_feeds(generator, writer):
+    for subcat, articles in generator.subcategories:
+        articles.sort(key=attrgetter('date'), reverse=True)
+        if generator.settings.get('SUBCATEGORY_FEED_ATOM'):
+            writer.write_feed(articles, generator.context,
+                    generator.settings['SUBCATEGORY_FEED_ATOM']
+                    % subcat.fullurl)
+        if generator.settings.get('SUBCATEGORY_FEED_RSS'):
+            writer.write_feed(articles, generator.context,
+                    generator.settings['SUBCATEGORY_FEED_RSS']
+                    % subcat.fullurl, feed_type='rss')
+
+def generate(generator, writer):
+    generate_subcategory_feeds(generator, writer)
+    generate_subcategories(generator, writer)
+
+def register():
+    signals.article_generator_context.connect(get_subcategories)
+    signals.article_generator_finalized.connect(create_subcategories)
+    signals.article_writer_finalized.connect(generate)
diff --git a/pelican-plugins/summary/Readme.rst b/pelican-plugins/summary/Readme.rst
new file mode 100644
index 0000000000000000000000000000000000000000..a52b0253e9bdb9ec548d75452a0cdca967613a89
--- /dev/null
+++ b/pelican-plugins/summary/Readme.rst
@@ -0,0 +1,63 @@
+Requirements
+------------
+
+This plugin requires beautifulsoup4 to ensure correctness of the extracted markup::
+
+    pip install beautifulsoup4
+
+Summary
+-------
+
+This plugin allows easy, variable length summaries directly embedded into the
+body of your articles. It introduces two new settings: ``SUMMARY_BEGIN_MARKER``
+and ``SUMMARY_END_MARKER``: strings which can be placed directly into an article
+to mark the beginning and end of a summary. When found, the standard
+``SUMMARY_MAX_LENGTH`` setting will be ignored. The markers themselves will also
+be removed from your articles before they are published. The default values
+are ``<!-- PELICAN_BEGIN_SUMMARY -->`` and ``<!-- PELICAN_END_SUMMARY -->``.
+For example::
+
+    Title: My super title
+    Date: 2010-12-03 10:20
+    Tags: thats, awesome
+    Category: yeah
+    Slug: my-super-post
+    Author: Alexis Metaireau
+
+    This is the content of my super blog post.
+    <!-- PELICAN_END_SUMMARY -->
+    and this content occurs after the summary.
+
+Here, the summary is taken to be the first line of the post. Because no
+beginning marker was found, it starts at the top of the body. It is possible
+to leave out the end marker instead, in which case the summary will start at the
+beginning marker and continue to the end of the body.
+
+If no beginning or end marker is found, and if ``SUMMARY_USE_FIRST_PARAGRAPH``
+is enabled in the settings, the summary will be the first paragraph of the post.
+
+The plugin also sets a ``has_summary`` attribute on every article. It is True
+for articles with an explicitly-defined summary, and False otherwise.  (It is
+also False for an article truncated by ``SUMMARY_MAX_LENGTH``.)  Your templates
+can use this e.g. to add a link to the full text at the end of the summary.
+
+reST example
+~~~~~~~~~~~~
+
+Inserting the markers into a reStructuredText document makes use of the
+comment directive, because raw HTML is automatically escaped. The reST equivalent of the above Markdown example looks like this::
+
+    My super title
+    ##############
+
+    :date: 2010-12-03 10:20
+    :tags: thats, awesome
+    :category: yeah
+    :slug: my-super-post
+    :author: Alexis Metaireau
+
+    This is the content of my super blog post.
+
+    .. PELICAN_END_SUMMARY
+
+    and this content occurs after the summary.
diff --git a/pelican-plugins/summary/__init__.py b/pelican-plugins/summary/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..afe9311f2db2c0b03d3d3cf75df4d532433c5e7b
--- /dev/null
+++ b/pelican-plugins/summary/__init__.py
@@ -0,0 +1 @@
+from .summary import *
diff --git a/pelican-plugins/summary/requirements.txt b/pelican-plugins/summary/requirements.txt
new file mode 100644
index 0000000000000000000000000000000000000000..c1f5f713cdafc4d0a904e0c158ccb14a0da6a445
--- /dev/null
+++ b/pelican-plugins/summary/requirements.txt
@@ -0,0 +1 @@
+beautifulsoup4
diff --git a/pelican-plugins/summary/summary.py b/pelican-plugins/summary/summary.py
new file mode 100644
index 0000000000000000000000000000000000000000..26f1d37ede9e47f07daa2fe4c5b81ca332248818
--- /dev/null
+++ b/pelican-plugins/summary/summary.py
@@ -0,0 +1,111 @@
+"""
+Summary
+-------
+
+This plugin allows easy, variable length summaries directly embedded into the
+body of your articles.
+"""
+
+from __future__ import unicode_literals
+from bs4 import BeautifulSoup
+from pelican import signals
+from pelican.generators import ArticlesGenerator, StaticGenerator, PagesGenerator
+import re
+
+def initialized(pelican):
+    from pelican.settings import DEFAULT_CONFIG
+    DEFAULT_CONFIG.setdefault('SUMMARY_BEGIN_MARKER',
+                              '<!-- PELICAN_BEGIN_SUMMARY -->')
+    DEFAULT_CONFIG.setdefault('SUMMARY_END_MARKER',
+                              '<!-- PELICAN_END_SUMMARY -->')
+    DEFAULT_CONFIG.setdefault('SUMMARY_USE_FIRST_PARAGRAPH', False)
+    if pelican:
+        pelican.settings.setdefault('SUMMARY_BEGIN_MARKER',
+                                    '<!-- PELICAN_BEGIN_SUMMARY -->')
+        pelican.settings.setdefault('SUMMARY_END_MARKER',
+                                    '<!-- PELICAN_END_SUMMARY -->')
+        pelican.settings.setdefault('SUMMARY_USE_FIRST_PARAGRAPH', False)
+
+def extract_summary(instance):
+    # if summary is already specified, use it
+    # if there is no content, there's nothing to do
+    if hasattr(instance, '_summary') or 'summary' in instance.metadata:
+        instance.has_summary = True
+        return
+
+    if not instance._content:
+        instance.has_summary = False
+        return
+
+    begin_marker = instance.settings['SUMMARY_BEGIN_MARKER']
+    end_marker   = instance.settings['SUMMARY_END_MARKER']
+    use_first_paragraph = instance.settings['SUMMARY_USE_FIRST_PARAGRAPH']
+    remove_markers = True
+
+    content = instance._update_content(instance._content, instance.settings['SITEURL'])
+    begin_summary = -1
+    end_summary = -1
+    if begin_marker:
+        begin_summary = content.find(begin_marker)
+    if end_marker:
+        end_summary = content.find(end_marker)
+
+    if begin_summary == -1 and end_summary == -1 and use_first_paragraph:
+        begin_marker, end_marker = '<p>', '</p>'
+        remove_markers = False
+        begin_summary = content.find(begin_marker)
+        end_summary = content.find(end_marker)
+
+    if begin_summary == -1 and end_summary == -1:
+        instance.has_summary = False
+        return
+
+    # skip over the begin marker, if present
+    if begin_summary == -1:
+        begin_summary = 0
+    else:
+        begin_summary = begin_summary + len(begin_marker)
+
+    if end_summary == -1:
+        end_summary = None
+
+    summary = content[begin_summary:end_summary]
+
+    if remove_markers:
+        # remove the markers from the content
+        if begin_summary:
+            content = content.replace(begin_marker, '', 1)
+        if end_summary:
+            content = content.replace(end_marker, '', 1)
+
+    summary = str(BeautifulSoup(summary, 'html.parser'))
+
+    instance._content = content
+    # default_status was added to Pelican Content objects after 3.7.1.
+    # Its use here is strictly to decide on how to set the summary.
+    # There's probably a better way to do this but I couldn't find it.
+    if hasattr(instance, 'default_status'):
+        instance.metadata['summary'] = summary
+    else:
+        instance._summary = summary
+    instance.has_summary = True
+
+
+def run_plugin(generators):
+    for generator in generators:
+        if isinstance(generator, ArticlesGenerator):
+            for article in generator.articles:
+                extract_summary(article)
+        elif isinstance(generator, PagesGenerator):
+            for page in generator.pages:
+                extract_summary(page)
+
+
+def register():
+    signals.initialized.connect(initialized)
+    try:
+        signals.all_generators_finalized.connect(run_plugin)
+    except AttributeError:
+        # NOTE: This results in #314 so shouldn't really be relied on
+        # https://github.com/getpelican/pelican-plugins/issues/314
+        signals.content_object_init.connect(extract_summary)
diff --git a/pelican-plugins/summary/test_summary.py b/pelican-plugins/summary/test_summary.py
new file mode 100644
index 0000000000000000000000000000000000000000..dd366e2ea2c2127f629d1dda445fbfe3ce6f9ca7
--- /dev/null
+++ b/pelican-plugins/summary/test_summary.py
@@ -0,0 +1,110 @@
+# -*- coding: utf-8 -*-
+
+import unittest
+
+from jinja2.utils import generate_lorem_ipsum
+
+# generate one paragraph, enclosed with <p>
+TEST_CONTENT = str(generate_lorem_ipsum(n=1))
+TEST_SUMMARY = generate_lorem_ipsum(n=1, html=False)
+
+
+from pelican.contents import Page
+import pelican.settings
+
+import summary
+
+class TestSummary(unittest.TestCase):
+    def setUp(self):
+        super(TestSummary, self).setUp()
+        pelican.settings.DEFAULT_CONFIG['SUMMARY_MAX_LENGTH'] = None
+        pelican.settings.DEFAULT_CONFIG['SUMMARY_USE_FIRST_PARAGRAPH'] = False
+
+        summary.register()
+        summary.initialized(None)
+        self.page_kwargs = {
+            'content': TEST_CONTENT,
+            'context': {
+                'localsiteurl': '',
+            },
+            'metadata': {
+                'summary': TEST_SUMMARY,
+                'title': 'foo bar',
+                'author': 'Blogger',
+            },
+        }
+
+    def _copy_page_kwargs(self):
+        # make a deep copy of page_kwargs
+        page_kwargs = dict([(key, self.page_kwargs[key]) for key in
+                            self.page_kwargs])
+        for key in page_kwargs:
+            if not isinstance(page_kwargs[key], dict):
+                break
+            page_kwargs[key] = dict([(subkey, page_kwargs[key][subkey])
+                                     for subkey in page_kwargs[key]])
+
+        return page_kwargs
+
+    def test_end_summary(self):
+        page_kwargs = self._copy_page_kwargs()
+        del page_kwargs['metadata']['summary']
+        page_kwargs['content'] = (
+            TEST_SUMMARY + '<!-- PELICAN_END_SUMMARY -->' + TEST_CONTENT)
+        page = Page(**page_kwargs)
+        summary.extract_summary(page)
+        # test both the summary and the marker removal
+        self.assertEqual(page.summary, TEST_SUMMARY)
+        self.assertEqual(page.content, TEST_SUMMARY + TEST_CONTENT)
+
+    def test_begin_summary(self):
+        page_kwargs = self._copy_page_kwargs()
+        del page_kwargs['metadata']['summary']
+        page_kwargs['content'] = (
+            'FOOBAR<!-- PELICAN_BEGIN_SUMMARY -->' + TEST_CONTENT)
+        page = Page(**page_kwargs)
+        summary.extract_summary(page)
+        # test both the summary and the marker removal
+        self.assertEqual(page.summary, TEST_CONTENT)
+        self.assertEqual(page.content, 'FOOBAR' + TEST_CONTENT)
+
+    def test_begin_end_summary(self):
+        page_kwargs = self._copy_page_kwargs()
+        del page_kwargs['metadata']['summary']
+        page_kwargs['content'] = (
+                'FOOBAR<!-- PELICAN_BEGIN_SUMMARY -->' + TEST_SUMMARY +
+                '<!-- PELICAN_END_SUMMARY -->' + TEST_CONTENT)
+        page = Page(**page_kwargs)
+        summary.extract_summary(page)
+        # test both the summary and the marker removal
+        self.assertEqual(page.summary, TEST_SUMMARY)
+        self.assertEqual(page.content, 'FOOBAR' + TEST_SUMMARY + TEST_CONTENT)
+
+    def test_use_first_paragraph(self):
+        page_kwargs = self._copy_page_kwargs()
+        del page_kwargs['metadata']['summary']
+        pelican.settings.DEFAULT_CONFIG['SUMMARY_USE_FIRST_PARAGRAPH'] = True
+        page_kwargs['content'] = '<p>' + TEST_SUMMARY + '</p>' + TEST_CONTENT
+        page = Page(**page_kwargs)
+        summary.extract_summary(page)
+        # test both the summary and the marker removal
+        self.assertEqual(page.summary, TEST_SUMMARY)
+        self.assertEqual(page.content, '<p>' + TEST_SUMMARY + '</p>' + TEST_CONTENT)
+
+    def test_correct_malformed_markup(self):
+        page_kwargs = self._copy_page_kwargs()
+        del page_kwargs['metadata']['summary']
+        malformed = '<article><div><h2>Title</h2><p>Some content</article>'
+        wellformed = (
+            '<article><div><h2>Title</h2>'
+            '<p>Some content</p></div></article>')
+        page_kwargs['content'] = (
+            '<!-- PELICAN_BEGIN_SUMMARY -->' + malformed +
+            '<!-- PELICAN_END_SUMMARY -->')
+        page = Page(**page_kwargs)
+        summary.extract_summary(page)
+        self.assertEqual(page.summary, wellformed)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/pelican-plugins/tag_cloud/README.rst b/pelican-plugins/tag_cloud/README.rst
new file mode 100644
index 0000000000000000000000000000000000000000..5f0590d31c5f8ce57991e8cf047ce0b3c98d7ca6
--- /dev/null
+++ b/pelican-plugins/tag_cloud/README.rst
@@ -0,0 +1,100 @@
+tag_cloud
+=========
+
+**NOTE:** `This plugin has been moved to its own repository <https://github.com/pelican-plugins/tag-cloud>`_. Please file any issues/PRs there. Once all plugins have been migrated to the `new Pelican Plugins organization <https://github.com/pelican-plugins>`_, this monolithic repository will be archived.
+
+This plugin generates a tag-cloud.
+
+Installation
+------------
+
+In order to use to use this plugin, you have to edit(*) or create(+) the following files::
+
+      blog/
+        ├── pelicanconf.py *
+        ├── content
+        ├── plugins +
+        │     └── tag_cloud.py +
+        └── themes
+              └── mytheme
+                    ├── templates
+                    │      └── base.html *
+                    └── static
+                          └── css
+                               └── style.css *
+
+In **pelicanconf.py** you have to activate the plugin::
+
+    PLUGIN_PATHS = ["plugins"]
+    PLUGINS = ["tag_cloud"]
+
+Into your **plugins** folder, you should add tag_cloud.py (from this repository).
+
+In your theme files, you should change **base.html** to apply formats (and sizes) defined in **style.css**, as specified in "Settings", below.
+
+Settings
+--------
+
+================================================    =====================================================
+Setting name (followed by default value)            What does it do?
+================================================    =====================================================
+``TAG_CLOUD_STEPS = 4``                             Count of different font sizes in the tag
+                                                    cloud.
+``TAG_CLOUD_MAX_ITEMS = 100``                       Maximum number of tags in the cloud.
+``TAG_CLOUD_SORTING = 'random'``                    The tag cloud ordering scheme.  Valid values:
+                                                    random, alphabetically, alphabetically-rev, size and
+                                                    size-rev
+``TAG_CLOUD_BADGE = True``                          Optionnal setting : can bring **badges**, which mean
+                                                    say : display the number of each tags present
+                                                    on all articles.
+================================================    =====================================================
+
+The default theme does not include a tag cloud, but it is pretty easy to add one::
+
+    <ul class="tagcloud">
+        {% for tag in tag_cloud %}
+            <li class="tag-{{ tag.1 }}">
+                <a href="{{ SITEURL }}/{{ tag.0.url }}">
+                {{ tag.0 }}
+                    {% if TAG_CLOUD_BADGE %}
+                        <span class="badge">{{ tag.2 }}</span>
+                    {% endif %}
+                </a>
+            </li>
+        {% endfor %}
+    </ul>
+
+You should then also define CSS styles with appropriate classes (tag-1 to tag-N,
+where N matches ``TAG_CLOUD_STEPS``), tag-1 being the most frequent, and
+define a ``ul.tagcloud`` class with appropriate list-style to create the cloud.
+You should copy/paste this **badge** CSS rule ``ul.tagcloud .list-group-item <span>.badge``
+if you're using ``TAG_CLOUD_BADGE`` setting. (this rule, potentially long , is suggested to avoid
+conflicts with CSS libs as twitter Bootstrap)
+
+For example::
+
+    ul.tagcloud {
+      list-style: none;
+        padding: 0;
+    }
+
+    ul.tagcloud li {
+        display: inline-block;
+    }
+
+    li.tag-1 {
+        font-size: 150%;
+    }
+
+    li.tag-2 {
+        font-size: 120%;
+    }
+
+    /* ... add li.tag-3 etc, as much as needed */
+
+    ul.tagcloud .list-group-item span.badge {
+        background-color: grey;
+        color: white;
+    }
+
+By default the tags in the cloud are sorted randomly, but if you prefers to have it alphabetically use the `alphabetically` (ascending) and `alphabetically-rev` (descending). Also is possible to sort the tags by it's size (number of articles with this specific tag) using the values `size` (ascending) and `size-rev` (descending).
diff --git a/pelican-plugins/tag_cloud/__init__.py b/pelican-plugins/tag_cloud/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..a7004f504c8bb1f1c168b97633231099752a0f56
--- /dev/null
+++ b/pelican-plugins/tag_cloud/__init__.py
@@ -0,0 +1,2 @@
+from .tag_cloud import *
+
diff --git a/pelican-plugins/tag_cloud/tag_cloud.py b/pelican-plugins/tag_cloud/tag_cloud.py
new file mode 100644
index 0000000000000000000000000000000000000000..bf91055effe3fe33478283cb1dac6ce3bfeb65de
--- /dev/null
+++ b/pelican-plugins/tag_cloud/tag_cloud.py
@@ -0,0 +1,90 @@
+'''
+tag_cloud
+===================================
+
+This plugin generates a tag cloud from available tags
+'''
+from __future__ import unicode_literals
+
+from collections import defaultdict
+from operator import itemgetter
+
+import logging
+import math
+import random
+
+from pelican import signals
+
+logger = logging.getLogger(__name__)
+
+
+def set_default_settings(settings):
+    settings.setdefault('TAG_CLOUD_STEPS', 4)
+    settings.setdefault('TAG_CLOUD_MAX_ITEMS', 100)
+    settings.setdefault('TAG_CLOUD_SORTING', 'random')
+    settings.setdefault('TAG_CLOUD_BADGE', False)
+
+
+def init_default_config(pelican):
+    from pelican.settings import DEFAULT_CONFIG
+    set_default_settings(DEFAULT_CONFIG)
+    if(pelican):
+            set_default_settings(pelican.settings)
+
+
+def generate_tag_cloud(generator):
+    tag_cloud = defaultdict(int)
+    for article in generator.articles:
+        for tag in getattr(article, 'tags', []):
+            tag_cloud[tag] += 1
+
+    tag_cloud = sorted(tag_cloud.items(), key=itemgetter(1), reverse=True)
+    tag_cloud = tag_cloud[:generator.settings.get('TAG_CLOUD_MAX_ITEMS')]
+
+    tags = list(map(itemgetter(1), tag_cloud))
+    if tags:
+        max_count = tags[0]
+        min_count = tags[-1]
+    steps = generator.settings.get('TAG_CLOUD_STEPS')
+
+    # calculate word sizes
+    def generate_tag(tag, count):
+        tag = (
+            tag,
+            int(math.floor(steps - (steps - 1) * math.log(count - min_count + 1)
+                / (math.log(max_count - min_count + 1) or 1)))
+        )
+        if generator.settings.get('TAG_CLOUD_BADGE'):
+            tag += (count,)
+        return tag
+
+    tag_cloud = [
+        generate_tag(tag, count)
+        for tag, count in tag_cloud
+    ]
+
+    sorting = generator.settings.get('TAG_CLOUD_SORTING')
+
+    if sorting == 'alphabetically':
+        tag_cloud.sort(key=lambda elem: elem[0].name)
+    elif sorting == 'alphabetically-rev':
+        tag_cloud.sort(key=lambda elem: elem[0].name, reverse=True)
+    elif sorting == 'size':
+        tag_cloud.sort(key=lambda elem: elem[1])
+    elif sorting == 'size-rev':
+        tag_cloud.sort(key=lambda elem: elem[1], reverse=True)
+    elif sorting == 'random':
+        random.shuffle(tag_cloud)
+    else:
+        logger.warning("setting for TAG_CLOUD_SORTING not recognized: %s, "
+                       "falling back to 'random'", sorting)
+        random.shuffle(tag_cloud)
+
+    # make available in context
+    generator.tag_cloud = tag_cloud
+    generator._update_context(['tag_cloud'])
+
+
+def register():
+    signals.initialized.connect(init_default_config)
+    signals.article_generator_finalized.connect(generate_tag_cloud)
diff --git a/pelican-plugins/tag_cloud/test_data/article_1.md b/pelican-plugins/tag_cloud/test_data/article_1.md
new file mode 100644
index 0000000000000000000000000000000000000000..bb44efefdcbfd8eb718ccc36eeaa2cc4796b621f
--- /dev/null
+++ b/pelican-plugins/tag_cloud/test_data/article_1.md
@@ -0,0 +1,4 @@
+Title: Article1
+tags: fun, pelican, plugins
+
+content, yeah!
\ No newline at end of file
diff --git a/pelican-plugins/tag_cloud/test_data/article_2.md b/pelican-plugins/tag_cloud/test_data/article_2.md
new file mode 100644
index 0000000000000000000000000000000000000000..74dbb6346f3acce2570ba870e4656b13727f8375
--- /dev/null
+++ b/pelican-plugins/tag_cloud/test_data/article_2.md
@@ -0,0 +1,5 @@
+Title: Article2
+tags: pelican, plugins, python
+
+content2, yeah!
+
diff --git a/pelican-plugins/tag_cloud/test_data/article_3.md b/pelican-plugins/tag_cloud/test_data/article_3.md
new file mode 100644
index 0000000000000000000000000000000000000000..bc0cd5aa1f9ff807ceec20bf7a10aa5d3362c920
--- /dev/null
+++ b/pelican-plugins/tag_cloud/test_data/article_3.md
@@ -0,0 +1,5 @@
+Title: Article3
+tags: pelican, plugins
+
+content3, yeah!
+
diff --git a/pelican-plugins/tag_cloud/test_data/article_4.md b/pelican-plugins/tag_cloud/test_data/article_4.md
new file mode 100644
index 0000000000000000000000000000000000000000..9a3132062427e4420ce692b7e5722a1be0d6ab2f
--- /dev/null
+++ b/pelican-plugins/tag_cloud/test_data/article_4.md
@@ -0,0 +1,5 @@
+Title: Article4
+tags: pelican, fun
+
+content4, yeah!
+
diff --git a/pelican-plugins/tag_cloud/test_data/article_5.md b/pelican-plugins/tag_cloud/test_data/article_5.md
new file mode 100644
index 0000000000000000000000000000000000000000..1d3f2ffa72d378575489bf9c01674859ecef514a
--- /dev/null
+++ b/pelican-plugins/tag_cloud/test_data/article_5.md
@@ -0,0 +1,5 @@
+Title: Article5
+tags: plugins, pelican, fun
+
+content5, yeah!
+
diff --git a/pelican-plugins/tag_cloud/test_tag_cloud.py b/pelican-plugins/tag_cloud/test_tag_cloud.py
new file mode 100644
index 0000000000000000000000000000000000000000..22fbf0ae35943b4fe2ed03111ba2b7b59188df76
--- /dev/null
+++ b/pelican-plugins/tag_cloud/test_tag_cloud.py
@@ -0,0 +1,114 @@
+import unittest
+import os
+import six
+import tag_cloud
+from shutil import rmtree
+from tempfile import mkdtemp
+
+from pelican.generators import ArticlesGenerator
+from pelican.tests.support import get_settings
+from pelican.urlwrappers import Tag
+
+CUR_DIR = os.path.dirname(__file__)
+CONTENT_DIR = os.path.join(CUR_DIR, 'test_data')
+
+
+class TestTagCloudGeneration(unittest.TestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        cls.temp_path = mkdtemp(prefix='pelicantests.')
+
+        cls._settings = get_settings(filenames={})
+        cls._settings['DEFAULT_CATEGORY'] = 'Default'
+        cls._settings['DEFAULT_DATE'] = (1970, 1, 1)
+        cls._settings['READERS'] = {'asc': None}
+        cls._settings['CACHE_CONTENT'] = False
+        tag_cloud.set_default_settings(cls._settings)
+
+        context = cls._settings.copy()
+        context['generated_content'] = dict()
+        context['static_links'] = set()
+        cls.generator = ArticlesGenerator(
+            context=context, settings=cls._settings,
+            path=CONTENT_DIR, theme=cls._settings['THEME'], output_path=cls.temp_path)
+        cls.generator.generate_context()
+
+    @classmethod
+    def tearDownClass(cls):
+        rmtree(cls.temp_path)
+
+    def test_tag_cloud_random(self):
+        self.generator.settings['TAG_CLOUD_STEPS'] = 10
+        self.generator.settings['TAG_CLOUD_BADGE'] = False
+        tag_cloud.generate_tag_cloud(self.generator)
+        expected = [
+            (Tag('pelican', self._settings), 1),
+            (Tag('plugins', self._settings), 2),
+            (Tag('fun', self._settings), 3),
+            (Tag('python', self._settings), 10)
+        ]
+        six.assertCountEqual(self, self.generator.tag_cloud, expected)
+
+    def test_tag_cloud_badge(self):
+        self.generator.settings['TAG_CLOUD_STEPS'] = 10
+        self.generator.settings['TAG_CLOUD_BADGE'] = True
+        tag_cloud.generate_tag_cloud(self.generator)
+        expected = [
+            (Tag('pelican', self._settings), 1, 5),
+            (Tag('plugins', self._settings), 2, 4),
+            (Tag('fun', self._settings), 3, 3),
+            (Tag('python', self._settings), 10, 1)
+        ]
+        six.assertCountEqual(self, self.generator.tag_cloud, expected)
+
+    def test_tag_cloud_alphabetical(self):
+        self.generator.settings['TAG_CLOUD_STEPS'] = 10
+        self.generator.settings['TAG_CLOUD_SORTING'] = 'alphabetically'
+        tag_cloud.generate_tag_cloud(self.generator)
+        expected = [
+            (Tag('fun', self._settings), 3),
+            (Tag('pelican', self._settings), 1),
+            (Tag('plugins', self._settings), 2),
+            (Tag('python', self._settings), 10)
+        ]
+        self.assertEqual(self.generator.tag_cloud, expected)
+
+    def test_tag_cloud_alphabetical_rev(self):
+        self.generator.settings['TAG_CLOUD_STEPS'] = 10
+        self.generator.settings['TAG_CLOUD_SORTING'] = 'alphabetically-rev'
+        tag_cloud.generate_tag_cloud(self.generator)
+        expected = [
+            (Tag('python', self._settings), 10),
+            (Tag('plugins', self._settings), 2),
+            (Tag('pelican', self._settings), 1),
+            (Tag('fun', self._settings), 3)
+        ]
+        self.assertEqual(self.generator.tag_cloud, expected)
+
+    def test_tag_cloud_size(self):
+        self.generator.settings['TAG_CLOUD_STEPS'] = 10
+        self.generator.settings['TAG_CLOUD_SORTING'] = 'size'
+        tag_cloud.generate_tag_cloud(self.generator)
+        expected = [
+            (Tag('pelican', self._settings), 1),
+            (Tag('plugins', self._settings), 2),
+            (Tag('fun', self._settings), 3),
+            (Tag('python', self._settings), 10)
+        ]
+        self.assertEqual(self.generator.tag_cloud, expected)
+
+    def test_tag_cloud_size_rev(self):
+        self.generator.settings['TAG_CLOUD_STEPS'] = 10
+        self.generator.settings['TAG_CLOUD_SORTING'] = 'size-rev'
+        tag_cloud.generate_tag_cloud(self.generator)
+        expected = [
+            (Tag('python', self._settings), 10),
+            (Tag('fun', self._settings), 3),
+            (Tag('plugins', self._settings), 2),
+            (Tag('pelican', self._settings), 1)
+        ]
+        self.assertEqual(self.generator.tag_cloud, expected)
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/pelican-plugins/test_data/Readme.rst b/pelican-plugins/test_data/Readme.rst
new file mode 100644
index 0000000000000000000000000000000000000000..9ed244d676df4ce9000a78db11f96eeddc02ed25
--- /dev/null
+++ b/pelican-plugins/test_data/Readme.rst
@@ -0,0 +1,13 @@
+Test Data
+---------
+
+Place tests for your plugin here. ``test_data`` folder contains following
+common data for your tests, if you need them. 
+
+===============   ===========================
+File/Folder       Description
+===============   ===========================
+content           A sample content folder
+themes            Default themes from Pelican
+pelican.conf.py   A sample settings file
+===============   ===========================
diff --git a/pelican-plugins/test_data/__init__.py b/pelican-plugins/test_data/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/pelican-plugins/test_data/content/2012-11-30_filename-metadata.rst b/pelican-plugins/test_data/content/2012-11-30_filename-metadata.rst
new file mode 100644
index 0000000000000000000000000000000000000000..b048103dd1cdfab2b84e3401b8106fa51917fb15
--- /dev/null
+++ b/pelican-plugins/test_data/content/2012-11-30_filename-metadata.rst
@@ -0,0 +1,4 @@
+FILENAME_METADATA example
+#########################
+
+Some cool stuff!
diff --git a/pelican-plugins/test_data/content/another_super_article-fr.rst b/pelican-plugins/test_data/content/another_super_article-fr.rst
new file mode 100644
index 0000000000000000000000000000000000000000..71ac96359c2a0c118bc12297e8f4426524bf2d2d
--- /dev/null
+++ b/pelican-plugins/test_data/content/another_super_article-fr.rst
@@ -0,0 +1,7 @@
+Trop bien !
+###########
+
+:lang: fr
+:slug: oh-yeah
+
+Et voila du contenu en français
diff --git a/pelican-plugins/test_data/content/another_super_article.rst b/pelican-plugins/test_data/content/another_super_article.rst
new file mode 100644
index 0000000000000000000000000000000000000000..e6e0a92c13cfeef8b7e6a3e69038e53a55ebe634
--- /dev/null
+++ b/pelican-plugins/test_data/content/another_super_article.rst
@@ -0,0 +1,20 @@
+Oh yeah !
+#########
+
+:tags: oh, bar, yeah
+:date: 2010-10-20 10:14
+:category: bar
+:author: Alexis Métaireau
+:slug: oh-yeah
+:license: WTFPL
+
+Why not ?
+=========
+
+After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst !
+YEAH !
+
+.. image:: |filename|/pictures/Sushi.jpg
+   :height: 450 px
+   :width: 600 px
+   :alt: alternate text
diff --git a/pelican-plugins/test_data/content/article2-fr.rst b/pelican-plugins/test_data/content/article2-fr.rst
new file mode 100644
index 0000000000000000000000000000000000000000..31970f7e4e280ace1799c6aa8cde1c1ce2ea7a2e
--- /dev/null
+++ b/pelican-plugins/test_data/content/article2-fr.rst
@@ -0,0 +1,9 @@
+Deuxième article
+################
+
+:tags: foo, bar, baz
+:date: 2012-02-29
+:lang: fr
+:slug: second-article
+
+Ceci est un article, en français.
diff --git a/pelican-plugins/test_data/content/article2.rst b/pelican-plugins/test_data/content/article2.rst
new file mode 100644
index 0000000000000000000000000000000000000000..66f768ea80ab2b9aa37f457ce144659842e9c544
--- /dev/null
+++ b/pelican-plugins/test_data/content/article2.rst
@@ -0,0 +1,9 @@
+Second article
+##############
+
+:tags: foo, bar, baz
+:date: 2012-02-29
+:lang: en
+:slug: second-article
+
+This is some article, in english
diff --git a/pelican-plugins/test_data/content/cat1/article1.rst b/pelican-plugins/test_data/content/cat1/article1.rst
new file mode 100644
index 0000000000000000000000000000000000000000..1148a8f90faa78704a002654c8044ba33ebb37ef
--- /dev/null
+++ b/pelican-plugins/test_data/content/cat1/article1.rst
@@ -0,0 +1,7 @@
+Article 1
+#########
+
+:date: 2011-02-17
+:yeah: oh yeah !
+
+Article 1
diff --git a/pelican-plugins/test_data/content/cat1/article2.rst b/pelican-plugins/test_data/content/cat1/article2.rst
new file mode 100644
index 0000000000000000000000000000000000000000..a4f87866266d6e5e1a3bdae8154dbe8523aefa65
--- /dev/null
+++ b/pelican-plugins/test_data/content/cat1/article2.rst
@@ -0,0 +1,6 @@
+Article 2
+#########
+
+:date: 2011-02-17
+
+Article 2
diff --git a/pelican-plugins/test_data/content/cat1/article3.rst b/pelican-plugins/test_data/content/cat1/article3.rst
new file mode 100644
index 0000000000000000000000000000000000000000..53471177912b5a55fb9af2541fcb47804cf1f0b0
--- /dev/null
+++ b/pelican-plugins/test_data/content/cat1/article3.rst
@@ -0,0 +1,6 @@
+Article 3
+#########
+
+:date: 2011-02-17
+
+Article 3
diff --git a/pelican-plugins/test_data/content/cat1/markdown-article.md b/pelican-plugins/test_data/content/cat1/markdown-article.md
new file mode 100644
index 0000000000000000000000000000000000000000..5307b47a18574fba56d6b2d7e763d549a2eb88ea
--- /dev/null
+++ b/pelican-plugins/test_data/content/cat1/markdown-article.md
@@ -0,0 +1,7 @@
+Title: A markdown powered article
+Date: 2011-04-20
+
+You're mutually oblivious.
+
+[a root-relative link to unbelievable](|filename|/unbelievable.rst)
+[a file-relative link to unbelievable](|filename|../unbelievable.rst)
diff --git a/pelican-plugins/test_data/content/draft_article.rst b/pelican-plugins/test_data/content/draft_article.rst
new file mode 100644
index 0000000000000000000000000000000000000000..76ce9a16808de95bf39c934bb51c3476e5d6f932
--- /dev/null
+++ b/pelican-plugins/test_data/content/draft_article.rst
@@ -0,0 +1,7 @@
+A draft article
+###############
+
+:status: draft
+
+This is a draft article, it should live under the /drafts/ folder and not be
+listed anywhere else.
diff --git a/pelican-plugins/test_data/content/extra/robots.txt b/pelican-plugins/test_data/content/extra/robots.txt
new file mode 100644
index 0000000000000000000000000000000000000000..ae5b0d05c52c7f175e9fd2433e3edf190985fead
--- /dev/null
+++ b/pelican-plugins/test_data/content/extra/robots.txt
@@ -0,0 +1,2 @@
+User-agent: *
+Disallow: /static/pictures
diff --git a/pelican-plugins/test_data/content/pages/hidden_page.rst b/pelican-plugins/test_data/content/pages/hidden_page.rst
new file mode 100644
index 0000000000000000000000000000000000000000..ab8704ed88693307f8311ab037673f781e94c301
--- /dev/null
+++ b/pelican-plugins/test_data/content/pages/hidden_page.rst
@@ -0,0 +1,9 @@
+This is a test hidden page
+##########################
+
+:category: test
+:status: hidden
+
+This is great for things like error(404) pages
+Anyone can see this page but it's not linked to anywhere!
+
diff --git a/pelican-plugins/test_data/content/pages/jinja2_template.html b/pelican-plugins/test_data/content/pages/jinja2_template.html
new file mode 100644
index 0000000000000000000000000000000000000000..1b0dc4e4d3c7210ed54b2452a69e81b51fd67328
--- /dev/null
+++ b/pelican-plugins/test_data/content/pages/jinja2_template.html
@@ -0,0 +1,6 @@
+{% extends "base.html" %}
+{% block content %}
+
+Some text
+
+{% endblock %}
diff --git a/pelican-plugins/test_data/content/pages/override_url_saveas.rst b/pelican-plugins/test_data/content/pages/override_url_saveas.rst
new file mode 100644
index 0000000000000000000000000000000000000000..8a515f60ce39ef89162e72fee4ad0b126a5a45da
--- /dev/null
+++ b/pelican-plugins/test_data/content/pages/override_url_saveas.rst
@@ -0,0 +1,9 @@
+Override url/save_as
+####################
+
+:date: 2012-12-07
+:url: override/
+:save_as: override/index.html
+
+Test page which overrides save_as and url so that this page will be generated
+at a custom location.
diff --git a/pelican-plugins/test_data/content/pages/test_page.rst b/pelican-plugins/test_data/content/pages/test_page.rst
new file mode 100644
index 0000000000000000000000000000000000000000..2285f17bc03c8a768e8df76ff54bf70baab46fde
--- /dev/null
+++ b/pelican-plugins/test_data/content/pages/test_page.rst
@@ -0,0 +1,12 @@
+This is a test page
+###################
+
+:category: test
+
+Just an image.
+
+.. image:: |filename|/pictures/Fat_Cat.jpg
+   :height: 450 px
+   :width: 600 px
+   :alt: alternate text
+
diff --git a/pelican-plugins/test_data/content/pictures/Fat_Cat.jpg b/pelican-plugins/test_data/content/pictures/Fat_Cat.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..d8a96d356be94e782aaf36ba4e47168435b56338
Binary files /dev/null and b/pelican-plugins/test_data/content/pictures/Fat_Cat.jpg differ
diff --git a/pelican-plugins/test_data/content/pictures/Sushi.jpg b/pelican-plugins/test_data/content/pictures/Sushi.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..e49e5f0ab53461187c2cf61ebc1f3befdc937aed
Binary files /dev/null and b/pelican-plugins/test_data/content/pictures/Sushi.jpg differ
diff --git a/pelican-plugins/test_data/content/pictures/Sushi_Macro.jpg b/pelican-plugins/test_data/content/pictures/Sushi_Macro.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..21f935a1bf82808aa2ded423caba3cc9f2911973
Binary files /dev/null and b/pelican-plugins/test_data/content/pictures/Sushi_Macro.jpg differ
diff --git a/pelican-plugins/test_data/content/super_article.rst b/pelican-plugins/test_data/content/super_article.rst
new file mode 100644
index 0000000000000000000000000000000000000000..76e57683b8c163a072f491b2f9ae1d4b7d11f3ec
--- /dev/null
+++ b/pelican-plugins/test_data/content/super_article.rst
@@ -0,0 +1,36 @@
+This is a super article !
+#########################
+
+:tags: foo, bar, foobar
+:date: 2010-12-02 10:14
+:category: yeah
+:author: Alexis Métaireau
+:summary:
+    Multi-line metadata should be supported
+    as well as **inline markup**.
+
+Some content here !
+
+This is a simple title
+======================
+
+And here comes the cool stuff_.
+
+.. image:: |filename|/pictures/Sushi.jpg
+   :height: 450 px
+   :width: 600 px
+   :alt: alternate text
+
+.. image:: |filename|/pictures/Sushi_Macro.jpg
+   :height: 450 px
+   :width: 600 px
+   :alt: alternate text
+
+::
+
+   >>> from ipdb import set_trace
+   >>> set_trace()
+
+→ And now try with some utf8 hell: ééé
+
+.. _stuff: http://books.couchdb.org/relax/design-documents/views
diff --git a/pelican-plugins/test_data/content/unbelievable.rst b/pelican-plugins/test_data/content/unbelievable.rst
new file mode 100644
index 0000000000000000000000000000000000000000..20cb9dc79d7f1f65901a8198e9bf08f9a04f176c
--- /dev/null
+++ b/pelican-plugins/test_data/content/unbelievable.rst
@@ -0,0 +1,9 @@
+Unbelievable !
+##############
+
+:date: 2010-10-15 20:30
+
+Or completely awesome. Depends the needs.
+
+`a root-relative link to markdown-article <|filename|/cat1/markdown-article.md>`_
+`a file-relative link to markdown-article <|filename|cat1/markdown-article.md>`_
diff --git a/pelican-plugins/test_data/content/unwanted_file b/pelican-plugins/test_data/content/unwanted_file
new file mode 100644
index 0000000000000000000000000000000000000000..591255aeacf59185ea15d00bff36de0e7c9af895
--- /dev/null
+++ b/pelican-plugins/test_data/content/unwanted_file
@@ -0,0 +1 @@
+not to be parsed
diff --git a/pelican-plugins/test_data/pelican.conf.py b/pelican-plugins/test_data/pelican.conf.py
new file mode 100755
index 0000000000000000000000000000000000000000..714a418cb370c2a9a5b924ec500ed1667d0950d9
--- /dev/null
+++ b/pelican-plugins/test_data/pelican.conf.py
@@ -0,0 +1,45 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+AUTHOR = 'Alexis Métaireau'
+SITENAME = "Alexis' log"
+SITEURL = 'http://blog.notmyidea.org'
+TIMEZONE = "Europe/Paris"
+
+GITHUB_URL = 'http://github.com/ametaireau/'
+DISQUS_SITENAME = "blog-notmyidea"
+PDF_GENERATOR = False
+REVERSE_CATEGORY_ORDER = True
+LOCALE = "C"
+DEFAULT_PAGINATION = 4
+DEFAULT_DATE = (2012, 3, 2, 14, 1, 1)
+
+FEED_ALL_RSS = 'feeds/all.rss.xml'
+CATEGORY_FEED_RSS = 'feeds/%s.rss.xml'
+
+LINKS = (('Biologeek', 'http://biologeek.org'),
+         ('Filyb', "http://filyb.info/"),
+         ('Libert-fr', "http://www.libert-fr.com"),
+         ('N1k0', "http://prendreuncafe.com/blog/"),
+         ('Tarek Ziadé', "http://ziade.org/blog"),
+         ('Zubin Mithra', "http://zubin71.wordpress.com/"),)
+
+SOCIAL = (('twitter', 'http://twitter.com/ametaireau'),
+          ('lastfm', 'http://lastfm.com/user/akounet'),
+          ('github', 'http://github.com/ametaireau'),)
+
+# global metadata to all the contents
+DEFAULT_METADATA = (('yeah', 'it is'),)
+
+# static paths will be copied under the same name
+STATIC_PATHS = ["pictures", ]
+
+# A list of files to copy from the source to the destination
+FILES_TO_COPY = (('extra/robots.txt', 'robots.txt'),)
+
+# custom page generated with a jinja2 template
+TEMPLATE_PAGES = {'pages/jinja2_template.html': 'jinja2_template.html'}
+
+# foobar will not be used, because it's not in caps. All configuration keys
+# have to be in caps
+foobar = "barbaz"
diff --git a/pelican-plugins/test_data/themes/notmyidea/static/css/main.css b/pelican-plugins/test_data/themes/notmyidea/static/css/main.css
new file mode 100644
index 0000000000000000000000000000000000000000..572244439f5cd01b41ccec340d03081ec304fe66
--- /dev/null
+++ b/pelican-plugins/test_data/themes/notmyidea/static/css/main.css
@@ -0,0 +1,446 @@
+/*
+	Name: Smashing HTML5
+	Date: July 2009
+	Description: Sample layout for HTML5 and CSS3 goodness.
+	Version: 1.0
+	Author: Enrique Ramírez
+	Autor URI: http://enrique-ramirez.com
+*/
+
+/* Imports */
+@import url("reset.css");
+@import url("pygment.css");
+@import url("typogrify.css");
+@import url(//fonts.googleapis.com/css?family=Yanone+Kaffeesatz&subset=latin);
+
+/***** Global *****/
+/* Body */
+body {
+    background: #F5F4EF;
+    color: #000305;
+    font-size: 87.5%; /* Base font size: 14px */
+    font-family: 'Trebuchet MS', Trebuchet, 'Lucida Sans Unicode', 'Lucida Grande', 'Lucida Sans', Arial, sans-serif;
+    line-height: 1.429;
+    margin: 0;
+    padding: 0;
+    text-align: left;
+}
+
+/* Headings */
+h1 {font-size: 2em }
+h2 {font-size: 1.571em}	/* 22px */
+h3 {font-size: 1.429em}	/* 20px */
+h4 {font-size: 1.286em}	/* 18px */
+h5 {font-size: 1.143em}	/* 16px */
+h6 {font-size: 1em}		/* 14px */
+
+h1, h2, h3, h4, h5, h6 {
+	font-weight: 400;
+	line-height: 1.1;
+	margin-bottom: .8em;
+    font-family: 'Yanone Kaffeesatz', arial, serif;
+}
+
+h3, h4, h5, h6 { margin-top: .8em; }
+	
+hr { border: 2px solid #EEEEEE; }
+
+/* Anchors */
+a {outline: 0;}
+a img {border: 0px; text-decoration: none;}
+a:link, a:visited {
+	color: #C74350;
+	padding: 0 1px;
+	text-decoration: underline;
+}
+a:hover, a:active {
+	background-color: #C74350;
+	color: #fff;
+	text-decoration: none;
+	text-shadow: 1px 1px 1px #333;
+}
+
+h1 a:hover {
+    background-color: inherit
+}
+	
+/* Paragraphs */
+div.line-block,
+p { margin-top: 1em;
+    margin-bottom: 1em;}
+
+strong, b {font-weight: bold;}
+em, i {font-style: italic;}
+
+/* Lists */
+ul {
+	list-style: outside disc;
+	margin: 0em 0 0 1.5em;
+}
+
+ol {
+	list-style: outside decimal;
+	margin: 0em 0 0 1.5em;
+}
+
+li { margin-top: 0.5em;}
+
+.post-info {
+    float:right;
+    margin:10px;
+    padding:5px;
+}
+
+.post-info p{
+    margin-top: 1px;
+    margin-bottom: 1px;
+}
+
+.readmore { float: right }
+
+dl {margin: 0 0 1.5em 0;}
+dt {font-weight: bold;}
+dd {margin-left: 1.5em;}
+
+pre{background-color:  rgb(238, 238, 238); padding: 10px; margin: 10px; overflow: auto;}
+
+/* Quotes */
+blockquote {
+    margin: 20px;
+    font-style: italic;
+}
+cite {}
+
+q {}
+
+div.note {
+   float: right;
+   margin: 5px;
+   font-size: 85%;
+   max-width: 300px;
+}
+
+/* Tables */
+table {margin: .5em auto 1.5em auto; width: 98%;}
+	
+	/* Thead */
+	thead th {padding: .5em .4em; text-align: left;}
+	thead td {}
+
+	/* Tbody */
+	tbody td {padding: .5em .4em;}
+	tbody th {}
+	
+	tbody .alt td {}
+	tbody .alt th {}
+	
+	/* Tfoot */
+	tfoot th {}
+	tfoot td {}
+	
+/* HTML5 tags */
+header, section, footer,
+aside, nav, article, figure {
+	display: block;
+}
+
+/***** Layout *****/
+.body {clear: both; margin: 0 auto; width: 800px;}
+img.right, figure.right {float: right; margin: 0 0 2em 2em;}
+img.left, figure.left {float: left; margin: 0 2em 2em 0;}
+
+/*
+	Header
+*****************/
+#banner {
+	margin: 0 auto;
+	padding: 2.5em 0 0 0;
+}
+
+	/* Banner */
+	#banner h1 {font-size: 3.571em; line-height: 0;}
+	#banner h1 a:link, #banner h1 a:visited {
+		color: #000305;
+		display: block;
+		font-weight: bold;
+		margin: 0 0 .6em .2em;
+		text-decoration: none;
+	}
+	#banner h1 a:hover, #banner h1 a:active {
+		background: none;
+		color: #C74350;
+		text-shadow: none;
+	}
+	
+	#banner h1 strong {font-size: 0.36em; font-weight: normal;}
+	
+	/* Main Nav */
+	#banner nav {
+		background: #000305;
+		font-size: 1.143em;
+		height: 40px;
+		line-height: 30px;
+		margin: 0 auto 2em auto;
+		padding: 0;
+		text-align: center;
+		width: 800px;
+		
+		border-radius: 5px;
+		-moz-border-radius: 5px;
+		-webkit-border-radius: 5px;
+	}
+	
+	#banner nav ul {list-style: none; margin: 0 auto; width: 800px;}
+	#banner nav li {float: left; display: inline; margin: 0;}
+	
+	#banner nav a:link, #banner nav a:visited {
+		color: #fff;
+		display: inline-block;
+		height: 30px;
+		padding: 5px 1.5em;
+		text-decoration: none;
+	}
+	#banner nav a:hover, #banner nav a:active,
+	#banner nav .active a:link, #banner nav .active a:visited {
+		background: #C74451;
+		color: #fff;
+		text-shadow: none !important;
+	}
+	
+	#banner nav li:first-child a {
+		border-top-left-radius: 5px;
+		-moz-border-radius-topleft: 5px;
+		-webkit-border-top-left-radius: 5px;
+		
+		border-bottom-left-radius: 5px;
+		-moz-border-radius-bottomleft: 5px;
+		-webkit-border-bottom-left-radius: 5px;
+	}
+
+/*
+	Featured
+*****************/
+#featured {
+	background: #fff;
+	margin-bottom: 2em;
+	overflow: hidden;
+	padding: 20px;
+	width: 760px;
+	
+	border-radius: 10px;
+	-moz-border-radius: 10px;
+	-webkit-border-radius: 10px;
+}
+
+#featured figure {
+	border: 2px solid #eee;
+	float: right;
+	margin: 0.786em 2em 0 5em;
+	width: 248px;
+}
+#featured figure img {display: block; float: right;}
+
+#featured h2 {color: #C74451; font-size: 1.714em; margin-bottom: 0.333em;}
+#featured h3 {font-size: 1.429em; margin-bottom: .5em;}
+
+#featured h3 a:link, #featured h3 a:visited {color: #000305; text-decoration: none;}
+#featured h3 a:hover, #featured h3 a:active {color: #fff;}
+
+/*
+	Body
+*****************/
+#content {
+	background: #fff;
+	margin-bottom: 2em;
+	overflow: hidden;
+	padding: 20px 20px;
+	width: 760px;
+	
+	border-radius: 10px;
+	-moz-border-radius: 10px;
+	-webkit-border-radius: 10px;
+}
+
+/*
+	Extras
+*****************/
+#extras {margin: 0 auto 3em auto; overflow: hidden;}
+
+#extras ul {list-style: none; margin: 0;}
+#extras li {border-bottom: 1px solid #fff;}
+#extras h2 {
+	color: #C74350;
+	font-size: 1.429em;
+	margin-bottom: .25em;
+	padding: 0 3px;
+}
+
+#extras a:link, #extras a:visited {
+	color: #444;
+	display: block;
+	border-bottom: 1px solid #F4E3E3;
+	text-decoration: none;
+	padding: .3em .25em;
+}
+
+#extras a:hover, #extras a:active {color: #fff;}
+
+	/* Blogroll */
+	#extras .blogroll {
+		float: left;
+		width: 615px;
+	}
+	
+	#extras .blogroll li {float: left; margin: 0 20px 0 0; width: 185px;}
+	
+	/* Social */
+	#extras .social {
+		float: right;
+		width: 175px;
+	}
+	
+	#extras div[class='social'] a {
+		background-repeat: no-repeat;
+		background-position: 3px 6px;
+		padding-left: 25px;
+	}
+	
+		/* Icons */
+		.social a[href*='about.me'] {background-image: url('../images/icons/aboutme.png');}
+		.social a[href*='bitbucket.org'] {background-image: url('../images/icons/bitbucket.png');}
+		.social a[href*='delicious.com'] {background-image: url('../images/icons/delicious.png');}
+		.social a[href*='digg.com'] {background-image: url('../images/icons/digg.png');}
+		.social a[href*='facebook.com'] {background-image: url('../images/icons/facebook.png');}
+		.social a[href*='gitorious.org'] {background-image: url('../images/icons/gitorious.png');}
+		.social a[href*='github.com'],
+		.social a[href*='git.io'] {background-image: url('../images/icons/github.png');}
+		.social a[href*='gittip.com'] {background-image: url('../images/icons/gittip.png');}
+		.social a[href*='plus.google.com'] {background-image: url('../images/icons/google-plus.png');}
+		.social a[href*='groups.google.com'] {background-image: url('../images/icons/google-groups.png');}
+		.social a[href*='news.ycombinator.com'],
+		.social a[href*='hackernewsers.com'] {background-image: url('../images/icons/hackernews.png');}
+		.social a[href*='last.fm'], .social a[href*='lastfm.'] {background-image: url('../images/icons/lastfm.png');}
+		.social a[href*='linkedin.com'] {background-image: url('../images/icons/linkedin.png');}
+		.social a[href*='reddit.com'] {background-image: url('../images/icons/reddit.png');}
+		.social a[type$='atom+xml'], .social a[type$='rss+xml'] {background-image: url('../images/icons/rss.png');}
+		.social a[href*='slideshare.net'] {background-image: url('../images/icons/slideshare.png');}
+		.social a[href*='speakerdeck.com'] {background-image: url('../images/icons/speakerdeck.png');}
+		.social a[href*='twitter.com'] {background-image: url('../images/icons/twitter.png');}
+		.social a[href*='vimeo.com'] {background-image: url('../images/icons/vimeo.png');}
+		.social a[href*='youtube.com'] {background-image: url('../images/icons/youtube.png');}
+
+/*
+	About
+*****************/
+#about {
+	background: #fff;
+	font-style: normal;
+	margin-bottom: 2em;
+	overflow: hidden;
+	padding: 20px;
+	text-align: left;
+	width: 760px;
+	
+	border-radius: 10px;
+	-moz-border-radius: 10px;
+	-webkit-border-radius: 10px;
+}
+
+#about .primary {float: left; width: 165px;}
+#about .primary strong {color: #C64350; display: block; font-size: 1.286em;}
+#about .photo {float: left; margin: 5px 20px;}
+
+#about .url:link, #about .url:visited {text-decoration: none;}
+
+#about .bio {float: right; width: 500px;}
+
+/*
+	Footer
+*****************/
+#contentinfo {padding-bottom: 2em; text-align: right;}
+
+/***** Sections *****/
+/* Blog */
+.hentry {
+	display: block;
+	clear: both;
+	border-bottom: 1px solid #eee;
+	padding: 1.5em 0;
+}
+li:last-child .hentry, #content > .hentry {border: 0; margin: 0;}
+#content > .hentry {padding: 1em 0;}
+.hentry img{display : none ;}
+.entry-title {font-size: 3em; margin-bottom: 10px; margin-top: 0;}
+.entry-title a:link, .entry-title a:visited {text-decoration: none; color: #333;}
+.entry-title a:visited {background-color: #fff;}
+
+.hentry .post-info * {font-style: normal;}
+
+	/* Content */
+	.hentry footer {margin-bottom: 2em;}
+	.hentry footer address {display: inline;}
+	#posts-list footer address {display: block;}
+
+	/* Blog Index */
+	#posts-list {list-style: none; margin: 0;}
+	#posts-list .hentry {padding-left: 10px; position: relative;}
+	
+	#posts-list footer {
+		left: 10px;
+		position: relative;
+        float: left;
+		top: 0.5em;
+		width: 190px;
+	}
+	
+	/* About the Author */
+	#about-author {
+		background: #f9f9f9;
+		clear: both;
+		font-style: normal;
+		margin: 2em 0;
+		padding: 10px 20px 15px 20px;
+		
+		border-radius: 5px;
+		-moz-border-radius: 5px;
+		-webkit-border-radius: 5px;
+	}
+	
+	#about-author strong {
+		color: #C64350;
+		clear: both;
+		display: block;
+		font-size: 1.429em;
+	}
+	
+	#about-author .photo {border: 1px solid #ddd; float: left; margin: 5px 1em 0 0;}
+	
+	/* Comments */
+	#comments-list {list-style: none; margin: 0 1em;}
+	#comments-list blockquote {
+		background: #f8f8f8;
+		clear: both;
+		font-style: normal;
+		margin: 0;
+		padding: 15px 20px;
+		
+		border-radius: 5px;
+		-moz-border-radius: 5px;
+		-webkit-border-radius: 5px;
+	}
+	#comments-list footer {color: #888; padding: .5em 1em 0 0; text-align: right;}
+	
+	#comments-list li:nth-child(2n) blockquote {background: #F5f5f5;}
+	
+	/* Add a Comment */
+	#add-comment label {clear: left; float: left; text-align: left; width: 150px;}
+	#add-comment input[type='text'],
+	#add-comment input[type='email'],
+	#add-comment input[type='url'] {float: left; width: 200px;}
+	
+	#add-comment textarea {float: left; height: 150px; width: 495px;}
+	
+	#add-comment p.req {clear: both; margin: 0 .5em 1em 0; text-align: right;}
+	
+	#add-comment input[type='submit'] {float: right; margin: 0 .5em;}
+	#add-comment * {margin-bottom: .5em;}
diff --git a/pelican-plugins/test_data/themes/notmyidea/static/css/pygment.css b/pelican-plugins/test_data/themes/notmyidea/static/css/pygment.css
new file mode 100644
index 0000000000000000000000000000000000000000..fdd056f6f8bbe4f98bb887d7d427b1f9d03acde8
--- /dev/null
+++ b/pelican-plugins/test_data/themes/notmyidea/static/css/pygment.css
@@ -0,0 +1,205 @@
+.hll {
+background-color:#eee;
+}
+.c {
+color:#408090;
+font-style:italic;
+}
+.err {
+border:1px solid #FF0000;
+}
+.k {
+color:#007020;
+font-weight:bold;
+}
+.o {
+color:#666666;
+}
+.cm {
+color:#408090;
+font-style:italic;
+}
+.cp {
+color:#007020;
+}
+.c1 {
+color:#408090;
+font-style:italic;
+}
+.cs {
+background-color:#FFF0F0;
+color:#408090;
+}
+.gd {
+color:#A00000;
+}
+.ge {
+font-style:italic;
+}
+.gr {
+color:#FF0000;
+}
+.gh {
+color:#000080;
+font-weight:bold;
+}
+.gi {
+color:#00A000;
+}
+.go {
+color:#303030;
+}
+.gp {
+color:#C65D09;
+font-weight:bold;
+}
+.gs {
+font-weight:bold;
+}
+.gu {
+color:#800080;
+font-weight:bold;
+}
+.gt {
+color:#0040D0;
+}
+.kc {
+color:#007020;
+font-weight:bold;
+}
+.kd {
+color:#007020;
+font-weight:bold;
+}
+.kn {
+color:#007020;
+font-weight:bold;
+}
+.kp {
+color:#007020;
+}
+.kr {
+color:#007020;
+font-weight:bold;
+}
+.kt {
+color:#902000;
+}
+.m {
+color:#208050;
+}
+.s {
+color:#4070A0;
+}
+.na {
+color:#4070A0;
+}
+.nb {
+color:#007020;
+}
+.nc {
+color:#0E84B5;
+font-weight:bold;
+}
+.no {
+color:#60ADD5;
+}
+.nd {
+color:#555555;
+font-weight:bold;
+}
+.ni {
+color:#D55537;
+font-weight:bold;
+}
+.ne {
+color:#007020;
+}
+.nf {
+color:#06287E;
+}
+.nl {
+color:#002070;
+font-weight:bold;
+}
+.nn {
+color:#0E84B5;
+font-weight:bold;
+}
+.nt {
+color:#062873;
+font-weight:bold;
+}
+.nv {
+color:#BB60D5;
+}
+.ow {
+color:#007020;
+font-weight:bold;
+}
+.w {
+color:#BBBBBB;
+}
+.mf {
+color:#208050;
+}
+.mh {
+color:#208050;
+}
+.mi {
+color:#208050;
+}
+.mo {
+color:#208050;
+}
+.sb {
+color:#4070A0;
+}
+.sc {
+color:#4070A0;
+}
+.sd {
+color:#4070A0;
+font-style:italic;
+}
+.s2 {
+color:#4070A0;
+}
+.se {
+color:#4070A0;
+font-weight:bold;
+}
+.sh {
+color:#4070A0;
+}
+.si {
+color:#70A0D0;
+font-style:italic;
+}
+.sx {
+color:#C65D09;
+}
+.sr {
+color:#235388;
+}
+.s1 {
+color:#4070A0;
+}
+.ss {
+color:#517918;
+}
+.bp {
+color:#007020;
+}
+.vc {
+color:#BB60D5;
+}
+.vg {
+color:#BB60D5;
+}
+.vi {
+color:#BB60D5;
+}
+.il {
+color:#208050;
+}
diff --git a/pelican-plugins/test_data/themes/notmyidea/static/css/reset.css b/pelican-plugins/test_data/themes/notmyidea/static/css/reset.css
new file mode 100644
index 0000000000000000000000000000000000000000..1e217566acc77f4b4aa491e92b69806b5d71f950
--- /dev/null
+++ b/pelican-plugins/test_data/themes/notmyidea/static/css/reset.css
@@ -0,0 +1,52 @@
+/*
+	Name: Reset Stylesheet
+	Description: Resets browser's default CSS
+	Author: Eric Meyer
+	Author URI: http://meyerweb.com/eric/tools/css/reset/
+*/
+
+/* v1.0 | 20080212 */
+html, body, div, span, applet, object, iframe,
+h1, h2, h3, h4, h5, h6, p, blockquote, pre,
+a, abbr, acronym, address, big, cite, code,
+del, dfn, em, font, img, ins, kbd, q, s, samp,
+small, strike, strong, sub, sup, tt, var,
+b, u, i, center,
+dl, dt, dd, ol, ul, li,
+fieldset, form, label, legend,
+table, caption, tbody, tfoot, thead, tr, th, td {
+	background: transparent;
+	border: 0;
+	font-size: 100%;
+	margin: 0;
+	outline: 0;
+	padding: 0;
+	vertical-align: baseline;
+}
+
+body {line-height: 1;}
+
+ol, ul {list-style: none;}
+
+blockquote, q {quotes: none;}
+
+blockquote:before, blockquote:after,
+q:before, q:after {
+	content: '';
+	content: none;
+}
+
+/* remember to define focus styles! */
+:focus {
+	outline: 0;
+}
+
+/* remember to highlight inserts somehow! */
+ins {text-decoration: none;}
+del {text-decoration: line-through;}
+
+/* tables still need 'cellspacing="0"' in the markup */
+table {
+	border-collapse: collapse;
+	border-spacing: 0;
+}
\ No newline at end of file
diff --git a/pelican-plugins/test_data/themes/notmyidea/static/css/typogrify.css b/pelican-plugins/test_data/themes/notmyidea/static/css/typogrify.css
new file mode 100644
index 0000000000000000000000000000000000000000..c9b34dc8a8754da5a484932fbfe976ff60bdca3f
--- /dev/null
+++ b/pelican-plugins/test_data/themes/notmyidea/static/css/typogrify.css
@@ -0,0 +1,3 @@
+.caps {font-size:.92em;}
+.amp {color:#666; font-size:1.05em;font-family:"Warnock Pro", "Goudy Old Style","Palatino","Book Antiqua",serif; font-style:italic;}    
+.dquo {margin-left:-.38em;}
diff --git a/pelican-plugins/test_data/themes/notmyidea/static/css/wide.css b/pelican-plugins/test_data/themes/notmyidea/static/css/wide.css
new file mode 100644
index 0000000000000000000000000000000000000000..88fd59ceb7d5c6bbfc989d3a5e9cb4df3cdfb80b
--- /dev/null
+++ b/pelican-plugins/test_data/themes/notmyidea/static/css/wide.css
@@ -0,0 +1,48 @@
+@import url("main.css");
+
+body {
+    font:1.3em/1.3 "Hoefler Text","Georgia",Georgia,serif,sans-serif;
+}
+
+.post-info{
+    display: none;
+}
+
+#banner nav {
+    display: none;
+    -moz-border-radius: 0px;
+    margin-bottom: 20px;
+    overflow: hidden;
+    font-size: 1em;
+    background: #F5F4EF;
+}
+
+#banner nav ul{
+    padding-right: 50px;
+}
+
+#banner nav li{
+    float: right;
+    color: #000;
+}
+
+#banner nav li a {
+    color: #000;
+}
+
+#banner h1 {
+    margin-bottom: -18px;
+}
+
+#featured, #extras {
+    padding: 50px;
+}
+
+#featured {
+    padding-top: 20px;
+}
+
+#extras {
+    padding-top: 0px;
+    padding-bottom: 0px;
+}
diff --git a/pelican-plugins/test_data/themes/notmyidea/static/images/icons/aboutme.png b/pelican-plugins/test_data/themes/notmyidea/static/images/icons/aboutme.png
new file mode 100644
index 0000000000000000000000000000000000000000..9609df3bd9d766cd4b827fb0a8339b700c1abf24
Binary files /dev/null and b/pelican-plugins/test_data/themes/notmyidea/static/images/icons/aboutme.png differ
diff --git a/pelican-plugins/test_data/themes/notmyidea/static/images/icons/bitbucket.png b/pelican-plugins/test_data/themes/notmyidea/static/images/icons/bitbucket.png
new file mode 100644
index 0000000000000000000000000000000000000000..d05ba1610eab6ec3f9a4dcae689d4d88bda5433f
Binary files /dev/null and b/pelican-plugins/test_data/themes/notmyidea/static/images/icons/bitbucket.png differ
diff --git a/pelican-plugins/test_data/themes/notmyidea/static/images/icons/delicious.png b/pelican-plugins/test_data/themes/notmyidea/static/images/icons/delicious.png
new file mode 100644
index 0000000000000000000000000000000000000000..3dccdd848ec99fba9950e8195d4e73867ada2738
Binary files /dev/null and b/pelican-plugins/test_data/themes/notmyidea/static/images/icons/delicious.png differ
diff --git a/pelican-plugins/test_data/themes/notmyidea/static/images/icons/facebook.png b/pelican-plugins/test_data/themes/notmyidea/static/images/icons/facebook.png
new file mode 100644
index 0000000000000000000000000000000000000000..74e7ad52f362b56d584df459bc5141395c32b7ed
Binary files /dev/null and b/pelican-plugins/test_data/themes/notmyidea/static/images/icons/facebook.png differ
diff --git a/pelican-plugins/test_data/themes/notmyidea/static/images/icons/github.png b/pelican-plugins/test_data/themes/notmyidea/static/images/icons/github.png
new file mode 100644
index 0000000000000000000000000000000000000000..6c52b486c5e392261c24d53fe2609e54d4f07c5d
Binary files /dev/null and b/pelican-plugins/test_data/themes/notmyidea/static/images/icons/github.png differ
diff --git a/pelican-plugins/test_data/themes/notmyidea/static/images/icons/gitorious.png b/pelican-plugins/test_data/themes/notmyidea/static/images/icons/gitorious.png
new file mode 100644
index 0000000000000000000000000000000000000000..3eeb3ecec36a73ff505e04ecdecbcc4792ef6786
Binary files /dev/null and b/pelican-plugins/test_data/themes/notmyidea/static/images/icons/gitorious.png differ
diff --git a/pelican-plugins/test_data/themes/notmyidea/static/images/icons/gittip.png b/pelican-plugins/test_data/themes/notmyidea/static/images/icons/gittip.png
new file mode 100644
index 0000000000000000000000000000000000000000..af949625ac83d2441215c81650e897c97d9e5d95
Binary files /dev/null and b/pelican-plugins/test_data/themes/notmyidea/static/images/icons/gittip.png differ
diff --git a/pelican-plugins/test_data/themes/notmyidea/static/images/icons/google-groups.png b/pelican-plugins/test_data/themes/notmyidea/static/images/icons/google-groups.png
new file mode 100644
index 0000000000000000000000000000000000000000..5de15e68f4d1e4176b46fe6346d42f53e3296b21
Binary files /dev/null and b/pelican-plugins/test_data/themes/notmyidea/static/images/icons/google-groups.png differ
diff --git a/pelican-plugins/test_data/themes/notmyidea/static/images/icons/google-plus.png b/pelican-plugins/test_data/themes/notmyidea/static/images/icons/google-plus.png
new file mode 100644
index 0000000000000000000000000000000000000000..3c6b74324031611f20c0a3810131aa74fd0a5a9f
Binary files /dev/null and b/pelican-plugins/test_data/themes/notmyidea/static/images/icons/google-plus.png differ
diff --git a/pelican-plugins/test_data/themes/notmyidea/static/images/icons/hackernews.png b/pelican-plugins/test_data/themes/notmyidea/static/images/icons/hackernews.png
new file mode 100644
index 0000000000000000000000000000000000000000..fc7a82d4d68068d5fb032885b93e670c385ae1b4
Binary files /dev/null and b/pelican-plugins/test_data/themes/notmyidea/static/images/icons/hackernews.png differ
diff --git a/pelican-plugins/test_data/themes/notmyidea/static/images/icons/lastfm.png b/pelican-plugins/test_data/themes/notmyidea/static/images/icons/lastfm.png
new file mode 100644
index 0000000000000000000000000000000000000000..3a6c6262b644dadbcf6cce5dfe4fed9740a9ec1f
Binary files /dev/null and b/pelican-plugins/test_data/themes/notmyidea/static/images/icons/lastfm.png differ
diff --git a/pelican-plugins/test_data/themes/notmyidea/static/images/icons/linkedin.png b/pelican-plugins/test_data/themes/notmyidea/static/images/icons/linkedin.png
new file mode 100644
index 0000000000000000000000000000000000000000..d29c1201bcb0c278d49f573f9ef95ebfe932fb5b
Binary files /dev/null and b/pelican-plugins/test_data/themes/notmyidea/static/images/icons/linkedin.png differ
diff --git a/pelican-plugins/test_data/themes/notmyidea/static/images/icons/reddit.png b/pelican-plugins/test_data/themes/notmyidea/static/images/icons/reddit.png
new file mode 100644
index 0000000000000000000000000000000000000000..71ae1215ac8ea274f089cc4aa55eaf8a546dea67
Binary files /dev/null and b/pelican-plugins/test_data/themes/notmyidea/static/images/icons/reddit.png differ
diff --git a/pelican-plugins/test_data/themes/notmyidea/static/images/icons/rss.png b/pelican-plugins/test_data/themes/notmyidea/static/images/icons/rss.png
new file mode 100644
index 0000000000000000000000000000000000000000..7862c65afeb66ba9cc7e9576a3a62b850b2e184d
Binary files /dev/null and b/pelican-plugins/test_data/themes/notmyidea/static/images/icons/rss.png differ
diff --git a/pelican-plugins/test_data/themes/notmyidea/static/images/icons/slideshare.png b/pelican-plugins/test_data/themes/notmyidea/static/images/icons/slideshare.png
new file mode 100644
index 0000000000000000000000000000000000000000..ecc97410138bb3c457a898b1e868b22ae9db9989
Binary files /dev/null and b/pelican-plugins/test_data/themes/notmyidea/static/images/icons/slideshare.png differ
diff --git a/pelican-plugins/test_data/themes/notmyidea/static/images/icons/speakerdeck.png b/pelican-plugins/test_data/themes/notmyidea/static/images/icons/speakerdeck.png
new file mode 100644
index 0000000000000000000000000000000000000000..087d0931caa301f8a1bba1497e1b91b7a7f105a4
Binary files /dev/null and b/pelican-plugins/test_data/themes/notmyidea/static/images/icons/speakerdeck.png differ
diff --git a/pelican-plugins/test_data/themes/notmyidea/static/images/icons/twitter.png b/pelican-plugins/test_data/themes/notmyidea/static/images/icons/twitter.png
new file mode 100644
index 0000000000000000000000000000000000000000..d0ef3cc1b36ab79ac7931c2269b70f3662950a97
Binary files /dev/null and b/pelican-plugins/test_data/themes/notmyidea/static/images/icons/twitter.png differ
diff --git a/pelican-plugins/test_data/themes/notmyidea/static/images/icons/vimeo.png b/pelican-plugins/test_data/themes/notmyidea/static/images/icons/vimeo.png
new file mode 100644
index 0000000000000000000000000000000000000000..dba472022f0fcf7ecdd8f4847a8a3bde90789bc7
Binary files /dev/null and b/pelican-plugins/test_data/themes/notmyidea/static/images/icons/vimeo.png differ
diff --git a/pelican-plugins/test_data/themes/notmyidea/static/images/icons/youtube.png b/pelican-plugins/test_data/themes/notmyidea/static/images/icons/youtube.png
new file mode 100644
index 0000000000000000000000000000000000000000..ce6cbe4fdaf6e7dff56c0418a19bd1466a40a02e
Binary files /dev/null and b/pelican-plugins/test_data/themes/notmyidea/static/images/icons/youtube.png differ
diff --git a/pelican-plugins/test_data/themes/notmyidea/templates/analytics.html b/pelican-plugins/test_data/themes/notmyidea/templates/analytics.html
new file mode 100644
index 0000000000000000000000000000000000000000..4de2c86bddc1fb8487343cbbe1c2eb0eb068336d
--- /dev/null
+++ b/pelican-plugins/test_data/themes/notmyidea/templates/analytics.html
@@ -0,0 +1,12 @@
+{% if GOOGLE_ANALYTICS %}
+    <script type="text/javascript">
+    var _gaq = _gaq || [];
+    _gaq.push(['_setAccount', '{{GOOGLE_ANALYTICS}}']);
+    _gaq.push(['_trackPageview']);
+    (function() {
+        var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
+        ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
+        var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
+    })();
+    </script>
+{% endif %}
\ No newline at end of file
diff --git a/pelican-plugins/test_data/themes/notmyidea/templates/archives.html b/pelican-plugins/test_data/themes/notmyidea/templates/archives.html
new file mode 100644
index 0000000000000000000000000000000000000000..f6784942f45a5dc81b7cf4da3148d90009470b77
--- /dev/null
+++ b/pelican-plugins/test_data/themes/notmyidea/templates/archives.html
@@ -0,0 +1,13 @@
+{% extends "base.html" %}
+{% block content %}
+<section id="content" class="body">
+<h1>Archives for {{ SITENAME }}</h1>
+
+<dl>
+{% for article in dates %}
+    <dt>{{ article.locale_date }}</dt>
+    <dd><a href="{{ SITEURL }}/{{ article.url }}">{{ article.title }}</a></dd>
+{% endfor %}
+</dl>
+</section>
+{% endblock %}
diff --git a/pelican-plugins/test_data/themes/notmyidea/templates/article.html b/pelican-plugins/test_data/themes/notmyidea/templates/article.html
new file mode 100644
index 0000000000000000000000000000000000000000..516fd3b5ac702ad4532e74871e7ce57c6a8caa73
--- /dev/null
+++ b/pelican-plugins/test_data/themes/notmyidea/templates/article.html
@@ -0,0 +1,35 @@
+{% extends "base.html" %}
+{% block title %}{{ article.title|striptags }}{% endblock %}
+{% block content %}
+<section id="content" class="body">
+  <article>
+    <header>
+      <h1 class="entry-title">
+        <a href="{{ SITEURL }}/{{ article.url }}" rel="bookmark"
+           title="Permalink to {{ article.title|striptags }}">{{ article.title}}</a></h1>
+      {% include 'twitter.html' %}
+    </header>
+
+    <div class="entry-content">
+      {% include 'article_infos.html' %}
+      {{ article.content }}
+    </div><!-- /.entry-content -->
+    {% if DISQUS_SITENAME and SITEURL and article.status != "draft" %}
+    <div class="comments">
+      <h2>Comments !</h2>
+      <div id="disqus_thread"></div>
+      <script type="text/javascript">
+        var disqus_identifier = "{{ article.url }}";
+        var disqus_url = "{{ SITEURL }}/{{ article.url }}";
+        (function() {
+        var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
+        dsq.src = 'http://{{ DISQUS_SITENAME }}.disqus.com/embed.js';
+        (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
+        })();
+      </script>
+    </div>
+    {% endif %}
+
+  </article>
+</section>
+{% endblock %}
diff --git a/pelican-plugins/test_data/themes/notmyidea/templates/article_infos.html b/pelican-plugins/test_data/themes/notmyidea/templates/article_infos.html
new file mode 100644
index 0000000000000000000000000000000000000000..4b86716d3414975672b779cf590a991814f0a55e
--- /dev/null
+++ b/pelican-plugins/test_data/themes/notmyidea/templates/article_infos.html
@@ -0,0 +1,15 @@
+<footer class="post-info">
+        <abbr class="published" title="{{ article.date.isoformat() }}">
+                {{ article.locale_date }}
+        </abbr>
+
+        {% if article.author %}
+        <address class="vcard author">
+                By <a class="url fn" href="{{ SITEURL }}/{{ article.author.url }}">{{ article.author }}</a>
+        </address>
+        {% endif %}
+<p>In <a href="{{ SITEURL }}/{{ article.category.url }}">{{ article.category }}</a>. {% if PDF_PROCESSOR %}<a href="{{ SITEURL }}/pdf/{{ article.slug }}.pdf">get the pdf</a>{% endif %}</p>
+{% include 'taglist.html' %}
+{% import 'translations.html' as translations with context %}
+{{ translations.translations_for(article) }}
+</footer><!-- /.post-info -->
diff --git a/pelican-plugins/test_data/themes/notmyidea/templates/author.html b/pelican-plugins/test_data/themes/notmyidea/templates/author.html
new file mode 100644
index 0000000000000000000000000000000000000000..0b372902d5552cb93a0eed266b6fd851f63bd1be
--- /dev/null
+++ b/pelican-plugins/test_data/themes/notmyidea/templates/author.html
@@ -0,0 +1,2 @@
+{% extends "index.html" %}
+{% block title %}{{ SITENAME }} - {{ author }}{% endblock %}
diff --git a/pelican-plugins/test_data/themes/notmyidea/templates/authors.html b/pelican-plugins/test_data/themes/notmyidea/templates/authors.html
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/pelican-plugins/test_data/themes/notmyidea/templates/base.html b/pelican-plugins/test_data/themes/notmyidea/templates/base.html
new file mode 100644
index 0000000000000000000000000000000000000000..42a2cec17dd7039360f028b2de901fe547d56efa
--- /dev/null
+++ b/pelican-plugins/test_data/themes/notmyidea/templates/base.html
@@ -0,0 +1,88 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+        <title>{% block title %}{{ SITENAME }}{%endblock%}</title>
+        <meta charset="utf-8" />
+        <link rel="stylesheet" href="{{ SITEURL }}/theme/css/{{ CSS_FILE }}" type="text/css" />
+        {% if FEED_ALL_ATOM %}
+        <link href="{{ FEED_DOMAIN }}/{{ FEED_ALL_ATOM }}" type="application/atom+xml" rel="alternate" title="{{ SITENAME }} Atom Feed" />
+        {% endif %}
+        {% if FEED_ALL_RSS %}
+        <link href="{{ FEED_DOMAIN }}/{{ FEED_ALL_RSS }}" type="application/rss+xml" rel="alternate" title="{{ SITENAME }} RSS Feed" />
+        {% endif %}
+
+        <!--[if IE]>
+                <script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script><![endif]-->
+
+        <!--[if lte IE 7]>
+                <link rel="stylesheet" type="text/css" media="all" href="{{ SITEURL }}/css/ie.css"/>
+                <script src="{{ SITEURL }}/js/IE8.js" type="text/javascript"></script><![endif]-->
+
+        <!--[if lt IE 7]>
+                <link rel="stylesheet" type="text/css" media="all" href="{{ SITEURL }}/css/ie6.css"/><![endif]-->
+
+</head>
+
+<body id="index" class="home">
+{% include 'github.html' %}
+        <header id="banner" class="body">
+                <h1><a href="{{ SITEURL }}/">{{ SITENAME }} {% if SITESUBTITLE %} <strong>{{ SITESUBTITLE }}</strong>{% endif %}</a></h1>
+                <nav><ul>
+                {% for title, link in MENUITEMS %}
+                    <li><a href="{{ link }}">{{ title }}</a></li>
+                {% endfor %}
+                {% if DISPLAY_PAGES_ON_MENU -%}
+                {% for page in PAGES %}
+                    <li><a href="{{ SITEURL }}/{{ page.url }}">{{ page.title }}</a></li>
+                {% endfor %}
+                {% endif %}
+                {% if DISPLAY_CATEGORIES_ON_MENU -%}
+                {% for cat, null in categories %}
+                    <li {% if cat == category %}class="active"{% endif %}><a href="{{ SITEURL }}/{{ cat.url }}">{{ cat }}</a></li>
+                {% endfor %}
+                {% endif %}
+                </ul></nav>
+        </header><!-- /#banner -->
+        {% block content %}
+        {% endblock %}
+        <section id="extras" class="body">
+        {% if LINKS %}
+                <div class="blogroll">
+                        <h2>blogroll</h2>
+                        <ul>
+                        {% for name, link in LINKS %}
+                            <li><a href="{{ link }}">{{ name }}</a></li>
+                        {% endfor %}
+                        </ul>
+                </div><!-- /.blogroll -->
+        {% endif %}
+        {% if SOCIAL or FEED_ALL_ATOM or FEED_ALL_RSS %}
+                <div class="social">
+                        <h2>social</h2>
+                        <ul>
+                            <li><a href="{{ FEED_DOMAIN }}/{{ FEED_ALL_ATOM }}" type="application/atom+xml" rel="alternate">atom feed</a></li>
+                            {% if FEED_ALL_RSS %}
+                            <li><a href="{{ FEED_DOMAIN }}/{{ FEED_ALL_RSS }}" type="application/rss+xml" rel="alternate">rss feed</a></li>
+                            {% endif %}
+
+                        {% for name, link in SOCIAL %}
+                            <li><a href="{{ link }}">{{ name }}</a></li>
+                        {% endfor %}
+                        </ul>
+                </div><!-- /.social -->
+        {% endif %}
+        </section><!-- /#extras -->
+
+        <footer id="contentinfo" class="body">
+                <address id="about" class="vcard body">
+                Proudly powered by <a href="http://getpelican.com/">Pelican</a>, which takes great advantage of <a href="http://python.org">Python</a>.
+                </address><!-- /#about -->
+
+                <p>The theme is by <a href="http://coding.smashingmagazine.com/2009/08/04/designing-a-html-5-layout-from-scratch/">Smashing Magazine</a>, thanks!</p>
+        </footer><!-- /#contentinfo -->
+
+{% include 'analytics.html' %}
+{% include 'piwik.html' %}
+{% include 'disqus_script.html' %}
+</body>
+</html>
diff --git a/pelican-plugins/test_data/themes/notmyidea/templates/category.html b/pelican-plugins/test_data/themes/notmyidea/templates/category.html
new file mode 100644
index 0000000000000000000000000000000000000000..56f8e93e949d635765ff09cdd9a1c7e9209676a9
--- /dev/null
+++ b/pelican-plugins/test_data/themes/notmyidea/templates/category.html
@@ -0,0 +1,2 @@
+{% extends "index.html" %}
+{% block title %}{{ SITENAME }} - {{ category }}{% endblock %}
diff --git a/pelican-plugins/test_data/themes/notmyidea/templates/comments.html b/pelican-plugins/test_data/themes/notmyidea/templates/comments.html
new file mode 100644
index 0000000000000000000000000000000000000000..bb033c0f4baeae5d2b32851548f9a1d11f55952b
--- /dev/null
+++ b/pelican-plugins/test_data/themes/notmyidea/templates/comments.html
@@ -0,0 +1 @@
+{% if DISQUS_SITENAME %}<p>There are <a href="{{ SITEURL }}/{{ article.url }}#disqus_thread">comments</a>.</p>{% endif %}
diff --git a/pelican-plugins/test_data/themes/notmyidea/templates/disqus_script.html b/pelican-plugins/test_data/themes/notmyidea/templates/disqus_script.html
new file mode 100644
index 0000000000000000000000000000000000000000..c4f442c8b0a6eadccdefe55a5a4a6f6af61999ef
--- /dev/null
+++ b/pelican-plugins/test_data/themes/notmyidea/templates/disqus_script.html
@@ -0,0 +1,11 @@
+{% if DISQUS_SITENAME %}
+<script type="text/javascript">
+    var disqus_shortname = '{{ DISQUS_SITENAME }}';
+    (function () {
+        var s = document.createElement('script'); s.async = true;
+        s.type = 'text/javascript';
+        s.src = 'http://' + disqus_shortname + '.disqus.com/count.js';
+        (document.getElementsByTagName('HEAD')[0] || document.getElementsByTagName('BODY')[0]).appendChild(s);
+    }());
+</script>
+{% endif %}
diff --git a/pelican-plugins/test_data/themes/notmyidea/templates/github.html b/pelican-plugins/test_data/themes/notmyidea/templates/github.html
new file mode 100644
index 0000000000000000000000000000000000000000..75592ac6b7bc827cb0001ec40cc3cd0520152c55
--- /dev/null
+++ b/pelican-plugins/test_data/themes/notmyidea/templates/github.html
@@ -0,0 +1,9 @@
+{% if GITHUB_URL %}
+<a href="{{ GITHUB_URL }}">
+{% if GITHUB_POSITION != "left" %}
+<img style="position: absolute; top: 0; right: 0; border: 0;" src="http://s3.amazonaws.com/github/ribbons/forkme_right_red_aa0000.png" alt="Fork me on GitHub" />
+{% else %}
+<img style="position: absolute; top: 0; left: 0; border: 0;" src="http://s3.amazonaws.com/github/ribbons/forkme_left_white_ffffff.png" alt="Fork me on GitHub" />
+{% endif %}
+</a>
+{% endif %}
diff --git a/pelican-plugins/test_data/themes/notmyidea/templates/index.html b/pelican-plugins/test_data/themes/notmyidea/templates/index.html
new file mode 100644
index 0000000000000000000000000000000000000000..8752a6b63335d77e5c583b46aea4fb2ba8887423
--- /dev/null
+++ b/pelican-plugins/test_data/themes/notmyidea/templates/index.html
@@ -0,0 +1,61 @@
+{% extends "base.html" %}
+{% block content_title %}{% endblock %}
+{% block content %}        
+{% if articles %}
+    {% for article in articles_page.object_list %}        
+
+        {# First item #}
+        {% if loop.first and not articles_page.has_previous() %}
+            <aside id="featured" class="body">
+                <article>
+                    <h1 class="entry-title"><a href="{{ SITEURL }}/{{ article.url }}">{{ article.title }}</a></h1> 
+                    {% include 'article_infos.html' %}{{ article.content }}{% include 'comments.html' %}
+                </article>
+                {% if loop.length == 1 %}
+                    {% include 'pagination.html' %}
+                {% endif %}
+            </aside><!-- /#featured -->
+            {% if loop.length > 1 %}
+                <section id="content" class="body">
+                    <h1>Other articles</h1>
+                    <hr />
+                    <ol id="posts-list" class="hfeed">
+            {% endif %}
+        {# other items #}
+        {% else %} 
+            {% if loop.first and articles_page.has_previous %}
+                <section id="content" class="body">
+                    <ol id="posts-list" class="hfeed" start="{{ articles_paginator.per_page -1 }}">
+            {% endif %}
+            <li><article class="hentry">    
+                <header>
+                    <h1><a href="{{ SITEURL }}/{{ article.url }}" rel="bookmark"
+                           title="Permalink to {{ article.title|striptags }}">{{ article.title }}</a></h1>
+                </header>
+                
+                <div class="entry-content">
+                {% include 'article_infos.html' %}
+                {{ article.summary }}
+                <a class="readmore" href="{{ SITEURL }}/{{ article.url }}">read more</a>
+                {% include 'comments.html' %}
+                </div><!-- /.entry-content -->
+            </article></li>
+        {% endif %}
+        {% if loop.last %}
+            </ol><!-- /#posts-list -->
+            {% if loop.last and (articles_page.has_previous() 
+            or not articles_page.has_previous() and loop.length > 1) %}
+                {% include 'pagination.html' %}
+            {% endif %}
+            </section><!-- /#content -->
+        {% endif %}
+    {% endfor %}
+{% else %}
+<section id="content" class="body">    
+<h2>Pages</h2>
+    {% for page in PAGES %}
+        <li><a href="{{ SITEURL }}/{{ page.url }}">{{ page.title }}</a></li>
+    {% endfor %}
+</section>
+{% endif %}
+{% endblock content %}
diff --git a/pelican-plugins/test_data/themes/notmyidea/templates/page.html b/pelican-plugins/test_data/themes/notmyidea/templates/page.html
new file mode 100644
index 0000000000000000000000000000000000000000..60409d5c58bbeaddc844a69cc20fb1df36ac35e1
--- /dev/null
+++ b/pelican-plugins/test_data/themes/notmyidea/templates/page.html
@@ -0,0 +1,12 @@
+{% extends "base.html" %}
+{% block title %}{{ page.title }}{% endblock %}
+{% block content %}        
+<section id="content" class="body">    
+    <h1 class="entry-title">{{ page.title }}</h1>
+    {% import 'translations.html' as translations with context %}
+    {{ translations.translations_for(page) }}
+    {% if PDF_PROCESSOR %}<a href="{{ SITEURL }}/pdf/{{ page.slug }}.pdf">get
+    the pdf</a>{% endif %}
+    {{ page.content }}
+</section>
+{% endblock %}
diff --git a/pelican-plugins/test_data/themes/notmyidea/templates/piwik.html b/pelican-plugins/test_data/themes/notmyidea/templates/piwik.html
new file mode 100644
index 0000000000000000000000000000000000000000..ff459af7200bebcc1f691dbf87fe4f9fd9555072
--- /dev/null
+++ b/pelican-plugins/test_data/themes/notmyidea/templates/piwik.html
@@ -0,0 +1,16 @@
+{% if PIWIK_URL and PIWIK_SITE_ID %}
+    <script type="text/javascript">
+    {% if PIWIK_SSL_URL %}
+        var pkBaseURL = (("https:" == document.location.protocol) ? "https://{{ PIWIK_SSL_URL }}/" : "http://{{ PIWIK_URL }}/");
+    {% else %}
+        var pkBaseURL = (("https:" == document.location.protocol) ? "https://{{ PIWIK_URL }}/" : "http://{{ PIWIK_URL }}/");
+    {% endif %}
+    document.write(unescape("%3Cscript src='" + pkBaseURL + "piwik.js' type='text/javascript'%3E%3C/script%3E"));
+    </script><script type="text/javascript">
+    try {
+    var piwikTracker = Piwik.getTracker(pkBaseURL + "piwik.php", {{ PIWIK_SITE_ID }});
+    piwikTracker.trackPageView();
+    piwikTracker.enableLinkTracking();
+    } catch( err ) {}
+    </script><noscript><p><img src="http://{{ PIWIK_URL }}/piwik.php?idsite={{ PIWIK_SITE_ID }}" style="border:0" alt="" /></p></noscript>
+{% endif %}
\ No newline at end of file
diff --git a/pelican-plugins/test_data/themes/notmyidea/templates/tag.html b/pelican-plugins/test_data/themes/notmyidea/templates/tag.html
new file mode 100644
index 0000000000000000000000000000000000000000..68cdcba652b9621a24990b45ec734c859914355f
--- /dev/null
+++ b/pelican-plugins/test_data/themes/notmyidea/templates/tag.html
@@ -0,0 +1,2 @@
+{% extends "index.html" %}
+{% block title %}{{ SITENAME }} - {{ tag }}{% endblock %}
diff --git a/pelican-plugins/test_data/themes/notmyidea/templates/taglist.html b/pelican-plugins/test_data/themes/notmyidea/templates/taglist.html
new file mode 100644
index 0000000000000000000000000000000000000000..c792fd7d4dec0eaae5fe4696546ad8651ee4e439
--- /dev/null
+++ b/pelican-plugins/test_data/themes/notmyidea/templates/taglist.html
@@ -0,0 +1,2 @@
+{% if article.tags %}<p>tags: {% for tag in article.tags %}<a href="{{ SITEURL }}/{{ tag.url }}">{{ tag }}</a>{% endfor %}</p>{% endif %}
+{% if PDF_PROCESSOR %}<p><a href="{{ SITEURL }}/pdf/{{ article.slug }}.pdf">get the pdf</a></p>{% endif %}
diff --git a/pelican-plugins/test_data/themes/notmyidea/templates/translations.html b/pelican-plugins/test_data/themes/notmyidea/templates/translations.html
new file mode 100644
index 0000000000000000000000000000000000000000..ca03a2c94986bac80e5a299326c5cbecdf1051c5
--- /dev/null
+++ b/pelican-plugins/test_data/themes/notmyidea/templates/translations.html
@@ -0,0 +1,8 @@
+{% macro translations_for(article) %}
+{% if article.translations %}
+Translations: 
+    {% for translation in article.translations %}
+        <a href="{{ SITEURL }}/{{ translation.url }}">{{ translation.lang }}</a>
+    {% endfor %}
+{% endif %}
+{% endmacro %}
diff --git a/pelican-plugins/test_data/themes/notmyidea/templates/twitter.html b/pelican-plugins/test_data/themes/notmyidea/templates/twitter.html
new file mode 100644
index 0000000000000000000000000000000000000000..c6b159f4ccc0de8861fb2235ef3dc4aa5d9c3739
--- /dev/null
+++ b/pelican-plugins/test_data/themes/notmyidea/templates/twitter.html
@@ -0,0 +1,3 @@
+{% if TWITTER_USERNAME %}
+<a href="http://twitter.com/share" class="twitter-share-button" data-count="horizontal" data-via="{{TWITTER_USERNAME}}">Tweet</a><script type="text/javascript" src="http://platform.twitter.com/widgets.js"></script>
+{% endif %}
\ No newline at end of file
diff --git a/pelican-plugins/test_data/themes/simple/templates/archives.html b/pelican-plugins/test_data/themes/simple/templates/archives.html
new file mode 100644
index 0000000000000000000000000000000000000000..050f268648ed42d56f111f7dfb12dfbd9dfa3afa
--- /dev/null
+++ b/pelican-plugins/test_data/themes/simple/templates/archives.html
@@ -0,0 +1,11 @@
+{% extends "base.html" %}
+{% block content %}
+<h1>Archives for {{ SITENAME }}</h1>
+
+<dl>
+{% for article in dates %}
+    <dt>{{ article.locale_date }}</dt>
+    <dd><a href="{{ SITEURL }}/{{ article.url }}">{{ article.title }}</a></dd>
+{% endfor %}
+</dl>
+{% endblock %}
diff --git a/pelican-plugins/test_data/themes/simple/templates/article.html b/pelican-plugins/test_data/themes/simple/templates/article.html
new file mode 100644
index 0000000000000000000000000000000000000000..98cc852c19c5c57b5e0efa3fd2e1e8f7be27c856
--- /dev/null
+++ b/pelican-plugins/test_data/themes/simple/templates/article.html
@@ -0,0 +1,25 @@
+{% extends "base.html" %}
+{% block content %}
+<section id="content" class="body">
+  <header>
+    <h2 class="entry-title">
+      <a href="{{ SITEURL }}/{{ article.url }}" rel="bookmark"
+         title="Permalink to {{ article.title|striptags }}">{{ article.title }}</a></h2>
+ {% import 'translations.html' as translations with context %}
+ {{ translations.translations_for(article) }}
+  </header>
+  <footer class="post-info">
+    <abbr class="published" title="{{ article.date.isoformat() }}">
+      {{ article.locale_date }}
+    </abbr>
+    {% if article.author %}
+    <address class="vcard author">
+      By <a class="url fn" href="{{ SITEURL }}/{{ article.author.url }}">{{ article.author }}</a>
+    </address>
+    {% endif %}
+  </footer><!-- /.post-info -->
+  <div class="entry-content">
+    {{ article.content }}
+  </div><!-- /.entry-content -->
+</section>
+{% endblock %}
diff --git a/pelican-plugins/test_data/themes/simple/templates/author.html b/pelican-plugins/test_data/themes/simple/templates/author.html
new file mode 100644
index 0000000000000000000000000000000000000000..e9f7870286be7a59d55448e417a74f94f9d091cb
--- /dev/null
+++ b/pelican-plugins/test_data/themes/simple/templates/author.html
@@ -0,0 +1,7 @@
+{% extends "index.html" %}
+
+{% block title %}{{ SITENAME }} - Articles by {{ author }}{% endblock %}
+{% block content_title %}
+<h2>Articles by {{ author }}</h2>
+{% endblock %}
+
diff --git a/pelican-plugins/test_data/themes/simple/templates/base.html b/pelican-plugins/test_data/themes/simple/templates/base.html
new file mode 100644
index 0000000000000000000000000000000000000000..7973d774d456e2375e8559954eb1abbb9cdc64b3
--- /dev/null
+++ b/pelican-plugins/test_data/themes/simple/templates/base.html
@@ -0,0 +1,63 @@
+<!DOCTYPE html>
+<html lang="{{ DEFAULT_LANG }}">
+<head>
+        {% block head %}
+        <title>{% block title %}{{ SITENAME }}{% endblock title %}</title>
+        <meta charset="utf-8" />
+        {% if FEED_ALL_ATOM %}
+        <link href="{{ FEED_DOMAIN }}/{{ FEED_ALL_ATOM }}" type="application/atom+xml" rel="alternate" title="{{ SITENAME }} Full Atom Feed" />
+        {% endif %}
+        {% if FEED_ALL_RSS %}
+        <link href="{{ FEED_DOMAIN }}/{{ FEED_ALL_RSS }}" type="application/rss+xml" rel="alternate" title="{{ SITENAME }} Full RSS Feed" />
+        {% endif %}
+        {% if FEED_ATOM %}
+        <link href="{{ FEED_DOMAIN }}/{{ FEED_ATOM }}" type="application/atom+xml" rel="alternate" title="{{ SITENAME }} Atom Feed" />
+        {% endif %}
+        {% if FEED_RSS %}
+        <link href="{{ FEED_DOMAIN }}/{{ FEED_RSS }}" type="application/rss+xml" rel="alternate" title="{{ SITENAME }} RSS Feed" />
+        {% endif %}
+        {% if CATEGORY_FEED_ATOM and category %}
+        <link href="{{ FEED_DOMAIN }}/{{ CATEGORY_FEED_ATOM|format(category.slug) }}" type="application/atom+xml" rel="alternate" title="{{ SITENAME }} Categories Atom Feed" />
+        {% endif %}
+        {% if CATEGORY_FEED_RSS and category %}
+        <link href="{{ FEED_DOMAIN }}/{{ CATEGORY_FEED_RSS|format(category.slug) }}" type="application/rss+xml" rel="alternate" title="{{ SITENAME }} Categories RSS Feed" />
+        {% endif %}
+        {% if TAG_FEED_ATOM and tag %}
+        <link href="{{ FEED_DOMAIN }}/{{ TAG_FEED_ATOM|format(tag.slug) }}" type="application/atom+xml" rel="alternate" title="{{ SITENAME }} Tags Atom Feed" />
+        {% endif %}
+        {% if TAG_FEED_RSS and tag %}
+        <link href="{{ FEED_DOMAIN }}/{{ TAG_FEED_RSS|format(tag.slug) }}" type="application/rss+xml" rel="alternate" title="{{ SITENAME }} Tags RSS Feed" />
+        {% endif %}
+        {% endblock head %}
+</head>
+
+<body id="index" class="home">
+        <header id="banner" class="body">
+                <h1><a href="{{ SITEURL }}">{{ SITENAME }} <strong>{{ SITESUBTITLE }}</strong></a></h1>
+        </header><!-- /#banner -->
+        <nav id="menu"><ul>
+        {% for title, link in MENUITEMS %}
+            <li><a href="{{ link }}">{{ title }}</a></li>
+        {% endfor %}
+        {% if DISPLAY_PAGES_ON_MENU %}
+          {% for p in PAGES %}
+            <li{% if p == page %} class="active"{% endif %}><a href="{{ SITEURL }}/{{ p.url }}">{{ p.title }}</a></li>
+          {% endfor %}
+        {% else %}
+          {% if DISPLAY_CATEGORIES_ON_MENU %}
+            {% for cat, null in categories %}
+              <li{% if cat == category %} class="active"{% endif %}><a href="{{ SITEURL }}/{{ cat.url }}">{{ cat }}</a></li>
+            {% endfor %}
+          {% endif %}
+        {% endif %}
+        </ul></nav><!-- /#menu -->
+        {% block content %}
+        {% endblock %}
+        <footer id="contentinfo" class="body">
+                <address id="about" class="vcard body">
+                Proudly powered by <a href="http://getpelican.com/">Pelican</a>,
+                which takes great advantage of <a href="http://python.org">Python</a>.
+                </address><!-- /#about -->
+        </footer><!-- /#contentinfo -->
+</body>
+</html>
diff --git a/pelican-plugins/test_data/themes/simple/templates/categories.html b/pelican-plugins/test_data/themes/simple/templates/categories.html
new file mode 100644
index 0000000000000000000000000000000000000000..e29be0cacc97993126abcbce31e4a7fc8889086d
--- /dev/null
+++ b/pelican-plugins/test_data/themes/simple/templates/categories.html
@@ -0,0 +1,8 @@
+{% extends "base.html" %}
+{% block content %}
+<ul>
+{% for category, articles in categories %}
+    <li><a href="{{ SITEURL }}/{{ category.url }}">{{ category }}</a></li>
+{% endfor %}
+</ul>
+{% endblock %}
diff --git a/pelican-plugins/test_data/themes/simple/templates/category.html b/pelican-plugins/test_data/themes/simple/templates/category.html
new file mode 100644
index 0000000000000000000000000000000000000000..4e6fd24e465d978fb9eb35d51eaee47c08e1eb1c
--- /dev/null
+++ b/pelican-plugins/test_data/themes/simple/templates/category.html
@@ -0,0 +1,5 @@
+{% extends "index.html" %}
+{% block content_title %}
+<h2>Articles in the {{ category }} category</h2>
+{% endblock %}
+
diff --git a/pelican-plugins/test_data/themes/simple/templates/gosquared.html b/pelican-plugins/test_data/themes/simple/templates/gosquared.html
new file mode 100644
index 0000000000000000000000000000000000000000..f47efcf4755a7ad26ec9c1b9f125660845ece4ce
--- /dev/null
+++ b/pelican-plugins/test_data/themes/simple/templates/gosquared.html
@@ -0,0 +1,14 @@
+{% if GOSQUARED_SITENAME %}
+<script type="text/javascript">
+    var GoSquared={};
+    GoSquared.acct = "{{ GOSQUARED_SITENAME }}";
+    (function(w){
+        function gs(){
+            w._gstc_lt=+(new Date); var d=document;
+            var g = d.createElement("script"); g.type = "text/javascript"; g.async = true; g.src = "//d1l6p2sc9645hc.cloudfront.net/tracker.js";
+            var s = d.getElementsByTagName("script")[0]; s.parentNode.insertBefore(g, s);
+        }
+        w.addEventListener?w.addEventListener("load",gs,false):w.attachEvent("onload",gs);
+    })(window);
+</script>
+{% endif %}
diff --git a/pelican-plugins/test_data/themes/simple/templates/index.html b/pelican-plugins/test_data/themes/simple/templates/index.html
new file mode 100644
index 0000000000000000000000000000000000000000..5bb94dfc7c7a2c60e0a3e81c9c5b3dbdccf03c28
--- /dev/null
+++ b/pelican-plugins/test_data/themes/simple/templates/index.html
@@ -0,0 +1,22 @@
+{% extends "base.html" %}
+{% block content %}
+<section id="content">
+{% block content_title %}
+<h2>All articles</h2>
+{% endblock %}
+
+<ol id="post-list">
+{% for article in articles_page.object_list %}
+        <li><article class="hentry">
+                <header> <h2 class="entry-title"><a href="{{ SITEURL }}/{{ article.url }}" rel="bookmark" title="Permalink to {{ article.title|striptags }}">{{ article.title }}</a></h2> </header>
+                <footer class="post-info">
+                    <abbr class="published" title="{{ article.date.isoformat() }}"> {{ article.locale_date }} </abbr>
+                    {% if article.author %}<address class="vcard author">By <a class="url fn" href="{{ SITEURL }}/{{ article.author.url }}">{{ article.author }}</a></address>{% endif %}
+                </footer><!-- /.post-info -->
+                <div class="entry-content"> {{ article.summary }} </div><!-- /.entry-content -->
+        </article></li>
+{% endfor %}
+</ol><!-- /#posts-list -->
+{% include 'pagination.html' %}
+</section><!-- /#content -->
+{% endblock content %}
diff --git a/pelican-plugins/test_data/themes/simple/templates/page.html b/pelican-plugins/test_data/themes/simple/templates/page.html
new file mode 100644
index 0000000000000000000000000000000000000000..3a0dc4a9a49f8ffbfe520c59cf461d20a07bd9ee
--- /dev/null
+++ b/pelican-plugins/test_data/themes/simple/templates/page.html
@@ -0,0 +1,9 @@
+{% extends "base.html" %}
+{% block title %}{{ page.title }}{%endblock%}
+{% block content %}
+    <h1>{{ page.title }}</h1>
+    {% import 'translations.html' as translations with context %}
+    {{ translations.translations_for(page) }}
+
+    {{ page.content }}
+{% endblock %}
diff --git a/pelican-plugins/test_data/themes/simple/templates/pagination.html b/pelican-plugins/test_data/themes/simple/templates/pagination.html
new file mode 100644
index 0000000000000000000000000000000000000000..83c587ace928b690306e6f13947c919f27f6cf67
--- /dev/null
+++ b/pelican-plugins/test_data/themes/simple/templates/pagination.html
@@ -0,0 +1,15 @@
+{% if DEFAULT_PAGINATION %}
+<p class="paginator">
+    {% if articles_page.has_previous() %}
+        {% if articles_page.previous_page_number() == 1 %}
+            <a href="{{ SITEURL }}/{{ page_name }}.html">&laquo;</a>
+        {% else %}
+            <a href="{{ SITEURL }}/{{ page_name }}{{ articles_page.previous_page_number() }}.html">&laquo;</a>
+        {% endif %}
+    {% endif %}
+    Page {{ articles_page.number }} / {{ articles_paginator.num_pages }}
+    {% if articles_page.has_next() %}
+        <a href="{{ SITEURL }}/{{ page_name }}{{ articles_page.next_page_number() }}.html">&raquo;</a>
+    {% endif %}
+</p>
+{% endif %}
diff --git a/pelican-plugins/test_data/themes/simple/templates/tag.html b/pelican-plugins/test_data/themes/simple/templates/tag.html
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/pelican-plugins/test_data/themes/simple/templates/tags.html b/pelican-plugins/test_data/themes/simple/templates/tags.html
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/pelican-plugins/test_data/themes/simple/templates/translations.html b/pelican-plugins/test_data/themes/simple/templates/translations.html
new file mode 100644
index 0000000000000000000000000000000000000000..db8c372df0023c64c7585f0106842dadfc0bccf5
--- /dev/null
+++ b/pelican-plugins/test_data/themes/simple/templates/translations.html
@@ -0,0 +1,9 @@
+{% macro translations_for(article) %}
+{% if article.translations %}
+Translations: 
+{% for translation in article.translations %}
+<a href="{{ SITEURL }}/{{ translation.url }}">{{ translation.lang }}</a>
+{% endfor %}
+{% endif %}
+{% endmacro %}
+
diff --git a/pelican-plugins/textile_reader/Readme.textile b/pelican-plugins/textile_reader/Readme.textile
new file mode 100644
index 0000000000000000000000000000000000000000..8a60b650272aefd6d0f5241630388388f8227549
--- /dev/null
+++ b/pelican-plugins/textile_reader/Readme.textile
@@ -0,0 +1,16 @@
+h1. Textile Reader Plugin for Pelican
+
+p. This plugin adds support for "Textile markup":https://github.com/netcarver/textile via the "textile egg on pypi":https://pypi.python.org/pypi/textile .
+
+p. Input files are similar in format to Markdown files, in that they start with the post metadata in "Key: value" pairs, one per line. However, the metadata is ended by a line containing only "----", and then all that follows is the body content.  If the separator line is missing then the whole file is assumed to be content.
+
+h2. Example Input File
+
+<pre>Title: An Example Textile-formatted Input for Pelican
+Date: 2013-08-12
+Category: Plugins
+Tags: textile, pelican
+Author: Joey Coleman
+----
+p. Lorem ipsum dolor sit amet...
+</pre>
diff --git a/pelican-plugins/textile_reader/__init__.py b/pelican-plugins/textile_reader/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..5798041ba3854cf2638b493b4ea786ca913ccc49
--- /dev/null
+++ b/pelican-plugins/textile_reader/__init__.py
@@ -0,0 +1 @@
+from .textile_reader import *
diff --git a/pelican-plugins/textile_reader/textile_reader.py b/pelican-plugins/textile_reader/textile_reader.py
new file mode 100644
index 0000000000000000000000000000000000000000..9737202dfdf7990ddd592754c7cacda1333d0019
--- /dev/null
+++ b/pelican-plugins/textile_reader/textile_reader.py
@@ -0,0 +1,73 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals, print_function
+
+from pelican import signals
+from pelican.readers import BaseReader
+from pelican.utils import pelican_open
+
+try:
+    from textile import textile
+except ImportError:
+    textile = False
+
+
+class TextileReader(BaseReader):
+    """Reader for Textile files.  Written using the core MarkdownReader as
+a template.  Textile input files must be of the form:
+
+Title: An example
+Date: 2013-08-10
+----
+p. Lorem ipsum dolar sit amet...
+
+Specifically, the header values as with Markdown files, then four
+dashes, then the body.
+
+    """
+
+    enabled = bool(textile)
+    file_extensions = ['textile']
+
+    def __init__(self, *args, **kwargs):
+        super(TextileReader, self).__init__(*args, **kwargs)
+
+    def _parse_metadata(self, meta):
+        """Process the metadata dict, lowercasing the keys and textilizing the
+value of the 'summary' key (if present).  Keys that share the same
+lowercased form will be overridden in some arbitrary order.
+
+        """
+        output = {}
+        for name, value in meta.items():
+            name = name.lower()
+            if name == "summary":
+                value = textile(value)
+            output[name] = self.process_metadata(name, value)
+        return output
+
+    def read(self, source_path):
+        """Parse content and metadata of textile files."""
+
+        with pelican_open(source_path) as text:
+            parts = text.split('----', 1)
+            if len(parts) == 2:
+                headerlines = parts[0].splitlines()
+                headerpairs = map(lambda l: l.split(':', 1), headerlines)
+                headerdict = {pair[0]: pair[1].strip()
+                              for pair in headerpairs
+                              if len(pair) == 2}
+                metadata = self._parse_metadata(headerdict)
+                content = textile(parts[1])
+            else:
+                metadata = {}
+                content = textile(text)
+
+        return content, metadata
+
+
+def add_reader(readers):
+    readers.reader_classes['textile'] = TextileReader
+
+
+def register():
+    signals.readers_init.connect(add_reader)
diff --git a/pelican-plugins/thumbnailer/Readme.md b/pelican-plugins/thumbnailer/Readme.md
new file mode 100644
index 0000000000000000000000000000000000000000..c4297ed632180beae479e0c4ec39a9da8c2c797f
--- /dev/null
+++ b/pelican-plugins/thumbnailer/Readme.md
@@ -0,0 +1,37 @@
+Thumbnail Creation of images
+============================
+
+**NOTE:** [This plugin has been moved to its own repository](https://github.com/pelican-plugins/thumbnailer). Please file any issues/PRs there. Once all plugins have been migrated to the [new Pelican Plugins organization](https://github.com/pelican-plugins), this monolithic repository will be archived.
+
+-------------------------------------------------------------------------------
+
+This plugin creates thumbnails for all of the images found under a specific directory, in various thumbnail sizes.
+It requires `PIL` to function properly since `PIL` is used to resize the images, and the thumbnail will only be re-built
+if it doesn't already exist (to save processing time).
+
+Installation
+-------------
+
+Set up like any other plugin, making sure to set `PLUGIN_PATH` and add `thumbnailer` to the `PLUGINS` list.
+
+[PIL](http://www.pythonware.com/products/pil/) or [Pillow](http://pillow.readthedocs.org/en/latest/installation.html#)
+is required. If you choose Pillow, you need to install some additional
+[external libraries](http://www.pythonware.com/products/pil/) to add support for image types like `jpg`.
+
+Configuration
+-------------
+
+* `IMAGE_PATH` is the path to the image directory. It should reside inside your content directory, and defaults to "pictures".
+* `THUMBNAIL_DIR` is the path to the output sub-directory where the thumbnails are generated.
+* `THUMBNAIL_SIZES` is a dictionary mapping size name to size specifications.
+  The generated filename will be `originalname_thumbnailname.ext` unless `THUMBNAIL_KEEP_NAME` is set.
+* `THUMBNAIL_KEEP_NAME` is a Boolean that, if set, puts the file with the original name in a thumbnailname folder, named like the key in `THUMBNAIL_SIZES`.
+* `THUMBNAIL_KEEP_TREE` is a Boolean that, if set, saves the image directory tree.
+* `THUMBNAIL_INCLUDE_REGEX` is an optional string that is used as regular expression to restrict thumbnailing to matching files. By default all files not starting with a dot are respected.
+
+Sizes can be specified using any of the following formats:
+
+* wxh will resize to exactly wxh cropping as necessary to get that size
+* wx? will resize so that the width is the specified size, and the height will scale to retain aspect ratio
+* ?xh same as wx? but will height being a set size
+* s is a shorthand for wxh where w=h
diff --git a/pelican-plugins/thumbnailer/__init__.py b/pelican-plugins/thumbnailer/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..20797b181eb8ced907631b5a866fa1c0727ad273
--- /dev/null
+++ b/pelican-plugins/thumbnailer/__init__.py
@@ -0,0 +1 @@
+from .thumbnailer import *
\ No newline at end of file
diff --git a/pelican-plugins/thumbnailer/test_data/expected_exact.jpg b/pelican-plugins/thumbnailer/test_data/expected_exact.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..58197925c41b08a12247d05bd2b86904ea0fb2b1
Binary files /dev/null and b/pelican-plugins/thumbnailer/test_data/expected_exact.jpg differ
diff --git a/pelican-plugins/thumbnailer/test_data/expected_height.jpg b/pelican-plugins/thumbnailer/test_data/expected_height.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..6459410b349d9d9a44215f26d8794fb0213b38bf
Binary files /dev/null and b/pelican-plugins/thumbnailer/test_data/expected_height.jpg differ
diff --git a/pelican-plugins/thumbnailer/test_data/expected_square.jpg b/pelican-plugins/thumbnailer/test_data/expected_square.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..de99e5bed56612c19f34802f7c50a03a98260afe
Binary files /dev/null and b/pelican-plugins/thumbnailer/test_data/expected_square.jpg differ
diff --git a/pelican-plugins/thumbnailer/test_data/expected_width.jpg b/pelican-plugins/thumbnailer/test_data/expected_width.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..9c2efc6d486b2af07c33ec3699c7d007b3d6f075
Binary files /dev/null and b/pelican-plugins/thumbnailer/test_data/expected_width.jpg differ
diff --git a/pelican-plugins/thumbnailer/test_data/sample_image.jpg b/pelican-plugins/thumbnailer/test_data/sample_image.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..cc83880237a931fab0f16ca56250769e87fb3f04
Binary files /dev/null and b/pelican-plugins/thumbnailer/test_data/sample_image.jpg differ
diff --git a/pelican-plugins/thumbnailer/test_data/subdir/sample_image.jpg b/pelican-plugins/thumbnailer/test_data/subdir/sample_image.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..cc83880237a931fab0f16ca56250769e87fb3f04
Binary files /dev/null and b/pelican-plugins/thumbnailer/test_data/subdir/sample_image.jpg differ
diff --git a/pelican-plugins/thumbnailer/test_thumbnails.py b/pelican-plugins/thumbnailer/test_thumbnails.py
new file mode 100644
index 0000000000000000000000000000000000000000..090036e1197532c38034ae8a43378c46de922b0d
--- /dev/null
+++ b/pelican-plugins/thumbnailer/test_thumbnails.py
@@ -0,0 +1,61 @@
+from thumbnailer import Resizer
+from unittest import TestCase, main
+import os
+from PIL import Image
+
+class ThumbnailerTests(TestCase):
+
+    def path(self, filename):
+        return os.path.join(self.img_path, filename)
+
+    def setUp(self):
+        self.img_path = os.path.join(os.path.dirname(__file__), "test_data")
+        self.img = Image.open(self.path("sample_image.jpg"))
+
+    def testSquare(self):
+        r = Resizer('square', '100', self.img_path)
+        output = r.resize(self.img)
+        self.assertEqual((100, 100), output.size)
+
+    def testExact(self):
+        r = Resizer('exact', '250x100', self.img_path)
+        output = r.resize(self.img)
+        self.assertEqual((250, 100), output.size)
+
+    def testWidth(self):
+        r = Resizer('aspect', '250x?', self.img_path)
+        output = r.resize(self.img)
+        self.assertEqual((250, 166), output.size)
+
+    def testHeight(self):
+        r = Resizer('aspect', '?x250', self.img_path)
+        output = r.resize(self.img)
+        self.assertEqual((376, 250), output.size)
+
+class ThumbnailerFilenameTest(TestCase):
+
+    def path(self, *parts):
+        return os.path.join(self.img_path, *parts)
+
+    def setUp(self):
+        self.img_path = os.path.join(os.path.dirname(__file__), "test_data")
+
+    def testRoot(self):
+        """Test a file that is in the root of img_path."""
+        r = Resizer('square', '100', self.img_path)
+        new_name = r.get_thumbnail_name(self.path('sample_image.jpg'))
+        self.assertEqual('sample_image_square.jpg', new_name)
+
+    def testRootWithSlash(self):
+        r = Resizer('square', '100', self.img_path + '/')
+        new_name = r.get_thumbnail_name(self.path('sample_image.jpg'))
+        self.assertEqual('sample_image_square.jpg', new_name)
+
+    def testSubdir(self):
+        """Test a file that is in a sub-directory of img_path."""
+        r = Resizer('square', '100', self.img_path)
+        new_name = r.get_thumbnail_name(self.path('subdir', 'sample_image.jpg'))
+        self.assertEqual('subdir/sample_image_square.jpg', new_name)
+
+if __name__=="__main__":
+    main()
diff --git a/pelican-plugins/thumbnailer/thumbnailer.py b/pelican-plugins/thumbnailer/thumbnailer.py
new file mode 100644
index 0000000000000000000000000000000000000000..5925c54d65d6b0787562fc63fd44102dbb7598a9
--- /dev/null
+++ b/pelican-plugins/thumbnailer/thumbnailer.py
@@ -0,0 +1,210 @@
+import os
+import os.path as path
+import re
+from pelican import signals
+
+import logging
+logger = logging.getLogger(__name__)
+
+try:
+    from PIL import Image, ImageOps
+    enabled = True
+except ImportError:
+    logger.warning("Unable to load PIL, disabling thumbnailer")
+    enabled = False
+
+DEFAULT_IMAGE_DIR = "pictures"
+DEFAULT_THUMBNAIL_DIR = "thumbnails"
+DEFAULT_THUMBNAIL_SIZES = {
+    'thumbnail_square': '150',
+    'thumbnail_wide': '150x?',
+    'thumbnail_tall': '?x150',
+}
+DEFAULT_TEMPLATE = """<a href="{url}" rel="shadowbox" title="{filename}"><img src="{thumbnail}" alt="{filename}"></a>"""
+DEFAULT_GALLERY_THUMB = "thumbnail_square"
+
+class Resizer(object):
+    """ Resizes based on a text specification, see readme """
+
+    REGEX = re.compile(r'(\d+|\?)x(\d+|\?)')
+
+    def __init__(self, name, spec, root):
+        self._name = name
+        self._spec = spec
+        # The location of input images from _image_path.
+        self._root = root
+
+    def _null_resize(self, w, h, image):
+        return image
+
+    def _exact_resize(self, w, h, image):
+        retval = ImageOps.fit(image, (w,h), Image.BICUBIC)
+        return retval
+
+    def _aspect_resize(self, w, h, image):
+        retval = image.copy()
+        retval.thumbnail((w, h), Image.ANTIALIAS)
+
+        return retval
+
+    def resize(self, image):
+        resizer = self._null_resize
+
+        # Square resize and crop
+        if 'x' not in self._spec:
+            resizer = self._exact_resize
+            targetw = int(self._spec)
+            targeth = targetw
+        else:
+            matches = self.REGEX.search(self._spec)
+            tmpw = matches.group(1)
+            tmph = matches.group(2)
+
+            # Full Size
+            if tmpw == '?' and tmph == '?':
+                targetw = image.size[0]
+                targeth = image.size[1]
+                resizer = self._null_resize
+
+            # Set Height Size
+            if tmpw == '?':
+                targetw = image.size[0]
+                targeth = int(tmph)
+                resizer = self._aspect_resize
+
+            # Set Width Size
+            elif tmph == '?':
+                targetw = int(tmpw)
+                targeth = image.size[1]
+                resizer = self._aspect_resize
+
+            # Scale and Crop
+            else:
+                targetw = int(tmpw)
+                targeth = int(tmph)
+                resizer = self._exact_resize
+
+        logger.debug("Using resizer {0}".format(resizer.__name__))
+        return resizer(targetw, targeth, image)
+
+    def get_thumbnail_name(self, in_path):
+        # Find the partial path + filename beyond the input image directory.
+        prefix = path.commonprefix([in_path, self._root])
+        new_filename = in_path[len(prefix):]
+        if new_filename.startswith('/'):
+            new_filename = new_filename[1:]
+
+        # Generate the new filename.
+        (basename, ext) = path.splitext(new_filename)
+        return "{0}_{1}{2}".format(basename, self._name, ext)
+
+    def resize_file_to(self, in_path, out_path, keep_filename=False):
+        """ Given a filename, resize and save the image per the specification into out_path
+
+        :param in_path: path to image file to save.  Must be supported by PIL
+        :param out_path: path to the directory root for the outputted thumbnails to be stored
+        :return: None
+        """
+        if keep_filename:
+            filename = path.join(out_path, path.basename(in_path))
+        else:
+            filename = path.join(out_path, self.get_thumbnail_name(in_path))
+        out_path = path.dirname(filename)
+        if not path.exists(out_path):
+            os.makedirs(out_path)
+        if not path.exists(filename):
+            try:
+                image = Image.open(in_path)
+                thumbnail = self.resize(image)
+                thumbnail.save(filename)
+                logger.info("Generated Thumbnail {0}".format(path.basename(filename)))
+            except IOError:
+                logger.info("Generating Thumbnail for {0} skipped".format(path.basename(filename)))
+
+
+def resize_thumbnails(pelican):
+    """ Resize a directory tree full of images into thumbnails
+
+    :param pelican: The pelican instance
+    :return: None
+    """
+    global enabled
+    if not enabled:
+        return
+
+    in_path = _image_path(pelican)
+
+    include_regex = pelican.settings.get('THUMBNAIL_INCLUDE_REGEX')
+    if include_regex:
+        pattern = re.compile(include_regex)
+        is_included = lambda name: pattern.match(name)
+    else:
+        is_included = lambda name: not name.startswith('.')
+
+    sizes = pelican.settings.get('THUMBNAIL_SIZES', DEFAULT_THUMBNAIL_SIZES)
+    resizers = dict((k, Resizer(k, v, in_path)) for k,v in sizes.items())
+    logger.debug("Thumbnailer Started")
+    for dirpath, _, filenames in os.walk(in_path):
+        for filename in filenames:
+            if is_included(filename):
+                for name, resizer in resizers.items():
+                    in_filename = path.join(dirpath, filename)
+                    out_path = get_out_path(pelican, in_path, in_filename, name)
+                    resizer.resize_file_to(
+                        in_filename,
+                        out_path, pelican.settings.get('THUMBNAIL_KEEP_NAME'))
+
+
+def get_out_path(pelican, in_path, in_filename, name):
+    base_out_path = path.join(pelican.settings['OUTPUT_PATH'],
+                         pelican.settings.get('THUMBNAIL_DIR', DEFAULT_THUMBNAIL_DIR))
+    logger.debug("Processing thumbnail {0}=>{1}".format(in_filename, name))
+    if pelican.settings.get('THUMBNAIL_KEEP_NAME', False):
+        if pelican.settings.get('THUMBNAIL_KEEP_TREE', False):
+            return path.join(base_out_path, name, path.dirname(path.relpath(in_filename, in_path)))
+        else:
+            return path.join(base_out_path, name)
+    else:
+        return base_out_path
+
+
+def _image_path(pelican):
+    return path.join(pelican.settings['PATH'],
+        pelican.settings.get("IMAGE_PATH", DEFAULT_IMAGE_DIR)).rstrip('/')
+
+
+def expand_gallery(generator, metadata):
+    """ Expand a gallery tag to include all of the files in a specific directory under IMAGE_PATH
+
+    :param pelican: The pelican instance
+    :return: None
+    """
+    if "gallery" not in metadata or metadata['gallery'] is None:
+        return  # If no gallery specified, we do nothing
+
+    lines = [ ]
+    base_path = _image_path(generator)
+    in_path = path.join(base_path, metadata['gallery'])
+    template = generator.settings.get('GALLERY_TEMPLATE', DEFAULT_TEMPLATE)
+    thumbnail_name = generator.settings.get("GALLERY_THUMBNAIL", DEFAULT_GALLERY_THUMB)
+    thumbnail_prefix = generator.settings.get("")
+    resizer = Resizer(thumbnail_name, '?x?', base_path)
+    for dirpath, _, filenames in os.walk(in_path):
+        for filename in filenames:
+            if not filename.startswith('.'):
+                url = path.join(dirpath, filename).replace(base_path, "")[1:]
+                url = path.join('/static', generator.settings.get('IMAGE_PATH', DEFAULT_IMAGE_DIR), url).replace('\\', '/')
+                logger.debug("GALLERY: {0}".format(url))
+                thumbnail = resizer.get_thumbnail_name(filename)
+                thumbnail = path.join('/', generator.settings.get('THUMBNAIL_DIR', DEFAULT_THUMBNAIL_DIR), thumbnail).replace('\\', '/')
+                lines.append(template.format(
+                    filename=filename,
+                    url=url,
+                    thumbnail=thumbnail,
+                ))
+    metadata['gallery_content'] = "\n".join(lines)
+
+
+def register():
+    signals.finalized.connect(resize_thumbnails)
+    signals.article_generator_context.connect(expand_gallery)
diff --git a/pelican-plugins/tipue_search/README.md b/pelican-plugins/tipue_search/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..61291edfab2a702ad27876846855f810ea7dce12
--- /dev/null
+++ b/pelican-plugins/tipue_search/README.md
@@ -0,0 +1,99 @@
+Tipue Search
+============
+
+> :warning: **Instead of this plugin, please use the [Pelican Search](https://github.com/pelican-plugins/search) plugin.**
+
+> :warning: **This plugin, and the jQuery code upon which it depends, _is abandoned and no longer maintained_.**
+
+
+Purpose
+-------
+
+Serialize generated HTML content to a JS variable for use by the Tipue static search jQuery plugin.
+
+
+Installation
+------------
+
+This plugin can be installed via:
+
+    python -m pip install -e "git+https://github.com/getpelican/pelican-plugins/#egg=pelican-tipue-search&subdirectory=tipue_search"
+
+
+Why Do You Need It?
+-------------------
+
+Static sites do not offer search feature out of the box. [Tipue Search](https://web.archive.org/web/20200703134724/https://tipue.com/search/)
+is a jQuery plugin that search the static site without using any third party service, like DuckDuckGo or Google.
+
+Tipue search requires the textual content of site in a JS variable.
+
+
+How Tipue Search Works
+----------------------
+
+Tipue Search serializes the generated HTML into JSON and saves it into a JS variable. Format of JSON is as follows
+
+```javascript
+var tipuesearch = {
+    "pages": [
+        {
+            "text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero. Sed cursus ante dapibus diam. Sed nisi. Nulla quis sem at nibh elementum imperdiet. Duis sagittis ipsum. Praesent mauris. Fusce nec tellus sed augue semper porta. Mauris massa. Vestibulum lacinia arcu eget nulla. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Curabitur sodales ligula in libero.",
+            "tags": "Example Category",
+            "url" : "http://oncrashreboot.com/plugin-example.html",
+            "title": "Everything you want to know about Lorem Ipsum"
+        },
+        {
+            "text": "Sed dignissim lacinia nunc. Curabitur tortor. Pellentesque nibh. Aenean quam. In scelerisque sem at dolor. Maecenas mattis. Sed convallis tristique sem. Proin ut ligula vel nunc egestas porttitor. Morbi lectus risus, iaculis vel, suscipit quis, luctus non, massa. Fusce ac turpis quis ligula lacinia aliquet. Mauris ipsum. Nulla metus metus, ullamcorper vel, tincidunt sed, euismod in, nibh.",
+            "tags": "Example Category",
+            "url" : "http://oncrashreboot.com/plugin-example-2.html",
+            "title": "Review of the book Lorem Ipsum"
+        }
+    ]
+};
+```
+
+JS variable is written to file `tipuesearch_content.js` which is created in the root of `output` directory.
+
+
+How to Use
+----------
+
+Your theme needs to have Tipue Search properly configured in it. [Official documentation](https://web.archive.org/web/20200703134724/https://tipue.com/search/help/) has the required details.
+
+In addition to the instructions from Tipue, the following has to be added in `pelicanconf.py`.
+
+```python
+DIRECT_TEMPLATES = ['index', 'tags', 'categories', 'authors', 'archives', 'search']
+```
+
+Furthermore, the generated JavaScript variable has to be sourced in the relevant HTML pages.
+
+```html
+<script src="{{ SITEURL }}tipuesearch_content.js"></script>
+```
+
+Pelican [Plumage theme](https://github.com/kdeldycke/plumage) has Tipue Search configured. Check out its code to understand the configuration.
+
+
+Backward Compatibility
+----------------------
+
+This plugin requires Tipue Search Version 7.0 or higher.
+
+Tipue used to expect content in a JSON file. Around Version 7.0, Tipue maintainers made a switch to JavaScript variable. tipue_search plugin was updated to reflect this change in commit `4a5f171fc`. Latest version of tipue_search plugin will not work with older versions of Tipue Search.
+
+If you are using older Tipue Search, prior to 7.0 release, then you will find old version of tipue_search plugin in commit `2dcdca8c8`.
+
+
+Source Archive
+--------------
+
+The Tipue Search project itself seems to have been long abandoned. There is no
+longer any official canonical reference of source code or documentation. There
+only some artifacts left that were archived by the community:
+
+* [Archived Tipue Search homepage](https://web.archive.org/web/20200703134724/https://tipue.com/search/)
+* [Archived Tipue plugin help page](https://web.archive.org/web/20200703134724/https://tipue.com/search/help/)
+* [Archived Tipue Search code v7.1](https://web.archive.org/web/20200703134724/https://www.tipue.com/search/tipuesearch.zip)
+* [GitHub repository copy](https://notabug.org/jorgesumle/Tipue-Search)
diff --git a/pelican-plugins/tipue_search/__init__.py b/pelican-plugins/tipue_search/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/pelican-plugins/tipue_search/pelican/__init__.py b/pelican-plugins/tipue_search/pelican/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/pelican-plugins/tipue_search/pelican/plugins/__init__.py b/pelican-plugins/tipue_search/pelican/plugins/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/pelican-plugins/tipue_search/pelican/plugins/tipue_search/__init__.py b/pelican-plugins/tipue_search/pelican/plugins/tipue_search/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..ebd6c0612d02b358eb48ae9ff6005c9ce28aef72
--- /dev/null
+++ b/pelican-plugins/tipue_search/pelican/plugins/tipue_search/__init__.py
@@ -0,0 +1 @@
+from .tipue_search import *
diff --git a/pelican-plugins/tipue_search/pelican/plugins/tipue_search/tipue_search.py b/pelican-plugins/tipue_search/pelican/plugins/tipue_search/tipue_search.py
new file mode 100644
index 0000000000000000000000000000000000000000..11e326124b775956a71d14726a87497465d5f87d
--- /dev/null
+++ b/pelican-plugins/tipue_search/pelican/plugins/tipue_search/tipue_search.py
@@ -0,0 +1,111 @@
+# -*- coding: utf-8 -*-
+"""
+Tipue Search
+============
+
+A Pelican plugin to serialize generated HTML to JSON
+that can be used by jQuery plugin - Tipue Search.
+
+Copyright (c) Talha Mansoor
+"""
+
+from __future__ import unicode_literals
+
+import os.path
+import json
+from bs4 import BeautifulSoup
+from codecs import open
+try:
+    from urlparse import urljoin
+except ImportError:
+    from urllib.parse import urljoin
+
+from pelican import signals
+
+
+class Tipue_Search_JSON_Generator(object):
+
+    def __init__(self, context, settings, path, theme, output_path, *null):
+
+        self.output_path = output_path
+        self.context = context
+        self.siteurl = settings.get('SITEURL')
+        self.relative_urls = settings.get('RELATIVE_URLS')
+        self.tpages = settings.get('TEMPLATE_PAGES')
+        self.output_path = output_path
+        self.json_nodes = []
+
+
+    def create_json_node(self, page):
+
+        if getattr(page, 'status', 'published') != 'published':
+            return
+
+        soup_title = BeautifulSoup(page.title.replace('&nbsp;', ' '), 'html.parser')
+        page_title = soup_title.get_text(' ', strip=True).replace('“', '"').replace('”', '"').replace('’', "'").replace('^', '&#94;')
+
+        soup_text = BeautifulSoup(page.content, 'html.parser')
+        page_text = soup_text.get_text(' ', strip=True).replace('“', '"').replace('”', '"').replace('’', "'").replace('¶', ' ').replace('^', '&#94;')
+        page_text = ' '.join(page_text.split())
+
+        page_category = page.category.name if getattr(page, 'category', 'None') != 'None' else ''
+
+        page_url = '.'
+        if page.url:
+            page_url = page.url if self.relative_urls else (self.siteurl + '/' + page.url)
+
+        node = {'title': page_title,
+                'text': page_text,
+                'tags': page_category,
+                'url': page_url,
+                'loc': page_url} # changed from 'url' following http://blog.siphos.be/2015/08/updates-on-my-pelican-adventure/ (an update to Pelican made it not work, because the update (e.g., in the theme folder, static/tipuesearch/tipuesearch.js is looking for the 'loc' attribute.
+
+        self.json_nodes.append(node)
+
+
+    def create_tpage_node(self, srclink):
+
+        srcfile = open(os.path.join(self.output_path, self.tpages[srclink]), encoding='utf-8')
+        soup = BeautifulSoup(srcfile, 'html.parser')
+        page_title = soup.title.string if soup.title is not None else ''
+        page_text = soup.get_text()
+
+        # Should set default category?
+        page_category = ''
+        page_url = urljoin(self.siteurl, self.tpages[srclink])
+
+        node = {'title': page_title,
+                'text': page_text,
+                'tags': page_category,
+                'url': page_url}
+
+        self.json_nodes.append(node)
+
+
+    def generate_output(self, writer):
+        path = os.path.join(self.output_path, 'tipuesearch_content.js')
+
+        pages = self.context['pages'] + self.context['articles']
+
+        for article in self.context['articles']:
+            pages += article.translations
+
+        for srclink in self.tpages:
+            self.create_tpage_node(srclink)
+
+        for page in pages:
+            self.create_json_node(page)
+        root_node = {'pages': self.json_nodes}
+        
+        root_node_js = 'var tipuesearch = ' + json.dumps(root_node, separators=(',', ':'), ensure_ascii=False) + ';'
+
+        with open(path, 'w', encoding='utf-8') as fd:
+            fd.write(root_node_js)
+
+
+def get_generators(generators):
+    return Tipue_Search_JSON_Generator
+
+
+def register():
+    signals.get_generators.connect(get_generators)
diff --git a/pelican-plugins/tipue_search/setup.py b/pelican-plugins/tipue_search/setup.py
new file mode 100644
index 0000000000000000000000000000000000000000..e6a85dc44e6921fab2a9212fa850be7e785015da
--- /dev/null
+++ b/pelican-plugins/tipue_search/setup.py
@@ -0,0 +1,32 @@
+# -*- coding: utf-8 -*-
+from setuptools import setup
+
+packages = \
+['pelican', 'pelican.plugins.tipue_search']
+
+package_data = \
+{'': ['*']}
+
+install_requires = \
+['beautifulsoup4>=4.9.1,<5.0.0', 'pelican>=4.5,<5.0']
+
+extras_require = \
+{'markdown': ['markdown>=3.2.2,<4.0.0']}
+
+setup_kwargs = {
+    'name': 'pelican-tipue-search',
+    'version': '0.0.0',
+    'description': 'Serialize generated HTML content to a JS variable for use by the Tipue static search jQuery plugin',
+    'long_description': 'Tipue Search\n============\n\nA Pelican plugin to serialize generated HTML to a JS variable that can be used by jQuery plugin - Tipue Search.\n\nCopyright (c) Talha Mansoor\n\nAuthor          | Talha Mansoor\n----------------|-----\nAuthor Email    | talha131@gmail.com \nAuthor Homepage | http://onCrashReboot.com \nGithub Account  | https://github.com/talha131 \n\nWhy do you need it?\n===================\n\nStatic sites do not offer search feature out of the box. [Tipue Search](http://www.tipue.com/search/)\nis a jQuery plugin that search the static site without using any third party service, like DuckDuckGo or Google.\n\nTipue search requires the textual content of site in a JS variable.\n\nRequirements\n============\n\nTipue Search requires BeautifulSoup.\n\n```bash\npip install beautifulsoup4\n```\n\nHow Tipue Search works\n=========================\n\nTipue Search serializes the generated HTML into JSON and saves it into a JS variable. Format of JSON is as follows\n\n```javascript\nvar tipuesearch = {\n    "pages": [\n        { \n            "text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero. Sed cursus ante dapibus diam. Sed nisi. Nulla quis sem at nibh elementum imperdiet. Duis sagittis ipsum. Praesent mauris. Fusce nec tellus sed augue semper porta. Mauris massa. Vestibulum lacinia arcu eget nulla. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Curabitur sodales ligula in libero.",\n            "tags": "Example Category",\n            "url" : "http://oncrashreboot.com/plugin-example.html",\n            "title": "Everything you want to know about Lorem Ipsum"\n        },\n        { \n            "text": "Sed dignissim lacinia nunc. Curabitur tortor. Pellentesque nibh. Aenean quam. In scelerisque sem at dolor. Maecenas mattis. Sed convallis tristique sem. Proin ut ligula vel nunc egestas porttitor. Morbi lectus risus, iaculis vel, suscipit quis, luctus non, massa. Fusce ac turpis quis ligula lacinia aliquet. Mauris ipsum. Nulla metus metus, ullamcorper vel, tincidunt sed, euismod in, nibh.",\n            "tags": "Example Category",\n            "url" : "http://oncrashreboot.com/plugin-example-2.html",\n            "title": "Review of the book Lorem Ipsum"\n        }\n    ]\n};\n```\n\nJS variable is written to file `tipuesearch_content.js` which is created in the root of `output` directory.\n\nHow to use\n==========\n\nYour theme needs to have Tipue Search properly configured in it. [Official documentation](http://www.tipue.com/search/help/) has the required details.\n\nIn addition to the instructions from Tipue, the following has to be added in `pelicanconf.py`.\n\n```python\nPLUGIN_PATH = \'plugins\'\nPLUGINS = [\'tipue_search\']\nDIRECT_TEMPLATES = [\'index\', \'tags\', \'categories\', \'authors\', \'archives\', \'search\']\n```\n\nFurthermore, the generated JavaScript variable has to be sourced in the relevant html pages.\n\n```html\n<script src="{{ SITEURL }}tipuesearch_content.js"></script>\n```\n\nPelican [Elegant Theme](https://github.com/talha131/pelican-elegant) and [Plumage theme](https://github.com/kdeldycke/plumage) have Tipue Search configured. You can view their code to understand the configuration.\n\nBackward Compatibility\n======================\n\nThis plugin requires Tipue Search Version 7.0 or higher.\n\nTipue used to expect content in a json file. Around Version 7.0, Tipue maintainers made a switch to JavaScript variable. tipue_search plugin was updated to reflect this change in commit `4a5f171fc`. Latest version of tipue_search plugin will not work with older versions of Tipue Search.\n\nIf you are using older Tipue Search, prior to 7.0 release, then you will find old version of tipue_search plugin in commit `2dcdca8c8`. \n',
+    'author': 'Talha Mansoor',
+    'author_email': 'talha131@gmail.com',
+    'url': 'https://github.com/getpelican/pelican-plugins/tree/master/tipue_search',
+    'packages': packages,
+    'package_data': package_data,
+    'install_requires': install_requires,
+    'extras_require': extras_require,
+    'python_requires': '>=3.6,<4.0',
+}
+
+
+setup(**setup_kwargs)
diff --git a/pelican-plugins/touch/README.rst b/pelican-plugins/touch/README.rst
new file mode 100644
index 0000000000000000000000000000000000000000..2f2321940f0c1106ddc7beb71f07848c0be9d56b
--- /dev/null
+++ b/pelican-plugins/touch/README.rst
@@ -0,0 +1,12 @@
+Touch Plugin
+############
+
+**NOTE:** `This plugin has been moved to its own repository <https://github.com/pelican-plugins/touch>`_.
+Please file any issues/PRs there. Once all plugins have been migrated to the
+`new Pelican Plugins organization <https://github.com/pelican-plugins>`_,
+this monolithic repository will be archived.
+
+A simple plugin doing a touch on your generated files using the date metadata
+from the content.
+
+This helps, among other things, to have the web server gently manage the cache.
diff --git a/pelican-plugins/touch/__init__.py b/pelican-plugins/touch/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..49b1fd2070e0533288df0cb7fc85584ac3757d73
--- /dev/null
+++ b/pelican-plugins/touch/__init__.py
@@ -0,0 +1,36 @@
+from pelican import signals
+
+import logging
+import os
+import time
+
+
+logger = logging.getLogger(__name__)
+
+
+def set_file_utime(path, datetime):
+    mtime = time.mktime(datetime.timetuple())
+    logger.info('touching %s', path)
+    os.utime(path, (mtime, mtime))
+
+
+def touch_file(path, context):
+    content = context.get('article', context.get('page'))
+    page = context.get('articles_page')
+    dates = context.get('dates')
+
+    if content and hasattr(content, 'date'):
+        set_file_utime(path, content.date)
+    elif page:
+        set_file_utime(path, max(x.date for x in page.object_list))
+    elif dates:
+        set_file_utime(path, max(x.date for x in dates))
+
+
+def touch_feed(path, context, feed):
+    set_file_utime(path, max(x['pubdate'] for x in feed.items))
+
+
+def register():
+    signals.content_written.connect(touch_file)
+    signals.feed_written.connect(touch_feed)
diff --git a/pelican-plugins/twitter_bootstrap_rst_directives/Demo.rst b/pelican-plugins/twitter_bootstrap_rst_directives/Demo.rst
new file mode 100644
index 0000000000000000000000000000000000000000..c38ae10ee2121294e866e649f36bd6df19f2c96c
--- /dev/null
+++ b/pelican-plugins/twitter_bootstrap_rst_directives/Demo.rst
@@ -0,0 +1,118 @@
+This will be turned into :abbr:`HTML (HyperText Markup Language)`.
+
+Love this music :glyph:`music` 
+
+.. role:: story_time_glyph(glyph)
+    :target: http://www.youtube.com/watch?v=5g8ykQLYnX0
+    :class: small text-info  
+
+Love this music :story_time_glyph:`music` 
+
+This is an example of code: :code:`<example>`.
+
+This is an example of kbd: :kbd:`<example>`.
+
+
+.. label-default::
+    
+    This is a default label content
+
+.. label-primary::
+    
+    This is a primary label content
+
+.. label-success::
+    
+    This is a success label content
+
+.. label-info::
+    
+    This is a info label content
+
+.. label-warning::
+    
+    This is a warning label content
+
+.. label-danger::
+    
+    This is a danger label content
+
+
+.. panel-default::
+    :title: panel default title
+    
+    This is a default panel content
+
+.. panel-primary::
+    :title: panel primary title
+    
+    This is a primary panel content
+
+.. panel-success::
+    :title: panel success title
+    
+    This is a success panel content
+
+.. panel-info::
+    :title: panel info title
+    
+    This is a info panel content
+
+.. panel-warning::
+    :title: panel warning title
+    
+    This is a warning panel content
+
+.. panel-danger::
+    :title: panel danger title
+    
+    This is a danger panel content
+
+
+.. alert-success::
+    
+    This is a success alert content
+
+.. alert-info::
+    
+    This is a info alert content
+
+.. alert-warning::
+    
+    This is a warning alert content
+
+.. alert-danger::
+    
+    This is a danger alert content
+
+        
+.. media:: http://stuffkit.com/wp-content/uploads/2012/11/Worlds-Most-Beautiful-Lady-Camilla-Belle-HD-Photos-4.jpg
+                :height: 750
+                :width: 1000
+                :scale: 20
+                :target: http://www.google.com
+                :alt: Camilla Belle
+                :position: left
+
+                .. class:: h3
+
+                    left position
+
+                This image is not mine. Credit goes to http://stuffkit.com
+                
+
+
+.. media:: http://stuffkit.com/wp-content/uploads/2012/11/Worlds-Most-Beautiful-Lady-Camilla-Belle-HD-Photos-4.jpg
+                :height: 750
+                :width: 1000
+                :scale: 20
+                :target: http://www.google.com
+                :alt: Camilla Belle
+                :position: right
+
+                .. class:: h3
+
+                    right position
+
+
+                This image is not mine. Credit goes to http://stuffkit.com
\ No newline at end of file
diff --git a/pelican-plugins/twitter_bootstrap_rst_directives/Readme.rst b/pelican-plugins/twitter_bootstrap_rst_directives/Readme.rst
new file mode 100644
index 0000000000000000000000000000000000000000..f84417c2e8a79ca2c6caa5a3d2b20872d45eef00
--- /dev/null
+++ b/pelican-plugins/twitter_bootstrap_rst_directives/Readme.rst
@@ -0,0 +1,46 @@
+Twitter Bootstrap Directive for restructured text
+-------------------------------------------------
+
+This plugin defines some rst directive that enable a clean usage of the twitter bootstrap CSS and Javascript components.
+
+Directives
+----------
+
+Implemented directives:
+
+    label,
+    alert,
+    panel,
+    media
+
+Implemented roles:
+
+    glyph,
+    code,
+    kbd
+
+Usage
+-----
+
+For more informations about the usage of each directive, read the corresponding class description.
+Or checkout this demo page.
+
+Dependencies
+------------
+
+In order to use this plugin, you need to use a template that supports bootstrap 3.1.1 with the glyph font setup
+correctly. Usually you should have this structure::
+
+    static
+    ├──  css
+    |     └──  bootstrap.min.css
+    ├──  font
+    |     └──  glyphicons-halflings-regular.ttf
+    └──  js
+          └──     
+
+Warning
+-------
+
+In order to support some unique features and avoid conflicts with bootstrap, this plugin will use a custom html writer which
+is modifying the traditional docutils output.
\ No newline at end of file
diff --git a/pelican-plugins/twitter_bootstrap_rst_directives/__init__.py b/pelican-plugins/twitter_bootstrap_rst_directives/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..7dfdc27c1241bdb0cc1dbf7006fe4bcd57c653ea
--- /dev/null
+++ b/pelican-plugins/twitter_bootstrap_rst_directives/__init__.py
@@ -0,0 +1,4 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+from .bootstrap_rst_directives import *
diff --git a/pelican-plugins/twitter_bootstrap_rst_directives/bootstrap_rst_directives.py b/pelican-plugins/twitter_bootstrap_rst_directives/bootstrap_rst_directives.py
new file mode 100644
index 0000000000000000000000000000000000000000..830b8410c81db5a8b8b283d61dc967e6dbe76874
--- /dev/null
+++ b/pelican-plugins/twitter_bootstrap_rst_directives/bootstrap_rst_directives.py
@@ -0,0 +1,543 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+
+"""
+Twitter Bootstrap RST directives Plugin For Pelican
+===================================================
+
+This plugin defines rst directives for different CSS and Javascript components from
+the twitter bootstrap framework.
+
+"""
+
+from uuid import uuid1
+
+from cgi import escape
+from docutils import nodes, utils
+import docutils
+from docutils.parsers import rst
+from docutils.parsers.rst import directives, roles, Directive
+from pelican import signals
+from pelican.readers import RstReader, PelicanHTMLTranslator
+
+
+
+class CleanHTMLTranslator(PelicanHTMLTranslator):
+
+    """
+        A custom HTML translator based on the Pelican HTML translator.
+        Used to clean up some components html classes that could conflict 
+        with the bootstrap CSS classes.
+        Also defines new tags that are not handleed by the current implementation of 
+        docutils.
+
+        The most obvious example is the Container component
+    """
+
+    def visit_literal(self, node):
+        classes = node.get('classes', node.get('class', []))
+        if 'code' in classes:
+            self.body.append(self.starttag(node, 'code'))
+        elif 'kbd' in classes:
+            self.body.append(self.starttag(node, 'kbd'))
+        else:
+            self.body.append(self.starttag(node, 'pre'))
+
+    def depart_literal(self, node):
+        classes = node.get('classes', node.get('class', []))
+        if 'code' in classes:
+            self.body.append('</code>\n')
+        elif 'kbd' in classes:
+            self.body.append('</kbd>\n')
+        else:
+            self.body.append('</pre>\n')
+
+    def visit_container(self, node):
+        self.body.append(self.starttag(node, 'div'))
+
+
+class CleanRSTReader(RstReader):
+
+    """
+        A custom RST reader that behaves exactly like its parent class RstReader with
+        the difference that it uses the CleanHTMLTranslator
+    """
+
+    def _get_publisher(self, source_path):
+        extra_params = {'initial_header_level': '2',
+                        'syntax_highlight': 'short',
+                        'input_encoding': 'utf-8'}
+        user_params = self.settings.get('DOCUTILS_SETTINGS')
+        if user_params:
+            extra_params.update(user_params)
+
+        pub = docutils.core.Publisher(
+            destination_class=docutils.io.StringOutput)
+        pub.set_components('standalone', 'restructuredtext', 'html')
+        pub.writer.translator_class = CleanHTMLTranslator
+        pub.process_programmatic_settings(None, extra_params, None)
+        pub.set_source(source_path=source_path)
+        pub.publish()
+        return pub
+
+
+def keyboard_role(name, rawtext, text, lineno, inliner,
+                  options={}, content=[]):
+    """
+        This function creates an inline console input block as defined in the twitter bootstrap documentation
+        overrides the default behaviour of the kbd role
+
+        *usage:*
+            :kbd:`<your code>`
+
+        *Example:*
+
+            :kbd:`<section>`
+
+        This code is not highlighted
+    """
+    new_element = nodes.literal(rawtext, text)
+    new_element.set_class('kbd')
+
+    return [new_element], []
+
+
+def code_role(name, rawtext, text, lineno, inliner,
+              options={}, content=[]):
+    """
+        This function creates an inline code block as defined in the twitter bootstrap documentation
+        overrides the default behaviour of the code role
+
+        *usage:*
+            :code:`<your code>`
+
+        *Example:*
+
+            :code:`<section>`
+
+        This code is not highlighted
+    """
+    new_element = nodes.literal(rawtext, text)
+    new_element.set_class('code')
+
+    return [new_element], []
+
+
+def glyph_role(name, rawtext, text, lineno, inliner,
+               options={}, content=[]):
+    """
+        This function defines a glyph inline role that show a glyph icon from the 
+        twitter bootstrap framework
+
+        *Usage:*
+
+            :glyph:`<glyph_name>`
+
+        *Example:*
+
+            Love this music :glyph:`music` :)
+
+        Can be subclassed to include a target
+
+        *Example:*
+
+            .. role:: story_time_glyph(glyph)
+                :target: http://www.youtube.com/watch?v=5g8ykQLYnX0
+                :class: small text-info
+
+            Love this music :story_time_glyph:`music` :)
+
+    """
+
+    target = options.get('target', None)
+    glyph_name = 'glyphicon-{}'.format(text)
+
+    if target:
+        target = utils.unescape(target)
+        new_element = nodes.reference(rawtext, ' ', refuri=target)
+    else:
+        new_element = nodes.container()
+    classes = options.setdefault('class', [])
+    classes += ['glyphicon', glyph_name]
+    for custom_class in classes:
+        new_element.set_class(custom_class)
+    return [new_element], []
+
+glyph_role.options = {
+    'target': rst.directives.unchanged,
+}
+glyph_role.content = False
+
+
+class Label(rst.Directive):
+
+    '''
+        generic Label directive class definition.
+        This class define a directive that shows 
+        bootstrap Labels around its content
+
+        *usage:*
+
+            .. label-<label-type>::
+
+                <Label content>
+
+        *example:*
+
+            .. label-default::
+
+                This is a default label content
+
+    '''
+
+    has_content = True
+    custom_class = ''
+
+    def run(self):
+        # First argument is the name of the glyph
+        label_name = 'label-{}'.format(self.custom_class)
+        # get the label content
+        text = '\n'.join(self.content)
+        # Create a new container element (div)
+        new_element = nodes.container(text)
+        # Update its content
+        self.state.nested_parse(self.content, self.content_offset,
+                                new_element)
+        # Set its custom bootstrap classes
+        new_element['classes'] += ['label ', label_name]
+        # Return one single element
+        return [new_element]
+
+
+class DefaultLabel(Label):
+
+    custom_class = 'default'
+
+
+class PrimaryLabel(Label):
+
+    custom_class = 'primary'
+
+
+class SuccessLabel(Label):
+
+    custom_class = 'success'
+
+
+class InfoLabel(Label):
+
+    custom_class = 'info'
+
+
+class WarningLabel(Label):
+
+    custom_class = 'warning'
+
+
+class DangerLabel(Label):
+
+    custom_class = 'danger'
+
+
+class Panel(rst.Directive):
+
+    """
+        generic Panel directive class definition.
+        This class define a directive that shows 
+        bootstrap Labels around its content
+
+        *usage:*
+
+            .. panel-<panel-type>:: 
+                :title: <title>
+
+                <Panel content>
+
+        *example:*
+
+            .. panel-default:: 
+                :title: panel title
+
+                This is a default panel content
+
+    """
+
+    has_content = True
+    option_spec = {
+        'title': rst.directives.unchanged,
+    }
+    custom_class = ''
+
+    def run(self):
+        # First argument is the name of the glyph
+        panel_name = 'panel-{}'.format(self.custom_class)
+        # get the label title
+        title_text = self.options.get('title', self.custom_class.title())
+        # get the label content
+        text = '\n'.join(self.content)
+        # Create the panel element
+        panel_element = nodes.container()
+        panel_element['classes'] += ['panel', panel_name]
+        # Create the panel headings
+        heading_element = nodes.container(title_text)
+        title_nodes, messages = self.state.inline_text(title_text,
+                                                       self.lineno)
+        title = nodes.paragraph(title_text, '', *title_nodes)
+        heading_element.append(title)
+        heading_element['classes'] += ['panel-heading']
+        # Create a new container element (div)
+        body_element = nodes.container(text)
+        # Update its content
+        self.state.nested_parse(self.content, self.content_offset,
+                                body_element)
+        # Set its custom bootstrap classes
+        body_element['classes'] += ['panel-body']
+        # add the heading and body to the panel
+        panel_element.append(heading_element)
+        panel_element.append(body_element)
+        # Return the panel element
+        return [panel_element]
+
+
+class DefaultPanel(Panel):
+
+    custom_class = 'default'
+
+
+class PrimaryPanel(Panel):
+
+    custom_class = 'primary'
+
+
+class SuccessPanel(Panel):
+
+    custom_class = 'success'
+
+
+class InfoPanel(Panel):
+
+    custom_class = 'info'
+
+
+class WarningPanel(Panel):
+
+    custom_class = 'warning'
+
+
+class DangerPanel(Panel):
+
+    custom_class = 'danger'
+
+
+class Alert(rst.Directive):
+
+    """
+        generic Alert directive class definition.
+        This class define a directive that shows 
+        bootstrap Labels around its content
+
+        *usage:*
+
+            .. alert-<alert-type>::
+
+                <alert content>
+
+        *example:*
+
+            .. alert-warning::
+
+                This is a warning alert content
+
+    """
+    has_content = True
+    custom_class = ''
+
+    def run(self):
+        # First argument is the name of the glyph
+        alert_name = 'alert-{}'.format(self.custom_class)
+        # get the label content
+        text = '\n'.join(self.content)
+        # Create a new container element (div)
+        new_element = nodes.compound(text)
+        # Update its content
+        self.state.nested_parse(self.content, self.content_offset,
+                                new_element)
+        # Recurse inside its children and change the hyperlinks classes
+        for child in new_element.traverse(include_self=False):
+            if isinstance(child, nodes.reference):
+                child.set_class('alert-link')
+        # Set its custom bootstrap classes
+        new_element['classes'] += ['alert ', alert_name]
+        # Return one single element
+        return [new_element]
+
+
+class SuccessAlert(Alert):
+
+    custom_class = 'success'
+
+
+class InfoAlert(Alert):
+
+    custom_class = 'info'
+
+
+class WarningAlert(Alert):
+
+    custom_class = 'warning'
+
+
+class DangerAlert(Alert):
+
+    custom_class = 'danger'
+
+
+class Media(rst.Directive):
+
+    '''
+        generic Media directive class definition.
+        This class define a directive that shows 
+        bootstrap media image with text according
+        to the media component on bootstrap
+
+        *usage*:
+            .. media:: <image_uri>
+                :position: <position>
+                :alt: <alt>
+                :height: <height>
+                :width: <width>
+                :scale: <scale>
+                :target: <target>
+
+                <text content>
+
+        *example*:
+            .. media:: http://stuffkit.com/wp-content/uploads/2012/11/Worlds-Most-Beautiful-Lady-Camilla-Belle-HD-Photos-4.jpg
+                :height: 750
+                :width: 1000
+                :scale: 20
+                :target: www.google.com
+                :alt: Camilla Belle
+                :position: left
+
+                This image is not mine. Credit goes to http://stuffkit.com
+
+
+
+    '''
+
+    has_content = True
+    required_arguments = 1
+
+    option_spec = {
+        'position': str,
+        'alt': rst.directives.unchanged,
+        'height': rst.directives.length_or_unitless,
+        'width': rst.directives.length_or_percentage_or_unitless,
+        'scale': rst.directives.percentage,
+        'target': rst.directives.unchanged_required,
+    }
+
+    def get_image_element(self):
+        # Get the image url
+        image_url = self.arguments[0]
+        image_reference = rst.directives.uri(image_url)
+        self.options['uri'] = image_reference
+
+        reference_node = None
+        messages = []
+        if 'target' in self.options:
+            block = rst.states.escape2null(
+                self.options['target']).splitlines()
+            block = [line for line in block]
+            target_type, data = self.state.parse_target(
+                block, self.block_text, self.lineno)
+            if target_type == 'refuri':
+                container_node = nodes.reference(refuri=data)
+            elif target_type == 'refname':
+                container_node = nodes.reference(
+                    refname=fully_normalize_name(data),
+                    name=whitespace_normalize_name(data))
+                container_node.indirect_reference_name = data
+                self.state.document.note_refname(container_node)
+            else:                           # malformed target
+                messages.append(data)       # data is a system message
+            del self.options['target']
+        else:
+            container_node = nodes.container()
+
+        # get image position
+        position = self.options.get('position', 'left')
+        position_class = 'pull-{}'.format(position)
+
+        container_node.set_class(position_class)
+
+        image_node = nodes.image(self.block_text, **self.options)
+        image_node['classes'] += ['media-object']
+
+        container_node.append(image_node)
+        return container_node
+
+    def run(self):
+        # now we get the content
+        text = '\n'.join(self.content)
+
+        # get image alternative text
+        alternative_text = self.options.get('alternative-text', '')
+
+        # get container element
+        container_element = nodes.container()
+        container_element['classes'] += ['media']
+
+        # get image element
+        image_element = self.get_image_element()
+
+        # get body element
+        body_element = nodes.container(text)
+        body_element['classes'] += ['media-body']
+        self.state.nested_parse(self.content, self.content_offset,
+                                body_element)
+
+        container_element.append(image_element)
+        container_element.append(body_element)
+        return [container_element, ]
+
+
+def register_directives():
+    rst.directives.register_directive('label-default', DefaultLabel)
+    rst.directives.register_directive('label-primary', PrimaryLabel)
+    rst.directives.register_directive('label-success', SuccessLabel)
+    rst.directives.register_directive('label-info', InfoLabel)
+    rst.directives.register_directive('label-warning', WarningLabel)
+    rst.directives.register_directive('label-danger', DangerLabel)
+
+    rst.directives.register_directive('panel-default', DefaultPanel)
+    rst.directives.register_directive('panel-primary', PrimaryPanel)
+    rst.directives.register_directive('panel-success', SuccessPanel)
+    rst.directives.register_directive('panel-info', InfoPanel)
+    rst.directives.register_directive('panel-warning', WarningPanel)
+    rst.directives.register_directive('panel-danger', DangerPanel)
+
+    rst.directives.register_directive('alert-success', SuccessAlert)
+    rst.directives.register_directive('alert-info', InfoAlert)
+    rst.directives.register_directive('alert-warning', WarningAlert)
+    rst.directives.register_directive('alert-danger', DangerAlert)
+
+    rst.directives.register_directive( 'media', Media )
+
+
+def register_roles():
+    rst.roles.register_local_role('glyph', glyph_role)
+    rst.roles.register_local_role('code', code_role)
+    rst.roles.register_local_role('kbd', keyboard_role)
+
+
+def add_reader(readers):
+    readers.reader_classes['rst'] = CleanRSTReader
+
+
+def register():
+    register_directives()
+    register_roles()
+    signals.readers_init.connect(add_reader)
diff --git a/pelican-plugins/txt2tags_reader/LICENSE b/pelican-plugins/txt2tags_reader/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..20efd1b3e9763444d06a2d5e47b6bf048d83e7f4
--- /dev/null
+++ b/pelican-plugins/txt2tags_reader/LICENSE
@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
diff --git a/pelican-plugins/txt2tags_reader/README.md b/pelican-plugins/txt2tags_reader/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..942390d45bd3cae59bbe51af6639c50957fde14a
--- /dev/null
+++ b/pelican-plugins/txt2tags_reader/README.md
@@ -0,0 +1,12 @@
+# txt2tags_reader
+A [txt2tags] reader plugin for the [Pelican static site generator](http://docs.getpelican.com/en/latest/).
+
+Requirements
+------------
+[txt2tags] in $PATH
+
+Installation
+------------
+Instructions on installing pelican plugins can be found in the [pelican plugin manual](https://github.com/getpelican/pelican-plugins/blob/master/Readme.rst).
+
+[txt2tags]:http://txt2tags.org/
diff --git a/pelican-plugins/txt2tags_reader/__init__.py b/pelican-plugins/txt2tags_reader/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..f44a2f07b1bedf0970df5567c111c057bbf87c6a
--- /dev/null
+++ b/pelican-plugins/txt2tags_reader/__init__.py
@@ -0,0 +1 @@
+from .txt2tags_reader import *
diff --git a/pelican-plugins/txt2tags_reader/txt2tags_reader.py b/pelican-plugins/txt2tags_reader/txt2tags_reader.py
new file mode 100644
index 0000000000000000000000000000000000000000..354f39018206a252eb078676d7df7de16c9fb5b5
--- /dev/null
+++ b/pelican-plugins/txt2tags_reader/txt2tags_reader.py
@@ -0,0 +1,42 @@
+import subprocess
+from pelican import signals
+from pelican.readers import BaseReader
+from pelican.utils import pelican_open
+
+class Txt2tagsReader(BaseReader):
+    enabled = True
+    file_extensions = ['t2t', 'txt2tags']
+
+    def read(self, filename):
+        with pelican_open(filename) as fp:
+            text = list(fp.splitlines())
+
+        metadata = {}
+        for i, line in enumerate(text):
+            kv = line.split(':', 1)
+            if len(kv) == 2:
+                name, value = kv[0].lower(), kv[1].strip()
+                metadata[name] = self.process_metadata(name, value)
+            else:
+                content = "\n".join(text[i:])
+                break
+
+        t2t_cmd = [r"txt2tags", r"--encoding=utf-8", r"--target=html", r"--infile=-", r"--outfile=-", r"--no-headers"]
+
+        proc = subprocess.Popen(t2t_cmd,
+                                stdin = subprocess.PIPE,
+                                stdout = subprocess.PIPE)
+
+        output = proc.communicate(content.encode('utf-8'))[0].decode('utf-8')
+        status = proc.wait()
+        if status:
+            raise subprocess.CalledProcessError(status, t2t_cmd)
+
+        return output, metadata
+
+def add_reader(readers):
+    for ext in Txt2tagsReader.file_extensions:
+        readers.reader_classes[ext] = Txt2tagsReader
+
+def register():
+    signals.readers_init.connect(add_reader)
diff --git a/pelican-plugins/video_privacy_enhancer/Readme.md b/pelican-plugins/video_privacy_enhancer/Readme.md
new file mode 100644
index 0000000000000000000000000000000000000000..24dfb56e90cc4542f69f28e9b21957ce0e7a1f24
--- /dev/null
+++ b/pelican-plugins/video_privacy_enhancer/Readme.md
@@ -0,0 +1,71 @@
+# Video Privacy Enhancer
+
+This plugin is a conceptual port of the Electronic Frontier Foundation's (EFF's) [MyTube Drupal plugin](https://www.eff.org/pages/mytube-limit-privacy-risks-embedded-video "EFF blog post about the MyTube Plugin") to Pelican. **It increases user privacy by stopping video services (e.g., for YouTube, Google) from placing cookies on a user's system through an embedded video without that user explicitly opting-in to viewing the video (by clicking on it).**
+
+
+## Copyright, Contact, and Acknowledgements
+
+This plugin is copyright 2014 Jacob Levernier  
+It is released under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 (as is the default for Pelican Plugins in the getpelican/pelican-plugins repository).
+
+Although not required by the license, I would very much appreciate it if you placed a note in the "About" page of your website that you use this plugin, in order to raise user awareness of web tracking issues.
+
+### To contact the author:
+
+* jleverni at uoregon dot edu  
+* http://AdUnumDatum.org  
+* BitBucket: https://bitbucket.org/jlev_uo/  
+* Github: https://github.com/publicus  
+
+This is the first plugin that I've written for Pelican, and it was intended as a training project for learning Pelican as well as Python better. I would be very happy to hear constructive feedback on the plugin and for suggestions for making it more efficient and/or expandable. Also, I've heavily annotated all of the Python and jQuery code in order to make it easier to understand for others looking to learn more, like I was when I wrote the plugin.
+
+### Acknowledgements
+
+I'm grateful to [Jordi Burgos](http://jordiburgos.com/post/2014/first-pelican-plugin-readtime.html "Blog post on writing plugins for Pelican") and [Duncan Lock](http://duncanlock.net/blog/2013/10/18/how-i-upgraded-this-website-to-pelican-33/ "Another blog post on writing plugins for Pelican") for writing helpful introductory blog posts on writing Pelican plugins. Both of these posts helped to decrease the intimidation factor associated with learning a new system like Pelican.
+
+I'm also grateful to the authors of the plugins in the pelican-plugins repo; being able to look over other plugins' authors' code helped me immensely in learning more about how Pelican's [signals](http://docs.getpelican.com/en/3.3.0/plugins.html#how-to-create-plugins "Pelican documentation on creating plugins") system works.
+
+
+## Explanation and Rationale
+
+Many videos (e.g., from YouTube) that are embedded in a website are capable of placing a cookie (or using other tracking methods) on a visitor's computer even if that visitor does not play the movie. For certain sites that deal with political or other potentially sensitive topics, this automatic tracking could raise privacy concerns among users. Thus, this plugin fetches and stores, on your server, a copy of the thumbnail image of each embedded YouTube video on your website. It then displays that image instead of the video until the user "opts-in" to watching the video by clicking on the thumbnail (at which point the image is replaced by the youtube embed iframe).
+
+This plugin allows video shortcodes to be used in any Pelican page or article.
+
+The currently-allowed shortcodes are as follows:
+
+* **YouTube:** `!youtube(video_id)`
+* **Vimeo:** `!vimeo(video_id)`
+
+For example, `!youtube(2XID_W4neJo)` can be used to embed and privacy-protect the video available at [https://www.youtube.com/watch?v=2XID_W4neJo](https://www.youtube.com/watch?v=2XID_W4neJo). During the `make html` process, the plugin will find every instance of each supported shortcode, and for each instance, will automatically download the video's thumbnail if it hasn't been downloaded previously, and will save the thumbnail to the Pelican output folder (by default, to /output/images/video-thumbnails). It will then replace the shortcode with the thumbnail image. A jQuery script then watches for thumbnail image clicks; when a user clicks a thumbnail image, the jQuery script will fade the image out, and replace it with the actual video iframe.
+
+YouTube does have a ["privacy-enhanced mode"](https://support.google.com/youtube/answer/171780?expand=PrivacyEnhancedMode#privacy) that purports not to place cookies on a user's page until the user clicks on a video. However, if you are a website author who wants to *ensure* that no cookies are initially placed, this plugin puts that power in your hands.
+
+
+## Usage
+
+`!youtube(2XID_W4neJo)` written anywhere in the content of a page or post will become `<img class="video-embed-dummy-image" id="2XID_W4neJo" src="YOUR_SITEURL/images/video-thumbnails/2XID_W4neJo.jpg" alt="Embedded Video - Click to view" title="Embedded Video - Click to view"></img>` during the `make html` process ('YOUR_SITEURL' is replaced by your actual `SITEURL` from pelicanconf.py). The plugin will take care of downloading `2XID_W4neJo.jpg`.
+
+The plugin comes with an example jQuery file to copy into your theme's static folder. **With this jQuery script, anytime a thumbnail image is clicked by a user, the thumbnail will fade out and then be replaced by the actual video embed.** Thus, the `<img>...</img>` code above will become `<iframe width="853" height="480" src="https://www.youtube-nocookie.com/embed/2XID_W4neJo" frameborder="0" allowfullscreen></iframe>` when clicked.
+
+To set the plugin up, just read the instructions at the top of youtube_privacy_enhancer.py. That file also includes some example CSS to copy into your theme's CSS file, as well as a setting or two for you to look over.
+
+**If you would like to add support for a new video service,** there are just a few steps:
+
+1. Look up how to download the thumbnail for a video from that service (e.g., through searching through the documentation for that service).
+2. Write a function in video_service_thumbnail_url_generating_functions.py (you can use one of the existing functions there as a template) that, when given the ID of a video from that service, will give back the URL for that video's thumbnail.
+3. Add the video service's name, shortcode, and name of the function you just created to the dictionary in the "SETTINGS" section of video_privacy_enhancer.py (you can use the existing entries in the dictionary as templates).
+4. Add the video service's name and URL to embed videos for it (you can find this using the "Share" button in many video websites) to the jQuery code that you copied from `video_privacy_enhancer_jQuery.js` when you first set up the plugin by following the instructions at the top of video_privacy_enhancer.py.
+
+
+## To Do
+
+As noted in an EFF [blog post](https://www.eff.org/deeplinks/2010/08/upgrade-mytube "EFF blog post about updates to MyTube"), the Drupal MyTube plugin from which this plugin takes its idea is capable of handling videos not only from YouTube and Vimeo but also Comedy Central and other sites. It would be nice to expand this Pelican plugin in the future to do the same.
+
+## Changelog
+
+* 2015-07-16: 
+	* Added support for Vimeo videos, refactored all of the code to make it easier to extend the plugin for new video services in the future.
+	* **NOTE WELL:** If you were using this plugin before this change, please note that several changes have been made that may require you to update your Pelican site:
+		1. The example CSS has changed to use 'embedded_privacy_video' instead of 'embedded_youtube_video', as well as 'video-embed-dummy-image' instead of 'youtube-embed-dummy-image'.
+		2. The default thumbnail directory has been changed from 'youtube-thumbnails' to 'video-thumbnails'.
diff --git a/pelican-plugins/video_privacy_enhancer/__init__.py b/pelican-plugins/video_privacy_enhancer/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..5e687aeb9a50b1faa7897db1e4d533f7bac56342
--- /dev/null
+++ b/pelican-plugins/video_privacy_enhancer/__init__.py
@@ -0,0 +1 @@
+from .video_privacy_enhancer import *
diff --git a/pelican-plugins/video_privacy_enhancer/video_privacy_enhancer.py b/pelican-plugins/video_privacy_enhancer/video_privacy_enhancer.py
new file mode 100644
index 0000000000000000000000000000000000000000..28ee8721ba0625a48afa5234ce121a52c25914bc
--- /dev/null
+++ b/pelican-plugins/video_privacy_enhancer/video_privacy_enhancer.py
@@ -0,0 +1,173 @@
+"""
+Video Privacy Enhancer
+--------------------------
+
+Authored by Jacob Levernier, 2014
+Released under the GNU AGPLv3
+
+For more information on this plugin, please see the attached Readme.md file.
+
+"""
+
+"""
+LIBRARIES FOR PYTHON TO USE
+"""
+
+from pelican import signals # For making this plugin work with Pelican.
+
+import os.path # For checking whether files are present in the filesystem.
+
+import re # For using regular expressions.
+
+import logging
+logger = logging.getLogger(__name__) # For using logger.debug() to log errors or other notes.
+
+import six
+# import urllib.request
+import six.moves.urllib.request 
+
+from . import video_service_thumbnail_url_generating_functions as video_thumbnail_functions # These are functions defined in 'video_service_thumbnail_url_generating_functions.py', which is located in the same directory as this file. 
+
+"""
+END OF LIBRARIES
+"""
+
+"""
+SETTINGS
+"""
+
+# Do not use a leading or trailing slash below (e.g., use "images/video-thumbnails"):
+output_directory_for_thumbnails = "images/video-thumbnails"
+
+# See the note in the Readme file re: adding support for other video services to this list.
+supported_video_services = {
+	"youtube": {
+		"shortcode_not_including_exclamation_point": "youtube",
+		"function_for_generating_thumbnail_url": video_thumbnail_functions.generate_thumbnail_download_link_youtube,
+	},
+	"vimeo": {
+		"shortcode_not_including_exclamation_point": "vimeo",
+		"function_for_generating_thumbnail_url": video_thumbnail_functions.generate_thumbnail_download_link_vimeo,
+	},
+}
+
+""" 
+In order for this plugin to work optimally, you need to do just a few things:
+
+1. Enable the plugn in pelicanconf.py (see http://docs.getpelican.com/en/3.3.0/plugins.html for documentation):  
+	PLUGIN_PATH = "/pelican-plugins"  
+	PLUGINS = ["video_privacy_enhancer"]
+
+2a. If necessary, install jQuery on your site (See https://stackoverflow.com/questions/1458349/installing-jquery -- the jQuery base file should go into your Pelican theme's 'static' directory)
+
+2b. Copy the jQuery file in this folder into, for example, your_theme_folder/static/video_privacy_enhancer_jQuery.js, and add a line like this to the <head></head> element of your website's base.html (or equivalent) template:
+	`<script src="{{ SITEURL }}/theme/video_privacy_enhancer_jquery.js"></script> <!--Load jQuery functions for the Video Privacy Enhancer Pelican plugin -->`
+
+3. Choose a default video embed size and add corresponding CSS to your theme's CSS file:
+
+Youtube allows the following sizes in its embed GUI (as of this writing, in March 2014). I recommend choosing one, and then having the iframe for the actual video embed match it (so that it's a seamless transition). This can be handled with CSS in both cases, so I haven't hard-coded it here:
+	1280 W x 720 H
+	853 W x 480 H
+	640 W x 360 H
+	560 W x 315 H
+
+Here's an example to add to your CSS file:
+
+```
+/* For use with the video-privacy-enhancer Pelican plugin */
+img.video-embed-dummy-image,
+iframe.embedded_privacy_video {
+	width: 853px;
+	max-height: 480px;
+
+	/* Center the element on the screen */
+	display: block;
+	margin-top: 2em;
+	margin-bottom: 2em;
+	margin-left: auto;
+	margin-right: auto;
+}
+iframe.embedded_privacy_video {
+	width: 843px;
+	height: 480px;
+}
+```
+
+"""
+
+"""
+END SETTINGS
+"""
+
+# A function to check whtether output_directory_for_thumbnails (a variable set above in the SETTINGS section) exists. If it doesn't exist, we'll create it.
+def check_for_thumbnail_directory(pelican_output_path):
+	# Per http://stackoverflow.com/a/84173, check if a file exists. isfile() works on files, and exists() works on files and directories.
+	try:
+		if not os.path.exists(pelican_output_path + "/" + output_directory_for_thumbnails): # If the directory doesn't exist already...
+			os.makedirs(pelican_output_path + "/" + output_directory_for_thumbnails) # Create the directory to hold the video thumbnails.
+			return True
+	except Exception as e:
+		logger.error("Video Privacy Enhancer Plugin: Error in checking if thumbnail folder exists and making the directory if it doesn't: %s", e) # In case something goes wrong.
+		return False
+
+
+def download_thumbnail(video_id_from_shortcode, video_thumbnail_url, video_service_name, pelican_output_path):
+	# Check if the thumbnail directory exists already:
+	check_for_thumbnail_directory(pelican_output_path)
+	
+	# Check if the thumbnail for this video exists already (if it's been previously downloaded). If it doesn't, download it:
+	if not os.path.exists(pelican_output_path + "/" + output_directory_for_thumbnails + "/" + video_service_name + "_" + video_id_from_shortcode + ".jpg"): # If the thumbnail doesn't already exist...
+		logger.debug("Video Privacy Enhancer Plugin: Downloading thumbnail from the following url: " + video_thumbnail_url)
+		six.moves.urllib.request.urlretrieve(video_thumbnail_url, pelican_output_path + "/" + output_directory_for_thumbnails + "/" + video_service_name + "_" + video_id_from_shortcode + ".jpg") # Download the thumbnail. This follows the instructions at http://www.reelseo.com/youtube-thumbnail-image/ for downloading YouTube thumbnail images.
+
+
+# A function to read through each page and post as it comes through from Pelican, find all instances of a shortcode (e.g., `!youtube(...)`) and change it into an HTML <img> element with the video thumbnail.
+# 'dictionary_of_services' below should be the dictionary defined in the settings above, which includes the service's name as the dictionary key, and, for each service, has a dictionary containing 'shortcode_to_search_for_not_including_exclamation_point' and 'function_for_generating_thumbnail_url'
+def process_shortcodes(data_passed_from_pelican):
+	dictionary_of_services = supported_video_services # This should be defined in the settings section above.
+	
+	if not data_passed_from_pelican._content: # If the item passed from Pelican has a "content" attribute (i.e., if it's not an image file or something else like that). NOTE: data_passed_from_pelican.content (without an underscore in front of 'content') seems to be read-only, whereas data_passed_from_pelican._content is able to be overwritten. This is somewhat explained in an IRC log from 2013-02-03 from user alexis to user webdesignhero_ at https://botbot.me/freenode/pelican/2013-02-01/?tz=America/Los_Angeles.
+		return # Exit the function, essentially passing over the (non-text) file.
+	
+	# Loop through services (e.g., youtube, vimeo), processing the output for each:
+	for video_service_name, video_service_information in six.iteritems(dictionary_of_services):
+		
+		# Good for debugging:
+		logger.debug("Video Privacy Enhancer Plugin: The name of the current service being processed is '" + video_service_name + "'")
+		
+		shortcode_to_search_for_not_including_exclamation_point = video_service_information['shortcode_not_including_exclamation_point']
+		logger.debug("Video Privacy Enhancer Plugin: Currently looking for the shortcode '" + shortcode_to_search_for_not_including_exclamation_point + "'")
+		
+		function_for_generating_thumbnail_url = video_service_information['function_for_generating_thumbnail_url']
+		
+		all_instances_of_the_shortcode = re.findall(r'(?<!\\\)\!' + shortcode_to_search_for_not_including_exclamation_point + r'.*?\)', data_passed_from_pelican._content) # Use a regular expression to find every instance of, e.g., '!youtube' followed by anything up to the first matching ')'.
+		
+		if(len(all_instances_of_the_shortcode) > 0): # If the article/page HAS any shortcodes, go on. Otherwise, don't (to do so would inadvertantly wipe out the output content for that article/page).
+			replace_shortcode_in_text = "" # This just gives this an initial value before going into the loop below.
+
+			# Go through each shortcode instance that we found above, and parse it:
+			for shortcode_to_parse in all_instances_of_the_shortcode:
+
+				video_id_from_shortcode = re.findall('(?<=' + shortcode_to_search_for_not_including_exclamation_point + r'\().*?(?=\))', shortcode_to_parse)[0] # Get what's inside of the parentheses in, e.g., '!youtube(...).'
+				
+				# print "Video ID is " + video_id_from_shortcode # Good for debugging purposes.
+				
+				# Use the Pelican pelicanconf.py settings:
+				pelican_output_path = data_passed_from_pelican.settings['OUTPUT_PATH']
+				pelican_site_url = data_passed_from_pelican.settings['SITEURL']
+				
+				# Download the video thumbnail if it's not already on the filesystem:
+				video_thumbnail_url = function_for_generating_thumbnail_url(video_id_from_shortcode)
+				
+				download_thumbnail(video_id_from_shortcode, video_thumbnail_url, video_service_name, pelican_output_path)
+				
+				# Replace the shortcode (e.g., '!youtube(...)') with '<img>...</img>'. Note that the <img> is given a class that the jQuery file mentioned at the top of this file will watch over. Any time an image with that class is clicked, the jQuery function will trigger and turn it into the full video embed.
+				replace_shortcode_in_text = re.sub(r'\!' + shortcode_to_search_for_not_including_exclamation_point + r'\(' + video_id_from_shortcode + r'\)', r'<img class="video-embed-dummy-image" id="' + video_id_from_shortcode + '" src="' +  pelican_site_url + '/' + output_directory_for_thumbnails + '/' + video_service_name + '_' + video_id_from_shortcode + '.jpg" alt="Embedded Video - Click to view" title="Embedded Video - Click to view" embed-service="' + video_service_name + '"></img>', data_passed_from_pelican._content)
+				
+				# Replace the content of the page or post with our now-updated content (having gone through all instances of the shortcode and updated them all, exiting the loop above.
+				data_passed_from_pelican._content = replace_shortcode_in_text
+
+
+# Make Pelican work (see http://docs.getpelican.com/en/3.3.0/plugins.html#how-to-create-plugins):
+def register():
+	signals.content_object_init.connect(process_shortcodes)
diff --git a/pelican-plugins/video_privacy_enhancer/video_privacy_enhancer_jquery.js b/pelican-plugins/video_privacy_enhancer/video_privacy_enhancer_jquery.js
new file mode 100644
index 0000000000000000000000000000000000000000..3fab23d336aadf267b8b29f28bf903a8cf99b350
--- /dev/null
+++ b/pelican-plugins/video_privacy_enhancer/video_privacy_enhancer_jquery.js
@@ -0,0 +1,39 @@
+$(document).ready(function() {
+// NOTE WELL: This is wrapped in a `$(document).ready(function() {...})` function (i.e., a "Document Ready" function that starts a section that won't load until the rest of the document/page has loaded (i.e., is "ready")). HOWEVER, this makes it so that jQuery functions from different .js files here are not able to see and talk to functions within this Document Ready function. Within a Document Ready function, functions and variables lose global scope. See http://stackoverflow.com/a/6547906 for more information.
+    
+///////////////////////////
+// SETTINGS
+///////////////////////////
+
+	// NOTE WELL: It's expected to use the code 'VIDEO_ID_GOES_HERE' in the URLs below where the video ID should go. This phrase will be replaced by the video id below.
+	// NOTE WELL: The URLS below should have 'http://' or 'https://' in front of them:
+	var dictionary_of_embed_urls_for_different_services = {
+		"youtube" : 'https://www.youtube-nocookie.com/embed/VIDEO_ID_GOES_HERE',
+		"vimeo" : 'https://player.vimeo.com/video/VIDEO_ID_GOES_HERE'
+	};
+
+///////////////////////////
+// END OF SETTINGS
+///////////////////////////
+
+
+// Whenever a link for an image created with the video_privacy_enhancer plugin is clicked, transform it into a video embed:
+$('body').on("click",'img.video-embed-dummy-image',function(event) // Whenever an image with the class "video-embed-dummy-image" is clicked...
+	{
+	    // '$(this)' below refers to the image that's been clicked. Before we go down into the function below, where the definition/scope of '$(this)' will change, we'll set it in a variable so that we can refer to it within the function below.
+	    var image_that_was_clicked = $(this);
+	    
+	    var video_id = $(this).attr('id'); // This assumes that the img ID attribute is set to the (e.g., youtube, Vimeo, etc.) video id.
+		var service_name = $(this).attr('embed-service'); // This assumes that the 'embed-service' attribute is set to a name (e.g., youtube, vimeo), and that that name is in the dictionary below.
+		
+		
+		var src_for_this_video = dictionary_of_embed_urls_for_different_services[service_name].replace("VIDEO_ID_GOES_HERE", video_id);
+
+        // Fade out the image, and replace it with the iframe embed code for the actual video:
+	    $(this).fadeOut(250, function() {
+	        image_that_was_clicked.replaceWith('<iframe class="embedded_privacy_video" src="' + src_for_this_video + '" frameborder="0" allowfullscreen></iframe>') // This will automatically show again.
+			}); // End fadeOut.
+	});
+
+
+}) // End Document Ready wrapper.
diff --git a/pelican-plugins/video_privacy_enhancer/video_service_thumbnail_url_generating_functions.py b/pelican-plugins/video_privacy_enhancer/video_service_thumbnail_url_generating_functions.py
new file mode 100644
index 0000000000000000000000000000000000000000..87b452ffc3aab6c9c28389c6d8d41f1ca3dd83a8
--- /dev/null
+++ b/pelican-plugins/video_privacy_enhancer/video_service_thumbnail_url_generating_functions.py
@@ -0,0 +1,33 @@
+"""A function for each service to download the video thumbnail
+
+Each function should accept one argument (the video id from that service) and should return the download URL for the video's thumbnail.
+
+"""
+
+"""
+LIBRARIES
+"""
+
+# from urllib.request import urlopen # For downloading the video thumbnails. Not as clean as, e.g., the requests module, but installed by default in many Python distributions.
+from six.moves.urllib.request import urlopen
+
+import json
+
+"""
+END OF LIBRARIES
+"""
+
+def generate_thumbnail_download_link_youtube(video_id_from_shortcode):
+	"""Thumbnail URL generator for YouTube videos."""
+	
+	thumbnail_download_link="https://img.youtube.com/vi/" + video_id_from_shortcode + "/0.jpg"
+	return thumbnail_download_link
+
+def generate_thumbnail_download_link_vimeo(video_id_from_shortcode):
+	"""Thumbnail URL generator for Vimeo videos."""
+	
+	# Following the Vimeo API at https://developer.vimeo.com/api#video-request, we need to request the video's metadata and get the thumbnail from that. First, then, we'll get the metadata in JSON format, and then will parse it to find the thumbnail URL.
+	video_metadata = urlopen("https://vimeo.com/api/v2/video/" + str(video_id_from_shortcode) + ".json").read() # Download the video's metadata in JSON format.
+	video_metadata_parsed = json.loads(video_metadata.decode('utf-8')) # Parse the JSON
+	video_thumbnail_large_location = video_metadata_parsed[0]['thumbnail_large'] # Go into the JSON and get the URL of the thumbnail.
+	return video_thumbnail_large_location	
diff --git a/pelican-plugins/w3c_validate/README.md b/pelican-plugins/w3c_validate/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..a06d961da3e5480fd37e4c2680ed570925adf7a3
--- /dev/null
+++ b/pelican-plugins/w3c_validate/README.md
@@ -0,0 +1,34 @@
+# w3c_validate plugin
+
+This is a plugin for Pelican that submits generated HTML content to the
+[W3C Markup Validation Service](http://validator.w3.org/).
+
+After all content is generated, the output folder is traversed for HTML files,
+and the content is submitted to the W3C validator, after which the results
+are displayed. For example:
+
+    -> writing /tmp/_output/sitemap.xml
+    -> Validating: /tmp/_output/archives.html
+    ERROR: line: 2; col: 52; message: Bad value http://www.w3.org/1999/html for the attribute xmlns (only http://www.w3.
+    -> Validating: /tmp/_output/categories.html
+    ERROR: line: 2; col: 52; message: Bad value http://www.w3.org/1999/html for the attribute xmlns (only http://www.w3.
+
+**Note**: The above output assumes you have called Pelican with the ``--debug``
+flag. Otherwise, you will see errors (if any) but not the file currently being
+validated.
+
+## Dependencies
+
+* [py_w3c](https://pypi.python.org/pypi/py_w3c/0.1.0), which can be installed with pip:
+
+    $ pip install py_w3c
+    
+## Instructions
+
+Add `w3c_validate` to your config file's plugins after installing dependencies - `PLUGINS = ['w3c_validate']`
+
+## Tests
+
+To execute them:
+
+    nosetests -w w3c_validate
diff --git a/pelican-plugins/w3c_validate/__init__.py b/pelican-plugins/w3c_validate/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..f01ad6eaa74f397eaf7be6cc2f362e7e521d0fcd
--- /dev/null
+++ b/pelican-plugins/w3c_validate/__init__.py
@@ -0,0 +1,2 @@
+# -*- coding: utf-8 -*-
+from .wc3_validate import *
diff --git a/pelican-plugins/w3c_validate/requirements.txt b/pelican-plugins/w3c_validate/requirements.txt
new file mode 100644
index 0000000000000000000000000000000000000000..86ebba489428e6446328b61e584579230c7a99e1
--- /dev/null
+++ b/pelican-plugins/w3c_validate/requirements.txt
@@ -0,0 +1 @@
+py_w3c
diff --git a/pelican-plugins/w3c_validate/test_content/getpelican.html b/pelican-plugins/w3c_validate/test_content/getpelican.html
new file mode 100644
index 0000000000000000000000000000000000000000..c1bc04d0392854d3668fc85f9a412609ae2b4484
--- /dev/null
+++ b/pelican-plugins/w3c_validate/test_content/getpelican.html
@@ -0,0 +1,93 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+        <meta charset="utf-8">
+        <title>Pelican Static Site Generator, Powered by Python</title>
+        <link rel="stylesheet" href="/theme/css/A.main.css.pagespeed.cf.zFbdR40MwZ.css" type="text/css"/>
+        <link href="/feeds/all.atom.xml" type="application/atom+xml" rel="alternate" title="Pelican Development Blog Atom Feed"/>
+
+        <!--[if IE]>
+            <script src="https://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
+        <![endif]-->
+</head>
+
+<body id="index" class="home">
+        <header id="banner" class="body">
+                <h1><a href="/">Pelican Development Blog </a></h1>
+                <nav><ul>
+                    <li><a href="https://docs.getpelican.com">documentation</a></li>
+                    <li><a href="https://donate.getpelican.com">contribute</a></li>
+                    <li><a href="/category/news.html">news</a></li>
+                </ul></nav>
+        </header><!-- /#banner -->
+        
+<section id="content" class="body">    
+    <h1 class="entry-title">Pelican Static Site Generator, Powered by Python</h1>
+    
+    <p>Maintained by <a class="reference external" href="https://justinmayer.com/">Justin Mayer</a> (<a class="reference external" href="https://twitter.com/jmayer">&#64;jmayer</a>), Pelican is a static site generator
+that requires no database or server-side logic.</p>
+<p>Some of the features include:</p>
+<ul class="simple">
+<li>Write content in <a class="reference external" href="http://docutils.sourceforge.net/rst.html">reStructuredText</a> or <a class="reference external" href="http://daringfireball.net/projects/markdown/">Markdown</a> markup</li>
+<li>Completely static output is easy to host anywhere</li>
+<li><a class="reference external" href="https://github.com/getpelican/pelican-themes">Themes</a> that can be customized via <a class="reference external" href="http://jinja.pocoo.org/">Jinja</a> templates</li>
+<li>Publish content in multiple languages</li>
+<li>Atom/RSS feeds</li>
+<li>Code syntax highlighting</li>
+<li>Import from WordPress, RSS feeds, and other services</li>
+<li>Modular plugin system and corresponding <a class="reference external" href="https://github.com/getpelican/pelican-plugins">plugin repository</a></li>
+</ul>
+<p>… and many other features.</p>
+<div class="section" id="next-steps">
+<h2>Next Steps</h2>
+<p>Learn more about the Pelican static site generator via:</p>
+<ul class="simple">
+<li><a class="reference external" href="http://blog.getpelican.com/category/news.html">Pelican news</a></li>
+<li>the extensive <a class="reference external" href="http://docs.getpelican.com/">documentation</a></li>
+<li><a class="reference external" href="https://github.com/getpelican/pelican">source code on GitHub</a></li>
+<li><a class="reference external" href="https://twitter.com/getpelican">Pelican on Twitter</a></li>
+</ul>
+</div>
+<div class="section" id="support-pelican-development">
+<h2>Support Pelican Development</h2>
+<p>Following are ways you can support Pelican’s development:</p>
+<ul class="simple">
+<li><a class="reference external" href="https://donate.getpelican.com">donate</a> to Pelican Dev Team</li>
+<li>follow <a class="reference external" href="https://twitter.com/jmayer">&#64;jmayer</a> and <a class="reference external" href="https://twitter.com/getpelican">Pelican on Twitter</a></li>
+<li>contribute pull requests, help triage issues, and improve documentation</li>
+</ul>
+<p><a class="reference external" href="https://donate.getpelican.com"><img alt="donate-fosspay" src="https://badgen.net/badge/fosspay/donate/yellow"/></a> <a class="reference external" href="https://liberapay.com/Pelican/donate"><img alt="donate-liberapay" src="https://badgen.net/badge/liberapay/donate/yellow"/></a></p>
+</div>
+
+</section>
+        <section id="extras" class="body">
+                <div class="blogroll">
+                        <h2>links</h2>
+                        <ul>
+                            <li><a href="https://docs.getpelican.com/">Pelican Docs</a></li>
+                            <li><a href="https://donate.getpelican.com/">Support Pelican</a></li>
+                            <li><a href="https://justinmayer.com/">Justin Mayer</a></li>
+                        </ul>
+                </div><!-- /.blogroll -->
+                <div class="social">
+                        <h2>follow</h2>
+                        <ul>
+                            <li><a href="/feeds/all.atom.xml" type="application/atom+xml" rel="alternate">atom feed</a></li>
+
+                            <li><a href="https://twitter.com/getpelican">twitter</a></li>
+                            <li><a href="https://twitter.com/jmayer">@jmayer</a></li>
+                            <li><a href="https://github.com/getpelican">github</a></li>
+                        </ul>
+                </div><!-- /.social -->
+        </section><!-- /#extras -->
+
+        <footer id="contentinfo" class="body">
+                <address id="about" class="vcard body">
+                Proudly powered by <a href="http://getpelican.com/">Pelican</a>, which takes great advantage of <a href="http://python.org">Python</a>.
+                </address><!-- /#about -->
+
+                <p>The theme is by <a href="http://coding.smashingmagazine.com/2009/08/04/designing-a-html-5-layout-from-scratch/">Smashing Magazine</a>, thanks!</p>
+        </footer><!-- /#contentinfo -->
+
+</body>
+</html>
diff --git a/pelican-plugins/w3c_validate/test_w3c_validate.py b/pelican-plugins/w3c_validate/test_w3c_validate.py
new file mode 100644
index 0000000000000000000000000000000000000000..91c9f721ad1d65d6d2cc3e0d7509f966eb7ea83a
--- /dev/null
+++ b/pelican-plugins/w3c_validate/test_w3c_validate.py
@@ -0,0 +1,24 @@
+import os
+
+from pelican.tests.support import unittest
+
+from w3c_validate import validate_files
+
+
+CUR_DIR = os.path.dirname(__file__)
+TEST_CONTENT_DIR = os.path.join(CUR_DIR, 'test_content')
+
+
+class W3CValidateTest(unittest.TestCase):
+
+    def test_validate_ok(self):
+        with self.assertLogs('w3c_validate.wc3_validate', level='INFO') as logs:
+            validate_files(PelicanMock({'OUTPUT_PATH': TEST_CONTENT_DIR}))
+
+        self.assertEqual(logs.output, ['INFO:w3c_validate.wc3_validate:Validating: {}/getpelican.html'.format(TEST_CONTENT_DIR)])
+
+
+class PelicanMock:
+    'A dummy class exposing the only attribute needed by w3c_validate.validate_files'
+    def __init__(self, settings):
+        self.settings = settings
diff --git a/pelican-plugins/w3c_validate/wc3_validate.py b/pelican-plugins/w3c_validate/wc3_validate.py
new file mode 100644
index 0000000000000000000000000000000000000000..b2f67d3147feffa166a77bfcbaf9c8e67b008ea7
--- /dev/null
+++ b/pelican-plugins/w3c_validate/wc3_validate.py
@@ -0,0 +1,76 @@
+# -*- coding: utf-8 -*-
+"""
+W3C HTML Validator plugin for genrated content.
+"""
+
+
+from pelican import signals
+import logging
+import os
+
+LOG = logging.getLogger(__name__)
+
+INCLUDE_TYPES = ['html']
+
+
+def validate_files(pelican):
+    """
+    Validate a generated HTML file
+    :param pelican: pelican object
+    """
+    for dirpath, _, filenames in os.walk(pelican.settings['OUTPUT_PATH']):
+        for name in filenames:
+            if should_validate(name):
+                filepath = os.path.join(dirpath, name)
+                validate(filepath)
+
+
+def validate(filename):
+    """
+    Use W3C validator service: https://bitbucket.org/nmb10/py_w3c/ .
+    :param filename: the filename to validate
+    """
+    try:
+        from html.parser import HTMLParser
+    except ImportError:  # fallback for Python 2:
+        from HTMLParser import HTMLParser
+    from py_w3c.validators.html.validator import HTMLValidator
+
+    h = HTMLParser()  # for unescaping WC3 messages
+
+    vld = HTMLValidator()
+    LOG.info("Validating: {0}".format(filename))
+
+    # call w3c webservice
+    vld.validate_file(filename)
+
+    # display errors and warning
+    for err in vld.errors:
+        line = err.get('line') or err['lastLine']
+        col = err.get('col') or '{}-{}'.format(err['firstColumn'], err['lastColumn'])
+        LOG.error(u'line: {0}; col: {1}; message: {2}'.
+                  format(line, col, h.unescape(err['message']))
+                  )
+    for err in vld.warnings:
+        line = err.get('line') or err['lastLine']
+        col = err.get('col') or '{}-{}'.format(err['firstColumn'], err['lastColumn'])
+        LOG.warning(u'line: {0}; col: {1}; message: {2}'.
+                    format(line, col, h.unescape(err['message']))
+                    )
+
+
+def should_validate(filename):
+    """Check if the filename is a type of file that should be validated.
+    :param filename: A file name to check against
+    """
+    for extension in INCLUDE_TYPES:
+        if filename.endswith(extension):
+            return True
+    return False
+
+
+def register():
+    """
+    Register Pelican signal for validating content after it is generated.
+    """
+    signals.finalized.connect(validate_files)
diff --git a/pelican-plugins/webring/README.md b/pelican-plugins/webring/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..087a88fa565220c974588a04b76cd6ada3fbe082
--- /dev/null
+++ b/pelican-plugins/webring/README.md
@@ -0,0 +1,107 @@
+# Webring Plugin
+
+**NOTE: [This plugin has been moved to its own repository](https://github.com/pelican-plugins/webring). Please file any issues/PRs there. Once all plugins have been migrated to the [new Pelican Plugins organization](https://github.com/pelican-plugins), this monolithic repository will be archived.**
+
+This plugin retrieves the latest posts from a list of web feeds and makes
+them available in templates, effectively creating a [partial webring][1]. Posts
+are sorted from newer to older.
+
+It is inspired by [openring](https://git.sr.ht/~sircmpwn/openring), a tool for
+generating an HTML file to include in your [SSG][2] from a template and a list of
+web feeds.
+
+## Requirements
+
+It requires the `feedparser` module:
+
+    $ pip install feedparser
+
+`feedparser` supports many common and not so common feed formats (e.g RSS/Atom). [See a list here](https://pythonhosted.org/feedparser/introduction.html).
+
+## Settings
+
+```
+WEBRING_FEED_URLS = []
+```
+A list of web feeds in the form of a URL or local file.
+
+```
+WEBRING_MAX_ARTICLES = 3
+```
+The maximum number of articles.
+
+```
+WEBRING_ARTICLES_PER_FEED = 1
+```
+The maximum number of articles per feed.
+
+```
+WEBRING_SUMMARY_LENGTH = 128
+```
+The maximum length of post summaries.
+
+```
+WEBRING_CLEAN_SUMMARY_HTML = True
+```
+Whether to clean html tags from post summaries or not.
+
+### Example
+Let's suppose we have two blogs in our webring and want to show two articles
+per blog. We would also like to show a quite short summary.
+
+```
+WEBRING_FEED_URLS = [
+    'https://justinmayer.com/feeds/all.atom.xml',
+    'https://danluu.com/atom.xml'
+]
+WEBRING_ARTICLES_PER_FEED = 2
+WEBRING_MAX_ARTICLES = 4
+WEBRING_SUMMARY_LENGTH = 25
+```
+
+## Templates
+The plugin makes available the resulting web feed articles in the variable
+`webring_articles`, which is a list of `Article` objects whose attributes are:
+
+- `title`: The article title.
+- `link`: The article URL.
+- `date`: The article date as a Pelican `utils.SafeDatetime` object, which can
+be used with [Pelican's Jinja filter `strftime`](https://docs.getpelican.com/en/stable/themes.html#date-formatting).
+- `summary`: The article summary, as provided in the web feed and modified
+according to this plugin's settings.
+- `source_title`: The title of the web feed.
+- `source_link`: A link to the web feed.
+- `source_id`: An identification field provided in some web feeds.
+
+See the following section for an example on how to iterate the article list.
+
+### Example
+Imagine we'd like to put our webring in the bottom of the default Pelican
+template (ie. notmyidea). To simplify, we'll use the existing CSS classes.
+
+Edit the `notmyidea/templates/base.html` file and make it look like this:
+
+```
+        ...
+        <section id="extras" class="body">
+        {% if WEBRING_FEED_URLS %}
+            <div class="webring">
+                <h2>Webring</h2>
+                {% for article in webring_articles %}
+                <p><a href="{{ article.link }}">{{ article.title }}</a></p>
+                <p>{{ article.date|strftime('%d %B %Y') }} - {{ article.summary}}</p>
+                {% endfor %}
+            </div>
+        {% endif %}
+        {% if LINKS %}
+        ...
+```
+
+If there were no links or social widgets, the result would be like in the
+image below:
+
+![Example of Webring](https://github.com/getpelican/pelican-plugins/raw/master/webring/example.png)
+
+
+[1]: https://en.wikipedia.org/wiki/Webring "In a proper webring, websites would be linked in a circular structure."
+[2]: https://en.wikipedia.org/wiki/Category:Static_website_generators "Static Site Generator"
diff --git a/pelican-plugins/webring/__init__.py b/pelican-plugins/webring/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..42eceb81a93f359500edea1c963dd8cfbac5710d
--- /dev/null
+++ b/pelican-plugins/webring/__init__.py
@@ -0,0 +1 @@
+from .webring import *
diff --git a/pelican-plugins/webring/example.png b/pelican-plugins/webring/example.png
new file mode 100644
index 0000000000000000000000000000000000000000..e8ce5f0bb43b26a843fd560408812d5cf6a52a41
Binary files /dev/null and b/pelican-plugins/webring/example.png differ
diff --git a/pelican-plugins/webring/requirements.txt b/pelican-plugins/webring/requirements.txt
new file mode 100644
index 0000000000000000000000000000000000000000..1b25361f86eaa497295cdf7fb17befa86cb20de3
--- /dev/null
+++ b/pelican-plugins/webring/requirements.txt
@@ -0,0 +1 @@
+feedparser
diff --git a/pelican-plugins/webring/test_data/pelican-atom.xml b/pelican-plugins/webring/test_data/pelican-atom.xml
new file mode 100644
index 0000000000000000000000000000000000000000..743009475fed484dd03aab636dd9b066e41df0b4
--- /dev/null
+++ b/pelican-plugins/webring/test_data/pelican-atom.xml
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="utf-8"?>
+<feed xmlns="http://www.w3.org/2005/Atom"><title>Site with Pelican sample content</title><link href="http://example.com/" rel="alternate"></link><link href="http://example.com/pelican-atom.xml" rel="self"></link><id>http://example.com/</id><updated>2013-11-17T23:29:00+01:00</updated><entry><title>FILENAME_METADATA example</title><link href="http://example.com/filename_metadata-example.html" rel="alternate"></link><published>2012-11-30T00:00:00+01:00</published><updated>2012-11-30T00:00:00+01:00</updated><author><name>davidag</name></author><id>tag:example.com,2012-11-30:/filename_metadata-example.html</id><content type="html">&lt;p&gt;Some cool stuff!&lt;/p&gt;
+</content></entry><entry><title>Second article</title><link href="http://example.com/second-article.html" rel="alternate"></link><published>2012-02-29T00:00:00+01:00</published><updated>2012-02-29T00:00:00+01:00</updated><author><name>davidag</name></author><id>tag:example.com,2012-02-29:/second-article.html</id><content type="html">&lt;p&gt;This is some article, in english&lt;/p&gt;
+</content><category term="foo"></category><category term="bar"></category><category term="baz"></category></entry><entry><title>Deuxième article</title><link href="http://example.com/second-article-fr.html" rel="alternate"></link><published>2012-02-29T00:00:00+01:00</published><updated>2012-02-29T00:00:00+01:00</updated><author><name>davidag</name></author><id>tag:example.com,2012-02-29:/second-article-fr.html</id><content type="html">&lt;p&gt;Ceci est un article, en français.&lt;/p&gt;
+</content><category term="foo"></category><category term="bar"></category><category term="baz"></category></entry><entry><title>A markdown powered article</title><link href="http://example.com/a-markdown-powered-article.html" rel="alternate"></link><published>2011-04-20T00:00:00+02:00</published><updated>2011-04-20T00:00:00+02:00</updated><author><name>davidag</name></author><id>tag:example.com,2011-04-20:/a-markdown-powered-article.html</id><content type="html">&lt;p&gt;You're mutually oblivious.&lt;/p&gt;
+&lt;p&gt;&lt;a href="http://example.com/unbelievable.html"&gt;a root-relative link to unbelievable&lt;/a&gt;
+&lt;a href="http://example.com/unbelievable.html"&gt;a file-relative link to unbelievable&lt;/a&gt;&lt;/p&gt;</content></entry><entry><title>Article 1</title><link href="http://example.com/article-1.html" rel="alternate"></link><published>2011-02-17T00:00:00+01:00</published><updated>2011-02-17T00:00:00+01:00</updated><author><name>davidag</name></author><id>tag:example.com,2011-02-17:/article-1.html</id><content type="html">&lt;p&gt;Article 1&lt;/p&gt;
+</content></entry><entry><title>Article 2</title><link href="http://example.com/article-2.html" rel="alternate"></link><published>2011-02-17T00:00:00+01:00</published><updated>2011-02-17T00:00:00+01:00</updated><author><name>davidag</name></author><id>tag:example.com,2011-02-17:/article-2.html</id><content type="html">&lt;p&gt;Article 2&lt;/p&gt;
+</content></entry><entry><title>Article 3</title><link href="http://example.com/article-3.html" rel="alternate"></link><published>2011-02-17T00:00:00+01:00</published><updated>2011-02-17T00:00:00+01:00</updated><author><name>davidag</name></author><id>tag:example.com,2011-02-17:/article-3.html</id><content type="html">&lt;p&gt;Article 3&lt;/p&gt;
+</content></entry><entry><title>This is a super article !</title><link href="http://example.com/this-is-a-super-article.html" rel="alternate"></link><published>2010-12-02T10:14:00+01:00</published><updated>2013-11-17T23:29:00+01:00</updated><author><name>Alexis Métaireau</name></author><id>tag:example.com,2010-12-02:/this-is-a-super-article.html</id><summary type="html">&lt;p class="first last"&gt;Multi-line metadata should be supported
+as well as &lt;strong&gt;inline markup&lt;/strong&gt;.&lt;/p&gt;
+</summary><content type="html">&lt;p&gt;Some content here !&lt;/p&gt;
+&lt;div class="section" id="this-is-a-simple-title"&gt;
+&lt;h2&gt;This is a simple title&lt;/h2&gt;
+&lt;p&gt;And here comes the cool &lt;a class="reference external" href="http://books.couchdb.org/relax/design-documents/views"&gt;stuff&lt;/a&gt;.&lt;/p&gt;
+&lt;img alt="alternate text" src="http://example.com/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /&gt;
+&lt;img alt="alternate text" src="http://example.com/pictures/Sushi_Macro.jpg" style="width: 600px; height: 450px;" /&gt;
+&lt;pre class="literal-block"&gt;
+&amp;gt;&amp;gt;&amp;gt; from ipdb import set_trace
+&amp;gt;&amp;gt;&amp;gt; set_trace()
+&lt;/pre&gt;
+&lt;p&gt;→ And now try with some utf8 hell: ééé&lt;/p&gt;
+&lt;/div&gt;
+</content><category term="foo"></category><category term="bar"></category><category term="foobar"></category></entry><entry><title>Oh yeah !</title><link href="http://example.com/oh-yeah.html" rel="alternate"></link><published>2010-10-20T10:14:00+02:00</published><updated>2010-10-20T10:14:00+02:00</updated><author><name>Alexis Métaireau</name></author><id>tag:example.com,2010-10-20:/oh-yeah.html</id><content type="html">&lt;div class="section" id="why-not"&gt;
+&lt;h2&gt;Why not ?&lt;/h2&gt;
+&lt;p&gt;After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst !
+YEAH !&lt;/p&gt;
+&lt;img alt="alternate text" src="http://example.com/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /&gt;
+&lt;/div&gt;
+</content><category term="oh"></category><category term="bar"></category><category term="yeah"></category></entry><entry><title>Unbelievable !</title><link href="http://example.com/unbelievable.html" rel="alternate"></link><published>2010-10-15T20:30:00+02:00</published><updated>2010-10-15T20:30:00+02:00</updated><author><name>davidag</name></author><id>tag:example.com,2010-10-15:/unbelievable.html</id><summary type="html">&lt;p&gt;Or completely awesome. Depends the needs.&lt;/p&gt;
+&lt;p&gt;&lt;a class="reference external" href="http://example.com/a-markdown-powered-article.html"&gt;a root-relative link to markdown-article&lt;/a&gt;
+&lt;a class="reference external" href="http://example.com/a-markdown-powered-article.html"&gt;a file-relative link to markdown-article&lt;/a&gt;&lt;/p&gt;
+&lt;div class="section" id="testing-sourcecode-directive"&gt;
+&lt;h2&gt;Testing sourcecode directive&lt;/h2&gt;
+&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;1&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;formatter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;VARIANTS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
+&lt;/pre&gt;&lt;/div&gt;
+&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;
+&lt;div class="section" id="testing-another-case"&gt;
+&lt;h2&gt;Testing another case&lt;/h2&gt;
+&lt;p&gt;This will now have a line number in 'custom' since it's the default in
+pelican.conf, it will …&lt;/p&gt;&lt;/div&gt;</summary><content type="html">&lt;p&gt;Or completely awesome. Depends the needs.&lt;/p&gt;
+&lt;p&gt;&lt;a class="reference external" href="http://example.com/a-markdown-powered-article.html"&gt;a root-relative link to markdown-article&lt;/a&gt;
+&lt;a class="reference external" href="http://example.com/a-markdown-powered-article.html"&gt;a file-relative link to markdown-article&lt;/a&gt;&lt;/p&gt;
+&lt;div class="section" id="testing-sourcecode-directive"&gt;
+&lt;h2&gt;Testing sourcecode directive&lt;/h2&gt;
+&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;1&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;formatter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;VARIANTS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
+&lt;/pre&gt;&lt;/div&gt;
+&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;
+&lt;div class="section" id="testing-another-case"&gt;
+&lt;h2&gt;Testing another case&lt;/h2&gt;
+&lt;p&gt;This will now have a line number in 'custom' since it's the default in
+pelican.conf, it will have nothing in default.&lt;/p&gt;
+&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;formatter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;VARIANTS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
+&lt;/pre&gt;&lt;/div&gt;
+&lt;p&gt;Lovely.&lt;/p&gt;
+&lt;/div&gt;
+&lt;div class="section" id="testing-more-sourcecode-directives"&gt;
+&lt;h2&gt;Testing more sourcecode directives&lt;/h2&gt;
+&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span id="foo-8"&gt;&lt;a name="foo-8"&gt;&lt;/a&gt;&lt;span class="lineno special"&gt; 8 &lt;/span&gt;&lt;span class="testingk"&gt;def&lt;/span&gt; &lt;span class="testingnf"&gt;run&lt;/span&gt;&lt;span class="testingp"&gt;(&lt;/span&gt;&lt;span class="testingbp"&gt;self&lt;/span&gt;&lt;span class="testingp"&gt;):&lt;/span&gt;&lt;br&gt;&lt;/span&gt;&lt;span id="foo-9"&gt;&lt;a name="foo-9"&gt;&lt;/a&gt;&lt;span class="lineno"&gt;   &lt;/span&gt;    &lt;span class="testingbp"&gt;self&lt;/span&gt;&lt;span class="testingo"&gt;.&lt;/span&gt;&lt;span class="testingn"&gt;assert_has_content&lt;/span&gt;&lt;span class="testingp"&gt;()&lt;/span&gt;&lt;br&gt;&lt;/span&gt;&lt;span id="foo-10"&gt;&lt;a name="foo-10"&gt;&lt;/a&gt;&lt;span class="lineno special"&gt;10 &lt;/span&gt;    &lt;span class="testingk"&gt;try&lt;/span&gt;&lt;span class="testingp"&gt;:&lt;/span&gt;&lt;br&gt;&lt;/span&gt;&lt;span id="foo-11"&gt;&lt;a name="foo-11"&gt;&lt;/a&gt;&lt;span class="lineno"&gt;   &lt;/span&gt;        &lt;span class="testingn"&gt;lexer&lt;/span&gt; &lt;span class="testingo"&gt;=&lt;/span&gt; &lt;span class="testingn"&gt;get_lexer_by_name&lt;/span&gt;&lt;span class="testingp"&gt;(&lt;/span&gt;&lt;span class="testingbp"&gt;self&lt;/span&gt;&lt;span class="testingo"&gt;.&lt;/span&gt;&lt;span class="testingn"&gt;arguments&lt;/span&gt;&lt;span class="testingp"&gt;[&lt;/span&gt;&lt;span class="testingmi"&gt;0&lt;/span&gt;&lt;span class="testingp"&gt;])&lt;/span&gt;&lt;br&gt;&lt;/span&gt;&lt;span id="foo-12"&gt;&lt;a name="foo-12"&gt;&lt;/a&gt;&lt;span class="lineno special"&gt;12 &lt;/span&gt;    &lt;span class="testingk"&gt;except&lt;/span&gt; &lt;span class="testingne"&gt;ValueError&lt;/span&gt;&lt;span class="testingp"&gt;:&lt;/span&gt;&lt;br&gt;&lt;/span&gt;&lt;span id="foo-13"&gt;&lt;a name="foo-13"&gt;&lt;/a&gt;&lt;span class="lineno"&gt;   &lt;/span&gt;        &lt;span class="testingc1"&gt;# no lexer found - use the text one instead of an exception&lt;/span&gt;&lt;br&gt;&lt;/span&gt;&lt;span id="foo-14"&gt;&lt;a name="foo-14"&gt;&lt;/a&gt;&lt;span class="lineno special"&gt;14 &lt;/span&gt;        &lt;span class="testingn"&gt;lexer&lt;/span&gt; &lt;span class="testingo"&gt;=&lt;/span&gt; &lt;span class="testingn"&gt;TextLexer&lt;/span&gt;&lt;span class="testingp"&gt;()&lt;/span&gt;&lt;br&gt;&lt;/span&gt;&lt;span id="foo-15"&gt;&lt;a name="foo-15"&gt;&lt;/a&gt;&lt;span class="lineno"&gt;   &lt;/span&gt;&lt;br&gt;&lt;/span&gt;&lt;span id="foo-16"&gt;&lt;a name="foo-16"&gt;&lt;/a&gt;&lt;span class="lineno special"&gt;16 &lt;/span&gt;    &lt;span class="testingk"&gt;if&lt;/span&gt; &lt;span class="testingp"&gt;(&lt;/span&gt;&lt;span class="testings1"&gt;&amp;#39;linenos&amp;#39;&lt;/span&gt; &lt;span class="testingow"&gt;in&lt;/span&gt; &lt;span class="testingbp"&gt;self&lt;/span&gt;&lt;span class="testingo"&gt;.&lt;/span&gt;&lt;span class="testingn"&gt;options&lt;/span&gt; &lt;span class="testingow"&gt;and&lt;/span&gt;&lt;br&gt;&lt;/span&gt;&lt;span id="foo-17"&gt;&lt;a name="foo-17"&gt;&lt;/a&gt;&lt;span class="lineno"&gt;   &lt;/span&gt;            &lt;span class="testingbp"&gt;self&lt;/span&gt;&lt;span class="testingo"&gt;.&lt;/span&gt;&lt;span class="testingn"&gt;options&lt;/span&gt;&lt;span class="testingp"&gt;[&lt;/span&gt;&lt;span class="testings1"&gt;&amp;#39;linenos&amp;#39;&lt;/span&gt;&lt;span class="testingp"&gt;]&lt;/span&gt; &lt;span class="testingow"&gt;not&lt;/span&gt; &lt;span class="testingow"&gt;in&lt;/span&gt; &lt;span class="testingp"&gt;(&lt;/span&gt;&lt;span class="testings1"&gt;&amp;#39;table&amp;#39;&lt;/span&gt;&lt;span class="testingp"&gt;,&lt;/span&gt; &lt;span class="testings1"&gt;&amp;#39;inline&amp;#39;&lt;/span&gt;&lt;span class="testingp"&gt;)):&lt;/span&gt;&lt;br&gt;&lt;/span&gt;&lt;span id="foo-18"&gt;&lt;a name="foo-18"&gt;&lt;/a&gt;&lt;span class="lineno special"&gt;18 &lt;/span&gt;        &lt;span class="testingbp"&gt;self&lt;/span&gt;&lt;span class="testingo"&gt;.&lt;/span&gt;&lt;span class="testingn"&gt;options&lt;/span&gt;&lt;span class="testingp"&gt;[&lt;/span&gt;&lt;span class="testings1"&gt;&amp;#39;linenos&amp;#39;&lt;/span&gt;&lt;span class="testingp"&gt;]&lt;/span&gt; &lt;span class="testingo"&gt;=&lt;/span&gt; &lt;span class="testings1"&gt;&amp;#39;table&amp;#39;&lt;/span&gt;&lt;br&gt;&lt;/span&gt;&lt;span id="foo-19"&gt;&lt;a name="foo-19"&gt;&lt;/a&gt;&lt;span class="lineno"&gt;   &lt;/span&gt;&lt;br&gt;&lt;/span&gt;&lt;span id="foo-20"&gt;&lt;a name="foo-20"&gt;&lt;/a&gt;&lt;span class="lineno special"&gt;20 &lt;/span&gt;    &lt;span class="testingk"&gt;for&lt;/span&gt; &lt;span class="testingn"&gt;flag&lt;/span&gt; &lt;span class="testingow"&gt;in&lt;/span&gt; &lt;span class="testingp"&gt;(&lt;/span&gt;&lt;span class="testings1"&gt;&amp;#39;nowrap&amp;#39;&lt;/span&gt;&lt;span class="testingp"&gt;,&lt;/span&gt; &lt;span class="testings1"&gt;&amp;#39;nobackground&amp;#39;&lt;/span&gt;&lt;span class="testingp"&gt;,&lt;/span&gt; &lt;span class="testings1"&gt;&amp;#39;anchorlinenos&amp;#39;&lt;/span&gt;&lt;span class="testingp"&gt;):&lt;/span&gt;&lt;br&gt;&lt;/span&gt;&lt;span id="foo-21"&gt;&lt;a name="foo-21"&gt;&lt;/a&gt;&lt;span class="lineno"&gt;   &lt;/span&gt;        &lt;span class="testingk"&gt;if&lt;/span&gt; &lt;span class="testingn"&gt;flag&lt;/span&gt; &lt;span class="testingow"&gt;in&lt;/span&gt; &lt;span class="testingbp"&gt;self&lt;/span&gt;&lt;span class="testingo"&gt;.&lt;/span&gt;&lt;span class="testingn"&gt;options&lt;/span&gt;&lt;span class="testingp"&gt;:&lt;/span&gt;&lt;br&gt;&lt;/span&gt;&lt;span id="foo-22"&gt;&lt;a name="foo-22"&gt;&lt;/a&gt;&lt;span class="lineno special"&gt;22 &lt;/span&gt;            &lt;span class="testingbp"&gt;self&lt;/span&gt;&lt;span class="testingo"&gt;.&lt;/span&gt;&lt;span class="testingn"&gt;options&lt;/span&gt;&lt;span class="testingp"&gt;[&lt;/span&gt;&lt;span class="testingn"&gt;flag&lt;/span&gt;&lt;span class="testingp"&gt;]&lt;/span&gt; &lt;span class="testingo"&gt;=&lt;/span&gt; &lt;span class="testingbp"&gt;True&lt;/span&gt;&lt;br&gt;&lt;/span&gt;&lt;span id="foo-23"&gt;&lt;a name="foo-23"&gt;&lt;/a&gt;&lt;span class="lineno"&gt;   &lt;/span&gt;&lt;br&gt;&lt;/span&gt;&lt;span id="foo-24"&gt;&lt;a name="foo-24"&gt;&lt;/a&gt;&lt;span class="lineno special"&gt;24 &lt;/span&gt;    &lt;span class="testingc1"&gt;# noclasses should already default to False, but just in case...&lt;/span&gt;&lt;br&gt;&lt;/span&gt;&lt;span id="foo-25"&gt;&lt;a name="foo-25"&gt;&lt;/a&gt;&lt;span class="lineno"&gt;   &lt;/span&gt;    &lt;span class="testingn"&gt;formatter&lt;/span&gt; &lt;span class="testingo"&gt;=&lt;/span&gt; &lt;span class="testingn"&gt;HtmlFormatter&lt;/span&gt;&lt;span class="testingp"&gt;(&lt;/span&gt;&lt;span class="testingn"&gt;noclasses&lt;/span&gt;&lt;span class="testingo"&gt;=&lt;/span&gt;&lt;span class="testingbp"&gt;False&lt;/span&gt;&lt;span class="testingp"&gt;,&lt;/span&gt; &lt;span class="testingo"&gt;**&lt;/span&gt;&lt;span class="testingbp"&gt;self&lt;/span&gt;&lt;span class="testingo"&gt;.&lt;/span&gt;&lt;span class="testingn"&gt;options&lt;/span&gt;&lt;span class="testingp"&gt;)&lt;/span&gt;&lt;br&gt;&lt;/span&gt;&lt;span id="foo-26"&gt;&lt;a name="foo-26"&gt;&lt;/a&gt;&lt;span class="lineno special"&gt;26 &lt;/span&gt;    &lt;span class="testingn"&gt;parsed&lt;/span&gt; &lt;span class="testingo"&gt;=&lt;/span&gt; &lt;span class="testingn"&gt;highlight&lt;/span&gt;&lt;span class="testingp"&gt;(&lt;/span&gt;&lt;span class="testings1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="testingse"&gt;\n&lt;/span&gt;&lt;span class="testings1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="testingo"&gt;.&lt;/span&gt;&lt;span class="testingn"&gt;join&lt;/span&gt;&lt;span class="testingp"&gt;(&lt;/span&gt;&lt;span class="testingbp"&gt;self&lt;/span&gt;&lt;span class="testingo"&gt;.&lt;/span&gt;&lt;span class="testingn"&gt;content&lt;/span&gt;&lt;span class="testingp"&gt;),&lt;/span&gt; &lt;span class="testingn"&gt;lexer&lt;/span&gt;&lt;span class="testingp"&gt;,&lt;/span&gt; &lt;span class="testingn"&gt;formatter&lt;/span&gt;&lt;span class="testingp"&gt;)&lt;/span&gt;&lt;br&gt;&lt;/span&gt;&lt;span id="foo-27"&gt;&lt;a name="foo-27"&gt;&lt;/a&gt;&lt;span class="lineno"&gt;   &lt;/span&gt;    &lt;span class="testingk"&gt;return&lt;/span&gt; &lt;span class="testingp"&gt;[&lt;/span&gt;&lt;span class="testingn"&gt;nodes&lt;/span&gt;&lt;span class="testingo"&gt;.&lt;/span&gt;&lt;span class="testingn"&gt;raw&lt;/span&gt;&lt;span class="testingp"&gt;(&lt;/span&gt;&lt;span class="testings1"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="testingp"&gt;,&lt;/span&gt; &lt;span class="testingn"&gt;parsed&lt;/span&gt;&lt;span class="testingp"&gt;,&lt;/span&gt; &lt;span class="testingn"&gt;format&lt;/span&gt;&lt;span class="testingo"&gt;=&lt;/span&gt;&lt;span class="testings1"&gt;&amp;#39;html&amp;#39;&lt;/span&gt;&lt;span class="testingp"&gt;)]&lt;/span&gt;&lt;br&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
+&lt;p&gt;Lovely.&lt;/p&gt;
+&lt;/div&gt;
+&lt;div class="section" id="testing-even-more-sourcecode-directives"&gt;
+&lt;h2&gt;Testing even more sourcecode directives&lt;/h2&gt;
+&lt;span class="n"&gt;formatter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;VARIANTS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
+&lt;p&gt;Lovely.&lt;/p&gt;
+&lt;/div&gt;
+&lt;div class="section" id="testing-overriding-config-defaults"&gt;
+&lt;h2&gt;Testing overriding config defaults&lt;/h2&gt;
+&lt;p&gt;Even if the default is line numbers, we can override it here&lt;/p&gt;
+&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;formatter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;VARIANTS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
+&lt;/pre&gt;&lt;/div&gt;
+&lt;p&gt;Lovely.&lt;/p&gt;
+&lt;/div&gt;
+</content></entry><entry><title>The baz tag</title><link href="http://example.com/tag/baz.html" rel="alternate"></link><published>2010-03-14T00:00:00+01:00</published><updated>2010-03-14T00:00:00+01:00</updated><author><name>davidag</name></author><id>tag:example.com,2010-03-14:/tag/baz.html</id><content type="html">&lt;p&gt;This article overrides the listening of the articles under the &lt;em&gt;baz&lt;/em&gt; tag.&lt;/p&gt;
+</content></entry></feed>
\ No newline at end of file
diff --git a/pelican-plugins/webring/test_data/pelican-rss.xml b/pelican-plugins/webring/test_data/pelican-rss.xml
new file mode 100644
index 0000000000000000000000000000000000000000..264a537b11a8309f621de05a9a5ad2ee7821067d
--- /dev/null
+++ b/pelican-plugins/webring/test_data/pelican-rss.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<rss version="2.0"><channel><title>Site with Pelican sample content</title><link>http://example.com/</link><description></description><lastBuildDate>Sun, 17 Nov 2013 23:29:00 +0100</lastBuildDate><item><title>FILENAME_METADATA example</title><link>http://example.com/filename_metadata-example.html</link><description>&lt;p&gt;Some cool stuff!&lt;/p&gt;
+</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">davidag</dc:creator><pubDate>Fri, 30 Nov 2012 00:00:00 +0100</pubDate><guid isPermaLink="false">tag:example.com,2012-11-30:/filename_metadata-example.html</guid></item><item><title>Second article</title><link>http://example.com/second-article.html</link><description>&lt;p&gt;This is some article, in english&lt;/p&gt;
+</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">davidag</dc:creator><pubDate>Wed, 29 Feb 2012 00:00:00 +0100</pubDate><guid isPermaLink="false">tag:example.com,2012-02-29:/second-article.html</guid><category>foo</category><category>bar</category><category>baz</category></item><item><title>Deuxième article</title><link>http://example.com/second-article-fr.html</link><description>&lt;p&gt;Ceci est un article, en français.&lt;/p&gt;
+</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">davidag</dc:creator><pubDate>Wed, 29 Feb 2012 00:00:00 +0100</pubDate><guid isPermaLink="false">tag:example.com,2012-02-29:/second-article-fr.html</guid><category>foo</category><category>bar</category><category>baz</category></item><item><title>A markdown powered article</title><link>http://example.com/a-markdown-powered-article.html</link><description>&lt;p&gt;You're mutually oblivious.&lt;/p&gt;
+&lt;p&gt;&lt;a href="http://example.com/unbelievable.html"&gt;a root-relative link to unbelievable&lt;/a&gt;
+&lt;a href="http://example.com/unbelievable.html"&gt;a file-relative link to unbelievable&lt;/a&gt;&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">davidag</dc:creator><pubDate>Wed, 20 Apr 2011 00:00:00 +0200</pubDate><guid isPermaLink="false">tag:example.com,2011-04-20:/a-markdown-powered-article.html</guid></item><item><title>Article 1</title><link>http://example.com/article-1.html</link><description>&lt;p&gt;Article 1&lt;/p&gt;
+</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">davidag</dc:creator><pubDate>Thu, 17 Feb 2011 00:00:00 +0100</pubDate><guid isPermaLink="false">tag:example.com,2011-02-17:/article-1.html</guid></item><item><title>Article 2</title><link>http://example.com/article-2.html</link><description>&lt;p&gt;Article 2&lt;/p&gt;
+</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">davidag</dc:creator><pubDate>Thu, 17 Feb 2011 00:00:00 +0100</pubDate><guid isPermaLink="false">tag:example.com,2011-02-17:/article-2.html</guid></item><item><title>Article 3</title><link>http://example.com/article-3.html</link><description>&lt;p&gt;Article 3&lt;/p&gt;
+</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">davidag</dc:creator><pubDate>Thu, 17 Feb 2011 00:00:00 +0100</pubDate><guid isPermaLink="false">tag:example.com,2011-02-17:/article-3.html</guid></item><item><title>This is a super article !</title><link>http://example.com/this-is-a-super-article.html</link><description>&lt;p class="first last"&gt;Multi-line metadata should be supported
+as well as &lt;strong&gt;inline markup&lt;/strong&gt;.&lt;/p&gt;
+</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Alexis Métaireau</dc:creator><pubDate>Thu, 02 Dec 2010 10:14:00 +0100</pubDate><guid isPermaLink="false">tag:example.com,2010-12-02:/this-is-a-super-article.html</guid><category>foo</category><category>bar</category><category>foobar</category></item><item><title>Oh yeah !</title><link>http://example.com/oh-yeah.html</link><description>&lt;div class="section" id="why-not"&gt;
+&lt;h2&gt;Why not ?&lt;/h2&gt;
+&lt;p&gt;After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst !
+YEAH !&lt;/p&gt;
+&lt;img alt="alternate text" src="http://example.com/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /&gt;
+&lt;/div&gt;
+</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Alexis Métaireau</dc:creator><pubDate>Wed, 20 Oct 2010 10:14:00 +0200</pubDate><guid isPermaLink="false">tag:example.com,2010-10-20:/oh-yeah.html</guid><category>oh</category><category>bar</category><category>yeah</category></item><item><title>Unbelievable !</title><link>http://example.com/unbelievable.html</link><description>&lt;p&gt;Or completely awesome. Depends the needs.&lt;/p&gt;
+&lt;p&gt;&lt;a class="reference external" href="http://example.com/a-markdown-powered-article.html"&gt;a root-relative link to markdown-article&lt;/a&gt;
+&lt;a class="reference external" href="http://example.com/a-markdown-powered-article.html"&gt;a file-relative link to markdown-article&lt;/a&gt;&lt;/p&gt;
+&lt;div class="section" id="testing-sourcecode-directive"&gt;
+&lt;h2&gt;Testing sourcecode directive&lt;/h2&gt;
+&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;1&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;formatter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;VARIANTS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
+&lt;/pre&gt;&lt;/div&gt;
+&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;
+&lt;div class="section" id="testing-another-case"&gt;
+&lt;h2&gt;Testing another case&lt;/h2&gt;
+&lt;p&gt;This will now have a line number in 'custom' since it's the default in
+pelican.conf, it will …&lt;/p&gt;&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">davidag</dc:creator><pubDate>Fri, 15 Oct 2010 20:30:00 +0200</pubDate><guid isPermaLink="false">tag:example.com,2010-10-15:/unbelievable.html</guid></item><item><title>The baz tag</title><link>http://example.com/tag/baz.html</link><description>&lt;p&gt;This article overrides the listening of the articles under the &lt;em&gt;baz&lt;/em&gt; tag.&lt;/p&gt;
+</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">davidag</dc:creator><pubDate>Sun, 14 Mar 2010 00:00:00 +0100</pubDate><guid isPermaLink="false">tag:example.com,2010-03-14:/tag/baz.html</guid></item></channel></rss>
\ No newline at end of file
diff --git a/pelican-plugins/webring/test_webring.py b/pelican-plugins/webring/test_webring.py
new file mode 100644
index 0000000000000000000000000000000000000000..fe3083dcfbf74e85065e0a01fafe0fe89f01db1c
--- /dev/null
+++ b/pelican-plugins/webring/test_webring.py
@@ -0,0 +1,107 @@
+# -*- coding: utf-8 -*-
+"""Tests for webring plugin
+
+Test Atom and RSS feeds have been generated using Pelican itself using the
+contents of its `samples/content` folder.
+"""
+import unittest
+import os
+from collections import Counter
+from operator import itemgetter, attrgetter
+
+from pelican.settings import DEFAULT_CONFIG
+from pelican.generators import Generator
+from pelican.tests.support import (
+    module_exists,
+    get_settings,
+    get_context,
+)
+
+import webring
+
+
+class NullGenerator(Generator):
+    pass
+
+
+@unittest.skipUnless(module_exists('feedparser'), 'install feedparser module')
+class WebringTest(unittest.TestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        webring.initialized(None)
+        cls.generators = [NullGenerator(
+            get_context(), get_settings(), '', '', '')]
+        cls.settings = cls.generators[0].settings
+
+    def setUp(self):
+        self.reset_settings()
+        self.set_feeds()
+
+    def reset_settings(self):
+        for name, value in self.settings.items():
+            if name.startswith('WEBRING'):
+                self.settings[name] = DEFAULT_CONFIG[name]
+
+    def set_feeds(self):
+        test_data_path = os.path.join(os.path.dirname(__file__), 'test_data')
+        # Contents will be duplicated but still different, as they come from
+        # two different feed URLs.
+        self.settings[webring.WEBRING_FEED_URLS_STR] = [
+            'file://' + os.path.join(test_data_path, 'pelican-rss.xml'),
+            'file://' + os.path.join(test_data_path, 'pelican-atom.xml'),
+        ]
+
+    def get_fetched_articles(self):
+        return self.generators[0].context['webring_articles']
+
+    def test_max_articles(self):
+        webring.fetch_feeds(self.generators)
+        articles = self.get_fetched_articles()
+        self.assertLessEqual(len(articles),
+                             self.settings[webring.WEBRING_MAX_ARTICLES_STR])
+
+    def test_articles_per_feed(self):
+        self.settings[webring.WEBRING_MAX_ARTICLES_STR] = 6
+        self.settings[webring.WEBRING_ARTICLES_PER_FEED_STR] = 3
+        webring.fetch_feeds(self.generators)
+        articles = self.get_fetched_articles()
+        feed_counts = Counter(a.source_id for a in articles)
+        self.assertEqual(list(map(itemgetter(1), feed_counts.items())), [3, 3])
+
+    def test_clean_summary(self):
+        webring.fetch_feeds(self.generators)
+        articles = self.get_fetched_articles()
+        summaries = list(map(attrgetter('summary'), articles))
+        self.assertTrue(all(s.find('<') < 0 for s in summaries))
+
+    def test_dont_clean_summary(self):
+        self.settings[webring.WEBRING_CLEAN_SUMMARY_HTML_STR] = False
+        webring.fetch_feeds(self.generators)
+        articles = self.get_fetched_articles()
+        summaries = list(map(attrgetter('summary'), articles))
+        self.assertTrue(all(s.find('<') >= 0 for s in summaries))
+
+    def test_summary_length(self):
+        self.settings[webring.WEBRING_SUMMARY_LENGTH_STR] = 10
+        webring.fetch_feeds(self.generators)
+        articles = self.get_fetched_articles()
+        self.assertTrue(all(
+            len(a.summary) - len('...') <= 10 for a in articles))
+
+    def test_long_summary_length(self):
+        self.settings[webring.WEBRING_SUMMARY_LENGTH_STR] = 1000
+        webring.fetch_feeds(self.generators)
+        articles = self.get_fetched_articles()
+        self.assertTrue(not any(a.summary.endswith('...') for a in articles))
+
+    def test_malformed_url(self):
+        self.settings[webring.WEBRING_FEED_URLS_STR] = [
+            '://pelican-atom.xml',
+        ]
+        webring.fetch_feeds(self.generators)
+        self.assertEqual(len(self.get_fetched_articles()), 0)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/pelican-plugins/webring/webring.py b/pelican-plugins/webring/webring.py
new file mode 100644
index 0000000000000000000000000000000000000000..3b6aa76405ba0f1641143f33d2d8ff7d4b93c3ef
--- /dev/null
+++ b/pelican-plugins/webring/webring.py
@@ -0,0 +1,170 @@
+# -*- coding: utf-8 -*-
+"""
+Webring plugin for Pelican
+==========================
+
+A plugin to create a webring in your web site from a list of web feeds.
+"""
+import re
+from six.moves.urllib.request import Request, urlopen
+from six.moves.urllib.error import URLError
+from collections import namedtuple
+import logging
+from operator import attrgetter
+
+from pelican import signals, utils
+
+log = logging.getLogger(__name__)
+
+try:
+    import feedparser
+except ImportError:
+    log.warning('Webring Plugin: Failed to load dependency (feedparser)')
+
+WEBRING_VERSION = '0.1'
+
+WEBRING_FEED_URLS_STR = 'WEBRING_FEED_URLS'
+WEBRING_MAX_ARTICLES_STR = 'WEBRING_MAX_ARTICLES'
+WEBRING_ARTICLES_PER_FEED_STR = 'WEBRING_ARTICLES_PER_FEED'
+WEBRING_SUMMARY_LENGTH_STR = 'WEBRING_SUMMARY_LENGTH'
+WEBRING_CLEAN_SUMMARY_HTML_STR = 'WEBRING_CLEAN_SUMMARY_HTML'
+
+Article = namedtuple(
+    'Article',
+    ['title', 'link', 'date', 'summary',
+     'source_title', 'source_link', 'source_id'])
+
+
+def register():
+    """Signal registration."""
+    signals.initialized.connect(initialized)
+    signals.all_generators_finalized.connect(fetch_feeds)
+
+
+def initialized(pelican):
+    from pelican.settings import DEFAULT_CONFIG
+    DEFAULT_CONFIG.setdefault(WEBRING_FEED_URLS_STR, [])
+    DEFAULT_CONFIG.setdefault(WEBRING_MAX_ARTICLES_STR, 3)
+    DEFAULT_CONFIG.setdefault(WEBRING_ARTICLES_PER_FEED_STR, 1)
+    DEFAULT_CONFIG.setdefault(WEBRING_SUMMARY_LENGTH_STR, 128)
+    DEFAULT_CONFIG.setdefault(WEBRING_CLEAN_SUMMARY_HTML_STR, True)
+    if pelican:
+        for name, value in DEFAULT_CONFIG.items():
+            if name.startswith('WEBRING'):
+                pelican.settings.setdefault(name, value)
+
+
+def fetch_feeds(generators):
+    settings = get_pelican_settings(generators)
+
+    fetched_articles = []
+    for feed_url in settings[WEBRING_FEED_URLS_STR]:
+        feed_html = get_feed_html(feed_url)
+        if feed_html:
+            fetched_articles.extend(
+                get_feed_articles(feed_html, feed_url, settings)
+            )
+
+    fetched_articles = sorted(
+        fetched_articles, key=attrgetter('date'), reverse=True)
+
+    max_articles = settings[WEBRING_MAX_ARTICLES_STR]
+    if len(fetched_articles) > max_articles:
+        fetched_articles = fetched_articles[:max_articles]
+
+    for generator in generators:
+        generator.context['webring_articles'] = fetched_articles
+
+
+def get_pelican_settings(generators):
+    """All generators contain a reference to the Pelican settings."""
+    assert len(generators) > 0
+    return generators[0].settings
+
+
+def get_feed_html(feed_url):
+    try:
+        req = Request(feed_url)
+        req.add_header(
+            'User-Agent',
+            (
+                'Webring Pelican plugin/{} '
+                + '+https://github.com/pelican/pelican-plugins'
+            ).format(WEBRING_VERSION)
+        )
+        return urlopen(req).read().decode('utf-8')
+    except URLError as e:
+        if hasattr(e, 'reason'):
+            log.warning('webring plugin: failed to connect to feed url (%s).',
+                        feed_url, e.reason)
+        if hasattr(e, 'code'):
+            log.warning('webring plugin: server returned %s error (%s).',
+                        e.code, feed_url)
+    except ValueError as e:
+        log.warning('webring plugin: wrong url provided (%s).', e)
+
+
+def get_feed_articles(feed_html, feed_url, settings):
+    parsed_feed = feedparser.parse(feed_html)
+
+    if parsed_feed.bozo:
+        log.warning('webring plugin: possible malformed or invalid feed (%s). '
+                    'Error=%s', feed_url, parsed_feed.bozo_exception)
+
+    articles = []
+    for n, entry in enumerate(parsed_feed.entries):
+        if n == settings[WEBRING_ARTICLES_PER_FEED_STR]:
+            break
+
+        published_dt = get_entry_datetime(entry)
+        truncated_summary = get_entry_summary(entry, settings)
+
+        articles.append(
+            Article(
+                title=entry.get('title', ''),
+                link=entry.get('link', ''),
+                date=published_dt,
+                summary=truncated_summary,
+                source_title=parsed_feed.feed.get('title', ''),
+                source_link=parsed_feed.feed.get('link', ''),
+                source_id=parsed_feed.feed.get('id', '')))
+
+    return articles
+
+
+def get_entry_datetime(entry):
+    try:
+        return utils.get_date(entry.get('published', ''))
+    except ValueError:
+        log.warning(
+            'Webring Plugin: Invalid date on feed entry titled "%s"'
+            % entry.get('title', 'Unknown title'))
+        return utils.SafeDatetime.now()
+
+
+def get_entry_summary(entry, settings):
+    # https://stackoverflow.com/a/12982689/11441
+    def cleanhtml(raw_html):
+        cleanr = re.compile('<.*?>')
+        cleantext = re.sub(cleanr, '', raw_html)
+        return cleantext
+
+    summary = entry.get('description', '')
+
+    # feedparser sanitizes html by default, but it can still contain html tags.
+    if settings[WEBRING_CLEAN_SUMMARY_HTML_STR]:
+        summary = cleanhtml(summary)
+
+    if len(summary) > settings[WEBRING_SUMMARY_LENGTH_STR]:
+        words = summary.split()
+        summary = ''
+        for w in words:
+            chars_left = settings[WEBRING_SUMMARY_LENGTH_STR] - len(summary)
+            if chars_left > 0:
+                summary += w if chars_left < len(w) else w[:chars_left]
+                summary += ' '
+            else:
+                break
+        summary = summary[:len(summary)-1] + "..."
+
+    return summary
diff --git a/pelican-plugins/yuicompressor/.gitignore b/pelican-plugins/yuicompressor/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..0d20b6487c61e7d1bde93acf4a14b7a89083a16d
--- /dev/null
+++ b/pelican-plugins/yuicompressor/.gitignore
@@ -0,0 +1 @@
+*.pyc
diff --git a/pelican-plugins/yuicompressor/README.md b/pelican-plugins/yuicompressor/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..1b7101f8ece9f52a44fea3588366e683f4912230
--- /dev/null
+++ b/pelican-plugins/yuicompressor/README.md
@@ -0,0 +1,37 @@
+# YUI Compressor Plugin
+
+A pelican plugin that minifies CSS/JS files using YUI Compressor during the
+building step.
+
+# Installation
+
+YUI Compressor needs to be present on your system. One way to obtain it is by
+installing it using pip:
+
+Important: This method assumes that JRE is already installed.
+
+```bash
+pip install yuicompressor
+```
+
+More information about YUI Compressor: https://github.com/yui/yuicompressor
+
+# Instructions
+
+Add `yuicompressor` to `pelicanconf.py` after installing YUI Compressor:
+
+```python
+PLUGINS = ['yuicompressor']
+```
+
+By default, this plugin expects the YUI Compressor executable to be named
+`yuicompressor`. This can be changed by defining `YUICOMPRESSOR_EXECUTABLE` in
+`pelicanconf.py`:
+
+```python
+YUICOMPRESSOR_EXECUTABLE = 'yui-compressor'
+```
+
+# Licence
+
+GNU AFFERO GENERAL PUBLIC LICENSE Version 3
diff --git a/pelican-plugins/yuicompressor/__init__.py b/pelican-plugins/yuicompressor/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..35e6c41a24ae967d1ac9a30f415fca15d73129a6
--- /dev/null
+++ b/pelican-plugins/yuicompressor/__init__.py
@@ -0,0 +1,2 @@
+# -*- coding: utf-8 -*-
+from .yuicompressor import *
diff --git a/pelican-plugins/yuicompressor/yuicompressor.py b/pelican-plugins/yuicompressor/yuicompressor.py
new file mode 100644
index 0000000000000000000000000000000000000000..e4a7bd5a21984b71f3c66ed4dd42faf2e6afbc00
--- /dev/null
+++ b/pelican-plugins/yuicompressor/yuicompressor.py
@@ -0,0 +1,29 @@
+# -*- coding: utf-8 -*-
+
+from pelican import signals
+from subprocess import check_call
+import logging
+import os
+
+logger = logging.getLogger(__name__)
+
+"""
+Minify CSS and JS files in output path with YUI Compressor from Yahoo.
+Required: an existing installation of YUI Compressor.
+"""
+
+def minify(pelican):
+    """
+      Minify CSS and JS with YUI Compressor
+      :param pelican: The Pelican instance
+    """
+    executable = pelican.settings.get('YUICOMPRESSOR_EXECUTABLE', 'yuicompressor')
+    for dirpath, _, filenames in os.walk(pelican.settings['OUTPUT_PATH']):
+        for name in filenames:
+            if os.path.splitext(name)[1] in ('.css','.js'):
+                filepath = os.path.join(dirpath, name)
+                logger.info('minify %s', filepath)
+                check_call([executable, '--charset', 'utf-8', filepath, '-o', filepath])
+
+def register():
+    signals.finalized.connect(minify)