From 82e9d7e45b5b69ed67a7bf68dc81538b22fce9a9 Mon Sep 17 00:00:00 2001 From: Matthias Kretschmann Date: Sun, 1 Dec 2013 18:23:11 +0100 Subject: [PATCH] titlecase, rubypants for post content, more stuff from octopress --- Gemfile | 4 +- Gemfile.lock | 6 + _src/_includes/index.html | 6 +- _src/_layouts/category_index.html | 8 +- _src/_layouts/link.html | 2 +- _src/_layouts/photo.html | 2 +- _src/_layouts/post.html | 4 +- _src/_plugins/octopress_filters.rb | 141 +++++++++++++++++++---- _src/_plugins/post_filters.rb | 176 +++++++++++++++++++++++++++++ _src/_plugins/raw.rb | 40 +++++++ _src/_plugins/titlecase.rb | 36 ++++++ _src/search.json | 2 +- 12 files changed, 392 insertions(+), 35 deletions(-) create mode 100644 _src/_plugins/post_filters.rb create mode 100644 _src/_plugins/raw.rb create mode 100644 _src/_plugins/titlecase.rb diff --git a/Gemfile b/Gemfile index c3031905..45ac5391 100644 --- a/Gemfile +++ b/Gemfile @@ -4,4 +4,6 @@ source "https://rubygems.org" # gem "rails" gem 'jekyll', '~>1.3.0' gem 'mini_magick', '~>3.6.0' -gem 'fileutils', '~>0.7' \ No newline at end of file +gem 'fileutils', '~>0.7' +gem 'rubypants', '~> 0.2.0' +gem 'gsl', '~> 1.15.3' \ No newline at end of file diff --git a/Gemfile.lock b/Gemfile.lock index f78303d7..04087e2e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -10,6 +10,8 @@ GEM ffi (1.9.3) fileutils (0.7) rmagick (>= 2.13.1) + gsl (1.15.3) + narray (>= 0.5.9) highline (1.6.20) jekyll (1.3.1) classifier (~> 1.3) @@ -30,6 +32,7 @@ GEM syntax (>= 1.0.0) mini_magick (3.6.0) subexec (~> 0.2.1) + narray (0.6.0.8) posix-spawn (0.3.6) pygments.rb (0.5.4) posix-spawn (~> 0.3.6) @@ -41,6 +44,7 @@ GEM ffi (>= 0.5.0) redcarpet (2.3.0) rmagick (2.13.2) + rubypants (0.2.0) safe_yaml (0.9.7) subexec (0.2.3) syntax (1.0.0) @@ -51,5 +55,7 @@ PLATFORMS DEPENDENCIES fileutils (~> 0.7) + gsl (~> 1.15.3) jekyll (~> 1.3.0) mini_magick (~> 3.6.0) + rubypants (~> 0.2.0) diff --git a/_src/_includes/index.html b/_src/_includes/index.html index 11f43588..785fb9bc 100644 --- a/_src/_includes/index.html +++ b/_src/_includes/index.html @@ -5,7 +5,7 @@

- {{ post.title }} + {{ post.title | titlecase }} {{ post.linkurl | remove:'http://' | remove:'https://' | remove:'www.' | split:'/' | first }}

@@ -29,7 +29,7 @@ {% picture {{ post.image }} %} -
{{ post.title }}
+
{{ post.title | titlecase }}
@@ -39,7 +39,7 @@
-

{{ post.title }}

+

{{ post.title | titlecase }}

diff --git a/_src/_layouts/category_index.html b/_src/_layouts/category_index.html index a6cd5f1e..6073e49b 100644 --- a/_src/_layouts/category_index.html +++ b/_src/_layouts/category_index.html @@ -17,7 +17,7 @@ layout: base
+ +{% include paginator.html %} \ No newline at end of file diff --git a/_src/_layouts/link.html b/_src/_layouts/link.html index 9c25d1e1..b89e61da 100644 --- a/_src/_layouts/link.html +++ b/_src/_layouts/link.html @@ -8,7 +8,7 @@ layout: base

