aboutsummaryrefslogtreecommitdiff
path: root/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'plugins')
-rw-r--r--plugins/backtick_code_block.rb10
-rw-r--r--plugins/blockquote.rb8
-rw-r--r--plugins/category_generator.rb36
-rw-r--r--plugins/code_block.rb9
-rw-r--r--plugins/compass_compiler.rb1
-rw-r--r--plugins/date.rb98
-rw-r--r--plugins/gist_tag.rb10
-rw-r--r--plugins/image_tag.rb45
-rw-r--r--plugins/include_code.rb2
-rw-r--r--plugins/jsfiddle.rb40
-rw-r--r--plugins/octopress_filters.rb77
-rw-r--r--plugins/preview_unpublished.rb48
-rw-r--r--plugins/pullquote.rb15
-rw-r--r--plugins/pygments_code.rb14
-rw-r--r--plugins/rubypants.rb489
-rw-r--r--plugins/titlecase.rb4
16 files changed, 824 insertions, 82 deletions
diff --git a/plugins/backtick_code_block.rb b/plugins/backtick_code_block.rb
index 7f5076df..40e7900b 100644
--- a/plugins/backtick_code_block.rb
+++ b/plugins/backtick_code_block.rb
@@ -10,7 +10,7 @@ module BacktickCodeBlock
@lang = nil
@url = nil
@title = nil
- input.gsub /^`{3} *([^\n]+)?\n(.+?)\n`{3}/m do
+ input.gsub(/^`{3} *([^\n]+)?\n(.+?)\n`{3}/m) do
@options = $1 || ''
str = $2
@@ -22,12 +22,12 @@ module BacktickCodeBlock
@caption = "<figcaption><span>#{$2}</span></figcaption>"
end
- if str.match(/\A {4}/)
- str = str.gsub /^ {4}/, ''
+ if str.match(/\A( {4}|\t)/)
+ str = str.gsub(/^( {4}|\t)/, '')
end
if @lang.nil? || @lang == 'plain'
code = tableize_code(str.gsub('<','&lt;').gsub('>','&gt;'))
- "<figure role=code>#{@caption}#{code}</figure>"
+ "<figure class='code'>#{@caption}#{code}</figure>"
else
if @lang.include? "-raw"
raw = "``` #{@options.sub('-raw', '')}\n"
@@ -35,7 +35,7 @@ module BacktickCodeBlock
raw += "\n```\n"
else
code = highlight(str, @lang)
- "<figure role=code>#{@caption}#{code}</figure>"
+ "<figure class='code'>#{@caption}#{code}</figure>"
end
end
end
diff --git a/plugins/blockquote.rb b/plugins/blockquote.rb
index a0bf12cc..62e7d143 100644
--- a/plugins/blockquote.rb
+++ b/plugins/blockquote.rb
@@ -46,7 +46,7 @@ module Jekyll
end
def render(context)
- quote = paragraphize(super.map(&:strip).join)
+ quote = paragraphize(super)
author = "<strong>#{@by.strip}</strong>" if @by
if @source
url = @source.match(/https?:\/\/(.+)/)[1].split('/')
@@ -60,9 +60,9 @@ module Jekyll
source << '/&hellip;' unless source == @source
end
if !@source.nil?
- cite = "<cite><a href='#{@source}'>#{(@title || source)}</a></cite>"
+ cite = " <cite><a href='#{@source}'>#{(@title || source)}</a></cite>"
elsif !@title.nil?
- cite = "<cite>#{@title}</cite>"
+ cite = " <cite>#{@title}</cite>"
end
blockquote = if @by.nil?
quote
@@ -75,7 +75,7 @@ module Jekyll
end
def paragraphize(input)
- "<p>#{input.gsub(/\n\n/, '</p><p>').gsub(/\n/, '<br/>')}</p>"
+ "<p>#{input.lstrip.rstrip.gsub(/\n\n/, '</p><p>').gsub(/\n/, '<br/>')}</p>"
end
end
end
diff --git a/plugins/category_generator.rb b/plugins/category_generator.rb
index d9357bc8..bb5fd329 100644
--- a/plugins/category_generator.rb
+++ b/plugins/category_generator.rb
@@ -48,6 +48,35 @@ module Jekyll
end
+ # The CategoryFeed class creates an Atom feed for the specified category.
+ class CategoryFeed < Page
+
+ # Initializes a new CategoryFeed.
+ #
+ # +base+ is the String path to the <source>.
+ # +category_dir+ is the String path between <source> and the category folder.
+ # +category+ is the category currently being processed.
+ def initialize(site, base, category_dir, category)
+ @site = site
+ @base = base
+ @dir = category_dir
+ @name = 'atom.xml'
+ self.process(@name)
+ # Read the YAML data from the layout page.
+ self.read_yaml(File.join(base, '_includes/custom'), 'category_feed.xml')
+ self.data['category'] = category
+ # Set the title for this page.
+ title_prefix = site.config['category_title_prefix'] || 'Category: '
+ self.data['title'] = "#{title_prefix}#{category}"
+ # Set the meta-description for this page.
+ meta_description_prefix = site.config['category_meta_description_prefix'] || 'Category: '
+ self.data['description'] = "#{meta_description_prefix}#{category}"
+
+ # Set the correct feed URL.
+ self.data['feed_url'] = "#{category_dir}/#{name}"
+ end
+
+ end
# The Site class is a built-in Jekyll class with access to global site config information.
class Site
@@ -63,6 +92,13 @@ module Jekyll
index.write(self.dest)
# Record the fact that this page has been added, otherwise Site::cleanup will remove it.
self.pages << index
+
+ # Create an Atom-feed for each index.
+ feed = CategoryFeed.new(self, self.source, category_dir, category)
+ feed.render(self.layouts, site_payload)
+ feed.write(self.dest)
+ # Record the fact that this page has been added, otherwise Site::cleanup will remove it.
+ self.pages << feed
end
# Loops through the list of category pages and processes each one.
diff --git a/plugins/code_block.rb b/plugins/code_block.rb
index 00b0b438..44e34945 100644
--- a/plugins/code_block.rb
+++ b/plugins/code_block.rb
@@ -24,7 +24,7 @@
#
# Output:
#
-# <figure role=code>
+# <figure class='code'>
# <figcaption><span>Got pain? painrelief.sh</span> <a href="http://site.com/painrelief.sh">Download it!</a>
# <div class="highlight"><pre><code class="sh">
# -- nicely escaped highlighted code --
@@ -37,7 +37,7 @@
# <sarcasm>Ooooh, sarcasm... How original!</sarcasm>
# {% endcodeblock %}
#
-# <figure role=code>
+# <figure class='code'>
# <pre><code>&lt;sarcasm> Ooooh, sarcasm... How original!&lt;/sarcasm></code></pre>
# </figure>
#
@@ -79,8 +79,8 @@ module Jekyll
def render(context)
output = super
- code = super.join
- source = "<figure role=code>"
+ code = super
+ source = "<figure class='code'>"
source += @caption if @caption
if @filetype
source += " #{highlight(code, @filetype)}</figure>"
@@ -90,6 +90,7 @@ module Jekyll
source = safe_wrap(source)
source = context['pygments_prefix'] + source if context['pygments_prefix']
source = source + context['pygments_suffix'] if context['pygments_suffix']
+ source
end
end
end
diff --git a/plugins/compass_compiler.rb b/plugins/compass_compiler.rb
deleted file mode 100644
index dcec746a..00000000
--- a/plugins/compass_compiler.rb
+++ /dev/null
@@ -1 +0,0 @@
-system "compass compile --css-dir source/stylesheets"
diff --git a/plugins/date.rb b/plugins/date.rb
new file mode 100644
index 00000000..b864f3e9
--- /dev/null
+++ b/plugins/date.rb
@@ -0,0 +1,98 @@
+module Octopress
+ module Date
+
+ # Returns a datetime if the input is a string
+ def datetime(date)
+ if date.class == String
+ date = Time.parse(date)
+ end
+ date
+ end
+
+ # Returns an ordidinal date eg July 22 2007 -> July 22nd 2007
+ def ordinalize(date)
+ date = datetime(date)
+ "#{date.strftime('%b')} #{ordinal(date.strftime('%e').to_i)}, #{date.strftime('%Y')}"
+ end
+
+ # Returns an ordinal number. 13 -> 13th, 21 -> 21st etc.
+ def ordinal(number)
+ if (11..13).include?(number.to_i % 100)
+ "#{number}<span>th</span>"
+ else
+ case number.to_i % 10
+ when 1; "#{number}<span>st</span>"
+ when 2; "#{number}<span>nd</span>"
+ when 3; "#{number}<span>rd</span>"
+ else "#{number}<span>th</span>"
+ end
+ end
+ end
+
+ # Formats date either as ordinal or by given date format
+ # Adds %o as ordinal representation of the day
+ def format_date(date, format)
+ date = datetime(date)
+ if format.nil? || format.empty? || format == "ordinal"
+ date_formatted = ordinalize(date)
+ else
+ date_formatted = date.strftime(format)
+ date_formatted.gsub!(/%o/, ordinal(date.strftime('%e').to_i))
+ end
+ date_formatted
+ end
+
+ end
+end
+
+
+module Jekyll
+
+ class Post
+ include Octopress::Date
+
+ # Convert this post into a Hash for use in Liquid templates.
+ #
+ # Returns <Hash>
+ def to_liquid
+ date_format = self.site.config['date_format']
+ self.data.deep_merge({
+ "title" => self.data['title'] || self.slug.split('-').select {|w| w.capitalize! || w }.join(' '),
+ "url" => self.url,
+ "date" => self.date,
+ # Monkey patch
+ "date_formatted" => format_date(self.date, date_format),
+ "updated_formatted" => self.data.has_key?('updated') ? format_date(self.data['updated'], date_format) : nil,
+ "id" => self.id,
+ "categories" => self.categories,
+ "next" => self.next,
+ "previous" => self.previous,
+ "tags" => self.tags,
+ "content" => self.content })
+ end
+ end
+
+ class Page
+ include Octopress::Date
+
+ # Initialize a new Page.
+ #
+ # site - The Site object.
+ # base - The String path to the source.
+ # dir - The String path between the source and the file.
+ # name - The String filename of the file.
+ def initialize(site, base, dir, name)
+ @site = site
+ @base = base
+ @dir = dir
+ @name = name
+
+ self.process(name)
+ self.read_yaml(File.join(base, dir), name)
+ # Monkey patch
+ date_format = self.site.config['date_format']
+ self.data['date_formatted'] = format_date(self.data['date'], date_format) if self.data.has_key?('date')
+ self.data['updated_formatted'] = format_date(self.data['updated'], date_format) if self.data.has_key?('updated')
+ end
+ end
+end \ No newline at end of file
diff --git a/plugins/gist_tag.rb b/plugins/gist_tag.rb
index 946ea23f..74dd3b37 100644
--- a/plugins/gist_tag.rb
+++ b/plugins/gist_tag.rb
@@ -16,7 +16,7 @@ module Jekyll
super
@text = text
@cache_disabled = false
- @cache_folder = File.expand_path "../_gist_cache", File.dirname(__FILE__)
+ @cache_folder = File.expand_path "../.gist-cache", File.dirname(__FILE__)
FileUtils.mkdir_p @cache_folder
end
@@ -71,7 +71,13 @@ module Jekyll
def get_gist_from_web(gist, file)
gist_url = get_gist_url_for gist, file
raw_uri = URI.parse gist_url
- https = Net::HTTP.new raw_uri.host, raw_uri.port
+ proxy = ENV['http_proxy']
+ if proxy
+ proxy_uri = URI.parse(proxy)
+ https = Net::HTTP::Proxy(proxy_uri.host, proxy_uri.port).new raw_uri.host, raw_uri.port
+ else
+ https = Net::HTTP.new raw_uri.host, raw_uri.port
+ end
https.use_ssl = true
https.verify_mode = OpenSSL::SSL::VERIFY_NONE
request = Net::HTTP::Get.new raw_uri.request_uri
diff --git a/plugins/image_tag.rb b/plugins/image_tag.rb
index 25a38df5..45670007 100644
--- a/plugins/image_tag.rb
+++ b/plugins/image_tag.rb
@@ -1,46 +1,47 @@
# Title: Simple Image tag for Jekyll
-# Author: Brandon Mathis http://brandonmathis.com
-# Description: Easily output images with optional class names and title/alt attributes
+# Authors: Brandon Mathis http://brandonmathis.com
+# Felix Schäfer, Frederic Hemberger
+# Description: Easily output images with optional class names, width, height, title and alt attributes
#
-# Syntax {% image [class name(s)] url [title text] %}
+# Syntax {% img [class name(s)] [http[s]:/]/path/to/image [width [height]] [title text | "title text" ["alt text"]] %}
#
-# Example:
-# {% ima left half http://site.com/images/ninja.png Ninja Attack! %}
+# Examples:
+# {% img /images/ninja.png Ninja Attack! %}
+# {% img left half http://site.com/images/ninja.png Ninja Attack! %}
+# {% img left half http://site.com/images/ninja.png 150 150 "Ninja Attack!" "Ninja in attack posture" %}
#
# Output:
-# <image class='left' src="http://site.com/images/ninja.png" title="Ninja Attack!" alt="Ninja Attack!">
+# <img src="/images/ninja.png">
+# <img class="left half" src="http://site.com/images/ninja.png" title="Ninja Attack!" alt="Ninja Attack!">
+# <img class="left half" src="http://site.com/images/ninja.png" width="150" height="150" title="Ninja Attack!" alt="Ninja in attack posture">
#
module Jekyll
class ImageTag < Liquid::Tag
@img = nil
- @title = nil
- @class = ''
- @width = ''
- @height = ''
def initialize(tag_name, markup, tokens)
- if markup =~ /(\S.*\s+)?(https?:\/\/|\/)(\S+)(\s+\d+\s+\d+)?(\s+.+)?/i
- @class = $1 || ''
- @img = $2 + $3
- if $5
- @title = $5.strip
- end
- if $4 =~ /\s*(\d+)\s+(\d+)/
- @width = $1
- @height = $2
+ attributes = ['class', 'src', 'width', 'height', 'title']
+
+ if markup =~ /(?<class>\S.*\s+)?(?<src>(?:https?:\/\/|\/|\S+\/)\S+)(?:\s+(?<width>\d+))?(?:\s+(?<height>\d+))?(?<title>\s+.+)?/i
+ @img = attributes.reduce({}) { |img, attr| img[attr] = $~[attr].strip if $~[attr]; img }
+ if /(?:"|')(?<title>[^"']+)?(?:"|')\s+(?:"|')(?<alt>[^"']+)?(?:"|')/ =~ @img['title']
+ @img['title'] = title
+ @img['alt'] = alt
+ else
+ @img['alt'] = @img['title'].gsub!(/"/, '&#34;') if @img['title']
end
+ @img['class'].gsub!(/"/, '') if @img['class']
end
super
end
def render(context)
- output = super
if @img
- "<img class='#{@class}' src='#{@img}' width='#{@width}' height='#{@height}' alt='#{@title}' title='#{@title}'>"
+ "<img #{@img.collect {|k,v| "#{k}=\"#{v}\"" if v}.join(" ")}>"
else
- "Error processing input, expected syntax: {% img [class name(s)] /url/to/image [width height] [title text] %}"
+ "Error processing input, expected syntax: {% img [class name(s)] [http[s]:/]/path/to/image [width [height]] [title text | \"title text\" [\"alt text\"]] %}"
end
end
end
diff --git a/plugins/include_code.rb b/plugins/include_code.rb
index 80951cb5..fc6daa36 100644
--- a/plugins/include_code.rb
+++ b/plugins/include_code.rb
@@ -61,7 +61,7 @@ module Jekyll
@filetype = file.extname.sub('.','') if @filetype.nil?
title = @title ? "#{@title} (#{file.basename})" : file.basename
url = "/#{code_dir}/#{@file}"
- source = "<figure role=code><figcaption><span>#{title}</span> <a href='#{url}'>download</a></figcaption>\n"
+ source = "<figure class='code'><figcaption><span>#{title}</span> <a href='#{url}'>download</a></figcaption>\n"
source += " #{highlight(code, @filetype)}</figure>"
safe_wrap(source)
end
diff --git a/plugins/jsfiddle.rb b/plugins/jsfiddle.rb
new file mode 100644
index 00000000..3ae173eb
--- /dev/null
+++ b/plugins/jsfiddle.rb
@@ -0,0 +1,40 @@
+# Title: jsFiddle tag for Jekyll
+# Author: Brian Arnold (@brianarn)
+# Description:
+# Given a jsFiddle shortcode, outputs the jsFiddle iframe code.
+# Using 'default' will preserve defaults as specified by jsFiddle.
+#
+# Syntax: {% jsfiddle shorttag [tabs] [skin] [height] [width] %}
+#
+# Examples:
+#
+# Input: {% jsfiddle ccWP7 %}
+# Output: <iframe style="width: 100%; height: 300px" src="http://jsfiddle.net/ccWP7/embedded/js,resources,html,css,result/light/"></iframe>
+#
+# Input: {% jsfiddle ccWP7 js,html,result %}
+# Output: <iframe style="width: 100%; height: 300px" src="http://jsfiddle.net/ccWP7/embedded/js,html,result/light/"></iframe>
+#
+
+module Jekyll
+ class JsFiddle < Liquid::Tag
+ def initialize(tag_name, markup, tokens)
+ if /(?<fiddle>\w+)(?:\s+(?<sequence>[\w,]+))?(?:\s+(?<skin>\w+))?(?:\s+(?<height>\w+))?(?:\s+(?<width>\w+))?/ =~ markup
+ @fiddle = fiddle
+ @sequence = (sequence unless sequence == 'default') || 'js,resources,html,css,result'
+ @skin = (skin unless skin == 'default') || 'light'
+ @width = width || '100%'
+ @height = height || '300px'
+ end
+ end
+
+ def render(context)
+ if @fiddle
+ "<iframe style=\"width: #{@width}; height: #{@height}\" src=\"http://jsfiddle.net/#{@fiddle}/embedded/#{@sequence}/#{@skin}/\"></iframe>"
+ else
+ "Error processing input, expected syntax: {% jsfiddle shorttag [tabs] [skin] [height] [width] %}"
+ end
+ end
+ end
+end
+
+Liquid::Template.register_tag('jsfiddle', Jekyll::JsFiddle) \ No newline at end of file
diff --git a/plugins/octopress_filters.rb b/plugins/octopress_filters.rb
index 1a959892..2ba93e9e 100644
--- a/plugins/octopress_filters.rb
+++ b/plugins/octopress_filters.rb
@@ -2,6 +2,7 @@
require './plugins/backtick_code_block'
require './plugins/post_filters'
require './plugins/raw'
+require './plugins/date'
require 'rubypants'
module OctopressFilters
@@ -23,16 +24,22 @@ module Jekyll
class ContentFilters < PostFilter
include OctopressFilters
def pre_render(post)
- post.content = pre_filter(post.content)
+ if post.ext.match('html|textile|markdown|haml|slim|xml')
+ post.content = pre_filter(post.content)
+ end
end
def post_render(post)
- post.content = post_filter(post.content)
+ if post.ext.match('html|textile|markdown|haml|slim|xml')
+ post.content = post_filter(post.content)
+ end
end
end
end
module OctopressLiquidFilters
+ include Octopress::Date
+
# Used on the blog index to split posts on the <!--more--> marker
def excerpt(input)
if input.index(/<!--\s*more\s*-->/i)
@@ -56,6 +63,18 @@ module OctopressLiquidFilters
end
end
+ # Extracts raw content DIV from template, used for page description as {{ content }}
+ # contains complete sub-template code on main page level
+ def raw_content(input)
+ /<div class="entry-content">(?<content>[\s\S]*?)<\/div>\s*<(footer|\/article)>/ =~ input
+ return (content.nil?) ? input : content
+ end
+
+ # Escapes CDATA sections in post content
+ def cdata_escape(input)
+ input.gsub(/<!\[CDATA\[/, '&lt;![CDATA[').gsub(/\]\]>/, ']]&gt;')
+ end
+
# Replaces relative urls with full urls
def expand_urls(input, url='')
url ||= '/'
@@ -64,6 +83,33 @@ module OctopressLiquidFilters
end
end
+ # Improved version of Liquid's truncate:
+ # - Doesn't cut in the middle of a word.
+ # - Uses typographically correct ellipsis (…) insted of '...'
+ def truncate(input, length)
+ if input.length > length && input[0..(length-1)] =~ /(.+)\b.+$/im
+ $1.strip + ' &hellip;'
+ else
+ input
+ end
+ end
+
+ # Improved version of Liquid's truncatewords:
+ # - Uses typographically correct ellipsis (…) insted of '...'
+ def truncatewords(input, length)
+ truncate = input.split(' ')
+ if truncate.length > length
+ truncate[0..length-1].join(' ').strip + ' &hellip;'
+ else
+ input
+ end
+ end
+
+ # Condenses multiple spaces and tabs into a single space
+ def condense_spaces(input)
+ input.gsub(/\s{2,}/, ' ')
+ end
+
# Removes trailing forward slash from a string for easily appending url segments
def strip_slash(input)
if input =~ /(.+)\/$|^\/$/
@@ -84,33 +130,6 @@ module OctopressLiquidFilters
input.titlecase
end
- # Returns a datetime if the input is a string
- def datetime(date)
- if date.class == String
- date = Time.parse(date)
- end
- date
- end
-
- # Returns an ordidinal date eg July 22 2007 -> July 22nd 2007
- def ordinalize(date)
- date = datetime(date)
- "#{date.strftime('%b')} #{ordinal(date.strftime('%e').to_i)}, #{date.strftime('%Y')}"
- end
-
- # Returns an ordinal number. 13 -> 13th, 21 -> 21st etc.
- def ordinal(number)
- if (11..13).include?(number.to_i % 100)
- "#{number}<span>th</span>"
- else
- case number.to_i % 10
- when 1; "#{number}<span>st</span>"
- when 2; "#{number}<span>nd</span>"
- when 3; "#{number}<span>rd</span>"
- else "#{number}<span>th</span>"
- end
- end
- end
end
Liquid::Template.register_filter OctopressLiquidFilters
diff --git a/plugins/preview_unpublished.rb b/plugins/preview_unpublished.rb
new file mode 100644
index 00000000..321ffd6f
--- /dev/null
+++ b/plugins/preview_unpublished.rb
@@ -0,0 +1,48 @@
+# Monkeypatch for Jekyll
+# Introduce distinction between preview/productive site generation
+# so posts with YAML attribute `published: false` can be previewed
+# on localhost without being published to the productive environment.
+
+module Jekyll
+
+ class Site
+ # Read all the files in <source>/<dir>/_posts and create a new Post
+ # object with each one.
+ #
+ # dir - The String relative path of the directory to read.
+ #
+ # Returns nothing.
+ def read_posts(dir)
+ base = File.join(self.source, dir, '_posts')
+ return unless File.exists?(base)
+ entries = Dir.chdir(base) { filter_entries(Dir['**/*']) }
+
+ # first pass processes, but does not yet render post content
+ entries.each do |f|
+ if Post.valid?(f)
+ post = Post.new(self, self.source, dir, f)
+
+ # Monkeypatch:
+ # On preview environment (localhost), publish all posts
+ if ENV.has_key?('OCTOPRESS_ENV') && ENV['OCTOPRESS_ENV'] == 'preview' && post.data.has_key?('published') && post.data['published'] == false
+ post.published = true
+ # Set preview mode flag (if necessary), `rake generate` will check for it
+ # to prevent pushing preview posts to productive environment
+ File.open(".preview-mode", "w") {}
+ end
+
+ if post.published && (self.future || post.date <= self.time)
+ self.posts << post
+ post.categories.each { |c| self.categories[c] << post }
+ post.tags.each { |c| self.tags[c] << post }
+ end
+ end
+ end
+
+ self.posts.sort!
+
+ # limit the posts if :limit_posts option is set
+ self.posts = self.posts[-limit_posts, limit_posts] if limit_posts
+ end
+ end
+end \ No newline at end of file
diff --git a/plugins/pullquote.rb b/plugins/pullquote.rb
index 03e307a7..3c65e66e 100644
--- a/plugins/pullquote.rb
+++ b/plugins/pullquote.rb
@@ -1,6 +1,6 @@
#
# Author: Brandon Mathis
-# Based on the sematic pullquote technique by Maykel Loomans at http://miekd.com/articles/pull-quotes-with-html5-and-css/
+# Based on the semantic pullquote technique by Maykel Loomans at http://miekd.com/articles/pull-quotes-with-html5-and-css/
#
# Outputs a span with a data-pullquote attribute set from the marked pullquote. Example:
#
@@ -13,23 +13,28 @@
# <p>
# <span data-pullquote="pullquotes are merely visual in presentation and should not appear twice in the text.">
# 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.
-# 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.
+# 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.
# </span>
# </p>
#
+# {% pullquote left %} will create a left-aligned pullquote instead.
+#
+# Note: this plugin now creates pullquotes with the class of pullquote-right by default
module Jekyll
class PullquoteTag < Liquid::Block
def initialize(tag_name, markup, tokens)
+ @align = (markup =~ /left/i) ? "left" : "right"
super
end
def render(context)
output = super
- if output.join =~ /\{"\s*(.+)\s*"\}/
- @quote = $1
- "<span class='has-pullquote' data-pullquote='#{@quote}'>#{output.join.gsub(/\{"\s*|\s*"\}/, '')}</span>"
+ if output =~ /\{"\s*(.+?)\s*"\}/m
+ @quote = RubyPants.new($1).to_html
+ "<span class='pullquote-#{@align}' data-pullquote='#{@quote}'>#{output.gsub(/\{"\s*|\s*"\}/, '')}</span>"
else
return "Surround your pullquote like this {\" text to be quoted \"}"
end
diff --git a/plugins/pygments_code.rb b/plugins/pygments_code.rb
index 67ce8c34..1676a3e0 100644
--- a/plugins/pygments_code.rb
+++ b/plugins/pygments_code.rb
@@ -2,7 +2,7 @@ require 'pygments'
require 'fileutils'
require 'digest/md5'
-PYGMENTS_CACHE_DIR = File.expand_path('../../_code_cache', __FILE__)
+PYGMENTS_CACHE_DIR = File.expand_path('../../.pygments-cache', __FILE__)
FileUtils.mkdir_p(PYGMENTS_CACHE_DIR)
module HighlightCode
@@ -21,21 +21,21 @@ module HighlightCode
if File.exist?(path)
highlighted_code = File.read(path)
else
- highlighted_code = Pygments.highlight(code, :lexer => lang, :formatter => 'html')
+ highlighted_code = Pygments.highlight(code, :lexer => lang, :formatter => 'html', :options => {:encoding => 'utf-8'})
File.open(path, 'w') {|f| f.print(highlighted_code) }
end
else
- highlighted_code = Pygments.highlight(code, :lexer => lang, :formatter => 'html')
+ highlighted_code = Pygments.highlight(code, :lexer => lang, :formatter => 'html', :options => {:encoding => 'utf-8'})
end
highlighted_code
end
def tableize_code (str, lang = '')
- table = '<div class="highlight"><table cellpadding="0" cellspacing="0"><tr><td class="gutter"><pre class="line-numbers">'
+ table = '<div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers">'
code = ''
str.lines.each_with_index do |line,index|
- table += "<span class='line'>#{index+1}</span>\n"
- code += "<div class='line'>#{line}</div>"
+ table += "<span class='line-number'>#{index+1}</span>\n"
+ code += "<span class='line'>#{line}</span>"
end
- table += "</pre></td><td class='code' width='100%'><pre><code class='#{lang}'>#{code}</code></pre></td></tr></table></div>"
+ table += "</pre></td><td class='code'><pre><code class='#{lang}'>#{code}</code></pre></td></tr></table></div>"
end
end
diff --git a/plugins/rubypants.rb b/plugins/rubypants.rb
new file mode 100644
index 00000000..e4f4502f
--- /dev/null
+++ b/plugins/rubypants.rb
@@ -0,0 +1,489 @@
+#
+# = RubyPants -- SmartyPants ported to Ruby
+#
+# Ported by Christian Neukirchen <mailto:chneukirchen@gmail.com>
+# Copyright (C) 2004 Christian Neukirchen
+#
+# Incooporates ideas, comments and documentation by Chad Miller
+# Copyright (C) 2004 Chad Miller
+#
+# Original SmartyPants by John Gruber
+# Copyright (C) 2003 John Gruber
+#
+
+#
+# = RubyPants -- SmartyPants ported to Ruby
+#
+# == Synopsis
+#
+# RubyPants is a Ruby port of the smart-quotes library SmartyPants.
+#
+# The original "SmartyPants" is a free web publishing plug-in for
+# Movable Type, Blosxom, and BBEdit that easily translates plain ASCII
+# punctuation characters into "smart" typographic punctuation HTML
+# entities.
+#
+#
+# == Description
+#
+# RubyPants can perform the following transformations:
+#
+# * Straight quotes (<tt>"</tt> and <tt>'</tt>) into "curly" quote
+# HTML entities
+# * Backticks-style quotes (<tt>``like this''</tt>) into "curly" quote
+# HTML entities
+# * Dashes (<tt>--</tt> and <tt>---</tt>) into en- and em-dash
+# entities
+# * Three consecutive dots (<tt>...</tt> or <tt>. . .</tt>) into an
+# ellipsis entity
+#
+# This means you can write, edit, and save your posts using plain old
+# ASCII straight quotes, plain dashes, and plain dots, but your
+# published posts (and final HTML output) will appear with smart
+# quotes, em-dashes, and proper ellipses.
+#
+# RubyPants does not modify characters within <tt><pre></tt>,
+# <tt><code></tt>, <tt><kbd></tt>, <tt><math></tt> or
+# <tt><script></tt> tag blocks. Typically, these tags are used to
+# display text where smart quotes and other "smart punctuation" would
+# not be appropriate, such as source code or example markup.
+#
+#
+# == Backslash Escapes
+#
+# If you need to use literal straight quotes (or plain hyphens and
+# periods), RubyPants accepts the following backslash escape sequences
+# to force non-smart punctuation. It does so by transforming the
+# escape sequence into a decimal-encoded HTML entity:
+#
+# \\ \" \' \. \- \`
+#
+# This is useful, for example, when you want to use straight quotes as
+# foot and inch marks: 6'2" tall; a 17" iMac. (Use <tt>6\'2\"</tt>
+# resp. <tt>17\"</tt>.)
+#
+#
+# == Algorithmic Shortcomings
+#
+# One situation in which quotes will get curled the wrong way is when
+# apostrophes are used at the start of leading contractions. For
+# example:
+#
+# 'Twas the night before Christmas.
+#
+# In the case above, RubyPants will turn the apostrophe into an
+# opening single-quote, when in fact it should be a closing one. I
+# don't think this problem can be solved in the general case--every
+# word processor I've tried gets this wrong as well. In such cases,
+# it's best to use the proper HTML entity for closing single-quotes
+# ("<tt>&#8217;</tt>") by hand.
+#
+#
+# == Bugs
+#
+# To file bug reports or feature requests (except see above) please
+# send email to: mailto:chneukirchen@gmail.com
+#
+# If the bug involves quotes being curled the wrong way, please send
+# example text to illustrate.
+#
+#
+# == Authors
+#
+# John Gruber did all of the hard work of writing this software in
+# Perl for Movable Type and almost all of this useful documentation.
+# Chad Miller ported it to Python to use with Pyblosxom.
+#
+# Christian Neukirchen provided the Ruby port, as a general-purpose
+# library that follows the *Cloth API.
+#
+#
+# == Copyright and License
+#
+# === SmartyPants license:
+#
+# Copyright (c) 2003 John Gruber
+# (http://daringfireball.net)
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+#
+# * Neither the name "SmartyPants" nor the names of its contributors
+# may be used to endorse or promote products derived from this
+# software without specific prior written permission.
+#
+# This software is provided by the copyright holders and contributors
+# "as is" and any express or implied warranties, including, but not
+# limited to, the implied warranties of merchantability and fitness
+# for a particular purpose are disclaimed. In no event shall the
+# copyright owner or contributors be liable for any direct, indirect,
+# incidental, special, exemplary, or consequential damages (including,
+# but not limited to, procurement of substitute goods or services;
+# loss of use, data, or profits; or business interruption) however
+# caused and on any theory of liability, whether in contract, strict
+# liability, or tort (including negligence or otherwise) arising in
+# any way out of the use of this software, even if advised of the
+# possibility of such damage.
+#
+# === RubyPants license
+#
+# RubyPants is a derivative work of SmartyPants and smartypants.py.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+#
+# This software is provided by the copyright holders and contributors
+# "as is" and any express or implied warranties, including, but not
+# limited to, the implied warranties of merchantability and fitness
+# for a particular purpose are disclaimed. In no event shall the
+# copyright owner or contributors be liable for any direct, indirect,
+# incidental, special, exemplary, or consequential damages (including,
+# but not limited to, procurement of substitute goods or services;
+# loss of use, data, or profits; or business interruption) however
+# caused and on any theory of liability, whether in contract, strict
+# liability, or tort (including negligence or otherwise) arising in
+# any way out of the use of this software, even if advised of the
+# possibility of such damage.
+#
+#
+# == Links
+#
+# John Gruber:: http://daringfireball.net
+# SmartyPants:: http://daringfireball.net/projects/smartypants
+#
+# Chad Miller:: http://web.chad.org
+#
+# Christian Neukirchen:: http://kronavita.de/chris
+#
+
+
+class RubyPants < String
+
+ # Create a new RubyPants instance with the text in +string+.
+ #
+ # Allowed elements in the options array:
+ #
+ # 0 :: do nothing
+ # 1 :: enable all, using only em-dash shortcuts
+ # 2 :: enable all, using old school en- and em-dash shortcuts (*default*)
+ # 3 :: enable all, using inverted old school en and em-dash shortcuts
+ # -1 :: stupefy (translate HTML entities to their ASCII-counterparts)
+ #
+ # If you don't like any of these defaults, you can pass symbols to change
+ # RubyPants' behavior:
+ #
+ # <tt>:quotes</tt> :: quotes
+ # <tt>:backticks</tt> :: backtick quotes (``double'' only)
+ # <tt>:allbackticks</tt> :: backtick quotes (``double'' and `single')
+ # <tt>:dashes</tt> :: dashes
+ # <tt>:oldschool</tt> :: old school dashes
+ # <tt>:inverted</tt> :: inverted old school dashes
+ # <tt>:ellipses</tt> :: ellipses
+ # <tt>:convertquotes</tt> :: convert <tt>&quot;</tt> entities to
+ # <tt>"</tt> for Dreamweaver users
+ # <tt>:stupefy</tt> :: translate RubyPants HTML entities
+ # to their ASCII counterparts.
+ #
+ def initialize(string, options=[2])
+ super string
+ @options = [*options]
+ end
+
+ # Apply SmartyPants transformations.
+ def to_html
+ do_quotes = do_backticks = do_dashes = do_ellipses = do_stupify = nil
+ convert_quotes = false
+
+ if @options.include? 0
+ # Do nothing.
+ return self
+ elsif @options.include? 1
+ # Do everything, turn all options on.
+ do_quotes = do_backticks = do_ellipses = true
+ do_dashes = :normal
+ elsif @options.include? 2
+ # Do everything, turn all options on, use old school dash shorthand.
+ do_quotes = do_backticks = do_ellipses = true
+ do_dashes = :oldschool
+ elsif @options.include? 3
+ # Do everything, turn all options on, use inverted old school
+ # dash shorthand.
+ do_quotes = do_backticks = do_ellipses = true
+ do_dashes = :inverted
+ elsif @options.include?(-1)
+ do_stupefy = true
+ else
+ do_quotes = @options.include? :quotes
+ do_backticks = @options.include? :backticks
+ do_backticks = :both if @options.include? :allbackticks
+ do_dashes = :normal if @options.include? :dashes
+ do_dashes = :oldschool if @options.include? :oldschool
+ do_dashes = :inverted if @options.include? :inverted
+ do_ellipses = @options.include? :ellipses
+ convert_quotes = @options.include? :convertquotes
+ do_stupefy = @options.include? :stupefy
+ end
+
+ # Parse the HTML
+ tokens = tokenize
+
+ # Keep track of when we're inside <pre> or <code> tags.
+ in_pre = false
+
+ # Here is the result stored in.
+ result = ""
+
+ # This is a cheat, used to get some context for one-character
+ # tokens that consist of just a quote char. What we do is remember
+ # the last character of the previous text token, to use as context
+ # to curl single- character quote tokens correctly.
+ prev_token_last_char = nil
+
+ tokens.each { |token|
+ if token.first == :tag
+ result << token[1]
+ if token[1] =~ %r!<(/?)(?:pre|code|kbd|script|math)[\s>]!
+ in_pre = ($1 != "/") # Opening or closing tag?
+ end
+ else
+ t = token[1]
+
+ # Remember last char of this token before processing.
+ last_char = t[-1].chr
+
+ unless in_pre
+ t = process_escapes t
+
+ t.gsub!(/&quot;/, '"') if convert_quotes
+
+ if do_dashes
+ t = educate_dashes t if do_dashes == :normal
+ t = educate_dashes_oldschool t if do_dashes == :oldschool
+ t = educate_dashes_inverted t if do_dashes == :inverted
+ end
+
+ t = educate_ellipses t if do_ellipses
+
+ # Note: backticks need to be processed before quotes.
+ if do_backticks
+ t = educate_backticks t
+ t = educate_single_backticks t if do_backticks == :both
+ end
+
+ if do_quotes
+ if t == "'"
+ # Special case: single-character ' token
+ if prev_token_last_char =~ /\S/
+ t = "&#8217;"
+ else
+ t = "&#8216;"
+ end
+ elsif t == '"'
+ # Special case: single-character " token
+ if prev_token_last_char =~ /\S/
+ t = "&#8221;"
+ else
+ t = "&#8220;"
+ end
+ else
+ # Normal case:
+ t = educate_quotes t
+ end
+ end
+
+ t = stupefy_entities t if do_stupefy
+ end
+
+ prev_token_last_char = last_char
+ result << t
+ end
+ }
+
+ # Done
+ result
+ end
+
+ protected
+
+ # Return the string, with after processing the following backslash
+ # escape sequences. This is useful if you want to force a "dumb" quote
+ # or other character to appear.
+ #
+ # Escaped are:
+ # \\ \" \' \. \- \`
+ #
+ def process_escapes(str)
+ str.gsub('\\\\', '&#92;').
+ gsub('\"', '&#34;').
+ gsub("\\\'", '&#39;').
+ gsub('\.', '&#46;').
+ gsub('\-', '&#45;').
+ gsub('\`', '&#96;')
+ end
+
+ # The string, with each instance of "<tt>--</tt>" translated to an
+ # em-dash HTML entity.
+ #
+ def educate_dashes(str)
+ str.gsub(/--/, '&#8212;')
+ end
+
+ # The string, with each instance of "<tt>--</tt>" translated to an
+ # en-dash HTML entity, and each "<tt>---</tt>" translated to an
+ # em-dash HTML entity.
+ #
+ def educate_dashes_oldschool(str)
+ str.gsub(/---/, '&#8212;').gsub(/--/, '&#8211;')
+ end
+
+ # Return the string, with each instance of "<tt>--</tt>" translated
+ # to an em-dash HTML entity, and each "<tt>---</tt>" translated to
+ # an en-dash HTML entity. Two reasons why: First, unlike the en- and
+ # em-dash syntax supported by +educate_dashes_oldschool+, it's
+ # compatible with existing entries written before SmartyPants 1.1,
+ # back when "<tt>--</tt>" was only used for em-dashes. Second,
+ # em-dashes are more common than en-dashes, and so it sort of makes
+ # sense that the shortcut should be shorter to type. (Thanks to
+ # Aaron Swartz for the idea.)
+ #
+ def educate_dashes_inverted(str)
+ str.gsub(/---/, '&#8211;').gsub(/--/, '&#8212;')
+ end
+
+ # Return the string, with each instance of "<tt>...</tt>" translated
+ # to an ellipsis HTML entity. Also converts the case where there are
+ # spaces between the dots.
+ #
+ def educate_ellipses(str)
+ str.gsub('...', '&#8230;').gsub('. . .', '&#8230;')
+ end
+
+ # Return the string, with "<tt>``backticks''</tt>"-style single quotes
+ # translated into HTML curly quote entities.
+ #
+ def educate_backticks(str)
+ str.gsub("``", '&#8220;').gsub("''", '&#8221;')
+ end
+
+ # Return the string, with "<tt>`backticks'</tt>"-style single quotes
+ # translated into HTML curly quote entities.
+ #
+ def educate_single_backticks(str)
+ str.gsub("`", '&#8216;').gsub("'", '&#8217;')
+ end
+
+ # Return the string, with "educated" curly quote HTML entities.
+ #
+ def educate_quotes(str)
+ punct_class = '[!"#\$\%\'()*+,\-.\/:;<=>?\@\[\\\\\]\^_`{|}~]'
+
+ str = str.dup
+
+ # Special case if the very first character is a quote followed by
+ # punctuation at a non-word-break. Close the quotes by brute
+ # force:
+ str.gsub!(/^'(?=#{punct_class}\B)/, '&#8217;')
+ str.gsub!(/^"(?=#{punct_class}\B)/, '&#8221;')
+
+ # Special case for double sets of quotes, e.g.:
+ # <p>He said, "'Quoted' words in a larger quote."</p>
+ str.gsub!(/"'(?=\w)/, '&#8220;&#8216;')
+ str.gsub!(/'"(?=\w)/, '&#8216;&#8220;')
+
+ # Special case for decade abbreviations (the '80s):
+ str.gsub!(/'(?=\d\ds)/, '&#8217;')
+
+ close_class = %![^\ \t\r\n\\[\{\(\-]!
+ dec_dashes = '&#8211;|&#8212;'
+
+ # Get most opening single quotes:
+ str.gsub!(/(\s|&nbsp;|--|&[mn]dash;|#{dec_dashes}|&#x201[34];)'(?=\w)/,
+ '\1&#8216;')
+ # Single closing quotes:
+ str.gsub!(/(#{close_class})'/, '\1&#8217;')
+ str.gsub!(/'(\s|s\b|$)/, '&#8217;\1')
+ # Any remaining single quotes should be opening ones:
+ str.gsub!(/'/, '&#8216;')
+
+ # Get most opening double quotes:
+ str.gsub!(/(\s|&nbsp;|--|&[mn]dash;|#{dec_dashes}|&#x201[34];)"(?=\w)/,
+ '\1&#8220;')
+ # Double closing quotes:
+ str.gsub!(/(#{close_class})"/, '\1&#8221;')
+ str.gsub!(/"(\s|s\b|$)/, '&#8221;\1')
+ # Any remaining quotes should be opening ones:
+ str.gsub!(/"/, '&#8220;')
+
+ str
+ end
+
+ # Return the string, with each RubyPants HTML entity translated to
+ # its ASCII counterpart.
+ #
+ # Note: This is not reversible (but exactly the same as in SmartyPants)
+ #
+ def stupefy_entities(str)
+ str.
+ gsub(/&#8211;/, '-'). # en-dash
+ gsub(/&#8212;/, '--'). # em-dash
+
+ gsub(/&#8216;/, "'"). # open single quote
+ gsub(/&#8217;/, "'"). # close single quote
+
+ gsub(/&#8220;/, '"'). # open double quote
+ gsub(/&#8221;/, '"'). # close double quote
+
+ gsub(/&#8230;/, '...') # ellipsis
+ end
+
+ # Return an array of the tokens comprising the string. Each token is
+ # either a tag (possibly with nested, tags contained therein, such
+ # as <tt><a href="<MTFoo>"></tt>, or a run of text between
+ # tags. Each element of the array is a two-element array; the first
+ # is either :tag or :text; the second is the actual value.
+ #
+ # Based on the <tt>_tokenize()</tt> subroutine from Brad Choate's
+ # MTRegex plugin. <http://www.bradchoate.com/past/mtregex.php>
+ #
+ # This is actually the easier variant using tag_soup, as used by
+ # Chad Miller in the Python port of SmartyPants.
+ #
+ def tokenize
+ tag_soup = /([^<]*)(<[^>]*>)/
+
+ tokens = []
+
+ prev_end = 0
+ scan(tag_soup) {
+ tokens << [:text, $1] if $1 != ""
+ tokens << [:tag, $2]
+
+ prev_end = $~.end(0)
+ }
+
+ if prev_end < size
+ tokens << [:text, self[prev_end..-1]]
+ end
+
+ tokens
+ end
+end
diff --git a/plugins/titlecase.rb b/plugins/titlecase.rb
index 103bf702..7648932c 100644
--- a/plugins/titlecase.rb
+++ b/plugins/titlecase.rb
@@ -11,8 +11,8 @@ class String
# capitalize first and last words
x.first.to_s.smart_capitalize!
x.last.to_s.smart_capitalize!
- # small words after colons are capitalized
- x.join(" ").gsub(/:\s?(\W*#{small_words.join("|")}\W*)\s/) { ": #{$1.smart_capitalize} " }
+ # small words are capitalized after colon, period, exclamation mark, question mark
+ x.join(" ").gsub(/(:|\.|!|\?)\s?(\W*#{small_words.join("|")}\W*)\s/) { "#{$1} #{$2.smart_capitalize} " }
end
def titlecase!