Loading lib/jsduck/guide_toc.rb +63 −33 Original line number Diff line number Diff line Loading @@ -4,50 +4,80 @@ module JsDuck # Adds Table of Contents section to guide HTML. class GuideToc def initialize(html, guide_name, max_level=2) @html = html @guide_name = guide_name @min_level = 2 @max_level = max_level # Count the number of heading increments we've seen so far. @heading_counts = Array.new(@max_level+1) { 0 } @toc = [] @new_html = [] end # Inserts table of contents at the top of guide HTML by looking # for headings at or below the specified maximum level. def self.inject(html, guide_name, max_level) toc = [] new_html = [] # Count the number of heading increments we've seen so far; use one fewer # than max_level, since <h1> tags don't go in the TOC. heading_counts = Array.new(max_level - 1) { 0 } html.each_line do |line| if line =~ /^\s*<(h([1-6]))>(.*?)<\/h[1-6]>$/ tag = $1 level = $2.to_i - 1 # ignore <h1> text = Util::HTML.strip_tags($3) id = guide_name + "-section-" + title_to_id(text) if (1...max_level).include? level heading_counts[level - 1] += 1 (level...heading_counts.length).each { |i| heading_counts[i] = 0 } prefix = heading_counts.slice(0...level).join('.') toc << "#{prefix}. <a href='#!/guide/#{id}'>#{text}</a><br/>\n" end new_html << "<#{tag} id='#{id}'>#{text}</#{tag}>\n" def inject! @html.each_line do |line| if line =~ /^\s*<h([1-6])>(.*?)<\/h[1-6]>$/ level = $1.to_i text = Util::HTML.strip_tags($2) id = title_to_id(text) if include_to_toc?(level) increment_heading_count!(level) @toc << toc_entry(level, id, text) end @new_html << "<h#{level} id='#{id}'>#{text}</h#{level}>\n" else new_html << line @new_html << line end end # Inject TOC below first heading if at least 2 items in TOC if toc.length >= 2 new_html.insert(1, [ "<div class='toc'>\n", "<p><strong>Contents</strong></p>\n", toc, "</div>\n", ]) inject_toc! @new_html.flatten.join end private def include_to_toc?(level) (@min_level..@max_level).include?(level) end # Increments count of current heading level. # Resets counts of all the subheadings. def increment_heading_count!(level) @heading_counts[level] += 1 ((level+1)..@max_level).each { |i| @heading_counts[i] = 0 } end new_html.flatten.join def toc_entry(level, id, text) "#{toc_prefix(level)}. <a href='#!/guide/#{id}'>#{text}</a><br/>\n" end def self.title_to_id(title) CGI::escape(title.downcase.gsub(/ /, "-")) def toc_prefix(level) @heading_counts.slice(@min_level..level).join('.') end def title_to_id(title) @guide_name + "-section-" + CGI::escape(title.downcase.gsub(/ /, "-")) end # Injects TOC below first heading if at least 2 items in TOC def inject_toc! return if @toc.length < 2 @new_html.insert(1, [ "<div class='toc'>\n", "<p><strong>Contents</strong></p>\n", @toc, "</div>\n", ]) end end Loading lib/jsduck/guides.rb +1 −1 Original line number Diff line number Diff line Loading @@ -73,7 +73,7 @@ module JsDuck @formatter.doc_context = {:filename => guide[:filename], :linenr => 0} @formatter.images = Img::Dir.new(guide["url"], "guides/#{guide["name"]}") html = @formatter.format(Util::IO.read(guide[:filename])) html = GuideToc.inject(html, guide['name'], @opts.guides_toc_level) html = GuideToc.new(html, guide['name'], @opts.guides_toc_level).inject! html = GuideAnchors.transform(html, guide['name']) # Report unused images (but ignore the icon files) Loading spec/guide_toc_spec.rb +1 −1 Original line number Diff line number Diff line Loading @@ -4,7 +4,7 @@ require "jsduck/guide_toc" describe JsDuck::GuideToc do def inject(html) JsDuck::GuideToc.inject(html, "myguide", max_level) JsDuck::GuideToc.new(html, "myguide", max_level).inject! end describe "With max_level=1" do Loading Loading
lib/jsduck/guide_toc.rb +63 −33 Original line number Diff line number Diff line Loading @@ -4,50 +4,80 @@ module JsDuck # Adds Table of Contents section to guide HTML. class GuideToc def initialize(html, guide_name, max_level=2) @html = html @guide_name = guide_name @min_level = 2 @max_level = max_level # Count the number of heading increments we've seen so far. @heading_counts = Array.new(@max_level+1) { 0 } @toc = [] @new_html = [] end # Inserts table of contents at the top of guide HTML by looking # for headings at or below the specified maximum level. def self.inject(html, guide_name, max_level) toc = [] new_html = [] # Count the number of heading increments we've seen so far; use one fewer # than max_level, since <h1> tags don't go in the TOC. heading_counts = Array.new(max_level - 1) { 0 } html.each_line do |line| if line =~ /^\s*<(h([1-6]))>(.*?)<\/h[1-6]>$/ tag = $1 level = $2.to_i - 1 # ignore <h1> text = Util::HTML.strip_tags($3) id = guide_name + "-section-" + title_to_id(text) if (1...max_level).include? level heading_counts[level - 1] += 1 (level...heading_counts.length).each { |i| heading_counts[i] = 0 } prefix = heading_counts.slice(0...level).join('.') toc << "#{prefix}. <a href='#!/guide/#{id}'>#{text}</a><br/>\n" end new_html << "<#{tag} id='#{id}'>#{text}</#{tag}>\n" def inject! @html.each_line do |line| if line =~ /^\s*<h([1-6])>(.*?)<\/h[1-6]>$/ level = $1.to_i text = Util::HTML.strip_tags($2) id = title_to_id(text) if include_to_toc?(level) increment_heading_count!(level) @toc << toc_entry(level, id, text) end @new_html << "<h#{level} id='#{id}'>#{text}</h#{level}>\n" else new_html << line @new_html << line end end # Inject TOC below first heading if at least 2 items in TOC if toc.length >= 2 new_html.insert(1, [ "<div class='toc'>\n", "<p><strong>Contents</strong></p>\n", toc, "</div>\n", ]) inject_toc! @new_html.flatten.join end private def include_to_toc?(level) (@min_level..@max_level).include?(level) end # Increments count of current heading level. # Resets counts of all the subheadings. def increment_heading_count!(level) @heading_counts[level] += 1 ((level+1)..@max_level).each { |i| @heading_counts[i] = 0 } end new_html.flatten.join def toc_entry(level, id, text) "#{toc_prefix(level)}. <a href='#!/guide/#{id}'>#{text}</a><br/>\n" end def self.title_to_id(title) CGI::escape(title.downcase.gsub(/ /, "-")) def toc_prefix(level) @heading_counts.slice(@min_level..level).join('.') end def title_to_id(title) @guide_name + "-section-" + CGI::escape(title.downcase.gsub(/ /, "-")) end # Injects TOC below first heading if at least 2 items in TOC def inject_toc! return if @toc.length < 2 @new_html.insert(1, [ "<div class='toc'>\n", "<p><strong>Contents</strong></p>\n", @toc, "</div>\n", ]) end end Loading
lib/jsduck/guides.rb +1 −1 Original line number Diff line number Diff line Loading @@ -73,7 +73,7 @@ module JsDuck @formatter.doc_context = {:filename => guide[:filename], :linenr => 0} @formatter.images = Img::Dir.new(guide["url"], "guides/#{guide["name"]}") html = @formatter.format(Util::IO.read(guide[:filename])) html = GuideToc.inject(html, guide['name'], @opts.guides_toc_level) html = GuideToc.new(html, guide['name'], @opts.guides_toc_level).inject! html = GuideAnchors.transform(html, guide['name']) # Report unused images (but ignore the icon files) Loading
spec/guide_toc_spec.rb +1 −1 Original line number Diff line number Diff line Loading @@ -4,7 +4,7 @@ require "jsduck/guide_toc" describe JsDuck::GuideToc do def inject(html) JsDuck::GuideToc.inject(html, "myguide", max_level) JsDuck::GuideToc.new(html, "myguide", max_level).inject! end describe "With max_level=1" do Loading