Browse code

1. Moved _plugins into themes/classic/_plugins I think it's probably better to ship plugins with themes to make it easier to update them. 2. Improved 'install' rake task and made nicer output

Brandon Mathis authored on 11/06/2011 at 19:58:53
Showing 22 changed files
... ...
@@ -25,9 +25,13 @@ task :install, :theme do |t, args|
25 25
   # copy theme into working Jekyll directories
26 26
   theme = args.theme || 'classic'
27 27
   puts "## Copying "+theme+" theme to Jekyll paths"
28
-  system "cp -R themes/"+theme+"/source source"
29
-  system "cp -R themes/"+theme+"/sass sass"
30
-  # system "cp -R themes/"+theme+"/_plugins/ _plugins/"
28
+  system "mkdir -p #{source}; cp -R themes/"+theme+"/source/ #{source}/"
29
+  system "mkdir -p sass; cp -R themes/"+theme+"/sass/ sass/"
30
+  system "mkdir -p _plugins; cp -R themes/"+theme+"/_plugins/ _plugins/"
31
+  system "mkdir -p #{source}/_posts";
32
+  puts "## Layouts, images, and javascritps from the #{theme} theme have been installed into ./#{source}"
33
+  puts "## Sass stylesheet sources from the #{theme} theme have been installed into ./sass"
34
+  puts "## Plugins from the #{theme} theme have been installed into ./_plugins"
31 35
 end
32 36
 
33 37
 #######################
