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