Commit c37f8fc5 authored by Rene Saarsoo's avatar Rene Saarsoo
Browse files

Move all HTML rendering classes to Render namespace.

Cleaning up the top-level JsDuck namespace.
parent 83c8293f
Loading
Loading
Loading
Loading
+2 −3
Original line number Diff line number Diff line
require 'jsduck/renderer'
require 'jsduck/doc_formatter'
require 'jsduck/render/class'
require 'jsduck/exporter/full'
require 'jsduck/tag_registry'

@@ -10,7 +9,7 @@ module JsDuck
    class App < Full
      def initialize(relations, opts)
        super(relations, opts)
        @renderer = Renderer.new(opts)
        @renderer = Render::Class.new(opts)
      end

      # Returns compacted class data hash which contains an additional
+142 −0
Original line number Diff line number Diff line
require 'jsduck/render/signature'
require 'jsduck/render/tags'
require 'jsduck/render/sidebar'

module JsDuck
  module Render

    # Renders the whole documentation page for a class.
    class Class
      def initialize(opts)
        @opts = opts
      end

      def render(cls)
        @cls = cls
        @signature = Render::Signature.new(cls)

        return [
          "<div>",
            render_sidebar,
            "<div class='doc-contents'>",
              render_tags(@cls),
            "</div>",
            "<div class='members'>",
              render_all_sections,
            "</div>",
          "</div>",
        ].flatten.compact.join
      end

      private

      def render_tags(member)
        Render::Tags.render(member)
      end

      def render_sidebar
        Render::Sidebar.new(@opts).render(@cls)
      end

      def render_all_sections
        sections = [
          {:type => :property, :title => "Properties"},
          {:type => :method, :title => "Methods"},
          {:type => :event, :title => "Events"},
          {:type => :css_var, :title => "CSS Variables"},
          {:type => :css_mixin, :title => "CSS Mixins"},
        ]

        render_configs_section + sections.map {|sec| render_section(sec) }
      end

      def render_configs_section
        configs = @cls[:members][:cfg] + @cls[:statics][:cfg]

        if configs.length > 0
          required, optional = configs.partition {|c| c[:required] }
          return [
            "<div class='members-section'>",
              required.length == 0 ? "<div class='definedBy'>Defined By</div>" : "",
              "<h3 class='members-title icon-cfg'>Config options</h3>",
              render_subsection(required, "Required Config options"),
              render_subsection(optional, required.length > 0 ? "Optional Config options" : nil),
            "</div>",
          ]
        else
          return []
        end
      end

      def render_section(sec)
        members = @cls[:members][sec[:type]]
        statics = @cls[:statics][sec[:type]]

        # Skip rendering empty sections
        if members.length > 0 || statics.length > 0
          return [
            "<div class='members-section'>",
              statics.length == 0 ? "<div class='definedBy'>Defined By</div>" : "",
              "<h3 class='members-title icon-#{sec[:type]}'>#{sec[:title]}</h3>",
              render_subsection(members, statics.length > 0 ? "Instance #{sec[:title]}" : nil),
              render_subsection(statics, "Static #{sec[:title]}"),
            "</div>",
          ]
        else
          return []
        end
      end

      def render_subsection(members, title)
        return if members.length == 0
        index = 0
        return [
          "<div class='subsection'>",
            title ? "<div class='definedBy'>Defined By</div><h4 class='members-subtitle'>#{title}</h3>" : "",
            members.map {|m| index += 1; render_member(m, index == 1) },
          "</div>",
        ]
      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" : ""
        # shorthand to owner class
        owner = m[:owner]
        # is this method inherited from parent?
        inherited = (owner != @cls[:name])

        return [
          "<div id='#{m[:id]}' class='member #{first_child} #{inherited ? 'inherited' : 'not-inherited'}'>",
            # leftmost column: expand button
            "<a href='#' class='side expandable'>",
              "<span>&nbsp;</span>",
            "</a>",
            # member name and type + link to owner class and source
            "<div class='title'>",
              "<div class='meta'>",
                inherited ? "<a href='#!/api/#{owner}' rel='#{owner}' class='defined-in docClass'>#{owner}</a>" :
                            "<span class='defined-in' rel='#{owner}'>#{owner}</span>",
                "<br/>",
                @opts.source ? "<a href='source/#{m[:files][0][:href]}' target='_blank' class='view-source'>view source</a>" : "",
              "</div>",
              # method params signature or property type signature
              @signature.render(m),
            "</div>",
            # short and long descriptions
            "<div class='description'>",
              "<div class='short'>",
                m[:shortDoc] ? m[:shortDoc] : m[:doc],
              "</div>",
              "<div class='long'>",
                render_tags(m),
              "</div>",
            "</div>",
          "</div>",
        ]
      end

    end

  end
