1 | 1 |
deleted file mode 100644 |
... | ... |
@@ -1,73 +0,0 @@ |
1 |
-# |
|
2 |
-# Author: Brandon Mathis |
|
3 |
-# Based on the work of: Josediaz Gonzalez - https://github.com/josegonzalez/josediazgonzalez.com/blob/master/_plugins/blockquote.rb |
|
4 |
-# |
|
5 |
-# Outputs a string with a given attribution as a quote |
|
6 |
-# |
|
7 |
-# {% blockquote Bobby Willis http://google.com/blah the search for bobby's mom %} |
|
8 |
-# Wheeee! |
|
9 |
-# {% endblockquote %} |
|
10 |
-# ... |
|
11 |
-# <blockquote> |
|
12 |
-# <p>Wheeee!</p> |
|
13 |
-# <footer> |
|
14 |
-# <strong>Bobby Willis</strong><cite><a href="http://google.com/blah">The Search For Bobby's Mom</a> |
|
15 |
-# </blockquote> |
|
16 |
-# |
|
17 |
-require './_plugins/titlecase.rb' |
|
18 |
-module Jekyll |
|
19 |
- |
|
20 |
- class Blockquote < Liquid::Block |
|
21 |
- FullCiteWithTitle = /([\w\s]+)(https?:\/\/)(\S+\s)([\w\s]+)/i |
|
22 |
- FullCite = /([\w\s]+)(https?:\/\/)(\S+)/i |
|
23 |
- Author = /([\w\s]+)/ |
|
24 |
- |
|
25 |
- def initialize(tag_name, markup, tokens) |
|
26 |
- @by = nil |
|
27 |
- @source = nil |
|
28 |
- @title = nil |
|
29 |
- if markup =~ FullCiteWithTitle |
|
30 |
- @by = $1 |
|
31 |
- @source = $2 + $3 |
|
32 |
- @title = $4.titlecase |
|
33 |
- elsif markup =~ FullCite |
|
34 |
- @by = $1 |
|
35 |
- @source = $2 + $3 |
|
36 |
- elsif markup =~ Author |
|
37 |
- @by = $1 |
|
38 |
- end |
|
39 |
- super |
|
40 |
- end |
|
41 |
- |
|
42 |
- def render(context) |
|
43 |
- output = paragraphize(super.map(&:strip).join) |
|
44 |
- author = "<strong>#{@by.strip}</strong>" |
|
45 |
- if @source |
|
46 |
- url = @source.match(/https?:\/\/(.+)/)[1].split('/') |
|
47 |
- parts = [] |
|
48 |
- url.each do |part| |
|
49 |
- if (parts + [part]).join('/').length < 32 |
|
50 |
- parts << part |
|
51 |
- end |
|
52 |
- end |
|
53 |
- source = parts.join('/') |
|
54 |
- source << '/…' unless source == @source |
|
55 |
- end |
|
56 |
- cite = "<cite><a href='#{@source}'>#{(@title || source)}</a></cite>" |
|
57 |
- reply = if @by.nil? |
|
58 |
- output |
|
59 |
- elsif !@source.nil? |
|
60 |
- "#{output}<footer>#{author + cite}</footer>" |
|
61 |
- else |
|
62 |
- "#{output}<footer>#{author}</footer>" |
|
63 |
- end |
|
64 |
- "<blockquote>#{reply}</blockquote>" |
|
65 |
- end |
|
66 |
- |
|
67 |
- def paragraphize(input) |
|
68 |
- "<p>#{input.gsub(/\n\n/, '</p><p>').gsub(/\n/, '<br/>')}</p>" |
|
69 |
- end |
|
70 |
- end |
|
71 |
-end |
|
72 |
- |
|
73 |
-Liquid::Template.register_tag('blockquote', Jekyll::Blockquote) |
74 | 1 |
deleted file mode 100644 |
... | ... |
@@ -1,161 +0,0 @@ |
1 |
-# Jekyll category page generator. |
|
2 |
-# http://recursive-design.com/projects/jekyll-plugins/ |
|
3 |
-# |
|
4 |
-# Version: 0.1.4 (201101061053) |
|
5 |
-# |
|
6 |
-# Copyright (c) 2010 Dave Perrett, http://recursive-design.com/ |
|
7 |
-# Licensed under the MIT license (http://www.opensource.org/licenses/mit-license.php) |
|
8 |
-# |
|
9 |
-# A generator that creates category pages for jekyll sites. |
|
10 |
-# |
|
11 |
-# To use it, simply drop this script into the _plugins directory of your Jekyll site. You should |
|
12 |
-# also create a file called 'category_index.html' in the _layouts directory of your jekyll site |
|
13 |
-# with the following contents (note: you should remove the leading '# ' characters): |
|
14 |
-# |
|
15 |
-# ================================== COPY BELOW THIS LINE ================================== |
|
16 |
-# --- |
|
17 |
-# layout: default |
|
18 |
-# --- |
|
19 |
-# |
|
20 |
-# <h1 class="category">{{ page.title }}</h1> |
|
21 |
-# <ul class="posts"> |
|
22 |
-# {% for post in site.categories[page.category] %} |
|
23 |
-# <div>{{ post.date | date_to_html_string }}</div> |
|
24 |
-# <h2><a href="{{ post.url }}">{{ post.title }}</a></h2> |
|
25 |
-# <div class="categories">Filed under {{ post.categories | category_links }}</div> |
|
26 |
-# {% endfor %} |
|
27 |
-# </ul> |
|
28 |
-# ================================== COPY ABOVE THIS LINE ================================== |
|
29 |
-# |
|
30 |
-# You can alter the _layout_ setting if you wish to use an alternate layout, and obviously you |
|
31 |
-# can change the HTML above as you see fit. |
|
32 |
-# |
|
33 |
-# When you compile your jekyll site, this plugin will loop through the list of categories in your |
|
34 |
-# site, and use the layout above to generate a page for each one with a list of links to the |
|
35 |
-# individual posts. |
|
36 |
-# |
|
37 |
-# Included filters : |
|
38 |
-# - category_links: Outputs the list of categories as comma-separated <a> links. |
|
39 |
-# - date_to_html_string: Outputs the post.date as formatted html, with hooks for CSS styling. |
|
40 |
-# |
|
41 |
-# Available _config.yml settings : |
|
42 |
-# - category_dir: The subfolder to build category pages in (default is 'categories'). |
|
43 |
-# - category_title_prefix: The string used before the category name in the page title (default is |
|
44 |
-# 'Category: '). |
|
45 |
-module Jekyll |
|
46 |
- |
|
47 |
- |
|
48 |
- # The CategoryIndex class creates a single category page for the specified category. |
|
49 |
- class CategoryIndex < Page |
|
50 |
- |
|
51 |
- # Initializes a new CategoryIndex. |
|
52 |
- # |
|
53 |
- # +base+ is the String path to the <source>. |
|
54 |
- # +category_dir+ is the String path between <source> and the category folder. |
|
55 |
- # +category+ is the category currently being processed. |
|
56 |
- def initialize(site, base, category_dir, category) |
|
57 |
- @site = site |
|
58 |
- @base = base |
|
59 |
- @dir = category_dir |
|
60 |
- @name = 'index.html' |
|
61 |
- self.process(@name) |
|
62 |
- # Read the YAML data from the layout page. |
|
63 |
- self.read_yaml(File.join(base, '_layouts'), 'category_index.html') |
|
64 |
- self.data['category'] = category |
|
65 |
- # Set the title for this page. |
|
66 |
- title_prefix = site.config['category_title_prefix'] || 'Category: ' |
|
67 |
- self.data['title'] = "#{title_prefix}#{category}" |
|
68 |
- # Set the meta-description for this page. |
|
69 |
- meta_description_prefix = site.config['category_meta_description_prefix'] || 'Category: ' |
|
70 |
- self.data['description'] = "#{meta_description_prefix}#{category}" |
|
71 |
- end |
|
72 |
- |
|
73 |
- end |
|
74 |
- |
|
75 |
- |
|
76 |
- # The Site class is a built-in Jekyll class with access to global site config information. |
|
77 |
- class Site |
|
78 |
- |
|
79 |
- # Creates an instance of CategoryIndex for each category page, renders it, and |
|
80 |
- # writes the output to a file. |
|
81 |
- # |
|
82 |
- # +category_dir+ is the String path to the category folder. |
|
83 |
- # +category+ is the category currently being processed. |
|
84 |
- def write_category_index(category_dir, category) |
|
85 |
- index = CategoryIndex.new(self, self.source, category_dir, category) |
|
86 |
- index.render(self.layouts, site_payload) |
|
87 |
- index.write(self.dest) |
|
88 |
- # Record the fact that this page has been added, otherwise Site::cleanup will remove it. |
|
89 |
- self.pages << index |
|
90 |
- end |
|
91 |
- |
|
92 |
- # Loops through the list of category pages and processes each one. |
|
93 |
- def write_category_indexes |
|
94 |
- if self.layouts.key? 'category_index' |
|
95 |
- dir = self.config['category_dir'] || 'categories' |
|
96 |
- self.categories.keys.each do |category| |
|
97 |
- self.write_category_index(File.join(dir, category.gsub(/_|\W/, '-')), category) |
|
98 |
- end |
|
99 |
- |
|
100 |
- # Throw an exception if the layout couldn't be found. |
|
101 |
- else |
|
102 |
- throw "No 'category_index' layout found." |
|
103 |
- end |
|
104 |
- end |
|
105 |
- |
|
106 |
- end |
|
107 |
- |
|
108 |
- |
|
109 |
- # Jekyll hook - the generate method is called by jekyll, and generates all of the category pages. |
|
110 |
- class GenerateCategories < Generator |
|
111 |
- safe true |
|
112 |
- priority :low |
|
113 |
- |
|
114 |
- def generate(site) |
|
115 |
- site.write_category_indexes |
|
116 |
- end |
|
117 |
- |
|
118 |
- end |
|
119 |
- |
|
120 |
- |
|
121 |
- # Adds some extra filters used during the category creation process. |
|
122 |
- module Filters |
|
123 |
- |
|
124 |
- # Outputs a list of categories as comma-separated <a> links. This is used |
|
125 |
- # to output the category list for each post on a category page. |
|
126 |
- # |
|
127 |
- # +categories+ is the list of categories to format. |
|
128 |
- # |
|
129 |
- # Returns string |
|
130 |
- # |
|
131 |
- def category_links(categories) |
|
132 |
- categories = categories.sort!.map do |item| |
|
133 |
- "<a class='category' href='/#{@context.registers[:site].config['category_dir']}/#{item.gsub(/_|\W/, '-')}'/>#{item}</a>" |
|
134 |
- end |
|
135 |
- |
|
136 |
- case categories.length |
|
137 |
- when 0 |
|
138 |
- "" |
|
139 |
- when 1 |
|
140 |
- categories[0].to_s |
|
141 |
- else |
|
142 |
- "#{categories[0...-1].join(', ')}, #{categories[-1]}" |
|
143 |
- end |
|
144 |
- end |
|
145 |
- |
|
146 |
- # Outputs the post.date as formatted html, with hooks for CSS styling. |
|
147 |
- # |
|
148 |
- # +date+ is the date object to format as HTML. |
|
149 |
- # |
|
150 |
- # Returns string |
|
151 |
- def date_to_html_string(date) |
|
152 |
- result = '<span class="month">' + date.strftime('%b').upcase + '</span> ' |
|
153 |
- result += date.strftime('<span class="day">%d</span> ') |
|
154 |
- result += date.strftime('<span class="year">%Y</span> ') |
|
155 |
- result |
|
156 |
- end |
|
157 |
- |
|
158 |
- end |
|
159 |
- |
|
160 |
-end |
|
161 |
- |
2 | 1 |
deleted file mode 100644 |
... | ... |
@@ -1,75 +0,0 @@ |
1 |
-#custom filters for Octopress |
|
2 |
- |
|
3 |
-module OctopressFilters |
|
4 |
- # Used on the blog index to split posts on the <!--more--> marker |
|
5 |
- def exerpt(input) |
|
6 |
- if input.index(/<!--\s*more\s*-->/i) |
|
7 |
- input.split(/<!--\s*more\s*-->/i)[0] |
|
8 |
- else |
|
9 |
- input |
|
10 |
- end |
|
11 |
- end |
|
12 |
- |
|
13 |
- # Summary is used on the Archive pages to return the first block of content from a post. |
|
14 |
- def summary(input) |
|
15 |
- if input.index(/\n\n/) |
|
16 |
- input.split(/\n\n/)[0] |
|
17 |
- else |
|
18 |
- input |
|
19 |
- end |
|
20 |
- end |
|
21 |
- |
|
22 |
- # Replaces relative urls with full urls |
|
23 |
- def full_urls(input, url='') |
|
24 |
- input.gsub /(\s+(href|src)\s*=\s*["|']{1})(\/[^\"'>]+)/ do |
|
25 |
- $1+url+$3 |
|
26 |
- end |
|
27 |
- end |
|
28 |
- |
|
29 |
- # Returns a url without the http:// for use in as a search modifier eg. 'search terms site:website.com' |
|
30 |
- def search_url(input) |
|
31 |
- input.gsub /(https?:\/\/)(\S+)/ do |
|
32 |
- $2 |
|
33 |
- end |
|
34 |
- end |
|
35 |
- |
|
36 |
- # replaces primes with smartquotes using RubyPants |
|
37 |
- def smart_quotes(input) |
|
38 |
- require 'rubypants' |
|
39 |
- RubyPants.new(input).to_html |
|
40 |
- end |
|
41 |
- |
|
42 |
- # Returns a title cased string based on John Gruber's title case http://daringfireball.net/2008/08/title_case_update |
|
43 |
- def titlecase(input) |
|
44 |
- input.titlecase |
|
45 |
- end |
|
46 |
- |
|
47 |
- # Returns a datetime if the input is a string |
|
48 |
- def datetime(date) |
|
49 |
- if date.class == String |
|
50 |
- date = Time.parse(date) |
|
51 |
- end |
|
52 |
- date |
|
53 |
- end |
|
54 |
- |
|
55 |
- # Returns an ordidinal date eg July 22 2007 -> July 22nd 2007 |
|
56 |
- def ordinalize(date) |
|
57 |
- date = datetime(date) |
|
58 |
- "#{date.strftime('%b')} #{ordinal(date.strftime('%e').to_i)}, #{date.strftime('%Y')}" |
|
59 |
- end |
|
60 |
- |
|
61 |
- # Returns an ordinal number. 13 -> 13th, 21 -> 21st etc. |
|
62 |
- def ordinal(number) |
|
63 |
- if (11..13).include?(number.to_i % 100) |
|
64 |
- "#{number}<span>th</span>" |
|
65 |
- else |
|
66 |
- case number.to_i % 10 |
|
67 |
- when 1; "#{number}<span>st</span>" |
|
68 |
- when 2; "#{number}<span>nd</span>" |
|
69 |
- when 3; "#{number}<span>rd</span>" |
|
70 |
- else "#{number}<span>th</span>" |
|
71 |
- end |
|
72 |
- end |
|
73 |
- end |
|
74 |
-end |
|
75 |
-Liquid::Template.register_filter OctopressFilters |
76 | 1 |
deleted file mode 100644 |
... | ... |
@@ -1,94 +0,0 @@ |
1 |
-# A Liquid tag for Jekyll sites that allows embedding Gists and showing code for non-JavaScript enabled browsers and readers. |
|
2 |
-# by: Brandon Tilly |
|
3 |
-# Source URL: https://gist.github.com/1027674 |
|
4 |
-# Post http://brandontilley.com/2011/01/31/gist-tag-for-jekyll.html |
|
5 |
-# |
|
6 |
-# Example usage: {% gist 1027674 gist_tag.rb %} //embeds a gist for this plugin |
|
7 |
- |
|
8 |
-require 'cgi' |
|
9 |
-require 'digest/md5' |
|
10 |
-require 'net/https' |
|
11 |
-require 'uri' |
|
12 |
- |
|
13 |
-module Jekyll |
|
14 |
- class GistTag < Liquid::Tag |
|
15 |
- def initialize(tag_name, text, token) |
|
16 |
- super |
|
17 |
- @text = text |
|
18 |
- @cache_disabled = false |
|
19 |
- @cache_folder = File.expand_path "../_gist_cache", File.dirname(__FILE__) |
|
20 |
- FileUtils.mkdir_p @cache_folder |
|
21 |
- end |
|
22 |
- |
|
23 |
- def render(context) |
|
24 |
- if parts = @text.match(/([\d]*) (.*)/) |
|
25 |
- gist, file = parts[1].strip, parts[2].strip |
|
26 |
- script_url = script_url_for gist, file |
|
27 |
- code = get_cached_gist(gist, file) || get_gist_from_web(gist, file) |
|
28 |
- html_output_for script_url, code |
|
29 |
- else |
|
30 |
- "" |
|
31 |
- end |
|
32 |
- end |
|
33 |
- |
|
34 |
- def html_output_for(script_url, code) |
|
35 |
- code = CGI.escapeHTML code |
|
36 |
- <<-HTML |
|
37 |
-<script src='#{script_url}'></script> |
|
38 |
-<noscript><pre><code>#{code}</code></pre></noscript> |
|
39 |
- HTML |
|
40 |
- end |
|
41 |
- |
|
42 |
- def script_url_for(gist_id, filename) |
|
43 |
- "https://gist.github.com/#{gist_id}.js?file=#{filename}" |
|
44 |
- end |
|
45 |
- |
|
46 |
- def get_gist_url_for(gist, file) |
|
47 |
- "https://raw.github.com/gist/#{gist}/#{file}" |
|
48 |
- end |
|
49 |
- |
|
50 |
- def cache(gist, file, data) |
|
51 |
- cache_file = get_cache_file_for gist, file |
|
52 |
- File.open(cache_file, "w") do |io| |
|
53 |
- io.write data |
|
54 |
- end |
|
55 |
- end |
|
56 |
- |
|
57 |
- def get_cached_gist(gist, file) |
|
58 |
- return nil if @cache_disabled |
|
59 |
- cache_file = get_cache_file_for gist, file |
|
60 |
- File.read cache_file if File.exist? cache_file |
|
61 |
- end |
|
62 |
- |
|
63 |
- def get_cache_file_for(gist, file) |
|
64 |
- bad_chars = /[^a-zA-Z0-9\-_.]/ |
|
65 |
- gist = gist.gsub bad_chars, '' |
|
66 |
- file = file.gsub bad_chars, '' |
|
67 |
- md5 = Digest::MD5.hexdigest "#{gist}-#{file}" |
|
68 |
- File.join @cache_folder, "#{gist}-#{file}-#{md5}.cache" |
|
69 |
- end |
|
70 |
- |
|
71 |
- def get_gist_from_web(gist, file) |
|
72 |
- gist_url = get_gist_url_for gist, file |
|
73 |
- raw_uri = URI.parse gist_url |
|
74 |
- https = Net::HTTP.new raw_uri.host, raw_uri.port |
|
75 |
- https.use_ssl = true |
|
76 |
- https.verify_mode = OpenSSL::SSL::VERIFY_NONE |
|
77 |
- request = Net::HTTP::Get.new raw_uri.request_uri |
|
78 |
- data = https.request request |
|
79 |
- data = data.body |
|
80 |
- cache gist, file, data unless @cache_disabled |
|
81 |
- data |
|
82 |
- end |
|
83 |
- end |
|
84 |
- |
|
85 |
- class GistTagNoCache < GistTag |
|
86 |
- def initialize(tag_name, text, token) |
|
87 |
- super |
|
88 |
- @cache_disabled = true |
|
89 |
- end |
|
90 |
- end |
|
91 |
-end |
|
92 |
- |
|
93 |
-Liquid::Template.register_tag('gist', Jekyll::GistTag) |
|
94 |
-Liquid::Template.register_tag('gistnocache', Jekyll::GistTagNoCache) |
95 | 1 |
deleted file mode 100644 |
... | ... |
@@ -1,24 +0,0 @@ |
1 |
-module Jekyll |
|
2 |
- require 'haml' |
|
3 |
- class HamlConverter < Converter |
|
4 |
- safe true |
|
5 |
- priority :low |
|
6 |
- |
|
7 |
- def matches(ext) |
|
8 |
- ext =~ /haml/i |
|
9 |
- end |
|
10 |
- |
|
11 |
- def output_ext(ext) |
|
12 |
- ".html" |
|
13 |
- end |
|
14 |
- |
|
15 |
- def convert(content) |
|
16 |
- begin |
|
17 |
- engine = Haml::Engine.new(content) |
|
18 |
- engine.render |
|
19 |
- rescue StandardError => e |
|
20 |
- puts "!!! HAML Error: " + e.message |
|
21 |
- end |
|
22 |
- end |
|
23 |
- end |
|
24 |
-end |
25 | 1 |
deleted file mode 100644 |
... | ... |
@@ -1,40 +0,0 @@ |
1 |
-require 'pathname' |
|
2 |
- |
|
3 |
-module Jekyll |
|
4 |
- |
|
5 |
- class IncludeCodeTag < Liquid::Tag |
|
6 |
- def initialize(tag_name, file, tokens) |
|
7 |
- super |
|
8 |
- @file = file.strip |
|
9 |
- end |
|
10 |
- |
|
11 |
- def render(context) |
|
12 |
- code_dir = (context.registers[:site].config['code_dir'] || 'downloads/code') |
|
13 |
- code_path = (Pathname.new(context.registers[:site].source) + code_dir).expand_path |
|
14 |
- file = code_path + @file |
|
15 |
- |
|
16 |
- if File.symlink?(code_path) |
|
17 |
- return "Code directory '#{code_path}' cannot be a symlink" |
|
18 |
- end |
|
19 |
- |
|
20 |
- unless file.file? |
|
21 |
- return "File #{file} could not be found" |
|
22 |
- end |
|
23 |
- |
|
24 |
- Dir.chdir(code_path) do |
|
25 |
- code = file.read |
|
26 |
- file_type = file.extname |
|
27 |
- url = "#{context.registers[:site].config['url']}/#{code_dir}/#{@file}" |
|
28 |
- source = "<figure><figcaption><span>#{file.basename}</span><a href='#{url}'>download</a></figcaption>\n" |
|
29 |
- source += "{% highlight #{file_type} %}\n" + code + "\n{% endhighlight %}</figure>" |
|
30 |
- partial = Liquid::Template.parse(source) |
|
31 |
- context.stack do |
|
32 |
- partial.render(context) |
|
33 |
- end |
|
34 |
- end |
|
35 |
- end |
|
36 |
- end |
|
37 |
- |
|
38 |
-end |
|
39 |
- |
|
40 |
-Liquid::Template.register_tag('include_code', Jekyll::IncludeCodeTag) |
41 | 1 |
deleted file mode 100644 |
... | ... |
@@ -1,31 +0,0 @@ |
1 |
-require 'pathname' |
|
2 |
- |
|
3 |
-module Jekyll |
|
4 |
- |
|
5 |
- class IncludePartialTag < Liquid::Tag |
|
6 |
- def initialize(tag_name, file, tokens) |
|
7 |
- super |
|
8 |
- @file = file.strip |
|
9 |
- end |
|
10 |
- |
|
11 |
- def render(context) |
|
12 |
- file_dir = (context.registers[:site].source || 'source') |
|
13 |
- file_path = Pathname.new(file_dir).expand_path |
|
14 |
- file = file_path + @file |
|
15 |
- |
|
16 |
- unless file.file? |
|
17 |
- return "File #{file} could not be found" |
|
18 |
- end |
|
19 |
- |
|
20 |
- Dir.chdir(file_path) do |
|
21 |
- partial = Liquid::Template.parse(file.read) |
|
22 |
- context.stack do |
|
23 |
- partial.render(context) |
|
24 |
- end |
|
25 |
- end |
|
26 |
- end |
|
27 |
- end |
|
28 |
-end |
|
29 |
- |
|
30 |
-Liquid::Template.register_tag('include_partial', Jekyll::IncludePartialTag) |
|
31 |
- |
32 | 1 |
deleted file mode 100644 |
... | ... |
@@ -1,42 +0,0 @@ |
1 |
-# |
|
2 |
-# Author: Brandon Mathis |
|
3 |
-# Based on the sematic pullquote technique by Maykel Loomans at http://miekd.com/articles/pull-quotes-with-html5-and-css/ |
|
4 |
-# |
|
5 |
-# Outputs a span with a data-pullquote attribute set from the marked pullquote. Example: |
|
6 |
-# |
|
7 |
-# {% pullquote %} |
|
8 |
-# When writing longform posts, I find it helpful to include pullquotes, which help those scanning a post discern whether or not a post is helpful. |
|
9 |
-# It is important to note, {" pullquotes are merely visual in presentation and should not appear twice in the text. "} That is why it is prefered |
|
10 |
-# to use a CSS only technique for styling pullquotes. |
|
11 |
-# {% endpullquote %} |
|
12 |
-# ...will output... |
|
13 |
-# <p> |
|
14 |
-# <span data-pullquote="pullquotes are merely visual in presentation and should not appear twice in the text."> |
|
15 |
-# When writing longform posts, I find it helpful to include pullquotes, which help those scanning a post discern whether or not a post is helpful. |
|
16 |
-# It is important to note, pullquotes are merely visual in presentation and should not appear twice in the text. This is why a CSS only approach # for styling pullquotes is prefered. |
|
17 |
-# </span> |
|
18 |
-# </p> |
|
19 |
-# |
|
20 |
- |
|
21 |
-module Jekyll |
|
22 |
- |
|
23 |
- class PullquoteTag < Liquid::Block |
|
24 |
- PullQuoteMarkup = /\{(.+)\}/i |
|
25 |
- |
|
26 |
- def initialize(tag_name, markup, tokens) |
|
27 |
- super |
|
28 |
- end |
|
29 |
- |
|
30 |
- def render(context) |
|
31 |
- output = super |
|
32 |
- if output.join =~ /\{"\s*(.+)\s*"\}/ |
|
33 |
- @quote = $1 |
|
34 |
- "<span class='has-pullquote' data-pullquote='#{@quote}'>#{output.join.gsub(/\{"\s*|\s*"\}/, '')}</span>" |
|
35 |
- else |
|
36 |
- return "Surround your pullquote like this {! text to be quoted !}" |
|
37 |
- end |
|
38 |
- end |
|
39 |
- end |
|
40 |
-end |
|
41 |
- |
|
42 |
-Liquid::Template.register_tag('pullquote', Jekyll::PullquoteTag) |
43 | 1 |
deleted file mode 100644 |
... | ... |
@@ -1,30 +0,0 @@ |
1 |
-# |
|
2 |
-# Author: Raimonds Simanovskis, http://blog.rayapps.com/ |
|
3 |
-# Source URL: https://github.com/rsim/blog.rayapps.com/blob/master/_plugins/pygments_cache_patch.rb |
|
4 |
-# |
|
5 |
- |
|
6 |
-require 'fileutils' |
|
7 |
-require 'digest/md5' |
|
8 |
- |
|
9 |
-PYGMENTS_CACHE_DIR = File.expand_path('../../_code_cache', __FILE__) |
|
10 |
-FileUtils.mkdir_p(PYGMENTS_CACHE_DIR) |
|
11 |
- |
|
12 |
-Jekyll::HighlightBlock.class_eval do |
|
13 |
- def render_pygments(context, code) |
|
14 |
- if defined?(PYGMENTS_CACHE_DIR) |
|
15 |
- path = File.join(PYGMENTS_CACHE_DIR, "#{@lang}-#{Digest::MD5.hexdigest(code)}.html") |
|
16 |
- if File.exist?(path) |
|
17 |
- highlighted_code = File.read(path) |
|
18 |
- else |
|
19 |
- highlighted_code = Albino.new(code, @lang).to_s(@options) |
|
20 |
- File.open(path, 'w') {|f| f.print(highlighted_code) } |
|
21 |
- end |
|
22 |
- else |
|
23 |
- highlighted_code = Albino.new(code, @lang).to_s(@options) |
|
24 |
- end |
|
25 |
- output = add_code_tags(highlighted_code, @lang) |
|
26 |
- output = context["pygments_prefix"] + output if context["pygments_prefix"] |
|
27 |
- output = output + context["pygments_suffix"] if context["pygments_suffix"] |
|
28 |
- output |
|
29 |
- end |
|
30 |
-end |
31 | 1 |
deleted file mode 100644 |
... | ... |
@@ -1,308 +0,0 @@ |
1 |
-# Sitemap.xml Generator is a Jekyll plugin that generates a sitemap.xml file by |
|
2 |
-# traversing all of the available posts and pages. |
|
3 |
-# |
|
4 |
-# How To Use: |
|
5 |
-# 1) Copy source file into your _plugins folder within your Jekyll project. |
|
6 |
-# 2) Change modify the url variable in _config.yml to reflect your domain name. |
|
7 |
-# 3) Run Jekyll: jekyll --server to re-generate your site. |
|
8 |
-# |
|
9 |
-# Variables: |
|
10 |
-# * Change SITEMAP_FILE_NAME if you want your sitemap to be called something |
|
11 |
-# other than sitemap.xml. |
|
12 |
-# * Change the PAGES_INCLUDE_POSTS list to include any pages that are looping |
|
13 |
-# through your posts (e.g. "index.html", "archive.html", etc.). This will |
|
14 |
-# ensure that right after you make a new post, the last modified date will |
|
15 |
-# be updated to reflect the new post. |
|
16 |
-# * A sitemap.xml should be included in your _site folder. |
|
17 |
-# * If there are any files you don't want included in the sitemap, add them |
|
18 |
-# to the EXCLUDED_FILES list. The name should match the name of the source |
|
19 |
-# file. |
|
20 |
-# * If you want to include the optional changefreq and priority attributes, |
|
21 |
-# simply include custom variables in the YAML Front Matter of that file. |
|
22 |
-# The names of these custom variables are defined below in the |
|
23 |
-# CHANGE_FREQUENCY_CUSTOM_VARIABLE_NAME and PRIORITY_CUSTOM_VARIABLE_NAME |
|
24 |
-# constants. |
|
25 |
-# |
|
26 |
-# Notes: |
|
27 |
-# * The last modified date is determined by the latest from the following: |
|
28 |
-# system modified date of the page or post, system modified date of |
|
29 |
-# included layout, system modified date of included layout within that |
|
30 |
-# layout, ... |
|
31 |
-# |
|
32 |
-# Author: Michael Levin |
|
33 |
-# Site: http://www.kinnetica.com |
|
34 |
-# Distributed Under A Creative Commons License |
|
35 |
-# - http://creativecommons.org/licenses/by/3.0/ |
|
36 |
-# |
|
37 |
-# Modified for Octopress by John W. Long |
|
38 |
-# |
|
39 |
-require 'rexml/document' |
|
40 |
- |
|
41 |
-module Jekyll |
|
42 |
- |
|
43 |
- # Change SITEMAP_FILE_NAME if you would like your sitemap file |
|
44 |
- # to be called something else |
|
45 |
- SITEMAP_FILE_NAME = "sitemap.xml" |
|
46 |
- |
|
47 |
- # Any files to exclude from being included in the sitemap.xml |
|
48 |
- EXCLUDED_FILES = ["atom.xml"] |
|
49 |
- |
|
50 |
- # Any files that include posts, so that when a new post is added, the last |
|
51 |
- # modified date of these pages should take that into account |
|
52 |
- PAGES_INCLUDE_POSTS = ["index.html"] |
|
53 |
- |
|
54 |
- # Custom variable names for changefreq and priority elements |
|
55 |
- # These names are used within the YAML Front Matter of pages or posts |
|
56 |
- # for which you want to include these properties |
|
57 |
- CHANGE_FREQUENCY_CUSTOM_VARIABLE_NAME = "change_frequency" |
|
58 |
- PRIORITY_CUSTOM_VARIABLE_NAME = "priority" |
|
59 |
- |
|
60 |
- class Post |
|
61 |
- attr_accessor :name |
|
62 |
- |
|
63 |
- def full_path_to_source |
|
64 |
- File.join(@base, @name) |
|
65 |
- end |
|
66 |
- |
|
67 |
- def location_on_server |
|
68 |
- "#{site.config['url']}#{url}" |
|
69 |
- end |
|
70 |
- end |
|
71 |
- |
|
72 |
- class Page |
|
73 |
- attr_accessor :name |
|
74 |
- |
|
75 |
- def full_path_to_source |
|
76 |
- File.join(@base, @dir, @name) |
|
77 |
- end |
|
78 |
- |
|
79 |
- def location_on_server |
|
80 |
- location = "#{site.config['url']}#{@dir}#{url}" |
|
81 |
- location.gsub(/index.html$/, "") |
|
82 |
- end |
|
83 |
- end |
|
84 |
- |
|
85 |
- class Layout |
|
86 |
- def full_path_to_source |
|
87 |
- File.join(@base, @name) |
|
88 |
- end |
|
89 |
- end |
|
90 |
- |
|
91 |
- # Recover from strange exception when starting server without --auto |
|
92 |
- class SitemapFile < StaticFile |
|
93 |
- def write(dest) |
|
94 |
- begin |
|
95 |
- super(dest) |
|
96 |
- rescue |
|
97 |
- end |
|
98 |
- |
|
99 |
- true |
|
100 |
- end |
|
101 |
- end |
|
102 |
- |
|
103 |
- class SitemapGenerator < Generator |
|
104 |
- |
|
105 |
- # Valid values allowed by sitemap.xml spec for change frequencies |
|
106 |
- VALID_CHANGE_FREQUENCY_VALUES = ["always", "hourly", "daily", "weekly", |
|
107 |
- "monthly", "yearly", "never"] |
|
108 |
- |
|
109 |
- # Goes through pages and posts and generates sitemap.xml file |
|
110 |
- # |
|
111 |
- # Returns nothing |
|
112 |
- def generate(site) |
|
113 |
- sitemap = REXML::Document.new << REXML::XMLDecl.new("1.0", "UTF-8") |
|
114 |
- |
|
115 |
- urlset = REXML::Element.new "urlset" |
|
116 |
- urlset.add_attribute("xmlns", |
|
117 |
- "http://www.sitemaps.org/schemas/sitemap/0.9") |
|
118 |
- |
|
119 |
- @last_modified_post_date = fill_posts(site, urlset) |
|
120 |
- fill_pages(site, urlset) |
|
121 |
- |
|
122 |
- sitemap.add_element(urlset) |
|
123 |
- |
|
124 |
- # File I/O: create sitemap.xml file and write out pretty-printed XML |
|
125 |
- file = File.new(File.join(site.dest, SITEMAP_FILE_NAME), "w") |
|
126 |
- formatter = REXML::Formatters::Pretty.new(4) |
|
127 |
- formatter.compact = true |
|
128 |
- formatter.write(sitemap, file) |
|
129 |
- file.close |
|
130 |
- |
|
131 |
- # Keep the sitemap.xml file from being cleaned by Jekyll |
|
132 |
- site.static_files << Jekyll::SitemapFile.new(site, site.dest, "/", SITEMAP_FILE_NAME) |
|
133 |
- end |
|
134 |
- |
|
135 |
- # Create url elements for all the posts and find the date of the latest one |
|
136 |
- # |
|
137 |
- # Returns last_modified_date of latest post |
|
138 |
- def fill_posts(site, urlset) |
|
139 |
- last_modified_date = nil |
|
140 |
- site.posts.each do |post| |
|
141 |
- if !excluded?(post.name) |
|
142 |
- url = fill_url(site, post) |
|
143 |
- urlset.add_element(url) |
|
144 |
- end |
|
145 |
- |
|
146 |
- path = post.full_path_to_source |
|
147 |
- date = File.mtime(path) |
|
148 |
- last_modified_date = date if last_modified_date == nil or date > last_modified_date |
|
149 |
- end |
|
150 |
- |
|
151 |
- last_modified_date |
|
152 |
- end |
|
153 |
- |
|
154 |
- # Create url elements for all the normal pages and find the date of the |
|
155 |
- # index to use with the pagination pages |
|
156 |
- # |
|
157 |
- # Returns last_modified_date of index page |
|
158 |
- def fill_pages(site, urlset) |
|
159 |
- site.pages.each do |page| |
|
160 |
- if !excluded?(page.name) |
|
161 |
- path = page.full_path_to_source |
|
162 |
- if File.exists?(path) |
|
163 |
- url = fill_url(site, page) |
|
164 |
- urlset.add_element(url) |
|
165 |
- end |
|
166 |
- end |
|
167 |
- end |
|
168 |
- end |
|
169 |
- |
|
170 |
- # Fill data of each URL element: location, last modified, |
|
171 |
- # change frequency (optional), and priority. |
|
172 |
- # |
|
173 |
- # Returns url REXML::Element |
|
174 |
- def fill_url(site, page_or_post) |
|
175 |
- url = REXML::Element.new "url" |
|
176 |
- |
|
177 |
- loc = fill_location(page_or_post) |
|
178 |
- url.add_element(loc) |
|
179 |
- |
|
180 |
- lastmod = fill_last_modified(site, page_or_post) |
|
181 |
- url.add_element(lastmod) if lastmod |
|
182 |
- |
|
183 |
- if (page_or_post.data[CHANGE_FREQUENCY_CUSTOM_VARIABLE_NAME]) |
|
184 |
- change_frequency = |
|
185 |
- page_or_post.data[CHANGE_FREQUENCY_CUSTOM_VARIABLE_NAME].downcase |
|
186 |
- |
|
187 |
- if (valid_change_frequency?(change_frequency)) |
|
188 |
- changefreq = REXML::Element.new "changefreq" |
|
189 |
- changefreq.text = change_frequency |
|
190 |
- url.add_element(changefreq) |
|
191 |
- else |
|
192 |
- puts "ERROR: Invalid Change Frequency In #{page_or_post.name}" |
|
193 |
- end |
|
194 |
- end |
|
195 |
- |
|
196 |
- if (page_or_post.data[PRIORITY_CUSTOM_VARIABLE_NAME]) |
|
197 |
- priority_value = page_or_post.data[PRIORITY_CUSTOM_VARIABLE_NAME] |
|
198 |
- if valid_priority?(priority_value) |
|
199 |
- priority = REXML::Element.new "priority" |
|
200 |
- priority.text = page_or_post.data[PRIORITY_CUSTOM_VARIABLE_NAME] |
|
201 |
- url.add_element(priority) |
|
202 |
- else |
|
203 |
- puts "ERROR: Invalid Priority In #{page_or_post.name}" |
|
204 |
- end |
|
205 |
- end |
|
206 |
- |
|
207 |
- url |
|
208 |
- end |
|
209 |
- |
|
210 |
- # Get URL location of page or post |
|
211 |
- # |
|
212 |
- # Returns the location of the page or post |
|
213 |
- def fill_location(page_or_post) |
|
214 |
- loc = REXML::Element.new "loc" |
|
215 |
- loc.text = page_or_post.location_on_server |
|
216 |
- |
|
217 |
- loc |
|
218 |
- end |
|
219 |
- |
|
220 |
- # Fill lastmod XML element with the last modified date for the page or post. |
|
221 |
- # |
|
222 |
- # Returns lastmod REXML::Element or nil |
|
223 |
- def fill_last_modified(site, page_or_post) |
|
224 |
- path = page_or_post.full_path_to_source |
|
225 |
- |
|
226 |
- lastmod = REXML::Element.new "lastmod" |
|
227 |
- date = File.mtime(path) |
|
228 |
- latest_date = find_latest_date(date, site, page_or_post) |
|
229 |
- |
|
230 |
- if @last_modified_post_date == nil |
|
231 |
- # This is a post |
|
232 |
- lastmod.text = latest_date.iso8601 |
|
233 |
- else |
|
234 |
- # This is a page |
|
235 |
- if posts_included?(page_or_post.name) |
|
236 |
- # We want to take into account the last post date |
|
237 |
- final_date = greater_date(latest_date, @last_modified_post_date) |
|
238 |
- lastmod.text = final_date.iso8601 |
|
239 |
- else |
|
240 |
- lastmod.text = latest_date.iso8601 |
|
241 |
- end |
|
242 |
- end |
|
243 |
- lastmod |
|
244 |
- end |
|
245 |
- |
|
246 |
- # Go through the page/post and any implemented layouts and get the latest |
|
247 |
- # modified date |
|
248 |
- # |
|
249 |
- # Returns formatted output of latest date of page/post and any used layouts |
|
250 |
- def find_latest_date(latest_date, site, page_or_post) |
|
251 |
- layouts = site.layouts |
|
252 |
- layout = layouts[page_or_post.data["layout"]] |
|
253 |
- while layout |
|
254 |
- path = layout.full_path_to_source |
|
255 |
- date = File.mtime(path) |
|
256 |
- |
|
257 |
- latest_date = date if (date > latest_date) |
|
258 |
- |
|
259 |
- layout = layouts[layout.data["layout"]] |
|
260 |
- end |
|
261 |
- |
|
262 |
- latest_date |
|
263 |
- end |
|
264 |
- |
|
265 |
- # Which of the two dates is later |
|
266 |
- # |
|
267 |
- # Returns latest of two dates |
|
268 |
- def greater_date(date1, date2) |
|
269 |
- if (date1 >= date2) |
|
270 |
- date1 |
|
271 |
- else |
|
272 |
- date2 |
|
273 |
- end |
|
274 |
- end |
|
275 |
- |
|
276 |
- # Is the page or post listed as something we want to exclude? |
|
277 |
- # |
|
278 |
- # Returns boolean |
|
279 |
- def excluded?(name) |
|
280 |
- EXCLUDED_FILES.include? name |
|
281 |
- end |
|
282 |
- |
|
283 |
- def posts_included?(name) |
|
284 |
- PAGES_INCLUDE_POSTS.include? name |
|
285 |
- end |
|
286 |
- |
|
287 |
- # Is the change frequency value provided valid according to the spec |
|
288 |
- # |
|
289 |
- # Returns boolean |
|
290 |
- def valid_change_frequency?(change_frequency) |
|
291 |
- VALID_CHANGE_FREQUENCY_VALUES.include? change_frequency |
|
292 |
- end |
|
293 |
- |
|
294 |
- # Is the priority value provided valid according to the spec |
|
295 |
- # |
|
296 |
- # Returns boolean |
|
297 |
- def valid_priority?(priority) |
|
298 |
- begin |
|
299 |
- priority_val = Float(priority) |
|
300 |
- return true if priority_val >= 0.0 and priority_val <= 1.0 |
|
301 |
- rescue ArgumentError |
|
302 |
- end |
|
303 |
- |
|
304 |
- false |
|
305 |
- end |
|
306 |
- end |
|
307 |
-end |
|
308 |
- |
309 | 1 |
deleted file mode 100644 |
... | ... |
@@ -1,36 +0,0 @@ |
1 |
-class String |
|
2 |
- def titlecase |
|
3 |
- small_words = %w(a an and as at but by en for if in of on or the to v v. via vs vs.) |
|
4 |
- |
|
5 |
- x = split(" ").map do |word| |
|
6 |
- # note: word could contain non-word characters! |
|
7 |
- # downcase all small_words, capitalize the rest |
|
8 |
- small_words.include?(word.gsub(/\W/, "").downcase) ? word.downcase! : word.smart_capitalize! |
|
9 |
- word |
|
10 |
- end |
|
11 |
- # capitalize first and last words |
|
12 |
- x.first.to_s.smart_capitalize! |
|
13 |
- x.last.to_s.smart_capitalize! |
|
14 |
- # small words after colons are capitalized |
|
15 |
- x.join(" ").gsub(/:\s?(\W*#{small_words.join("|")}\W*)\s/) { ": #{$1.smart_capitalize} " } |
|
16 |
- end |
|
17 |
- |
|
18 |
- def titlecase! |
|
19 |
- replace(titlecase) |
|
20 |
- end |
|
21 |
- |
|
22 |
- def smart_capitalize |
|
23 |
- # ignore any leading crazy characters and capitalize the first real character |
|
24 |
- if self =~ /^['"\(\[']*([a-z])/ |
|
25 |
- i = index($1) |
|
26 |
- x = self[i,self.length] |
|
27 |
- # word with capitals and periods mid-word are left alone |
|
28 |
- self[i,1] = self[i,1].upcase unless x =~ /[A-Z]/ or x =~ /\.\w+/ |
|
29 |
- end |
|
30 |
- self |
|
31 |
- end |
|
32 |
- |
|
33 |
- def smart_capitalize! |
|
34 |
- replace(smart_capitalize) |
|
35 |
- end |
|
36 |
-end |
37 | 1 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,73 @@ |
0 |
+# |
|
1 |
+# Author: Brandon Mathis |
|
2 |
+# Based on the work of: Josediaz Gonzalez - https://github.com/josegonzalez/josediazgonzalez.com/blob/master/_plugins/blockquote.rb |
|
3 |
+# |
|
4 |
+# Outputs a string with a given attribution as a quote |
|
5 |
+# |
|
6 |
+# {% blockquote Bobby Willis http://google.com/blah the search for bobby's mom %} |
|
7 |
+# Wheeee! |
|
8 |
+# {% endblockquote %} |
|
9 |
+# ... |
|
10 |
+# <blockquote> |
|
11 |
+# <p>Wheeee!</p> |
|
12 |
+# <footer> |
|
13 |
+# <strong>Bobby Willis</strong><cite><a href="http://google.com/blah">The Search For Bobby's Mom</a> |
|
14 |
+# </blockquote> |
|
15 |
+# |
|
16 |
+require './_plugins/titlecase.rb' |
|
17 |
+module Jekyll |
|
18 |
+ |
|
19 |
+ class Blockquote < Liquid::Block |
|
20 |
+ FullCiteWithTitle = /([\w\s]+)(https?:\/\/)(\S+\s)([\w\s]+)/i |
|
21 |
+ FullCite = /([\w\s]+)(https?:\/\/)(\S+)/i |
|
22 |
+ Author = /([\w\s]+)/ |
|
23 |
+ |
|
24 |
+ def initialize(tag_name, markup, tokens) |
|
25 |
+ @by = nil |
|
26 |
+ @source = nil |
|
27 |
+ @title = nil |
|
28 |
+ if markup =~ FullCiteWithTitle |
|
29 |
+ @by = $1 |
|
30 |
+ @source = $2 + $3 |
|
31 |
+ @title = $4.titlecase |
|
32 |
+ elsif markup =~ FullCite |
|
33 |
+ @by = $1 |
|
34 |
+ @source = $2 + $3 |
|
35 |
+ elsif markup =~ Author |
|
36 |
+ @by = $1 |
|
37 |
+ end |
|
38 |
+ super |
|
39 |
+ end |
|
40 |
+ |
|
41 |
+ def render(context) |
|
42 |
+ output = paragraphize(super.map(&:strip).join) |
|
43 |
+ author = "<strong>#{@by.strip}</strong>" |
|
44 |
+ if @source |
|
45 |
+ url = @source.match(/https?:\/\/(.+)/)[1].split('/') |
|
46 |
+ parts = [] |
|
47 |
+ url.each do |part| |
|
48 |
+ if (parts + [part]).join('/').length < 32 |
|
49 |
+ parts << part |
|
50 |
+ end |
|
51 |
+ end |
|
52 |
+ source = parts.join('/') |
|
53 |
+ source << '/…' unless source == @source |
|
54 |
+ end |
|
55 |
+ cite = "<cite><a href='#{@source}'>#{(@title || source)}</a></cite>" |
|
56 |
+ reply = if @by.nil? |
|
57 |
+ output |
|
58 |
+ elsif !@source.nil? |
|
59 |
+ "#{output}<footer>#{author + cite}</footer>" |
|
60 |
+ else |
|
61 |
+ "#{output}<footer>#{author}</footer>" |
|
62 |
+ end |
|
63 |
+ "<blockquote>#{reply}</blockquote>" |
|
64 |
+ end |
|
65 |
+ |
|
66 |
+ def paragraphize(input) |
|
67 |
+ "<p>#{input.gsub(/\n\n/, '</p><p>').gsub(/\n/, '<br/>')}</p>" |
|
68 |
+ end |
|
69 |
+ end |
|
70 |
+end |
|
71 |
+ |
|
72 |
+Liquid::Template.register_tag('blockquote', Jekyll::Blockquote) |
0 | 73 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,161 @@ |
0 |
+# Jekyll category page generator. |
|
1 |
+# http://recursive-design.com/projects/jekyll-plugins/ |
|
2 |
+# |
|
3 |
+# Version: 0.1.4 (201101061053) |
|
4 |
+# |
|
5 |
+# Copyright (c) 2010 Dave Perrett, http://recursive-design.com/ |
|
6 |
+# Licensed under the MIT license (http://www.opensource.org/licenses/mit-license.php) |
|
7 |
+# |
|
8 |
+# A generator that creates category pages for jekyll sites. |
|
9 |
+# |
|
10 |
+# To use it, simply drop this script into the _plugins directory of your Jekyll site. You should |
|
11 |
+# also create a file called 'category_index.html' in the _layouts directory of your jekyll site |
|
12 |
+# with the following contents (note: you should remove the leading '# ' characters): |
|
13 |
+# |
|
14 |
+# ================================== COPY BELOW THIS LINE ================================== |
|
15 |
+# --- |
|
16 |
+# layout: default |
|
17 |
+# --- |
|
18 |
+# |
|
19 |
+# <h1 class="category">{{ page.title }}</h1> |
|
20 |
+# <ul class="posts"> |
|
21 |
+# {% for post in site.categories[page.category] %} |
|
22 |
+# <div>{{ post.date | date_to_html_string }}</div> |
|
23 |
+# <h2><a href="{{ post.url }}">{{ post.title }}</a></h2> |
|
24 |
+# <div class="categories">Filed under {{ post.categories | category_links }}</div> |
|
25 |
+# {% endfor %} |
|
26 |
+# </ul> |
|
27 |
+# ================================== COPY ABOVE THIS LINE ================================== |
|
28 |
+# |
|
29 |
+# You can alter the _layout_ setting if you wish to use an alternate layout, and obviously you |
|
30 |
+# can change the HTML above as you see fit. |
|
31 |
+# |
|
32 |
+# When you compile your jekyll site, this plugin will loop through the list of categories in your |
|
33 |
+# site, and use the layout above to generate a page for each one with a list of links to the |
|
34 |
+# individual posts. |
|
35 |
+# |
|
36 |
+# Included filters : |
|
37 |
+# - category_links: Outputs the list of categories as comma-separated <a> links. |
|
38 |
+# - date_to_html_string: Outputs the post.date as formatted html, with hooks for CSS styling. |
|
39 |
+# |
|
40 |
+# Available _config.yml settings : |
|
41 |
+# - category_dir: The subfolder to build category pages in (default is 'categories'). |
|
42 |
+# - category_title_prefix: The string used before the category name in the page title (default is |
|
43 |
+# 'Category: '). |
|
44 |
+module Jekyll |
|
45 |
+ |
|
46 |
+ |
|
47 |
+ # The CategoryIndex class creates a single category page for the specified category. |
|
48 |
+ class CategoryIndex < Page |
|
49 |
+ |
|
50 |
+ # Initializes a new CategoryIndex. |
|
51 |
+ # |
|
52 |
+ # +base+ is the String path to the <source>. |
|
53 |
+ # +category_dir+ is the String path between <source> and the category folder. |
|
54 |
+ # +category+ is the category currently being processed. |
|
55 |
+ def initialize(site, base, category_dir, category) |
|
56 |
+ @site = site |
|
57 |
+ @base = base |
|
58 |
+ @dir = category_dir |
|
59 |
+ @name = 'index.html' |
|
60 |
+ self.process(@name) |
|
61 |
+ # Read the YAML data from the layout page. |
|
62 |
+ self.read_yaml(File.join(base, '_layouts'), 'category_index.html') |
|
63 |
+ self.data['category'] = category |
|
64 |
+ # Set the title for this page. |
|
65 |
+ title_prefix = site.config['category_title_prefix'] || 'Category: ' |
|
66 |
+ self.data['title'] = "#{title_prefix}#{category}" |
|
67 |
+ # Set the meta-description for this page. |
|
68 |
+ meta_description_prefix = site.config['category_meta_description_prefix'] || 'Category: ' |
|
69 |
+ self.data['description'] = "#{meta_description_prefix}#{category}" |
|
70 |
+ end |
|
71 |
+ |
|
72 |
+ end |
|
73 |
+ |
|
74 |
+ |
|
75 |
+ # The Site class is a built-in Jekyll class with access to global site config information. |
|
76 |
+ class Site |
|
77 |
+ |
|
78 |
+ # Creates an instance of CategoryIndex for each category page, renders it, and |
|
79 |
+ # writes the output to a file. |
|
80 |
+ # |
|
81 |
+ # +category_dir+ is the String path to the category folder. |
|
82 |
+ # +category+ is the category currently being processed. |
|
83 |
+ def write_category_index(category_dir, category) |
|
84 |
+ index = CategoryIndex.new(self, self.source, category_dir, category) |
|
85 |
+ index.render(self.layouts, site_payload) |
|
86 |
+ index.write(self.dest) |
|
87 |
+ # Record the fact that this page has been added, otherwise Site::cleanup will remove it. |
|
88 |
+ self.pages << index |
|
89 |
+ end |
|
90 |
+ |
|
91 |
+ # Loops through the list of category pages and processes each one. |
|
92 |
+ def write_category_indexes |
|
93 |
+ if self.layouts.key? 'category_index' |
|
94 |
+ dir = self.config['category_dir'] || 'categories' |
|
95 |
+ self.categories.keys.each do |category| |
|
96 |
+ self.write_category_index(File.join(dir, category.gsub(/_|\W/, '-')), category) |
|
97 |
+ end |
|
98 |
+ |
|
99 |
+ # Throw an exception if the layout couldn't be found. |
|
100 |
+ else |
|
101 |
+ throw "No 'category_index' layout found." |
|
102 |
+ end |
|
103 |
+ end |
|
104 |
+ |
|
105 |
+ end |
|
106 |
+ |
|
107 |
+ |
|
108 |
+ # Jekyll hook - the generate method is called by jekyll, and generates all of the category pages. |
|
109 |
+ class GenerateCategories < Generator |
|
110 |
+ safe true |
|
111 |
+ priority :low |
|
112 |
+ |
|
113 |
+ def generate(site) |
|
114 |
+ site.write_category_indexes |
|
115 |
+ end |
|
116 |
+ |
|
117 |
+ end |
|
118 |
+ |
|
119 |
+ |
|
120 |
+ # Adds some extra filters used during the category creation process. |
|
121 |
+ module Filters |
|
122 |
+ |
|
123 |
+ # Outputs a list of categories as comma-separated <a> links. This is used |
|
124 |
+ # to output the category list for each post on a category page. |
|
125 |
+ # |
|
126 |
+ # +categories+ is the list of categories to format. |
|
127 |
+ # |
|
128 |
+ # Returns string |
|
129 |
+ # |
|
130 |
+ def category_links(categories) |
|
131 |
+ categories = categories.sort!.map do |item| |
|
132 |
+ "<a class='category' href='/#{@context.registers[:site].config['category_dir']}/#{item.gsub(/_|\W/, '-')}'/>#{item}</a>" |
|
133 |
+ end |
|
134 |
+ |
|
135 |
+ case categories.length |
|
136 |
+ when 0 |
|
137 |
+ "" |
|
138 |
+ when 1 |
|
139 |
+ categories[0].to_s |
|
140 |
+ else |
|
141 |
+ "#{categories[0...-1].join(', ')}, #{categories[-1]}" |
|
142 |
+ end |
|
143 |
+ end |
|
144 |
+ |
|
145 |
+ # Outputs the post.date as formatted html, with hooks for CSS styling. |
|
146 |
+ # |
|
147 |
+ # +date+ is the date object to format as HTML. |
|
148 |
+ # |
|
149 |
+ # Returns string |
|
150 |
+ def date_to_html_string(date) |
|
151 |
+ result = '<span class="month">' + date.strftime('%b').upcase + '</span> ' |
|
152 |
+ result += date.strftime('<span class="day">%d</span> ') |
|
153 |
+ result += date.strftime('<span class="year">%Y</span> ') |
|
154 |
+ result |
|
155 |
+ end |
|
156 |
+ |
|
157 |
+ end |
|
158 |
+ |
|
159 |
+end |
|
160 |
+ |
0 | 1 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,75 @@ |
0 |
+#custom filters for Octopress |
|
1 |
+ |
|
2 |
+module OctopressFilters |
|
3 |
+ # Used on the blog index to split posts on the <!--more--> marker |
|
4 |
+ def exerpt(input) |
|
5 |
+ if input.index(/<!--\s*more\s*-->/i) |
|
6 |
+ input.split(/<!--\s*more\s*-->/i)[0] |
|
7 |
+ else |
|
8 |
+ input |
|
9 |
+ end |
|
10 |
+ end |
|
11 |
+ |
|
12 |
+ # Summary is used on the Archive pages to return the first block of content from a post. |
|
13 |
+ def summary(input) |
|
14 |
+ if input.index(/\n\n/) |
|
15 |
+ input.split(/\n\n/)[0] |
|
16 |
+ else |
|
17 |
+ input |
|
18 |
+ end |
|
19 |
+ end |
|
20 |
+ |
|
21 |
+ # Replaces relative urls with full urls |
|
22 |
+ def full_urls(input, url='') |
|
23 |
+ input.gsub /(\s+(href|src)\s*=\s*["|']{1})(\/[^\"'>]+)/ do |
|
24 |
+ $1+url+$3 |
|
25 |
+ end |
|
26 |
+ end |
|
27 |
+ |
|
28 |
+ # Returns a url without the http:// for use in as a search modifier eg. 'search terms site:website.com' |
|
29 |
+ def search_url(input) |
|
30 |
+ input.gsub /(https?:\/\/)(\S+)/ do |
|
31 |
+ $2 |
|
32 |
+ end |
|
33 |
+ end |
|
34 |
+ |
|
35 |
+ # replaces primes with smartquotes using RubyPants |
|
36 |
+ def smart_quotes(input) |
|
37 |
+ require 'rubypants' |
|
38 |
+ RubyPants.new(input).to_html |
|
39 |
+ end |
|
40 |
+ |
|
41 |
+ # Returns a title cased string based on John Gruber's title case http://daringfireball.net/2008/08/title_case_update |
|
42 |
+ def titlecase(input) |
|
43 |
+ input.titlecase |
|
44 |
+ end |
|
45 |
+ |
|
46 |
+ # Returns a datetime if the input is a string |
|
47 |
+ def datetime(date) |
|
48 |
+ if date.class == String |
|
49 |
+ date = Time.parse(date) |
|
50 |
+ end |
|
51 |
+ date |
|
52 |
+ end |
|
53 |
+ |
|
54 |
+ # Returns an ordidinal date eg July 22 2007 -> July 22nd 2007 |
|
55 |
+ def ordinalize(date) |
|
56 |
+ date = datetime(date) |
|
57 |
+ "#{date.strftime('%b')} #{ordinal(date.strftime('%e').to_i)}, #{date.strftime('%Y')}" |
|
58 |
+ end |
|
59 |
+ |
|
60 |
+ # Returns an ordinal number. 13 -> 13th, 21 -> 21st etc. |
|
61 |
+ def ordinal(number) |
|
62 |
+ if (11..13).include?(number.to_i % 100) |
|
63 |
+ "#{number}<span>th</span>" |
|
64 |
+ else |
|
65 |
+ case number.to_i % 10 |
|
66 |
+ when 1; "#{number}<span>st</span>" |
|
67 |
+ when 2; "#{number}<span>nd</span>" |
|
68 |
+ when 3; "#{number}<span>rd</span>" |
|
69 |
+ else "#{number}<span>th</span>" |
|
70 |
+ end |
|
71 |
+ end |
|
72 |
+ end |
|
73 |
+end |
|
74 |
+Liquid::Template.register_filter OctopressFilters |
0 | 75 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,94 @@ |
0 |
+# A Liquid tag for Jekyll sites that allows embedding Gists and showing code for non-JavaScript enabled browsers and readers. |
|
1 |
+# by: Brandon Tilly |
|
2 |
+# Source URL: https://gist.github.com/1027674 |
|
3 |
+# Post http://brandontilley.com/2011/01/31/gist-tag-for-jekyll.html |
|
4 |
+# |
|
5 |
+# Example usage: {% gist 1027674 gist_tag.rb %} //embeds a gist for this plugin |
|
6 |
+ |
|
7 |
+require 'cgi' |
|
8 |
+require 'digest/md5' |
|
9 |
+require 'net/https' |
|
10 |
+require 'uri' |
|
11 |
+ |
|
12 |
+module Jekyll |
|
13 |
+ class GistTag < Liquid::Tag |
|
14 |
+ def initialize(tag_name, text, token) |
|
15 |
+ super |
|
16 |
+ @text = text |
|
17 |
+ @cache_disabled = false |
|
18 |
+ @cache_folder = File.expand_path "../_gist_cache", File.dirname(__FILE__) |
|
19 |
+ FileUtils.mkdir_p @cache_folder |
|
20 |
+ end |
|
21 |
+ |
|
22 |
+ def render(context) |
|
23 |
+ if parts = @text.match(/([\d]*) (.*)/) |
|
24 |
+ gist, file = parts[1].strip, parts[2].strip |
|
25 |
+ script_url = script_url_for gist, file |
|
26 |
+ code = get_cached_gist(gist, file) || get_gist_from_web(gist, file) |
|
27 |
+ html_output_for script_url, code |
|
28 |
+ else |
|
29 |
+ "" |
|
30 |
+ end |
|
31 |
+ end |
|
32 |
+ |
|
33 |
+ def html_output_for(script_url, code) |
|
34 |
+ code = CGI.escapeHTML code |
|
35 |
+ <<-HTML |
|
36 |
+<script src='#{script_url}'></script> |
|
37 |
+<noscript><pre><code>#{code}</code></pre></noscript> |
|
38 |
+ HTML |
|
39 |
+ end |
|
40 |
+ |
|
41 |
+ def script_url_for(gist_id, filename) |
|
42 |
+ "https://gist.github.com/#{gist_id}.js?file=#{filename}" |
|
43 |
+ end |
|
44 |
+ |
|
45 |
+ def get_gist_url_for(gist, file) |
|
46 |
+ "https://raw.github.com/gist/#{gist}/#{file}" |
|
47 |
+ end |
|
48 |
+ |
|
49 |
+ def cache(gist, file, data) |
|
50 |
+ cache_file = get_cache_file_for gist, file |
|
51 |
+ File.open(cache_file, "w") do |io| |
|
52 |
+ io.write data |
|
53 |
+ end |
|
54 |
+ end |
|
55 |
+ |
|
56 |
+ def get_cached_gist(gist, file) |
|
57 |
+ return nil if @cache_disabled |
|
58 |
+ cache_file = get_cache_file_for gist, file |
|
59 |
+ File.read cache_file if File.exist? cache_file |
|
60 |
+ end |
|
61 |
+ |
|
62 |
+ def get_cache_file_for(gist, file) |
|
63 |
+ bad_chars = /[^a-zA-Z0-9\-_.]/ |
|
64 |
+ gist = gist.gsub bad_chars, '' |
|
65 |
+ file = file.gsub bad_chars, '' |
|
66 |
+ md5 = Digest::MD5.hexdigest "#{gist}-#{file}" |
|
67 |
+ File.join @cache_folder, "#{gist}-#{file}-#{md5}.cache" |
|
68 |
+ end |
|
69 |
+ |
|
70 |
+ def get_gist_from_web(gist, file) |
|
71 |
+ gist_url = get_gist_url_for gist, file |
|
72 |
+ raw_uri = URI.parse gist_url |
|
73 |
+ https = Net::HTTP.new raw_uri.host, raw_uri.port |
|
74 |
+ https.use_ssl = true |
|
75 |
+ https.verify_mode = OpenSSL::SSL::VERIFY_NONE |
|
76 |
+ request = Net::HTTP::Get.new raw_uri.request_uri |
|
77 |
+ data = https.request request |
|
78 |
+ data = data.body |
|
79 |
+ cache gist, file, data unless @cache_disabled |
|
80 |
+ data |
|
81 |
+ end |
|
82 |
+ end |
|
83 |
+ |
|
84 |
+ class GistTagNoCache < GistTag |
|
85 |
+ def initialize(tag_name, text, token) |
|
86 |
+ super |
|
87 |
+ @cache_disabled = true |
|
88 |
+ end |
|
89 |
+ end |
|
90 |
+end |
|
91 |
+ |
|
92 |
+Liquid::Template.register_tag('gist', Jekyll::GistTag) |
|
93 |
+Liquid::Template.register_tag('gistnocache', Jekyll::GistTagNoCache) |
0 | 94 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,24 @@ |
0 |
+module Jekyll |
|
1 |
+ require 'haml' |
|
2 |
+ class HamlConverter < Converter |
|
3 |
+ safe true |
|
4 |
+ priority :low |
|
5 |
+ |
|
6 |
+ def matches(ext) |
|
7 |
+ ext =~ /haml/i |
|
8 |
+ end |
|
9 |
+ |
|
10 |
+ def output_ext(ext) |
|
11 |
+ ".html" |
|
12 |
+ end |
|
13 |
+ |
|
14 |
+ def convert(content) |
|
15 |
+ begin |
|
16 |
+ engine = Haml::Engine.new(content) |
|
17 |
+ engine.render |
|
18 |
+ rescue StandardError => e |
|
19 |
+ puts "!!! HAML Error: " + e.message |
|
20 |
+ end |
|
21 |
+ end |
|
22 |
+ end |
|
23 |
+end |
0 | 24 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,40 @@ |
0 |
+require 'pathname' |
|
1 |
+ |
|
2 |
+module Jekyll |
|
3 |
+ |
|
4 |
+ class IncludeCodeTag < Liquid::Tag |
|
5 |
+ def initialize(tag_name, file, tokens) |
|
6 |
+ super |
|
7 |
+ @file = file.strip |
|
8 |
+ end |
|
9 |
+ |
|
10 |
+ def render(context) |
|
11 |
+ code_dir = (context.registers[:site].config['code_dir'] || 'downloads/code') |
|
12 |
+ code_path = (Pathname.new(context.registers[:site].source) + code_dir).expand_path |
|
13 |
+ file = code_path + @file |
|
14 |
+ |
|
15 |
+ if File.symlink?(code_path) |
|
16 |
+ return "Code directory '#{code_path}' cannot be a symlink" |
|
17 |
+ end |
|
18 |
+ |
|
19 |
+ unless file.file? |
|
20 |
+ return "File #{file} could not be found" |
|
21 |
+ end |
|
22 |
+ |
|
23 |
+ Dir.chdir(code_path) do |
|
24 |
+ code = file.read |
|
25 |
+ file_type = file.extname |
|
26 |
+ url = "#{context.registers[:site].config['url']}/#{code_dir}/#{@file}" |
|
27 |
+ source = "<figure><figcaption><span>#{file.basename}</span><a href='#{url}'>download</a></figcaption>\n" |
|
28 |
+ source += "{% highlight #{file_type} %}\n" + code + "\n{% endhighlight %}</figure>" |
|
29 |
+ partial = Liquid::Template.parse(source) |
|
30 |
+ context.stack do |
|
31 |
+ partial.render(context) |
|
32 |
+ end |
|
33 |
+ end |
|
34 |
+ end |
|
35 |
+ end |
|
36 |
+ |
|
37 |
+end |
|
38 |
+ |
|
39 |
+Liquid::Template.register_tag('include_code', Jekyll::IncludeCodeTag) |
0 | 40 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,31 @@ |
0 |
+require 'pathname' |
|
1 |
+ |
|
2 |
+module Jekyll |
|
3 |
+ |
|
4 |
+ class IncludePartialTag < Liquid::Tag |
|
5 |
+ def initialize(tag_name, file, tokens) |
|
6 |
+ super |
|
7 |
+ @file = file.strip |
|
8 |
+ end |
|
9 |
+ |
|
10 |
+ def render(context) |
|
11 |
+ file_dir = (context.registers[:site].source || 'source') |
|
12 |
+ file_path = Pathname.new(file_dir).expand_path |
|
13 |
+ file = file_path + @file |
|
14 |
+ |
|
15 |
+ unless file.file? |
|
16 |
+ return "File #{file} could not be found" |
|
17 |
+ end |
|
18 |
+ |
|
19 |
+ Dir.chdir(file_path) do |
|
20 |
+ partial = Liquid::Template.parse(file.read) |
|
21 |
+ context.stack do |
|
22 |
+ partial.render(context) |
|
23 |
+ end |
|
24 |
+ end |
|
25 |
+ end |
|
26 |
+ end |
|
27 |
+end |
|
28 |
+ |
|
29 |
+Liquid::Template.register_tag('include_partial', Jekyll::IncludePartialTag) |
|
30 |
+ |
0 | 31 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,42 @@ |
0 |
+# |
|
1 |
+# Author: Brandon Mathis |
|
2 |
+# Based on the sematic pullquote technique by Maykel Loomans at http://miekd.com/articles/pull-quotes-with-html5-and-css/ |
|
3 |
+# |
|
4 |
+# Outputs a span with a data-pullquote attribute set from the marked pullquote. Example: |
|
5 |
+# |
|
6 |
+# {% pullquote %} |
|
7 |
+# When writing longform posts, I find it helpful to include pullquotes, which help those scanning a post discern whether or not a post is helpful. |
|
8 |
+# It is important to note, {" pullquotes are merely visual in presentation and should not appear twice in the text. "} That is why it is prefered |
|
9 |
+# to use a CSS only technique for styling pullquotes. |
|
10 |
+# {% endpullquote %} |
|
11 |
+# ...will output... |
|
12 |
+# <p> |
|
13 |
+# <span data-pullquote="pullquotes are merely visual in presentation and should not appear twice in the text."> |
|
14 |
+# When writing longform posts, I find it helpful to include pullquotes, which help those scanning a post discern whether or not a post is helpful. |
|
15 |
+# It is important to note, pullquotes are merely visual in presentation and should not appear twice in the text. This is why a CSS only approach # for styling pullquotes is prefered. |
|
16 |
+# </span> |
|
17 |
+# </p> |
|
18 |
+# |
|
19 |
+ |
|
20 |
+module Jekyll |
|
21 |
+ |
|
22 |
+ class PullquoteTag < Liquid::Block |
|
23 |
+ PullQuoteMarkup = /\{(.+)\}/i |
|
24 |
+ |
|
25 |
+ def initialize(tag_name, markup, tokens) |
|
26 |
+ super |
|
27 |
+ end |
|
28 |
+ |
|
29 |
+ def render(context) |
|
30 |
+ output = super |
|
31 |
+ if output.join =~ /\{"\s*(.+)\s*"\}/ |
|
32 |
+ @quote = $1 |
|
33 |
+ "<span class='has-pullquote' data-pullquote='#{@quote}'>#{output.join.gsub(/\{"\s*|\s*"\}/, '')}</span>" |
|
34 |
+ else |
|
35 |
+ return "Surround your pullquote like this {! text to be quoted !}" |
|
36 |
+ end |
|
37 |
+ end |
|
38 |
+ end |
|
39 |
+end |
|
40 |
+ |
|
41 |
+Liquid::Template.register_tag('pullquote', Jekyll::PullquoteTag) |
0 | 42 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,30 @@ |
0 |
+# |
|
1 |
+# Author: Raimonds Simanovskis, http://blog.rayapps.com/ |
|
2 |
+# Source URL: https://github.com/rsim/blog.rayapps.com/blob/master/_plugins/pygments_cache_patch.rb |
|
3 |
+# |
|
4 |
+ |
|
5 |
+require 'fileutils' |
|
6 |
+require 'digest/md5' |
|
7 |
+ |
|
8 |
+PYGMENTS_CACHE_DIR = File.expand_path('../../_code_cache', __FILE__) |
|
9 |
+FileUtils.mkdir_p(PYGMENTS_CACHE_DIR) |
|
10 |
+ |
|
11 |
+Jekyll::HighlightBlock.class_eval do |
|
12 |
+ def render_pygments(context, code) |
|
13 |
+ if defined?(PYGMENTS_CACHE_DIR) |
|
14 |
+ path = File.join(PYGMENTS_CACHE_DIR, "#{@lang}-#{Digest::MD5.hexdigest(code)}.html") |
|
15 |
+ if File.exist?(path) |
|
16 |
+ highlighted_code = File.read(path) |
|
17 |
+ else |
|
18 |
+ highlighted_code = Albino.new(code, @lang).to_s(@options) |
|
19 |
+ File.open(path, 'w') {|f| f.print(highlighted_code) } |
|
20 |
+ end |
|
21 |
+ else |
|
22 |
+ highlighted_code = Albino.new(code, @lang).to_s(@options) |
|
23 |
+ end |
|
24 |
+ output = add_code_tags(highlighted_code, @lang) |
|
25 |
+ output = context["pygments_prefix"] + output if context["pygments_prefix"] |
|
26 |
+ output = output + context["pygments_suffix"] if context["pygments_suffix"] |
|
27 |
+ output |
|
28 |
+ end |
|
29 |
+end |
0 | 30 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,308 @@ |
0 |
+# Sitemap.xml Generator is a Jekyll plugin that generates a sitemap.xml file by |
|
1 |
+# traversing all of the available posts and pages. |
|
2 |
+# |
|
3 |
+# How To Use: |
|
4 |
+# 1) Copy source file into your _plugins folder within your Jekyll project. |
|
5 |
+# 2) Change modify the url variable in _config.yml to reflect your domain name. |
|
6 |
+# 3) Run Jekyll: jekyll --server to re-generate your site. |
|
7 |
+# |
|
8 |
+# Variables: |
|
9 |
+# * Change SITEMAP_FILE_NAME if you want your sitemap to be called something |
|
10 |
+# other than sitemap.xml. |
|
11 |
+# * Change the PAGES_INCLUDE_POSTS list to include any pages that are looping |
|
12 |
+# through your posts (e.g. "index.html", "archive.html", etc.). This will |
|
13 |
+# ensure that right after you make a new post, the last modified date will |
|
14 |
+# be updated to reflect the new post. |
|
15 |
+# * A sitemap.xml should be included in your _site folder. |
|
16 |
+# * If there are any files you don't want included in the sitemap, add them |
|
17 |
+# to the EXCLUDED_FILES list. The name should match the name of the source |
|
18 |
+# file. |
|
19 |
+# * If you want to include the optional changefreq and priority attributes, |
|
20 |
+# simply include custom variables in the YAML Front Matter of that file. |
|
21 |
+# The names of these custom variables are defined below in the |
|
22 |
+# CHANGE_FREQUENCY_CUSTOM_VARIABLE_NAME and PRIORITY_CUSTOM_VARIABLE_NAME |
|
23 |
+# constants. |
|
24 |
+# |
|
25 |
+# Notes: |
|
26 |
+# * The last modified date is determined by the latest from the following: |
|
27 |
+# system modified date of the page or post, system modified date of |
|
28 |
+# included layout, system modified date of included layout within that |
|
29 |
+# layout, ... |
|
30 |
+# |
|
31 |
+# Author: Michael Levin |
|
32 |
+# Site: http://www.kinnetica.com |
|
33 |
+# Distributed Under A Creative Commons License |
|
34 |
+# - http://creativecommons.org/licenses/by/3.0/ |
|
35 |
+# |
|
36 |
+# Modified for Octopress by John W. Long |
|
37 |
+# |
|
38 |
+require 'rexml/document' |
|
39 |
+ |
|
40 |
+module Jekyll |
|
41 |
+ |
|
42 |
+ # Change SITEMAP_FILE_NAME if you would like your sitemap file |
|
43 |
+ # to be called something else |
|
44 |
+ SITEMAP_FILE_NAME = "sitemap.xml" |
|
45 |
+ |
|
46 |
+ # Any files to exclude from being included in the sitemap.xml |
|
47 |
+ EXCLUDED_FILES = ["atom.xml"] |
|
48 |
+ |
|
49 |
+ # Any files that include posts, so that when a new post is added, the last |
|
50 |
+ # modified date of these pages should take that into account |
|
51 |
+ PAGES_INCLUDE_POSTS = ["index.html"] |
|
52 |
+ |
|
53 |
+ # Custom variable names for changefreq and priority elements |
|
54 |
+ # These names are used within the YAML Front Matter of pages or posts |
|
55 |
+ # for which you want to include these properties |
|
56 |
+ CHANGE_FREQUENCY_CUSTOM_VARIABLE_NAME = "change_frequency" |
|
57 |
+ PRIORITY_CUSTOM_VARIABLE_NAME = "priority" |
|
58 |
+ |
|
59 |
+ class Post |
|
60 |
+ attr_accessor :name |
|
61 |
+ |
|
62 |
+ def full_path_to_source |
|
63 |
+ File.join(@base, @name) |
|
64 |
+ end |
|
65 |
+ |
|
66 |
+ def location_on_server |
|
67 |
+ "#{site.config['url']}#{url}" |
|
68 |
+ end |
|
69 |
+ end |
|
70 |
+ |
|
71 |
+ class Page |
|
72 |
+ attr_accessor :name |
|
73 |
+ |
|
74 |
+ def full_path_to_source |
|
75 |
+ File.join(@base, @dir, @name) |
|
76 |
+ end |
|
77 |
+ |
|
78 |
+ def location_on_server |
|
79 |
+ location = "#{site.config['url']}#{@dir}#{url}" |
|
80 |
+ location.gsub(/index.html$/, "") |
|
81 |
+ end |
|
82 |
+ end |
|
83 |
+ |
|
84 |
+ class Layout |
|
85 |
+ def full_path_to_source |
|
86 |
+ File.join(@base, @name) |
|
87 |
+ end |
|
88 |
+ end |
|
89 |
+ |
|
90 |
+ # Recover from strange exception when starting server without --auto |
|
91 |
+ class SitemapFile < StaticFile |
|
92 |
+ def write(dest) |
|
93 |
+ begin |
|
94 |
+ super(dest) |
|
95 |
+ rescue |
|
96 |
+ end |
|
97 |
+ |
|
98 |
+ true |
|
99 |
+ end |
|
100 |
+ end |
|
101 |
+ |
|
102 |
+ class SitemapGenerator < Generator |
|
103 |
+ |
|
104 |
+ # Valid values allowed by sitemap.xml spec for change frequencies |
|
105 |
+ VALID_CHANGE_FREQUENCY_VALUES = ["always", "hourly", "daily", "weekly", |
|
106 |
+ "monthly", "yearly", "never"] |
|
107 |
+ |
|
108 |
+ # Goes through pages and posts and generates sitemap.xml file |
|
109 |
+ # |
|
110 |
+ # Returns nothing |
|
111 |
+ def generate(site) |
|
112 |
+ sitemap = REXML::Document.new << REXML::XMLDecl.new("1.0", "UTF-8") |
|
113 |
+ |
|
114 |
+ urlset = REXML::Element.new "urlset" |
|
115 |
+ urlset.add_attribute("xmlns", |
|
116 |
+ "http://www.sitemaps.org/schemas/sitemap/0.9") |
|
117 |
+ |
|
118 |
+ @last_modified_post_date = fill_posts(site, urlset) |
|
119 |
+ fill_pages(site, urlset) |
|
120 |
+ |
|
121 |
+ sitemap.add_element(urlset) |
|
122 |
+ |
|
123 |
+ # File I/O: create sitemap.xml file and write out pretty-printed XML |
|
124 |
+ file = File.new(File.join(site.dest, SITEMAP_FILE_NAME), "w") |
|
125 |
+ formatter = REXML::Formatters::Pretty.new(4) |
|
126 |
+ formatter.compact = true |
|
127 |
+ formatter.write(sitemap, file) |
|
128 |
+ file.close |
|
129 |
+ |
|
130 |
+ # Keep the sitemap.xml file from being cleaned by Jekyll |
|
131 |
+ site.static_files << Jekyll::SitemapFile.new(site, site.dest, "/", SITEMAP_FILE_NAME) |
|
132 |
+ end |
|
133 |
+ |
|
134 |
+ # Create url elements for all the posts and find the date of the latest one |
|
135 |
+ # |
|
136 |
+ # Returns last_modified_date of latest post |
|
137 |
+ def fill_posts(site, urlset) |
|
138 |
+ last_modified_date = nil |
|
139 |
+ site.posts.each do |post| |
|
140 |
+ if !excluded?(post.name) |
|
141 |
+ url = fill_url(site, post) |
|
142 |
+ urlset.add_element(url) |
|
143 |
+ end |
|
144 |
+ |
|
145 |
+ path = post.full_path_to_source |
|
146 |
+ date = File.mtime(path) |
|
147 |
+ last_modified_date = date if last_modified_date == nil or date > last_modified_date |
|
148 |
+ end |
|
149 |
+ |
|
150 |
+ last_modified_date |
|
151 |
+ end |
|
152 |
+ |
|
153 |
+ # Create url elements for all the normal pages and find the date of the |
|
154 |
+ # index to use with the pagination pages |
|
155 |
+ # |
|
156 |
+ # Returns last_modified_date of index page |
|
157 |
+ def fill_pages(site, urlset) |
|
158 |
+ site.pages.each do |page| |
|
159 |
+ if !excluded?(page.name) |
|
160 |
+ path = page.full_path_to_source |
|
161 |
+ if File.exists?(path) |
|
162 |
+ url = fill_url(site, page) |
|
163 |
+ urlset.add_element(url) |
|
164 |
+ end |
|
165 |
+ end |
|
166 |
+ end |
|
167 |
+ end |
|
168 |
+ |
|
169 |
+ # Fill data of each URL element: location, last modified, |
|
170 |
+ # change frequency (optional), and priority. |
|
171 |
+ # |
|
172 |
+ # Returns url REXML::Element |
|
173 |
+ def fill_url(site, page_or_post) |
|
174 |
+ url = REXML::Element.new "url" |
|
175 |
+ |
|
176 |
+ loc = fill_location(page_or_post) |
|
177 |
+ url.add_element(loc) |
|
178 |
+ |
|
179 |
+ lastmod = fill_last_modified(site, page_or_post) |
|
180 |
+ url.add_element(lastmod) if lastmod |
|
181 |
+ |
|
182 |
+ if (page_or_post.data[CHANGE_FREQUENCY_CUSTOM_VARIABLE_NAME]) |
|
183 |
+ change_frequency = |
|
184 |
+ page_or_post.data[CHANGE_FREQUENCY_CUSTOM_VARIABLE_NAME].downcase |
|
185 |
+ |
|
186 |
+ if (valid_change_frequency?(change_frequency)) |
|
187 |
+ changefreq = REXML::Element.new "changefreq" |
|
188 |
+ changefreq.text = change_frequency |
|
189 |
+ url.add_element(changefreq) |
|
190 |
+ else |
|
191 |
+ puts "ERROR: Invalid Change Frequency In #{page_or_post.name}" |
|
192 |
+ end |
|
193 |
+ end |
|
194 |
+ |
|
195 |
+ if (page_or_post.data[PRIORITY_CUSTOM_VARIABLE_NAME]) |
|
196 |
+ priority_value = page_or_post.data[PRIORITY_CUSTOM_VARIABLE_NAME] |
|
197 |
+ if valid_priority?(priority_value) |
|
198 |
+ priority = REXML::Element.new "priority" |
|
199 |
+ priority.text = page_or_post.data[PRIORITY_CUSTOM_VARIABLE_NAME] |
|
200 |
+ url.add_element(priority) |
|
201 |
+ else |
|
202 |
+ puts "ERROR: Invalid Priority In #{page_or_post.name}" |
|
203 |
+ end |
|
204 |
+ end |
|
205 |
+ |
|
206 |
+ url |
|
207 |
+ end |
|
208 |
+ |
|
209 |
+ # Get URL location of page or post |
|
210 |
+ # |
|
211 |
+ # Returns the location of the page or post |
|
212 |
+ def fill_location(page_or_post) |
|
213 |
+ loc = REXML::Element.new "loc" |
|
214 |
+ loc.text = page_or_post.location_on_server |
|
215 |
+ |
|
216 |
+ loc |
|
217 |
+ end |
|
218 |
+ |
|
219 |
+ # Fill lastmod XML element with the last modified date for the page or post. |
|
220 |
+ # |
|
221 |
+ # Returns lastmod REXML::Element or nil |
|
222 |
+ def fill_last_modified(site, page_or_post) |
|
223 |
+ path = page_or_post.full_path_to_source |
|
224 |
+ |
|
225 |
+ lastmod = REXML::Element.new "lastmod" |
|
226 |
+ date = File.mtime(path) |
|
227 |
+ latest_date = find_latest_date(date, site, page_or_post) |
|
228 |
+ |
|
229 |
+ if @last_modified_post_date == nil |
|
230 |
+ # This is a post |
|
231 |
+ lastmod.text = latest_date.iso8601 |
|
232 |
+ else |
|
233 |
+ # This is a page |
|
234 |
+ if posts_included?(page_or_post.name) |
|
235 |
+ # We want to take into account the last post date |
|
236 |
+ final_date = greater_date(latest_date, @last_modified_post_date) |
|
237 |
+ lastmod.text = final_date.iso8601 |
|
238 |
+ else |
|
239 |
+ lastmod.text = latest_date.iso8601 |
|
240 |
+ end |
|
241 |
+ end |
|
242 |
+ lastmod |
|
243 |
+ end |
|
244 |
+ |
|
245 |
+ # Go through the page/post and any implemented layouts and get the latest |
|
246 |
+ # modified date |
|
247 |
+ # |
|
248 |
+ # Returns formatted output of latest date of page/post and any used layouts |
|
249 |
+ def find_latest_date(latest_date, site, page_or_post) |
|
250 |
+ layouts = site.layouts |
|
251 |
+ layout = layouts[page_or_post.data["layout"]] |
|
252 |
+ while layout |
|
253 |
+ path = layout.full_path_to_source |
|
254 |
+ date = File.mtime(path) |
|
255 |
+ |
|
256 |
+ latest_date = date if (date > latest_date) |
|
257 |
+ |
|
258 |
+ layout = layouts[layout.data["layout"]] |
|
259 |
+ end |
|
260 |
+ |
|
261 |
+ latest_date |
|
262 |
+ end |
|
263 |
+ |
|
264 |
+ # Which of the two dates is later |
|
265 |
+ # |
|
266 |
+ # Returns latest of two dates |
|
267 |
+ def greater_date(date1, date2) |
|
268 |
+ if (date1 >= date2) |
|
269 |
+ date1 |
|
270 |
+ else |
|
271 |
+ date2 |
|
272 |
+ end |
|
273 |
+ end |
|
274 |
+ |
|
275 |
+ # Is the page or post listed as something we want to exclude? |
|
276 |
+ # |
|
277 |
+ # Returns boolean |
|
278 |
+ def excluded?(name) |
|
279 |
+ EXCLUDED_FILES.include? name |
|
280 |
+ end |
|
281 |
+ |
|
282 |
+ def posts_included?(name) |
|
283 |
+ PAGES_INCLUDE_POSTS.include? name |
|
284 |
+ end |
|
285 |
+ |
|
286 |
+ # Is the change frequency value provided valid according to the spec |
|
287 |
+ # |
|
288 |
+ # Returns boolean |
|
289 |
+ def valid_change_frequency?(change_frequency) |
|
290 |
+ VALID_CHANGE_FREQUENCY_VALUES.include? change_frequency |
|
291 |
+ end |
|
292 |
+ |
|
293 |
+ # Is the priority value provided valid according to the spec |
|
294 |
+ # |
|
295 |
+ # Returns boolean |
|
296 |
+ def valid_priority?(priority) |
|
297 |
+ begin |
|
298 |
+ priority_val = Float(priority) |
|
299 |
+ return true if priority_val >= 0.0 and priority_val <= 1.0 |
|
300 |
+ rescue ArgumentError |
|
301 |
+ end |
|
302 |
+ |
|
303 |
+ false |
|
304 |
+ end |
|
305 |
+ end |
|
306 |
+end |
|
307 |
+ |
0 | 308 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,36 @@ |
0 |
+class String |
|
1 |
+ def titlecase |
|
2 |
+ small_words = %w(a an and as at but by en for if in of on or the to v v. via vs vs.) |
|
3 |
+ |
|
4 |
+ x = split(" ").map do |word| |
|
5 |
+ # note: word could contain non-word characters! |
|
6 |
+ # downcase all small_words, capitalize the rest |
|
7 |
+ small_words.include?(word.gsub(/\W/, "").downcase) ? word.downcase! : word.smart_capitalize! |
|
8 |
+ word |
|
9 |
+ end |
|
10 |
+ # capitalize first and last words |
|
11 |
+ x.first.to_s.smart_capitalize! |
|
12 |
+ x.last.to_s.smart_capitalize! |
|
13 |
+ # small words after colons are capitalized |
|
14 |
+ x.join(" ").gsub(/:\s?(\W*#{small_words.join("|")}\W*)\s/) { ": #{$1.smart_capitalize} " } |
|
15 |
+ end |
|
16 |
+ |
|
17 |
+ def titlecase! |
|
18 |
+ replace(titlecase) |
|
19 |
+ end |
|
20 |
+ |
|
21 |
+ def smart_capitalize |
|
22 |
+ # ignore any leading crazy characters and capitalize the first real character |
|
23 |
+ if self =~ /^['"\(\[']*([a-z])/ |
|
24 |
+ i = index($1) |
|
25 |
+ x = self[i,self.length] |
|
26 |
+ # word with capitals and periods mid-word are left alone |
|
27 |
+ self[i,1] = self[i,1].upcase unless x =~ /[A-Z]/ or x =~ /\.\w+/ |
|
28 |
+ end |
|
29 |
+ self |
|
30 |
+ end |
|
31 |
+ |
|
32 |
+ def smart_capitalize! |
|
33 |
+ replace(smart_capitalize) |
|
34 |
+ end |
|
35 |
+end |
... | ... |
@@ -25,10 +25,10 @@ desc "Initial setup for Octopress: copies the default theme into the path of Jek |
25 | 25 |
task :install, :theme do |t, args| |
26 | 26 |
# copy theme into working Jekyll directories |
27 | 27 |
theme = args.theme || 'classic' |
28 |
- puts "## Copying "+theme+" theme into ./#{source_dir} ./sass and ./_plugins " |
|
28 |
+ puts "## Copying "+theme+" theme into ./#{source_dir} ./sass and ./plugins " |
|
29 | 29 |
system "mkdir -p #{source_dir}; cp -R #{themes_dir}/"+theme+"/source/ #{source_dir}/" |
30 | 30 |
system "mkdir -p sass; cp -R #{themes_dir}/"+theme+"/sass/ sass/" |
31 |
- system "mkdir -p _plugins; cp -R #{themes_dir}/"+theme+"/_plugins/ _plugins/" |
|
31 |
+ system "mkdir -p plugins; cp -R #{themes_dir}/"+theme+"/plugins/ plugins/" |
|
32 | 32 |
system "mkdir -p #{source_dir}/#{posts_dir}"; |
33 | 33 |
end |
34 | 34 |
|
... | ... |
@@ -138,6 +138,7 @@ task :config_deploy, :branch do |t, args| |
138 | 138 |
f.write rakefile |
139 | 139 |
end |
140 | 140 |
end |
141 |
+ puts "## Deployment configured. Now you can deploy to the #{args.branch} branch with `rake deploy` ##" |
|
141 | 142 |
end |
142 | 143 |
|
143 | 144 |
def ok_failed(condition) |