jsduck 0.4 → 0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.md CHANGED
@@ -117,26 +117,35 @@ JsDuck depends on [json][], [RDiscount][], and [parallel][] plus [RSpec][] for t
117
117
  Usage
118
118
  -----
119
119
 
120
- Just call it from command line with output directory and a list of
121
- JavaScript files:
120
+ Just call it from command line with output directory and a directory
121
+ containing your JavaScript files:
122
122
 
123
- $ jsduck --verbose --output some/dir your/project/*.js
123
+ $ jsduck --verbose --output some/dir your/project/js
124
124
 
125
- To specify a lot of files you should probably create a script that
126
- generates a file list and passes it through `xargs` to `jsduck`.
125
+ The `--verbose` flag creates a lot of output, but at least you will
126
+ see that something is happening.
127
127
 
128
- For example to generate documentation for ExtJS:
128
+ You pass in both directories and JavaScript files. For example to
129
+ generate docs for ExtJS 3, the simplest way is the following:
129
130
 
130
- $ find ext-3.3.1/src/ -name '*.js' | egrep -v 'locale/|test/|adapter/' | xargs jsduck -v -o output/
131
+ $ jsduck -v -o output/ ext-3.3.1/src/
131
132
 
132
- The `--verbose` flag creates a lot of output, but at least you will
133
- see that something is happening.
133
+ But this will give you a bunch of warnings, so you should better
134
+ create a script that takes just the files really needed and passes
135
+ them through `xargs` to `jsduck`:
134
136
 
135
- Here's how the resulting documentation will look:
137
+ $ find ext-3.3.1/src/ -name '*.js' | egrep -v 'locale/|test/|adapter/' | xargs jsduck -v -o output/
138
+
139
+ Here's how the resulting documentation will look (ExtJS 3.3.1):
136
140
 
137
141
  * [JsDuck generated documentation](http://triin.net/temp/jsduck/)
138
142
  * [Official ExtJS documentation](http://dev.sencha.com/deploy/dev/docs/) (for comparison)
139
143
 
144
+ Here's the same for ExtJS 4 Preview 5:
145
+
146
+ * [JsDuck generated documentation](http://triin.net/temp/jsduck4/)
147
+ * [Official ExtJS documentation](http://dev.sencha.com/deploy/ext-4.0-pr5/docs/) (for comparison)
148
+
140
149
 
141
150
  Documentation
142
151
  -------------
@@ -178,8 +187,6 @@ missing.
178
187
  Missing features and TODO
179
188
  -------------------------
180
189
 
181
- * Search, not just searching from official ExtJS documentation.
182
-
183
190
  * Support for custom @tags. Ext-doc supports this, I personally have
184
191
  never used this feature, so I'm thinking it's not really needed.
185
192
 
@@ -189,12 +196,21 @@ Copying
189
196
 
190
197
  JsDuck is distributed under the terms of the GNU General Public License version 3.
191
198
 
192
- JsDuck was developed by [Rene Saarsoo](http://triin.net).
199
+ JsDuck was developed by [Rene Saarsoo](http://triin.net),
200
+ with contributions from [Ondřej Jirman](https://github.com/megous).
193
201
 
194
202
 
195
203
  Changelog
196
204
  ---------
197
205
 
206
+ * 0.5 - Search and export
207
+ * Search from the actually generated docs (not through sencha.com)
208
+ * JSON export with --json switch.
209
+ * Listing of mixed into classes.
210
+ * Option to control or disable parallel processing.
211
+ * Accepting directories as input (those are scanned for .js files)
212
+ * Many bug fixes.
213
+
198
214
  * 0.4 - Ext4 support
199
215
  * Support for Ext.define() syntax from ExtJS 4.
200
216
  * Showing @xtype and @author information on generated pages.
data/Rakefile CHANGED
@@ -3,11 +3,11 @@ require 'rake'
3
3
 
4
4
  $LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
5
5
 
6
- require 'spec'
7
- require 'spec/rake/spectask'
8
- Spec::Rake::SpecTask.new(:spec) do |spec|
9
- spec.spec_opts = ["--color"]
10
- spec.spec_files = FileList["spec/**/*_spec.rb"]
6
+ require 'rspec'
7
+ require 'rspec/core/rake_task'
8
+ RSpec::Core::RakeTask.new(:spec) do |spec|
9
+ spec.rspec_opts = ["--color"]
10
+ spec.pattern = "spec/**/*_spec.rb"
11
11
  end
12
12
 
13
13
  desc "Build gem locally"
data/bin/jsduck CHANGED
@@ -24,7 +24,7 @@ app = JsDuck::App.new
24
24
  app.template_dir = File.dirname(File.dirname(__FILE__)) + "/template"
25
25
 
26
26
  opts = OptionParser.new do | opts |
27
- opts.banner = "Usage: jsduck [options] files..."
27
+ opts.banner = "Usage: jsduck [options] files/dirs..."
28
28
 
29
29
  opts.on('-o', '--output=PATH', "Directory to output all this amazing documentation.") do |path|
30
30
  app.output_dir = path
@@ -34,6 +34,17 @@ opts = OptionParser.new do | opts |
34
34
  app.template_dir = path
35
35
  end
36
36
 
37
+ opts.on('--json', "Produces JSON export instead of HTML documentation.") do |path|
38
+ app.export = :json
39
+ end
40
+
41
+ # For debugging it's often useful to set --processes=0 to get deterministic results.
42
+ opts.on('-p', '--processes=COUNT', "The number of parallel processes to use.",
43
+ "Defaults to the number of processors/cores.",
44
+ "Set to 0 to disable parallel processing completely.") do |count|
45
+ app.processes = count.to_i
46
+ end
47
+
37
48
  opts.on('-v', '--verbose', "This will fill up your console.") do
38
49
  app.verbose = true
39
50
  end
@@ -44,7 +55,20 @@ opts = OptionParser.new do | opts |
44
55
  end
45
56
  end
46
57
 
47
- app.input_files = opts.parse!(ARGV)
58
+ js_files = []
59
+ # scan directories for .js files
60
+ opts.parse!(ARGV).each do |fname|
61
+ if File.exists?(fname)
62
+ if File.directory?(fname)
63
+ Dir[fname+"/**/*.js"].each {|f| js_files << f }
64
+ else
65
+ js_files << fname
66
+ end
67
+ else
68
+ puts "Warning: File #{fname} not found"
69
+ end
70
+ end
71
+ app.input_files = js_files
48
72
 
49
73
  if app.input_files.length == 0
