Loading lib/jsduck/app.rb +2 −1 Original line number Diff line number Diff line Loading @@ -160,10 +160,12 @@ module JsDuck # Writes JSON export or JsonP file for each class def write_classes exporter = Exporter.new(@relations, get_doc_formatter) renderer = Renderer.new(@opts) @parallel.each(@relations.classes) do |cls| filename = @opts.output_dir+"/output/" + cls[:name] + (@opts.export ? ".json" : ".js") Logger.instance.log("Writing to #{filename} ...") data = exporter.export(cls) data[:html] = renderer.render(data) if @opts.export JsonDuck.write_json(filename, data) else Loading @@ -172,7 +174,6 @@ module JsDuck end # Print export: needs to be done non-parallel if @opts.seo renderer = Renderer.new(@opts) @relations.classes.each do |cls| renderer.write(exporter.export(cls)) end Loading lib/jsduck/renderer.rb +308 −0 Original line number Diff line number Diff line require 'cgi' module JsDuck # Ruby-side implementation of class docs Renderer. Loading @@ -20,6 +22,312 @@ module JsDuck # clean up temporary file FileUtils.rm(@tmp_filename) end def render(cls) @cls = cls return [ "<div>", render_hierarchy, "<div class='doc-contents'>", @cls[:doc], render_private_class_notice, "</div>", "<div class='members'>", render_member_sections, "</div>", "</div>", ].flatten.compact.join end def render_private_class_notice return if !@cls[:private] return [ "<tpl if='private'>", "<p class='private'><strong>NOTE</strong> ", "This is a private utility class for internal use by the framework. ", "Don't rely on its existence.</p>", "</tpl>", ] end def render_hierarchy has_parents = @cls[:extends] && @cls[:extends] != "Object" has_alt_names = @cls[:alternateClassNames].length > 0 has_mixins = @cls[:superclasses].length > 0 return if !has_parents && !has_alt_names && !has_mixins return [ '<pre class="hierarchy">', render_alternate_class_names, render_tree, render_mixins, '</pre>' ] end def render_alternate_class_names return if @cls[:alternateClassNames].length == 0 return [ "<h4>Alternate names</h4>", @cls[:alternateClassNames].map {|name| "<div class='alternate-class-name'>#{name}</div>" }, ] end def render_mixins return if @cls[:allMixins].length == 0 return [ "<h4>Mixins</h4>", @cls[:allMixins].map {|name| "<div class='mixin'>#{render_link(name)}</div>" }, ] end # Take care of the special case where class has parent for which we have no docs. # In that case the "extends" property exists but "superclasses" is empty. # We still create the tree, but without links in it. def render_tree return if !@cls[:extends] || @cls[:extends] == "Object" tree = ["<h4>Hierarchy</h4>"] if @cls[:superclasses].length > 0 tree + render_class_tree(@cls[:superclasses].concat([@cls[:name]]), {:first => true, :links => true}) else tree + render_class_tree([@cls[:extends], @cls[:name]], {:first => true}) end end def render_class_tree(superclasses, o) return "" if superclasses.length == 0 name = superclasses[0] return [ "<div class='subclass #{o[:first] ? 'first-child' : ''}'>", superclasses.length > 1 ? (o[:links] ? render_link(name) : name) : "<strong>#{name}</strong>", render_class_tree(superclasses.slice(1, superclasses.length-1), {:links => o[:links]}), "</div>", ] end def render_link(cls_name) return "<a href='#!/api/#{cls_name}' rel='#{cls_name}' class='docClass'>#{cls_name}</a>" end def render_member_sections sections = [ {:type => :cfg, :title => "Config options"}, {:type => :property, :title => "Properties"}, {:type => :method, :title => "Methods"}, {:type => :event, :title => "Events"}, ] # Skip rendering empty sections sections.map do |sec| members = @cls[:members][sec[:type]] statics = @cls[:statics][sec[:type]] if members.length > 0 || statics.length > 0 [ "<div id='m-#{sec[:type]}'>", statics.length == 0 ? "<div class='definedBy'>Defined By</div>" : "", "<h3 class='members-title'>#{sec[:title]}</h3>", render_subsection(members, statics.length > 0 ? "Instance #{sec[:title]}" : nil), render_subsection(statics, "Static #{sec[:title]}"), "</div>", ] else nil end end end def render_subsection(members, title) return if members.length == 0 index = 0 return [ "<div class='subsection'>", title ? "<div class='definedBy'>Defined By</div><h4 class='members-subtitle'>#{title}</h3>" : "", members.map {|m| index += 1; render_member(m, index == 1) }, "</div>", ] end def render_member(m, is_first) # use classname "first-child" when it's first member in its category first_child = is_first ? "first-child" : "" # use classname "expandable" when member has shortened description expandable = m[:shortDoc] ? "expandable" : "not-expandable" # shorthand to owner class owner = m[:owner] # use classname "inherited" when member is not defined in this class inherited = owner == @cls[:name] ? "not-inherited" : "inherited" return [ "<div id='#{m[:tagname]}-#{m[:name]}' class='member #{first_child} #{inherited}'>", # leftmost column: expand button "<a href='#' class='side #{expandable}'>", "<span> </span>", "</a>", # member name and type + link to owner class and source "<div class='title'>", "<div class='meta'>", "<a href='#!/api/#{owner}' rel='#{owner}' class='definedIn docClass'>#{owner}</a><br/>", "<a href='source/#{m[:href]}' target='_blank' class='viewSource'>view source</a>", "</div>", # method params signature or property type signature render_signature(m), "</div>", # short and long descriptions "<div class='description'>", "<div class='short'>", m[:shortDoc], "</div>", "<div class='long'>", render_long_doc(m), "</div>", "</div>", "</div>", ] end def render_signature(m) expandable = m[:shortDoc] ? "expandable" : "not-expandable" name = m[:name] before = "" if m[:tagname] == :method && m[:name] == "constructor" before = "<strong class='constructor-signature'>new</strong>" name = @cls[:name] end if m[:tagname] == :cfg || m[:tagname] == :property params = "<span> : #{m[:type]}</span>" else ps = m[:params].map {|p| render_short_param(p) }.join(", ") params = "( <span class='pre'>#{ps}</span> )" if m[:tagname] == :method && m[:return][:type] != "undefined" params += " : " + m[:return][:type] end end after = "" if m[:protected] after += "<strong class='protected-signature'>protected</strong>" end if m[:static] after += "<strong class='static-signature'>static</strong>" end if m[:deprecated] after += "<strong class='deprecated-signature'>deprecated</strong>" end if m[:tagname] == :cfg && !m[:optional] after += "<strong class='required-signature'>required</strong>" end uri = "#!/api/#{m[:owner]}-#{m[:tagname]}-#{m[:name]}" return [ before, "<a href='#{uri}' class='name #{expandable}'>#{name}</a>", params, after ] end def render_short_param(param) p = param[:type] + " " + param[:name] return param[:optional] ? "["+p+"]" : p end def render_long_doc(m) doc = [m[:doc]] if m[:default] doc << "<p>Defaults to: <code>" + CGI.escapeHTML(m[:default]) + "</code></p>" end if m[:deprecated] v = m[:deprecated][:version] ? "since " + m[:deprecated][:version] : "" doc << [ "<div class='deprecated'>", "<p>This #{m[:tagname]} has been <strong>deprecated</strong> #{v}</p>", m[:deprecated][:text], "</div>", ] end doc << render_params_and_return(m) doc end # Handles both rendering of method parameters and return value. # Plus the rendering of object properties, which could also be # functions in which case they too will be rendered with # parameters and return value. def render_params_and_return(item) doc = [] if item[:params] && item[:params].length > 0 params = item[:params] elsif item[:properties] && item[:properties].length > 0 params = item[:properties] # If the name of last property is "return" # remove it from params list and handle it separately afterwards if params.last[:name] == "return" ret = params.last params = params.slice(0, params.length-1) end end if params if item[:type] == "Function" || item[:tagname] == :method || item[:tagname] == :event doc << '<h3 class="pa">Parameters</h3>' end doc << [ "<ul>", params.map {|p| render_long_param(p) }, "</ul>", ] end if item[:return] doc << render_return(item[:return]) elsif ret doc << render_return(ret) end doc end def render_long_param(p) return [ "<li>", "<span class='pre'>#{p[:name]}</span> : ", p[:type], p[:optional] ? " (optional)" : "", "<div class='sub-desc'>", p[:doc], p[:default] ? "<p>Defaults to: <code>#{CGI.escapeHTML(p[:default])}</code></p>" : "", p[:properties] && p[:properties].length > 0 ? render_params_and_return(p) : "", "</div>", "</li>", ] end def render_return(ret) return [ "<h3 class='pa'>Returns</h3>", "<ul>", "<li>", "<span class='pre'>#{ret[:type]}</span>", "<div class='sub-desc'>", ret[:doc], ret[:properties] && ret[:properties].length > 0 ? render_params_and_return(ret) : "", "</div>", "</li>", "</ul>", ] end end end template/app/view/cls/Overview.js +3 −1 Original line number Diff line number Diff line Loading @@ -76,7 +76,9 @@ Ext.define('Docs.view.cls.Overview', { }); this.addDocked(this.toolbar); this.update(this.renderer.render(docClass)); //this.update(this.renderer.render(docClass)); this.update(docClass.html); Docs.Syntax.highlight(this.getEl()); if (Docs.Settings.get("hideInherited")) { Loading Loading
lib/jsduck/app.rb +2 −1 Original line number Diff line number Diff line Loading @@ -160,10 +160,12 @@ module JsDuck # Writes JSON export or JsonP file for each class def write_classes exporter = Exporter.new(@relations, get_doc_formatter) renderer = Renderer.new(@opts) @parallel.each(@relations.classes) do |cls| filename = @opts.output_dir+"/output/" + cls[:name] + (@opts.export ? ".json" : ".js") Logger.instance.log("Writing to #{filename} ...") data = exporter.export(cls) data[:html] = renderer.render(data) if @opts.export JsonDuck.write_json(filename, data) else Loading @@ -172,7 +174,6 @@ module JsDuck end # Print export: needs to be done non-parallel if @opts.seo renderer = Renderer.new(@opts) @relations.classes.each do |cls| renderer.write(exporter.export(cls)) end Loading
lib/jsduck/renderer.rb +308 −0 Original line number Diff line number Diff line require 'cgi' module JsDuck # Ruby-side implementation of class docs Renderer. Loading @@ -20,6 +22,312 @@ module JsDuck # clean up temporary file FileUtils.rm(@tmp_filename) end def render(cls) @cls = cls return [ "<div>", render_hierarchy, "<div class='doc-contents'>", @cls[:doc], render_private_class_notice, "</div>", "<div class='members'>", render_member_sections, "</div>", "</div>", ].flatten.compact.join end def render_private_class_notice return if !@cls[:private] return [ "<tpl if='private'>", "<p class='private'><strong>NOTE</strong> ", "This is a private utility class for internal use by the framework. ", "Don't rely on its existence.</p>", "</tpl>", ] end def render_hierarchy has_parents = @cls[:extends] && @cls[:extends] != "Object" has_alt_names = @cls[:alternateClassNames].length > 0 has_mixins = @cls[:superclasses].length > 0 return if !has_parents && !has_alt_names && !has_mixins return [ '<pre class="hierarchy">', render_alternate_class_names, render_tree, render_mixins, '</pre>' ] end def render_alternate_class_names return if @cls[:alternateClassNames].length == 0 return [ "<h4>Alternate names</h4>", @cls[:alternateClassNames].map {|name| "<div class='alternate-class-name'>#{name}</div>" }, ] end def render_mixins return if @cls[:allMixins].length == 0 return [ "<h4>Mixins</h4>", @cls[:allMixins].map {|name| "<div class='mixin'>#{render_link(name)}</div>" }, ] end # Take care of the special case where class has parent for which we have no docs. # In that case the "extends" property exists but "superclasses" is empty. # We still create the tree, but without links in it. def render_tree return if !@cls[:extends] || @cls[:extends] == "Object" tree = ["<h4>Hierarchy</h4>"] if @cls[:superclasses].length > 0 tree + render_class_tree(@cls[:superclasses].concat([@cls[:name]]), {:first => true, :links => true}) else tree + render_class_tree([@cls[:extends], @cls[:name]], {:first => true}) end end def render_class_tree(superclasses, o) return "" if superclasses.length == 0 name = superclasses[0] return [ "<div class='subclass #{o[:first] ? 'first-child' : ''}'>", superclasses.length > 1 ? (o[:links] ? render_link(name) : name) : "<strong>#{name}</strong>", render_class_tree(superclasses.slice(1, superclasses.length-1), {:links => o[:links]}), "</div>", ] end def render_link(cls_name) return "<a href='#!/api/#{cls_name}' rel='#{cls_name}' class='docClass'>#{cls_name}</a>" end def render_member_sections sections = [ {:type => :cfg, :title => "Config options"}, {:type => :property, :title => "Properties"}, {:type => :method, :title => "Methods"}, {:type => :event, :title => "Events"}, ] # Skip rendering empty sections sections.map do |sec| members = @cls[:members][sec[:type]] statics = @cls[:statics][sec[:type]] if members.length > 0 || statics.length > 0 [ "<div id='m-#{sec[:type]}'>", statics.length == 0 ? "<div class='definedBy'>Defined By</div>" : "", "<h3 class='members-title'>#{sec[:title]}</h3>", render_subsection(members, statics.length > 0 ? "Instance #{sec[:title]}" : nil), render_subsection(statics, "Static #{sec[:title]}"), "</div>", ] else nil end end end def render_subsection(members, title) return if members.length == 0 index = 0 return [ "<div class='subsection'>", title ? "<div class='definedBy'>Defined By</div><h4 class='members-subtitle'>#{title}</h3>" : "", members.map {|m| index += 1; render_member(m, index == 1) }, "</div>", ] end def render_member(m, is_first) # use classname "first-child" when it's first member in its category first_child = is_first ? "first-child" : "" # use classname "expandable" when member has shortened description expandable = m[:shortDoc] ? "expandable" : "not-expandable" # shorthand to owner class owner = m[:owner] # use classname "inherited" when member is not defined in this class inherited = owner == @cls[:name] ? "not-inherited" : "inherited" return [ "<div id='#{m[:tagname]}-#{m[:name]}' class='member #{first_child} #{inherited}'>", # leftmost column: expand button "<a href='#' class='side #{expandable}'>", "<span> </span>", "</a>", # member name and type + link to owner class and source "<div class='title'>", "<div class='meta'>", "<a href='#!/api/#{owner}' rel='#{owner}' class='definedIn docClass'>#{owner}</a><br/>", "<a href='source/#{m[:href]}' target='_blank' class='viewSource'>view source</a>", "</div>", # method params signature or property type signature render_signature(m), "</div>", # short and long descriptions "<div class='description'>", "<div class='short'>", m[:shortDoc], "</div>", "<div class='long'>", render_long_doc(m), "</div>", "</div>", "</div>", ] end def render_signature(m) expandable = m[:shortDoc] ? "expandable" : "not-expandable" name = m[:name] before = "" if m[:tagname] == :method && m[:name] == "constructor" before = "<strong class='constructor-signature'>new</strong>" name = @cls[:name] end if m[:tagname] == :cfg || m[:tagname] == :property params = "<span> : #{m[:type]}</span>" else ps = m[:params].map {|p| render_short_param(p) }.join(", ") params = "( <span class='pre'>#{ps}</span> )" if m[:tagname] == :method && m[:return][:type] != "undefined" params += " : " + m[:return][:type] end end after = "" if m[:protected] after += "<strong class='protected-signature'>protected</strong>" end if m[:static] after += "<strong class='static-signature'>static</strong>" end if m[:deprecated] after += "<strong class='deprecated-signature'>deprecated</strong>" end if m[:tagname] == :cfg && !m[:optional] after += "<strong class='required-signature'>required</strong>" end uri = "#!/api/#{m[:owner]}-#{m[:tagname]}-#{m[:name]}" return [ before, "<a href='#{uri}' class='name #{expandable}'>#{name}</a>", params, after ] end def render_short_param(param) p = param[:type] + " " + param[:name] return param[:optional] ? "["+p+"]" : p end def render_long_doc(m) doc = [m[:doc]] if m[:default] doc << "<p>Defaults to: <code>" + CGI.escapeHTML(m[:default]) + "</code></p>" end if m[:deprecated] v = m[:deprecated][:version] ? "since " + m[:deprecated][:version] : "" doc << [ "<div class='deprecated'>", "<p>This #{m[:tagname]} has been <strong>deprecated</strong> #{v}</p>", m[:deprecated][:text], "</div>", ] end doc << render_params_and_return(m) doc end # Handles both rendering of method parameters and return value. # Plus the rendering of object properties, which could also be # functions in which case they too will be rendered with # parameters and return value. def render_params_and_return(item) doc = [] if item[:params] && item[:params].length > 0 params = item[:params] elsif item[:properties] && item[:properties].length > 0 params = item[:properties] # If the name of last property is "return" # remove it from params list and handle it separately afterwards if params.last[:name] == "return" ret = params.last params = params.slice(0, params.length-1) end end if params if item[:type] == "Function" || item[:tagname] == :method || item[:tagname] == :event doc << '<h3 class="pa">Parameters</h3>' end doc << [ "<ul>", params.map {|p| render_long_param(p) }, "</ul>", ] end if item[:return] doc << render_return(item[:return]) elsif ret doc << render_return(ret) end doc end def render_long_param(p) return [ "<li>", "<span class='pre'>#{p[:name]}</span> : ", p[:type], p[:optional] ? " (optional)" : "", "<div class='sub-desc'>", p[:doc], p[:default] ? "<p>Defaults to: <code>#{CGI.escapeHTML(p[:default])}</code></p>" : "", p[:properties] && p[:properties].length > 0 ? render_params_and_return(p) : "", "</div>", "</li>", ] end def render_return(ret) return [ "<h3 class='pa'>Returns</h3>", "<ul>", "<li>", "<span class='pre'>#{ret[:type]}</span>", "<div class='sub-desc'>", ret[:doc], ret[:properties] && ret[:properties].length > 0 ? render_params_and_return(ret) : "", "</div>", "</li>", "</ul>", ] end end end
template/app/view/cls/Overview.js +3 −1 Original line number Diff line number Diff line Loading @@ -76,7 +76,9 @@ Ext.define('Docs.view.cls.Overview', { }); this.addDocked(this.toolbar); this.update(this.renderer.render(docClass)); //this.update(this.renderer.render(docClass)); this.update(docClass.html); Docs.Syntax.highlight(this.getEl()); if (Docs.Settings.get("hideInherited")) { Loading