Commit 75f8c2d3 authored by Rene Saarsoo's avatar Rene Saarsoo
Browse files

Move Categories related classes to subdir.

parent 64f47067
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -3,7 +3,7 @@ require 'jsduck/welcome'
require 'jsduck/guides'
require 'jsduck/videos'
require 'jsduck/examples'
require 'jsduck/categories'
require 'jsduck/categories/factory'
require 'jsduck/doc_formatter'

module JsDuck
@@ -34,7 +34,7 @@ module JsDuck
      @guides = Guides.create(@opts.guides, doc_formatter, @opts)
      @videos = Videos.create(@opts.videos)
      @examples = Examples.create(@opts.examples, @opts)
      @categories = Categories.create(@opts.categories_path, doc_formatter, @relations)
      @categories = Categories::Factory.create(@opts.categories_path, doc_formatter, @relations)
    end

    # Writes out the assets that can be written out separately:

lib/jsduck/auto_categories.rb

deleted100644 → 0
+0 −80
Original line number Diff line number Diff line
module JsDuck

  # Automatically divides all available classes into categories
  class AutoCategories
    def initialize(relations)
      @relations = relations
    end

    # Performs the generation
    def generate
      # list names of all public classes
      class_names = @relations.to_a.find_all {|cls| !cls[:private] }.map {|cls| cls[:name] }

      # divide classes into top-level categories by namespace
      categories = categorize(class_names)

      # in each category, create sub-categories
      categories.each_pair do |ns, classes|
        categories[ns] = categorize(classes, 1)
      end

      # Turn categories hash into array, sort everything
      categories_array = []
      categories.each_pair do |ns, groups|
        groups_array = []
        groups.each_pair do |gns, classes|
          groups_array << {
            "name" => gns,
            "classes" => classes.sort
          }
        end
        groups_array.sort! {|a, b| cat_compare(a, b) }
        categories_array << {
          "name" => ns,
          "groups" => groups_array
        }
      end
      categories_array.sort! {|a, b| cat_compare(a, b) }

      return categories_array
    end

    # Divides classes into categories by namespace.  Collapses
    # categories having only one class into a category "Others..."
    def categorize(class_names, level=0)
      categories = {}
      class_names.each do |name|
        ns = name.split(/\./)[level] || name.split(/\./)[0]
        categories[ns] = [] unless categories[ns]
        categories[ns] << name
      end

      globals = []
      categories.each_pair do |ns, classes|
        if classes.length == 1
          globals << classes[0]
          categories.delete(ns)
        end
      end
      if globals.length > 0
        categories["Others..."] = globals
      end

      categories
    end

    # Comparison function for sorting categories that always places
    # "Others..." category at the end.
    def cat_compare(a, b)
      if a["name"] == "Others..."
        1
      elsif b["name"] == "Others..."
        -1
      else
        a["name"] <=> b["name"]
      end
    end
  end

end

lib/jsduck/categories.rb

deleted100644 → 0
+0 −111
Original line number Diff line number Diff line
require 'jsduck/logger'
require 'jsduck/file_categories'
require 'jsduck/auto_categories'
require 'jsduck/categories_class_name'

module JsDuck

  # Reads in categories and outputs them as HTML div
  class Categories
    def self.create(filename, doc_formatter, relations)
      if filename
        categories = FileCategories.new(filename, relations)
      else
        categories = AutoCategories.new(relations)
      end
      Categories.new(categories.generate, doc_formatter, relations)
    end

    def initialize(categories, doc_formatter, relations={})
      @categories = categories
      @class_name = CategoriesClassName.new(doc_formatter, relations)
    end

    # Returns HTML listing of classes divided into categories
    def to_html(style="")
      html = @categories.map do |category|
        [
          "<div class='section'>",
          "<h1>#{category['name']}</h1>",
          render_columns(category['groups']),
          "<div style='clear:both'></div>",
          "</div>",
        ]
      end.flatten.join("\n")

      return <<-EOHTML
        <div id='categories-content' style='#{style}'>
            #{html}
        </div>
      EOHTML
    end

    def render_columns(groups)
      align = ["left-column", "middle-column", "right-column"]
      i = -1
      return split(groups, 3).map do |col|
        i += 1
        [
          "<div class='#{align[i]}'>",
          render_groups(col),
          "</div>",
        ]
      end
    end

    def render_groups(groups)
      return groups.map do |g|
        [
          "<h3>#{g['name']}</h3>",
          "<ul class='links'>",
          g["classes"].map {|cls| "<li>" + @class_name.render(cls) + "</li>" },
          "</ul>",
        ]
      end
    end

    # Splits the array of items into n chunks so that the sum of
    # largest chunk is as small as possible.
    #
    # This is a brute-force implementation - we just try all the
    # combinations and choose the best one.
    def split(items, n)
      if n == 1
        [items]
      elsif items.length <= n
        Array.new(n) {|i| items[i] ? [items[i]] : [] }
      else
        min_max = nil
        min_arr = nil
        i = 0
        while i <= items.length-n
          i += 1
          # Try placing 1, 2, 3, ... items to first chunk.
          # Calculate the remaining chunks recursively.
          cols = [items[0,i]] + split(items[i, items.length], n-1)
          max = max_sum(cols)
          # Is this the optimal solution so far? Remember it.
          if !min_max || max < min_max
            min_max = max
            min_arr = cols
          end
        end
        min_arr
      end
    end

    def max_sum(cols)
      cols.map {|col| sum(col) }.max
    end

    # Finds the total size of items in array
    #
    # The size of one item is it's number of classes + the space for header
    def sum(arr)
      header_size = 3
      arr.reduce(0) {|sum, item| sum + item["classes"].length + header_size }
    end

  end

