class Fuse::Document

Attributes

source_path[R]
xsl_path[R]

Public Class Methods

new(options) click to toggle source
# File lib/fuse/document.rb, line 7
def initialize(options)
  source = (@options = options)[:source]
  raise Fuse::Exception::SourceUnknown if source.nil?
  raise Fuse::Exception::SourceUnknown::NotFound.new(source) unless File.exists?(source)
  @source_path = expect_one(potential_sources, :source, Fuse::Exception::SourceUnknown)
  @xsl_path = expect_one(potential_xsl, :xsl, Fuse::Exception::XslMissing) if source_xml?
end

Public Instance Methods

result() click to toggle source
# File lib/fuse/document.rb, line 21
def result

  document = if xsl_path
    Nokogiri::XSLT(File.read xsl_path).transform(Nokogiri(File.read source_path))
  else
    Nokogiri::HTML(File.read source_path)
  end

  while (html = document.css('> html').first).nil?
    document = Nokogiri::HTML(document.to_html encoding: @options[:encoding])
  end
  body = html.css('> body').first
  head = html.css('> head').first || body.add_previous_sibling(Nokogiri::XML::Node.new 'head', document)

  unless @options[:title].nil?
    title = head.css('> title').first || head.add_child(Nokogiri::XML::Node.new('title', document))
    title.children = Nokogiri::XML::Text.new(@options[:title], document)
  end

  #add favicon
  assets.of_type(Fuse::Document::Asset::Image).select{ |a| a.path.match %r`\bfavicon\.\w+$` }.each do |asset|
    head << link = Nokogiri::XML::Node.new('link', document)
    link['rel'] = 'shortcut icon'
    link['href'] = asset.relative_path
  end

  #attach scripts
  scripts = assets.of_type(Fuse::Document::Asset::JavaScript).sort!
  head << tag_for_collection(scripts, document) unless scripts.empty?

  #attach stylesheets
  style_sheets = assets.of_type(Fuse::Document::Asset::StyleSheet)
  style_sheets.group_by(&:conditional_signature).each_value do |conditional_collection|
    conditional_collection.group_by(&:media).each do |media, media_collection|
      media_collection.sort!
      tag = tag_for_collection(media_collection, document)
      tag['media'] = media unless media.nil? || media.empty? || Nokogiri::XML::NodeSet === tag
      head << Nokogiri::HTML.fragment(media_collection.first.conditional.wrap(tag.to_html(encoding: @options[:encoding])), @options[:encoding])
    end
  end

  #create font stylesheet
  font_css = ''
  fonts = {}
  assets.of_type(Fuse::Document::Asset::Font).each do |asset|
    (fonts[asset.face] ||= {})[asset.extension.to_sym] = asset
  end
  if fonts.length > 0
    fonts.values.each do |formats|
      first = formats.values.first
      font_css << '@font-face{'
      font_css << first.variant.map{ |k, v| 'font-%s: "%s";' % [k, v] }.join
      font_css << 'src: url("%s");'    % formats[:eot].relative_path if formats[:eot] && !@options[:embed_assets]
      css_formats = []
      Fuse::Document::Asset::Font::CSS_FORMATS.each do |css_format|
        css_formats << 'url("%s") format("%s")' % [
            formats[css_format[:extension]].relative_path,
            css_format[:format]
        ] if formats[css_format[:extension]]
      end
      font_css << 'src: %s;' % css_formats.join(', ') if css_formats.any?
      font_css << '}'
    end
  end
  unless font_css.empty?
    style_node = head.add_child(Nokogiri::XML::Node.new 'style', document)
    style_node.content = font_css + style_node.content
  end

  #embed images and fonts
  if @options[:embed_assets]
    %w|@src @href @style style|.each do |xpath|
      document.xpath('//' + xpath).each do |node|
        assets.of_type(Fuse::Document::Asset::Image, Fuse::Document::Asset::Font).each do |asset|
          node.content = node.content.gsub asset.relative_path, asset.to_datauri
        end
      end
    end
  end

  document
end
root() click to toggle source
# File lib/fuse/document.rb, line 104
def root
  @root ||= File.directory?((opt = @options[:source])) ? opt : File.dirname(opt)
end
to_s() click to toggle source
# File lib/fuse/document.rb, line 15
def to_s
  ret = result.to_html encoding: @options[:encoding], indent: 0
  ret.gsub!(/(\s)\s+/, '\\1') unless @options[:preserve_white]
  ret
end

Private Instance Methods

assets() click to toggle source
# File lib/fuse/document.rb, line 140
def assets
  @assets ||= Asset[root]
end
expect_one(list, option, missing_exception) click to toggle source
# File lib/fuse/document.rb, line 134
def expect_one(list, option, missing_exception)
  raise missing_exception if list.empty?
  raise Fuse::Exception::SourceUnknown::TooManySources.new(option, list) if list.length > 1
  list.first
end
potential_sources() click to toggle source
# File lib/fuse/document.rb, line 118
def potential_sources
  @potential_docs ||= begin
    extensions = %w|html htm|
    extensions << 'xml' if potential_xsl.any?
    potentials(:source, extensions)
  end
end
potential_xsl() click to toggle source
# File lib/fuse/document.rb, line 114
def potential_xsl
  @potential_xsl ||= potentials(:xsl, %w(xsl))
end
potentials(option, extensions) click to toggle source
# File lib/fuse/document.rb, line 126
def potentials(option, extensions)
  if (option_value = @options[option])
    raise Fuse::Exception::SourceUnknown::NotFound.new(option_value) unless File.exists?(option_value)
    return [option_value] unless File.directory? option_value
  end
  Dir[File.join(option_value || root, "**/*.{#{extensions.join(',')}}")].select{ |f| File.size? f }
end
source_xml?() click to toggle source
# File lib/fuse/document.rb, line 110
def source_xml?
  source_path.match(/\.xml$/i)
end
tag_for_collection(collection, document) click to toggle source
# File lib/fuse/document.rb, line 144
def tag_for_collection(collection, document)
  return if collection.empty?
  klass = collection.first.class
  if @options[:embed_assets]
    tag = Nokogiri::XML::Node.new(klass::EMBED_WITH, document)
    raw = collection.map do |asset|
      tag['type'] = asset.type
      (@options[:compress_assets] ? asset.compress : asset.filtered).strip
    end.reject{ |x| x.length == 0 }.join(klass::JOIN_WITH)
    return unless raw.length > 0
    tag.content = raw
    tag
  else
    Nokogiri::XML::NodeSet.new(document, collection.map do |asset|
      data = asset.reference_with
      tag = Nokogiri::XML::Node.new(data[:tag_name], document)
      data[:attributes].each { |k, v| tag[k] = v unless v.nil? }
      tag
    end)
  end
end