| 1 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,74 +0,0 @@ |
| 1 |
-# |
|
| 2 |
-# Author: Brandon Mathis |
|
| 3 |
-# A full rewrite 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/search?q=pants the search for bobby's pants %}
|
|
| 8 |
-# Wheeee! |
|
| 9 |
-# {% endblockquote %}
|
|
| 10 |
-# ... |
|
| 11 |
-# <blockquote> |
|
| 12 |
-# <p>Wheeee!</p> |
|
| 13 |
-# <footer> |
|
| 14 |
-# <strong>Bobby Willis</strong><cite><a href="http://google.com/search?q=pants">The Search For Bobby's Pants</a> |
|
| 15 |
-# </blockquote> |
|
| 16 |
-# |
|
| 17 |
-require './plugins/titlecase.rb' |
|
| 18 |
- |
|
| 19 |
-module Jekyll |
|
| 20 |
- |
|
| 21 |
- class Blockquote < Liquid::Block |
|
| 22 |
- FullCiteWithTitle = /(\S[\S\s]*)\s+(https?:\/\/)(\S+)\s+(.+)/i |
|
| 23 |
- FullCite = /(\S[\S\s]*)\s+(https?:\/\/)(\S+)/i |
|
| 24 |
- Author = /(\S[\S\s]*)/ |
|
| 25 |
- |
|
| 26 |
- def initialize(tag_name, markup, tokens) |
|
| 27 |
- @by = nil |
|
| 28 |
- @source = nil |
|
| 29 |
- @title = nil |
|
| 30 |
- if markup =~ FullCiteWithTitle |
|
| 31 |
- @by = $1 |
|
| 32 |
- @source = $2 + $3 |
|
| 33 |
- @title = $4.titlecase |
|
| 34 |
- elsif markup =~ FullCite |
|
| 35 |
- @by = $1 |
|
| 36 |
- @source = $2 + $3 |
|
| 37 |
- elsif markup =~ Author |
|
| 38 |
- @by = $1 |
|
| 39 |
- end |
|
| 40 |
- super |
|
| 41 |
- end |
|
| 42 |
- |
|
| 43 |
- def render(context) |
|
| 44 |
- output = paragraphize(super.map(&:strip).join) |
|
| 45 |
- author = "<strong>#{@by.strip}</strong>"
|
|
| 46 |
- if @source |
|
| 47 |
- url = @source.match(/https?:\/\/(.+)/)[1].split('/')
|
|
| 48 |
- parts = [] |
|
| 49 |
- url.each do |part| |
|
| 50 |
- if (parts + [part]).join('/').length < 32
|
|
| 51 |
- parts << part |
|
| 52 |
- end |
|
| 53 |
- end |
|
| 54 |
- source = parts.join('/')
|
|
| 55 |
- source << '/…' unless source == @source |
|
| 56 |
- end |
|
| 57 |
- cite = "<cite><a href='#{@source}'>#{(@title || source)}</a></cite>"
|
|
| 58 |
- result = if @by.nil? |
|
| 59 |
- output |
|
| 60 |
- elsif !@source.nil? |
|
| 61 |
- "#{output}<footer>#{author + cite}</footer>"
|
|
| 62 |
- else |
|
| 63 |
- "#{output}<footer>#{author}</footer>"
|
|
| 64 |
- end |
|
| 65 |
- "<blockquote>#{result}</blockquote>"
|
|
| 66 |
- end |
|
| 67 |
- |
|
| 68 |
- def paragraphize(input) |
|
| 69 |
- "<p>#{input.gsub(/\n\n/, '</p><p>').gsub(/\n/, '<br/>')}</p>"
|
|
| 70 |
- end |
|
| 71 |
- end |
|
| 72 |
-end |
|
| 73 |
- |
|
| 74 |
-Liquid::Template.register_tag('blockquote', Jekyll::Blockquote)
|
| 75 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,163 +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 |
- dir = @context.registers[:site].config['category_dir'] |
|
| 133 |
- root_url = @context.registers[:site].config['root'] |
|
| 134 |
- categories = categories.sort!.map do |item| |
|
| 135 |
- "<a class='category' href='#{root_url}/#{dir}/#{item.gsub(/_|\W/, '-')}/'>#{item}</a>"
|
|
| 136 |
- end |
|
| 137 |
- |
|
| 138 |
- case categories.length |
|
| 139 |
- when 0 |
|
| 140 |
- "" |
|
| 141 |
- when 1 |
|
| 142 |
- categories[0].to_s |
|
| 143 |
- else |
|
| 144 |
- "#{categories[0...-1].join(', ')}, #{categories[-1]}"
|
|
| 145 |
- end |
|
| 146 |
- end |
|
| 147 |
- |
|
| 148 |
- # Outputs the post.date as formatted html, with hooks for CSS styling. |
|
| 149 |
- # |
|
| 150 |
- # +date+ is the date object to format as HTML. |
|
| 151 |
- # |
|
| 152 |
- # Returns string |
|
| 153 |
- def date_to_html_string(date) |
|
| 154 |
- result = '<span class="month">' + date.strftime('%b').upcase + '</span> '
|
|
| 155 |
- result += date.strftime('<span class="day">%d</span> ')
|
|
| 156 |
- result += date.strftime('<span class="year">%Y</span> ')
|
|
| 157 |
- result |
|
| 158 |
- end |
|
| 159 |
- |
|
| 160 |
- end |
|
| 161 |
- |
|
| 162 |
-end |
|
| 163 |
- |
| 164 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,80 +0,0 @@ |
| 1 |
-# Title: Simple Code Blocks for Jekyll |
|
| 2 |
-# Author: Brandon Mathis http://brandonmathis.com |
|
| 3 |
-# Description: Write codeblocks with semantic HTML5 <figure> and <figcaption> elements and optional syntax highlighting — all with a simple, intuitive interface. |
|
| 4 |
-# |
|
| 5 |
-# Syntax: {% codeblock [title] [url] [link text] %}
|
|
| 6 |
-# |
|
| 7 |
-# For syntax highlighting, put a file extension somewhere in the title. examples: |
|
| 8 |
-# {% codeblock file.sh %}
|
|
| 9 |
-# {% codeblock Time to be Awesome! (awesome.rb) %}
|
|
| 10 |
-# |
|
| 11 |
-# Example: |
|
| 12 |
-# |
|
| 13 |
-# {% codeblock Got pain? painreleif.sh http://site.com/painreleief.sh Download it! %}
|
|
| 14 |
-# $ rm -rf ~/PAIN |
|
| 15 |
-# {% endcodeblock %}
|
|
| 16 |
-# |
|
| 17 |
-# Output: |
|
| 18 |
-# |
|
| 19 |
-# <figure role=code> |
|
| 20 |
-# <figcaption><span>Got pain? painrelief.sh</span> <a href="http://site.com/painrelief.sh">Download it!</a> |
|
| 21 |
-# <div class="highlight"><pre><code class="sh"> |
|
| 22 |
-# -- nicely escaped highlighted code -- |
|
| 23 |
-# </code></pre></div> |
|
| 24 |
-# </figure> |
|
| 25 |
-# |
|
| 26 |
-# Example 2 (no syntax highlighting): |
|
| 27 |
-# |
|
| 28 |
-# {% codeblock %}
|
|
| 29 |
-# <sarcasm>Ooooh, sarcasm... How original!</sarcasm> |
|
| 30 |
-# {% endcodeblock %}
|
|
| 31 |
-# |
|
| 32 |
-# <figure role=code> |
|
| 33 |
-# <pre><code><sarcasm> Ooooh, sarcasm... How original!</sarcasm></code></pre> |
|
| 34 |
-# </figure> |
|
| 35 |
-# |
|
| 36 |
-module Jekyll |
|
| 37 |
- |
|
| 38 |
- class CodeBlock < Liquid::Block |
|
| 39 |
- CaptionUrlTitle = /(\S[\S\s]*)\s+(https?:\/\/)(\S+)\s+(.+)/i |
|
| 40 |
- CaptionUrl = /(\S[\S\s]*)\s+(https?:\/\/)(\S+)/i |
|
| 41 |
- Caption = /(\S[\S\s]*)/ |
|
| 42 |
- def initialize(tag_name, markup, tokens) |
|
| 43 |
- @title = nil |
|
| 44 |
- @caption = nil |
|
| 45 |
- @highlight = true |
|
| 46 |
- if markup =~ CaptionUrlTitle |
|
| 47 |
- @file = $1 |
|
| 48 |
- @caption = "<figcaption><span>#{$1}</span><a href='#{$2 + $3}'>#{$4}</a</figcaption>"
|
|
| 49 |
- elsif markup =~ CaptionUrl |
|
| 50 |
- @file = $1 |
|
| 51 |
- @caption = "<figcaption><span>#{$1}</span><a href='#{$2 + $3}'>link</a</figcaption>"
|
|
| 52 |
- elsif markup =~ Caption |
|
| 53 |
- @file = $1 |
|
| 54 |
- @caption = "<figcaption><span>#{$1}</span></figcaption>\n"
|
|
| 55 |
- end |
|
| 56 |
- if @file =~ /\S[\S\s]*\.(\w+)/ |
|
| 57 |
- @filetype = $1 |
|
| 58 |
- end |
|
| 59 |
- super |
|
| 60 |
- end |
|
| 61 |
- |
|
| 62 |
- def render(context) |
|
| 63 |
- output = super |
|
| 64 |
- code = super.join |
|
| 65 |
- source = "<figure role=code>\n" |
|
| 66 |
- source += @caption if @caption |
|
| 67 |
- if @filetype |
|
| 68 |
- source += "{% highlight #{@filetype} %}\n" + code + "\n{% endhighlight %}\n</figure>"
|
|
| 69 |
- else |
|
| 70 |
- source += "<pre><code>" + code.gsub!(/</,'<') + "</code></pre>\n</figure>" |
|
| 71 |
- end |
|
| 72 |
- partial = Liquid::Template.parse(source) |
|
| 73 |
- context.stack do |
|
| 74 |
- partial.render(context) |
|
| 75 |
- end |
|
| 76 |
- end |
|
| 77 |
- end |
|
| 78 |
-end |
|
| 79 |
- |
|
| 80 |
-Liquid::Template.register_tag('codeblock', Jekyll::CodeBlock)
|
| 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,69 +0,0 @@ |
| 1 |
-# Title: Simple Image Figure tag for Jekyll |
|
| 2 |
-# Author: Brandon Mathis http://brandonmathis.com |
|
| 3 |
-# Description: Easily output images in <figure> with an optional <figcaption> and class names. |
|
| 4 |
-# |
|
| 5 |
-# Syntax {% figure [class name(s)] url [caption text] %}
|
|
| 6 |
-# |
|
| 7 |
-# Example: |
|
| 8 |
-# {% figure left half http://site.com/images/ninja.png Ninja Attack! %}
|
|
| 9 |
-# |
|
| 10 |
-# Output: |
|
| 11 |
-# <figure class='left half'><img src="http://site.com/images/ninja.png"><figcaption>Ninja Attack!</figcaption></figure> |
|
| 12 |
-# |
|
| 13 |
-# Example 2 (image with caption) |
|
| 14 |
-# {% figure /images/ninja.png Ninja Attack! %}
|
|
| 15 |
-# |
|
| 16 |
-# Output: |
|
| 17 |
-# <figure><img src="/images/ninja.png"><figcaption>Ninja Attack!</figcaption></figure> |
|
| 18 |
-# |
|
| 19 |
-# Example 3 (just an image with classes) |
|
| 20 |
-# {% figure right /images/ninja.png %}
|
|
| 21 |
-# |
|
| 22 |
-# Output: |
|
| 23 |
-# <figure><img class="right" src="/images/ninja.png"></figure> |
|
| 24 |
-# |
|
| 25 |
- |
|
| 26 |
-module Jekyll |
|
| 27 |
- |
|
| 28 |
- class FigureImageTag < Liquid::Tag |
|
| 29 |
- ClassImgCaption = /(\S[\S\s]*)\s+(https?:\/\/|\/)(\S+)\s+(.+)/i |
|
| 30 |
- ClassImg = /(\S[\S\s]*)\s+(https?:\/\/|\/)(\S+)/i |
|
| 31 |
- ImgCaption = /^\s*(https?:\/\/|\/)(\S+)\s+(.+)/i |
|
| 32 |
- Img = /^\s*(https?:\/\/|\/)(\S+\s)/i |
|
| 33 |
- |
|
| 34 |
- @img = nil |
|
| 35 |
- @caption = nil |
|
| 36 |
- @class = '' |
|
| 37 |
- |
|
| 38 |
- def initialize(tag_name, markup, tokens) |
|
| 39 |
- if markup =~ ClassImgCaption |
|
| 40 |
- @class = $1 |
|
| 41 |
- @img = $2 + $3 |
|
| 42 |
- @caption = $4 |
|
| 43 |
- elsif markup =~ ClassImg |
|
| 44 |
- @class = $1 |
|
| 45 |
- @img = $2 + $3 |
|
| 46 |
- elsif markup =~ ImgCaption |
|
| 47 |
- @img = $1 + $2 |
|
| 48 |
- @caption = $3 |
|
| 49 |
- elsif markup =~ Img |
|
| 50 |
- @img = $1 + $2 |
|
| 51 |
- end |
|
| 52 |
- super |
|
| 53 |
- end |
|
| 54 |
- |
|
| 55 |
- def render(context) |
|
| 56 |
- output = super |
|
| 57 |
- if @img |
|
| 58 |
- figure = "<figure class='#{@class}'>"
|
|
| 59 |
- figure += "<img src='#{@img}'>"
|
|
| 60 |
- figure += "<figcaption>#{@caption}</figcaption>" if @caption
|
|
| 61 |
- figure += "</figure>" |
|
| 62 |
- else |
|
| 63 |
- "Error processing input, expected syntax: {% figure [class name(s)] /url/to/image [caption] %}"
|
|
| 64 |
- end |
|
| 65 |
- end |
|
| 66 |
- end |
|
| 67 |
-end |
|
| 68 |
- |
|
| 69 |
-Liquid::Template.register_tag('figure', Jekyll::FigureImageTag)
|
| 70 | 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,55 +0,0 @@ |
| 1 |
-# Title: Include Code Tag for Jekyll |
|
| 2 |
-# Author: Brandon Mathis http://brandonmathis.com |
|
| 3 |
-# Description: Import files on your filesystem into any blog post as embedded code snippets with syntax highlighting and a download link. |
|
| 4 |
-# Configuration: You can set default import path in _config.yml (defaults to code_dir: downloads/code) |
|
| 5 |
-# |
|
| 6 |
-# Syntax {% include_code path/to/file %}
|
|
| 7 |
-# |
|
| 8 |
-# Example: |
|
| 9 |
-# {% include_code javascripts/test.js %}
|
|
| 10 |
-# |
|
| 11 |
-# This will import test.js from source/downloads/code/javascripts/test.js |
|
| 12 |
-# and output the contents in a syntax highlighted code block inside a figure, |
|
| 13 |
-# with a figcaption listing the file name and download link |
|
| 14 |
-# |
|
| 15 |
- |
|
| 16 |
-require 'pathname' |
|
| 17 |
- |
|
| 18 |
-module Jekyll |
|
| 19 |
- |
|
| 20 |
- class IncludeCodeTag < Liquid::Tag |
|
| 21 |
- def initialize(tag_name, file, tokens) |
|
| 22 |
- super |
|
| 23 |
- @file = file.strip |
|
| 24 |
- end |
|
| 25 |
- |
|
| 26 |
- def render(context) |
|
| 27 |
- code_dir = (context.registers[:site].config['code_dir'] || 'downloads/code') |
|
| 28 |
- code_path = (Pathname.new(context.registers[:site].source) + code_dir).expand_path |
|
| 29 |
- file = code_path + @file |
|
| 30 |
- |
|
| 31 |
- if File.symlink?(code_path) |
|
| 32 |
- return "Code directory '#{code_path}' cannot be a symlink"
|
|
| 33 |
- end |
|
| 34 |
- |
|
| 35 |
- unless file.file? |
|
| 36 |
- return "File #{file} could not be found"
|
|
| 37 |
- end |
|
| 38 |
- |
|
| 39 |
- Dir.chdir(code_path) do |
|
| 40 |
- code = file.read |
|
| 41 |
- file_type = file.extname |
|
| 42 |
- url = "#{context.registers[:site].config['url']}/#{code_dir}/#{@file}"
|
|
| 43 |
- source = "<figure role=code><figcaption><span>#{file.basename}</span> <a href='#{url}'>download</a></figcaption>\n"
|
|
| 44 |
- source += "{% highlight #{file_type} %}\n" + code + "\n{% endhighlight %}</figure>"
|
|
| 45 |
- partial = Liquid::Template.parse(source) |
|
| 46 |
- context.stack do |
|
| 47 |
- partial.render(context) |
|
| 48 |
- end |
|
| 49 |
- end |
|
| 50 |
- end |
|
| 51 |
- end |
|
| 52 |
- |
|
| 53 |
-end |
|
| 54 |
- |
|
| 55 |
-Liquid::Template.register_tag('include_code', Jekyll::IncludeCodeTag)
|
| 56 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,40 +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 |
- def initialize(tag_name, markup, tokens) |
|
| 25 |
- super |
|
| 26 |
- end |
|
| 27 |
- |
|
| 28 |
- def render(context) |
|
| 29 |
- output = super |
|
| 30 |
- if output.join =~ /\{"\s*(.+)\s*"\}/
|
|
| 31 |
- @quote = $1 |
|
| 32 |
- "<span class='has-pullquote' data-pullquote='#{@quote}'>#{output.join.gsub(/\{"\s*|\s*"\}/, '')}</span>"
|
|
| 33 |
- else |
|
| 34 |
- return "Surround your pullquote like this {! text to be quoted !}"
|
|
| 35 |
- end |
|
| 36 |
- end |
|
| 37 |
- end |
|
| 38 |
-end |
|
| 39 |
- |
|
| 40 |
-Liquid::Template.register_tag('pullquote', Jekyll::PullquoteTag)
|
| 41 | 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,52 +0,0 @@ |
| 1 |
-# Title: Render Partial Tag for Jekyll |
|
| 2 |
-# Author: Brandon Mathis http://brandonmathis.com |
|
| 3 |
-# Description: Import files on your filesystem into any blog post and render them inline. |
|
| 4 |
-# Note: Paths are relative to the source directory |
|
| 5 |
-# |
|
| 6 |
-# Syntax {% render_partial path/to/file %}
|
|
| 7 |
-# |
|
| 8 |
-# Example 1: |
|
| 9 |
-# {% render_partial about/_bio.markdown %}
|
|
| 10 |
-# |
|
| 11 |
-# This will import source/about/_bio.markdown and render it inline. |
|
| 12 |
-# In this example I used an underscore at the beginning of the filename to prevent Jekyll |
|
| 13 |
-# from generating an about/bio.html (Jekyll doesn't convert files beginning with underscores) |
|
| 14 |
-# |
|
| 15 |
-# Example 2: |
|
| 16 |
-# {% render_partial ../README.markdown %}
|
|
| 17 |
-# |
|
| 18 |
-# You can use relative pathnames, to include files outside of the source directory. |
|
| 19 |
-# This might be useful if you want to have a page for a project's README without having |
|
| 20 |
-# to duplicated the contents |
|
| 21 |
-# |
|
| 22 |
- |
|
| 23 |
-require 'pathname' |
|
| 24 |
- |
|
| 25 |
-module Jekyll |
|
| 26 |
- |
|
| 27 |
- class RenderPartialTag < Liquid::Tag |
|
| 28 |
- def initialize(tag_name, file, tokens) |
|
| 29 |
- super |
|
| 30 |
- @file = file.strip |
|
| 31 |
- end |
|
| 32 |
- |
|
| 33 |
- def render(context) |
|
| 34 |
- file_dir = (context.registers[:site].source || 'source') |
|
| 35 |
- file_path = Pathname.new(file_dir).expand_path |
|
| 36 |
- file = file_path + @file |
|
| 37 |
- |
|
| 38 |
- unless file.file? |
|
| 39 |
- return "File #{file} could not be found"
|
|
| 40 |
- end |
|
| 41 |
- |
|
| 42 |
- Dir.chdir(file_path) do |
|
| 43 |
- partial = Liquid::Template.parse(file.read) |
|
| 44 |
- context.stack do |
|
| 45 |
- partial.render(context) |
|
| 46 |
- end |
|
| 47 |
- end |
|
| 48 |
- end |
|
| 49 |
- end |
|
| 50 |
-end |
|
| 51 |
- |
|
| 52 |
-Liquid::Template.register_tag('render_partial', Jekyll::RenderPartialTag)
|
| 53 | 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 |
| ... | ... |
@@ -21,17 +21,15 @@ themes_dir = ".themes" # directory for blog files |
| 21 | 21 |
post_format = "markdown" # file format for new posts when using the post rake task |
| 22 | 22 |
|
| 23 | 23 |
|
| 24 |
-desc "Initial setup for Octopress: copies the default theme into the path of Jekyll's generator. rake install defaults to rake install[classic] to install a different theme run rake install[some_theme_name]" |
|
| 24 |
+desc "Initial setup for Octopress: copies the default theme into the path of Jekyll's generator. Rake install defaults to rake install[classic] to install a different theme run rake install[some_theme_name]" |
|
| 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} and ./sass"
|
|
| 29 | 29 |
mkdir_p source_dir |
| 30 | 30 |
cp_r "#{themes_dir}/#{theme}/source/.", source_dir
|
| 31 | 31 |
mkdir_p "sass" |
| 32 | 32 |
cp_r "#{themes_dir}/#{theme}/sass/.", "sass"
|
| 33 |
- mkdir_p "plugins" |
|
| 34 |
- cp_r "#{themes_dir}/#{theme}/plugins/.", "plugins"
|
|
| 35 | 33 |
mkdir_p "#{source_dir}/#{posts_dir}"
|
| 36 | 34 |
mkdir_p public_dir |
| 37 | 35 |
end |
| ... | ... |
@@ -67,7 +65,7 @@ task :post, :filename do |t, args| |
| 67 | 67 |
post.puts "title: #{args.filename.gsub(/[-_]/, ' ').titlecase}"
|
| 68 | 68 |
post.puts "date: #{Time.now.strftime('%Y-%m-%d %H:%M')}"
|
| 69 | 69 |
post.puts "layout: post" |
| 70 |
- post.puts "categories: []" |
|
| 70 |
+ post.puts "categories: " |
|
| 71 | 71 |
post.puts "---" |
| 72 | 72 |
end |
| 73 | 73 |
end |
| 74 | 74 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,74 @@ |
| 0 |
+# |
|
| 1 |
+# Author: Brandon Mathis |
|
| 2 |
+# A full rewrite 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/search?q=pants the search for bobby's pants %}
|
|
| 7 |
+# Wheeee! |
|
| 8 |
+# {% endblockquote %}
|
|
| 9 |
+# ... |
|
| 10 |
+# <blockquote> |
|
| 11 |
+# <p>Wheeee!</p> |
|
| 12 |
+# <footer> |
|
| 13 |
+# <strong>Bobby Willis</strong><cite><a href="http://google.com/search?q=pants">The Search For Bobby's Pants</a> |
|
| 14 |
+# </blockquote> |
|
| 15 |
+# |
|
| 16 |
+require './plugins/titlecase.rb' |
|
| 17 |
+ |
|
| 18 |
+module Jekyll |
|
| 19 |
+ |
|
| 20 |
+ class Blockquote < Liquid::Block |
|
| 21 |
+ FullCiteWithTitle = /(\S[\S\s]*)\s+(https?:\/\/)(\S+)\s+(.+)/i |
|
| 22 |
+ FullCite = /(\S[\S\s]*)\s+(https?:\/\/)(\S+)/i |
|
| 23 |
+ Author = /(\S[\S\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 |
+ result = 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>#{result}</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)
|
| 0 | 74 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,163 @@ |
| 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 |
+ dir = @context.registers[:site].config['category_dir'] |
|
| 132 |
+ root_url = @context.registers[:site].config['root'] |
|
| 133 |
+ categories = categories.sort!.map do |item| |
|
| 134 |
+ "<a class='category' href='#{root_url}/#{dir}/#{item.gsub(/_|\W/, '-')}/'>#{item}</a>"
|
|
| 135 |
+ end |
|
| 136 |
+ |
|
| 137 |
+ case categories.length |
|
| 138 |
+ when 0 |
|
| 139 |
+ "" |
|
| 140 |
+ when 1 |
|
| 141 |
+ categories[0].to_s |
|
| 142 |
+ else |
|
| 143 |
+ "#{categories[0...-1].join(', ')}, #{categories[-1]}"
|
|
| 144 |
+ end |
|
| 145 |
+ end |
|
| 146 |
+ |
|
| 147 |
+ # Outputs the post.date as formatted html, with hooks for CSS styling. |
|
| 148 |
+ # |
|
| 149 |
+ # +date+ is the date object to format as HTML. |
|
| 150 |
+ # |
|
| 151 |
+ # Returns string |
|
| 152 |
+ def date_to_html_string(date) |
|
| 153 |
+ result = '<span class="month">' + date.strftime('%b').upcase + '</span> '
|
|
| 154 |
+ result += date.strftime('<span class="day">%d</span> ')
|
|
| 155 |
+ result += date.strftime('<span class="year">%Y</span> ')
|
|
| 156 |
+ result |
|
| 157 |
+ end |
|
| 158 |
+ |
|
| 159 |
+ end |
|
| 160 |
+ |
|
| 161 |
+end |
|
| 162 |
+ |
| 0 | 163 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,80 @@ |
| 0 |
+# Title: Simple Code Blocks for Jekyll |
|
| 1 |
+# Author: Brandon Mathis http://brandonmathis.com |
|
| 2 |
+# Description: Write codeblocks with semantic HTML5 <figure> and <figcaption> elements and optional syntax highlighting — all with a simple, intuitive interface. |
|
| 3 |
+# |
|
| 4 |
+# Syntax: {% codeblock [title] [url] [link text] %}
|
|
| 5 |
+# |
|
| 6 |
+# For syntax highlighting, put a file extension somewhere in the title. examples: |
|
| 7 |
+# {% codeblock file.sh %}
|
|
| 8 |
+# {% codeblock Time to be Awesome! (awesome.rb) %}
|
|
| 9 |
+# |
|
| 10 |
+# Example: |
|
| 11 |
+# |
|
| 12 |
+# {% codeblock Got pain? painreleif.sh http://site.com/painreleief.sh Download it! %}
|
|
| 13 |
+# $ rm -rf ~/PAIN |
|
| 14 |
+# {% endcodeblock %}
|
|
| 15 |
+# |
|
| 16 |
+# Output: |
|
| 17 |
+# |
|
| 18 |
+# <figure role=code> |
|
| 19 |
+# <figcaption><span>Got pain? painrelief.sh</span> <a href="http://site.com/painrelief.sh">Download it!</a> |
|
| 20 |
+# <div class="highlight"><pre><code class="sh"> |
|
| 21 |
+# -- nicely escaped highlighted code -- |
|
| 22 |
+# </code></pre></div> |
|
| 23 |
+# </figure> |
|
| 24 |
+# |
|
| 25 |
+# Example 2 (no syntax highlighting): |
|
| 26 |
+# |
|
| 27 |
+# {% codeblock %}
|
|
| 28 |
+# <sarcasm>Ooooh, sarcasm... How original!</sarcasm> |
|
| 29 |
+# {% endcodeblock %}
|
|
| 30 |
+# |
|
| 31 |
+# <figure role=code> |
|
| 32 |
+# <pre><code><sarcasm> Ooooh, sarcasm... How original!</sarcasm></code></pre> |
|
| 33 |
+# </figure> |
|
| 34 |
+# |
|
| 35 |
+module Jekyll |
|
| 36 |
+ |
|
| 37 |
+ class CodeBlock < Liquid::Block |
|
| 38 |
+ CaptionUrlTitle = /(\S[\S\s]*)\s+(https?:\/\/)(\S+)\s+(.+)/i |
|
| 39 |
+ CaptionUrl = /(\S[\S\s]*)\s+(https?:\/\/)(\S+)/i |
|
| 40 |
+ Caption = /(\S[\S\s]*)/ |
|
| 41 |
+ def initialize(tag_name, markup, tokens) |
|
| 42 |
+ @title = nil |
|
| 43 |
+ @caption = nil |
|
| 44 |
+ @highlight = true |
|
| 45 |
+ if markup =~ CaptionUrlTitle |
|
| 46 |
+ @file = $1 |
|
| 47 |
+ @caption = "<figcaption><span>#{$1}</span><a href='#{$2 + $3}'>#{$4}</a</figcaption>"
|
|
| 48 |
+ elsif markup =~ CaptionUrl |
|
| 49 |
+ @file = $1 |
|
| 50 |
+ @caption = "<figcaption><span>#{$1}</span><a href='#{$2 + $3}'>link</a</figcaption>"
|
|
| 51 |
+ elsif markup =~ Caption |
|
| 52 |
+ @file = $1 |
|
| 53 |
+ @caption = "<figcaption><span>#{$1}</span></figcaption>\n"
|
|
| 54 |
+ end |
|
| 55 |
+ if @file =~ /\S[\S\s]*\.(\w+)/ |
|
| 56 |
+ @filetype = $1 |
|
| 57 |
+ end |
|
| 58 |
+ super |
|
| 59 |
+ end |
|
| 60 |
+ |
|
| 61 |
+ def render(context) |
|
| 62 |
+ output = super |
|
| 63 |
+ code = super.join |
|
| 64 |
+ source = "<figure role=code>\n" |
|
| 65 |
+ source += @caption if @caption |
|
| 66 |
+ if @filetype |
|
| 67 |
+ source += "{% highlight #{@filetype} %}\n" + code + "\n{% endhighlight %}\n</figure>"
|
|
| 68 |
+ else |
|
| 69 |
+ source += "<pre><code>" + code.gsub!(/</,'<') + "</code></pre>\n</figure>" |
|
| 70 |
+ end |
|
| 71 |
+ partial = Liquid::Template.parse(source) |
|
| 72 |
+ context.stack do |
|
| 73 |
+ partial.render(context) |
|
| 74 |
+ end |
|
| 75 |
+ end |
|
| 76 |
+ end |
|
| 77 |
+end |
|
| 78 |
+ |
|
| 79 |
+Liquid::Template.register_tag('codeblock', Jekyll::CodeBlock)
|
| 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,69 @@ |
| 0 |
+# Title: Simple Image Figure tag for Jekyll |
|
| 1 |
+# Author: Brandon Mathis http://brandonmathis.com |
|
| 2 |
+# Description: Easily output images in <figure> with an optional <figcaption> and class names. |
|
| 3 |
+# |
|
| 4 |
+# Syntax {% figure [class name(s)] url [caption text] %}
|
|
| 5 |
+# |
|
| 6 |
+# Example: |
|
| 7 |
+# {% figure left half http://site.com/images/ninja.png Ninja Attack! %}
|
|
| 8 |
+# |
|
| 9 |
+# Output: |
|
| 10 |
+# <figure class='left half'><img src="http://site.com/images/ninja.png"><figcaption>Ninja Attack!</figcaption></figure> |
|
| 11 |
+# |
|
| 12 |
+# Example 2 (image with caption) |
|
| 13 |
+# {% figure /images/ninja.png Ninja Attack! %}
|
|
| 14 |
+# |
|
| 15 |
+# Output: |
|
| 16 |
+# <figure><img src="/images/ninja.png"><figcaption>Ninja Attack!</figcaption></figure> |
|
| 17 |
+# |
|
| 18 |
+# Example 3 (just an image with classes) |
|
| 19 |
+# {% figure right /images/ninja.png %}
|
|
| 20 |
+# |
|
| 21 |
+# Output: |
|
| 22 |
+# <figure><img class="right" src="/images/ninja.png"></figure> |
|
| 23 |
+# |
|
| 24 |
+ |
|
| 25 |
+module Jekyll |
|
| 26 |
+ |
|
| 27 |
+ class FigureImageTag < Liquid::Tag |
|
| 28 |
+ ClassImgCaption = /(\S[\S\s]*)\s+(https?:\/\/|\/)(\S+)\s+(.+)/i |
|
| 29 |
+ ClassImg = /(\S[\S\s]*)\s+(https?:\/\/|\/)(\S+)/i |
|
| 30 |
+ ImgCaption = /^\s*(https?:\/\/|\/)(\S+)\s+(.+)/i |
|
| 31 |
+ Img = /^\s*(https?:\/\/|\/)(\S+\s)/i |
|
| 32 |
+ |
|
| 33 |
+ @img = nil |
|
| 34 |
+ @caption = nil |
|
| 35 |
+ @class = '' |
|
| 36 |
+ |
|
| 37 |
+ def initialize(tag_name, markup, tokens) |
|
| 38 |
+ if markup =~ ClassImgCaption |
|
| 39 |
+ @class = $1 |
|
| 40 |
+ @img = $2 + $3 |
|
| 41 |
+ @caption = $4 |
|
| 42 |
+ elsif markup =~ ClassImg |
|
| 43 |
+ @class = $1 |
|
| 44 |
+ @img = $2 + $3 |
|
| 45 |
+ elsif markup =~ ImgCaption |
|
| 46 |
+ @img = $1 + $2 |
|
| 47 |
+ @caption = $3 |
|
| 48 |
+ elsif markup =~ Img |
|
| 49 |
+ @img = $1 + $2 |
|
| 50 |
+ end |
|
| 51 |
+ super |
|
| 52 |
+ end |
|
| 53 |
+ |
|
| 54 |
+ def render(context) |
|
| 55 |
+ output = super |
|
| 56 |
+ if @img |
|
| 57 |
+ figure = "<figure class='#{@class}'>"
|
|
| 58 |
+ figure += "<img src='#{@img}'>"
|
|
| 59 |
+ figure += "<figcaption>#{@caption}</figcaption>" if @caption
|
|
| 60 |
+ figure += "</figure>" |
|
| 61 |
+ else |
|
| 62 |
+ "Error processing input, expected syntax: {% figure [class name(s)] /url/to/image [caption] %}"
|
|
| 63 |
+ end |
|
| 64 |
+ end |
|
| 65 |
+ end |
|
| 66 |
+end |
|
| 67 |
+ |
|
| 68 |
+Liquid::Template.register_tag('figure', Jekyll::FigureImageTag)
|
| 0 | 69 |
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,55 @@ |
| 0 |
+# Title: Include Code Tag for Jekyll |
|
| 1 |
+# Author: Brandon Mathis http://brandonmathis.com |
|
| 2 |
+# Description: Import files on your filesystem into any blog post as embedded code snippets with syntax highlighting and a download link. |
|
| 3 |
+# Configuration: You can set default import path in _config.yml (defaults to code_dir: downloads/code) |
|
| 4 |
+# |
|
| 5 |
+# Syntax {% include_code path/to/file %}
|
|
| 6 |
+# |
|
| 7 |
+# Example: |
|
| 8 |
+# {% include_code javascripts/test.js %}
|
|
| 9 |
+# |
|
| 10 |
+# This will import test.js from source/downloads/code/javascripts/test.js |
|
| 11 |
+# and output the contents in a syntax highlighted code block inside a figure, |
|
| 12 |
+# with a figcaption listing the file name and download link |
|
| 13 |
+# |
|
| 14 |
+ |
|
| 15 |
+require 'pathname' |
|
| 16 |
+ |
|
| 17 |
+module Jekyll |
|
| 18 |
+ |
|
| 19 |
+ class IncludeCodeTag < Liquid::Tag |
|
| 20 |
+ def initialize(tag_name, file, tokens) |
|
| 21 |
+ super |
|
| 22 |
+ @file = file.strip |
|
| 23 |
+ end |
|
| 24 |
+ |
|
| 25 |
+ def render(context) |
|
| 26 |
+ code_dir = (context.registers[:site].config['code_dir'] || 'downloads/code') |
|
| 27 |
+ code_path = (Pathname.new(context.registers[:site].source) + code_dir).expand_path |
|
| 28 |
+ file = code_path + @file |
|
| 29 |
+ |
|
| 30 |
+ if File.symlink?(code_path) |
|
| 31 |
+ return "Code directory '#{code_path}' cannot be a symlink"
|
|
| 32 |
+ end |
|
| 33 |
+ |
|
| 34 |
+ unless file.file? |
|
| 35 |
+ return "File #{file} could not be found"
|
|
| 36 |
+ end |
|
| 37 |
+ |
|
| 38 |
+ Dir.chdir(code_path) do |
|
| 39 |
+ code = file.read |
|
| 40 |
+ file_type = file.extname |
|
| 41 |
+ url = "#{context.registers[:site].config['url']}/#{code_dir}/#{@file}"
|
|
| 42 |
+ source = "<figure role=code><figcaption><span>#{file.basename}</span> <a href='#{url}'>download</a></figcaption>\n"
|
|
| 43 |
+ source += "{% highlight #{file_type} %}\n" + code + "\n{% endhighlight %}</figure>"
|
|
| 44 |
+ partial = Liquid::Template.parse(source) |
|
| 45 |
+ context.stack do |
|
| 46 |
+ partial.render(context) |
|
| 47 |
+ end |
|
| 48 |
+ end |
|
| 49 |
+ end |
|
| 50 |
+ end |
|
| 51 |
+ |
|
| 52 |
+end |
|
| 53 |
+ |
|
| 54 |
+Liquid::Template.register_tag('include_code', Jekyll::IncludeCodeTag)
|
| 0 | 55 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,40 @@ |
| 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 |
+ def initialize(tag_name, markup, tokens) |
|
| 24 |
+ super |
|
| 25 |
+ end |
|
| 26 |
+ |
|
| 27 |
+ def render(context) |
|
| 28 |
+ output = super |
|
| 29 |
+ if output.join =~ /\{"\s*(.+)\s*"\}/
|
|
| 30 |
+ @quote = $1 |
|
| 31 |
+ "<span class='has-pullquote' data-pullquote='#{@quote}'>#{output.join.gsub(/\{"\s*|\s*"\}/, '')}</span>"
|
|
| 32 |
+ else |
|
| 33 |
+ return "Surround your pullquote like this {! text to be quoted !}"
|
|
| 34 |
+ end |
|
| 35 |
+ end |
|
| 36 |
+ end |
|
| 37 |
+end |
|
| 38 |
+ |
|
| 39 |
+Liquid::Template.register_tag('pullquote', Jekyll::PullquoteTag)
|
| 0 | 40 |
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,52 @@ |
| 0 |
+# Title: Render Partial Tag for Jekyll |
|
| 1 |
+# Author: Brandon Mathis http://brandonmathis.com |
|
| 2 |
+# Description: Import files on your filesystem into any blog post and render them inline. |
|
| 3 |
+# Note: Paths are relative to the source directory |
|
| 4 |
+# |
|
| 5 |
+# Syntax {% render_partial path/to/file %}
|
|
| 6 |
+# |
|
| 7 |
+# Example 1: |
|
| 8 |
+# {% render_partial about/_bio.markdown %}
|
|
| 9 |
+# |
|
| 10 |
+# This will import source/about/_bio.markdown and render it inline. |
|
| 11 |
+# In this example I used an underscore at the beginning of the filename to prevent Jekyll |
|
| 12 |
+# from generating an about/bio.html (Jekyll doesn't convert files beginning with underscores) |
|
| 13 |
+# |
|
| 14 |
+# Example 2: |
|
| 15 |
+# {% render_partial ../README.markdown %}
|
|
| 16 |
+# |
|
| 17 |
+# You can use relative pathnames, to include files outside of the source directory. |
|
| 18 |
+# This might be useful if you want to have a page for a project's README without having |
|
| 19 |
+# to duplicated the contents |
|
| 20 |
+# |
|
| 21 |
+ |
|
| 22 |
+require 'pathname' |
|
| 23 |
+ |
|
| 24 |
+module Jekyll |
|
| 25 |
+ |
|
| 26 |
+ class RenderPartialTag < Liquid::Tag |
|
| 27 |
+ def initialize(tag_name, file, tokens) |
|
| 28 |
+ super |
|
| 29 |
+ @file = file.strip |
|
| 30 |
+ end |
|
| 31 |
+ |
|
| 32 |
+ def render(context) |
|
| 33 |
+ file_dir = (context.registers[:site].source || 'source') |
|
| 34 |
+ file_path = Pathname.new(file_dir).expand_path |
|
| 35 |
+ file = file_path + @file |
|
| 36 |
+ |
|
| 37 |
+ unless file.file? |
|
| 38 |
+ return "File #{file} could not be found"
|
|
| 39 |
+ end |
|
| 40 |
+ |
|
| 41 |
+ Dir.chdir(file_path) do |
|
| 42 |
+ partial = Liquid::Template.parse(file.read) |
|
| 43 |
+ context.stack do |
|
| 44 |
+ partial.render(context) |
|
| 45 |
+ end |
|
| 46 |
+ end |
|
| 47 |
+ end |
|
| 48 |
+ end |
|
| 49 |
+end |
|
| 50 |
+ |
|
| 51 |
+Liquid::Template.register_tag('render_partial', Jekyll::RenderPartialTag)
|
| 0 | 52 |
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 |