50
74
  puts "You should specify some input files, otherwise there's nothing I can do :("
data/jsduck.gemspec CHANGED
@@ -2,8 +2,8 @@ Gem::Specification.new do |s|
2
2
  s.required_rubygems_version = ">= 1.3.7"
3
3
 
4
4
  s.name = 'jsduck'
5
- s.version = '0.4'
6
- s.date = '2011-02-28'
5
+ s.version = '0.5'
6
+ s.date = '2011-03-29'
7
7
  s.summary = "Simple JavaScript Duckumentation generator"
8
8
  s.description = "Better ext-doc like JavaScript documentation generator for ExtJS"
9
9
  s.homepage = "https://github.com/nene/jsduck"
data/lib/jsduck/app.rb CHANGED
@@ -5,12 +5,14 @@ require 'jsduck/source_formatter'
5
5
  require 'jsduck/class'
6
6
  require 'jsduck/tree'
7
7
  require 'jsduck/tree_icons'
8
- require 'jsduck/subclasses'
8
+ require 'jsduck/members'
9
+ require 'jsduck/relations'
9
10
  require 'jsduck/page'
11
+ require 'jsduck/exporter'
10
12
  require 'jsduck/timer'
13
+ require 'jsduck/parallel_wrap'
11
14
  require 'json'
12
15
  require 'fileutils'
13
- require 'parallel'
14
16
 
15
17
  module JsDuck
16
18
 
@@ -21,32 +23,53 @@ module JsDuck
21
23
  attr_accessor :template_dir
22
24
  attr_accessor :input_files
23
25
  attr_accessor :verbose
26
+ attr_accessor :export
24
27
 
25
28
  def initialize
26
29
  @output_dir = nil
27
30
  @template_dir = nil
28
31
  @input_files = []
29
32
  @verbose = false
33
+ @export = nil
30
34
  @timer = Timer.new
35
+ @parallel = ParallelWrap.new
36
+ end
37
+
38
+ # Sets the nr of parallel processes to use.
39
+ # Set to 0 to disable parallelization completely.
40
+ def processes=(count)
41
+ @parallel = ParallelWrap.new(:in_processes => count)
31
42
  end
32
43
 
33
44
  # Call this after input parameters set
34
45
  def run
35
- copy_template(@template_dir, @output_dir)
46
+ clear_dir(@output_dir)
47
+ if @export
48
+ FileUtils.mkdir(@output_dir)
49
+ init_output_dirs(@output_dir)
50
+ else
51
+ copy_template(@template_dir, @output_dir)
52
+ end
36
53
 
37
54
  parsed_files = @timer.time(:parsing) { parallel_parse(@input_files) }
38
55
  result = @timer.time(:aggregating) { aggregate(parsed_files) }
39
- classes = @timer.time(:aggregating) { filter_classes(result) }
40
- @timer.time(:generating) { write_tree(@output_dir+"/output/tree.js", classes) }
41
- @timer.time(:generating) { write_pages(@output_dir+"/output", classes) }
56
+ relations = @timer.time(:aggregating) { filter_classes(result) }
57
+
58
+ if @export == :json
59
+ @timer.time(:generating) { write_json(@output_dir+"/output", relations) }
60
+ else
61
+ @timer.time(:generating) { write_tree(@output_dir+"/output/tree.js", relations) }
62
+ @timer.time(:generating) { write_members(@output_dir+"/output/members.js", relations) }
63
+ @timer.time(:generating) { write_pages(@output_dir+"/output", relations) }
64
+ end
42
65
 
43
66
  @timer.report if @verbose
44
67
  end
45
68
 
46
69
  # Parses the files in parallel using as many processes as available CPU-s
47
70
  def parallel_parse(filenames)
48
- src = SourceFormatter.new(@output_dir + "/source")
49
- Parallel.map(filenames) do |fname|
71
+ src = SourceFormatter.new(@output_dir + "/source", @export ? :format_pre : :format_page)
72
+ @parallel.map(filenames) do |fname|
50
73
  puts "Parsing #{fname} ..." if @verbose
51
74
  code = IO.read(fname)
