jsduck 3.0.pre2 → 3.0.pre3

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.
Files changed (53) hide show
  1. data/README.md +14 -7
  2. data/Rakefile +277 -24
  3. data/bin/compare +163 -0
  4. data/bin/stats +92 -0
  5. data/jsduck.gemspec +3 -3
  6. data/lib/jsduck/accessors.rb +64 -8
  7. data/lib/jsduck/aggregator.rb +10 -6
  8. data/lib/jsduck/aliases.rb +3 -2
  9. data/lib/jsduck/app.rb +51 -44
  10. data/lib/jsduck/author_tag.rb +11 -0
  11. data/lib/jsduck/categories.rb +14 -8
  12. data/lib/jsduck/class.rb +2 -1
  13. data/lib/jsduck/class_formatter.rb +5 -4
  14. data/lib/jsduck/doc_author_tag.rb +11 -0
  15. data/lib/jsduck/doc_formatter.rb +17 -37
  16. data/lib/jsduck/doc_parser.rb +39 -11
  17. data/lib/jsduck/exporter.rb +11 -0
  18. data/lib/jsduck/guides.rb +3 -3
  19. data/lib/jsduck/images.rb +72 -0
  20. data/lib/jsduck/js_parser.rb +20 -4
  21. data/lib/jsduck/lexer.rb +2 -8
  22. data/lib/jsduck/lint.rb +3 -2
  23. data/lib/jsduck/logger.rb +24 -5
  24. data/lib/jsduck/merger.rb +38 -8
  25. data/lib/jsduck/meta_tag.rb +49 -0
  26. data/lib/jsduck/meta_tag_loader.rb +48 -0
  27. data/lib/jsduck/options.rb +37 -25
  28. data/lib/jsduck/os.rb +11 -0
  29. data/lib/jsduck/renderer.rb +37 -34
  30. data/lib/jsduck/search_data.rb +1 -1
  31. data/lib/jsduck/source_file.rb +13 -8
  32. data/template-min/app.js +1 -1
  33. data/template-min/{egIframe.html → extIframe.html} +3 -4
  34. data/template-min/extjs/ext-all-debug.js +3107 -2026
  35. data/template-min/extjs/ext-all.js +1 -1
  36. data/template-min/extjs/resources/css/ext-all.css +1 -1
  37. data/template-min/resources/css/app.css +1 -1
  38. data/template-min/resources/images/down-arr.png +0 -0
  39. data/template-min/resources/images/gettingstarted.jpg +0 -0
  40. data/template-min/resources/images/ipad-l.jpg +0 -0
  41. data/template-min/resources/images/ipad-p.jpg +0 -0
  42. data/template-min/resources/images/iphone-l.jpg +0 -0
  43. data/template-min/resources/images/iphone-p.jpg +0 -0
  44. data/template-min/resources/images/iphone-small-l.jpg +0 -0
  45. data/template-min/resources/images/iphone-small-p.jpg +0 -0
  46. data/template-min/resources/images/link-arrow-next.png +0 -0
  47. data/template-min/template.html +5 -1
  48. data/template-min/touch-welcome.html +122 -0
  49. data/template-min/touchIframe.html +85 -0
  50. data/template-min/welcome.html +2 -0
  51. metadata +25 -8
  52. data/lib/jsduck/page.rb +0 -118
  53. data/lib/jsduck/timer.rb +0 -44
@@ -29,9 +29,12 @@ module JsDuck
29
29
  # Default value: '<img src="%u" alt="%a"/>'
30
30
  attr_accessor :img_tpl
31
31
 
32
- # Assign to this a function that retrieves the example code when
33
- # passed in a filename
34
- attr_accessor :get_example
32
+ # This will hold list of all image paths gathered from {@img} tags.
33
+ attr_accessor :images
34
+
35
+ # Base path to prefix images from {@img} tags.
36
+ # Defaults to no prefix.
37
+ attr_accessor :img_path
35
38
 
36
39
  # Sets up instance to work in context of particular class, so
37
40
  # that when {@link #blah} is encountered it knows that
@@ -57,13 +60,12 @@ module JsDuck
57
60
  @doc_context = {}
58
61
  @max_length = 120
59
62
  @relations = {}
63
+ @images = []
60
64
  @link_tpl = '<a href="%c%#%m">%a</a>'
