Loading lib/jsduck/app.rb +10 −0 Original line number Diff line number Diff line Loading @@ -5,6 +5,7 @@ require 'jsduck/source_formatter' require 'jsduck/class' require 'jsduck/tree' require 'jsduck/tree_icons' require 'jsduck/members' require 'jsduck/subclasses' require 'jsduck/page' require 'jsduck/timer' Loading Loading @@ -38,6 +39,7 @@ module JsDuck result = @timer.time(:aggregating) { aggregate(parsed_files) } classes = @timer.time(:aggregating) { filter_classes(result) } @timer.time(:generating) { write_tree(@output_dir+"/output/tree.js", classes) } @timer.time(:generating) { write_members(@output_dir+"/output/members.js", classes) } @timer.time(:generating) { write_pages(@output_dir+"/output", classes) } @timer.report if @verbose Loading Loading @@ -95,6 +97,14 @@ module JsDuck File.open(filename, 'w') {|f| f.write(js) } end # Given array of doc-objects, generates members data for search and writes in # in JSON form into a file. def write_members(filename, docs) members = Members.new.create(docs) js = "Docs.membersData = " + JSON.generate( members ) + ";" File.open(filename, 'w') {|f| f.write(js) } end # Writes documentation page for each class # We do it in parallel using as many processes as available CPU-s def write_pages(path, docs) Loading lib/jsduck/members.rb 0 → 100644 +69 −0 Original line number Diff line number Diff line module JsDuck # Creator of package-tree in the format expected by the # documentation browser UI. # # See unit test for example output. # class Members def initialize @root = { :data => [] } end # Given list of class documentation objects returns a # tree-structure that can be turned into JSON that's needed by # documentation browser interface. def create(docs) docs.each {|cls| add_class(cls) } #sort_tree(@root) @root end # Sorts all child nodes, and recursively all child packages. def sort_tree(node) node[:data].sort! {|a,b| compare(a, b) } end # Comparson method that sorts package nodes before class nodes. def compare(a, b) if a[:cls] == b[:cls] a[:member] <=> b[:member] else a[:cls] <=> b[:cls] end end # When package for the class exists, add class node to that # package; otherwise create the package first. def add_class(cls) cls.members(:cfg).each do |m| @root[:data] << member_node(m, cls) end cls.members(:property).each do |m| @root[:data] << member_node(m, cls) end cls.members(:method).each do |m| @root[:data] << member_node(m, cls) end cls.members(:event).each do |m| @root[:data] << member_node(m, cls) end end # Given full doc object for class creates class node def member_node(m, cls) #puts "DBG: #{m}" return { :cls => "#{cls.full_name}", :member => m[:name], :type => m[:tagname], :doc => m[:doc] } end end end template/index.html +1 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ <script type="text/javascript" src="resources/prettify/prettify.js"></script> <script type="text/javascript" src="resources/docs.js"></script> <script type="text/javascript" src="output/tree.js"></script> <script type="text/javascript" src="output/members.js"></script> </head> <body scroll="no" id="docs"> Loading template/resources/docs.css +3 −1 Original line number Diff line number Diff line Loading @@ -250,10 +250,12 @@ a:hover { .icon-event { background-image: url(event.gif) !important; } .icon-cfg, .icon-config { background-image: url(config.gif) !important; } .icon-prop { .icon-prop, .icon-property { background-image: url(prop.gif) !important; } .icon-method { Loading template/resources/docs.js +39 −27 Original line number Diff line number Diff line Loading @@ -75,7 +75,7 @@ Ext.extend(ApiPanel, Ext.tree.TreePanel, { handler: function(){ this.root.collapse(true); }, scope: this }] }) }); ApiPanel.superclass.initComponent.call(this); }, filterTree: function(t, e){ Loading Loading @@ -226,21 +226,16 @@ DocPanel = Ext.extend(Ext.Panel, { MainPanel = function(){ this.searchStore = new Ext.data.Store({ proxy: new Ext.data.ScriptTagProxy({ url: 'http://extjs.com/playpen/api.php' }), reader: new Ext.data.JsonReader({ root: 'data' }, ['cls', 'member', 'type', 'doc'] ), baseParams: {}, listeners: { 'beforeload' : function(){ this.baseParams.qt = Ext.getCmp('search-type').getValue(); } } root: 'data', fields: ['cls', 'member', 'type', 'doc'] }) }); this.searchStore.loadData(Docs.membersData); this.searchStore.filterBy(function(r) { return false; }); MainPanel.superclass.constructor.call(this, { id:'doc-body', Loading @@ -256,7 +251,9 @@ MainPanel = function(){ items: { id:'welcome-panel', title: 'API Home', autoLoad: {url: 'welcome.html', callback: this.initSearch, scope: this}, autoLoad: {url: 'welcome.html', callback: function() { this.initSearch.defer(100, this); }, scope: this}, iconCls:'icon-docs', autoScroll: true, tbar: [ Loading Loading @@ -291,7 +288,7 @@ Ext.extend(MainPanel, Ext.TabPanel, { }, onClick: function(e, target){ if(target = e.getTarget('a:not(.exi)', 3)){ if((target = e.getTarget('a:not(.exi)', 3))){ var cls = Ext.fly(target).getAttributeNS('ext', 'cls'); e.stopEvent(); if(cls){ Loading @@ -302,7 +299,7 @@ Ext.extend(MainPanel, Ext.TabPanel, { }else{ window.open(target.href); } }else if(target = e.getTarget('.micon', 2)){ }else if((target = e.getTarget('.micon', 2))){ e.stopEvent(); var tr = Ext.fly(target.parentNode); if(tr.hasClass('expandable')){ Loading Loading @@ -344,7 +341,7 @@ Ext.extend(MainPanel, Ext.TabPanel, { '<tpl for=".">', '<div class="search-item">', '<a class="member" ext:cls="{cls}" ext:member="{member}" href="output/{cls}.html">', '<img src="../resources/images/default/s.gif" class="item-icon icon-{type}"/>{member}', '<img src="resources/images/default/s.gif" class="item-icon icon-{type}"/>{member}', '</a> ', '<a class="cls" ext:cls="{cls}" href="output/{cls}.html">{cls}</a>', '<p>{doc}</p>', Loading @@ -352,7 +349,7 @@ Ext.extend(MainPanel, Ext.TabPanel, { ); var p = new Ext.DataView({ applyTo: 'search', applyTo: Ext.get('search') ? 'search' : Ext.getCmp('welcome-panel').body, tpl: resultTpl, loadingText:'Searching...', store: this.searchStore, Loading Loading @@ -452,8 +449,9 @@ Ext.app.SearchField = Ext.extend(Ext.form.TwinTriggerField, { onTrigger1Click : function(){ if(this.hasSearch){ this.store.baseParams[this.paramName] = ''; this.store.removeAll(); this.store.filterBy(function(r) { return false; }); this.el.dom.value = ''; this.triggers[0].hide(); this.hasSearch = false; Loading @@ -471,9 +469,21 @@ Ext.app.SearchField = Ext.extend(Ext.form.TwinTriggerField, { Ext.Msg.alert('Invalid Search', 'You must enter a minimum of 2 characters to search the API'); return; } this.store.baseParams[this.paramName] = v; var o = {start: 0}; this.store.reload({params:o}); var type = Ext.getCmp('search-type').getValue(); v = v.toLowerCase(); this.store.filterBy(function(r) { var name = r.get('member'); if (type == 'Starts with') { return name.toLowerCase().indexOf(v) === 0; } else if (type == 'Ends with') { var idx = name.toLowerCase().indexOf(v); return idx != -1 && name.length == v.length + idx; } return name.toLowerCase().indexOf(v) != -1; }); this.hasSearch = true; this.triggers[0].show(); this.focus(); Loading Loading @@ -548,6 +558,8 @@ Ext.extend(Ext.ux.SelectBox, Ext.form.ComboBox, { this.selectPrevPage(); e.stopEvent(); return; default: break; } // skip special keys other than the shift key Loading Loading
lib/jsduck/app.rb +10 −0 Original line number Diff line number Diff line Loading @@ -5,6 +5,7 @@ require 'jsduck/source_formatter' require 'jsduck/class' require 'jsduck/tree' require 'jsduck/tree_icons' require 'jsduck/members' require 'jsduck/subclasses' require 'jsduck/page' require 'jsduck/timer' Loading Loading @@ -38,6 +39,7 @@ module JsDuck result = @timer.time(:aggregating) { aggregate(parsed_files) } classes = @timer.time(:aggregating) { filter_classes(result) } @timer.time(:generating) { write_tree(@output_dir+"/output/tree.js", classes) } @timer.time(:generating) { write_members(@output_dir+"/output/members.js", classes) } @timer.time(:generating) { write_pages(@output_dir+"/output", classes) } @timer.report if @verbose Loading Loading @@ -95,6 +97,14 @@ module JsDuck File.open(filename, 'w') {|f| f.write(js) } end # Given array of doc-objects, generates members data for search and writes in # in JSON form into a file. def write_members(filename, docs) members = Members.new.create(docs) js = "Docs.membersData = " + JSON.generate( members ) + ";" File.open(filename, 'w') {|f| f.write(js) } end # Writes documentation page for each class # We do it in parallel using as many processes as available CPU-s def write_pages(path, docs) Loading
lib/jsduck/members.rb 0 → 100644 +69 −0 Original line number Diff line number Diff line module JsDuck # Creator of package-tree in the format expected by the # documentation browser UI. # # See unit test for example output. # class Members def initialize @root = { :data => [] } end # Given list of class documentation objects returns a # tree-structure that can be turned into JSON that's needed by # documentation browser interface. def create(docs) docs.each {|cls| add_class(cls) } #sort_tree(@root) @root end # Sorts all child nodes, and recursively all child packages. def sort_tree(node) node[:data].sort! {|a,b| compare(a, b) } end # Comparson method that sorts package nodes before class nodes. def compare(a, b) if a[:cls] == b[:cls] a[:member] <=> b[:member] else a[:cls] <=> b[:cls] end end # When package for the class exists, add class node to that # package; otherwise create the package first. def add_class(cls) cls.members(:cfg).each do |m| @root[:data] << member_node(m, cls) end cls.members(:property).each do |m| @root[:data] << member_node(m, cls) end cls.members(:method).each do |m| @root[:data] << member_node(m, cls) end cls.members(:event).each do |m| @root[:data] << member_node(m, cls) end end # Given full doc object for class creates class node def member_node(m, cls) #puts "DBG: #{m}" return { :cls => "#{cls.full_name}", :member => m[:name], :type => m[:tagname], :doc => m[:doc] } end end end
template/index.html +1 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ <script type="text/javascript" src="resources/prettify/prettify.js"></script> <script type="text/javascript" src="resources/docs.js"></script> <script type="text/javascript" src="output/tree.js"></script> <script type="text/javascript" src="output/members.js"></script> </head> <body scroll="no" id="docs"> Loading
template/resources/docs.css +3 −1 Original line number Diff line number Diff line Loading @@ -250,10 +250,12 @@ a:hover { .icon-event { background-image: url(event.gif) !important; } .icon-cfg, .icon-config { background-image: url(config.gif) !important; } .icon-prop { .icon-prop, .icon-property { background-image: url(prop.gif) !important; } .icon-method { Loading
template/resources/docs.js +39 −27 Original line number Diff line number Diff line Loading @@ -75,7 +75,7 @@ Ext.extend(ApiPanel, Ext.tree.TreePanel, { handler: function(){ this.root.collapse(true); }, scope: this }] }) }); ApiPanel.superclass.initComponent.call(this); }, filterTree: function(t, e){ Loading Loading @@ -226,21 +226,16 @@ DocPanel = Ext.extend(Ext.Panel, { MainPanel = function(){ this.searchStore = new Ext.data.Store({ proxy: new Ext.data.ScriptTagProxy({ url: 'http://extjs.com/playpen/api.php' }), reader: new Ext.data.JsonReader({ root: 'data' }, ['cls', 'member', 'type', 'doc'] ), baseParams: {}, listeners: { 'beforeload' : function(){ this.baseParams.qt = Ext.getCmp('search-type').getValue(); } } root: 'data', fields: ['cls', 'member', 'type', 'doc'] }) }); this.searchStore.loadData(Docs.membersData); this.searchStore.filterBy(function(r) { return false; }); MainPanel.superclass.constructor.call(this, { id:'doc-body', Loading @@ -256,7 +251,9 @@ MainPanel = function(){ items: { id:'welcome-panel', title: 'API Home', autoLoad: {url: 'welcome.html', callback: this.initSearch, scope: this}, autoLoad: {url: 'welcome.html', callback: function() { this.initSearch.defer(100, this); }, scope: this}, iconCls:'icon-docs', autoScroll: true, tbar: [ Loading Loading @@ -291,7 +288,7 @@ Ext.extend(MainPanel, Ext.TabPanel, { }, onClick: function(e, target){ if(target = e.getTarget('a:not(.exi)', 3)){ if((target = e.getTarget('a:not(.exi)', 3))){ var cls = Ext.fly(target).getAttributeNS('ext', 'cls'); e.stopEvent(); if(cls){ Loading @@ -302,7 +299,7 @@ Ext.extend(MainPanel, Ext.TabPanel, { }else{ window.open(target.href); } }else if(target = e.getTarget('.micon', 2)){ }else if((target = e.getTarget('.micon', 2))){ e.stopEvent(); var tr = Ext.fly(target.parentNode); if(tr.hasClass('expandable')){ Loading Loading @@ -344,7 +341,7 @@ Ext.extend(MainPanel, Ext.TabPanel, { '<tpl for=".">', '<div class="search-item">', '<a class="member" ext:cls="{cls}" ext:member="{member}" href="output/{cls}.html">', '<img src="../resources/images/default/s.gif" class="item-icon icon-{type}"/>{member}', '<img src="resources/images/default/s.gif" class="item-icon icon-{type}"/>{member}', '</a> ', '<a class="cls" ext:cls="{cls}" href="output/{cls}.html">{cls}</a>', '<p>{doc}</p>', Loading @@ -352,7 +349,7 @@ Ext.extend(MainPanel, Ext.TabPanel, { ); var p = new Ext.DataView({ applyTo: 'search', applyTo: Ext.get('search') ? 'search' : Ext.getCmp('welcome-panel').body, tpl: resultTpl, loadingText:'Searching...', store: this.searchStore, Loading Loading @@ -452,8 +449,9 @@ Ext.app.SearchField = Ext.extend(Ext.form.TwinTriggerField, { onTrigger1Click : function(){ if(this.hasSearch){ this.store.baseParams[this.paramName] = ''; this.store.removeAll(); this.store.filterBy(function(r) { return false; }); this.el.dom.value = ''; this.triggers[0].hide(); this.hasSearch = false; Loading @@ -471,9 +469,21 @@ Ext.app.SearchField = Ext.extend(Ext.form.TwinTriggerField, { Ext.Msg.alert('Invalid Search', 'You must enter a minimum of 2 characters to search the API'); return; } this.store.baseParams[this.paramName] = v; var o = {start: 0}; this.store.reload({params:o}); var type = Ext.getCmp('search-type').getValue(); v = v.toLowerCase(); this.store.filterBy(function(r) { var name = r.get('member'); if (type == 'Starts with') { return name.toLowerCase().indexOf(v) === 0; } else if (type == 'Ends with') { var idx = name.toLowerCase().indexOf(v); return idx != -1 && name.length == v.length + idx; } return name.toLowerCase().indexOf(v) != -1; }); this.hasSearch = true; this.triggers[0].show(); this.focus(); Loading Loading @@ -548,6 +558,8 @@ Ext.extend(Ext.ux.SelectBox, Ext.form.ComboBox, { this.selectPrevPage(); e.stopEvent(); return; default: break; } // skip special keys other than the shift key Loading