Permalink
Browse files

- merb-gen app .

Signed-off-by: damm <damm@livid.dk>
  • Loading branch information...
1 parent 380f72b commit b68289eb38d5420db2c8904c7a39e73c8cc531fe damm committed Oct 21, 2008
View
12 config/rack.rb
@@ -0,0 +1,12 @@
+
+# use PathPrefix Middleware if :path_prefix is set in Merb::Config
+if prefix = ::Merb::Config[:path_prefix]
+ use Merb::Rack::PathPrefix, prefix
+end
+
+# comment this out if you are running merb behind a load balancer
+# that serves static files
+use Merb::Rack::Static, Merb.dir_for(:public)
+
+# this is our main merb application
+run Merb::Rack::Application.new
View
1,362 doc/rdoc/generators/merb_generator.rb
@@ -0,0 +1,1362 @@
+
+# # We're responsible for generating all the HTML files
+# from the object tree defined in code_objects.rb. We
+# generate:
+#
+# [files] an html file for each input file given. These
+# input files appear as objects of class
+# TopLevel
+#
+# [classes] an html file for each class or module encountered.
+# These classes are not grouped by file: if a file
+# contains four classes, we'll generate an html
+# file for the file itself, and four html files
+# for the individual classes.
+#
+# Method descriptions appear in whatever entity (file, class,
+# or module) that contains them.
+#
+# We generate files in a structure below a specified subdirectory,
+# normally +doc+.
+#
+# opdir
+# |
+# |___ files
+# | |__ per file summaries
+# |
+# |___ classes
+# |__ per class/module descriptions
+#
+# HTML is generated using the Template class.
+#
+
+require 'ftools'
+
+require 'rdoc/options'
+require 'rdoc/template'
+require 'rdoc/markup/simple_markup'
+require 'rdoc/markup/simple_markup/to_html'
+require 'cgi'
+
+module Generators
+
+ # Name of sub-direcories that hold file and class/module descriptions
+
+ FILE_DIR = "files"
+ CLASS_DIR = "classes"
+ CSS_NAME = "stylesheet.css"
+
+
+ ##
+ # Build a hash of all items that can be cross-referenced.
+ # This is used when we output required and included names:
+ # if the names appear in this hash, we can generate
+ # an html cross reference to the appropriate description.
+ # We also use this when parsing comment blocks: any decorated
+ # words matching an entry in this list are hyperlinked.
+
+ class AllReferences
+ @@refs = {}
+
+ def AllReferences::reset
+ @@refs = {}
+ end
+
+ def AllReferences.add(name, html_class)
+ @@refs[name] = html_class
+ end
+
+ def AllReferences.[](name)
+ @@refs[name]
+ end
+
+ def AllReferences.keys
+ @@refs.keys
+ end
+ end
+
+
+ ##
+ # Subclass of the SM::ToHtml class that supports looking
+ # up words in the AllReferences list. Those that are
+ # found (like AllReferences in this comment) will
+ # be hyperlinked
+
+ class HyperlinkHtml < SM::ToHtml
+ # We need to record the html path of our caller so we can generate
+ # correct relative paths for any hyperlinks that we find
+ def initialize(from_path, context)
+ super()
+ @from_path = from_path
+
+ @parent_name = context.parent_name
+ @parent_name += "::" if @parent_name
+ @context = context
+ end
+
+ # We're invoked when any text matches the CROSSREF pattern
+ # (defined in MarkUp). If we fine the corresponding reference,
+ # generate a hyperlink. If the name we're looking for contains
+ # no punctuation, we look for it up the module/class chain. For
+ # example, HyperlinkHtml is found, even without the Generators::
+ # prefix, because we look for it in module Generators first.
+
+ def handle_special_CROSSREF(special)
+ name = special.text
+ if name[0,1] == '#'
+ lookup = name[1..-1]
+ name = lookup unless Options.instance.show_hash
+ else
+ lookup = name
+ end
+
+ if /([A-Z].*)[.\#](.*)/ =~ lookup
+ container = $1
+ method = $2
+ ref = @context.find_symbol(container, method)
+ else
+ ref = @context.find_symbol(lookup)
+ end
+
+ if ref and ref.document_self
+ "<a href=\"index.html?a=#{ref.aref}&name=#{name}\">#{name}</a>"
+ else
+ name #it does not need to be a link
+ end
+ end
+
+
+ # Generate a hyperlink for url, labeled with text. Handle the
+ # special cases for img: and link: described under handle_special_HYPEDLINK
+ def gen_url(url, text)
+ if url =~ /([A-Za-z]+):(.*)/
+ type = $1
+ path = $2
+ else
+ type = "http"
+ path = url
+ url = "http://#{url}"
+ end
+
+ if type == "link"
+ url = path
+ end
+
+ if (type == "http" || type == "link") && url =~ /\.(gif|png|jpg|jpeg|bmp)$/
+ "<img src=\"#{url}\">"
+ elsif (type == "http" || type == "link")
+ "<a href=\"#{url}\" target=\"_blank\">#{text}</a>"
+ else
+ "<a href=\"#\" onclick=\"jsHref('#{url}');\">#{text.sub(%r{^#{type}:/*}, '')}</a>"
+
+ end
+ end
+
+ # And we're invoked with a potential external hyperlink mailto:
+ # just gets inserted. http: links are checked to see if they
+ # reference an image. If so, that image gets inserted using an
+ # <img> tag. Otherwise a conventional <a href> is used. We also
+ # support a special type of hyperlink, link:, which is a reference
+ # to a local file whose path is relative to the --op directory.
+
+ def handle_special_HYPERLINK(special)
+ url = special.text
+ gen_url(url, url)
+ end
+
+ # HEre's a hypedlink where the label is different to the URL
+ # <label>[url]
+ #
+
+ def handle_special_TIDYLINK(special)
+ text = special.text
+ # unless text =~ /(\S+)\[(.*?)\]/
+ unless text =~ /\{(.*?)\}\[(.*?)\]/ or text =~ /(\S+)\[(.*?)\]/
+ return text
+ end
+ label = $1
+ url = $2
+ gen_url(url, label)
+ end
+
+ end
+
+
+
+ #####################################################################
+ #
+ # Handle common markup tasks for the various Html classes
+ #
+
+ module MarkUp
+
+ # Convert a string in markup format into HTML. We keep a cached
+ # SimpleMarkup object lying around after the first time we're
+ # called per object.
+
+ def markup(str, remove_para=false)
+ return '' unless str
+ unless defined? @markup
+ @markup = SM::SimpleMarkup.new
+
+ # class names, variable names, file names, or instance variables
+ @markup.add_special(/(
+ \b([A-Z]\w*(::\w+)*[.\#]\w+) # A::B.meth
+ | \b([A-Z]\w+(::\w+)*) # A::B..
+ | \#\w+[!?=]? # #meth_name
+ | \b\w+([_\/\.]+\w+)+[!?=]? # meth_name
+ )/x, :CROSSREF)
+
+ # external hyperlinks
+ @markup.add_special(/((link:|https?:|mailto:|ftp:|www\.)\S+\w)/, :HYPERLINK)
+
+ # and links of the form <text>[<url>]
+ @markup.add_special(/(((\{.*?\})|\b\S+?)\[\S+?\.\S+?\])/, :TIDYLINK)
+ # @markup.add_special(/\b(\S+?\[\S+?\.\S+?\])/, :TIDYLINK)
+
+ end
+ unless defined? @html_formatter
+ @html_formatter = HyperlinkHtml.new(self.path, self)
+ end
+
+ # Convert leading comment markers to spaces, but only
+ # if all non-blank lines have them
+
+ if str =~ /^(?>\s*)[^\#]/
+ content = str
+ else
+ content = str.gsub(/^\s*(#+)/) { $1.tr('#',' ') }
+ end
+
+ res = @markup.convert(content, @html_formatter)
+ if remove_para
+ res.sub!(/^<p>/, '')
+ res.sub!(/<\/p>$/, '')
+ end
+ res
+ end
+
+
+ def style_url(path, css_name=nil)
+ css_name ||= CSS_NAME
+ end
+
+ # Build a webcvs URL with the given 'url' argument. URLs with a '%s' in them
+ # get the file's path sprintfed into them; otherwise they're just catenated
+ # together.
+
+ def cvs_url(url, full_path)
+ if /%s/ =~ url
+ return sprintf( url, full_path )
+ else
+ return url + full_path
+ end
+ end
+ end
+
+
+ #####################################################################
+ #
+ # A Context is built by the parser to represent a container: contexts
+ # hold classes, modules, methods, require lists and include lists.
+ # ClassModule and TopLevel are the context objects we process here
+ #
+ class ContextUser
+
+ include MarkUp
+
+ attr_reader :context
+
+ def initialize(context, options)
+ @context = context
+ @options = options
+
+ end
+
+ # convenience method to build a hyperlink # Where's the DRY in this?? Put this in the template where it belongs
+ def href(link, cls, name)
+ %{"<a href=\"#\" onclick=\"jsHref('#{link}');\" class=\"#{cls}\">#{name}</a>"}
+ end
+
+ # Create a list of HtmlMethod objects for each method
+ # in the corresponding context object. If the @options.show_all
+ # variable is set (corresponding to the <tt>--all</tt> option,
+ # we include all methods, otherwise just the public ones.
+
+ def collect_methods
+ list = @context.method_list
+ unless @options.show_all
+ list = list.find_all {|m| m.visibility == :public || m.visibility == :protected || m.force_documentation }
+ end
+ @methods = list.collect {|m| HtmlMethod.new(m, self, @options) }
+ end
+
+ # Build a summary list of all the methods in this context
+ def build_method_summary_list(path_prefix="")
+ collect_methods unless @methods
+ meths = @methods.sort
+ res = []
+ meths.each do |meth|
+ res << {
+ "name" => CGI.escapeHTML(meth.name),
+ "aref" => meth.aref,
+ "href" => meth.path
+ }
+ end
+ res
+ end
+
+
+ # Build a list of aliases for which we couldn't find a
+ # corresponding method
+ def build_alias_summary_list(section)
+ values = []
+ @context.aliases.each do |al|
+ next unless al.section == section
+ res = {
+ 'old_name' => al.old_name,
+ 'new_name' => al.new_name,
+ }
+ if al.comment && !al.comment.empty?
+ res['desc'] = markup(al.comment, true)
+ end
+ values << res
+ end
+ values
+ end
+
+ # Build a list of constants
+ def build_constants_summary_list(section)
+ values = []
+ @context.constants.each do |co|
+ next unless co.section == section
+ res = {
+ 'name' => co.name,
+ 'value' => CGI.escapeHTML(co.value)
+ }
+ res['desc'] = markup(co.comment, true) if co.comment && !co.comment.empty?
+ values << res
+ end
+ values
+ end
+
+ def build_requires_list(context)
+ potentially_referenced_list(context.requires) {|fn| [fn + ".rb"] }
+ end
+
+ def build_include_list(context)
+ potentially_referenced_list(context.includes)
+ end
+
+ # Build a list from an array of <i>Htmlxxx</i> items. Look up each
+ # in the AllReferences hash: if we find a corresponding entry,
+ # we generate a hyperlink to it, otherwise just output the name.
+ # However, some names potentially need massaging. For example,
+ # you may require a Ruby file without the .rb extension,
+ # but the file names we know about may have it. To deal with
+ # this, we pass in a block which performs the massaging,
+ # returning an array of alternative names to match
+
+ def potentially_referenced_list(array)
+ res = []
+ array.each do |i|
+ ref = AllReferences[i.name]
+ # if !ref
+ # container = @context.parent
+ # while !ref && container
+ # name = container.name + "::" + i.name
+ # ref = AllReferences[name]
+ # container = container.parent
+ # end
+ # end
+
+ ref = @context.find_symbol(i.name)
+ ref = ref.viewer if ref
+
+ if !ref && block_given?
+ possibles = yield(i.name)
+ while !ref and !possibles.empty?
+ ref = AllReferences[possibles.shift]
+ end
+ end
+ h_name = CGI.escapeHTML(i.name)
+ if ref and ref.document_self
+ path = ref.path
+ res << { "name" => h_name, "href" => path }
+ else
+ res << { "name" => h_name, "href" => "" }
+ end
+ end
+ res
+ end
+
+ # Build an array of arrays of method details. The outer array has up
+ # to six entries, public, private, and protected for both class
+ # methods, the other for instance methods. The inner arrays contain
+ # a hash for each method
+
+ def build_method_detail_list(section)
+ outer = []
+
+ methods = @methods.sort
+ for singleton in [true, false]
+ for vis in [ :public, :protected, :private ]
+ res = []
+ methods.each do |m|
+ if m.section == section and
+ m.document_self and
+ m.visibility == vis and
+ m.singleton == singleton
+ row = {}
+ if m.call_seq
+ row["callseq"] = m.call_seq.gsub(/->/, '&rarr;')
+ else
+ row["name"] = CGI.escapeHTML(m.name)
+ row["params"] = m.params
+ end
+ desc = m.description.strip
+ row["m_desc"] = desc unless desc.empty?
+ row["aref"] = m.aref
+ row["href"] = m.path
+ row["m_seq"] = m.seq
+ row["visibility"] = m.visibility.to_s
+
+ alias_names = []
+ m.aliases.each do |other|
+ if other.viewer # won't be if the alias is private
+ alias_names << {
+ 'name' => other.name,
+ 'href' => other.viewer.path,
+ 'aref' => other.viewer.aref
+ }
+ end
+ end
+ unless alias_names.empty?
+ row["aka"] = alias_names
+ end
+
+ #if @options.inline_source
+ code = m.source_code
+ row["sourcecode"] = code if code
+ #else
+ # code = m.src_url
+ #if code
+ # row["codeurl"] = code
+ # row["imgurl"] = m.img_url
+ #end
+ #end
+ res << row
+ end
+ end
+ if res.size > 0
+ outer << {
+ "type" => vis.to_s.capitalize,
+ "category" => singleton ? "Class" : "Instance",
+ "methods" => res
+ }
+ end
+ end
+ end
+ outer
+ end
+
+ # Build the structured list of classes and modules contained
+ # in this context.
+
+ def build_class_list(level, from, section, infile=nil)
+ res = ""
+ prefix = "&nbsp;&nbsp;::" * level;
+
+ from.modules.sort.each do |mod|
+ next unless mod.section == section
+ next if infile && !mod.defined_in?(infile)
+ if mod.document_self
+ res <<
+ prefix <<
+ "Module " <<
+ href(mod.viewer.path, "link", mod.full_name) <<
+ "<br />\n" <<
+ build_class_list(level + 1, mod, section, infile)
+ end
+ end
+
+ from.classes.sort.each do |cls|
+ next unless cls.section == section
+ next if infile && !cls.defined_in?(infile)
+ if cls.document_self
+ res <<
+ prefix <<
+ "Class " <<
+ href(cls.viewer.path, "link", cls.full_name) <<
+ "<br />\n" <<
+ build_class_list(level + 1, cls, section, infile)
+ end
+ end
+
+ res
+ end
+
+ def document_self
+ @context.document_self
+ end
+
+ def diagram_reference(diagram)
+ res = diagram.gsub(/((?:src|href)=")(.*?)"/) {
+ $1 + $2 + '"'
+ }
+ res
+ end
+
+
+ # Find a symbol in ourselves or our parent
+ def find_symbol(symbol, method=nil)
+ res = @context.find_symbol(symbol, method)
+ if res
+ res = res.viewer
+ end
+ res
+ end
+
+ # create table of contents if we contain sections
+
+ def add_table_of_sections
+ toc = []
+ @context.sections.each do |section|
+ if section.title
+ toc << {
+ 'secname' => section.title,
+ 'href' => section.sequence
+ }
+ end
+ end
+
+ @values['toc'] = toc unless toc.empty?
+ end
+
+
+ end
+
+ #####################################################################
+ #
+ # Wrap a ClassModule context
+
+ class HtmlClass < ContextUser
+
+ @@c_seq = "C00000000"
+
+ attr_reader :path
+
+ def initialize(context, html_file, prefix, options)
+ super(context, options)
+ @@c_seq = @@c_seq.succ
+ @c_seq = @@c_seq
+ @html_file = html_file
+ @is_module = context.is_module?
+ @values = {}
+
+ context.viewer = self
+
+ @path = http_url(context.full_name, prefix)
+
+ collect_methods
+
+ AllReferences.add(name, self)
+ end
+
+ # return the relative file name to store this class in,
+ # which is also its url
+ def http_url(full_name, prefix)
+ path = full_name.dup
+ if path['<<']
+ path.gsub!(/<<\s*(\w*)/) { "from-#$1" }
+ end
+ File.join(prefix, path.split("::")) + ".html"
+ end
+
+ def seq
+ @c_seq
+ end
+
+ def aref
+ @c_seq
+ end
+
+ def scope
+ a = @context.full_name.split("::")
+ if a.length > 1
+ a.pop
+ a.join("::")
+ else
+ ""
+ end
+ end
+
+ def name
+ @context.full_name.gsub("#{scope}::", '')
+ end
+
+ def full_name
+ @context.full_name
+ end
+
+ def parent_name
+ @context.parent.full_name
+ end
+
+ def write_on(f)
+ value_hash
+ template = TemplatePage.new(RDoc::Page::BODY,
+ RDoc::Page::CLASS_PAGE,
+ RDoc::Page::METHOD_LIST)
+ template.write_html_on(f, @values)
+ end
+
+ def value_hash
+ class_attribute_values
+ add_table_of_sections
+
+ @values["charset"] = @options.charset
+ @values["style_url"] = style_url(path, @options.css)
+
+ # Convert README to html
+ unless File.exist?('files/README.html')
+ File.open('files/README.html', 'w') do |file|
+ file << markup(File.read(File.expand_path(@options.main_page)))
+ end
+ end
+
+ d = markup(@context.comment)
+ @values["description"] = d unless d.empty?
+
+ ml = build_method_summary_list
+ @values["methods"] = ml unless ml.empty?
+
+ il = build_include_list(@context)
+ @values["includes"] = il unless il.empty?
+
+ @values["sections"] = @context.sections.map do |section|
+
+ secdata = {
+ "sectitle" => section.title,
+ "secsequence" => section.sequence,
+ "seccomment" => markup(section.comment)
+ }
+
+ al = build_alias_summary_list(section)
+ secdata["aliases"] = al unless al.empty?
+
+ co = build_constants_summary_list(section)
+ secdata["constants"] = co unless co.empty?
+
+ al = build_attribute_list(section)
+ secdata["attributes"] = al unless al.empty?
+
+ cl = build_class_list(0, @context, section)
+ secdata["classlist"] = cl unless cl.empty?
+
+ mdl = build_method_detail_list(section)
+ secdata["method_list"] = mdl unless mdl.empty?
+
+ secdata
+ end
+
+ @values
+ end
+
+ def build_attribute_list(section)
+ atts = @context.attributes.sort
+ res = []
+ atts.each do |att|
+ next unless att.section == section
+ if att.visibility == :public || att.visibility == :protected || @options.show_all
+ entry = {
+ "name" => CGI.escapeHTML(att.name),
+ "rw" => att.rw,
+ "a_desc" => markup(att.comment, true)
+ }
+ unless att.visibility == :public || att.visibility == :protected
+ entry["rw"] << "-"
+ end
+ res << entry
+ end
+ end
+ res
+ end
+
+ def class_attribute_values
+ h_name = CGI.escapeHTML(name)
+
+ @values["classmod"] = @is_module ? "Module" : "Class"
+ @values["title"] = "#{@values['classmod']}: #{h_name}"
+
+ c = @context
+ c = c.parent while c and !c.diagram
+ if c && c.diagram
+ @values["diagram"] = diagram_reference(c.diagram)
+ end
+
+ @values["full_name"] = h_name
+ @values["class_seq"] = seq
+ parent_class = @context.superclass
+
+ if parent_class
+ @values["parent"] = CGI.escapeHTML(parent_class)
+
+ if parent_name
+ lookup = parent_name + "::" + parent_class
+ else
+ lookup = parent_class
+ end
+
+ parent_url = AllReferences[lookup] || AllReferences[parent_class]
+
+ if parent_url and parent_url.document_self
+ @values["par_url"] = parent_url.path
+ end
+ end
+
+ files = []
+ @context.in_files.each do |f|
+ res = {}
+ full_path = CGI.escapeHTML(f.file_absolute_name)
+
+ res["full_path"] = full_path
+ res["full_path_url"] = f.viewer.path if f.document_self
+
+ if @options.webcvs
+ res["cvsurl"] = cvs_url( @options.webcvs, full_path )
+ end
+
+ files << res
+ end
+
+ @values['infiles'] = files
+ end
+
+ def <=>(other)
+ self.name <=> other.name
+ end
+
+ end
+
+ #####################################################################
+ #
+ # Handles the mapping of a file's information to HTML. In reality,
+ # a file corresponds to a +TopLevel+ object, containing modules,
+ # classes, and top-level methods. In theory it _could_ contain
+ # attributes and aliases, but we ignore these for now.
+
+ class HtmlFile < ContextUser
+
+ @@f_seq = "F00000000"
+
+ attr_reader :path
+ attr_reader :name
+
+ def initialize(context, options, file_dir)
+ super(context, options)
+ @@f_seq = @@f_seq.succ
+ @f_seq = @@f_seq
+ @values = {}
+
+ @path = http_url(file_dir)
+ @source_file_path = File.expand_path(@context.file_relative_name).gsub("\/doc\/", "/")
+ @name = @context.file_relative_name
+
+ collect_methods
+ AllReferences.add(name, self)
+ context.viewer = self
+ end
+
+ def http_url(file_dir)
+ File.join(file_dir, @context.file_relative_name.tr('.', '_')) + ".html"
+ end
+
+ def filename_to_label
+ @context.file_relative_name.gsub(/%|\/|\?|\#/) {|s| '%' + ("%x" % s[0]) }
+ end
+
+ def seq
+ @f_seq
+ end
+
+ def aref
+ @f_seq
+ end
+
+ def name
+ full_path = @context.file_absolute_name
+ short_name = File.basename(full_path)
+ end
+
+ def full_name
+ @context.file_absolute_name
+ end
+
+ def scope
+ @context.file_relative_name.gsub(/\/#{name}$/, '')
+ end
+
+ def parent_name
+ nil
+ end
+
+ def full_file_source
+ ret_str = ""
+ File.open(@source_file_path, 'r') do |f|
+ while(!f.eof?) do
+ ret_str += f.readline()
+ end
+ end
+ ret_str
+ rescue
+ "file not found -#{@source_file_path}-"
+ #@source_file_path
+ end
+
+ def value_hash
+ file_attribute_values
+ add_table_of_sections
+
+ @values["charset"] = @options.charset
+ @values["href"] = path
+ @values["style_url"] = style_url(path, @options.css)
+ @values["file_seq"] = seq
+
+ #pulling in the source for this file
+ #@values["source_code"] = @context.token_stream
+
+ @values["file_source_code"] = CGI.escapeHTML(full_file_source)
+
+ if @context.comment
+ d = markup(@context.comment)
+ @values["description"] = d if d.size > 0
+ end
+
+ ml = build_method_summary_list
+ @values["methods"] = ml unless ml.empty?
+
+ il = build_include_list(@context)
+ @values["includes"] = il unless il.empty?
+
+ rl = build_requires_list(@context)
+ @values["requires"] = rl unless rl.empty?
+
+
+ file_context = @context
+
+ @values["sections"] = @context.sections.map do |section|
+
+ secdata = {
+ "sectitle" => section.title,
+ "secsequence" => section.sequence,
+ "seccomment" => markup(section.comment)
+ }
+
+ cl = build_class_list(0, @context, section, file_context)
+ @values["classlist"] = cl unless cl.empty?
+
+ mdl = build_method_detail_list(section)
+ secdata["method_list"] = mdl unless mdl.empty?
+
+ al = build_alias_summary_list(section)
+ secdata["aliases"] = al unless al.empty?
+
+ co = build_constants_summary_list(section)
+ @values["constants"] = co unless co.empty?
+
+ secdata
+ end
+
+ @values
+ end
+
+ def write_on(f)
+ value_hash
+ template = TemplatePage.new(RDoc::Page::SRC_BODY,RDoc::Page::FILE_PAGE, RDoc::Page::METHOD_LIST)
+ template.write_html_on(f, @values)
+ end
+
+ def file_attribute_values
+ full_path = @context.file_absolute_name
+ short_name = File.basename(full_path)
+
+ @values["title"] = CGI.escapeHTML("File: #{short_name}")
+
+ if @context.diagram
+ @values["diagram"] = diagram_reference(@context.diagram)
+ end
+
+ @values["short_name"] = CGI.escapeHTML(short_name)
+ @values["full_path"] = CGI.escapeHTML(full_path)
+ @values["dtm_modified"] = @context.file_stat.mtime.to_s
+
+ if @options.webcvs
+ @values["cvsurl"] = cvs_url( @options.webcvs, @values["full_path"] )
+ end
+ end
+
+ def <=>(other)
+ self.name <=> other.name
+ end
+ end
+
+ #####################################################################
+
+ class HtmlMethod
+ include MarkUp
+
+ attr_reader :context
+ attr_reader :src_url
+ attr_reader :img_url
+ attr_reader :source_code
+
+ @@m_seq = "M000000"
+
+ @@all_methods = []
+
+ def HtmlMethod::reset
+ @@all_methods = []
+ end
+
+ def initialize(context, html_class, options)
+ @context = context
+ @html_class = html_class
+ @options = options
+ @@m_seq = @@m_seq.succ
+ @m_seq = @@m_seq
+ @@all_methods << self
+
+ context.viewer = self
+
+ if (ts = @context.token_stream)
+ @source_code = markup_code(ts)
+ #unless @options.inline_source
+ # @src_url = create_source_code_file(@source_code)
+ # @img_url = MERBGenerator.gen_url(path, 'source.png')
+ #end
+ end
+ AllReferences.add(name, self)
+ end
+
+ def seq
+ @m_seq
+ end
+
+ def aref
+ @m_seq
+ end
+
+ def scope
+ @html_class.full_name
+ end
+
+ # return a reference to outselves to be used as an href=
+ # the form depends on whether we're all in one file
+ # or in multiple files
+
+ def name
+ @context.name
+ end
+
+ def section
+ @context.section
+ end
+
+ def parent_name
+ if @context.parent.parent
+ @context.parent.parent.full_name
+ else
+ nil
+ end
+ end
+
+ def path
+ @html_class.path
+ end
+
+ def description
+ markup(@context.comment)
+ end
+
+ def visibility
+ @context.visibility
+ end
+
+ def singleton
+ @context.singleton
+ end
+
+ def call_seq
+ cs = @context.call_seq
+ if cs
+ cs.gsub(/\n/, "<br />\n")
+ else
+ nil
+ end
+ end
+
+ def params
+ # params coming from a call-seq in 'C' will start with the
+ # method name
+ p = @context.params
+ if p !~ /^\w/
+ p = @context.params.gsub(/\s*\#.*/, '')
+ p = p.tr("\n", " ").squeeze(" ")
+ p = "(" + p + ")" unless p[0] == ?(
+
+ if (block = @context.block_params)
+ # If this method has explicit block parameters, remove any
+ # explicit &block
+
+ p.sub!(/,?\s*&\w+/, '')
+
+ block.gsub!(/\s*\#.*/, '')
+ block = block.tr("\n", " ").squeeze(" ")
+ if block[0] == ?(
+ block.sub!(/^\(/, '').sub!(/\)/, '')
+ end
+ p << " {|#{block.strip}| ...}"
+ end
+ end
+ CGI.escapeHTML(p)
+ end
+
+ def create_source_code_file(code_body)
+ meth_path = @html_class.path.sub(/\.html$/, '.src')
+ File.makedirs(meth_path)
+ file_path = File.join(meth_path, seq) + ".html"
+
+ template = TemplatePage.new(RDoc::Page::SRC_PAGE)
+ File.open(file_path, "w") do |f|
+ values = {
+ 'title' => CGI.escapeHTML(name),
+ 'code' => code_body,
+ 'style_url' => style_url(file_path, @options.css),
+ 'charset' => @options.charset
+ }
+ template.write_html_on(f, values)
+ end
+ file_path
+ end
+
+ def HtmlMethod.all_methods
+ @@all_methods
+ end
+
+ def <=>(other)
+ @context <=> other.context
+ end
+
+ ##
+ # Given a sequence of source tokens, mark up the source code
+ # to make it look purty.
+
+
+ def markup_code(tokens)
+ src = ""
+ tokens.each do |t|
+ next unless t
+ # p t.class
+ # style = STYLE_MAP[t.class]
+ style = case t
+ when RubyToken::TkCONSTANT then "ruby-constant"
+ when RubyToken::TkKW then "ruby-keyword kw"
+ when RubyToken::TkIVAR then "ruby-ivar"
+ when RubyToken::TkOp then "ruby-operator"
+ when RubyToken::TkId then "ruby-identifier"
+ when RubyToken::TkNode then "ruby-node"
+ when RubyToken::TkCOMMENT then "ruby-comment cmt"
+ when RubyToken::TkREGEXP then "ruby-regexp re"
+ when RubyToken::TkSTRING then "ruby-value str"
+ when RubyToken::TkVal then "ruby-value"
+ else
+ nil
+ end
+
+ text = CGI.escapeHTML(t.text)
+
+ if style
+ src << "<span class=\"#{style}\">#{text}</span>"
+ else
+ src << text
+ end
+ end
+
+ add_line_numbers(src)
+ src
+ end
+
+ # we rely on the fact that the first line of a source code
+ # listing has
+ # # File xxxxx, line dddd
+
+ def add_line_numbers(src)
+ if src =~ /\A.*, line (\d+)/
+ first = $1.to_i - 1
+ last = first + src.count("\n")
+ size = last.to_s.length
+ real_fmt = "%#{size}d: "
+ fmt = " " * (size+2)
+ src.gsub!(/^/) do
+ res = sprintf(fmt, first)
+ first += 1
+ fmt = real_fmt
+ res
+ end
+ end
+ end
+
+ def document_self
+ @context.document_self
+ end
+
+ def aliases
+ @context.aliases
+ end
+
+ def find_symbol(symbol, method=nil)
+ res = @context.parent.find_symbol(symbol, method)
+ if res
+ res = res.viewer
+ end
+ res
+ end
+ end
+
+ #####################################################################
+
+ class MERBGenerator
+
+ include MarkUp
+
+ # Generators may need to return specific subclasses depending
+ # on the options they are passed. Because of this
+ # we create them using a factory
+
+ def MERBGenerator.for(options)
+ AllReferences::reset
+ HtmlMethod::reset
+
+ MERBGenerator.new(options)
+
+ end
+
+ class <<self
+ protected :new
+ end
+
+ # Set up a new HTML generator. Basically all we do here is load
+ # up the correct output temlate
+
+ def initialize(options) #:not-new:
+ @options = options
+ load_html_template
+ end
+
+
+ ##
+ # Build the initial indices and output objects
+ # based on an array of TopLevel objects containing
+ # the extracted information.
+
+ def generate(toplevels)
+ @toplevels = toplevels
+ @files = []
+ @classes = []
+
+ write_style_sheet
+ write_javascript
+ gen_sub_directories()
+ build_indices
+ generate_html
+ end
+
+ private
+
+ ##
+ # Load up the AJAX HTML template specified in the options.
+ # If the template name contains a slash, use it literally
+ #
+ def load_html_template
+ template = @options.template
+ unless template =~ %r{/|\\}
+ template = File.join("rdoc/generators/template", @options.generator.key, template)
+ end
+ require template
+ extend RDoc::Page
+ rescue LoadError
+ $stderr.puts "Could not find AJAX template '#{template}'"
+ exit 99
+ end
+
+ ##
+ # Write out the style sheet used by the main frames
+ #
+
+ def write_style_sheet
+ template = TemplatePage.new(RDoc::Page::STYLE)
+ File.open(CSS_NAME, "w") do |f|
+ values = { "font" => "helvetica"} #this is not used anywhere but the template function demands a hash of values
+ template.write_html_on(f, values)
+ end
+ end
+
+ def write_javascript
+ #Argh... I couldn't figure out how to copy these from the template dir so they were copied into
+ # the template file "ajax.rb" and processed similarlly to the style sheets. Not exactly a good thing to do with
+ # external library code. Not very DRY.
+
+ File.open("api_grease.js", "w") do |f|
+ f << RDoc::Page::API_GREASE_JS
+ end
+
+ File.open("prototype.js", "w") do |f|
+ f << RDoc::Page::PROTOTYPE_JS
+ end
+
+ rescue LoadError
+ $stderr.puts "Could not find AJAX template"
+ exit 99
+ end
+
+ ##
+ # See the comments at the top for a description of the
+ # directory structure
+
+ def gen_sub_directories
+ File.makedirs(FILE_DIR, CLASS_DIR)
+ rescue
+ $stderr.puts $!.message
+ exit 1
+ end
+
+ ##
+ # Generate:
+ #
+ # * a list of HtmlFile objects for each TopLevel object.
+ # * a list of HtmlClass objects for each first level
+ # class or module in the TopLevel objects
+ # * a complete list of all hyperlinkable terms (file,
+ # class, module, and method names)
+
+ def build_indices
+
+ @toplevels.each do |toplevel|
+ @files << HtmlFile.new(toplevel, @options, FILE_DIR)
+ end
+
+ RDoc::TopLevel.all_classes_and_modules.each do |cls|
+ build_class_list(cls, @files[0], CLASS_DIR)
+ end
+ end
+
+ def build_class_list(from, html_file, class_dir)
+ @classes << HtmlClass.new(from, html_file, class_dir, @options)
+ from.each_classmodule do |mod|
+ build_class_list(mod, html_file, class_dir)
+ end
+ end
+
+ ##
+ # Generate all the HTML
+ #
+ def generate_html
+ # the individual descriptions for files and classes
+ gen_into(@files)
+ gen_into(@classes)
+ # and the index files
+ gen_file_index
+ gen_class_index
+ gen_method_index
+ gen_main_index
+
+ # this method is defined in the template file
+ write_extra_pages if defined? write_extra_pages
+ end
+
+ def gen_into(list)
+ list.each do |item|
+ if item.document_self
+ op_file = item.path
+ File.makedirs(File.dirname(op_file))
+ File.open(op_file, "w") { |file| item.write_on(file) }
+ end
+ end
+
+ end
+
+ def gen_file_index
+ gen_an_index(@files, 'Files',
+ RDoc::Page::FILE_INDEX,
+ "fr_file_index.html")
+ end
+
+ def gen_class_index
+ gen_an_index(@classes, 'Classes',
+ RDoc::Page::CLASS_INDEX,
+ "fr_class_index.html")
+ end
+
+ def gen_method_index
+ gen_an_index(HtmlMethod.all_methods, 'Methods',
+ RDoc::Page::METHOD_INDEX,
+ "fr_method_index.html")
+ end
+
+
+ def gen_an_index(collection, title, template, filename)
+ template = TemplatePage.new(RDoc::Page::FR_INDEX_BODY, template)
+ res = []
+ collection.sort.each do |f|
+ if f.document_self
+ res << { "href" => f.path, "name" => f.name, "scope" => f.scope, "seq_id" => f.seq }
+ end
+ end
+
+ values = {
+ "entries" => res,
+ 'list_title' => CGI.escapeHTML(title),
+ 'index_url' => main_url,
+ 'charset' => @options.charset,
+ 'style_url' => style_url('', @options.css),
+ }
+
+ File.open(filename, "w") do |f|
+ template.write_html_on(f, values)
+ end
+ end
+
+ # The main index page is mostly a template frameset, but includes
+ # the initial page. If the <tt>--main</tt> option was given,
+ # we use this as our main page, otherwise we use the
+ # first file specified on the command line.
+
+ def gen_main_index
+ template = TemplatePage.new(RDoc::Page::INDEX)
+ File.open("index.html", "w") do |f|
+ tStr = ""
+ #File.open(main_url, 'r') do |g|
+ # tStr = markup(g)
+ #end
+ values = {
+ "initial_page" => tStr,
+ 'title' => CGI.escapeHTML(@options.title),
+ 'charset' => @options.charset,
+ 'content' => File.read('files/README.html')
+ }
+
+ values['inline_source'] = true
+ template.write_html_on(f, values)
+ end
+ end
+
+ # return the url of the main page
+ def main_url
+ "files/README.html"
+ end
+
+
+ end
+
+end
View
640 doc/rdoc/generators/template/merb/api_grease.js
@@ -0,0 +1,640 @@
+
+function setupPage(){
+ hookUpActiveSearch();
+ hookUpTabs();
+ suppressPostbacks();
+ var url_params = getUrlParams();
+ if (url_params != null){
+ loadUrlParams(url_params);
+ }else{
+ loadDefaults();
+ }
+ resizeDivs();
+ window.onresize = function(){ resizeDivs(); };
+}
+
+function getUrlParams(){
+ var window_location = window.location.href
+ var param_pos = window_location.search(/\?/)
+ if (param_pos > 0){
+ return(window_location.slice(param_pos, window_location.length));
+ }else{
+ return(null);
+ }
+}
+
+function loadUrlParams(url_param){
+ //get the tabs
+ var t = getTabs();
+ // now find our variables
+ var s_params = /(\?)(a=.+?)(&)(name=.*)/;
+ var results = url_param.match(s_params);
+ url_anchor = results[2].replace(/a=/,'');
+
+ if (url_anchor.match(/M.+/)){//load the methods tab and scroller content
+ setActiveTabAndLoadContent(t[0]);
+ }else{
+ if(url_anchor.match(/C.+/)){ //load the classes tab and scroller content
+ setActiveTabAndLoadContent(t[1]);
+ }else{
+ if (url_anchor.match(/F.+/)){//load the files tab
+ setActiveTabAndLoadContent(t[2]);
+ }else{
+ // default to loading the methods
+ setActiveTabAndLoadContent(t[0]);
+ }
+ }
+ }
+ paramLoadOfContentAnchor(url_anchor + "_link");
+}
+
+function updateUrlParams(anchor_id, name){
+ //Also setting the page title
+ //window.document.title = name + " method - MerbBrain.com ";
+
+ //updating the window location
+ var current_href = window.location.href;
+ //var m_name = name.replace("?","?");
+ var rep_str = ".html?a=" + anchor_id + "&name=" + name;
+ var new_href = current_href.replace(/\.html.*/, rep_str);
+ if (new_href != current_href){
+ window.location.href = new_href;
+ }
+}
+
+//does as it says...
+function hookUpActiveSearch(){
+
+ var s_field = $('searchForm').getInputs('text')[0];
+ //var s_field = document.forms[0].searchText;
+ Event.observe(s_field, 'keydown', function(event) {
+ var el = Event.element(event);
+ var key = event.which || event.keyCode;
+
+ switch (key) {
+ case Event.KEY_RETURN:
+ forceLoadOfContentAnchor(getCurrentAnchor());
+ Event.stop(event);
+ break;
+
+ case Event.KEY_UP:
+ scrollListToElementOffset(getCurrentAnchor(),-1);
+ break;
+
+ case Event.KEY_DOWN:
+ scrollListToElementOffset(getCurrentAnchor(),1);
+ break;
+
+ default:
+ break;
+ }
+
+ });
+
+ Event.observe(s_field, 'keyup', function(event) {
+ var el = Event.element(event);
+ var key = event.which || event.keyCode;
+ switch (key) {
+ case Event.KEY_RETURN:
+ Event.stop(event);
+ break;
+
+ case Event.KEY_UP:
+ break;
+
+ case Event.KEY_DOWN:
+ break;
+
+ default:
+ scrollToName(el.value);
+ setSavedSearch(getCurrentTab(), el.value);
+ break;
+ }
+
+ });
+
+ Event.observe(s_field, 'keypress', function(event){
+ var el = Event.element(event);
+ var key = event.which || event.keyCode;
+ switch (key) {
+ case Event.KEY_RETURN:
+ Event.stop(event);
+ break;
+
+ default:
+ break;
+ }
+
+ });
+
+ //Event.observe(document, 'keypress', function(event){
+ // var key = event.which || event.keyCode;
+ // if (key == Event.KEY_TAB){
+ // cycleNextTab();
+ // Event.stop(event);
+ // }
+ //});
+}
+
+function hookUpTabs(){
+
+ var tabs = getTabs();
+ for(x=0; x < tabs.length; x++)
+ {
+ Event.observe(tabs[x], 'click', function(event){
+ var el = Event.element(event);
+ setActiveTabAndLoadContent(el);
+ });
+ //tabs[x].onclick = function (){ return setActiveTabAndLoadContent(this);}; //the prototype guys say this is bad..
+ }
+
+}
+
+function suppressPostbacks(){
+ Event.observe('searchForm', 'submit', function(event){
+ Event.stop(event);
+ });
+}
+
+function loadDefaults(){
+ var t = getTabs();
+ setActiveTabAndLoadContent(t[0]); //default loading of the first tab
+}
+
+function resizeDivs(){
+ var inner_height = 700;
+ if (window.innerHeight){
+ inner_height = window.innerHeight; //all browsers except IE use this to determine the space available inside a window. Thank you Microsoft!!
+ }else{
+ if(document.documentElement.clientHeight > 0){ //IE uses this in 'strict' mode
+ inner_height = document.documentElement.clientHeight;
+ }else{
+ inner_height = document.body.clientHeight; //IE uses this in 'quirks' mode
+ }
+ }
+ $('rdocContent').style.height = (inner_height - 92) + "px";//Thankfully all browsers can agree on how to set the height of a div
+ $('listScroller').style.height = (inner_height - 88) + "px";
+}
+
+//The main function for handling clicks on the tabs
+function setActiveTabAndLoadContent(current_tab){
+ changeLoadingStatus("on");
+ var tab_string = String(current_tab.innerHTML).strip(); //thank you ProtoType!
+ switch (tab_string){
+ case "classes":
+ setCurrentTab("classes");
+ loadScrollerContent('fr_class_index.html');
+ setSearchFieldValue(getSavedSearch("classes"));
+ scrollToName(getSavedSearch("classes"));
+ setSearchFocus();
+ break;
+
+ case "files":
+ setCurrentTab("files");
+ loadScrollerContent('fr_file_index.html');
+ setSearchFieldValue(getSavedSearch("files"));
+ scrollToName(getSavedSearch("files"));
+ setSearchFocus();
+ break;
+
+ case "methods":
+ setCurrentTab("methods");
+ loadScrollerContent('fr_method_index.html');
+ setSearchFieldValue(getSavedSearch("methods"));
+ scrollToName(getSavedSearch("methods"));
+ setSearchFocus();
+ break;
+
+ default:
+ break;
+ }
+ changeLoadingStatus("off");
+}
+
+function cycleNextTab(){
+ var currentT = getCurrentTab();
+ var tabs = getTabs();
+ if (currentT == "methods"){
+ setActiveTabAndLoadContent(tabs[1]);
+ setSearchFocus();
+ }else{
+ if (currentT == "classes"){
+ setActiveTabAndLoadContent(tabs[2]);
+ setSearchFocus();
+ }else{
+ if (currentT == "files"){
+ setActiveTabAndLoadContent(tabs[0]);
+ setSearchFocus();
+ }
+ }
+ }
+}
+
+function getTabs(){
+ return($('groupType').getElementsByTagName('li'));
+}
+
+var Active_Tab = "";
+function getCurrentTab(){
+ return Active_Tab;
+}
+
+function setCurrentTab(tab_name){
+ var tabs = getTabs();
+ for(x=0; x < tabs.length; x++)
+ {
+ if(tabs[x].innerHTML.strip() == tab_name) //W00t!!! String.prototype.strip!
+ {
+ tabs[x].className = "activeLi";
+ Active_Tab = tab_name;
+ }
+ else
+ {
+ tabs[x].className = "";
+ }
+ }
+}
+
+//These globals should not be used globally (hence the getters and setters)
+var File_Search = "";
+var Method_Search = "";
+var Class_Search = "";
+function setSavedSearch(tab_name, s_val){
+ switch(tab_name){
+ case "methods":
+ Method_Search = s_val;
+ break;
+ case "files":
+ File_Search = s_val;
+ break;
+ case "classes":
+ Class_Search = s_val;
+ break;
+ }
+}
+
+function getSavedSearch(tab_name){
+ switch(tab_name){
+ case "methods":
+ return (Method_Search);
+ break;
+ case "files":
+ return (File_Search);
+ break;
+ case "classes":
+ return (Class_Search);
+ break;
+ }
+}
+
+//These globals handle the history stack
+
+
+function setListScrollerContent(s){
+
+ $('listScroller').innerHTML = s;
+}
+
+function setMainContent(s){
+
+ $('rdocContent').innerHTML = s;
+}
+
+function setSearchFieldValue(s){
+
+ document.forms[0].searchText.value = s;
+}
+
+function getSearchFieldValue(){
+
+ return Form.Element.getValue('searchText');
+}
+
+function setSearchFocus(){
+
+ document.forms[0].searchText.focus();
+}
+
+var Anchor_ID_Of_Current = null; // holds the last highlighted anchor tag in the scroll lsit
+function getCurrentAnchor(){
+ return(Anchor_ID_Of_Current);
+}
+
+function setCurrentAnchor(a_id){
+ Anchor_ID_Of_Current = a_id;
+}
+
+//var Index_Of_Current = 0; //holds the last highlighted index
+//function getCurrentIndex(){
+// return (Index_Of_Current);
+//}
+
+//function setCurrentIndex(new_i){
+// Index_Of_Current = new_i;
+//}
+
+function loadScrollerContent(url){
+
+ var scrollerHtml = new Ajax.Request(url, {
+ asynchronous: false,
+ method: 'get',
+ onComplete: function(method_data) {
+ setListScrollerContent(method_data.responseText);
+ }
+ });
+
+}
+
+//called primarily from the links inside the scroller list
+//loads the main page div then jumps to the anchor/element with id
+function loadContent(url, anchor_id){
+
+ var mainHtml = new Ajax.Request(url, {
+ method: 'get',
+ onLoading: changeLoadingStatus("on"),
+ onSuccess: function(method_data) {
+ setMainContent(method_data.responseText);},
+ onComplete: function(request) {
+ changeLoadingStatus("off");
+ new jumpToAnchor(anchor_id);
+ }
+ });
+}
+
+//An alternative function that also will stuff the index history for methods, files, classes
+function loadIndexContent(url, anchor_id, name, scope)
+{
+ if (From_URL_Param == true){
+ var mainHtml = new Ajax.Request(url, {
+ method: 'get',
+ onLoading: changeLoadingStatus("on"),
+ onSuccess: function(method_data) {
+ setMainContent(method_data.responseText);},
+ onComplete: function(request) {
+ changeLoadingStatus("off");
+ updateBrowserBar(name, anchor_id, scope);
+ new jumpToAnchor(anchor_id);}
+ });
+ From_URL_Param = false;
+ }else{
+ updateUrlParams(anchor_id, name);
+ }
+
+}
+
+function updateBrowserBar(name, anchor_id, scope){
+ if (getCurrentTab() == "methods"){
+ $('browserBarInfo').update("<small>class/module:</small>&nbsp;<a href=\"#\" onclick=\"jumpToTop();\">" + scope + "</a>&nbsp;&nbsp;<small>method:</small>&nbsp;<strong><a href=\"#\" onclick=\"jumpToAnchor('"+ anchor_id +"')\">" + name + "</a></strong> ");
+ }else{ if(getCurrentTab() == "classes"){
+ $('browserBarInfo').update("<small>class/module:</small>&nbsp;<a href=\"#\" onclick=\"jumpToTop();\">" + scope + "::" + name + "</strong> ");
+ }else{
+ $('browserBarInfo').update("<small>file:</small>&nbsp;<a href=\"#\" onclick=\"jumpToTop();\">" + scope + "/" + name + "</strong> ");
+ }
+ }
+}
+
+
+// Force loads the contents of the index of the current scroller list. It does this by
+// pulling the onclick method out and executing it manually.
+function forceLoadOfContent(index_to_load){
+ var scroller = $('listScroller');
+ var a_array = scroller.getElementsByTagName('a');
+ if ((index_to_load >= 0) && (index_to_load < a_array.length)){
+ var load_element = a_array[index_to_load];
+ var el_text = load_element.innerHTML.strip();
+ setSearchFieldValue(el_text);
+ setSavedSearch(getCurrentTab(), el_text);
+ eval("new " + load_element.onclick);
+ }
+}
+
+function forceLoadOfContentAnchor(anchor_id){
+
+ var load_element = $(anchor_id);
+ if (load_element != null){
+ var el_text = load_element.innerHTML.strip();
+ setSearchFieldValue(el_text);
+ scrollToAnchor(anchor_id);
+ setSavedSearch(getCurrentTab(), el_text);
+ eval("new " + load_element.onclick);
+ }
+}
+
+var From_URL_Param = false;
+function paramLoadOfContentAnchor(anchor_id){
+ From_URL_Param = true;
+ forceLoadOfContentAnchor(anchor_id);
+}
+
+//this handles the up/down keystrokes to move the selection of items in the list
+function scrollListToElementOffset(anchor_id, offset){
+ var scroller = $('listScroller');
+ var a_array = scroller.getElementsByTagName('a');
+ var current_index = findIndexOfAnchor(a_array, anchor_id);
+ if ((current_index >= 0) && (current_index < a_array.length)){
+ scrollListToAnchor(a_array[current_index + offset].id);
+ setListActiveAnchor(a_array[current_index + offset].id);
+ }
+}
+
+function findIndexOfAnchor(a_array, anchor_id){
+ var found=false;
+ var counter = 0;
+ while(!found && counter < a_array.length){
+ if (a_array[counter].id == anchor_id){
+ found = true;
+ }else{
+ counter +=1;
+ }
+ }
+ return(counter);
+}
+
+function scrollToName(searcher_name){
+
+ var scroller = $('listScroller');
+ var a_array = scroller.getElementsByTagName('a');
+
+ if (!searcher_name.match(new RegExp(/\s+/))){ //if searcher name is blank
+
+ var searcher_pattern = new RegExp("^"+searcher_name, "i"); //the "i" is for case INsensitive
+ var found_index = -1;
+
+ var found = false;
+ var x = 0;
+ while(!found && x < a_array.length){
+ if(a_array[x].innerHTML.match(searcher_pattern)){
+ found = true;
+ found_index = x;
+ }
+ else{
+ x++;
+ }
+ }
+
+ // // an attempt at binary searching... have not given up on this yet...
+ //found_index = binSearcher(searcher_pattern, a_array, 0, a_array.length);
+
+ if ((found_index >= 0) && (found_index < a_array.length)){
+
+ scrollListToAnchor(a_array[found_index].id);//scroll to the item
+ setListActiveAnchor(a_array[found_index].id);//highlight the item
+ }
+ }else{ //since searcher name is blank
+ //scrollListToIndex(a_array, 0);//scroll to the item
+ //setListActiveItem(a_array, 0);//highlight the item
+ }
+}
+
+function scrollToAnchor(anchor_id){
+ var scroller = $('listScroller');
+ if ($(anchor_id) != null){
+ scrollListToAnchor(anchor_id);
+ setListActiveAnchor(anchor_id);
+ }
+}
+
+function getY(element){
+
+ var y = 0;
+ for( var e = element; e; e = e.offsetParent)//iterate the offset Parents
+ {
+ y += e.offsetTop; //add up the offsetTop values
+ }
+ //for( e = element.parentNode; e && e != document.body; e = e.parentNode)
+ // if (e.scrollTop) y -= e.scrollTop; //subtract scrollbar values
+ return y;
+}
+
+//function setListActiveItem(item_array, active_index){
+//
+// item_array[getCurrentIndex()].className = "";
+// setCurrentIndex(active_index);
+// item_array[getCurrentIndex()].className = "activeA"; //setting the active class name
+//}
+
+function setListActiveAnchor(active_anchor){
+ if ((getCurrentAnchor() != null) && ($(getCurrentAnchor()) != null)){
+ $(getCurrentAnchor()).className = "";
+ }
+ setCurrentAnchor(active_anchor);
+ $(getCurrentAnchor()).className = "activeA";
+
+}
+
+//handles the scrolling of the list and setting of the current index
+//function scrollListToIndex(a_array, scroll_index){
+// if (scroll_index > 0){
+// var scroller = $('listScroller');
+// scroller.scrollTop = getY(a_array[scroll_index]) - 120; //the -120 is what keeps it from going to the top...
+// }
+//}
+
+function scrollListToAnchor(scroll2_anchor){
+ var scroller = $('listScroller');
+ scroller.scrollTop = getY($(scroll2_anchor)) - 120;
+}
+
+function jumpToAnchor(anchor_id){
+
+ var contentScroller = $('rdocContent');
+ var a_div = $(anchor_id);
+ contentScroller.scrollTop = getY(a_div) - 80; //80 is the offset to adjust scroll point
+ var a_title = $(anchor_id + "_title");
+ a_title.style.backgroundColor = "#222";
+ a_title.style.color = "#FFF";
+ a_title.style.padding = "3px";
+ // a_title.style.borderBottom = "2px solid #ccc";
+
+ //other attempts
+ //a_div.className = "activeMethod"; //setting the active class name
+ //a_div.style.backgroundColor = "#ffc";
+ //var titles = a_div.getElementsByClassName("title");
+ //titles[0].className = "activeTitle";
+
+}
+
+function jumpToTop(){
+ $('rdocContent').scrollTop = 0;
+}
+
+function changeLoadingStatus(status){
+ if (status == "on"){
+ $('loadingStatus').show();
+ }
+ else{
+ $('loadingStatus').hide();
+ }
+}
+
+//************* Misc functions (mostly from the old rdocs) ***********************
+//snagged code from the old templating system
+function toggleSource( id ){
+
+ var elem
+ var link
+
+ if( document.getElementById )
+ {
+ elem = document.getElementById( id )
+ link = document.getElementById( "l_" + id )
+ }
+ else if ( document.all )
+ {
+ elem = eval( "document.all." + id )
+ link = eval( "document.all.l_" + id )
+ }
+ else
+ return false;
+
+ if( elem.style.display == "block" )
+ {
+ elem.style.display = "none"
+ link.innerHTML = "show source"
+ }
+ else
+ {
+ elem.style.display = "block"
+ link.innerHTML = "hide source"
+ }
+}
+
+function openCode( url ){
+ window.open( url, "SOURCE_CODE", "width=400,height=400,scrollbars=yes" )
+}
+
+//this function handles the ajax calling and afterits loaded the jumping to the anchor...
+function jsHref(url){
+ //alert(url);
+ var mainHtml = new Ajax.Request(url, {
+ method: 'get',
+ onSuccess: function(method_data) {
+ setMainContent(method_data.responseText);}
+ });
+}
+
+//function comparePatterns(string, regexp){
+// var direction = 0;
+//
+//
+// return (direction)
+//}
+
+////returns the index of the element
+//function binSearcher(regexp_pattern, list, start_index, stop_index){
+// //divide the list in half
+// var split_point = 0;
+// split_point = parseInt((stop_index - start_index)/2);
+// direction = comparePatterns(list[split_point].innerHTML, regexp_pattern);
+// if(direction < 0)
+// return (binSearcher(regexp_pattern, list, start_index, split_point));
+// else
+// if(direction > 0)
+// return (binSearcher(regexp_pattern, list, split_point, stop_index));
+// else
+// return(split_point);
+//
+//}
+
+
+
View
37 doc/rdoc/generators/template/merb/index.html.erb
@@ -0,0 +1,37 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html lang='en' xml:lang='en' xmlns='http://www.w3.org/1999/xhtml'>
+ <head>
+ <title>Documentation</title>
+ <meta content='text/html;charset=UTF-8' http-equiv='content-type' />
+ <meta content='all' name='robots' />
+ <meta content='text/html;charset=utf-8' http-equiv='content-type' />
+ <meta content='no' http-equiv='msthemecompatible' />
+ <meta content='false' http-equiv='imagetoolbar' />
+ <link href="generators/template/merb/merb.css" media="screen" rel="stylesheet" type="text/css" />
+ </head>
+ <body>
+ <div id='content'>
+ <div class='wrap_to_center'>
+ <div class='full_width' id='content_top'>
+ <div id='content_bottom'>
+ <div id='content_full'>
+ <h1>Documentation</h1>
+ <ul>
+ <% @directories.each do |directory| %>
+ <li id="merb-core"><a href="<%= directory %>/index.html"><%= directory.capitalize %></a></li>
+ <% end %>
+ </ul>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div id='footer'>
+ <div class='wrap_to_center'>
+ <p>
+ &copy; 2008, Merb Team | Merb is released under the <a href="http://www.opensource.org/licenses/mit-license.php">MIT License</a>
+ </p>
+ </div>
+ </div>
+ </div>
+ </body>
+</html>
View
252 doc/rdoc/generators/template/merb/merb.css
@@ -0,0 +1,252 @@
+html, body, div, span, applet, object, iframe,h1, h2, h3, h4, h5, h6, p, blockquote, pre,a, abbr, acronym, address, big, cite, code,del, dfn, em, font, img, ins, kbd, q, s, samp,small, strike, strong, sub, sup, tt, var,dl, dt, dd, ol, ul, li,fieldset, form, label, legend,table, caption, tbody, tfoot, thead, tr, th, td {
+ margin: 0;
+ padding: 0;
+ border: 0;
+ font-weight: inherit;
+ font-style: inherit;
+ font-size: 100%;
+ font-family: inherit;
+ vertical-align: baseline; }
+
+/* GENERAL RULES */
+
+body {
+ background: #000 url(../img/body.gif) repeat-x bottom center;
+ color: #000;
+ font: normal 12px "Lucida Grande", "Arial", sans-serif;
+ line-height: 1;
+}
+ul {list-style-type: none;}
+#content_full ul.revisions{list-style-type: disc;}
+#content_full ul.revisions li{margin-left: 15px;padding: 3px 0;}
+li a {display: block;}
+#content_full ul.revisions li a{display: inline;}
+strong {font-weight: bold;}
+table {border-collapse: separate;border-spacing: 0; }
+caption, th, td {text-align: left;font-weight: normal; }
+.invisible {display: none;}
+.full_width {width:100%;}
+
+/* LAYOUT */
+
+.wrap_to_center, #foot {
+ margin: 0 auto;
+ display: block;
+ width: 800px;
+}
+
+#content {width: 100%;}
+
+#content_top {
+ background: #fff url(../img/content_top.gif) no-repeat top center;
+ float:left;
+ margin:25px 0px;
+ width:100%;
+}
+#content_bottom {
+ background: url(../img/content_bottom.gif) no-repeat bottom center;
+ width:100%;
+ float:left;
+}
+#content_main {
+ float:left;
+ margin: 10px 20px 20px 20px;
+ width:506px;
+}
+#content p {
+ line-height:17px;
+}
+#content_full {margin: 10px 20px 20px 20px;}
+
+/* HEADER & NAVIGATION */
+
+#header {
+ background: #1db900 url(../img/header_waves.gif) repeat-x top center;
+ height:74px;
+ width: 100%;
+}
+#waves {
+ background: url(../img/header_waves.gif)no-repeat top left;
+ height:74px;
+ width:980px;
+}
+#header img {margin-top:8px; float:left;}
+#header a {color:#fff; text-decoration:none;}
+#header a:hover {color:#000;}
+ul#nav {float:right;display:block;width:43.3em;margin-top:25px;}
+ul#nav li {display:block;float:left;}
+ul#nav li a {display:block;float:left;margin:0px 5px;padding:6px 9px 31px 9px;}
+ul#nav li a:hover {background:url(../img/header_hover.gif) repeat-x bottom center;}
+ul#nav li a#active {background:url(../img/header_arrow.gif)no-repeat bottom center;}
+ul#nav li.last a {margin-right:0;}
+
+/* TEXT FORMATTING */
+
+h1 {
+ border-bottom:2px solid #ccc;
+ color:#000;
+ font:bold 28px "Arial" sans-serif;
+ letter-spacing:1px;
+ margin:20px 0px;
+ text-align:left;
+ width:100%;
+}
+h1.home {
+ border:0;
+ color:#fff;
+ font-size:36px;
+ margin:20px 0px;
+ text-align:center;
+}
+h2 {
+ color:#7aad00;
+ font:bold 22px "Lucida Grande" sans-serif;
+ margin:10px 0px;
+}
+h3 {
+ font:bold 16px "Lucida Grande";
+ margin:10px 0px;
+}
+#content a {color:#d7ff00;}
+#content a:hover {background:#d7ff00;color:#000;}
+#content_main ul {margin:10px 0px;}
+#content_main ul li {
+ background: url(../img/li.gif) no-repeat left center;
+ padding: 4px 4px 4px 16px;
+ font-weight:bold;
+}
+p {margin-bottom:12px;}
+#content_main a,#content_full a {color:#11b716;font-weight:bold;}
+#content_main a:hover,#content_full a:hover {background:#22d716;}
+pre {
+ background:#222;
+ color:#fff;
+ font:12px "Courier" serif;
+ line-height:18px;
+ padding: 12px;
+ margin-bottom: 10px;
+}
+code {
+ font:bold 12px "Courier" serif;
+}
+pre code {font-weight:normal;}
+
+/* SIDEBAR FOR CONTENT */
+
+#content_sidebar {
+ float: left;
+ margin: 20px 20px 15px 10px;
+ width: 224px;
+}
+.sidebar_top {
+ background:#868686 url(../img/sidebar_top.gif) no-repeat top center;
+ margin-bottom:12px;
+ width:224px;
+}
+dl.sidebar_bottom {
+ background: url(../img/sidebar_bottom.gif) no-repeat bottom center;
+ padding:12px;
+}
+dl.sidebar_bottom dt {
+ color:#fff;
+ font:bold 14px "Lucida Grande" sans-serif;
+ margin-bottom:6px;
+}
+dl.sidebar_bottom dd {padding:3px 0px;}
+#content_sidebar p {padding:10px 0px;}
+p#rss a {
+ background: url(../img/rss.gif) no-repeat left center;
+ color:#000;
+ font:bold 14px "Lucida Grande";
+ padding: 8px 6px 8px 34px;
+ text-decoration:none;
+}
+p#rss a:hover {
+ background: url(../img/rss.gif) no-repeat left center;
+ background-color:#fff;
+ text-decoration:underline;
+}
+
+/* FOOTER */
+
+#footer {background:#444; clear:both;}
+#footer p {padding:12px; color:#999; margin:0; text-align:center;}
+
+/* FEATURES PAGE */
+.feature {
+ background-repeat:no-repeat;
+ background-position:top left;
+ border-bottom:2px solid #ccc;
+ padding-left:150px;
+}
+div#speed {background-image: url(../img/feature_speed.gif);}
+div#light {background-image: url(../img/feature_light.gif);}
+div#power {background-image: url(../img/feature_power.gif);}
+
+.quicklinks_top {
+ background:#868686 url(../img/quicklinks_top.gif) no-repeat top center;
+ float:right;
+ margin-bottom:12px;
+ width:169px;
+}
+ul.quicklinks_bottom {
+ background: url(../img/quicklinks_bottom.gif) no-repeat bottom center;
+ padding:12px;
+}
+ul.quicklinks_bottom li {
+ display:block;
+ padding:3px 0px;
+}
+#content_full ul.quicklinks_bottom li a{
+ color:#d7ff00;
+ display:inline;
+}
+#content_full ul.quicklinks_bottom li a:hover {
+ background:#d7ff00;
+ color:#000;
+}
+
+/* DOCUMENTATION PAGE */
+.sub-framework {
+ border-bottom:2px solid #ccc;
+ margin-bottom: 20px;
+ padding-bottom: 10px;
+}
+
+
+/* ICONS FOR HOMEPAGE */
+
+#icons_top {
+ background: url(../img/icons_top.gif) no-repeat top center;
+ float:left;
+ width:800px;
+}
+#icons_bottom {
+ background: url(../img/icons_bottom.gif) no-repeat bottom center;
+ float:left;
+ width:800px;
+}
+#icons_top dl {
+ color:#fff;
+ float:left;
+ width: 224px;
+ padding: 15px 20px;
+}
+#icons_top dt {
+ background-repeat:no-repeat;
+ background-position:center 2.5em;
+ color:#35d726;
+ font:bold 18px 'Lucida Grande' sans-serif;
+ padding: 6px 6px 150px 6px;
+ text-align:center;
+}
+#icons_top dd {
+ font: 11px "Lucida Grande";
+ line-height:18px;
+ text-align:center;
+}
+dl#speed, dl#light {border-right:1px solid #444;}
+dl#light, dl#power {border-left:1px solid #000;}
+dl#speed dt {background-image: url(../img/icon_speed.gif);}
+dl#light dt {background-image: url(../img/icon_light.gif);}
+dl#power dt {background-image: url(../img/icon_power.gif);}
View
351 doc/rdoc/generators/template/merb/merb.rb
@@ -0,0 +1,351 @@
+module RDoc
+module Page
+
+STYLE = File.read(File.join(File.dirname(__FILE__), 'merb_doc_styles.css'))
+FONTS = ""
+
+###################################################################
+
+CLASS_PAGE = <<HTML
+<div id="%class_seq%">
+<div class='banner'>
+ <span class="file-title-prefix">%classmod%</span><br />%full_name%<br/>
+ In:
+START:infiles
+<a href="#" onclick="jsHref('%full_path_url%');">%full_path%</a>
+IF:cvsurl
+&nbsp;(<a href="#" onclick="jsHref('%cvsurl%');">CVS</a>)
+ENDIF:cvsurl
+END:infiles
+
+IF:parent
+Parent:&nbsp;
+IF:par_url
+ <a href="#" onclick="jsHref('%par_url%');">
+ENDIF:par_url
+%parent%
+IF:par_url
+ </a>
+ENDIF:par_url
+ENDIF:parent
+</div>
+HTML
+
+###################################################################
+
+METHOD_LIST = <<HTML
+ <div id="content">
+IF:diagram
+ <table cellpadding='0' cellspacing='0' border='0' width="100%"><tr><td align="center">
+ %diagram%
+ </td></tr></table>
+ENDIF:diagram
+
+IF:description
+ <div class="description">%description%</div>
+ENDIF:description
+
+IF:requires
+ <div class="sectiontitle">Required Files</div>
+ <ul>
+START:requires
+ <li><a href="#" onclick="jsHref('%href%');">%name%</a></li>
+END:requires
+ </ul>
+ENDIF:requires
+
+IF:toc
+ <div class="sectiontitle">Contents</div>
+ <ul>
+START:toc
+ <li><a href="#" onclick="jsHref('%href%');">%secname%</a></li>
+END:toc
+ </ul>
+ENDIF:toc
+
+IF:methods
+ <div class="sectiontitle">Methods</div>
+ <ul>
+START:methods
+ <li><a href="index.html?a=%href%&name=%name%" >%name%</a></li>
+END:methods
+ </ul>
+ENDIF:methods
+
+IF:includes
+<div class="sectiontitle">Included Modules</div>
+<ul>