1 /**
  2  * @author Amit Kumar
  3  * @date 02/27/2008
  4  * @since 1.2M1
  5  * @extends Workbench.component.Component
  6  * @class Monk.component.ComparisonComponent
  7  * @description This component displays various feature comparisons
  8  */
  9 
 10 Ext.namespace('Ext.ux');
 11 Ext.ux.ErrorHandlingConnection = Ext.extend(Ext.data.Connection, {
 12     timeout: 600000,
 13     handleResponse : function(response){
 14         this.transId = false;
 15         var options = response.argument.options;
 16         response.argument = options ? options.argument : null;
 17 
 18         var error = null;
 19         if (response.getResponseHeader['Content-Type'].match(/text\/javascript/) != null) {
 20             error = Ext.decode(response.responseText).error;
 21         } else if (response.getResponseHeader['Content-Type'].match(/text\/html/) != null) {
 22             var errorMatch = response.responseText.match(/<div id="error">(.*)<\/div>/);
 23             if (errorMatch != null) error = errorMatch[1];
 24         }else if(response.getResponseHeader['Content-Type'].match(/text\/plain/) != null){
 25             // this assumes there are no text/plain content type
 26             error=null;
 27         }else {
 28             var errorTest = response.responseXML.getElementsByTagName('error');
 29             if(errorTest==null){
 30                 error="could not retrieve error message";   
 31             }else{
 32                 if (errorTest.length > 0) error = errorTest[0].firstChild.data;
 33             }
 34         }
 35 
 36         if (error != null) {
 37             Workbench.console.info('An error has occurred: '+error);
 38             Monk.component.messenger.alert('MONK Workbench', 'An server error has occurred:<br/>'+error);
 39             Workbench.component.manager.notify(
 40                 new Monk.event.ServerError({
 41                     label: 'A server error has occurred: '+error
 42                 }), {
 43                     error: error,
 44                     url: this.url // the url we stored in beforerequest
 45                 }
 46             );
 47         } else {
 48             Ext.callback(options.success, options.scope, [response, options]);
 49         }
 50 
 51         this.fireEvent("requestcomplete", this, response, options);
 52         Ext.callback(options.callback, options.scope, [options, true, response]);
 53     }
 54 });
 55 
 56  
 57 Monk.component.ComparisonComponent = function(args) {
 58 
 59   this.searchParams ={'corpusCriterion':'eaf','limit':'1000','start':'0'};
 60   
 61       this.genreStore = new Ext.data.SimpleStore({
 62          fields: ['id','label']
 63          , data : [['*','All'],
 64          ['play','Play'],
 65          ["prose","Prose All"],
 66          ["prose-sermon","--Sermon"],
 67          ["prose-witchcraft","--Witchcraft"],
 68          ["poetry","Poetry"],
 69          ["fiction","Fiction"]
 70          ]
 71     });
 72  
 73                            
 74           
 75     this.dunningFeatureRecord = new Ext.data.Record.create([
 76         {name : 'term', mapping : '@tag'},
 77         {name : 'freq_ref', mapping : '@freq_ref', type: 'float',
 78         convert:function(value){var num = parseFloat(value);return num.toFixed(2)}},
 79         {name : 'count_ref', mapping : '@count_ref',type: 'float'},
 80         {name : 'freq_analysis', mapping : '@freq_analysis', type: 'float',
 81         convert:function(value){var num = parseFloat(value);return num.toFixed(2)}},
 82         {name : 'count_analysis', mapping : '@count_analysis',type: 'float'},
 83         {name : 'score', mapping : '@score',type: 'float',
 84         convert:function(value){var num = parseFloat(value);return num.toFixed(2)}},
 85         {name : 'score_scaled', mapping : '@score_scaled',type: 'float',
 86         convert:function(value){var num = parseFloat(value);return num.toFixed(2)}}
 87     ]);
 88 
 89   
 90     this.dunningStore = new Ext.data.Store({
 91         proxy: new Ext.data.HttpProxy(
 92             new Ext.ux.ErrorHandlingConnection({url: Monk.data.PROXY_URL + 'get/AnalyticsManager.compareFeaturesFrequencyDunning'})
 93         ),
 94         reader: new Ext.data.XmlReader({record : 'ft'}, this.dunningFeatureRecord),
 95         autoLoad: false,
 96         sortInfo:{field: 'score', direction: "DESC"}
 97   });
 98   
 99 
100   this.tfidfStore = new Ext.data.JsonStore({
101      url: Monk.data.PROXY_URL + 'get/FeatureSearchManager.compareFeaturesTfIdf',
102      root: 'features',
103      totalProperty: 'numFeatures',
104      id: 'tag',
105      fields: [
106         {name: 'term', mapping: 'term'},
107         {name: 'field', mapping: 'field'},
108         {name: 'score', mapping: 'score', type: 'float'},
109         {name: 'idf', mapping: 'idf', type: 'float'},
110         {name: 'index_freq', mapping: 'index_freq', type: 'float'},
111         {name: 'source_freq', mapping: 'source_freq', type: 'float'}
112      ],
113      baseParams: {maxFeatures : '-1', limit:20, feature:'lemma', onlyFeatures: true, minWordLen:5},
114      listeners: {
115         beforeload: {
116             fn: function(store, options){
117                 store.baseParams = this.searchParams;
118             },
119             scope: this
120         }
121     }
122   });
123   
124       
125     this.featureRecord = new Ext.data.Record.create([
126         {name : 'type', mapping : '@type'},
127         {name : 'term', mapping : '@tag'},
128         {name : 'freq', mapping : '@freq', type: 'float'},
129         {name : 'count', mapping : '@count'}
130     ]);
131 
132     this.featureStore = new Ext.data.Store({
133         proxy: new Ext.data.HttpProxy(
134             new Ext.ux.ErrorHandlingConnection({url: Monk.data.PROXY_URL + 'get/AnalyticsManager.compareFeatures'})
135         ),
136         reader: new Ext.data.XmlReader({record : 'ft'}, this.featureRecord),
137         autoLoad: false
138     });
139     this.featureStore.proxy.on({load: {
140         fn: function(proxy, data, options){
141             var nodes = Ext.DomQuery.select('chart', data.reader.xmlData);
142             if(nodes!=null){
143             this.chartUrl = nodes[0].firstChild.data;
144             }
145         },
146         scope: this
147     }});
148       
149     this.chartUrl = '';
150     this.chartWin = null;
151       
152   Monk.component.ComparisonComponent.superclass.constructor.call(this, args);
153 }
154 
155 
156 Workbench.extend(Monk.component.ComparisonComponent, Workbench.component.Component, {
157 label : "Feature Comparison",
158 description : "This component is for feature comparison",
159 "window" : this.window,
160 displayFeatureCirculationPeriodChart:   function (searchParams, sm){
161       Ext.get("feature_img").update('Loading...');
162       var conn = new Ext.data.Connection();
163       var featureValues= new Array();
164       var featureGroupTypes = new Array();
165       var records = sm.getSelections();
166       for(record in records){
167                 if(records[record].data==null){
168                    break;
169                 }
170                  Workbench.console.info(records[record].data.term);
171                  featureValues.push(records[record].data.term);
172                  featureGroupTypes.push(records[record].data.type); // this is a misnomer -it is actually a group
173            }
174       this.searchParams.featureValues=featureValues;
175       this.searchParams.featureGroupTypes=featureGroupTypes;
176 
177       conn.request({
178           url: Monk.data.PROXY_URL+'get/AnalyticsManager.compareFeatureFrequency',
179           method: 'POST',
180           params: this.searchParams,
181           success: function(responseObject) {
182               var document = responseObject.responseXML;
183               var chartUrl = '';
184               var nodes = Ext.DomQuery.select("chart", document);
185               if(nodes==null ||  nodes.length==0){
186               	// check for error node
187               	var errorNode = Ext.DomQuery.select("error",document);
188               	if(errorNode!=null){
189               		var message=errorNode[0].firstChild.data;
190               		Ext.Msg.alert('Monk Workbench', 'Error trying to get  feature frequency chart <br/>'+message);
191                     return;
192               	}else{
193                     Ext.Msg.alert('Monk Workbench', 'Error trying to get  feature frequency chart <br/><b>Error '+response.status+'</b>: '+response.statusText);
194                  }
195               }
196               if(nodes[0].firstChild==null){
197               	return;
198               }
199               chartUrl = nodes[0].firstChild.data;
200               
201               Ext.get("feature_img").update('<img alt="Feature Frequency Graph" width="350" height="150" src="'+chartUrl+'"/>');
202           },
203           failure: function(response, options) {
204               Ext.Msg.alert('Monk Workbench', 'Error trying to get  feature frequency chart <br/><b>Error '+response.status+'</b>: '+response.statusText);
205           }
206       });
207 },
208 displayTfIdfTermCirculationPeriodChart: function(searchParams, sm){
209       Ext.get("tfidffeature_img").update('Loading...');
210       var conn = new Ext.data.Connection();
211       var featureValues= new Array();
212       var featureGroupTypes = new Array();
213       var records = sm.getSelections();
214       for(record in records) {
215           if(records[record].data==null){
216               break;
217           }
218           Workbench.console.info(records[record].data.term);
219           featureValues.push(records[record].data.term);
220           featureGroupTypes.push('combined'); // this is a misnomer -it is actually a group
221       }
222       this.searchParams.featureValues=featureValues;
223       this.searchParams.featureGroupTypes=featureGroupTypes;
224       this.searchParams.wordClass="*";
225       conn.request({
226           url: Monk.data.PROXY_URL+'get/AnalyticsManager.compareFeatureFrequency',
227           method: 'GET',
228           params: this.searchParams,
229           success: function(responseObject) {
230               var document = responseObject.responseXML;
231               var chartUrl = '';
232               var nodes = Ext.DomQuery.select("chart", document);
233               
234               if(nodes[0]==null){
235               	return;
236               }
237               
238               if(nodes[0].firstChild==null){
239                 return;
240               }
241               if (nodes != null) chartUrl = nodes[0].firstChild.data;
242               
243               Ext.get("tfidffeature_img").update('<img alt="TF IDF Feature Frequency Graph" width="350" height="150" src="'+chartUrl+'"/>');
244           },
245           failure: function(response, options) {
246               Ext.Msg.alert('Monk Workbench', 'Error trying to get  feature frequency chart <br/><b>Error '+response.status+'</b>: '+response.statusText);
247           }
248       });
249 },
250 
251 
252 displayConcordance: function(word) {
253     var tabs = Ext.getCmp('tab-container');
254     if (tabs.getItem('tab-'+word) != null) {
255         tabs.setActiveTab('tab-'+word);
256     } else {
257         var record = new Ext.data.Record.create([
258             {name: 'id', mapping: '@id'},
259             {name: 'title', mapping: 'title'},
260             {name: 'numFound', mapping:'@numFound'},
261             {name: 'author', mapping: function(record){
262                 return Ext.DomQuery.selectValue('@id', record.parentNode);
263             }},
264             {name: 'circulation', mapping: '@start'},
265             {name: 'concordances', mapping: function(record){
266                 var xmlArray = Ext.DomQuery.select('c', record);
267                 var stringArray = [];
268                 for (var i = 0, len = xmlArray.length; i < len; i++) {
269                     var c = xmlArray[i];
270                     var string = '';
271                     string += c.firstChild.data;
272                     string += '<span class="concordance">'+c.childNodes[1].firstChild.data+'</span>';
273                     string += c.lastChild.data;
274                     stringArray.push(string);
275                 }
276                 if (stringArray.length == 0) {
277                     return [''];
278                 }
279                 return stringArray;
280             }
281             
282             },
283             {name:'showing' , mapping: function(record){
284              var xmlArray = Ext.DomQuery.select('c', record);
285             return xmlArray.length;
286             }
287             }
288             
289         ]);
290        /* var params = {
291             corpusCriterion: this.searchParams.corpusCriterion,
292             wordClass: this.searchParams.wordClass,
293             feature: this.searchParams.feature,
294             numMatchesPerDocument: 3,
295             format: 'xml',
296             featureValues: word
297         }
298         */
299         var params = this.searchParams;
300         params.numMatchesPerDocument = 3;
301         params.format = 'xml';
302         params.featureValues = word;
303         params.limit = 10;
304         delete params.start;
305         
306         var concordanceStore = new Ext.data.Store({
307             proxy: new Ext.data.HttpProxy({
308                 url: Monk.data.PROXY_URL + "get/FeatureSearchManager.getConcordance",
309                 timeout: 50000,
310                 method: 'GET'
311             }),
312             baseParams: params,
313             reader: new Ext.ux.XmlReader({record : 'doc', totalRecords: '@total'}, record),
314             listeners: {
315                 beforeload: function() {
316                     Ext.getCmp('tab-'+word).getEl().mask('Loading '+word+'...');
317                 },
318                 load: function() {
319                     Ext.getCmp('tab-'+word).getEl().unmask();
320                 }
321             }
322         });
323         
324         var tab = tabs.add({
325             id: 'tab-'+word,
326             title: 'Concordance: '+word,
327             closable: true,
328             autoScroll: true,
329             items: {
330                 title: '',
331                 border: false,
332                 xtype: 'dataview',
333                 tpl: new Ext.XTemplate(
334                     '<font color="blue"><i>Click on the Work/Workpart to View the document.</i></font><hr/>',
335                     '<tpl for=".">',
336                         '<div class="concordance-item" id="{id}">',
337                             '<div class="circulation">{circulation}</div>',
338                             '<div class="header"><span class="title">{title}</span> by <span>{author}</span> <i>({numFound}) hits -showing {showing}</i></div>',
339                             '<div class="concordances">',
340                                 '<tpl for="concordances"><div>{.}</div></tpl>',
341                             '</div>',
342                         '</div>',
343                         '<hr/>',
344                     '</tpl>'
345                 ),
346                 singleSelect: true,
347                 overClass: 'x-view-over',
348                 selectedClass: '',
349                 itemSelector: 'div.concordance-item',
350                 store: concordanceStore,
351                 listeners: {
352                     click: {
353                         fn: function(dataview, index, node, event) {
354                             var id = node.id;
355                             var text = Ext.DomQuery.selectValue('span[class=title]', node);
356                             this.notify(new Monk.event.toolflowmanager.OpenTool({
357                                     label: 'Open tool: ChunkViewer'
358                                 }),
359                                 {id: 'chunk-viewer'}
360                             );
361                             this.notify(new Monk.event.chunk.ChunkSelected({
362                                     label: 'Work selected: '+'"'+text+'"'
363                                 }),
364                                 {id: id, text: text, chunkType: 'work', displayText:true}
365                             );
366                         },
367                         scope: this
368                     }
369                 }
370             },
371             tbar:[
372             	{
373             	text:"Toggle show all",
374             	tooltip: {title:'Toggle',text:'Show/Hide All the Concordance'},
375                 iconCls: 'toogle-icon',
376                 handler:function(){
377             	    var params = this.searchParams;
378             	    
379                     if( params.numMatchesPerDocument==-1)
380                         params.numMatchesPerDocument = 3;
381                     else
382                         params.numMatchesPerDocument = -1;
383                     
384                     params.format = 'xml';
385                     params.featureValues = word;
386                     params.limit = 10;
387                     concordanceStore.reload();
388                },
389             	scope:this
390             	}
391             	],
392             bbar: new Ext.PagingToolbar({
393                 store: concordanceStore,
394                 pageSize: 10,
395                 displayInfo: true,
396                 displayMsg: 'Concordance {0} - {1} of {2}',
397                 emptyMsg: 'No concordance found.'
398             })
399         });
400         tabs.setActiveTab(tab);
401         concordanceStore.load({params: {start: 0}});
402     }
403 },
404 getDunnings: function(){
405     var dunningsBody = Ext.get('dunningsBody');
406     var parent = dunningsBody.findParent('div.x-panel-body', 2, true);
407     parent.mask('Loading Dunnings...');
408     
409     var conn = new Ext.ux.ErrorHandlingConnection();
410     conn.request({
411         url: Monk.data.PROXY_URL+'get/AnalyticsManager.compareFeaturesFrequencyDunning',
412         method: 'POST',
413         scope:this,
414         params: this.searchParams,
415         success: function(responseObject, options) {
416             parent.unmask();
417             var height = parent.getHeight();
418             dunningsBody.setHeight(height);
419             dunningsBody.update(responseObject.responseText);
420             dunningsBody.scroll('up', dunningsBody.getScroll().top);
421             var links = Ext.DomQuery.select('a', dunningsBody.dom);
422             for (var i = 0, len = links.length; i < len; i++) {
423                 var linkEl = links[i];
424                 Ext.EventManager.on(linkEl, 'click', function(event, el) {
425                     event.preventDefault();
426                     var word = el.firstChild.data.replace(/\s/g, '');
427                     var type = el.getAttribute('class').split(' hi')[0];
428                     // handling for lemma
429                     if (type != 'spelling') word += ' ('+type+')';
430                     this.displayConcordance(word);
431                 }, this);
432             }
433             this.searchParams.format="xml";
434             this.searchParams.formatType="xml";
435             this.dunningStore.load({params: this.searchParams});
436             
437         },
438         failure: function(response, options) {
439             parent.unmask();
440             Ext.Msg.alert('Monk Workbench', 'Error trying to get dunnings.<br/><b>Error '+response.status+'</b>: '+response.statusText);
441         }
442     });
443 },
444 handle : function(monkEvent, data) {
445 	if(monkEvent.instanceOf(Monk.event.workbench.AdvancedSearchQuery)){
446         this.chartUrl = '';
447         this.searchParams = data[0];
448         this.searchParams.limit = 1000;
449         this.searchParams.start = 0;
450     }else if(monkEvent.instanceOf( Monk.event.workbench.WorksetFeatureComparison)){
451         this.searchParams=data;
452         if(this.searchParams.method=="Dunnings" || this.searchParams.method=="Dunnings:swap"){
453             this.searchParams.format = 'html';
454             this.searchParams.formatType = 'html';
455           /*
456             if(this.searchParams.firstComparisonCriterionAsReference){
457             	this.searchParams.firstComparisonCriterionAsReference=true;
458             }else{
459               this.searchParams.firstComparisonCriterionAsReference=false;
460             }
461            */ 
462             Ext.getCmp("tab-container").activate('dunningsPanel');
463             this.getDunnings();
464       
465         }else if(this.searchParams.method=="FrequencyCount"){
466             Ext.getCmp("tab-container").activate('frequencyComparisonPanel');
467             this.featureStore.load({params: this.searchParams});
468         }else if(this.searchParams.method=="SimilarFeatures" || 
469                  this.searchParams.method=="SimilarFeatures:swap"){
470             Ext.getCmp("tab-container").activate('tfidfComparisonPanel');
471             this.tfidfStore.load({params: this.searchParams});
472         }
473     } else if (monkEvent.instanceOf(Monk.event.workbench.SimpleSearchQuery)) {
474             var query = data.featureValue.split(' (*)')[0];
475             var tab = Ext.getCmp('tab-container').getActiveTab();
476             switch (tab.id) {
477                 case 'frequencyComparisonPanel':
478                     this.filterGrid(query, Ext.getCmp('features-grid'));
479                     break;
480                 case 'dunningsPanel':
481                     this.highlightLinks(query);
482                     this.filterGrid(query,Ext.getCmp('dunningfeatures-grid'));
483                     break;
484                 case 'tfidfComparisonPanel':
485                     this.filterGrid(query, Ext.getCmp('tfidffeatures-grid'));
486                     break;
487             }
488             
489     } else if (monkEvent.instanceOf(Monk.event.workbench.ClearSearch)){
490         var tab = Ext.getCmp('tab-container').getActiveTab();
491         switch (tab.id) {
492             case 'frequencyComparisonPanel':
493                 this.clearFilter(Ext.getCmp('features-grid'));
494                 break;
495             case 'dunningsPanel':
496                 this.highlightLinks('');
497                 this.clearFilter(Ext.getCmp('dunningfeatures-grid'));
498                 break;
499             case 'tfidfComparisonPanel':
500                 this.clearFilter(Ext.getCmp('tfidffeatures-grid'));
501                 break;
502         }
503     }
504 },
505 
506 filterGrid: function(query, grid) {
507     grid.getStore().filter('term', query, true, false);
508 },
509 
510 clearFilter: function(grid) {
511     grid.getStore().clearFilter();
512 },
513 
514 exportItems: function(grid,list){
515 	       var model=grid.getColumnModel();
516 	       var output=[];
517 	       var columnOutput ="";
518 	       for(var i=0; i < model.getColumnCount();i++){
519 	           output[i]=model.getColumnHeader(i);
520 	       }
521 	       output = output.join("\t")+"\n";
522 	       grid.getStore().each(function(record) {
523             var values=[];
524 	       	for (c in list){
525 	       		values.push(record.get(list[c]));
526 	       	}
527 	       	output+=values.join("\t") + "\n";
528             
529            }, this);
530     
531 	                Monk.component.messenger.show({
532                         title: 'Export Features as Tab Separated Values (TSV)',
533                         msg: 'Select all the data below, copy, and paste directly into your favourite spreadsheet program.',
534                         width: 600,
535                         defaultTextHeight: 25,
536                         buttons: Ext.MessageBox.OK,
537                         multiline: true,
538                         value: output,
539                         scope: this
540                     }, this.window.parent ? this.window.parent.window : this.window);
541            
542 	
543 },
544 
545 highlightLinks: function(query, cls) {
546     if (query == '') query = '^$'; // clears selections
547     var re = new RegExp(query,'gi');
548     var links = Ext.DomQuery.select('a', Ext.get('dunningsBody').dom);
549     var firstHighlight = true;
550     for (var i = 0, len = links.length; i < len; i++) {
551         var link = Ext.get(links[i]);
552         if (link.dom.innerHTML.search(re) != -1) {
553             link.addClass('hi');
554             if (firstHighlight) {
555                 link.scrollIntoView(Ext.get('dunningsBody'));
556                 firstHighlight = false;
557             }
558         } else {
559             link.removeClass('hi');
560         }
561     }
562 },
563 
564 beforeExit: function() {
565         // destroy the results grid and remove any listeners it might have set
566         var results = Ext.getCmp('features-grid');
567         results.getStore().purgeListeners();
568         results.destroy();
569         
570         var results1 = Ext.getCmp('tfidffeatures-grid');
571         results1.getStore().purgeListeners();
572         results1.destroy();
573         
574         var results2 = Ext.getCmp('dunningfeatures-grid');
575         results2.getStore().purgeListeners();
576         results2.destroy();
577         
578         
579 },
580 
581 init: function(){
582 	
583     this.notify(
584         new Monk.event.view.ShowSearchOption({label: 'Show Search Option'})
585     );
586     
587       this.notify(
588         new Monk.event.view.HideAdvancedSearchLink({label: 'Hide Advanced Search Link'})
589     );
590     
591     
592     var selectionModel = new Ext.grid.RowSelectionModel({
593         singleSelect: true
594     });
595     
596     // create the  feature Grid
597     var dunningGrid = new Ext.grid.GridPanel({
598         id: 'dunningfeatures-grid',
599         region: 'west',
600         minWidth: 245,
601         width: 300,
602         autoScroll: true,
603         split: true,
604          tbar:[{
605          iconCls: 'exportBtn',
606          text: 'Export Features',
607          handler: function(button, even){this.exportItems(dunningGrid,['term','score','score_scaled',
608          'freq_analysis','freq_ref',"count_analysis","count_ref"])},
609          scope:this
610          }],
611         loadMask: {msg: 'Loading features, please wait.'},
612         store: this.dunningStore,
613         sm: new Ext.grid.RowSelectionModel({
614             singleSelect: true,
615             listeners: {
616                 rowselect: {
617                     fn: function(sm,rowindex,record) {
618                     }, scope: this
619                 }
620             }
621         }),
622        columns: [
623             {id:'term',header: "Term", width: 60, sortable: true, dataIndex: 'term'},
624             {header: "score", width: 60, sortable: true,  dataIndex: 'score',type: 'float'},
625             {header: "score_scaled", width: 60, hidden:true,sortable: true,  dataIndex: 'score_scaled',type: 'float'},
626             {header: "#Ref", width: 60, sortable: true,  dataIndex: 'freq_analysis',type: 'float'},
627             {header: "#Analysis", width: 60, sortable: true, dataIndex: 'freq_ref',type: 'float'},
628             {header: "Ref", width: 60, sortable: true,  dataIndex: 'count_analysis',type: 'float'},
629             {header: "Analysis", width: 60, sortable: true, dataIndex: 'count_ref',type: 'float'}
630         ],
631         stripeRows: true,
632         title:'Dunning Features',
633         listeners: {
634              rowclick:function(grid,rowIndex,event){
635 
636              }
637 
638 
639         }
640     });
641 
642 
643 
644 	
645 	 // create the  feature Grid
646     var tfidfGrid = new Ext.grid.GridPanel({
647     	id: 'tfidffeatures-grid',
648     	region: 'west',
649         minWidth: 245,
650         width: 300,
651         autoScroll: true,
652         split: true,
653         loadMask: {msg: 'Loading features, please wait.'},
654         store: this.tfidfStore,
655          tbar:[{
656          iconCls: 'exportBtn',
657          text: 'Export Features',
658          handler: function(button, even){this.exportItems(tfidfGrid,['term','score','idf',
659          'index_freq','source_freq'])},
660          scope:this
661          }],
662         sm: new Ext.grid.RowSelectionModel({
663             singleSelect: false,
664             listeners: {
665                 rowselect: {
666                     fn: function(sm,rowindex,record) {
667                         this.displayTfIdfTermCirculationPeriodChart(this.searchParams, sm);
668                     }, scope: this
669                 }
670             }
671         }),
672        columns: [
673             {id:'term',header: "Term", width: 60, sortable: true, dataIndex: 'term'},
674             {header: "score", width: 60, sortable: true,  dataIndex: 'score',type: 'float'},
675             {header: "idf", width: 60, sortable: true, dataIndex: 'idf',type: 'float'},
676             {header: "#Test", width: 60, sortable: true,  dataIndex: 'index_freq',type: 'float'},
677             {header: "#Train", width: 60, sortable: true, dataIndex: 'source_freq',type: 'float'}
678         ],
679         stripeRows: true,
680     //    autoExpandColumn: 'term',
681     //    viewConfig: {forceFit: true},
682    //   autoWidth: true,
683         title:'Features',
684        // bbar: featureListPagingBar,
685         listeners: {
686              rowclick:function(grid,rowIndex,event){
687                var record = grid.getSelectionModel().getSelected();
688                var featureInstance = record.get('term');
689                var posTest = /\(.+?\)$/;
690                var type="spelling";
691                if (featureInstance.match(posTest)) {
692                   type="lemma";
693                }else{
694                   type="spelling";
695                }
696 
697              var params = {
698                 term: featureInstance,
699                 type: type
700               };
701 
702               Workbench.component.manager.notify(new Monk.event.chunk.FeatureSelected({
703                   label: 'Text feature selected: '+'"'+featureInstance+'"'
704                }), params);
705 
706 
707 
708              }
709         }
710     });
711 
712 
713 function doHeight() {
714     var h = Ext.getCmp('viewport').getBox().height;
715     var tabs = Ext.getCmp('tab-container');
716     tabs.setHeight(h);
717     var dunningsBody = Ext.get('dunningsBody');
718     if (dunningsBody) {
719         var height = dunningsBody.findParent('div.x-panel-body', 2, true).getHeight();
720         dunningsBody.setHeight(height);
721     }
722 }
723 
724 this.viewport = new Ext.Viewport({
725     id: 'viewport',
726     layout: 'fit',
727     renderTo: 'container',
728     items: {
729     	
730         items: {
731         	
732         	
733             id: 'tab-container',
734             xtype: 'tabpanel',
735             activeTab: 0,
736             autoWidth: true,
737             layoutOnTabChange: true,
738             border: false,
739             listeners: {
740                 tabchange: doHeight
741             },
742             items:[{
743                 xtype: 'panel',
744                 tbar:[{
745                     text: 'Summary',
746                     tooltip: {title:'Vocabulary Summary',text:'Display Vocabulary Summary'},
747                     iconCls: 'summary-icon',
748                     handler: function(button, event){
749                         if (this.chartUrl == '') {
750                             
751                         } else {
752                             if (this.chartWin == null) {
753                                 this.chartWin = new Ext.Window({
754                                     title: 'Summary (Venn Diagram)',
755                                     width: 420,
756                                     height: 240,
757                                     closeAction: 'hide',
758                                     modal: false,
759                                     plain: true,
760                                     border: false,
761                                     items: {
762                                         border: false,
763                                         html: '<div id="chart_img"></div>'
764                                     }
765                                 });
766                             }
767                             this.chartWin.on('show', function(){
768                                 Ext.get('chart_img').update('<img alt="Venn Diagram" src="'+this.chartUrl+'"/>');
769                             }, this, {single: true});
770                             this.chartWin.show(button.getEl());
771                         }
772                     },
773                     scope: this
774                 },
775                 {
776                 iconCls: 'exportBtn',
777                 text: 'Export Features',
778                 handler: function(button, event){this.exportItems(Ext.getCmp("features-grid"),
779                 ['term','count',
780                 'freq','type']
781                 )
782                 },
783                 scope:this
784                 }
785                 
786                 ],
787                 id: 'frequencyComparisonPanel',
788                 title: 'Frequency Comparison',
789                 layout: 'border',
790                 border: false,
791                 items: [{
792                     xtype: 'grid',
793                     id: 'features-grid',
794                     title: 'Features',
795                     region: 'west',
796                     minWidth: 245,
797                     width: 300,
798                     autoScroll: true,
799                     split: true,
800                     store: this.featureStore,
801                     loadMask: {msg: 'Loading features, please wait.'},
802                     columns: [
803                         {header: "Feature", width: 100, dataIndex: 'term', renderer: function(value, metadata, record){
804                             var type = record.data.type;
805                             if (type=="group1") metadata.attr = 'style = "background-color:#9C9CDB;"';
806                             else if(type=="group2") metadata.attr = 'style = "background-color:#8BA353;"';
807                             return value;
808                         }, sortable:true},
809                         {header: "Count", width: 50, dataIndex: 'count', sortable:true},
810                         {header: "Frequency", width: 70, dataIndex: 'freq', renderer: function(value, metadata, record){
811                             var num = parseFloat(value); 
812                             return num.toFixed(4);
813                         }, sortable:true},
814                         {header: "Type", width: 55, dataIndex: 'type', sortable:true, hidden:false}
815                     ],
816                     sm: new Ext.grid.RowSelectionModel({
817                         singleSelect: false,
818                         listeners: {
819                             rowselect: {
820                                 fn: function(sm,rowindex,record) {
821                                 	this.displayFeatureCirculationPeriodChart(this.searchParams, sm);
822                                 }, scope: this
823                             }
824                         }
825                     })
826                 },{
827                     title: 'Frequency Graph',
828                     region: 'center',
829                     split: true,
830                     //collapsible: true,
831                     minWidth: 400,
832                     width: 400,
833                     autoScroll: false,
834                    // bodyStyle: 'padding: 5px;',
835                     html: '<div id="feature_img" style=""></div>'
836                 }]
837             },{
838                 xtype: 'panel',
839                 id: 'dunningsPanel',
840                 title: 'Dunnings',
841                 layout: 'border',
842                 border: false,
843                 items: [
844                     dunningGrid,
845                     {
846                     title: 'Cloud',
847                     region: 'center',
848                     split: true,
849                     collapsible: true,
850                     minWidth: 400,
851                     width: 400,
852                     autoScroll: true,
853                     border: false,
854                     html: '<div id="dunningsBody"></div>',
855                     bodyStyle: 'padding: 5px;'
856                 }
857                 ]
858             },
859             	{
860                 xtype: 'panel',
861                 id: 'tfidfComparisonPanel',
862                 title: 'TF IDF Comparison',
863                 layout: 'border',
864                 border: false,
865                 items: [
866                     tfidfGrid,
867                 	{
868                     title: 'Frequency Graph',
869                     region: 'center',
870                     split: true,
871                     //collapsible: true,
872                     minWidth: 400,
873                     width: 400,
874                     autoScroll: true,
875                     bodyStyle: 'padding: 5px;',
876                     html: '<div id="tfidffeature_img"></div>'
877                 }]
878             }
879             
880             	
881             ]
882         }
883     }
884 });
885 
886 
887 
888 
889 
890      Ext.getCmp("tab-container").activate('tfidfComparisonPanel');
891      Ext.getCmp("tab-container").activate('dunningsPanel');
892      Ext.getCmp("tab-container").activate('frequencyComparisonPanel');
893 
894 } // end init function
895 
896 });
897