end
+83 −0
Original line number Diff line number Diff line
module JsDuck
  module Categories

    # Automatically divides all available classes into categories
    class Auto
      def initialize(relations)
        @relations = relations
      end

      # Performs the generation
      def generate
        # list names of all public classes
        class_names = @relations.to_a.find_all {|cls| !cls[:private] }.map {|cls| cls[:name] }

        # divide classes into top-level categories by namespace
        categories = categorize(class_names)

        # in each category, create sub-categories
        categories.each_pair do |ns, classes|
          categories[ns] = categorize(classes, 1)
        end

        # Turn categories hash into array, sort everything
        categories_array = []
        categories.each_pair do |ns, groups|
          groups_array = []
          groups.each_pair do |gns, classes|
            groups_array << {
              "name" => gns,
              "classes" => classes.sort
            }
          end
          groups_array.sort! {|a, b| cat_compare(a, b) }
          categories_array << {
            "name" => ns,
            "groups" => groups_array
          }
        end
        categories_array.sort! {|a, b| cat_compare(a, b) }

        return categories_array
      end

      # Divides classes into categories by namespace.  Collapses
      # categories having only one class into a category "Others..."
      def categorize(class_names, level=0)
        categories = {}
        class_names.each do |name|
          ns = name.split(/\./)[level] || name.split(/\./)[0]
          categories[ns] = [] unless categories[ns]
          categories[ns] << name
        end

        globals = []
        categories.each_pair do |ns, classes|
          if classes.length == 1
            globals << classes[0]
            categories.delete(ns)
          end
        end
        if globals.length > 0
          categories["Others..."] = globals
        end

        categories
      end

      # Comparison function for sorting categories that always places
      # "Others..." category at the end.
      def cat_compare(a, b)
        if a["name"] == "Others..."
          1
        elsif b["name"] == "Others..."
          -1
        else
          a["name"] <=> b["name"]
        end
      end

    end

  end
end
+63 −0
Original line number Diff line number Diff line
module JsDuck
  module Categories

    # Renders class names for class categories page.
    class ClassName
      def initialize(doc_formatter, relations={})
        @doc_formatter = doc_formatter
        @relations = relations
      end

      # Renders the class name as a link or plain text.
      #
      # For new classes appends a star behind class name.  For classes
      # with new members appends list n small stars behind class name
      # (reflecting the number of new members).
      def render(name)
        cls = @relations[name]
        if cls
          @doc_formatter.link(name, nil, name) + render_new_label(cls)
        else
          name
        end
      end

      private

      # Adds small star to new classes in the current version.
      def render_new_label(cls)
        if cls[:new]
          "&nbsp;<span class='new-class' title='New class'>#{stars(1)}</span>"
        else
          n = new_members_count(cls)
          if n > 0
            title = "#{n} new member#{(n>1) ? 's' : ''}"
            "&nbsp;<span class='new-members' title='#{title}'>#{stars(n)}</span>"
          else
            ""
          end
        end
      end

      # Produces string of n stars.
      # First 3 stars are rendered as "<unicode-star>", the following as "+".
      # At max 15 stars are rendered.
      def stars(n)
        if n > 15
          stars(3) + ("+" * (15-3))
        elsif n > 3
          stars(3) + ("+" * (n-3))
        else
          "&#9733;" * n
        end
      end

      # Returns number of new members the class has in the current version
      def new_members_count(cls)
        cls.find_members(:local => true).find_all {|m| m[:new] && !m[:private] }.length
      end

    end

  end
end
Loading