Commit 9104d1c3 authored by Ondrej Jirman's avatar Ondrej Jirman
Browse files

Search implementation

parent 975d48b8
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -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'
@@ -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
@@ -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)

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
+1 −0
Original line number Diff line number Diff line
@@ -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">

+3 −1
Original line number Diff line number Diff line
@@ -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 {
+39 −27
Original line number Diff line number Diff line
@@ -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){
@@ -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',
@@ -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: [
@@ -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){
@@ -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')){
@@ -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>',
@@ -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,
@@ -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;
@@ -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();
@@ -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