61
65
  @img_tpl = '<img src="%u" alt="%a"/>'
62
- @example_tpl = '<pre class="inline-example"><code>%a</code></pre>'
63
66
  @link_re = /\{@link\s+(\S*?)(?:\s+(.+?))?\}/m
64
67
  @img_re = /\{@img\s+(\S*?)(?:\s+(.+?))?\}/m
65
- @example_re = /\{@example\s+(\S*?)\s*\}/m
66
- @example_annotation_re = /<pre><code>@example( +[^\n]*)?\s+/m
68
+ @example_annotation_re = /<pre><code>\s*@example( +[^\n]*)?\s+/m
67
69
  end
68
70
 
69
71
  # Replaces {@link} and {@img} tags, auto-generates links for
@@ -74,8 +76,6 @@ module JsDuck
74
76
  #
75
77
  # Replaces {@img path/to/image.jpg Alt text} with HTML from @img_tpl.
76
78
  #
77
- # Replaces {@example path/to/example.js} with source from that file.
78
- #
79
79
  # Adds 'inline-example' class to code examples beginning with @example.
80
80
  #
81
81
  # Additionally replaces strings recognized as ClassNames with
@@ -89,11 +89,12 @@ module JsDuck
89
89
  out += replace_link_tag(s.scan(@link_re))
90
90
  elsif s.check(@img_re)
91
91
  out += replace_img_tag(s.scan(@img_re))
92
- elsif s.check(@example_re)
93
- out += replace_example_tag(s.scan(@example_re))
94
92
  elsif s.check(@example_annotation_re)