34 38
deleted file mode 100644
... ...
@@ -1,109 +0,0 @@
1
-#
2
-# Author: Josediaz Gonzalez - https://github.com/josegonzalez
3
-# Source URL: https://github.com/josegonzalez/josediazgonzalez.com/blob/master/_plugins/blockquote.rb
4
-# Modified by Brandon Mathis
5
-#
6
-require './_plugins/titlecase.rb'
7
-module Jekyll
8
-
9
-  # Outputs a string with a given attribution as a quote
10
-  #
11
-  #   {% blockquote John Paul Jones %}
12
-  #     Monkeys!
13
-  #   {% endblockquote %}
14
-  #   ...
15
-  #   <blockquote>
16
-  #     Monkeys!
17
-  #     <br />
18
-  #     John Paul Jones
19
-  #   </blockquote>
20
-  #
21
-  class Blockquote < Liquid::Block
22
-    FullCiteWithTitle = /([\w\s]+)(https?:\/\/)(\S+\s)([\w\s]+)/i
23
-    FullCite = /([\w\s]+)(https?:\/\/)(\S+)/i
24
-    Author =  /([\w\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 = super
45
-      if @by.nil?
46
-        '<blockquote><p>' + output.join + '</p></blockquote>'
47
-      elsif !@title.nil?
48
-        '<blockquote><p>' + output.join + '</p></blockquote>' + '<p><cite><strong>' + @by + '</strong>' + '<a class="source" href="' + @source + '">' + @title + '</a></cite></p>'
49
-      elsif !@source.nil?
50
-        '<blockquote><p>' + output.join + '</p></blockquote>' + '<p><cite><strong>' + @by + '</strong>' + '<a class="source" href="' + @source + '">source</a></cite></p>'
51
-      else
52
-        '<blockquote><p>' + output.join + '</p></blockquote>' + '<p><cite><strong>' + @by + '</strong></cite></p>'
53
-      end
54
-    end
55
-  end
56
-
57
-  # Outputs a string with a given attribution as a pullquote
58
-  #
59
-  #   {% blockquote John Paul Jones %}
60
-  #     Monkeys!
61
-  #   {% endblockquote %}
62
-  #   ...
63
-  #   <blockquote class="pullquote">
64
-  #     Monkeys!
65
-  #     <br />
66
-  #     John Paul Jones
67
-  #   </blockquote>
68
-  #
69
-  class Pullquote < Liquid::Block
70
-    FullCiteWithTitle = /([\w\s]+)(http:\/\/|https:\/\/)(\S+)([\w\s]+)/i
71
-    FullCite = /([\w\s]+)(http:\/\/|https:\/\/)(\S+)/i
72
-    Author =  /([\w\s]+)/
73
-
74
-    def initialize(tag_name, markup, tokens)
75
-      @by = nil
76
-      @source = nil
77
-      @title = nil
78
-      if markup =~ FullCiteWithTitle
79
-        @by = $1
80
-        @source = $2 + $3
81
-        @title = $4
82
-      elsif markup =~ FullCite
83
-        @by = $1
84
-        @source = $2 + $3
85
-      elsif markup =~ Author
86
-        @by = $1
87
-      end
88
-      super
89
-    end
90
-
91
-    def render(context)
92
-      output = super
93
-      if @by.nil?
94
-        '<blockquote class="pullquote"><p>' + output.join + '</p></blockquote>'
95
-      elsif @title
96
-        '<blockquote class="pullquote"><p>' + output.join + '</p></blockquote>' + '<p><cite><strong>' + @by + '</strong>' + ' <a class="source" href="' + @source + '">' + @title + '</a></cite></p>'
97
-      elsif @source
98
-        '<blockquote class="pullquote"><p>' + output.join + '</p></blockquote>' + '<p><cite><strong>' + @by + '</strong>' + ' <a class="source" href="' + @source + '">source</a></cite></p>'
99
-      elsif @by
100
-        '<blockquote class="pullquote"><p>' + output.join + '</p></blockquote>' + '<p><cite><strong>' + @by + '</strong></cite></p>'
101
-      end
102
-    end
103
-  end
104
-end
105
-
106
-Liquid::Template.register_tag('blockquote', Jekyll::Blockquote)
107
-Liquid::Template.register_tag('pullquote', Jekyll::Pullquote)
108
-
109
-
110 1
deleted file mode 100644
... ...
@@ -1,65 +0,0 @@
1
-module Jekyll
2
-
3
-  class CategoryIndex < Page
4
-    def initialize(site, base, dir, category)
5
-      @site = site
6
-      @base = base
7
-      @dir = dir
8
-      @name = 'index.html'
9
-
10
-      self.process(@name)
11
-      self.read_yaml(File.join(base, '_layouts'), 'category_index.html')
12
-      self.data['category'] = category
13
-
14
-      category_title_prefix = site.config['category_title_prefix'] || 'Category: '
15
-      self.data['title'] = "#{category_title_prefix}#{category}"
16
-    end
17
-  end
18
-
19
-  class CategoryList < Page
20
-    def initialize(site,  base, dir, categories)
21
-      @site = site
22
-      @base = base
23
-      @dir = dir
24
-      @name = 'index.html'
25
-
26
-      self.process(@name)
27
-      self.read_yaml(File.join(base, '_layouts'), 'category_list.html')
28
-      self.data['categories'] = categories
29
-    end
30
-  end
31
-
32
-  class CategoryGenerator < Generator
33
-    safe true
34
-
35
-    def generate(site)
36
-      if site.layouts.key? 'category_index'
37
-        dir = site.config['category_dir'] || 'categories'
38
-        site.categories.keys.each do |category|
39
-          write_category_index(site, File.join(dir, category.gsub(/\s/, "-").gsub(/[^\w-]/, '').downcase), category)
40
-        end
41
-      end
42
-
43
-      if site.layouts.key? 'category_list'
44
-        dir = site.config['category_dir'] || 'categories'
45
-        write_category_list(site, dir, site.categories.keys.sort)
46
-      end
47
-    end
48
-
49
-    def write_category_index(site, dir, category)
50
-      index = CategoryIndex.new(site, site.source, dir, category)
51
-      index.render(site.layouts, site.site_payload)
52
-      index.write(site.dest)
53
-      site.static_files << index
54
-    end
55
-
56
-    def write_category_list(site, dir, categories)
57
-      index = CategoryList.new(site, site.source, dir, categories)
58
-      index.render(site.layouts, site.site_payload)
59
-      index.write(site.dest)
60
-      site.static_files << index
61
-    end
62
-  end
63
-
64
-end
65
-
66 1
deleted file mode 100644
... ...
@@ -1 +0,0 @@
1
-system "compass compile --css-dir source/stylesheets"
2 1
deleted file mode 100644
... ...
@@ -1,59 +0,0 @@
1
-#custom filters for Octopress
2
-
3
-module OctopressFilters
4
-  def exerpt(input, url, url_text="Reade more&hellip;", permalink_text=false)
5
-    if input.index(/<!--\s?more\s?-->/i)
6
-      input.split(/<!--\s?more\s?-->/i)[0] + "<p><a href='#{url}'>#{url_text}</a></p>"
7
-    elsif permalink_text
8
-      input + "<p><a href='#{url}'>#{permalink_text}</a></p>"
9
-    else
10
-      input
11
-    end
12
-  end
13
-  def full_urls(input, url='')
14
-    input.gsub /(\s+(href|src)\s*=\s*["|']{1})(\/[^\"'>]+)/ do
15
-      $1+url+$3
16
-    end
17
-  end
18
-  def search_url(input)
19
-    input.gsub /(http:\/\/)(\S+)/ do
20
-      $2
21
-    end
22
-  end
23
-  def smart_quotes(input)
24
-    require 'rubypants'
25
-    RubyPants.new(input).to_html
26
-  end
27
-  def titlecase(input)
28
-    input.titlecase
29
-  end
30
-  def datetime(date)
31
-    if date.class == String
32
-      date = Time.parse(date)
33
-    end
34
-    date
35
-  end
36
-  def ordinalize(date)
37
-    date = datetime(date)
38
-    "#{date.strftime('%B')} #{ordinal(date.strftime('%e').to_i)}, #{date.strftime('%Y')}"
39
-  end
40
-  def ordinal(number)
41
-    if (11..13).include?(number.to_i % 100)
42
-      "#{number}<span>th</span>"
43
-    else
44
-      case number.to_i % 10
45
-      when 1; "#{number}<span>st</span>"
46
-      when 2; "#{number}<span>nd<span>"
47
-      when 3; "#{number}<span>rd</span>"
48
-      else    "#{number}<span>th</span>"
49
-      end
50
-    end
51
-  end
52
-  #YearlyPost = Struct.new('YearlyPost', :year, :posts)
53
-  def yearly_posts(site)
54
-    #site.posts.reverse.group_by { |p| p.date.strftime("%Y") }.map { |k,v| YearlyPost.new(k,v) }
55
-    site
56
-  end
57
-end
58
-Liquid::Template.register_filter OctopressFilters
59
-
60 1
deleted file mode 100644
... ...
@@ -1,133 +0,0 @@
1
-# Jekyll sitemap page generator.
2
-# http://recursive-design.com/projects/jekyll-plugins/
3
-#
4
-# Version: 0.1.3 (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 a sitemap.xml page for jekyll sites, suitable for submission to
10
-# google etc.
11
-#
12
-# To use it, simply drop this script into the _plugins directory of your Jekyll site.
13
-#
14
-# When you compile your jekyll site, this plugin will loop through the list of pages in your
15
-# site, and generate an entry in sitemap.xml for each one.
16
-
17
-require 'pathname'
18
-
19
-module Jekyll
20
-
21
-
22
-  # Monkey-patch an accessor for a page's containing folder, since
23
-  # we need it to generate the sitemap.
24
-  class Page
25
-    def subfolder
26
-      @dir
27
-    end
28
-  end
29
-
30
-
31
-  # Sub-class Jekyll::StaticFile to allow recovery from unimportant exception
32
-  # when writing the sitemap file.
33
-  class StaticSitemapFile < StaticFile
34
-    def write(dest)
35
-      super(dest) rescue ArgumentError
36
-      true
37
-    end
38
-  end
39
-
40
-
41
-  # Generates a sitemap.xml file containing URLs of all pages and posts.
42
-  class SitemapGenerator < Generator
43
-    safe true
44
-    priority :low
45
-
46
-    # Domain that you are generating the sitemap for - update this to match your site.
47
-
48
-    # Generates the sitemap.xml file.
49
-    #
50
-    #  +site+ is the global Site object.
51
-    def generate(site)
52
-      # Create the destination folder if necessary.
53
-      site_folder = site.config['destination']
54
-      unless File.directory?(site_folder)
55
-        p = Pathname.new(site_folder)
56
-        p.mkdir
57
-      end
58
-
59
-      # Write the contents of sitemap.xml.
60
-      File.open(File.join(site_folder, 'sitemap.xml'), 'w') do |f|
61
-        f.write(generate_header())
62
-        f.write(generate_content(site))
63
-        f.write(generate_footer())
64
-        f.close
65
-      end
66
-
67
-      # Add a static file entry for the zip file, otherwise Site::cleanup will remove it.
68
-      site.static_files << Jekyll::StaticSitemapFile.new(site, site.dest, '/', 'sitemap.xml')
69
-    end
70
-
71
-    private
72
-
73
-    # Returns the XML header.
74
-    def generate_header
75
-      "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">"
76
-    end
77
-
78
-    # Returns a string containing the the XML entries.
79
-    #
80
-    #  +site+ is the global Site object.
81
-    def generate_content(site)
82
-      result   = ''
83
-
84
-      base_url = site.config['url']
85
-
86
-      # First, try to find any stand-alone pages.
87
-      site.pages.each{ |page|
88
-        path     = page.subfolder + '/' + page.name
89
-        mod_date = File.mtime(site.source + path)
90
-
91
-  			# Remove the trailing 'index.html' if there is one, and just output the folder name.
92
-  			if path=~/index.html$/
93
-  			  path = path[0..-11]
94
-  		  end
95
-
96
-        unless path =~/error/
97
-          result += entry(base_url, path, mod_date)
98
-        end
99
-      }
100
-
101
-      # Next, find all the posts.
102
-      posts = site.site_payload['site']['posts']
103
-      for post in posts do
104
-        result += entry(base_url, post.id, post.date)
105
-      end
106
-
107
-    	result
108
-    end
109
-
110
-    # Returns the XML footer.
111
-    def generate_footer
112
-      "\n</urlset>"
113
-    end
114
-
115
-    # Creates an XML entry from the given path and date.
116
-    #
117
-    #  +path+ is the URL path to the page.
118
-    #  +date+ is the date the file was modified (in the case of regular pages), or published (for blog posts).
119
-    def entry(base_url, path, date)
120
-        # Force extensions to .html from markdown, textile.
121
-        path = path.gsub(/\.(markdown|textile)$/i, '.html')
122
-      "
123
-  <url>
124
-      <loc>#{base_url}#{path}</loc>
125
-      <lastmod>#{date.strftime("%Y-%m-%d")}</lastmod>
126
-  </url>"
127
-    end
128
-
129
-  end
130
-
131
-end
132
-
133
-
134 1
deleted file mode 100644
... ...
@@ -1,83 +0,0 @@
1
-# Nicked from Brandon Tilly
2
-# Gist https://gist.github.com/803483
3
-# Post http://brandontilley.com/2011/01/31/gist-tag-for-jekyll.html
4
-#
5
-# Example usage: {% gist 803483 gist_tag.rb %} //embeds a gist for this plugin
6
-
7
-require 'digest/md5'
8
-require 'net/https'
9
-require 'uri'
10
-
11
-module Jekyll
12
-  class GistTag < Liquid::Tag
13
-    def initialize(tag_name, text, token)
14
-      super
15
-      system('mkdir -p .gist_cache')
16
-      @text         = text
17
-      @cache        = true
18
-      @cache_folder = File.expand_path "../.gist_cache", File.dirname(__FILE__)
19
-    end
20
-
21
-    def render(context)
22
-      return "" unless @text =~ /([\d]*) (.*)/
23
-
24
-      gist, file = $1.strip, $2.strip
25
-      script_url = "https://gist.github.com/#{gist}.js?file=#{file}"
26
-
27
-      code       = get_cached_gist(gist, file) || get_gist_from_web(gist, file)
28
-      code       = code.gsub "<", "&lt;"
29
-      string     = "<script src='#{script_url}'></script>"
30
-      string    += "<noscript><pre><code>#{code}</code></pre></noscript>"
31
-      return string
32
-    end
33
-
34
-    def get_gist_url_for(gist, file)
35
-      "https://gist.github.com/raw/#{gist}/#{file}"
36
-    end
37
-
38
-    def cache_gist(gist, file, data)
39
-      file = get_cache_file_for gist, file
40
-      File.open(file, "w+") do |f|
41
-        f.write(data)
42
-      end
43
-    end
44
-
45
-    def get_cached_gist(gist, file)
46
-      return nil if @cache == false
47
-      file = get_cache_file_for gist, file
48
-      return nil unless File.exist?(file)
49
-      return File.new(file).readlines.join
50
-    end
51
-
52
-    def get_cache_file_for(gist, file)
53
-      gist.gsub! /[^a-zA-Z0-9\-_\.]/, ''
54
-      file.gsub! /[^a-zA-Z0-9\-_\.]/, ''
55
-      md5 = Digest::MD5.hexdigest "#{gist}-#{file}"
56
-      File.join @cache_folder, "#{gist}-#{file}-#{md5}.cache"
57
-    end
58
-
59
-    def get_gist_from_web(gist, file)
60
-      gist_url          = get_gist_url_for(gist, file)
61
-      raw_uri           = URI.parse(gist_url)
62
-      https             = Net::HTTP.new(raw_uri.host, raw_uri.port)
63
-      https.use_ssl     = true
64
-      https.verify_mode = OpenSSL::SSL::VERIFY_NONE
65
-      request           = Net::HTTP::Get.new(raw_uri.request_uri)
66
-      data              = https.request(request)
67
-      data              = data.body
68
-      cache_gist(gist, file, data) unless @cache == false
69
-      data
70
-    end
71
-  end
72
-
73
-  class GistTagNoCache < GistTag
74
-    def initialize(tag_name, text, token)
75
-      super
76
-      @cache = false
77
-    end
78
-  end
79
-end
80
-
81
-Liquid::Template.register_tag('gist', Jekyll::GistTag)
82
-Liquid::Template.register_tag('gistnocache', Jekyll::GistTagNoCache)
83
-
84 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,49 +0,0 @@
1
-##
2
-## Author: Jose Gonzalez - https://github.com/josegonzalez
3
-## Source URL: https://github.com/josegonzalez/josediazgonzalez.com/blob/master/_plugins/iterator.rb
4
-##
5
-
6
-#module Jekyll
7
-  #class Site
8
-    #alias_method :orig_site_payload, :site_payload
9
-
10
-    ## Constuct an array of hashes that will allow the user, using Liquid, to
11
-    ## iterate through the keys of _kv_hash_ and be able to iterate through the
12
-    ## elements under each key.
13
-    ##
14
-    ## Example:
15
-    ##   categories = { 'Ruby' => [<Post>, <Post>] }
16
-    ##   make_iterable(categories, :index => 'name', :items => 'posts')
17
-    ## Will allow the user to iterate through all categories and then iterate
18
-    ## though each post in the current category like so:
19
-    ##   {% for category in site.categories %}
20
-    ##     h1. {{ category.name }}
21
-    ##     <ul>
22
-    ##       {% for post in category.posts %}
23
-    ##         <li>{{ post.title }}</li>
24
-    ##       {% endfor %}
25
-    ##       </ul>
26
-    ##   {% endfor %}
27
-    ##
28
-    ## Returns [ {<index> => <kv_hash_key>, <items> => kv_hash[<kv_hash_key>]}, ... ]
29
-
30
-    #def make_iterable(kv_hash, options)
31
-      #options = {:index => 'name', :items => 'items'}.merge(options)
32
-      #result = []
33
-      #kv_hash.sort.each do |key, value|
34
-        #result << { options[:index] => key, options[:items] => value }
35
-      #end
36
-      #result
37
-    #end
38
-
39
-    #def site_payload
40
-      #payload = orig_site_payload
41
-      #payload['site']['iterable'].merge!({
42
-        #'categories'  => make_iterable(self.categories, :index => 'name', :items => 'posts'),
43
-        #'tags'        => make_iterable(self.tags, :index => 'name', :items => 'posts')
44
-      #})
45
-      #payload
46
-    #end
47
-
48
-  #end
49
-#end
50 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('../../_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,36 +0,0 @@
1
-class String
2
-  def titlecase
3
-    small_words = %w(a an and as at but by en for if in of on or the to v v. via vs vs.)
4
-
5
-    x = split(" ").map do |word|
6
-      # note: word could contain non-word characters!
7
-      # downcase all small_words, capitalize the rest
8
-      small_words.include?(word.gsub(/\W/, "").downcase) ? word.downcase! : word.smart_capitalize!
9
-      word
10
-    end
11
-    # capitalize first and last words
12
-    x.first.to_s.smart_capitalize!
13
-    x.last.to_s.smart_capitalize!
14
-    # small words after colons are capitalized
15
-    x.join(" ").gsub(/:\s?(\W*#{small_words.join("|")}\W*)\s/) { ": #{$1.smart_capitalize} " }
16
-  end
17
-
18
-  def titlecase!
19
-    replace(titlecase)
20
-  end
21
-
22
-  def smart_capitalize
23
-    # ignore any leading crazy characters and capitalize the first real character
24
-    if self =~ /^['"\(\[']*([a-z])/
25
-      i = index($1)
26
-      x = self[i,self.length]
27
-      # word with capitals and periods mid-word are left alone
28
-      self[i,1] = self[i,1].upcase unless x =~ /[A-Z]/ or x =~ /\.\w+/
29
-    end
30
-    self
31
-  end
32
-
33
-  def smart_capitalize!
34
-    replace(smart_capitalize)
35
-  end
36
-end
37 1
new file mode 100644
... ...
@@ -0,0 +1,109 @@
0
+#
1
+# Author: Josediaz Gonzalez - https://github.com/josegonzalez
2
+# Source URL: https://github.com/josegonzalez/josediazgonzalez.com/blob/master/_plugins/blockquote.rb
3
+# Modified by Brandon Mathis
4
+#
5
+require './_plugins/titlecase.rb'
6
+module Jekyll
7
+
8
+  # Outputs a string with a given attribution as a quote
9
+  #
10
+  #   {% blockquote John Paul Jones %}
11
+  #     Monkeys!
12
+  #   {% endblockquote %}
13
+  #   ...
14
+  #   <blockquote>
15
+  #     Monkeys!
16
+  #     <br />
17
+  #     John Paul Jones
18
+  #   </blockquote>
19
+  #
20
+  class Blockquote < Liquid::Block
21
+    FullCiteWithTitle = /([\w\s]+)(https?:\/\/)(\S+\s)([\w\s]+)/i
22
+    FullCite = /([\w\s]+)(https?:\/\/)(\S+)/i
23
+    Author =  /([\w\s]+)/
24
+
25
+    def initialize(tag_name, markup, tokens)
26
+      @by = nil
27
+      @source = nil
28
+      @title = nil
29
+      if markup =~ FullCiteWithTitle
30
+        @by = $1
31
+        @source = $2 + $3
32
+        @title = $4.titlecase
33
+      elsif markup =~ FullCite
34
+        @by = $1
35
+        @source = $2 + $3
36
+      elsif markup =~ Author
37
+        @by = $1
38
+      end
39
+      super
40
+    end
41
+
42
+    def render(context)
43
+      output = super
44
+      if @by.nil?
45
+        '<blockquote><p>' + output.join + '</p></blockquote>'
46
+      elsif !@title.nil?
47
+        '<blockquote><p>' + output.join + '</p></blockquote>' + '<p><cite><strong>' + @by + '</strong>' + '<a class="source" href="' + @source + '">' + @title + '</a></cite></p>'
48
+      elsif !@source.nil?
49
+        '<blockquote><p>' + output.join + '</p></blockquote>' + '<p><cite><strong>' + @by + '</strong>' + '<a class="source" href="' + @source + '">source</a></cite></p>'
50
+      else
51
+        '<blockquote><p>' + output.join + '</p></blockquote>' + '<p><cite><strong>' + @by + '</strong></cite></p>'
52
+      end
53
+    end
54
+  end
55
+
56
+  # Outputs a string with a given attribution as a pullquote
57
+  #
58
+  #   {% blockquote John Paul Jones %}
59
+  #     Monkeys!
60
+  #   {% endblockquote %}
61
+  #   ...
62
+  #   <blockquote class="pullquote">
63
+  #     Monkeys!
64
+  #     <br />
65
+  #     John Paul Jones
66
+  #   </blockquote>
67
+  #
68
+  class Pullquote < Liquid::Block
69
+    FullCiteWithTitle = /([\w\s]+)(http:\/\/|https:\/\/)(\S+)([\w\s]+)/i
70
+    FullCite = /([\w\s]+)(http:\/\/|https:\/\/)(\S+)/i
71
+    Author =  /([\w\s]+)/
72
+
73
+    def initialize(tag_name, markup, tokens)
74
+      @by = nil
75
+      @source = nil
76
+      @title = nil
77
+      if markup =~ FullCiteWithTitle
78
+        @by = $1
79
+        @source = $2 + $3
80
+        @title = $4
81
+      elsif markup =~ FullCite
82
+        @by = $1
83
+        @source = $2 + $3
84
+      elsif markup =~ Author
85
+        @by = $1
86
+      end
87
+      super
88
+    end
89
+
90
+    def render(context)
91
+      output = super
92
+      if @by.nil?
93
+        '<blockquote class="pullquote"><p>' + output.join + '</p></blockquote>'
94
+      elsif @title
95
+        '<blockquote class="pullquote"><p>' + output.join + '</p></blockquote>' + '<p><cite><strong>' + @by + '</strong>' + ' <a class="source" href="' + @source + '">' + @title + '</a></cite></p>'
96
+      elsif @source
97
+        '<blockquote class="pullquote"><p>' + output.join + '</p></blockquote>' + '<p><cite><strong>' + @by + '</strong>' + ' <a class="source" href="' + @source + '">source</a></cite></p>'
98
+      elsif @by
99
+        '<blockquote class="pullquote"><p>' + output.join + '</p></blockquote>' + '<p><cite><strong>' + @by + '</strong></cite></p>'
100
+      end
101
+    end
102
+  end
103
+end
104
+
105
+Liquid::Template.register_tag('blockquote', Jekyll::Blockquote)
106
+Liquid::Template.register_tag('pullquote', Jekyll::Pullquote)
107
+
108
+
0 109
new file mode 100644
... ...
@@ -0,0 +1,65 @@
0
+module Jekyll
1
+
2
+  class CategoryIndex < Page
3
+    def initialize(site, base, dir, category)
4
+      @site = site
5
+      @base = base
6
+      @dir = dir
7
+      @name = 'index.html'
8
+
9
+      self.process(@name)
10
+      self.read_yaml(File.join(base, '_layouts'), 'category_index.html')
11
+      self.data['category'] = category
12
+
13
+      category_title_prefix = site.config['category_title_prefix'] || 'Category: '
14
+      self.data['title'] = "#{category_title_prefix}#{category}"
15
+    end
16
+  end
17
+
18
+  class CategoryList < Page
19
+    def initialize(site,  base, dir, categories)
20
+      @site = site
21
+      @base = base
22
+      @dir = dir
23
+      @name = 'index.html'
24
+
25
+      self.process(@name)
26
+      self.read_yaml(File.join(base, '_layouts'), 'category_list.html')
27
+      self.data['categories'] = categories
28
+    end
29
+  end
30
+
31
+  class CategoryGenerator < Generator
32
+    safe true
33
+
34
+    def generate(site)
35
+      if site.layouts.key? 'category_index'
36
+        dir = site.config['category_dir'] || 'categories'
37
+        site.categories.keys.each do |category|
38
+          write_category_index(site, File.join(dir, category.gsub(/\s/, "-").gsub(/[^\w-]/, '').downcase), category)
39
+        end
40
+      end
41
+
42
+      if site.layouts.key? 'category_list'
43
+        dir = site.config['category_dir'] || 'categories'
44
+        write_category_list(site, dir, site.categories.keys.sort)
45
+      end
46
+    end
47
+
48
+    def write_category_index(site, dir, category)
49
+      index = CategoryIndex.new(site, site.source, dir, category)
50
+      index.render(site.layouts, site.site_payload)
51
+      index.write(site.dest)
52
+      site.static_files << index
53
+    end
54
+
55
+    def write_category_list(site, dir, categories)
56
+      index = CategoryList.new(site, site.source, dir, categories)
57
+      index.render(site.layouts, site.site_payload)
58
+      index.write(site.dest)
59
+      site.static_files << index
60
+    end
61
+  end
62
+
63
+end
64
+
0 65
new file mode 100644
... ...
@@ -0,0 +1 @@
0
+system "compass compile --css-dir source/stylesheets"
0 1
new file mode 100644
... ...
@@ -0,0 +1,59 @@
0
+#custom filters for Octopress
1
+
2
+module OctopressFilters
3
+  def exerpt(input, url, url_text="Reade more&hellip;", permalink_text=false)
4
+    if input.index(/<!--\s?more\s?-->/i)
5
+      input.split(/<!--\s?more\s?-->/i)[0] + "<p><a href='#{url}'>#{url_text}</a></p>"
6
+    elsif permalink_text
7
+      input + "<p><a href='#{url}'>#{permalink_text}</a></p>"
8
+    else
9
+      input
10
+    end
11
+  end
12
+  def full_urls(input, url='')
13
+    input.gsub /(\s+(href|src)\s*=\s*["|']{1})(\/[^\"'>]+)/ do
14
+      $1+url+$3
15
+    end
16
+  end
17
+  def search_url(input)
18
+    input.gsub /(http:\/\/)(\S+)/ do
19
+      $2
20
+    end
21
+  end
22
+  def smart_quotes(input)
23
+    require 'rubypants'
24
+    RubyPants.new(input).to_html
25
+  end
26
+  def titlecase(input)
27
+    input.titlecase
28
+  end
29
+  def datetime(date)
30
+    if date.class == String
31
+      date = Time.parse(date)
32
+    end
33
+    date
34
+  end
35
+  def ordinalize(date)
36
+    date = datetime(date)
37
+    "#{date.strftime('%B')} #{ordinal(date.strftime('%e').to_i)}, #{date.strftime('%Y')}"
38
+  end
39
+  def ordinal(number)
40
+    if (11..13).include?(number.to_i % 100)
41
+      "#{number}<span>th</span>"
42
+    else
43
+      case number.to_i % 10
44
+      when 1; "#{number}<span>st</span>"
45
+      when 2; "#{number}<span>nd<span>"
46
+      when 3; "#{number}<span>rd</span>"
47
+      else    "#{number}<span>th</span>"
48
+      end
49
+    end
50
+  end
51
+  #YearlyPost = Struct.new('YearlyPost', :year, :posts)
52
+  def yearly_posts(site)
53
+    #site.posts.reverse.group_by { |p| p.date.strftime("%Y") }.map { |k,v| YearlyPost.new(k,v) }
54
+    site
55
+  end
56
+end
57
+Liquid::Template.register_filter OctopressFilters
58
+
0 59
new file mode 100644
... ...
@@ -0,0 +1,133 @@
0
+# Jekyll sitemap page generator.
1
+# http://recursive-design.com/projects/jekyll-plugins/
2
+#
3
+# Version: 0.1.3 (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 a sitemap.xml page for jekyll sites, suitable for submission to
9
+# google etc.
10
+#
11
+# To use it, simply drop this script into the _plugins directory of your Jekyll site.
12
+#
13
+# When you compile your jekyll site, this plugin will loop through the list of pages in your
14
+# site, and generate an entry in sitemap.xml for each one.
15
+
16
+require 'pathname'
17
+
18
+module Jekyll
19
+
20
+
21
+  # Monkey-patch an accessor for a page's containing folder, since
22
+  # we need it to generate the sitemap.
23
+  class Page
24
+    def subfolder
25
+      @dir
26
+    end
27
+  end
28
+
29
+
30
+  # Sub-class Jekyll::StaticFile to allow recovery from unimportant exception
31
+  # when writing the sitemap file.
32
+  class StaticSitemapFile < StaticFile
33
+    def write(dest)
34
+      super(dest) rescue ArgumentError
35
+      true
36
+    end
37
+  end
38
+
39
+
40
+  # Generates a sitemap.xml file containing URLs of all pages and posts.
41
+  class SitemapGenerator < Generator
42
+    safe true
43
+    priority :low
44
+
45
+    # Domain that you are generating the sitemap for - update this to match your site.
46
+
47
+    # Generates the sitemap.xml file.
48
+    #
49
+    #  +site+ is the global Site object.
50
+    def generate(site)
51
+      # Create the destination folder if necessary.
52
+      site_folder = site.config['destination']
53
+      unless File.directory?(site_folder)
54
+        p = Pathname.new(site_folder)
55
+        p.mkdir
56
+      end
57
+
58
+      # Write the contents of sitemap.xml.
59
+      File.open(File.join(site_folder, 'sitemap.xml'), 'w') do |f|
60
+        f.write(generate_header())
61
+        f.write(generate_content(site))
62
+        f.write(generate_footer())
63
+        f.close
64
+      end
65
+
66
+      # Add a static file entry for the zip file, otherwise Site::cleanup will remove it.
67
+      site.static_files << Jekyll::StaticSitemapFile.new(site, site.dest, '/', 'sitemap.xml')
68
+    end
69
+
70
+    private
71
+
72
+    # Returns the XML header.
73
+    def generate_header
74
+      "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">"
75
+    end
76
+
77
+    # Returns a string containing the the XML entries.
78
+    #
79
+    #  +site+ is the global Site object.
80
+    def generate_content(site)
81
+      result   = ''
82
+
83
+      base_url = site.config['url']
84
+
85
+      # First, try to find any stand-alone pages.
86
+      site.pages.each{ |page|
87
+        path     = page.subfolder + '/' + page.name
88
+        mod_date = File.mtime(site.source + path)
89
+
90
+  			# Remove the trailing 'index.html' if there is one, and just output the folder name.
91
+  			if path=~/index.html$/
92
+  			  path = path[0..-11]
93
+  		  end
94
+
95
+        unless path =~/error/
96
+          result += entry(base_url, path, mod_date)
97
+        end
98
+      }
99
+
100
+      # Next, find all the posts.
101
+      posts = site.site_payload['site']['posts']
102
+      for post in posts do
103
+        result += entry(base_url, post.id, post.date)
104
+      end
105
+
106
+    	result
107
+    end
108
+
109
+    # Returns the XML footer.
110
+    def generate_footer
111
+      "\n</urlset>"
112
+    end
113
+
114
+    # Creates an XML entry from the given path and date.
115
+    #
116
+    #  +path+ is the URL path to the page.
117
+    #  +date+ is the date the file was modified (in the case of regular pages), or published (for blog posts).
118
+    def entry(base_url, path, date)
119
+        # Force extensions to .html from markdown, textile.
120
+        path = path.gsub(/\.(markdown|textile)$/i, '.html')
121
+      "
122
+  <url>
123
+      <loc>#{base_url}#{path}</loc>
124
+      <lastmod>#{date.strftime("%Y-%m-%d")}</lastmod>
125
+  </url>"
126
+    end
127
+
128
+  end
129
+
130
+end
131
+
132
+
0 133
new file mode 100644
... ...
@@ -0,0 +1,83 @@
0
+# Nicked from Brandon Tilly
1
+# Gist https://gist.github.com/803483
2
+# Post http://brandontilley.com/2011/01/31/gist-tag-for-jekyll.html
3
+#
4
+# Example usage: {% gist 803483 gist_tag.rb %} //embeds a gist for this plugin
5
+
6
+require 'digest/md5'
7
+require 'net/https'
8
+require 'uri'
9
+
10
+module Jekyll
11
+  class GistTag < Liquid::Tag
12
+    def initialize(tag_name, text, token)
13
+      super
14
+      system('mkdir -p .gist_cache')
15
+      @text         = text
16
+      @cache        = true
17
+      @cache_folder = File.expand_path "../.gist_cache", File.dirname(__FILE__)
18
+    end
19
+
20
+    def render(context)
21
+      return "" unless @text =~ /([\d]*) (.*)/
22
+
23
+      gist, file = $1.strip, $2.strip
24
+      script_url = "https://gist.github.com/#{gist}.js?file=#{file}"
25
+
26
+      code       = get_cached_gist(gist, file) || get_gist_from_web(gist, file)
27
+      code       = code.gsub "<", "&lt;"
28
+      string     = "<script src='#{script_url}'></script>"
29
+      string    += "<noscript><pre><code>#{code}</code></pre></noscript>"
30
+      return string
31
+    end
32
+
33
+    def get_gist_url_for(gist, file)
34
+      "https://gist.github.com/raw/#{gist}/#{file}"
35
+    end
36
+
37
+    def cache_gist(gist, file, data)
38
+      file = get_cache_file_for gist, file
39
+      File.open(file, "w+") do |f|
40
+        f.write(data)
41
+      end
42
+    end
43
+
44
+    def get_cached_gist(gist, file)
45
+      return nil if @cache == false
46
+      file = get_cache_file_for gist, file
47
+      return nil unless File.exist?(file)
48
+      return File.new(file).readlines.join
49
+    end
50
+
51
+    def get_cache_file_for(gist, file)
52
+      gist.gsub! /[^a-zA-Z0-9\-_\.]/, ''
53
+      file.gsub! /[^a-zA-Z0-9\-_\.]/, ''
54
+      md5 = Digest::MD5.hexdigest "#{gist}-#{file}"
55
+      File.join @cache_folder, "#{gist}-#{file}-#{md5}.cache"
56
+    end
57
+
58
+    def get_gist_from_web(gist, file)
59
+      gist_url          = get_gist_url_for(gist, file)
60
+      raw_uri           = URI.parse(gist_url)
61
+      https             = Net::HTTP.new(raw_uri.host, raw_uri.port)
62
+      https.use_ssl     = true
63
+      https.verify_mode = OpenSSL::SSL::VERIFY_NONE
64
+      request           = Net::HTTP::Get.new(raw_uri.request_uri)
65
+      data              = https.request(request)
66
+      data              = data.body
67
+      cache_gist(gist, file, data) unless @cache == false
68
+      data
69
+    end
70
+  end
71
+
72
+  class GistTagNoCache < GistTag
73
+    def initialize(tag_name, text, token)
74
+      super
75
+      @cache = false
76
+    end
77
+  end
78
+end
79
+
80
+Liquid::Template.register_tag('gist', Jekyll::GistTag)
81
+Liquid::Template.register_tag('gistnocache', Jekyll::GistTagNoCache)
82
+
0 83
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,49 @@
0
+##
1
+## Author: Jose Gonzalez - https://github.com/josegonzalez
2
+## Source URL: https://github.com/josegonzalez/josediazgonzalez.com/blob/master/_plugins/iterator.rb
3
+##
4
+
5
+#module Jekyll
6
+  #class Site
7
+    #alias_method :orig_site_payload, :site_payload
8
+
9
+    ## Constuct an array of hashes that will allow the user, using Liquid, to
10
+    ## iterate through the keys of _kv_hash_ and be able to iterate through the
11
+    ## elements under each key.
12
+    ##
13
+    ## Example:
14
+    ##   categories = { 'Ruby' => [<Post>, <Post>] }
15
+    ##   make_iterable(categories, :index => 'name', :items => 'posts')
16
+    ## Will allow the user to iterate through all categories and then iterate
17
+    ## though each post in the current category like so:
18
+    ##   {% for category in site.categories %}
19
+    ##     h1. {{ category.name }}
20
+    ##     <ul>
21
+    ##       {% for post in category.posts %}
22
+    ##         <li>{{ post.title }}</li>
23
+    ##       {% endfor %}
24
+    ##       </ul>
25
+    ##   {% endfor %}
26
+    ##
27
+    ## Returns [ {<index> => <kv_hash_key>, <items> => kv_hash[<kv_hash_key>]}, ... ]
28
+
29
+    #def make_iterable(kv_hash, options)
30
+      #options = {:index => 'name', :items => 'items'}.merge(options)
31
+      #result = []
32
+      #kv_hash.sort.each do |key, value|
33
+        #result << { options[:index] => key, options[:items] => value }
34
+      #end
35
+      #result
36
+    #end
37
+
38
+    #def site_payload
39
+      #payload = orig_site_payload
40
+      #payload['site']['iterable'].merge!({
41
+        #'categories'  => make_iterable(self.categories, :index => 'name', :items => 'posts'),
42
+        #'tags'        => make_iterable(self.tags, :index => 'name', :items => 'posts')
43
+      #})
44
+      #payload
45
+    #end
46
+
47
+  #end
48
+#end
0 49
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('../../_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,309 @@
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 MY_URL to reflect your domain name.
6
+#   3.) Change SITEMAP_FILE_NAME if you want your sitemap to be called something
7
+#       other than sitemap.xml.
8
+#   4.) Change the PAGES_INCLUDE_POSTS list to include any pages that are looping
9
+#       through your posts (e.g. "index.html", "archive.html", etc.). This will
10
+#       ensure that right after you make a new post, the last modified date will
11
+#       be updated to reflect the new post.
12
+#   5.) Run Jekyll: jekyll --server to re-generate your site.
13
+#   6.) A sitemap.xml should be included in your _site folder.
14
+#
15
+# Customizations:
16
+#   1.) 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
+#   2.) 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
+#   1.) 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
+require 'rexml/document'
37
+
38
+module Jekyll
39
+
40
+  # Change MY_URL to reflect the site you are using
41
+  MY_URL = "http://www.mysite.com"
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
+      "#{MY_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 = "#{MY_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
+
0 309
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