end
+97 −0
Original line number Diff line number Diff line
module JsDuck
  module Render

    # Generates HTML for the class hierarchy sidebar inside class
    # documentation.
    class Sidebar
      def initialize(opts)
        @opts = opts
      end

      # Renders a sidebar for given class.
      # Returns Array of HTML or nil.
      def render(cls)
        items = [
          render_alternate_class_names(cls[:alternateClassNames]),
          render_tree(cls),
          render_dependencies(cls[:mixins], "Mixins"),
          render_dependencies(cls[:parentMixins], "Inherited mixins"),
          render_dependencies(cls[:requires], "Requires"),
          render_dependencies(cls[:subclasses], "Subclasses"),
          render_dependencies(cls[:mixedInto], "Mixed into"),
          render_dependencies(cls[:uses], "Uses"),
          render_files(cls[:files])
        ]

        if items.compact.length > 0
          return ['<pre class="hierarchy">', items, '</pre>']
        else
          return nil
        end
      end

      private

      def render_alternate_class_names(names)
        return if names.length == 0

        return [
          "<h4>Alternate names</h4>",
          names.map {|name| "<div class='alternate-class-name'>#{name}</div>" },
        ]
      end

      def render_dependencies(names, title)
        return if !names || names.length == 0

        return [
          "<h4>#{title}</h4>",
          names.map {|name| "<div class='dependency'>#{name.exists? ? render_link(name) : name}</div>" },
        ]
      end

      def render_files(files)
        return if !@opts.source || files.length == 0 || files[0][:filename] == ""

        return [
          "<h4>Files</h4>",
          files.map do |file|
            url = "source/" + file[:href]
            title = File.basename(file[:filename])
            "<div class='dependency'><a href='#{url}' target='_blank'>#{title}</a></div>"
          end
        ]
      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(cls)
        return if !cls[:extends] || cls[:extends] == "Object"

        return [
          "<h4>Hierarchy</h4>",
          render_class_tree(cls[:superclasses] + [cls[:name]])
        ]
      end

      def render_class_tree(classes, i=0)
        return "" if classes.length <= i

        name = classes[i]
        return [
          "<div class='subclass #{i == 0 ? 'first-child' : ''}'>",
            classes.length-1 == i ? "<strong>#{name}</strong>" : (name.exists? ? render_link(name) : name),
            render_class_tree(classes, i+1),
          "</div>",
        ]
      end

      def render_link(cls_name)
        "<a href='#!/api/#{cls_name}' rel='#{cls_name}' class='docClass'>#{cls_name}</a>"
      end

    end

  end
end
+93 −0
Original line number Diff line number Diff line
require 'jsduck/render/tags'

