require 'nuggets/range/quantile'
require 'erb'
module Jekyll
class Tagger < Generator
safe true
attr_accessor :site
@types = [:page, :feed]
class << self; attr_accessor :types, :site; end
def generate(site)
self.class.site = self.site = site
generate_tag_pages
add_tag_cloud
end
private
# Generates a page per tag and adds them to all the pages of +site+.
# A tag_page_layout have to be defined in your _config.yml
# to use this.
def generate_tag_pages
active_tags.each { |tag, posts| new_tag(tag, posts) }
end
def new_tag(tag, posts)
self.class.types.each do |type|
if layout = site.config["tag_#{type}_layout"]
data = { 'layout' => layout, 'posts' => posts.sort.reverse!, 'tag' => tag, 'title' => tag }
name = yield data if block_given?
name ||= tag
tag_dir = site.config["tag_#{type}_dir"]
tag_dir = File.join(tag_dir, (pretty? ? name : ''))
page_name = "#{pretty? ? 'index' : name}#{site.layouts[data['layout']].ext}"
site.pages << TagPage.new(
site, site.source, tag_dir, page_name, data
)
end
end
end
def add_tag_cloud(num = 5, name = 'tag_data')
s, t = site, { name => calculate_tag_cloud(num) }
s.respond_to?(:add_payload) ? s.add_payload(t) : s.config.update(t)
end
# Calculates the css class of every tag for a tag cloud. The possible
# classes are: set-1..set-5.
#
# [[, ], ...]
def calculate_tag_cloud(num = 5)
range = 0
tags = active_tags.map do |tag, posts|
[tag.to_s, range < (size = posts.size) ? range = size : size]
end
range = 1..range
tags.sort!.map! { |tag, size| [tag, range.quantile(size, num)] }
end
def active_tags
return site.tags unless site.config['ignored_tags']
site.tags.reject { |t| site.config['ignored_tags'].include? t[0] }
end
def pretty?
@pretty ||= (site.permalink_style == :pretty || site.config['tag_permalink_style'] == 'pretty')
end
end
class TagPage < Page
def initialize(site, base, dir, name, data = {})
self.content = data.delete('content') || ''
self.data = data
super(site, base, dir[-1, 1] == '/' ? dir : '/' + dir, name)
end
def read_yaml(*)
# Do nothing
end
end
module Filters
def tag_cloud(site)
active_tag_data.map do |tag, set|
tag_link(tag, tag_url(tag), :class => "set-#{set} label label-default")
end.join(' ')
end
def tag_link(tag, url = tag_url(tag), html_opts = nil)
html_opts &&= ' ' << html_opts.map { |k, v| %Q{#{k}="#{v}"} }.join(' ')
%Q{#{tag}}
end
def tag_url(tag, type = :page, site = Tagger.site)
# FIXME generate full url for atom.xml page
url = File.join('', site.config["tag_#{type}_dir"], ERB::Util.u(tag))
site.permalink_style == :pretty || site.config['tag_permalink_style'] == 'pretty' ? url : url << '.html'
end
def tags(obj)
tags = obj['tags'].dup
tags.map! { |t| t.first } if tags.first.is_a?(Array)
tags.map! { |t| tag_link(t, tag_url(t), rel: 'tag', class: 'label label-default') if t.is_a?(String) }.compact!
tags.join(' ')
end
def keywords(obj)
return '' if not obj['tags']
tags = obj['tags'].dup
tags.join(',')
end
def active_tag_data(site = Tagger.site)
return site.config['tag_data'] unless site.config['ignored_tags']
site.config['tag_data'].reject { |tag, set| site.config['ignored_tags'].include? tag }
end
end
end