52
75
  {
@@ -70,10 +93,10 @@ module JsDuck
70
93
  # Filters out class-documentations, converting them to Class objects.
71
94
  # For each other type, prints a warning message and discards it
72
95
  def filter_classes(docs)
73
- classes = {}
96
+ classes = []
74
97
  docs.each do |d|
75
98
  if d[:tagname] == :class
76
- classes[d[:name]] = Class.new(d, classes)
99
+ classes << Class.new(d)
77
100
  else
78
101
  type = d[:tagname].to_s
79
102
  name = d[:name]
@@ -82,38 +105,64 @@ module JsDuck
82
105
  puts "Warning: Ignoring #{type}: #{name} in #{file} line #{line}"
83
106
  end
84
107
  end
85
- classes.values
108
+ Relations.new(classes)
86
109
  end
87
110
 
88
- # Given array of doc-objects, generates namespace tree and writes in
111
+ # Given all classes, generates namespace tree and writes it
89
112
  # in JSON form into a file.
90
- def write_tree(filename, docs)
91
- tree = Tree.new.create(docs)
113
+ def write_tree(filename, relations)
114
+ tree = Tree.new.create(relations.classes)
92
115
  icons = TreeIcons.new.extract_icons(tree)
93
116
  js = "Docs.classData = " + JSON.generate( tree ) + ";"
94
117
  js += "Docs.icons = " + JSON.generate( icons ) + ";"
95
118
  File.open(filename, 'w') {|f| f.write(js) }
96
119
  end
97
120
 
121
+ # Given all classes, generates members data for search and writes in
122
+ # in JSON form into a file.
123
+ def write_members(filename, relations)
124
+ members = Members.new.create(relations.classes)
125
+ js = "Docs.membersData = " + JSON.generate( {:data => members} ) + ";"
126
+ File.open(filename, 'w') {|f| f.write(js) }
127
+ end
128
+
98
129
  # Writes documentation page for each class
99
130
  # We do it in parallel using as many processes as available CPU-s
100
- def write_pages(path, docs)
101
- subclasses = Subclasses.new(docs)
131
+ def write_pages(path, relations)
102
132
  cache = {}
103
- Parallel.each(docs) do |cls|
133
+ @parallel.each(relations.classes) do |cls|
104
134
  filename = path + "/" + cls[:name] + ".html"
105
135
  puts "Writing to #{filename} ..." if @verbose
106
- html = Page.new(cls, subclasses, cache).to_html
136
+ html = Page.new(cls, relations, cache).to_html
107
137
  File.open(filename, 'w') {|f| f.write(html) }
108
138
  end
109
139
  end
110
140
 
141
+ # Writes JSON export file for each class
142
+ def write_json(path, relations)
143
+ exporter = Exporter.new(relations)
144
+ @parallel.each(relations.classes) do |cls|
145
+ filename = path + "/" + cls[:name] + ".json"
146
+ puts "Writing to #{filename} ..." if @verbose
147
+ hash = exporter.export(cls)
148
+ json = JSON.pretty_generate(hash)
149
+ File.open(filename, 'w') {|f| f.write(json) }
150
+ end
151
+ end
152
+
111
153
  def copy_template(template_dir, dir)
112
154
  puts "Copying template files to #{dir}..." if @verbose
155
+ FileUtils.cp_r(template_dir, dir)
156
+ init_output_dirs(dir)
157
+ end
158
+
159
+ def clear_dir(dir)
113
160
  if File.exists?(dir)
114
161
  FileUtils.rm_r(dir)
115
162
  end
116
- FileUtils.cp_r(template_dir, dir)
163
+ end
164
+
165
+ def init_output_dirs(dir)
117
166
  FileUtils.mkdir(dir + "/output")
118
167
  FileUtils.mkdir(dir + "/source")
119
168
  end
data/lib/jsduck/class.rb CHANGED
@@ -4,9 +4,11 @@ module JsDuck
4
4
  # methods on it. Otherwise it acts like Hash, providing the []
5
5
  # method.
6
6
  class Class
7
- def initialize(doc, classes={})
7
+ attr_accessor :relations
8
+
9
+ def initialize(doc)
8
10
  @doc = doc
9
- @classes = classes
11
+ @relations = nil
10
12
  end
11
13
 
12
14
  def [](key)
@@ -15,13 +17,38 @@ module JsDuck
15
17
 
16
18
  # Returns instance of parent class, or nil if there is none
17
19
  def parent
18
- @doc[:extends] ? @classes[@doc[:extends]] : nil
20
+ @doc[:extends] ? lookup(@doc[:extends]) : nil
21
+ end
22
+
23
+ # Returns array of ancestor classes.
24
+ # Example result when asking ancestors of MyPanel might be:
25
+ #
26
+ # [Ext.util.Observable, Ext.Component, Ext.Panel]
27
+ #
28
+ def superclasses
29
+ p = parent
30
+ p ? p.superclasses + [p] : []
19
31
  end
20
32
 
21
33
  # Returns array of mixin class instances.
22
34
  # Returns empty array if no mixins
23
35
  def mixins
24
- @doc[:mixins] ? @doc[:mixins].collect {|classname| @classes[classname] } : []
36
+ @doc[:mixins] ? @doc[:mixins].collect {|classname| lookup(classname) }.compact : []
37
+ end
38
+
39
+ # Looks up class object by name
40
+ # When not found, prints warning message.
41
+ def lookup(classname)
42
+ if @relations[classname]
43
+ @relations[classname]
44
+ elsif classname != "Object"
45
+ puts "Warning: Class #{classname} not found in #{@doc[:filename]} line #{@doc[:linenr]}"
46
+ end
47
+ end
48
+
49
+ # Returns copy of @doc hash
50
+ def to_hash
51
+ @doc.clone
25
52
  end
26
53
 
27
54
  # Returns true when this class inherits from the specified class.
@@ -5,11 +5,27 @@ module JsDuck
5
5
 
6
6
  # Formats doc-comments
7
7
  class DocFormatter
8
- # Initializes instance to work in context of particular class, so
8
+ # CSS class to add to each link
9
+ attr_accessor :css_class
10
+
11
+ # Template for the href URL.
12
+ # Can contain %cls% which is replaced with actual classname.
13
+ # Also '#' and member name is appended to link if needed
14
+ attr_accessor :url_template
15
+
16
+ # Sets up instance to work in context of particular class, so
9
17
  # that when {@link #blah} is encountered it knows that
10
18
  # Context#blah is meant.
11
- def initialize(context)
12
- @context = context
19
+ attr_accessor :context
20
+
21
+ # Maximum length for text that doesn't get shortened, defaults to 120
22
+ attr_accessor :max_length
23
+
24
+ def initialize
25
+ @context = ""
26
+ @css_class = nil
27
+ @url_template = "%cls%"
28
+ @max_length = 120
13
29
  end
14
30
 
15
31
  # Replaces {@link Class#member link text} in given string with
@@ -17,7 +33,7 @@ module JsDuck
17
33
  # attribute links will also contain ext:cls and ext:member
18
34
  # attributes.
19
35
  def replace(input)
20
- input.gsub(/\{@link +(\S*?)(?: +(.+?))?\}/) do
36
+ input.gsub(/\{@link\s+(\S*?)(?:\s+(.+?))?\}/m) do
21
37
  target = $1
22
38
  text = $2
23
39
  if target =~ /^(.*)#(.*)$/
@@ -28,11 +44,6 @@ module JsDuck
28
44
  member = false
29
45
  end
30
46
 
31
- # Construct link attributes
32
- href = " href=\"output/#{cls}.html" + (member ? "#" + cls + "-" + member : "") + '"'
33
- ext_cls = ' ext:cls="' + cls + '"'
34
- ext_member = member ? ' ext:member="' + member + '"' : ""
35
-
36
47
  # Construct link text
37
48
  if text
38
49
  text = text
@@ -42,10 +53,20 @@ module JsDuck
42
53
  text = cls
43
54
  end
44
55
 
45
- "<a" + href + ext_cls + ext_member + ">" + text + "</a>"
56
+ link(cls, member, text)
46
57
  end
47
58
  end
48
59
 
60
+ # Creates HTML link to class and/or member
61
+ def link(cls, member, label)
62
+ anchor = member ? "#" + member : ""
63
+ url = @url_template.sub(/%cls%/, cls) + anchor
64
+ href = ' href="' + url + '"'
65
+ rel = ' rel="' + cls + anchor + '"'
66
+ cssCls = @css_class ? ' class="' + @css_class + '"' : ''
67
+ "<a" + href + rel + cssCls + ">" + label + "</a>"
68
+ end
69
+
49
70
  # Formats doc-comment for placement into HTML.
50
71
  # Renders it with Markdown-formatter and replaces @link-s.
51
72
  def format(input)
@@ -64,6 +85,36 @@ module JsDuck
64
85
  replace(RDiscount.new(input).to_html)
65
86
  end
66
87
 
88
+ # Shortens text if needed.
89
+ #
90
+ # 116 chars is also where ext-doc makes its cut, but unlike
91
+ # ext-doc we only make the cut when there's more than 120 chars.
92
+ #
93
+ # This way we don't get stupid expansions like:
94
+ #
95
+ # Blah blah blah some text...
96
+ #
97
+ # expanding to:
98
+ #
99
+ # Blah blah blah some text.
100
+ #
101
+ # Ellipsis is only added when input actually gets shortened.
102
+ def shorten(input)
103
+ if too_long?(input)
104
+ strip_tags(input)[0..(@max_length-4)] + "..."
105
+ else
106
+ input
107
+ end
108
+ end
109
+
110
+ # Returns true when input should get shortened.
111
+ def too_long?(input)
112
+ strip_tags(input).length > @max_length
113
+ end
114
+
115
+ def strip_tags(str)
116
+ str.gsub(/<.*?>/, "")
117
+ end
67
118
  end
68
119
 
69
120
  end
@@ -200,7 +200,7 @@ module JsDuck
200
200
  def at_xtype
201
201
  match(/@xtype/)
202
202
  add_tag(:xtype)
203
- maybe_name
203
+ maybe_ident_chain(:name)
204
204
  skip_white
205
205
  end
206
206
 
@@ -0,0 +1,75 @@
1
+ require 'jsduck/doc_formatter'
2
+
3
+ module JsDuck
4
+
5
+ # Export class data as hash with :cfg being replace with :cfgs and
6
+ # including all the inherited members too. Same for :properties,
7
+ # :methods, and :events.
8
+ #
9
+ # Also all the :doc elements will be formatted - converted from
10
+ # markdown to HTML and @links resolved.
11
+ class Exporter
12
+ attr_accessor :relations
13
+
14
+ def initialize(relations)
15
+ @relations = relations
16
+ @formatter = DocFormatter.new
17
+ @formatter.css_class = 'docClass'
18
+ end
19
+
20
+ # Returns all data in Class object as hash.
21
+ def export(cls)
22
+ h = cls.to_hash
23
+ h[:cfgs] = cls.members(:cfg)
24
+ h[:properties] = cls.members(:property)
25
+ h[:methods] = cls.members(:method)
26
+ h[:events] = cls.members(:event)
27
+ h.delete(:cfg)
28
+ h.delete(:property)
29
+ h.delete(:method)
30
+ h.delete(:event)
31
+ h[:component] = cls.inherits_from?("Ext.Component")
32
+ h[:superclasses] = cls.superclasses.collect {|c| c.full_name }
33
+ h[:subclasses] = @relations.subclasses(cls).collect {|c| c.full_name }
34
+ h[:mixedInto] = @relations.mixed_into(cls).collect {|c| c.full_name }
35
+ format_class(h)
36
+ end
37
+
38
+ # converts :doc properties from markdown to html and resolve @links
39
+ def format_class(c)
40
+ @formatter.context = c[:name]
41
+ c[:doc] = @formatter.format(c[:doc]) if c[:doc]
42
+ [:cfgs, :properties, :methods, :events].each do |type|
43
+ c[type] = c[type].map {|m| format_member(m) }
44
+ end
45
+ c
46
+ end
47
+
48
+ def format_member(m)
49
+ m = m.clone
50
+ m[:doc] = @formatter.format(m[:doc]) if m[:doc]
51
+ if m[:params] || @formatter.too_long?(m[:doc])
52
+ m[:shortDoc] = @formatter.shorten(m[:doc])
53
+ end
54
+ m[:params] = format_params(m[:params]) if m[:params]
55
+ m[:return] = format_return(m[:return]) if m[:return]
56
+ m
57
+ end
58
+
59
+ def format_params(params)
60
+ params.map do |p|
61
+ p = p.clone
62
+ p[:doc] = @formatter.format(p[:doc]) if p[:doc]
63
+ p
64
+ end
65
+ end
66
+
67
+ def format_return(r)
68
+ r = r.clone
69
+ r[:doc] = @formatter.format(r[:doc]) if r[:doc]
70
+ r
71
+ end
72
+
73
+ end
74
+
75
+ end
@@ -1,15 +1,21 @@
1
+ require 'jsduck/doc_formatter'
2
+
1
3
  module JsDuck
2
4
 
3
5
  # Creates the inheritance tree shown on class documentation page.
4
6
  class InheritanceTree
5
7
  def initialize(cls)
6
8
  @cls = cls
9
+ @formatter = DocFormatter.new
10
+ @formatter.context = cls.full_name
11
+ @formatter.css_class = 'docClass'
12
+ @formatter.url_template = 'output/%cls%.html'
7
13
  end
8
14
 
9
15
  # Renders the tree using HTML <pre> element
10
16
  def to_html
11
17
  i = -1
12
- html = ancestors(@cls).reverse.collect do |cls|
18
+ html = (@cls.superclasses + [@cls]).collect do |cls|
13
19
  i += 1
14
20
  make_indent(i) + make_link(cls)
15
21
  end.join("\n")
@@ -21,16 +27,6 @@ module JsDuck
21
27
  EOHTML
22
28
  end
23
29
 
24
- # Returns array of the names of ancestor classes for given class.
25
- # Including the name of the class itself.
26
- # Example result when ascing ancestors of MyPanel might be:
27
- #
28
- # [MyPanel, Ext.Panel, Ext.Component, Ext.util.Observable]
29
- #
30
- def ancestors(cls)
31
- cls.parent ? [cls] + ancestors(cls.parent) : [cls]
32
- end
33
-
34
30
  def make_indent(level)
35
31
  if level > 0
36
32
  (" " * level) + "<img src='resources/elbow-end.gif' alt=''>"
@@ -43,7 +39,7 @@ module JsDuck
43
39
  if cls == @cls
44
40
  cls.short_name
45
41
  else
46
- "<a href='output/#{cls.full_name}.html' ext:cls='#{cls.full_name}'>#{cls.short_name}</a>"
42
+ @formatter.link(cls.full_name, nil, cls.short_name)
47
43
  end
48
44
  end
49
45
  end
data/lib/jsduck/lexer.rb CHANGED
@@ -97,12 +97,12 @@ module JsDuck
97
97
  elsif @input.check(/'/)
98
98
  @tokens << {
99
99
  :type => :string,
100
- :value => eval(@input.scan(/'([^'\\]|\\.)*'/))
100
+ :value => @input.scan(/'([^'\\]|\\.)*'/).sub(/^'(.*)'$/, "\\1")
101
101
  }
102
102
  elsif @input.check(/"/)
103
103
  @tokens << {
104
104
  :type => :string,
105
- :value => eval(@input.scan(/"([^"\\]|\\.)*"/))
105
+ :value => @input.scan(/"([^"\\]|\\.)*"/).sub(/^"(.*)"$/, "\\1")
106
106
  }
107
107
  elsif @input.check(/\//)
108
108
  # Several things begin with dash:
@@ -6,7 +6,10 @@ module JsDuck
6
6
  # for use in documentation body.
7
7
  class LongParams
8
8
  def initialize(cls)
9
- @formatter = DocFormatter.new(cls.full_name)
9
+ @formatter = DocFormatter.new()
10
+ @formatter.context = cls.full_name
11
+ @formatter.css_class = 'docClass'
12
+ @formatter.url_template = 'output/%cls%.html'
10
13
  end
11
14
 
12
15
  def render(params)
@@ -0,0 +1,57 @@
1
+
2
+ module JsDuck
3
+
4
+ # Creates list of all members in all classes that is used by the
5
+ # searching feature in UI.
6
+ class Members
7
+ # Given list of class documentation objects returns an array of
8
+ # hashes describing all the members.
9
+ def create(docs)
10
+ list = []
11
+ docs.each do |cls|
12
+ [:cfg, :property, :method, :event].each do |type|
13
+ cls.members(type).each do |m|
14
+ list << member_node(m, cls)
15
+ end
16
+ end
17
+ end
18
+ list
19
+ end
20
+
21
+ # Creates structure representing one member
22
+ def member_node(member, cls)
23
+ return {
24
+ :cls => cls.full_name,
25
+ :member => member[:name],
26
+ :type => member[:tagname],
27
+ :doc => short_desc(member[:doc])
28
+ }
29
+ end
30
+
31
+ def short_desc(str)
32
+ tagless = first_sentence(strip_tags(strip_links(str)))
33
+ if tagless.length > 120
34
+ short_doc = tagless[0..116]
35
+ ellipsis = tagless.length > short_doc.length ? "..." : ""
36
+ tagless[0..116] + ellipsis
37
+ else
38
+ tagless
39
+ end
40
+ end
41
+
42
+ def strip_tags(str)
43
+ str.gsub(/<.*?>/, "")
44
+ end
45
+
46
+ def strip_links(str)
47
+ str = str.gsub(/\{@link +(\S*?)(?: +(.+?))?\}/, "\\1")
48
+ str = str.gsub(/#/, ".")
49
+ end
50
+
51
+ def first_sentence(str)
52
+ str.sub(/\A(.+?\.)\s.*\Z/m, "\\1")
53
+ end
54
+
55
+ end
56
+
57
+ end
data/lib/jsduck/merger.rb CHANGED
@@ -248,10 +248,13 @@ module JsDuck
248
248
  [implicit.length, explicit.length].max.times do |i|
249
249
  im = implicit[i] || {}
250
250
  ex = explicit[i] || {}
251
+ doc = ex[:doc] || im[:doc] || ""
251
252
  params << {
252
253
  :type => ex[:type] || im[:type] || "Object",
253
254
  :name => ex[:name] || im[:name] || "",
254
- :doc => ex[:doc] || im[:doc] || "",
255
+ :doc => doc,
256
+ # convert to boolean for JavaScript export, otherwise it's 0 or nil
257
+ :optional => !!(doc =~ /\(optional\)/),
255
258
  }
256
259
  end
257
260
  params
@@ -14,6 +14,10 @@ module JsDuck
14
14
  @row_class = "method-row"
15
15
  @short_params = ShortParams.new
16
16
  @long_params = LongParams.new(@cls)
17
+ @formatter = DocFormatter.new()
18
+ @formatter.context = @cls.full_name
19
+ @formatter.css_class = 'docClass'
20
+ @formatter.url_template = 'output/%cls%.html'
17
21
  end
18
22
 
19
23
  def signature_suffix(item)
@@ -33,7 +37,7 @@ module JsDuck
33
37
 
34
38
  def render_return(item)
35
39
  type = item[:return][:type]
36
- doc = item[:return][:doc]
40
+ doc = @formatter.format(item[:return][:doc])
37
41
  if type == "void" && doc.length == 0
38
42
  "<ul><li>void</li></ul>"
39
43
  else
data/lib/jsduck/page.rb CHANGED
@@ -12,14 +12,17 @@ module JsDuck
12
12
  # Initializes doc page generator
13
13
  #
14
14
  # - cls : the Class object for which to generate documentation
15
- # - subclasses : lookup table for easy access to subclasses
15
+ # - relations : access to subclasses, mixins, etc
16
16
  # - cache : cache for already generated HTML rows for class members
17
17
  #
18
- def initialize(cls, subclasses = {}, cache = {})
18
+ def initialize(cls, relations, cache = {})
19
19
  @cls = cls
20
- @subclasses = subclasses
20
+ @relations = relations
21
21
  @cache = cache
22
- @formatter = DocFormatter.new(cls.full_name)
22
+ @formatter = DocFormatter.new
23
+ @formatter.context = cls.full_name
24
+ @formatter.css_class = 'docClass'
25
+ @formatter.url_template = 'output/%cls%.html'
23
26
  end
24
27
 
25
28
  def to_html
@@ -50,36 +53,49 @@ module JsDuck
50
53
  def abstract
51
54
  [
52
55
  "<table cellspacing='0'>",
53
- abstract_row("Extends:", @cls.parent ? class_link(@cls.parent.full_name) : "Object"),
54
- @cls.mixins.length > 0 ? abstract_row("Mixins:", mixins) : "",
55
- abstract_row("Defind In:", file_link),
56
- @subclasses[@cls] ? abstract_row("Subclasses:", subclasses) : "",
57
- @cls[:xtype] ? abstract_row("xtype:", @cls[:xtype]) : "",
58
- @cls[:author] ? abstract_row("Author:", @cls[:author]) : "",
56
+ row("Extends:", extends_link),
57
+ classes_row("Mixins:", @cls.mixins),
58
+ row("Defind In:", file_link),
59
+ classes_row("Subclasses:", @relations.subclasses(@cls)),
60
+ classes_row("Mixed into:", @relations.mixed_into(@cls)),
61
+ boolean_row("xtype:", @cls[:xtype]),
62
+ boolean_row("Author:", @cls[:author]),
59
63
  "</table>",
60
64
  ].join("\n")
61
65
  end
62
66
 
63
67
  def class_link(class_name, label=nil)
64
68
  label = label || class_name
65
- "<a href='output/#{class_name}.html' ext:cls='#{class_name}'>#{label}</a>"
69
+ @formatter.link(class_name, nil, label || class_name)
66
70
  end
67
71
 
68
72
  def file_link
69
73
  "<a href='source/#{@cls[:href]}'>#{File.basename(@cls[:filename])}</a>"
70
74
  end
71
75
 
72
- def subclasses
73
- subs = @subclasses[@cls].sort {|a, b| a.short_name <=> b.short_name }
74
- subs.collect {|cls| class_link(cls.full_name, cls.short_name) }.join(", ")
76
+ def extends_link
77
+ if @cls[:extends]
78
+ @relations[@cls[:extends]] ? class_link(@cls[:extends]) : @cls[:extends]
79
+ else
80
+ "Object"
81
+ end
75
82
  end
76
83
 
77
- def mixins
78
- mixs = @cls.mixins.sort {|a, b| a.full_name <=> b.full_name }
79
- mixs.collect {|cls| class_link(cls.full_name, cls.short_name) }.join(", ")
84
+ def classes_row(label, classes)
85
+ if classes.length > 0
86
+ classes = classes.sort {|a, b| a.short_name <=> b.short_name }
87
+ html = classes.collect {|cls| class_link(cls.full_name, cls.short_name) }.join(", ")
88
+ row(label, html)
89
+ else
90
+ ""
91
+ end
80
92
  end
81
93
 
82
- def abstract_row(label, info)
94
+ def boolean_row(label, item)
95
+ item ? row(label, item) : ""
96
+ end
97
+
98
+ def row(label, info)
83
99
  "<tr><td class='label'>#{label}</td><td class='hd-info'>#{info}</td></tr>"
84
100
  end
85
101
 
@@ -0,0 +1,31 @@
1
+ require 'parallel'
2
+
3
+ module JsDuck
4
+
5
+ # Wrapper around the parallel gem that falls back to simple
6
+ # Array#map and Array#each when :in_processes => 0 specified.
7
+ class ParallelWrap
8
+
9
+ # Takes config object for parallel
10
+ def initialize(cfg = {})
11
+ @cfg = cfg
12
+ end
13
+
14
+ def each(arr, &block)
15
+ if @cfg[:in_processes] == 0
16
+ arr.each &block
17
+ else
18
+ Parallel.each(arr, @cfg, &block)
19
+ end
20
+ end
21
+
22
+ def map(arr, &block)
23
+ if @cfg[:in_processes] == 0
24
+ arr.map &block
25
+ else
26
+ Parallel.map(arr, @cfg, &block)
27
+ end
28
+ end
29
+ end
30
+
31
+ end
@@ -0,0 +1,68 @@
1
+ module JsDuck
2
+
3
+ # Provides information about relations between classes.
4
+ #
5
+ # Also provides a place to look up classes by name.
6
+ #
7
+ # The constructor is initialized with array of all available
8
+ # classes.
9
+ class Relations
10
+ # Returns list of all classes
11
+ attr_reader :classes
12
+
13
+ def initialize(classes = [])
14
+ @classes = classes
15
+
16
+ # First build class lookup table; building lookup tables for
17
+ # mixins and subclasses will depend on that.
18
+ @lookup = {}
19
+ @classes.each do |cls|
20
+ @lookup[cls.full_name] = cls
21
+ cls.relations = self
22
+ end
23
+
24
+ @subs = {}
25
+ @mixes = {}
26
+ @classes.each do |cls|
27
+ reg_subclasses(cls)
28
+ reg_mixed_into(cls)
29
+ end
30
+ end
31
+
32
+ # Looks up class by name, nil if not found
33
+ def [](classname)
34
+ @lookup[classname]
35
+ end
36
+
37
+ def reg_subclasses(cls)
38
+ if !cls.parent
39
+ # do nothing
40
+ elsif @subs[cls.parent.full_name]
41
+ @subs[cls.parent.full_name] << cls
42
+ else
43
+ @subs[cls.parent.full_name] = [cls]
44
+ end
45
+ end
46
+
47
+ # Returns subclasses of particular class, empty array if none
48
+ def subclasses(cls)
49
+ @subs[cls.full_name] || []
50
+ end
51
+
52
+ def reg_mixed_into(cls)
53
+ cls.mixins.each do |mix|
54
+ if @mixes[mix.full_name]
55
+ @mixes[mix.full_name] << cls
56
+ else
57
+ @mixes[mix.full_name] = [cls]
58
+ end
59
+ end
60
+ end
61
+
62
+ # Returns classes having particular mixin, empty array if none
63
+ def mixed_into(cls)
64
+ @mixes[cls.full_name] || []
65
+ end
66
+ end
67
+
68
+ end
@@ -13,16 +13,12 @@ module JsDuck
13
13
 
14
14
  def render_single(param)
15
15
  str = "<code>#{param[:type]} #{param[:name]}</code>"
16
- if optional?(param)
16
+ if param[:optional]
17
17
  "<span title='Optional' class='optional'>[" + str + "]</span>"
18
18
  else
19
19
  str
20
20
  end
21
21
  end
22
-
23
- def optional?(param)
24
- return param[:doc] =~ /\(optional\)/
25
- end
26
22
  end
27
23
 
28
24
  end
@@ -8,16 +8,21 @@ module JsDuck
8
8
  class SourceFormatter
9
9
 
10
10
  # Initializes SourceFormatter to the directory where
11
- # HTML-formatted source files will be placed
12
- def initialize(output_dir)
11
+ # HTML-formatted source files will be placed.
12
+ #
13
+ # formatter can be either :format_page or :format_pre; with the
14
+ # first one the whole HTML page is created, otherwise just a
15
+ # contents of <pre> element.
16
+ def initialize(output_dir, formatter = :format_page)
13
17
  @output_dir = output_dir
18
+ @formatter = formatter
14
19
  end
15
20
 
16
21
  # Converts source to HTML and writes into file in output
17
22
  # directory. It returns the name of the file that it wrote.
18
23
  def write(source, filename)
19
24
  fname = uniq_html_filename(filename)
20
- File.open(fname, 'w') {|f| f.write(format(source)) }
25
+ File.open(fname, 'w') {|f| f.write(self.send(@formatter, source)) }
21
26
  fname
22
27
  end
23
28
 
@@ -36,7 +41,7 @@ module JsDuck
36
41
  end
37
42
 
38
43
  # Returns full source for HTML page
39
- def format(source)
44
+ def format_page(source)
40
45
  return <<-EOHTML
41
46
  <!DOCTYPE html>
42
47
  <html>
data/lib/jsduck/table.rb CHANGED
@@ -20,7 +20,10 @@ module JsDuck
20
20
  def initialize(cls, cache={})
21
21
  @cls = cls
22
22
  @cache = cache
23
- @formatter = DocFormatter.new(cls.full_name)
23
+ @formatter = DocFormatter.new
24
+ @formatter.context = cls.full_name
25
+ @formatter.css_class = 'docClass'
26
+ @formatter.url_template = 'output/%cls%.html'
24
27
  end
25
28
 
26
29
  def to_html
@@ -75,11 +78,7 @@ module JsDuck
75
78
  end
76
79
 
77
80
  def member_link(item)
78
- cls = item[:member]
79
- member = item[:name]
80
- "<a href='output/#{cls}.html##{member}' " +
81
- "ext:member='##{member}' " +
82
- "ext:cls='#{cls}'>#{Class.short_name(cls)}</a>"
81
+ @formatter.link(item[:member], item[:name], Class.short_name(item[:member]))
83
82
  end
84
83
 
85
84
  def signature(item)
@@ -88,24 +87,11 @@ module JsDuck
88
87
  return "<a id='#{id}'></a><b><a href='#{src}'>#{item[:name]}</a></b>" + signature_suffix(item)
89
88
  end
90
89
 
91
- # 116 chars is also where ext-doc makes its cut, but unlike
92
- # ext-doc we only make the cut when there's more than 120 chars.
93
- #
94
- # This way we don't get stupid expansions like:
95
- #
96
- # Blah blah blah some text...
97
- #
98
- # expanding to:
99
- #
100
- # Blah blah blah some text.
101
- #
90
+ # Creates either expandable or normal doc-entry
102
91
  def expandable_desc(p_doc, e_doc)
103
92
  if expandable?(p_doc, e_doc)
104
- # Only show ellipsis when primary_doc is shortened.
105
- tagless = strip_tags(p_doc)
106
- short_doc = tagless[0..116]
107
- ellipsis = tagless.length > short_doc.length ? "..." : ""
108
- "<div class='short'>#{short_doc}#{ellipsis}</div>" +
93
+ short_doc = @formatter.shorten(p_doc)
94
+ "<div class='short'>#{short_doc}</div>" +
109
95
  "<div class='long'>#{p_doc}#{e_doc}</div>"
110
96
  else
111
97
  p_doc
@@ -122,11 +108,7 @@ module JsDuck
122
108
  end
123
109
 
124
110
  def expandable?(p_doc, e_doc)
125
- strip_tags(p_doc).length > 120 || e_doc.length > 0
126
- end
127
-
128
- def strip_tags(str)
129
- str.gsub(/<.*?>/, "")
111
+ @formatter.too_long?(p_doc) || e_doc.length > 0
130
112
  end
131
113
 
132
114
  def inherited?(item)
data/template/index.html CHANGED
@@ -16,6 +16,7 @@
16
16
  <script type="text/javascript" src="resources/prettify/prettify.js"></script>
17
17
  <script type="text/javascript" src="resources/docs.js"></script>
18
18
  <script type="text/javascript" src="output/tree.js"></script>
19
+ <script type="text/javascript" src="output/members.js"></script>
19
20
  </head>
20
21
  <body scroll="no" id="docs">
21
22
 
@@ -250,10 +250,12 @@ a:hover {
250
250
  .icon-event {
251
251
  background-image: url(event.gif) !important;
252
252
  }
253
+ .icon-cfg,
253
254
  .icon-config {
254
255
  background-image: url(config.gif) !important;
255
256
  }
256
- .icon-prop {
257
+ .icon-prop,
258
+ .icon-property {
257
259
  background-image: url(prop.gif) !important;
258
260
  }
259
261
  .icon-method {
@@ -75,7 +75,7 @@ Ext.extend(ApiPanel, Ext.tree.TreePanel, {
75
75
  handler: function(){ this.root.collapse(true); },
76
76
  scope: this
77
77
  }]
78
- })
78
+ });
79
79
  ApiPanel.superclass.initComponent.call(this);
80
80
  },
81
81
  filterTree: function(t, e){
@@ -226,21 +226,20 @@ DocPanel = Ext.extend(Ext.Panel, {
226
226
  MainPanel = function(){
227
227
 
228
228
  this.searchStore = new Ext.data.Store({
229
- proxy: new Ext.data.ScriptTagProxy({
230
- url: 'http://extjs.com/playpen/api.php'
231
- }),
232
- reader: new Ext.data.JsonReader({
233
- root: 'data'
234
- },
235
- ['cls', 'member', 'type', 'doc']
236
- ),
237
- baseParams: {},
238
- listeners: {
239
- 'beforeload' : function(){
240
- this.baseParams.qt = Ext.getCmp('search-type').getValue();
241
- }
242
- }
243
- });
229
+ sortInfo: {
230
+ field: 'member',
231
+ direction: 'ASC'
232
+ },
233
+ reader: new Ext.data.JsonReader({
234
+ root: 'data',
235
+ fields: ['cls', 'member', 'type', 'doc']
236
+ })
237
+ });
238
+ this.searchStore.loadData(Docs.membersData);
239
+ this.searchStore.filterBy(function(r) {
240
+ return false;
241
+ });
242
+
244
243
 
245
244
  MainPanel.superclass.constructor.call(this, {
246
245
  id:'doc-body',
@@ -256,7 +255,7 @@ MainPanel = function(){
256
255
  items: {
257
256
  id:'welcome-panel',
258
257
  title: 'API Home',
259
- autoLoad: {url: 'welcome.html', callback: this.initSearch, scope: this},
258
+ autoLoad: {url: 'welcome.html', callback: this.initSearch, scope: this, delay: 100},
260
259
  iconCls:'icon-docs',
261
260
  autoScroll: true,
262
261
  tbar: [
@@ -291,18 +290,17 @@ Ext.extend(MainPanel, Ext.TabPanel, {
291
290
  },
292
291
 
293
292
  onClick: function(e, target){
294
- if(target = e.getTarget('a:not(.exi)', 3)){
295
- var cls = Ext.fly(target).getAttributeNS('ext', 'cls');
293
+ if((target = e.getTarget('a:not(.exi)', 3))){
296
294
  e.stopEvent();
297
- if(cls){
298
- var member = Ext.fly(target).getAttributeNS('ext', 'member');
299
- this.loadClass(target.href, cls, member);
295
+ if(/\bdocClass\b/.test(target.className)){
296
+ var m = target.rel.split("#");
297
+ this.loadClass(target.href, m[0], m[1]);
300
298
  }else if(target.className == 'inner-link'){
301
299
  this.getActiveTab().scrollToSection(target.href.split('#')[1]);
302
300
  }else{
303
301
  window.open(target.href);
304
302
  }
305
- }else if(target = e.getTarget('.micon', 2)){
303
+ }else if((target = e.getTarget('.micon', 2))){
306
304
  e.stopEvent();
307
305
  var tr = Ext.fly(target.parentNode);
308
306
  if(tr.hasClass('expandable')){
@@ -343,17 +341,18 @@ Ext.extend(MainPanel, Ext.TabPanel, {
343
341
  var resultTpl = new Ext.XTemplate(
344
342
  '<tpl for=".">',
345
343
  '<div class="search-item">',
346
- '<a class="member" ext:cls="{cls}" ext:member="{member}" href="output/{cls}.html">',
347
- '<img src="../resources/images/default/s.gif" class="item-icon icon-{type}"/>{member}',
344
+ '<a class="member docClass" rel="{cls}#{member}" href="output/{cls}.html">',
345
+ '<img src="resources/images/default/s.gif" class="item-icon icon-{type}"/>{member}',
348
346
  '</a> ',
349
- '<a class="cls" ext:cls="{cls}" href="output/{cls}.html">{cls}</a>',
347
+ '<a class="cls docClass" rel="{cls}" href="output/{cls}.html">{cls}</a>',
350
348
  '<p>{doc}</p>',
351
349
  '</div></tpl>'
352
350
  );
353
-
351
+
354
352
  var p = new Ext.DataView({
355
- applyTo: 'search',
353
+ applyTo: Ext.get('search') ? 'search' : Ext.getCmp('welcome-panel').body,
356
354
  tpl: resultTpl,
355
+ deferEmptyText: false,
357
356
  loadingText:'Searching...',
358
357
  store: this.searchStore,
359
358
  itemSelector: 'div.search-item',
@@ -452,12 +451,13 @@ Ext.app.SearchField = Ext.extend(Ext.form.TwinTriggerField, {
452
451
 
453
452
  onTrigger1Click : function(){
454
453
  if(this.hasSearch){
455
- this.store.baseParams[this.paramName] = '';
456
- this.store.removeAll();
457
- this.el.dom.value = '';
454
+ this.store.filterBy(function(r) {
455
+ return false;
456
+ });
457
+ this.el.dom.value = '';
458
458
  this.triggers[0].hide();
459
459
  this.hasSearch = false;
460
- this.focus();
460
+ this.focus();
461
461
  }
462
462
  },
463
463
 
@@ -471,12 +471,24 @@ Ext.app.SearchField = Ext.extend(Ext.form.TwinTriggerField, {
471
471
  Ext.Msg.alert('Invalid Search', 'You must enter a minimum of 2 characters to search the API');
472
472
  return;
473
473
  }
474
- this.store.baseParams[this.paramName] = v;
475
- var o = {start: 0};
476
- this.store.reload({params:o});
474
+
475
+ var type = Ext.getCmp('search-type').getValue();
476
+ this.store.filter("member", this.createRegex(type, v));
477
+
477
478
  this.hasSearch = true;
478
479
  this.triggers[0].show();
479
480
  this.focus();
481
+ },
482
+
483
+ createRegex: function(type, text) {
484
+ var safeText = Ext.escapeRe(text);
485
+ if (type === 'Starts with') {
486
+ return new RegExp("^" + safeText, "i");
487
+ } else if (type === 'Ends with') {
488
+ return new RegExp(safeText + "$", "i");
489
+ } else {
490
+ return new RegExp(safeText, "i");
491
+ }
480
492
  }
481
493
  });
482
494
 
@@ -548,6 +560,8 @@ Ext.extend(Ext.ux.SelectBox, Ext.form.ComboBox, {
548
560
  this.selectPrevPage();
549
561
  e.stopEvent();
550
562
  return;
563
+ default:
564
+ break;
551
565
  }
552
566
 
553
567
  // skip special keys other than the shift key
metadata CHANGED
@@ -1,12 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jsduck
3
3
  version: !ruby/object:Gem::Version
4
- hash: 3
4
+ hash: 1
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
- - 4
9
- version: "0.4"
8
+ - 5
9
+ version: "0.5"
10
10
  platform: ruby
11
11
  authors:
12
12
  - Rene Saarsoo
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2011-02-28 00:00:00 +02:00
17
+ date: 2011-03-29 00:00:00 +03:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -81,17 +81,20 @@ files:
81
81
  - lib/jsduck/doc_formatter.rb
82
82
  - lib/jsduck/doc_parser.rb
83
83
  - lib/jsduck/event_table.rb
84
+ - lib/jsduck/exporter.rb
84
85
  - lib/jsduck/inheritance_tree.rb
85
86
  - lib/jsduck/lexer.rb
86
87
  - lib/jsduck/long_params.rb
88
+ - lib/jsduck/members.rb
87
89
  - lib/jsduck/merger.rb
88
90
  - lib/jsduck/method_table.rb
89
91
  - lib/jsduck/page.rb
92
+ - lib/jsduck/parallel_wrap.rb
90
93
  - lib/jsduck/parser.rb
91
94
  - lib/jsduck/property_table.rb
95
+ - lib/jsduck/relations.rb
92
96
  - lib/jsduck/short_params.rb
93
97
  - lib/jsduck/source_formatter.rb
94
- - lib/jsduck/subclasses.rb
95
98
  - lib/jsduck/table.rb
96
99
  - lib/jsduck/timer.rb
97
100
  - lib/jsduck/tree.rb
@@ -1,27 +0,0 @@
1
- module JsDuck
2
-
3
- # Provides information about direct descendants of particular class.
4
- #
5
- # The constructor is initialized with array of all available
6
- # classes. Then through [] method subclasses of particlular class
7
- # can be asked for.
8
- class Subclasses
9
- def initialize(classes)
10
- @subs = {}
11
- classes.each do |cls|
12
- if !cls.parent
13
- # do nothing
14
- elsif @subs[cls.parent.full_name]
15
- @subs[cls.parent.full_name] << cls
16
- else
17
- @subs[cls.parent.full_name] = [cls]
18
- end
19
- end
20
- end
21
-
22
- def [](cls)
23
- @subs[cls.full_name]
24
- end
25
- end
26
-
27
- end