95
- s.scan(@example_annotation_re)
96
- out += '<pre class="inline-example"><code>'
93
+ # Match possible classnames following @example and add them
94
+ # as CSS classes inside <pre> element.
95
+ s.scan(@example_annotation_re) =~ @example_annotation_re
96
+ css_classes = ($1 || "").strip
97
+ out += "<pre class='inline-example #{css_classes}'><code>"
97
98
  elsif s.check(/[{<]/)
98
99
  out += s.scan(/[{<]/)
99
100
  else
@@ -129,10 +130,10 @@ module JsDuck
129
130
  file = @doc_context[:filename]
130
131
  line = @doc_context[:linenr]
131
132
  if !@relations[cls]
132
- Logger.instance.warn("#{file} line #{line} #{input} links to non-existing class.")
133
+ Logger.instance.warn("#{input} links to non-existing class", file, line)
133
134
  text
134
135
  elsif member && !get_member(cls, member, type)
135
- Logger.instance.warn("#{file} line #{line} #{input} links to non-existing member.")
136
+ Logger.instance.warn("#{input} links to non-existing member", file, line)
136
137
  text
137
138
  else
138
139
  link(cls, member, text, type)
@@ -144,10 +145,6 @@ module JsDuck
144
145
  input.sub(@img_re) { img($1, $2) }
145
146
  end
146
147
 
147
- def replace_example_tag(input)
148
- input.sub(@example_re) { example($1) }
149
- end
150
-
151
148
  def replace_class_names(input)
152
149
  input.gsub(/(\A|\s)([A-Z][A-Za-z0-9.]*[A-Za-z0-9])(?:(#)([A-Za-z0-9]+))?([.,]?(?:\s|\Z))/m) do
153
150
  before = $1
@@ -167,10 +164,11 @@ module JsDuck
167
164
 
168
165
  # applies the image template
169
166
  def img(url, alt_text)
167
+ @images << url
170
168
  @img_tpl.gsub(/(%\w)/) do
171
169
  case $1
172
170
  when '%u'
173
- url
171
+ @img_path ? (@img_path + "/" + url) : url
174
172
  when '%a'
175
173
  CGI.escapeHTML(alt_text||"")
176
174
  else
@@ -179,24 +177,6 @@ module JsDuck
179
177
  end
180
178
  end
181
179
 
182
- # Replaces example template with example read from file
183
- def example(path)
184
- @example_tpl.gsub(/(%\w)/) do
185
- case $1
186
- when '%a'
187
- if @get_example
188
- CGI.escapeHTML(@get_example.call(path))
189
- else
190
- file = @doc_context[:filename]
191
- line = @doc_context[:linenr]
192
- Logger.instance.warn("--examples not specified, but {@example} found in #{file} line #{line}.")
193
- end
194
- else
195
- $1
196
- end
197
- end
198
- end
199
-
200
180
  # applies the link template
201
181
  def link(cls, member, anchor_text, type=nil)
202
182
  # Use the canonical class name for link (not some alternateClassName)
@@ -30,7 +30,7 @@ module JsDuck
30
30
 
31
31
  @meta_tags_map = {}
32
32
  (meta_tags || []).each do |tag|
33
- @meta_tags_map[tag[:name]] = true
33
+ @meta_tags_map[tag.name] = tag
34
34
  end
35
35
  end
36
36
 
@@ -136,6 +136,8 @@ module JsDuck
136
136
  boolean_at_tag(/@protected/, :protected)
137
137
  elsif look(/@accessor\b/)
138
138
  boolean_at_tag(/@accessor/, :accessor)
139
+ elsif look(/@evented\b/)
140
+ boolean_at_tag(/@evented/, :evented)
139
141
  elsif look(/@template\b/)
140
142
  boolean_at_tag(/@template/, :template)
141
143
  elsif look(/@markdown\b/)
@@ -146,12 +148,9 @@ module JsDuck
146
148
  boolean_at_tag(/@abstract/, :abstract)
147
149
  elsif look(/@/)
148
150
  @input.scan(/@/)
149
- if @meta_tags_map[look(/\w+/)]
150
- add_tag(:meta)
151
- @current_tag[:name] = match(/\w+/)
152
- skip_horiz_white
153
- @current_tag[:content] = @input.scan(/.*$/)
154
- skip_white
151
+ tag = @meta_tags_map[look(/\w+/)]
152
+ if tag
153
+ meta_at_tag(tag)
155
154
  else
156
155
  @current_tag[:doc] += "@"
157
156
  end
@@ -161,6 +160,24 @@ module JsDuck
161
160
  end
162
161
  end
163
162
 
163
+ # Matches the given meta-tag
164
+ def meta_at_tag(tag)
165
+ prev_tag = @current_tag
166
+
167
+ add_tag(:meta)
168
+ @current_tag[:name] = match(/\w+/)
169
+ skip_horiz_white
170
+
171
+ # Fors singleline tags, scan to the end of line and finish the
172
+ # tag. For multiline tags we leave the tag open for :doc
173
+ # addition just like with built-in multiline tags.
174
+ unless tag.multiline
175
+ @current_tag[:doc] = @input.scan(/.*$/).strip
176
+ skip_white
177
+ @current_tag = prev_tag
178
+ end
179
+ end
180
+
164
181
  # matches @class name ...
165
182
  def at_class
166
183
  match(/@class/)
@@ -354,7 +371,7 @@ module JsDuck
354
371
  end
355
372
  end
356
373
 
357
- # matches: <ident-chain> | "[" <ident-chain> [ "=" <literal> ] "]"
374
+ # matches: <ident-chain> | "[" <ident-chain> [ "=" <default-value> ] "]"
358
375
  def maybe_name_with_default
359
376
  skip_horiz_white
360
377
  if look(/\[/)
@@ -364,7 +381,7 @@ module JsDuck
364
381
  if look(/=/)
365
382
  match(/=/)
366
383
  skip_horiz_white
367
- @current_tag[:default] = literal
384
+ @current_tag[:default] = default_value
368
385
  end
369
386
  skip_horiz_white
370
387
  match(/\]/)
@@ -408,9 +425,20 @@ module JsDuck
408
425
  end
409
426
  end
410
427
 
411
- def literal
428
+ # attempts to match javascript literal,
429
+ # when it fails grabs anything up to closing "]"
430
+ def default_value
431
+ start_pos = @input.pos
412
432
  lit = JsLiteralParser.new(@input).literal
413
- lit ? JsLiteralBuilder.new.to_s(lit) : nil
433
+ if lit && look(/ *\]/)
434
+ # When lital matched and there's nothing after it up to the closing "]"
435
+ JsLiteralBuilder.new.to_s(lit)
436
+ else
437
+ # Otherwise reset parsing position to where we started
438
+ # and rescan up to "]" using simple regex.
439
+ @input.pos = start_pos
440
+ match(/[^\]]*/)
441
+ end
414
442
  end
415
443
 
416
444
  # matches {...} and returns text inside brackets
@@ -29,6 +29,7 @@ module JsDuck
29
29
  cls.delete(:doc)
30
30
  cls[:members] = compact_members_group(cls[:members])
31
31
  cls[:statics] = compact_members_group(cls[:statics])
32
+ cls[:files] = compact_files(cls[:files])
32
33
  cls
33
34
  end
34
35
 
@@ -47,6 +48,16 @@ module JsDuck
47
48
  end
48
49
  m_copy
49
50
  end
51
+
52
+ # Remove full path from filename for privacy considerations as the
53
+ # path can reveal information about the system where JSDuck was
54
+ # run. The docs app doesn't need to have this information.
55
+ def compact_files(files)
56
+ files.map do |f|
57
+ {:filename => File.basename(f[:filename]), :href => f[:href]}
58
+ end
59
+ end
60
+
50
61
  end
51
62
 
52
63
  end
data/lib/jsduck/guides.rb CHANGED
@@ -44,14 +44,14 @@ module JsDuck
44
44
  guide_file = in_dir + "/README.md"
45
45
  return Logger.instance.warn("README.md not found in #{in_dir}") unless File.exists?(guide_file)
46
46
 
47
- Logger.instance.log("Writing guide #{out_dir} ...")
47
+ Logger.instance.log("Writing guide", out_dir)
48
48
  # Copy the whole guide dir over
49
49
  FileUtils.cp_r(in_dir, out_dir)
50
50
 
51
51
  @formatter.doc_context = {:filename => guide_file, :linenr => 0}
52
- html = @formatter.format(IO.read(guide_file))
53
52
  name = File.basename(in_dir)
54
- html.gsub!(/<img src="/, "<img src=\"guides/#{name}/")
53
+ @formatter.img_path = "guides/#{name}"
54
+ html = @formatter.format(IO.read(guide_file))
55
55
 
56
56
  JsonDuck.write_jsonp(out_dir+"/README.js", name, {:guide => html, :title => guide["title"]})
57
57
  end
@@ -0,0 +1,72 @@
1
+ require "jsduck/logger"
2
+ require "fileutils"
3
+
4
+ module JsDuck
5
+
6
+ # Looks up images from directories specified through --images option.
7
+ class Images
8
+ def initialize(paths)
9
+ @paths = scan_for_images(paths)
10
+ @images = {}
11
+ end
12
+
13
+ # Scans each path for image files, building a hash of paths where
14
+ # each path points to a hash of image files found in that path.
15
+ def scan_for_images(paths)
16
+ map = {}
17
+ paths.each do |path|
18
+ # Scans directory for image files
19
+ map[path] = {}
20
+ Dir[path+"/**/*.{png,jpg,jpeg,gif}"].each do |img|
21
+ map[path][img] = false
22
+ end
23
+ end
24
+ map
25
+ end
26
+
27
+ # Adds relative image path of an image
28
+ def add(filename)
29
+ unless @images[filename]
30
+ @images[filename] = true
31
+ end
32
+ end
33
+
34
+ # Copys over images to given output dir
35
+ def copy(output_dir)
36
+ @images.each_key do |img|
37
+ unless copy_img(img, output_dir)
38
+ Logger.instance.warn("Image not found.", img)
39
+ end
40
+ end
41
+ report_unused
42
+ end
43
+
44
+ # Attempts to copy one image, returns true on success
45
+ def copy_img(img, output_dir)
46
+ @paths.each_pair do |path, map|
47
+ filename = path + "/" + img
48
+ if map.has_key?(filename)
49
+ dest = output_dir + "/" + img
50
+ Logger.instance.log("Copying image", dest)
51
+ FileUtils.makedirs(File.dirname(dest))
52
+ FileUtils.cp(filename, dest)
53
+ # mark file as used.
54
+ map[filename] = true
55
+ return true
56
+ end
57
+ end
58
+ return false
59
+ end
60
+
61
+ # Report unused images
62
+ def report_unused
63
+ @paths.each_pair do |path, map|
64
+ map.each_pair do |img, used|
65
+ Logger.instance.warn("Image not used.", img) unless used
66
+ end
67
+ end
68
+ end
69
+
70
+ end
71
+
72
+ end
@@ -234,7 +234,7 @@ module JsDuck
234
234
  end
235
235
 
236
236
  # <ext-define-cfg> := "{" ( <extend> | <mixins> | <alternate-class-name> | <alias> |
237
- # <requires> | <uses> | <singleton> | <?> )*
237
+ # <xtype> | <requires> | <uses> | <singleton> | <?> )*
238
238
  def ext_define_cfg
239
239
  match("{")
240
240
  cfg = {}
@@ -249,6 +249,8 @@ module JsDuck
249
249
  cfg[:alternateClassNames] = found
250
250
  elsif found = ext_define_alias
251
251
  cfg[:alias] = found
252
+ elsif found = ext_define_xtype
253
+ cfg[:xtype] = found
252
254
  elsif found = ext_define_requires
253
255
  cfg[:requires] = found
254
256
  elsif found = ext_define_uses
@@ -270,12 +272,18 @@ module JsDuck
270
272
  end
271
273
  end
272
274
 
273
- # <mixins> := "mixins" ":" <object-literal>
275
+ # <mixins> := "mixins" ":" [ <object-literal> | <array-literal> ]
274
276
  def ext_define_mixins
275
- if look("mixins", ":", "{")
277
+ if look("mixins", ":")
276
278
  match("mixins", ":")
277
279
  lit = literal
278
- lit && lit[:value].map {|x| x[:value][:value] }
280
+ if lit && lit[:type] == :object
281
+ lit[:value].map {|x| x[:value][:value] }
282
+ elsif lit && lit[:type] == :array
283
+ lit[:value].map {|x| x[:value] }
284
+ else
285
+ nil
286
+ end
279
287
  end
280
288
  end
281
289
 
@@ -295,6 +303,14 @@ module JsDuck
295
303
  end
296
304
  end
297
305
 
306
+ # <xtype> := "xtype" ":" <string-or-list>
307
+ def ext_define_xtype
308
+ if look("xtype", ":")
309
+ match("xtype", ":")
310
+ string_or_list
311
+ end
312
+ end
313
+
298
314
  # <requires> := "requires" ":" <string-or-list>
299
315
  def ext_define_requires
300
316
  if look("requires", ":")
data/lib/jsduck/lexer.rb CHANGED
@@ -118,8 +118,8 @@ module JsDuck
118
118
  :type => :operator,
119
119
  :value => @input.scan(/./)
120
120
  }
121
- elsif @input.check(/[a-zA-Z_]/)
122
- value = @input.scan(/\w+/)
121
+ elsif @input.check(/[a-zA-Z_$]/)
122
+ value = @input.scan(/[$\w]+/)
123
123
  return {
124
124
  :type => KEYWORDS[value] ? :keyword : :ident,
125
125
  :value => value
@@ -167,12 +167,6 @@ module JsDuck
167
167
  :type => :number,
168
168
  :value => nr
169
169
  }
170
- elsif @input.check(/\$/)
171
- value = @input.scan(/\$\w*/)
172
- return {
173
- :type => :ident,
174
- :value => value
175
- }
176
170
  elsif @input.check(/./)
177
171
  return {
178
172
  :type => :operator,
data/lib/jsduck/lint.rb CHANGED
@@ -75,8 +75,9 @@ module JsDuck
75
75
  end
76
76
 
77
77
  # Prints warning + filename and linenumber from doc-context
78
- def warn(msg, context)
79
- Logger.instance.warn(msg + " in #{context[:filename]} line #{context[:linenr]}")
78
+ def warn(msg, member)
79
+ context = member[:files][0]
80
+ Logger.instance.warn(msg, context[:filename], context[:linenr])
80
81
  end
81
82
 
82
83
  end
data/lib/jsduck/logger.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'singleton'
2
+ require 'jsduck/os'
2
3
 
3
4
  module JsDuck
4
5
 
@@ -15,9 +16,11 @@ module JsDuck
15
16
  @shown_warnings = {}
16
17
  end
17
18
 
18
- # Prints log message
19
- def log(msg)
20
- puts msg if @verbose
19
+ # Prints log message with optional filename appended
20
+ def log(msg, filename=nil)
21
+ if @verbose
22
+ puts msg + " " + format(filename) + "..."
23
+ end
21
24
  end
22
25
 
23
26
  # Prints warning message.
@@ -25,12 +28,28 @@ module JsDuck
25
28
  # Ignores duplicate warnings - only prints the first one.
26
29
  # Works best when --processes=0, but it reduces the amount of
27
30
  # warnings greatly also when run multiple processes.
28
- def warn(msg)
31
+ #
32
+ # Optionally filename and line number will be inserted to message.
33
+ def warn(msg, filename=nil, line=nil)
34
+ msg = "Warning: " + format(filename, line) + " " + msg
35
+
29
36
  if @warnings && !@shown_warnings[msg]
30
- $stderr.puts "Warning: " + msg
37
+ $stderr.puts msg
31
38
  @shown_warnings[msg] = true
32
39
  end
33
40
  end
41
+
42
+ # Formats filename and line number for output
43
+ def format(filename=nil, line=nil)
44
+ out = ""
45
+ if filename
46
+ out = OS::windows? ? filename.gsub('/', '\\') : filename
47
+ if line
48
+ out += ":#{line}:"
49
+ end
50
+ end
51
+ out
52
+ end
34
53
  end
35
54
 
36
55
  end
data/lib/jsduck/merger.rb CHANGED
@@ -1,3 +1,5 @@
1
+ require 'jsduck/logger'
2
+
1
3
  module JsDuck
2
4
 
3
5
  # Takes data from doc-comment and code that follows it and combines
@@ -6,6 +8,15 @@ module JsDuck
6
8
  #
7
9
  # The main method merge() produces a hash as a result.
8
10
  class Merger
11
+ # Allow passing in filename and line for error reporting
12
+ attr_accessor :filename
13
+ attr_accessor :linenr
14
+
15
+ def initialize
16
+ @filename = ""
17
+ @linenr = 0
18
+ end
19
+
9
20
  def merge(docs, code)
10
21
  case detect_doc_type(docs, code)
11
22
  when :class
@@ -170,6 +181,7 @@ module JsDuck
170
181
  :default => detect_default(:cfg, doc_map, code),
171
182
  :properties => detect_subproperties(docs, :cfg),
172
183
  :accessor => !!doc_map[:accessor],
184
+ :evented => !!doc_map[:evented],
173
185
  }, doc_map)
174
186
  end
175
187
 
@@ -222,7 +234,9 @@ module JsDuck
222
234
  end
223
235
 
224
236
  def create_member_id(m)
225
- "#{m[:static] ? 'static-' : ''}#{m[:tagname]}-#{m[:name]}"
237
+ # Sanitize $ in member names with something safer
238
+ name = m[:name].gsub(/\$/, 'S-')
239
+ "#{m[:static] ? 'static-' : ''}#{m[:tagname]}-#{name}"
226
240
  end
227
241
 
228
242
  def detect_name(tagname, doc_map, code, name_type = :last_name)
@@ -319,10 +333,17 @@ module JsDuck
319
333
  def detect_xtypes(doc_map, code)
320
334
  if doc_map[:xtype]
321
335
  {"widget" => doc_map[:xtype].map {|tag| tag[:name] } }
322
- elsif code[:alias]
336
+ elsif code[:xtype] || code[:alias]
323
337
  xtypes = {}
324
- code[:alias].each do |a|
325
- if a =~ /^(\w+)\.(\w+)$/
338
+ (code[:xtype] || []).each do |a|
339
+ if xtypes["widget"]
340
+ xtypes["widget"] << a
341
+ else
342
+ xtypes["widget"] = [a]
343
+ end
344
+ end
345
+ (code[:alias] || []).each do |a|
346
+ if a =~ /^([\w.]+)\.(\w+)$/
326
347
  if xtypes[$1]
327
348
  xtypes[$1] << $2
328
349
  else
@@ -337,7 +358,12 @@ module JsDuck
337
358
  end
338
359
 
339
360
  def detect_meta(doc_map)
340
- doc_map[:meta] ? doc_map[:meta].map {|tag| {:name => tag[:name], :content => tag[:content]} } : []
361
+ meta = {}
362
+ (doc_map[:meta] || []).map do |tag|
363
+ meta[tag[:name]] = [] unless meta[tag[:name]]
364
+ meta[tag[:name]] << tag[:doc]
365
+ end
366
+ meta
341
367
  end
342
368
 
343
369
  def detect_deprecated(doc_map)
@@ -407,8 +433,12 @@ module JsDuck
407
433
  if it[:name] =~ /^(.+)\.([^.]+)$/
408
434
  it[:name] = $2
409
435
  parent = index[$1]
410
- parent[:properties] = [] unless parent[:properties]
411
- parent[:properties] << it
436
+ if parent
437
+ parent[:properties] = [] unless parent[:properties]
438
+ parent[:properties] << it
439
+ else
440
+ Logger.instance.warn("Ignoring subproperty #{$1}.#{$2}, no parent found with name '#{$1}'.", @filename, @linenr)
441
+ end
412
442
  else
413
443
  items << it
414
444
  end
@@ -429,7 +459,7 @@ module JsDuck
429
459
  # Combines :doc-s of most tags
430
460
  # Ignores tags that have doc comment themselves and subproperty tags
431
461
  def detect_doc(docs)
432
- ignore_tags = [:param, :return]
462
+ ignore_tags = [:param, :return, :meta]
433
463
  doc_tags = docs.find_all { |tag| !ignore_tags.include?(tag[:tagname]) && !subproperty?(tag) }
434
464
  doc_tags.map { |tag| tag[:doc] }.compact.join(" ")
435
465
  end
@@ -0,0 +1,49 @@
1
+ module JsDuck
2
+
3
+ # Abstract base class for all meta tag implementations.
4
+ #
5
+ # Child classes must define value for @name attribute. They can
6
+ # also provide @multiline, and override #to_html method.
7
+ class MetaTag
8
+ # Name of the tag (required)
9
+ attr_reader :name
10
+
11
+ # True to include all lines up to next @tag as part of this meta-tag
12
+ attr_reader :multiline
13
+
14
+ # Override this to transform the content of meta-tag to HTML to be
15
+ # included into documentation.
16
+ #
17
+ # It gets passed an array of contents gathered from all meta-tags
18
+ # of given type. It should return an HTML string to inject into
19
+ # document. For help in that it can use the #format method to
20
+ # easily support Markdown and {@link/img} tags inside the contents
21
+ # of meta-tag.
22
+ #
23
+ # By default the method returns nil, which means the tag will not
24
+ # be rendered at all.
25
+ def to_html(contents)
26
+ end
27
+
28
+ # This is used to inject the formatter object for #markdown method
29
+ attr_accessor :formatter
30
+
31
+ # Helper method to format the text in standard JsDuck way.
32
+ # This means running it through Markdown engine and expanding
33
+ # {@link} and {@img} tags.
34
+ def format(text)
35
+ @formatter.format(text)
36
+ end
37
+
38
+ # Returns all descendants of MetaTag class.
39
+ def self.descendants
40
+ result = []
41
+ ObjectSpace.each_object(::Class) do |cls|
42
+ result << cls if cls < self
43
+ end
44
+ result
45
+ end
46
+
47
+ end
48
+
49
+ end
@@ -0,0 +1,48 @@
1
+ require "jsduck/meta_tag"
2
+ require 'jsduck/author_tag'
3
+ require 'jsduck/doc_author_tag'
4
+
5
+ module JsDuck
6
+
7
+ # Loads user-defined meta-tags
8
+ class MetaTagLoader
9
+ # instatiates builtin meta tags
10
+ def initialize
11
+ @classes = MetaTag.descendants
12
+ @meta_tags = @classes.map {|cls| cls.new }
13
+ end
14
+
15
+ # Loads user-defined meta-tags from given paths.
16
+ # Returns list of meta-tag instances.
17
+ def load(paths)
18
+ paths.each do |path|
19
+ if File.directory?(path)
20
+ Dir[path+"/**/*.rb"].each do |file|
21
+ require(file)
22
+ init_remaining
23
+ end
24
+ else
25
+ require(path)
26
+ init_remaining
27
+ end
28
+ end
29
+ @meta_tags
30
+ end
31
+
32
+ # Instantiates meta tag classes that haven't been instantiated
33
+ # already. This is called after each meta-tags file is loaded so
34
+ # that the list of meta-tags will be in order specified from
35
+ # command line.
36
+ def init_remaining
37
+ MetaTag.descendants.each do |cls|
38
+ if !@classes.include?(cls)
39
+ @classes << cls
40
+ newtag = cls.new
41
+ @meta_tags = @meta_tags.find_all {|t| t.name != newtag.name }
42
+ @meta_tags << newtag
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ end