require 'cgi' module JsDuck # Ruby-side implementation of class docs Renderer. # Uses PhantomJS to run Docs.Renderer JavaScript. class Renderer def initialize(options={}) @options = options end def render(cls) @cls = cls return [ "
", render_hierarchy, "
", render_private_class_notice, @cls[:doc], render_meta_data, "
", "
", render_member_sections, "
", "
", ].flatten.compact.join end def render_private_class_notice return if !@cls[:private] return [ "

NOTE ", "This is a private utility class for internal use by the framework. ", "Don't rely on its existence.

", ] end def render_meta_data return if !@cls[:meta] || @cls[:meta].length == 0 html = ["" html end def render_hierarchy has_parents = @cls[:extends] && @cls[:extends] != "Object" has_alt_names = @cls[:alternateClassNames].length > 0 has_mixins = @cls[:superclasses].length > 0 return if !has_parents && !has_alt_names && !has_mixins return [ '
',
        render_alternate_class_names,
        render_tree,
        render_mixins,
        '
' ] end def render_alternate_class_names return if @cls[:alternateClassNames].length == 0 return [ "

Alternate names

", @cls[:alternateClassNames].map {|name| "
#{name}
" }, ] end def render_mixins return if @cls[:allMixins].length == 0 return [ "

Mixins

", @cls[:allMixins].map {|name| "
#{render_link(name)}
" }, ] end # Take care of the special case where class has parent for which we have no docs. # In that case the "extends" property exists but "superclasses" is empty. # We still create the tree, but without links in it. def render_tree return if !@cls[:extends] || @cls[:extends] == "Object" tree = ["

Hierarchy

"] if @cls[:superclasses].length > 0 tree + render_class_tree(@cls[:superclasses].concat([@cls[:name]]), {:first => true, :links => true}) else tree + render_class_tree([@cls[:extends], @cls[:name]], {:first => true}) end end def render_class_tree(superclasses, o) return "" if superclasses.length == 0 name = superclasses[0] return [ "
", superclasses.length > 1 ? (o[:links] ? render_link(name) : name) : "#{name}", render_class_tree(superclasses.slice(1, superclasses.length-1), {:links => o[:links]}), "
", ] end def render_link(cls_name) return "#{cls_name}" end def render_member_sections sections = [ {:type => :cfg, :title => "Config options"}, {:type => :property, :title => "Properties"}, {:type => :method, :title => "Methods"}, {:type => :event, :title => "Events"}, {:type => :css_var, :title => "CSS Variables"}, {:type => :css_mixin, :title => "CSS Mixins"}, ] # Skip rendering empty sections sections.map do |sec| members = @cls[:members][sec[:type]] statics = @cls[:statics][sec[:type]] if members.length > 0 || statics.length > 0 [ "
", statics.length == 0 ? "
Defined By
" : "", "

#{sec[:title]}

", render_subsection(members, statics.length > 0 ? "Instance #{sec[:title]}" : nil), render_subsection(statics, "Static #{sec[:title]}"), "
", ] else nil end end end def render_subsection(members, title) return if members.length == 0 index = 0 return [ "
", title ? "
Defined By

#{title}

" : "", members.map {|m| index += 1; render_member(m, index == 1) }, "
", ] end def render_member(m, is_first) # use classname "first-child" when it's first member in its category first_child = is_first ? "first-child" : "" # use classname "expandable" when member has shortened description expandable = m[:shortDoc] ? "expandable" : "not-expandable" # shorthand to owner class owner = m[:owner] # use classname "inherited" when member is not defined in this class inherited = owner == @cls[:name] ? "not-inherited" : "inherited" return [ "
", # leftmost column: expand button "", " ", "", # member name and type + link to owner class and source "
", "", # method params signature or property type signature render_signature(m), "
", # short and long descriptions "
", "
", m[:shortDoc] ? m[:shortDoc] : m[:doc], "
", "
", render_long_doc(m), "
", "
", "
", ] end def render_signature(m) expandable = m[:shortDoc] ? "expandable" : "not-expandable" name = m[:name] before = "" if m[:tagname] == :method && m[:name] == "constructor" before = "new" name = @cls[:name] end if m[:tagname] == :cfg || m[:tagname] == :property || m[:tagname] == :css_var params = " : #{m[:html_type]}" else ps = m[:params].map {|p| render_short_param(p) }.join(", ") params = "( #{ps} )" if m[:tagname] == :method && m[:return][:type] != "undefined" params += " : " + m[:return][:html_type] end end after = "" if m[:protected] after += "protected" end if m[:static] after += "static" end if m[:deprecated] after += "deprecated" end if m[:required] after += "required" end if m[:template] after += "template" end uri = "#!/api/#{m[:owner]}-#{m[:id]}" return [ before, "#{name}", params, after ] end def render_short_param(param) p = param[:html_type] + " " + param[:name] return param[:optional] ? "["+p+"]" : p end def render_long_doc(m) doc = [m[:doc]] if m[:default] && m[:default] != "undefined" doc << "

Defaults to: " + CGI.escapeHTML(m[:default]) + "

" end if m[:deprecated] v = m[:deprecated][:version] ? "since " + m[:deprecated][:version] : "" doc << [ "
", "

This #{m[:tagname]} has been deprecated #{v}

", m[:deprecated][:text], "
", ] end if m[:template] doc << [ "
", "

This is a template method. A hook into the functionality of this class.", "Feel free to override it in child classes.

", "
", ] end doc << render_params_and_return(m) doc end # Handles both rendering of method parameters and return value. # Plus the rendering of object properties, which could also be # functions in which case they too will be rendered with # parameters and return value. def render_params_and_return(item) doc = [] if item[:params] && item[:params].length > 0 params = item[:params] elsif item[:properties] && item[:properties].length > 0 params = item[:properties] # If the name of last property is "return" # remove it from params list and handle it separately afterwards if params.last[:name] == "return" ret = params.last params = params.slice(0, params.length-1) end end if params if item[:type] == "Function" || item[:tagname] == :method || item[:tagname] == :event doc << '

Parameters

' end doc << [ "", ] end if item[:return] doc << render_return(item[:return]) elsif ret doc << render_return(ret) end doc end def render_long_param(p) return [ "
  • ", "#{p[:name]} : ", p[:html_type], p[:optional] ? " (optional)" : "", "
    ", p[:doc], p[:default] ? "

    Defaults to: #{CGI.escapeHTML(p[:default])}

    " : "", p[:properties] && p[:properties].length > 0 ? render_params_and_return(p) : "", "
    ", "
  • ", ] end def render_return(ret) return if ret[:type] == "undefined" return [ "

    Returns

    ", "", ] end end end