jsduck 3.7.0 → 3.8.0

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
@@ -120,7 +120,9 @@ Thanks to [Ondřej Jirman](https://github.com/megous),
120
120
  [ligaard](https://github.com/ligaard),
121
121
  [Bill Hubbard](http://www.sencha.com/forum/member.php?272458-BillHubbard),
122
122
  [Ed Spencer](https://github.com/edspencer),
123
- [atian25](https://github.com/atian25) and many-many others who
123
+ [atian25](https://github.com/atian25),
124
+ Katherine Chu,
125
+ [Rob Dougan](https://github.com/rdougan) and many-many others who
124
126
  reported bugs, submitted patches, and provided a lot of useful input.
125
127
 
126
128
 
data/Rakefile CHANGED
@@ -13,7 +13,7 @@ end
13
13
 
14
14
  def load_sdk_vars
15
15
  if File.exists?("sdk-vars.rb")
16
- require "sdk-vars.rb"
16
+ require "./sdk-vars.rb"
17
17
  else
18
18
  puts "Error: sdk-vars.rb not found."
19
19
  puts
data/jsduck.gemspec CHANGED
@@ -2,8 +2,8 @@ Gem::Specification.new do |s|
2
2
  s.required_rubygems_version = ">= 1.3.5"
3
3
 
4
4
  s.name = 'jsduck'
5
- s.version = '3.7.0'
6
- s.date = '2012-02-29'
5
+ s.version = '3.8.0'
6
+ s.date = '2012-03-15'
7
7
  s.summary = "Simple JavaScript Duckumentation generator"
8
8
  s.description = "Documentation generator for Sencha JS frameworks"
9
9
  s.homepage = "https://github.com/senchalabs/jsduck"
data/lib/jsduck/app.rb CHANGED
@@ -10,6 +10,7 @@ require 'jsduck/parallel_wrap'
10
10
  require 'jsduck/logger'
11
11
  require 'jsduck/assets'
12
12
  require 'jsduck/json_duck'
13
+ require 'jsduck/io'
13
14
  require 'jsduck/lint'
14
15
  require 'jsduck/template_dir'
15
16
  require 'jsduck/class_writer'
@@ -81,7 +82,7 @@ module JsDuck
81
82
  def parallel_parse(filenames)
82
83
  @parallel.map(filenames) do |fname|
83
84
  Logger.instance.log("Parsing", fname)
84
- SourceFile.new(IO.read(fname), fname, @opts)
85
+ SourceFile.new(JsDuck::IO.read(fname), fname, @opts)
85
86
  end
86
87
  end
87
88
 
@@ -22,7 +22,7 @@ module JsDuck
22
22
  :guides => @assets.guides.to_array,
23
23
  :videos => @assets.videos.to_array,
24
24
  :examples => @assets.examples.to_array,
25
- :search => SearchData.new.create(@relations.classes),
25
+ :search => SearchData.new.create(@relations.classes, @assets),
26
26
  :stats => @opts.stats ? Stats.new.create(@relations.classes) : [],
27
27
  :signatures => MetaTagRegistry.instance.signatures,
28
28
  :localStorageDb => @opts.local_storage_db,
data/lib/jsduck/class.rb CHANGED
@@ -178,7 +178,7 @@ module JsDuck
178
178
  end
179
179
 
180
180
  # merges second members hash into first one
181
- def merge!(hash1, hash2)
181
+ def merge!(hash1, hash2, skip_overrides=false)
182
182
  hash2.each_pair do |name, m|
183
183
  if m[:meta] && m[:meta][:hide]
184
184
  if hash1[name]
@@ -210,7 +210,18 @@ module JsDuck
210
210
  # this, ignore overriding itself.
211
211
  if new[:owner] != old[:owner]
212
212
  new[:overrides] = [] unless new[:overrides]
213
- new[:overrides] << old unless new[:overrides].any? {|m| m[:owner] == old[:owner] }
213
+ unless new[:overrides].any? {|m| m[:owner] == old[:owner] }
214
+ # Make a copy of the important properties for us. We can't
215
+ # just push the actual `old` member itself, because there
216
+ # can be circular overrides (notably with Ext.Base), which
217
+ # will result in infinite loop when we try to convert our
218
+ # class into JSON.
219
+ new[:overrides] << {
220
+ :name => old[:name],
221
+ :owner => old[:owner],
222
+ :id => old[:id],
223
+ }
224
+ end
214
225
  end
215
226
  end
216
227
 
@@ -1,8 +1,11 @@
1
+ # -*- coding: utf-8 -*-
1
2
  require 'rubygems'
2
3
  require 'rdiscount'
3
4
  require 'strscan'
4
5
  require 'cgi'
5
6
  require 'jsduck/logger'
7
+ require 'jsduck/inline_img'
8
+ require 'jsduck/inline_video'
6
9
 
7
10
  module JsDuck
8
11
 
@@ -20,22 +23,6 @@ module JsDuck
20
23
  # Default value: '<a href="%c%M">%a</a>'
21
24
  attr_accessor :link_tpl
22
25
 
23
- # Template HTML that replaces {@img URL alt text}
24
- # Can contain placeholders:
25
- #
26
- # %u - URL from @img tag (e.g. "some/path.png")
27
- # %a - alt text for image
28
- #
29
- # Default value: '<img src="%u" alt="%a"/>'
30
- attr_accessor :img_tpl
31
-
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
38
-
39
26
  # Sets up instance to work in context of particular class, so
40
27
  # that when {@link #blah} is encountered it knows that
41
28
  # Context#blah is meant.
@@ -61,13 +48,26 @@ module JsDuck
61
48
  @max_length = 120
62
49
  @relations = relations
63
50
  @images = []
51
+
52
+ @inline_img = InlineImg.new(opts)
53
+ @inline_video = InlineVideo.new(opts)
54
+
64
55
  @link_tpl = opts[:link_tpl] || '<a href="%c%#%m">%a</a>'
65
- @img_tpl = opts[:img_tpl] || '<img src="%u" alt="%a"/>'
66
56
  @link_re = /\{@link\s+(\S*?)(?:\s+(.+?))?\}/m
67
- @img_re = /\{@img\s+(\S*?)(?:\s+(.+?))?\}/m
57
+
68
58
  @example_annotation_re = /<pre><code>\s*@example( +[^\n]*)?\s+/m
69
59
  end
70
60
 
61
+ # Sets base path to prefix images from {@img} tags.
62
+ def img_path=(path)
63
+ @inline_img.base_path = path
64
+ end
65
+
66
+ # Returns list of all image paths gathered from {@img} tags.
67
+ def images
68
+ @inline_img.images
69
+ end
70
+
71
71
  # Replaces {@link} and {@img} tags, auto-generates links for
72
72
  # recognized classnames.
73
73
  #
@@ -93,8 +93,10 @@ module JsDuck
93
93
  while !s.eos? do
94
94
  if s.check(@link_re)
95
95
  out += replace_link_tag(s.scan(@link_re))
96
- elsif s.check(@img_re)
97
- out += replace_img_tag(s.scan(@img_re))
96
+ elsif substitute = @inline_img.replace(s)
97
+ out += substitute
98
+ elsif substitute = @inline_video.replace(s)
99
+ out += substitute
98
100
  elsif s.check(/[{]/)
99
101
  # There might still be "{" that doesn't begin {@link} or {@img} - ignore it
100
102
  out += s.scan(/[{]/)
@@ -185,10 +187,6 @@ module JsDuck
185
187
  end
186
188
  end
187
189
 
188
- def replace_img_tag(input)
189
- input.sub(@img_re) { img($1, $2) }
190
- end
191
-
192
190
  # Looks input text for patterns like:
193
191
  #
194
192
  # My.ClassName
@@ -258,21 +256,6 @@ module JsDuck
258
256
  Logger.instance.warn(:link_auto, msg, @doc_context[:filename], @doc_context[:linenr])
259
257
  end
260
258
 
261
- # applies the image template
262
- def img(url, alt_text)
263
- @images << url
264
- @img_tpl.gsub(/(%\w)/) do
265
- case $1
266
- when '%u'
267
- @img_path ? (@img_path + "/" + url) : url
268
- when '%a'
269
- CGI.escapeHTML(alt_text||"")
270
- else
271
- $1
272
- end
273
- end
274
- end
275
-
276
259
  # applies the link template
277
260
  def link(cls, member, anchor_text, type=nil, static=false)
278
261
  # Use the canonical class name for link (not some alternateClassName)
@@ -345,21 +328,25 @@ module JsDuck
345
328
  #
346
329
  def shorten(input)
347
330
  sent = first_sentence(strip_tags(input))
348
- if sent.length > @max_length
349
- sent[0..(@max_length-4)] + "..."
331
+ # Use u-modifier to correctly count multi-byte characters
332
+ chars = sent.scan(/./mu)
333
+ if chars.length > @max_length
334
+ chars[0..(@max_length-4)].join + "..."
350
335
  else
351
336
  sent + " ..."
352
337
  end
353
338
  end
354
339
 
355
340
  def first_sentence(str)
356
- str.sub(/\A(.+?\.)\s.*\Z/m, "\\1")
341
+ str.sub(/\A(.+?(\.|。))\s.*\Z/mu, "\\1")
357
342
  end
358
343
 
359
344
  # Returns true when input should get shortened.
360
345
  def too_long?(input)
361
346
  stripped = strip_tags(input)
362
- first_sentence(stripped).length < stripped.length || stripped.length > @max_length
347
+ # for sentence v/s full - compare byte length
348
+ # for full v/s max - compare char length
349
+ first_sentence(stripped).length < stripped.length || stripped.scan(/./mu).length > @max_length
363
350
  end
364
351
 
365
352
  def strip_tags(str)
@@ -286,7 +286,9 @@ module JsDuck
286
286
  add_tag(:type)
287
287
  skip_horiz_white
288
288
  if look(/\{/)
289
- @current_tag[:type] = typedef
289
+ tdf = typedef
290
+ @current_tag[:type] = tdf[:type]
291
+ @current_tag[:optional] = true if tdf[:optional]
290
292
  elsif look(/\S/)
291
293
  @current_tag[:type] = @input.scan(/\S+/)
292
294
  end
@@ -354,10 +356,13 @@ module JsDuck
354
356
  end
355
357
 
356
358
  # matches {type} if possible and sets it on @current_tag
359
+ # Also checks for {optionality=} in type definition.
357
360
  def maybe_type
358
361
  skip_horiz_white
359
362
  if look(/\{/)
360
- @current_tag[:type] = typedef
363
+ tdf = typedef
364
+ @current_tag[:type] = tdf[:type]
365
+ @current_tag[:optional] = true if tdf[:optional]
361
366
  end
362
367
  end
363
368
 
@@ -431,12 +436,28 @@ module JsDuck
431
436
  end
432
437
  end
433
438
 
434
- # matches {...} and returns text inside brackets
439
+ # matches {...=} and returns text inside brackets
435
440
  def typedef
436
441
  match(/\{/)
437
- name = @input.scan(/[^}]+/)
442
+ name = @input.scan(/[^{}]*/)
443
+
444
+ # Type definition can contain nested braces: {{foo:Number}}
445
+ # In such case we parse the definition so that the braces are balanced.
446
+ while @input.check(/[{]/)
447
+ name += "{" + typedef[:type] +"}"
448
+ name += @input.scan(/[^{}]*/)
449
+ end
450
+
451
+ if name =~ /=$/
452
+ name = name.chop
453
+ optional = true
454
+ else
455
+ optional = nil
456
+ end
457
+
438
458
  match(/\}/)
439
- return name
459
+
460
+ return {:type => name, :optional => optional}
440
461
  end
441
462
 
442
463
  # matches <ident_chain> <ident_chain> ... until line end
data/lib/jsduck/guides.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require 'jsduck/logger'
2
2
  require 'jsduck/json_duck'
3
+ require 'jsduck/io'
3
4
  require 'jsduck/null_object'
4
5
  require 'jsduck/logger'
5
6
  require 'jsduck/grouped_asset'
@@ -51,7 +52,7 @@ module JsDuck
51
52
  @formatter.doc_context = {:filename => guide_file, :linenr => 0}
52
53
  name = File.basename(in_dir)
53
54
  @formatter.img_path = "guides/#{name}"
54
- html = add_toc(guide, @formatter.format(IO.read(guide_file)))
55
+ html = add_toc(guide, @formatter.format(JsDuck::IO.read(guide_file)))
55
56
 
56
57
  JsonDuck.write_jsonp(out_dir+"/README.js", name, {:guide => html, :title => guide["title"]})
57
58
  end
@@ -1,4 +1,5 @@
1
1
  require 'jsduck/logger'
2
+ require 'jsduck/io'
2
3
  require 'fileutils'
3
4
 
4
5
  module JsDuck
@@ -49,7 +50,7 @@ module JsDuck
49
50
  # Opens in_file, replaces {keys} inside it, writes to out_file
50
51
  def write_template(in_file, out_file, replacements)
51
52
  Logger.instance.log("Writing", out_file)
52
- html = IO.read(in_file)
53
+ html = JsDuck::IO.read(in_file)
53
54
  html.gsub!(/\{\w+\}/) do |key|
54
55
  replacements[key] ? replacements[key] : key
55
56
  end
@@ -0,0 +1,53 @@
1
+ require 'cgi'
2
+ require 'jsduck/logger'
3
+
4
+ module JsDuck
5
+
6
+ # Implementation of inline tag {@img}
7
+ class InlineImg
8
+ # Base path to prefix images from {@img} tags.
9
+ # Defaults to no prefix.
10
+ attr_accessor :base_path
11
+
12
+ # This will hold list of all image paths gathered from {@img} tags.
13
+ attr_accessor :images
14
+
15
+ def initialize(opts={})
16
+ @tpl = opts[:img_tpl] || '<img src="%u" alt="%a"/>'
17
+
18
+ @re = /\{@img\s+(\S*?)(?:\s+(.+?))?\}/m
19
+
20
+ @base_path = nil
21
+ @images = []
22
+ end
23
+
24
+ # Takes StringScanner instance.
25
+ #
26
+ # Looks for inline tag at the current scan pointer position, when
27
+ # found, moves scan pointer forward and performs the apporpriate
28
+ # replacement.
29
+ def replace(input)
30
+ if input.check(@re)
31
+ input.scan(@re).sub(@re) { apply_tpl($1, $2) }
32
+ else
33
+ false
34
+ end
35
+ end
36
+
37
+ # applies the image template
38
+ def apply_tpl(url, alt_text)
39
+ @images << url
40
+ @tpl.gsub(/(%\w)/) do
41
+ case $1
42
+ when '%u'
43
+ @base_path ? (@base_path + "/" + url) : url
44
+ when '%a'
45
+ CGI.escapeHTML(alt_text||"")
46
+ else
47
+ $1
48
+ end
49
+ end
50
+ end
51
+ end
52
+
53
+ end
@@ -0,0 +1,58 @@
1
+ require 'cgi'
2
+ require 'jsduck/logger'
3
+
4
+ module JsDuck
5
+
6
+ # Implementation of inline tag {@video}
7
+ class InlineVideo
8
+ def initialize(opts={})
9
+ @templates = {
10
+ "html5" => '<video src="%u">%a</video>',
11
+ "vimeo" => [
12
+ '<p><object width="640" height="360">',
13
+ '<param name="allowfullscreen" value="true" />',
14
+ '<param name="allowscriptaccess" value="always" />',
15
+ '<param name="flashvars" value="api=1" />',
16
+ '<param name="movie" value="http://vimeo.com/moogaloop.swf?clip_id=%u&amp;server=vimeo.com&amp;color=4CC208&amp;fullscreen=1" />',
17
+ '<embed src="http://vimeo.com/moogaloop.swf?clip_id=%u&amp;server=vimeo.com&amp;color=4CC208&amp;fullscreen=1" ',
18
+ 'type="application/x-shockwave-flash" allowfullscreen="true" allowscriptaccess="always" width="640" height="360"></embed>',
19
+ '</object></p>',
20
+ ].join
21
+ }
22
+
23
+ @re = /\{@video\s+(\w+)\s+(\S*?)(?:\s+(.+?))?\}/m
24
+ end
25
+
26
+ # Takes StringScanner instance.
27
+ #
28
+ # Looks for inline tag at the current scan pointer position, when
29
+ # found, moves scan pointer forward and performs the apporpriate
30
+ # replacement.
31
+ def replace(input)
32
+ if input.check(@re)
33
+ input.scan(@re).sub(@re) { apply_tpl($1, $2, $3) }
34
+ else
35
+ false
36
+ end
37
+ end
38
+
39
+ # applies the video template of the specified type
40
+ def apply_tpl(type, url, alt_text)
41
+ unless @templates.has_key?(type)
42
+ Logger.instance.warn(nil, "Unknown video type #{type}")
43
+ end
44
+
45
+ @templates[type].gsub(/(%\w)/) do
46
+ case $1
47
+ when '%u'
48
+ url
49
+ when '%a'
50
+ CGI.escapeHTML(alt_text||"")
51
+ else
52
+ $1
53
+ end
54
+ end
55
+ end
56
+ end
57
+
58
+ end
data/lib/jsduck/io.rb ADDED
@@ -0,0 +1,30 @@
1
+ module JsDuck
2
+
3
+ # A helper to use instead the builtin IO class to read files in
4
+ # correct encoding.
5
+ #
6
+ # By default in Ruby 1.9 the encoding is auto-detected, which can
7
+ # have surprising results. So in here we read in all files in UTF-8
8
+ # (the default) or in some other encoding specified through --encoding
9
+ # option and convert it to UTF-8 internally.
10
+ class IO
11
+ @@encoding = "UTF-8"
12
+
13
+ # Sets the external encoding to be used for reading files.
14
+ # When it's different from UTF-8, the input will be converted to UTF-8.
15
+ def self.encoding=(e)
16
+ if e =~ /^UTF-8$/i
17
+ @@encoding = e
18
+ else
19
+ @@encoding = e+":UTF-8"
20
+ end
21
+ end
22
+
23
+ # Reads given filename into string
24
+ def self.read(filename)
25
+ File.open(filename, "r:"+@@encoding) {|f| f.read }
26
+ end
27
+
28
+ end
29
+
30
+ end