diff --git a/.circleci/config.yml b/.circleci/config.yml
index 9091f75..cba784d 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -377,7 +377,7 @@ jobs:
deploy_homepage:
docker:
- - image: circleci/python:3.6
+ - image: circleci/python:3.6-node
working_directory: ~/offen/homepage
environment:
- SOURCE_BRANCH: master
@@ -397,6 +397,11 @@ jobs:
paths:
- ~/offen/homepage/venv
key: offen-homepage-{{ checksum "requirements.txt" }}
+ - run:
+ name: Install image optimization deps
+ command: |
+ npm install svgo -g
+ sudo apt-get install libjpeg-progs optipng
- run:
name: Deploy
command: |
diff --git a/homepage/docker-compose.yml b/homepage/docker-compose.yml
index 0d43786..e105b1a 100644
--- a/homepage/docker-compose.yml
+++ b/homepage/docker-compose.yml
@@ -12,6 +12,8 @@ services:
command: make devserver
ports:
- 8000:8000
+ environment:
+ DEBUG: '1'
volumes:
homepagedeps:
diff --git a/homepage/pelicanconf.py b/homepage/pelicanconf.py
index 8a546fb..9a786bf 100644
--- a/homepage/pelicanconf.py
+++ b/homepage/pelicanconf.py
@@ -4,12 +4,10 @@ from __future__ import unicode_literals
import logging
# If your site is available via HTTPS, make sure SITEURL begins with https://
-#SITEURL = 'https://www.offen.dev'
RELATIVE_URLS = False
AUTHOR = 'offen'
SITENAME = 'offen'
-SITEURL = 'https://www.offen.dev'
PATH = 'content'
TIMEZONE = 'Europe/Berlin'
DEFAULT_LANG = 'en'
@@ -30,6 +28,9 @@ THEME = './theme'
# Delete the output directory before generating new files.
DELETE_OUTPUT_DIRECTORY = True
+PLUGIN_PATHS = ['./plugins']
+PLUGINS = ['assets']
+
# dont create following standard pages
AUTHORS_SAVE_AS = None
ARCHIVES_SAVE_AS = None
diff --git a/homepage/plugins/assets/Readme.rst b/homepage/plugins/assets/Readme.rst
new file mode 100644
index 0000000..42091cb
--- /dev/null
+++ b/homepage/plugins/assets/Readme.rst
@@ -0,0 +1,106 @@
+Asset management
+----------------
+
+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" %}
+
+ {% endassets %}
+
+... will produce a minified css file with a version identifier that looks like:
+
+.. code-block:: html
+
+
+
+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" %}
+
+ {% endassets %}
+
+Another example for Javascript:
+
+.. code-block:: jinja
+
+ {% assets filters="uglifyjs", output="js/packed.js", "js/jquery.js", "js/base.js", "js/widgets.js" %}
+
+ {% endassets %}
+
+The above will produce a minified JS file:
+
+.. code-block:: html
+
+
+
+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/homepage/plugins/assets/__init__.py b/homepage/plugins/assets/__init__.py
new file mode 100644
index 0000000..67b75dd
--- /dev/null
+++ b/homepage/plugins/assets/__init__.py
@@ -0,0 +1 @@
+from .assets import *
diff --git a/homepage/plugins/assets/assets.py b/homepage/plugins/assets/assets.py
new file mode 100644
index 0000000..e204dd6
--- /dev/null
+++ b/homepage/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::
+
+
+
+.. _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/homepage/plugins/optimize_images/Readme.md b/homepage/plugins/optimize_images/Readme.md
new file mode 100644
index 0000000..8efc926
--- /dev/null
+++ b/homepage/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/homepage/plugins/optimize_images/__init__.py b/homepage/plugins/optimize_images/__init__.py
new file mode 100644
index 0000000..cbb056a
--- /dev/null
+++ b/homepage/plugins/optimize_images/__init__.py
@@ -0,0 +1 @@
+from .optimize_images import *
diff --git a/homepage/plugins/optimize_images/optimize_images.py b/homepage/plugins/optimize_images/optimize_images.py
new file mode 100644
index 0000000..33593ce
--- /dev/null
+++ b/homepage/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 -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/homepage/publishconf.py b/homepage/publishconf.py
index cf5b588..e442015 100644
--- a/homepage/publishconf.py
+++ b/homepage/publishconf.py
@@ -19,9 +19,6 @@ CATEGORY_FEED_ATOM = 'feeds/{slug}.atom.xml'
DELETE_OUTPUT_DIRECTORY = True
-# Following items are often useful when publishing
-
-#DISQUS_SITENAME = ""
-#GOOGLE_ANALYTICS = ""
+PLUGINS += ['optimize_images']
OFFEN_ACCOUNT_ID = "5ec8345a-2a45-4eb9-92e5-8d9e5684db58"
diff --git a/homepage/requirements.txt b/homepage/requirements.txt
index a9d0eea..a62e2d6 100644
--- a/homepage/requirements.txt
+++ b/homepage/requirements.txt
@@ -1,2 +1,4 @@
pelican==4.0.1
markdown==3.1.1
+webassets==0.12.1
+cssmin==0.2.0
diff --git a/homepage/theme/static/scripts/fade.js b/homepage/theme/static/scripts/fade.js
index a752dad..70e7e13 100644
--- a/homepage/theme/static/scripts/fade.js
+++ b/homepage/theme/static/scripts/fade.js
@@ -1,3 +1,7 @@
-$(window).scroll(function(){
- $(".brand-index").css("opacity", 0 + $(window).scrollTop() / 100);
- });
+;(function ($) {
+ $(document).ready(function () {
+ $(window).scroll(function () {
+ $('.brand-index').css('opacity', 0 + $(window).scrollTop() / 100)
+ })
+ })
+})(window.jQuery)
diff --git a/homepage/theme/static/scripts/menu.js b/homepage/theme/static/scripts/menu.js
index e34d5b3..d8967d4 100644
--- a/homepage/theme/static/scripts/menu.js
+++ b/homepage/theme/static/scripts/menu.js
@@ -1,18 +1,16 @@
-(function($) {
- $(function() {
- $('nav ul li a:not(:only-child)').click(function(e) {
- $(this).siblings('.nav-dropdown').toggle();
- $('.dropdown').not($(this).siblings()).hide();
- e.stopPropagation();
- });
- $('html').click(function() {
- $('.nav-dropdown').hide();
- });
- $('#nav-toggle').click(function() {
- $('nav ul').slideToggle();
- });
- $('#nav-toggle').on('click', function() {
- this.classList.toggle('active');
- });
- });
-})(jQuery);
+;(function ($) {
+ $(function () {
+ $('nav ul li a:not(:only-child)').click(function (e) {
+ $(this).siblings('.nav-dropdown').toggle()
+ $('.dropdown').not($(this).siblings()).hide()
+ e.stopPropagation()
+ })
+ $('html').click(function () {
+ $('.nav-dropdown').hide()
+ })
+ $('#nav-toggle').click(function () {
+ $(this).closest('nav').find('ul').slideToggle()
+ $(this).toggleClass('active')
+ })
+ })
+})(window.jQuery)
diff --git a/homepage/theme/templates/base.html b/homepage/theme/templates/base.html
index d8a3e61..698fbdf 100644
--- a/homepage/theme/templates/base.html
+++ b/homepage/theme/templates/base.html
@@ -17,12 +17,9 @@
-
-
-
-
+ {% assets filters="cssmin", output="css/style.min.css", "css/normalize.css", "css/fonts.css", "css/style.css" %}
+
+ {% endassets %}
{% if OFFEN_ACCOUNT_ID %}
{% endif %}
@@ -40,7 +37,7 @@