- {{ page.title }} + {{ page.title | titlecase }} {{ page.linkurl | remove:'http://' | remove:'https://' | remove:'www.' | split:'/' | first }}

diff --git a/_src/_layouts/photo.html b/_src/_layouts/photo.html index 2e2c4c96..f0caebc8 100644 --- a/_src/_layouts/photo.html +++ b/_src/_layouts/photo.html @@ -10,7 +10,7 @@ layout: base {% picture {{ page.image }} %} -
{{ page.title }}
+
{{ page.title | titlecase }}
diff --git a/_src/_layouts/post.html b/_src/_layouts/post.html index d82b46e7..cf1f1e2f 100644 --- a/_src/_layouts/post.html +++ b/_src/_layouts/post.html @@ -6,7 +6,7 @@ layout: base
-

{{ page.title }}

+

{{ page.title | titlecase }}

@@ -14,7 +14,7 @@ layout: base {% picture {{ page.image }} class="teaser" %} {% endif %} - {{ content }} + {{ content | condense_spaces }}
{% include entry_meta.html %} diff --git a/_src/_plugins/octopress_filters.rb b/_src/_plugins/octopress_filters.rb index d61ba30c..c2671eee 100644 --- a/_src/_plugins/octopress_filters.rb +++ b/_src/_plugins/octopress_filters.rb @@ -1,32 +1,127 @@ -# Filters taken from the Octopress project by Brandon Mathis. -# https://github.com/imathis/octopress/blob/master/plugins/octopress_filters.rb +#custom filters for Octopress +require './_src/_plugins/post_filters.rb' +require './_src/_plugins/raw.rb' +require 'rubypants' + +module OctopressFilters + include TemplateWrapper + def pre_filter(input) + input = input + end + def post_filter(input) + input = unwrap(input) + RubyPants.new(input).to_html + end +end + module Jekyll - - module Filters - - # Used on the blog index to split posts on the marker - def excerpt(input) - if input.index(//i) - input.split(//i)[0] - else - input + class ContentFilters < PostFilter + include OctopressFilters + def pre_render(post) + if post.ext.match('html|textile|markdown|md|haml|slim|xml') + post.content = pre_filter(post.content) end end - - # Checks for excerpts (helpful for template conditionals) - def has_more(input) - input =~ //i ? true : false - end - - # Replaces relative urls with full urls - def expand_urls(input, url='') - url ||= '/' - input.gsub /(\s+(href|src)\s*=\s*["|']{1})(\/[^\"'>]*)/ do - $1+url+$3 + def post_render(post) + if post.ext.match('html|textile|markdown|md|haml|slim|xml') + post.content = post_filter(post.content) end end + end +end + +module OctopressLiquidFilters + + # Used on the blog index to split posts on the marker + def excerpt(input) + if input.index(//i) + input.split(//i)[0] + else + input + end + end + + # Checks for excerpts (helpful for template conditionals) + def has_excerpt(input) + input =~ //i ? true : false + end + + # Summary is used on the Archive pages to return the first block of content from a post. + def summary(input) + if input.index(/\n\n/) + input.split(/\n\n/)[0] + else + input + 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) + /
(?[\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(//, ']]>') + end + + # Replaces relative urls with full urls + def expand_urls(input, url='') + url ||= '/' + input.gsub /(\s+(href|src)\s*=\s*["|']{1})(\/[^\"'>]*)/ do + $1+url+$3 + 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 + ' …' + 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 + ' …' + 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 =~ /(.+)\/$|^\/$/ + input = $1 + end + input + end + + # Returns a url without the protocol (http://) + def shorthand_url(input) + input.gsub /(https?:\/\/)(\S+)/ do + $2 + end + end + + # Returns a title cased string based on John Gruber's title case http://daringfireball.net/2008/08/title_case_update + def titlecase(input) + input.titlecase end end -Liquid::Template.register_filter(Jekyll::Filters) +Liquid::Template.register_filter OctopressLiquidFilters \ No newline at end of file diff --git a/_src/_plugins/post_filters.rb b/_src/_plugins/post_filters.rb new file mode 100644 index 00000000..6edbda7f --- /dev/null +++ b/_src/_plugins/post_filters.rb @@ -0,0 +1,176 @@ +module Jekyll + + # Extended plugin type that allows the plugin + # to be called on varous callback methods. + # + # Examples: + # https://github.com/tedkulp/octopress/blob/master/plugins/post_metaweblog.rb + # https://github.com/tedkulp/octopress/blob/master/plugins/post_twitter.rb + class PostFilter < Plugin + + #Called before post is sent to the converter. Allows + #you to modify the post object before the converter + #does it's thing + def pre_render(post) + end + + #Called after the post is rendered with the converter. + #Use the post object to modify it's contents before the + #post is inserted into the template. + def post_render(post) + end + + #Called after the post is written to the disk. + #Use the post object to read it's contents to do something + #after the post is safely written. + def post_write(post) + end + end + + # Monkey patch for the Jekyll Site class. For the original class, + # see: https://github.com/mojombo/jekyll/blob/master/lib/jekyll/site.rb + class Site + + # Instance variable to store the various post_filter + # plugins that are loaded. + attr_accessor :post_filters + + # Instantiates all of the post_filter plugins. This is basically + # a duplication of the other loaders in Site#setup. + def load_post_filters + self.post_filters = Jekyll::PostFilter.subclasses.select do |c| + !self.safe || c.safe + end.map do |c| + c.new(self.config) + end + end + end + + # Monkey patch for the Jekyll Post class. For the original class, + # see: https://github.com/mojombo/jekyll/blob/master/lib/jekyll/post.rb + class Post + + # Copy the #write method to #old_write, so we can redefine #write + # method. + alias_method :old_write, :write + + # Write the generated post file to the destination directory. It + # then calls any post_write methods that may exist. + # +dest+ is the String path to the destination dir + # + # Returns nothing + def write(dest) + old_write(dest) + post_write if respond_to?(:post_write) + end + end + + # Monkey patch for the Jekyll Page class. For the original class, + # see: https://github.com/mojombo/jekyll/blob/master/lib/jekyll/page.rb + class Page + + # Copy the #write method to #old_write, so we can redefine #write + # method. + alias_method :old_write, :write + + # Write the generated post file to the destination directory. It + # then calls any post_write methods that may exist. + # +dest+ is the String path to the destination dir + # + # Returns nothing + def write(dest) + old_write(dest) + post_write if respond_to?(:post_write) + end + end + + # Monkey patch for the Jekyll Convertible module. For the original class, + # see: https://github.com/mojombo/jekyll/blob/master/lib/jekyll/convertible.rb + module Convertible + + def is_post? + self.class.to_s == 'Jekyll::Post' + end + + def is_page? + self.class.to_s == 'Jekyll::Page' + end + + def is_filterable? + is_post? or is_page? + end + + # Call the #pre_render methods on all of the loaded + # post_filter plugins. + # + # Returns nothing + def pre_render + self.site.load_post_filters unless self.site.post_filters + + if self.site.post_filters and is_filterable? + self.site.post_filters.each do |filter| + filter.pre_render(self) + end + end + end + + # Call the #post_render methods on all of the loaded + # post_filter plugins. + # + # Returns nothing + def post_render + if self.site.post_filters and is_filterable? + self.site.post_filters.each do |filter| + filter.post_render(self) + end + end + end + + # Call the #post_write methods on all of the loaded + # post_filter plugins. + # + # Returns nothing + def post_write + if self.site.post_filters and is_filterable? + self.site.post_filters.each do |filter| + filter.post_write(self) + end + end + end + + # Copy the #transform method to #old_transform, so we can + # redefine #transform method. + alias_method :old_transform, :transform + + # Transform the contents based on the content type. Then calls the + # #post_render method if it exists + # + # Returns nothing. + def transform + old_transform + post_render if respond_to?(:post_render) + end + + # Copy the #do_layout method to #old_do_layout, so we can + # redefine #do_layout method. + alias_method :old_do_layout, :do_layout + + # Calls the pre_render method if it exists and then adds any necessary + # layouts to this convertible document. + # + # payload - The site payload Hash. + # layouts - A Hash of {"name" => "layout"}. + # + # Returns nothing. + def do_layout(payload, layouts) + pre_render if respond_to?(:pre_render) + old_do_layout(payload, layouts) + end + + # Returns the full url of the post, including the + # configured url + def full_url + self.site.config['url'] + self.url + end + end +end \ No newline at end of file diff --git a/_src/_plugins/raw.rb b/_src/_plugins/raw.rb new file mode 100644 index 00000000..069469ae --- /dev/null +++ b/_src/_plugins/raw.rb @@ -0,0 +1,40 @@ +# Author: Brandon Mathis +# Description: Provides plugins with a method for wrapping and unwrapping input to prevent Markdown and Textile from parsing it. +# Purpose: This is useful for preventing Markdown and Textile from being too aggressive and incorrectly parsing in-line HTML. +module TemplateWrapper + # Wrap input with a
+ def safe_wrap(input) + "
#{input}
" + end + # This must be applied after the + def unwrap(input) + input.gsub /
(.+?)<\/notextile><\/div>/m do + $1 + end + end +end + +# Author: phaer, https://github.com/phaer +# Source: https://gist.github.com/1020852 +# Description: Raw tag for jekyll. Keeps liquid from parsing text betweeen {% raw %} and {% endraw %} + +module Jekyll + class RawTag < Liquid::Block + def parse(tokens) + @nodelist ||= [] + @nodelist.clear + + while token = tokens.shift + if token =~ FullToken + if block_delimiter == $1 + end_tag + return + end + end + @nodelist << token if not token.empty? + end + end + end +end + +Liquid::Template.register_tag('raw', Jekyll::RawTag) \ No newline at end of file diff --git a/_src/_plugins/titlecase.rb b/_src/_plugins/titlecase.rb new file mode 100644 index 00000000..921b8fa9 --- /dev/null +++ b/_src/_plugins/titlecase.rb @@ -0,0 +1,36 @@ +class String + def titlecase + small_words = %w(a an and as at but by en for if in of on or the to v v. via vs vs. ezeep) + + x = split(" ").map do |word| + # note: word could contain non-word characters! + # downcase all small_words, capitalize the rest + small_words.include?(word.gsub(/\W/, "").downcase) ? word.downcase! : word.smart_capitalize! + word + end + # capitalize first and last words + #x.first.to_s.smart_capitalize! + #x.last.to_s.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! + replace(titlecase) + end + + def smart_capitalize + # ignore any leading crazy characters and capitalize the first real character + if self =~ /^['"\(\[']*([a-z])/ + i = index($1) + x = self[i,self.length] + # word with capitals and periods mid-word are left alone + self[i,1] = self[i,1].upcase unless x =~ /[A-Z]/ or x =~ /\.\w+/ + end + self + end + + def smart_capitalize! + replace(smart_capitalize) + end +end \ No newline at end of file diff --git a/_src/search.json b/_src/search.json index bee90d8b..e86255dc 100644 --- a/_src/search.json +++ b/_src/search.json @@ -3,7 +3,7 @@ [ {% for post in site.posts %} { - "title" : "{{ post.title | escape }}", + "title" : "{{ post.title | escape | titlecase }}", "category" : "{{ post.categories }}", "url" : "{{ post.url }}", "date" : "{{ post.date }}",