module JsDuck
  module Render

    # Performs the rendering of member signatures.
    class Signature
      # Initializes the renderer for rendering members of the given
      # class.
      def initialize(cls)
        @cls = cls
      end

      # Renders signature of the given member.
      def render(member)
        # Keep the code simpler by not passing around the member hash
        @m = member

        return [
          render_new,
          render_link,
          render_type,
          render_tag_signature,
        ]
      end

      private

      def render_new
        constructor? ? "<strong class='new-keyword'>new</strong>" : ""
      end

      def render_link
        "<a href='#{render_url}' class='name #{render_expandable}'>#{render_name}</a>"
      end

      def render_url
        "#!/api/#{@m[:owner]}-#{@m[:id]}"
      end

      def render_expandable
        @m[:shortDoc] ? "expandable" : "not-expandable"
      end

      def render_name
        constructor? ? @cls[:name] : @m[:name]
      end

      def constructor?
        @m[:tagname] == :method && @m[:name] == "constructor"
      end

      def render_type
        if like_property?
          render_property_type
        else
          render_params + render_return
        end
      end

      def like_property?
        @m[:tagname] == :cfg || @m[:tagname] == :property || @m[:tagname] == :css_var
      end

      def render_property_type
        "<span> : #{@m[:html_type]}</span>"
      end

      def render_params
        ps = @m[:params].map {|p| render_single_param(p) }.join(", ")
        "( <span class='pre'>#{ps}</span> )"
      end

      def render_single_param(param)
        param[:optional] ? "["+param[:name]+"]" : param[:name]
      end

      def render_return
        method_with_return? ? (" : " + @m[:return][:html_type]) : ""
      end

      def method_with_return?
        @m[:tagname] == :method && @m[:return] != nil
      end

      def render_tag_signature
        Render::Tags.render_signature(@m)
      end

    end

  end
end
+99 −0
Original line number Diff line number Diff line
require 'jsduck/util/html'
require 'jsduck/util/singleton'

module JsDuck
  module Render

    # Renders params, return values and everything else that can have
    # nested subproperties.
    class Subproperties
      include Util::Singleton

      # Renders object properties, which could also be functions in
      # which case they will be rendered with parameters and return
      # value.
      def render(item)
        doc = []

        if item[:type] == "Function"
          params = item[:properties]
          # If the name of last property is "return" remove it from
          # properties list and format as a return value afterwards.
          if item[:properties].last[:name] == "return"
            ret = params.last
            params = params.slice(0, params.length-1)
          end

          doc << render_params(params)
          doc << render_return(ret) if ret
        else
          doc << render_list(item[:properties])
        end

        doc
      end

      def render_params(params)
        return [
          '<h3 class="pa">Parameters</h3>',
          render_list(params),
        ]
      end

      def render_list(params)
        return [
          "<ul>",
          params.map {|p| render_single_param(p) },
          "</ul>",
        ]
      end

      def render_single_param(p)
        return [
          "<li>",
            "<span class='pre'>#{p[:name]}</span> : ",
            p[:html_type],
            p[:optional] ? " (optional)" : "",
            "<div class='sub-desc'>",
              p[:doc],
              p[:default] ? "<p>Defaults to: <code>#{Util::HTML.escape(p[:default])}</code></p>" : "",
              p[:properties] && p[:properties].length > 0 ? render(p) : "",
            "</div>",
          "</li>",
        ]
      end

      def render_return(ret)
        return [
          "<h3 class='pa'>Returns</h3>",
          "<ul>",
            "<li>",
              "<span class='pre'>#{ret[:html_type]}</span>",
              "<div class='sub-desc'>",
                ret[:doc],
                ret[:properties] && ret[:properties].length > 0 ? render(ret) : "",
              "</div>",
            "</li>",
          "</ul>",
        ]
      end

      def render_throws(throws)
        return [
          "<h3 class='pa'>Throws</h3>",
          "<ul>",
            throws.map do |thr|
              [
                "<li>",
                  "<span class='pre'>#{thr[:html_type]}</span>",
                  "<div class='sub-desc'>#{thr[:doc]}</div>",
                "</li>",
              ]
            end,
          "</ul>",
        ]
      end
    end

  end
end
Loading