Chris@0: module CodeRay Chris@0: module Encoders Chris@0: Chris@0: class HTML Chris@0: Chris@0: # This module is included in the output String from thew HTML Encoder. Chris@0: # Chris@0: # It provides methods like wrap, div, page etc. Chris@0: # Chris@0: # Remember to use #clone instead of #dup to keep the modules the object was Chris@0: # extended with. Chris@0: # Chris@0: # TODO: more doc. Chris@0: module Output Chris@0: Chris@0: require 'coderay/encoders/html/numerization.rb' Chris@0: Chris@0: attr_accessor :css Chris@0: Chris@0: class << self Chris@0: Chris@0: # This makes Output look like a class. Chris@0: # Chris@0: # Example: Chris@0: # Chris@0: # a = Output.new 'Code' Chris@0: # a.wrap! :page Chris@0: def new string, css = CSS.new, element = nil Chris@0: output = string.clone.extend self Chris@0: output.wrapped_in = element Chris@0: output.css = css Chris@0: output Chris@0: end Chris@0: Chris@0: # Raises an exception if an object that doesn't respond to to_str is extended by Output, Chris@0: # to prevent users from misuse. Use Module#remove_method to disable. Chris@0: def extended o Chris@0: warn "The Output module is intended to extend instances of String, not #{o.class}." unless o.respond_to? :to_str Chris@0: end Chris@0: Chris@0: def make_stylesheet css, in_tag = false Chris@0: sheet = css.stylesheet Chris@0: sheet = <<-CSS if in_tag Chris@0: Chris@0: CSS Chris@0: sheet Chris@0: end Chris@0: Chris@0: def page_template_for_css css Chris@0: sheet = make_stylesheet css Chris@0: PAGE.apply 'CSS', sheet Chris@0: end Chris@0: Chris@0: # Define a new wrapper. This is meta programming. Chris@0: def wrapper *wrappers Chris@0: wrappers.each do |wrapper| Chris@0: define_method wrapper do |*args| Chris@0: wrap wrapper, *args Chris@0: end Chris@0: define_method "#{wrapper}!".to_sym do |*args| Chris@0: wrap! wrapper, *args Chris@0: end Chris@0: end Chris@0: end Chris@0: Chris@0: end Chris@0: Chris@0: wrapper :div, :span, :page Chris@0: Chris@0: def wrapped_in? element Chris@0: wrapped_in == element Chris@0: end Chris@0: Chris@0: def wrapped_in Chris@0: @wrapped_in ||= nil Chris@0: end Chris@0: attr_writer :wrapped_in Chris@0: Chris@0: def wrap_in template Chris@0: clone.wrap_in! template Chris@0: end Chris@0: Chris@0: def wrap_in! template Chris@0: Template.wrap! self, template, 'CONTENT' Chris@0: self Chris@0: end Chris@0: Chris@0: def apply_title! title Chris@0: self.sub!(/()(<\/title>)/) { $1 + title + $2 } Chris@0: self Chris@0: end Chris@0: Chris@0: def wrap! element, *args Chris@0: return self if not element or element == wrapped_in Chris@0: case element Chris@0: when :div Chris@0: raise "Can't wrap %p in %p" % [wrapped_in, element] unless wrapped_in? nil Chris@0: wrap_in! DIV Chris@0: when :span Chris@0: raise "Can't wrap %p in %p" % [wrapped_in, element] unless wrapped_in? nil Chris@0: wrap_in! SPAN Chris@0: when :page Chris@0: wrap! :div if wrapped_in? nil Chris@0: raise "Can't wrap %p in %p" % [wrapped_in, element] unless wrapped_in? :div Chris@0: wrap_in! Output.page_template_for_css(@css) Chris@0: if args.first.is_a?(Hash) && title = args.first[:title] Chris@0: apply_title! title Chris@0: end Chris@0: self Chris@0: when nil Chris@0: return self Chris@0: else Chris@0: raise "Unknown value %p for :wrap" % element Chris@0: end Chris@0: @wrapped_in = element Chris@0: self Chris@0: end Chris@0: Chris@0: def wrap *args Chris@0: clone.wrap!(*args) Chris@0: end Chris@0: Chris@0: def stylesheet in_tag = false Chris@0: Output.make_stylesheet @css, in_tag Chris@0: end Chris@0: Chris@0: class Template < String Chris@0: Chris@0: def self.wrap! str, template, target Chris@0: target = Regexp.new(Regexp.escape("<%#{target}%>")) Chris@0: if template =~ target Chris@0: str[0,0] = $` Chris@0: str << $' Chris@0: else Chris@0: raise "Template target <%%%p%%> not found" % target Chris@0: end Chris@0: end Chris@0: Chris@0: def apply target, replacement Chris@0: target = Regexp.new(Regexp.escape("<%#{target}%>")) Chris@0: if self =~ target Chris@0: Template.new($` + replacement + $') Chris@0: else Chris@0: raise "Template target <%%%p%%> not found" % target Chris@0: end Chris@0: end Chris@0: Chris@0: module Simple Chris@0: def ` str #` <-- for stupid editors Chris@0: Template.new str Chris@0: end Chris@0: end Chris@0: end Chris@0: Chris@0: extend Template::Simple Chris@0: Chris@0: #-- don't include the templates in docu Chris@0: Chris@0: SPAN = `<span class="CodeRay"><%CONTENT%></span>` Chris@0: Chris@0: DIV = <<-`DIV` Chris@0: <div class="CodeRay"> Chris@0: <div class="code"><pre><%CONTENT%></pre></div> Chris@0: </div> Chris@0: DIV Chris@0: Chris@0: TABLE = <<-`TABLE` Chris@0: <table class="CodeRay"><tr> Chris@0: <td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"><pre><%LINE_NUMBERS%></pre></td> Chris@0: <td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"><%CONTENT%></pre></td> Chris@0: </tr></table> Chris@0: TABLE Chris@0: # title="double click to expand" Chris@0: Chris@0: LIST = <<-`LIST` Chris@0: <ol class="CodeRay"> Chris@0: <%CONTENT%> Chris@0: </ol> Chris@0: LIST Chris@0: Chris@0: PAGE = <<-`PAGE` Chris@0: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" Chris@0: "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> Chris@0: <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="de"> Chris@0: <head> Chris@0: <meta http-equiv="content-type" content="text/html; charset=utf-8" /> Chris@0: <title> Chris@0: Chris@0: Chris@0: Chris@0: Chris@0: <%CONTENT%> Chris@0: Chris@0: Chris@0: PAGE Chris@0: Chris@0: end Chris@0: Chris@0: end Chris@0: Chris@0: end Chris@0: end