1 /**
  2  * A set of data calls which use the new MONK proxy
  3  * @name Monk Project Library 0.1 Alpha 1
  4  * @copyright Copyright(c) 2007, Monk Project, All rights reserved.
  5  * @license Code licensed under the <a href="http://www.gnu.org/licenses/lgpl.html">LGPL License</a>.
  6  *
  7  * @url <a href="http://monkproject.org">monkproject.org</a>
  8  */
  9 
 10 /**
 11  * @name Ext.ux
 12  * @namespace
 13  */
 14 Ext.namespace('Ext.ux');
 15 
 16 Ext.ux.Ajax = Ext.lib.Ajax; // tried to use Ext.extend here but it doesn't work
 17 Ext.ux.Ajax.handleReadyState = function(o, callback) {
 18     var oConn = this;
 19 
 20     if (callback && callback.timeout) {
 21         var confirmAbort = function() {
 22             Ext.Msg.confirm('MONK Workbench', 'The response from the server is taking longer than expected, would you like to continue waiting? (Choosing No might affect further usage of the workbench.)', function(btn, text){
 23                 if (btn == 'yes') {
 24                     oConn.timeout[o.tId] = window.setTimeout(function(){
 25                         confirmAbort();
 26                     }, callback.timeout);
 27                 } else {
 28                     oConn.abort(o, callback, true);
 29                 }
 30             }, this);
 31         }
 32         
 33         this.timeout[o.tId] = window.setTimeout(function(){
 34             confirmAbort();
 35         }, callback.timeout);
 36     }
 37 
 38     this.poll[o.tId] = window.setInterval(
 39             function() {
 40                 if (o.conn && o.conn.readyState == 4) {
 41                     window.clearInterval(oConn.poll[o.tId]);
 42                     delete oConn.poll[o.tId];
 43 
 44                     if (callback && callback.timeout) {
 45                         window.clearTimeout(oConn.timeout[o.tId]);
 46                         delete oConn.timeout[o.tId];
 47                     }
 48 
 49                     oConn.handleTransactionResponse(o, callback);
 50                 }
 51             }
 52             , this.pollInterval);
 53 }
 54 
 55 Ext.ux.ErrorHandlingConnection = Ext.extend(Ext.data.Connection, {
 56     timeout: 60000,
 57     request : function(o){
 58         if(this.fireEvent("beforerequest", this, o) !== false){
 59             var p = o.params;
 60 
 61             if(typeof p == "function"){
 62                 p = p.call(o.scope||window, o);
 63             }
 64             if(typeof p == "object"){
 65                 p = Ext.urlEncode(p);
 66             }
 67             if(this.extraParams){
 68                 var extras = Ext.urlEncode(this.extraParams);
 69                 p = p ? (p + '&' + extras) : extras;
 70             }
 71 
 72             var url = o.url || this.url;
 73             if(typeof url == 'function'){
 74                 url = url.call(o.scope||window, o);
 75             }
 76 
 77             if(o.form){
 78                 var form = Ext.getDom(o.form);
 79                 url = url || form.action;
 80 
 81                 var enctype = form.getAttribute("enctype");
 82                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
 83                     return this.doFormUpload(o, p, url);
 84                 }
 85                 var f = Ext.ux.Ajax.serializeForm(form);
 86                 p = p ? (p + '&' + f) : f;
 87             }
 88 
 89             var hs = o.headers;
 90             if(this.defaultHeaders){
 91                 hs = Ext.apply(hs || {}, this.defaultHeaders);
 92                 if(!o.headers){
 93                     o.headers = hs;
 94                 }
 95             }
 96 
 97             var cb = {
 98                 success: this.handleResponse,
 99                 failure: this.handleFailure,
100                 scope: this,
101                 argument: {options: o},
102                 timeout : this.timeout
103             };
104 
105             var method = o.method||this.method||(p ? "POST" : "GET");
106 
107             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
108                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
109             }
110 
111             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
112                 if(o.autoAbort){
113                     this.abort();
114                 }
115             }else if(this.autoAbort !== false){
116                 this.abort();
117             }
118             if((method == 'GET' && p) || o.xmlData || o.jsonData){
119                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
120                 p = '';
121             }
122             this.transId = Ext.ux.Ajax.request(method, url, cb, p, o);
123             return this.transId;
124         }else{
125             Ext.callback(o.callback, o.scope, [o, null, null]);
126             return null;
127         }
128     },
129     handleResponse : function(response){
130         this.transId = false;
131         var options = response.argument.options;
132         response.argument = options ? options.argument : null;
133 
134         var error = null;
135         if (response.getResponseHeader['Content-Type'].match(/text\/javascript/) != null) {
136             error = Ext.decode(response.responseText).error;
137         } else if (response.getResponseHeader['Content-Type'].match(/text\/html/) != null) {
138             var errorMatch = response.responseText.match(/<div id="error">(.*)<\/div>/);
139             if (errorMatch != null) error = errorMatch[1];
140         }else if(response.getResponseHeader['Content-Type'].match(/text\/plain/) != null){
141         	// this assumes there are no text/plain content type
142             error=null;
143         }else {
144             var errorTest = response.responseXML.getElementsByTagName('error');
145             if(errorTest==null){
146                 error="could not retrieve error message";	
147             }else{
148                 if (errorTest.length > 0) error = errorTest[0].firstChild.data;
149             }
150         }
151 
152         if (error != null) {
153             Workbench.console.info('An error has occurred: '+error);
154             Monk.component.messenger.alert('MONK Workbench', 'An server error has occurred:<br/>'+error);
155             Workbench.component.manager.notify(
156                 new Monk.event.ServerError({
157                     label: 'A server error has occurred: '+error
158                 }), {
159                     error: error,
160                     url: this.url // the url we stored in beforerequest
161                 }
162             );
163         } else {
164             Ext.callback(options.success, options.scope, [response, options]);
165         }
166 
167         this.fireEvent("requestcomplete", this, response, options);
168         Ext.callback(options.callback, options.scope, [options, true, response]);
169     }
170 });
171 
172 /**
173  * @name Monk.data.collection
174  * @namespace
175  */
176 Workbench.namespace("Monk.data.collection");
177 // listeners used by all connection objects
178 var listeners = {
179     'beforerequest': function(conn, options){
180         // store the url in the connection object if we need access to it later
181         this.url = options.url;
182 		var li = Ext.getCmp('loading-indicator');
183 		if (li) li.show();
184     },
185     'requestcomplete': function(conn, response, options){
186         var li = Ext.getCmp('loading-indicator');
187 		if (li) li.hide();
188     },
189     'requestexception': function(conn, response, options){
190         var li = Ext.getCmp('loading-indicator');
191 		if (li) li.hide();
192     }
193 };
194 
195 
196 Monk.data.collection._cachedMetaDataHierarchy = null;
197 
198 Monk.data.collection.works = new Ext.util.MixedCollection();
199 
200 /**
201  * Used locally to convert XML nodes from collection metadata into a JSON array.
202  * @param {String} collectionId The collection ID to prepend
203  * @param {Object} node The root of the tree
204  * @param {Array} children An array to store the converted nodes
205  */
206 function processChildNodes(collectionId, node, children) {
207     var childNodes = node.childNodes;
208     for (var i = 0; i < childNodes.length; i++) {
209         var node = childNodes[i];
210         if (node.nodeType == 1) {
211         if(node.nodeName=="groups"){
212         continue;
213         }
214             var id = node.getAttribute('id');
215 
216             var label = node.getElementsByTagName('label')[0].firstChild.data;
217             var chunkType = "work"; // hard coded for now
218             var numWorkPart = parseInt(node.getAttribute('numWorkPart'));
219 			var pubNode = node.getElementsByTagName('publication')[0]
220 			var startDate = pubNode.getAttribute('start');
221 			var endDate = pubNode.getAttribute('end');
222 			var authors = new Array();
223 			var authorsLabel = "";
224 			var authorNodes = node.getElementsByTagName('author');
225 			for (var j=0;j<authorNodes.length;j++) {
226 				var authorNode = authorNodes[j];
227 				var authorName = authorNode.firstChild.data;
228 				var authorBirthdate = authorNode.getAttribute("birthdate");
229                 if (authorBirthdate == 'null') authorBirthdate = '?';
230 				var authorEnddate = authorNode.getAttribute("enddate")
231                 if (authorEnddate == 'null') authorEnddate = '?';
232 				authorsLabel += (j>0 ? "; " : "") + authorName + " (" + authorBirthdate + "-" + authorEnddate + ")";
233 				authors.push({name : authorName, birthdate : authorBirthdate, enddate: authorEnddate})
234 			}
235             var jsonNode = {
236                 id: id,
237                 collectionId: collectionId,
238                 text: label,
239                 chunkType: chunkType,
240                 numWorkPart: numWorkPart,
241 				authorsLabel : authorsLabel,
242 				authors : authors,
243 				fullLabel : authorsLabel + ": " + label,
244 				publicationDates : startDate + (startDate==endDate ? "" : "-"+endDate),
245                 checked: false,
246 				leaf: false
247             };
248 			Monk.data.collection.works.add(id, jsonNode);
249             if (node.hasChildNodes() && node.getElementsByTagName('node').length > 0) {
250                 var subchildren = []
251                 processChildNodes(collectionId, node, subchildren);
252                 jsonNode.children = subchildren;
253                 jsonNode.leaf = false;
254             }
255 
256             children.push(jsonNode);
257         }
258     }
259 }
260 
261 /**
262  * Loads the collections metadata hierarchy from the server
263  */
264 Monk.data.collection.loadMetaDataHierarchy = function(progressWin) {
265     if (Monk.data.collection._cachedMetaDataHierarchy == null) {
266         Monk.data.collection._cachedMetaDataHierarchy = [];
267 
268         var conn = new Ext.ux.ErrorHandlingConnection();
269         conn.request({
270             url: Monk.data.PROXY_URL + 'get/CorpusManager.getCorpora',
271             method: 'GET',
272             success: function(responseObject) {
273 
274                 // first get the list of collections
275                 var collections = responseObject.responseXML.getElementsByTagName('collection');
276                 var collectionCounter = 0;
277 
278                 function getCollectionHierarchy(collection) {
279                     var collectionId = collection.getAttribute('id');
280                     var label = collection.getElementsByTagName('label')[0].firstChild.data;
281                     var firstChunkType = collection.getElementsByTagName('chunkType')[0].getAttribute('value');
282 
283                     var progressBar = (collectionCounter + 1) / collections.length;
284                     if (progressWin) {
285 						progressWin.updateProgress(progressBar, 'Loading ' + label);
286 					}
287 
288                     conn.request({
289                         url: Monk.data.PROXY_URL + 'get/CorpusManager.getWorkList',
290                         method: 'GET',
291                         params: {
292                             corpus: collectionId
293                         },
294                         success: function(responseObject) {
295                             var children = []
296                             var root = responseObject.responseXML.getElementsByTagName('works')[0];
297                             try {
298                                 processChildNodes(collectionId, root, children);
299 
300                                 var collectionNode = {
301                                     id: collectionId,
302                                     collectionId: collectionId,
303                                     text: label,
304                                     chunkType: 'collection',
305                                     leaf: false,
306                                     children: children
307                                 };
308                                 Monk.data.collection._cachedMetaDataHierarchy.push(collectionNode);
309 
310                                 // get metadata for each collection sequentially
311                                 collectionCounter++;
312                                 if (collectionCounter == collections.length) {
313                                     Workbench.component.manager.notify(
314                                         new Monk.event.CollectionsMetadataHierarchyLoaded({
315                                             label: 'Collections Metadata has been loaded.'
316                                         }), Monk.data.collection._cachedMetaDataHierarchy
317                                     );
318                                 } else {
319                                     getCollectionHierarchy(collections[collectionCounter]);
320                                 }
321                             } catch (e) {
322                                 var childrenString = '';
323                                 for (var i = 0, length = children.length; i < length; i++) {
324                                     childrenString += children[i].id + ', ';
325                                 }
326                                 childrenString = childrenString.substr(0, childrenString.length-2);
327                                 Ext.Msg.alert('Monk Workbench', "There was an error processing collections metadata."+
328                                 "Things may not work as expected so it's recommended to refresh your browser.<br/><br/>"+
329                                 "<b>Error</b>:<br/>"+e+'<br/><br/>'+
330                                 '<b>P.S.</b> The error occurred while processing this collection: '+collectionId+'<br/><br/>')
331                                 //'<b>P.P.S.</b> The following child nodes were already processed: '+childrenString+
332                                 //'<b>P.P.P.S.</b> The responseObject is: '+responseObject.responseText);
333 
334                                 //alert('Error: '+e);
335                             }
336                         },
337                         failure: function(response, options) {
338                             Ext.Msg.alert('Monk Workbench', "There was an error processing collections metadata. Things may not work as expected so it's recommended to refresh your browser.<br/><b>Error "+response.status+"</b>: "+response.statusText);
339                         }
340                     });
341                 }
342 
343                 // get chunk hierarchy for each collection
344                 getCollectionHierarchy(collections[collectionCounter]);
345 
346             },
347             failure: function(response, options) {
348                 Ext.Msg.alert('Monk Workbench', 'Error trying to get collections metadata.<br/><b>Error '+response.status+'</b>: '+response.statusText);
349             }
350         });
351     } else {
352         // if metadata has already been loaded, then return the cached copy
353         Workbench.component.manager.notify(
354             new Monk.event.CollectionsMetadataHierarchyLoaded({
355                 label: 'Collections Metadata has been loaded.'
356             }), Monk.data.collection._cachedMetaDataHierarchy
357         );
358     }
359 }
360 
361 /**
362  * Gets the collections metadata hierarchy as a literal
363  * @return {Object} The collections metadata hierarhcy as a literal
364  */
365 Monk.data.collection.getMetaDataHierarchy = function() {
366     if (Monk.data.collection._cachedMetaDataHierarchy == null) {
367         Monk.data.collection.loadMetaDataHierarchy();
368     } else {
369         // return a copy so the orginal doesn't get altered
370         return Workbench.clone(Monk.data.collection._cachedMetaDataHierarchy);
371     }
372 }
373 
374 // this should be access through Monk.data.collection.getWork()
375 Monk.data.collection.getWork = function(workId) {
376 	var parts = workId.split('-');
377 	return Monk.data.collection.works.get(parts[0]+"-"+parts[1]);
378 }
379 
380 Workbench.namespace("Monk.data.seasr");
381 
382 /**
383  * Run a SEASR analysis on the workset.
384  * @param {Object} params
385  * @config {String} projectId
386  * @config {String} worksetId
387  * @config {String} flowId
388  * @config {String} resultName
389  * @config {String} resultDescription
390  * @config {String} toolsetId
391  * @config {String} emailAddress
392  * @config {String} feature
393  * @config {String} numfeature
394  * @config {String} share
395  * @config {Boolean} async Set to true when user wants email notification
396  */
397 Monk.data.seasr.runAnalysis = function(params) {
398     var token = '';
399     var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz";
400     for (var i = 0; i < 32; i++) {
401         var rnum = Math.floor(Math.random() * chars.length);
402         token += chars.substring(rnum, rnum + 1);
403     }
404     Monk.component.dataManager.token = token; // set token now, so we don't have to wait for the server response
405     params.token = token;
406     var conn = new Ext.ux.ErrorHandlingConnection({listeners: listeners});
407     conn.request({
408         url: Monk.data.PROXY_URL + 'get/SchedulerManager.runAnalysis',
409         method: 'GET',
410         params: params,
411         success: function(responseObject) {
412             Workbench.component.manager.notify(new Monk.event.analysis.SeasrAnalysisRun({
413                 label: 'SEASR analysis running on workset: ' + params.worksetId
414             }), {token: token});
415         },
416         failure: function(response, options) {
417            Ext.Msg.alert('Data', 'Error trying to run analysis on workset.<br/><b>Error</b>: '+response.statusText);
418         }
419     });
420 }
421 
422 /**
423  * Returns the status of a particular SEASR analysis.
424  * @param {String} token The token for this analysis
425  */
426 Monk.data.seasr.getStatus = function(token) {
427     var conn = new Ext.ux.ErrorHandlingConnection({listeners: listeners});
428     conn.request({
429         url: Monk.data.PROXY_URL + 'get/SchedulerManager.status',
430         method: 'GET',
431         params: {token: token},
432         success: function(responseObject) {
433             var status = responseObject.responseXML.getElementsByTagName('FlowStatusMessage')[0].getAttribute('type');
434             var instanceId = responseObject.responseXML.getElementsByTagName('flowInstanceId')[0].firstChild.data;
435             var queuePos = responseObject.responseXML.getElementsByTagName('queueSize')[0].firstChild.data;
436             Workbench.component.manager.notify(new Monk.event.analysis.GotJobStatus({
437                 label: 'Status for token ' + token + ' received: '+status
438             }), {status: status, instanceId: instanceId, queuePos: queuePos});
439         },
440         failure: function(response, options) {
441            Ext.Msg.alert('Data', 'Error trying to get flow info.<br/><b>Error</b>: '+response.statusText);
442         }
443     });
444 }
445 
446 /**
447  * Returns the status of a particular SEASR analysis.
448  * @param {String} projectId The ID of the project
449  * @param {String} [status] Provide a status and only jobs with this status will be returned (possible values: FINISHED, ABORTED, SUBMITTED, RUNNING)
450  */
451 Monk.data.seasr.getJobList = function(projectId, status) {
452     var conn = new Ext.ux.ErrorHandlingConnection({listeners: listeners});
453     var params = {projectId: projectId};
454     if (status != null) params.status = status;
455     conn.request({
456         url: Monk.data.PROXY_URL + 'get/SchedulerManager.getJobListAndStatus',
457         method: 'GET',
458         params: params,
459         success: function(responseObject) {
460             var jobs = responseObject.responseXML.getElementsByTagName('job');
461             Workbench.component.manager.notify(new Monk.event.analysis.GotJobList({
462                 label: 'Job list for project' + projectId + ' received.'
463             }), {projectId: projectId, status: status, jobs: jobs});
464         },
465         failure: function(response, options) {
466            Ext.Msg.alert('Data', 'Error trying to get job list.<br/><b>Error</b>: '+response.statusText);
467         }
468     });
469 }
470 
471 /**
472  * Abort an analysis
473  * @param {String} token The token for this analysis
474  */
475 Monk.data.seasr.abort = function(token) {
476     var conn = new Ext.ux.ErrorHandlingConnection({listeners: listeners});
477     conn.request({
478         url: Monk.data.PROXY_URL + 'get/SchedulerManager.abort',
479         method: 'GET',
480         params: {token: token},
481         success: function(responseObject) {
482             var status = responseObject.responseXML.getElementsByTagName('status')[0].firstChild.data;
483             Workbench.component.manager.notify(new Monk.event.analysis.Aborted({
484                 label: 'Analysis aborted for token ' + token + ' Click on Show Log to display the log message'
485             }), {status: status});
486         },
487         failure: function(response, options) {
488            Ext.Msg.alert('Data', 'Error trying to abort analysis.<br/><b>Error</b>: '+response.statusText);
489         }
490     });
491 }
492 
493 /**
494  * Get the results/status of a SEASR analysis.
495  * @param {String} instanceId The ID for the analysis (returned by Monk.data.seasr.doSetup)
496  * @param {Boolean} runningAnalysis Set to true if an analysis is being run (as opposed to loading previous results)
497  */
498 Monk.data.seasr.getAnalysisResults = function(instanceId, runningAnalysis) {
499     var conn = new Ext.ux.ErrorHandlingConnection({listeners: listeners});
500     conn.request({
501         url: Monk.data.PROXY_URL + 'get/SeasrManager.getPrediction',
502         method: 'GET',
503         params: {
504             instanceId : instanceId
505         },
506         success: function(responseObject) {
507             var results = responseObject.responseXML.getElementsByTagName('results')[0];
508             if (results.getAttribute('num') != null) {
509                 Workbench.component.manager.notify(
510                     new Monk.event.analysis.GotSeasrAnalysisResults({
511                         label: 'Got the prediction for instance: '+instanceId
512                     }),
513                 {instanceId: instanceId, results: results, runningAnalysis: runningAnalysis});
514             }
515         },
516         failure: function(response, options) {
517             Ext.Msg.alert('Data', 'Error trying to get prediction for workset.<br/><b>Error</b>: '+response.statusText);
518         }
519     });
520 }
521 
522 /**
523  * Get the results/status of a SEASR analysis.
524  * @param {String} instanceId The ID for the analysis (returned by Monk.data.seasr.doSetup)
525  */
526 Monk.data.seasr.getConfigParameters = function(instanceId) {
527     var conn = new Ext.ux.ErrorHandlingConnection({listeners: listeners});
528     conn.request({
529         url: Monk.data.PROXY_URL + 'get/SeasrManager.getConfigParameters',
530         method: 'GET',
531         params: {
532             instanceId : instanceId
533         },
534         success: function(responseObject) {
535             var data = responseObject.responseXML.getElementsByTagName('result')[0];
536             Workbench.component.manager.notify(new Monk.event.analysis.GotConfigParameters({
537                 label: 'Got config parameters for: ' + instanceId
538             }), {xml: data, instanceId: instanceId});
539         },
540         failure: function(response, options) {
541             Ext.Msg.alert('Data', 'Error trying to get config parameters for the analysis call.<br/><b>Error</b>: '+response.statusText);
542         }
543     });
544 }
545 
546 /**
547  * Retrieve the previously saved results of a SEASR analysis.
548  * @param {Integer} projectId The ID for the project
549  * @param {Boolean} [getValues] True to get the actual result values, defaults to false
550  */
551 Monk.data.seasr.getSavedResultsForProject = function(projectId, getValues) {
552     var conn = new Ext.ux.ErrorHandlingConnection({listeners: listeners});
553     var getValues = getValues == null ? false : getValues;
554     conn.request({
555         url: Monk.data.PROXY_URL + 'get/SeasrManager.getSavedResultsForProject',
556         method: 'GET',
557         params: {
558             projectId: projectId,
559             value: getValues
560         },
561         success: function(responseObject) {
562             var results = responseObject.responseXML.getElementsByTagName('results')[0];
563             Workbench.component.manager.notify(new Monk.event.analysis.GotSavedResultsForProject({
564                 label: 'SEASR analysis results retrieved for: ' + projectId
565             }), {projectId: projectId, results: results});
566         },
567         failure: function(response, options) {
568             Ext.Msg.alert('Data', 'Error trying to get saved results.<br/><b>Error</b>: '+response.statusText);
569         }
570     });
571 }
572 
573 /**
574  * Retrieve all previous results of a SEASR analysis.
575  * @param {Integer} projectId The ID for the project
576  * @param {String} [status] Filter the list by a particular status.
577  * Possible values: null (all results), 'SUBMITTED', 'RUNNING', 'FINISHED', 'ABORTED'
578  */
579 Monk.data.seasr.getAllResultsForProject = function(projectId, status) {
580     var conn = new Ext.ux.ErrorHandlingConnection({listeners: listeners});
581     conn.request({
582         url: Monk.data.PROXY_URL + 'get/SchedulerManager.getJobListAndStatus',
583         method: 'GET',
584         params: {
585             projectId: projectId,
586             status: status
587         },
588         success: function(responseObject) {
589             var results = responseObject.responseXML.getElementsByTagName('jobs')[0];
590             Workbench.component.manager.notify(new Monk.event.analysis.GotAllResultsForProject({
591                 label: 'All SEASR analysis results retrieved for: ' + projectId
592             }), {projectId: projectId, results: results});
593         },
594         failure: function(response, options) {
595             Ext.Msg.alert('Data', 'Error trying to get all results.<br/><b>Error</b>: '+response.statusText);
596         }
597     });
598 }
599 
600 /**
601  * Delete the result of a SEASR analysis.
602  * @param {String} instanceId The ID for the analysis (returned by Monk.data.seasr.doSetup)
603  */
604 Monk.data.seasr.deleteResult = function(instanceId) {
605     var conn = new Ext.ux.ErrorHandlingConnection({listeners: listeners});
606     conn.request({
607         url: Monk.data.PROXY_URL + 'get/SeasrManager.removeResult',
608         method: 'GET',
609         params: {
610             instanceId : instanceId
611         },
612         success: function(responseObject) {
613             var success = responseObject.responseXML.getElementsByTagName('success')[0];
614             if (success != null) {
615                 Workbench.component.manager.notify(
616                     new Monk.event.analysis.AnalysisResultDeleted({
617                         label: 'Delete the analysis result for: '+instanceId
618                     }),
619                 {instanceId: instanceId});
620             } else {
621                 // error
622             }
623         },
624         failure: function(response, options) {
625             Ext.Msg.alert('Data', 'Error trying to delete the analysis result.<br/><b>Error</b>: '+response.statusText);
626         }
627     });
628 }
629 
630 Workbench.namespace("Monk.data.workset");
631 
632 /**
633  * Create a workset
634  * @param {Object} workset The workset object to create
635  * @config {String} projectId
636  * @config {String} collectionId
637  * @config {String} chunkType
638  * @config {String} [workList]
639  * @config {String} [workListRating]
640  * @config {String} [trainingSet]
641  * @config {String} [trainingSetRating]
642  * @config {String} worksetName
643  * @config {String} itinerary
644  * @config {String} feature
645  */
646 Monk.data.workset.createWorkset = function(workset) {
647 	var conn = new Ext.ux.ErrorHandlingConnection({listeners: listeners});
648 	conn.request({
649 	    url: Monk.data.PROXY_URL + 'get/ProjectManager.createNewWorkset',
650 	    method: 'POST',
651 	    params: {
652             projectId : workset.projectId,
653             collectionId : 'cha',
654             worksetName : workset.worksetName,
655             //chunkType : workset.chunkType,
656             workList : workset.workList,
657             workListRating : workset.workListRating,
658             trainingList : workset.trainingList,
659             trainingListRatings : workset.trainingListRating,
660             itinerary : workset.itinerary,
661             feature : workset.feature
662 		},
663 	    success: function(responseObject) {
664             var worksetId = responseObject.responseXML.getElementsByTagName('workset')[0].getAttribute('id');
665 
666 	        Workbench.component.manager.notify(
667 	            new Monk.event.workset.WorksetCreated({
668 					label: 'Workset created: '+workset.worksetName
669 				}),
670 	            {worksetId: worksetId, projectId: workset.projectId, response:responseObject});
671 	    },
672 	    failure: function(response, options) {
673 	        Ext.Msg.alert('Data', 'Workset '+workset.worksetName+' could not be created.<br/><b>Error</b>: '+response.statusText);
674 	    }
675 	});
676 }
677 
678 /**
679  * Save the workset
680  * @param {Object} workset The workset object to save
681  * @config {String} worksetId
682  * @config {String} projectId
683  * @config {String} collectionId
684  * @config {String} worksetName
685  * @config {String} chunkType
686  * @config {String} [workList]
687  * @config {String} [workListRating]
688  * @config {String} [trainingSet]
689  * @config {String} [trainingSetRating]
690  * @config {String} itinerary
691  * @config {String} feature
692  */
693 Monk.data.workset.saveWorkset = function(workset) {
694     var conn = new Ext.ux.ErrorHandlingConnection({listeners: listeners});
695     conn.request({
696         url: Monk.data.PROXY_URL + 'get/ProjectManager.saveWorkset',
697         method: 'POST',
698         timeout: 600000,
699         params: {
700 			worksetId : workset.worksetId,
701             projectId : workset.projectId,
702             collectionId : 'cha',
703 			worksetName : workset.worksetName,
704             //chunkType : workset.chunkType,
705             workList : workset.workList,
706             workListRating : workset.workListRating,
707             trainingList : workset.trainingList,
708             trainingListRatings : workset.trainingListRating,
709             itinerary : workset.itinerary,
710             feature : workset.feature
711         },
712         success: function(responseObject) {
713             Workbench.component.manager.notify(
714                 new Monk.event.workset.WorksetSaved({
715 					label: 'Workset saved: '+workset.worksetId
716 				}),
717                 {worksetId: workset.worksetId, response:responseObject});
718         },
719         failure: function(response, options) {
720             Ext.Msg.alert('Data', 'Workset '+workset.worksetId+' could not be saved.<br/><b>Error</b>: '+response.statusText);
721         }
722     });
723 }
724 
725 /**
726  * Delete a workset
727  * @params {Integer} projectId The ID of the project that contains the workset
728  * @params {Integer} worksetId The ID of the workset to delete
729  */
730 Monk.data.workset.deleteWorkset = function(projectId, worksetId) {
731     var conn = new Ext.ux.ErrorHandlingConnection({listeners: listeners});
732     conn.request({
733         url: Monk.data.PROXY_URL + 'get/ProjectManager.deleteWorkset',
734         method: 'GET',
735         params: {worksetId : worksetId, projectId: projectId},
736         success: function(responseObject) {
737             Workbench.component.manager.notify(
738                 new Monk.event.workset.WorksetDeleted({
739 					label: 'Workset deleted: '+worksetId
740 				}),
741                 {worksetId: worksetId, response: responseObject});
742         },
743         failure: function(response, options) {
744             Ext.Msg.alert('Data', 'Workset '+worksetId+' could not be deleted.<br/><b>Error</b>: '+response.statusText);
745         }
746     });
747 }
748 
749 /**
750  * Gets the labels for the workparts in a workset
751  * @param {Integer} worksetId The ID of the workset
752  * @param {String} type Either 'worklist' or 'traininglist' to get the labels for those workparts
753  */
754 Monk.data.workset.getWorkpartLabels = function(worksetId, type) {
755     var conn = new Ext.ux.ErrorHandlingConnection({listeners: listeners});
756     conn.on('requestexception', function(con, resp, opt, error) {
757     });
758     conn.request({
759         url: Monk.data.PROXY_URL + 'get/WorkSetsManager.getWorkPartLabels',
760         method: 'GET',
761         params: {worksetId : worksetId, type: type},
762         success: function(responseObject) {
763             Workbench.component.manager.notify(
764                 new Monk.event.workset.GotWorkpartLabels({
765                     label: 'Got '+type+' labels for : '+worksetId
766                 }),
767                 {worksetId: worksetId, type: type, response: responseObject});
768         },
769         failure: function(response, options) {
770             Ext.Msg.alert('Data', 'Could not get '+type+' labels for: '+worksetId+'.<br/><b>Error</b>: '+response.statusText);
771         }
772     });
773 }
774 
775 Monk.data.workset.retrieveDunningsLogLikelyHood = function(data) {
776     var conn = new Ext.ux.ErrorHandlingConnection({listeners: listeners});
777     conn.request({
778         url: Monk.data.PROXY_URL + 'get/AnalyticsManager.getDunningsLogLikelyHood',
779         method: 'GET',
780         params: {referenceList : data.worksets[0].workList, documentList: data.worksets[1].workList, feature : 'lemma', cutoff : 20},
781         success: function(responseObject) {
782             Workbench.component.manager.notify(
783                 new Monk.event.workset.DunningsLogLikelyHoodRetrieved({
784 					label: 'Dunnings Log Likelihood loaded.'
785 				}),
786                 {response: responseObject});
787         },
788         failure: function(response, options) {
789             Ext.Msg.alert('Data', 'Unable to retrieve Dunnings log likelihood values..<br/><b>Error</b>: '+response.statusText);
790         }
791     });
792 }
793 
794 Workbench.namespace('Monk.data.project');
795 
796 /**
797  * Create a project
798  * @param {String} name The project's name
799  * @param {String} comment A comment about the project
800  */
801 Monk.data.project.createProject = function(name, comment) {
802     var conn = new Ext.ux.ErrorHandlingConnection({listeners: listeners});
803     conn.request({
804         url: Monk.data.PROXY_URL + 'get/ProjectManager.createProject',
805         method: 'GET',
806 		params: {
807 			name : name,
808 			comment : comment
809 		},
810         success: function(responseObject) {
811             Workbench.component.manager.notify(
812                 new Monk.event.project.ProjectCreated({
813 					label: 'Project created: '+name
814 				}),
815                 responseObject);
816         },
817         failure: function(response, options) {
818             Ext.Msg.alert('Data', 'Project '+name+' could not be created.<br/><b>Error</b>: '+response.statusText);
819         }
820     });
821 }
822 
823 /**
824  * Delete a project
825  * @param {Integer} projectId The project's ID
826  */
827 Monk.data.project.deleteProject = function(projectId) {
828     var conn = new Ext.ux.ErrorHandlingConnection({listeners: listeners});
829     conn.request({
830         url: Monk.data.PROXY_URL + 'get/ProjectManager.deleteProject',
831         method: 'GET',
832         params: {projectId : projectId},
833         success: function(responseObject) {
834             Workbench.component.manager.notify(
835                 new Monk.event.project.ProjectDeleted({
836 					label: 'Project deleted: '+projectId
837 				}),
838                 responseObject);
839         },
840         failure: function(response, options) {
841             Ext.Msg.alert('Data', 'Error deleting project: '+projectId+'.<br/><b>Error</b>: '+response.statusText);
842         }
843     });
844 }
845 
846 /**
847  * Gets the projects for a user.  User ID is set in a cookie after login.
848  */
849 Monk.data.project.getProjects = function() {
850     var conn = new Ext.ux.ErrorHandlingConnection({listeners: listeners});
851     conn.request({
852         url: Monk.data.PROXY_URL + 'get/UserManager.getProjects',
853         method: 'GET',
854         success: function(responseObject) {
855             Workbench.component.manager.notify(
856                 new Monk.event.project.ProjectsReceived({
857 					label: 'Projects received'
858 				}),
859                 responseObject);
860         },
861         failure: function(response, options) {
862             Ext.Msg.alert('Data', 'Error trying to get projects.<br/><b>Error</b>: '+response.statusText);
863         }
864     });
865 }
866 
867 /**
868  * Get info about a specfic project.
869  * @param {Integer} projectId The ID of the project
870  */
871 Monk.data.project.getProjectInfo = function(projectId) {
872     var conn = new Ext.ux.ErrorHandlingConnection({listeners: listeners});
873     conn.request({
874         url: Monk.data.PROXY_URL + 'get/ProjectManager.getProjectInfo',
875         method: 'GET',
876 		params: {projectId : projectId},
877         success: function(responseObject) {
878             Workbench.component.manager.notify(
879                 new Monk.event.project.ProjectInfoReceived({
880 					label: 'Project info received: '+projectId
881 				}),
882                 responseObject);
883         },
884         failure: function(response, options) {
885             Ext.Msg.alert('Data','Error trying to get project info.<br/><b>Error</b>: '+response.statusText);
886         }
887     });
888 }
889 
890 /**
891  * Get the workset for the current project.  Needs to be used in the context of
892  * the component that calls it (using call()).
893  * @param {Integer} projectId The ID of the project
894  */
895 Monk.data.project.getWorksets = function(projectId) {
896 	var component = this;
897     var conn = new Ext.ux.ErrorHandlingConnection({listeners: listeners});
898     conn.request({
899         url: Monk.data.PROXY_URL + 'get/ProjectManager.getWorksets',
900         method: 'GET',
901 		params: {projectId : projectId},
902         success: function(responseObject) {
903             Workbench.component.manager.notify(
904                 new Monk.event.project.WorksetsReceived({
905 					label: 'Worksets received for project: '+projectId
906 				}),
907                 {projectId: projectId, worksets: responseObject});
908         },
909         failure: function(response, options) {
910             Ext.Msg.alert('Data', 'Error trying to get worksets.<br/><b>Error</b>: '+response.statusText);
911         },
912 		scope: component
913     });
914 }
915 
916 Workbench.namespace('Monk.data.chunk');
917 
918 /**
919  * Retrieve the info (child workparts) for a particular work.
920  * @param {String} corpus The ID of a collection
921  * @param {String} id The ID of a work or workpart
922  */
923 Monk.data.chunk.getWorkInfo = function(corpus, id) {
924     var conn = new Ext.ux.ErrorHandlingConnection({listeners: listeners});
925     conn.request({
926         url: Monk.data.PROXY_URL + 'get/CorpusManager.getWorkInfo',
927         method: 'GET',
928         params: {
929             corpus: corpus,
930             id: id
931         },
932         success: function(responseObject) {
933             Workbench.component.manager.notify(
934                 new Monk.event.chunk.WorkPartsRetrieved({
935                     label: 'Retrieved work parts for: '+id,
936                     uri: responseObject.responseXML.documentURI
937                 }),
938                 responseObject);
939         },
940         failure: function(response, options) {
941             Ext.Msg.alert('Data', 'Unable to retrieve work parts at this time.<br/><b>Error</b>: '+response.statusText);
942         }
943     });
944 }
945 
946 /**
947  * Retrieve the rendered table of content of a chunk into the specified chunk object.
948  * The chunk is expected to have the following members: id, text, chunkType.
949  * @param {Object} chunk The chunk object (with members)
950  * @config {String} id The chunk id
951  * @config {String} corpus The collection id
952  * @config {String} text The chunk label
953  * @config {String} chunkType The type of the chunk
954  * @throws {Ext.Msg} MessageBox error
955  */
956 Monk.data.chunk.retrieveChunkTOC = function(chunk){
957 	if (chunk.id=="collections-root") {
958 		var chunkHierarchy = Monk.data.collection.getMetaDataHierarchy();
959 		chunk.html = "<h2>"+chunk.text+"</h2>"+"<ul><li>number of collections: "+chunkHierarchy.length+"</li></ul>"
960 		Workbench.component.manager.notify(new Monk.event.chunk.ChunkContentsRetrieved({
961 			label: 'Text toc retrieved: "' + chunk.text + '"'
962 		//uri: responseObject.responseXML.documentURI
963 		}), chunk);
964 	}
965 	else if (chunk.chunkType == "collection") {
966 		var chunkHierarchy = Monk.data.collection.getMetaDataHierarchy();
967 		for (var i=0;i<chunkHierarchy.length;i++) {
968 			if (chunkHierarchy[i].id==chunk.id) {
969 				chunk.html = "<h2>"+chunkHierarchy[i].text+"</h2>"+"<ul><li>number of works: "+chunkHierarchy[i].children.length+"</li></ul>"
970 				Workbench.component.manager.notify(new Monk.event.chunk.ChunkContentsRetrieved({
971 					label: 'Text toc retrieved: "' + chunk.text + '"'
972 				//uri: responseObject.responseXML.documentURI
973 				}), chunk);
974 				break;
975 			}
976 		}
977 	}
978 	else {
979 		var conn = new Ext.ux.ErrorHandlingConnection({listeners: listeners});
980 		conn.request({
981 			url: Monk.data.PROXY_URL + 'get/CorpusManager.getDocumentTOC',
982 			method: 'GET',
983 			params: {
984 				id: chunk.id
985 			},
986 			success: function(responseObject){
987 				chunk.html = responseObject.responseText;
988 				Workbench.component.manager.notify(new Monk.event.chunk.ChunkTOCRetrieved({
989 					label: 'Text toc retrieved: "' + chunk.text + '"'
990 				}), chunk);
991 			},
992 			failure: function(response, options){
993 				Ext.Msg.alert('Data', 'Unable to get work TOC.<br/><b>Error</b>: '+chunk.id+" response: " + response.statusText);
994 			}
995 		});
996 	}
997 
998 }
999 
1000 
1001 Monk.data.chunk.retrieveChunkHeader = function(chunk){
1002     if (chunk.id=="collections-root") {
1003         var chunkHierarchy = Monk.data.collection.getMetaDataHierarchy();
1004         chunk.html = "<h2>"+chunk.text+"</h2>"+"<ul><li>number of collections: "+chunkHierarchy.length+"</li></ul>"
1005         Workbench.component.manager.notify(new Monk.event.chunk.ChunkContentsRetrieved({
1006             label: 'Text header retrieved: "' + chunk.text + '"'
1007         }), chunk);
1008     }else if (chunk.chunkType == "collection") {
1009         var chunkHierarchy = Monk.data.collection.getMetaDataHierarchy();
1010         for (var i=0;i<chunkHierarchy.length;i++) {
1011             if (chunkHierarchy[i].id==chunk.id) {
1012                 chunk.html = "<h2>"+chunkHierarchy[i].text+"</h2>"+"<ul><li>number of works: "+chunkHierarchy[i].children.length+"</li></ul>"
1013                 Workbench.component.manager.notify(new Monk.event.chunk.ChunkContentsRetrieved({
1014                     label: 'Text header retrieved: "' + chunk.text + '"'
1015                 //uri: responseObject.responseXML.documentURI
1016                 }), chunk);
1017                 break;
1018             }
1019         }
1020     }
1021     else {
1022         var conn = new Ext.ux.ErrorHandlingConnection({listeners: listeners});
1023         conn.request({
1024             url: Monk.data.PROXY_URL + 'get/CorpusManager.getDocumentHeader',
1025             method: 'GET',
1026             params: {
1027                 id: chunk.id
1028             },
1029             success: function(responseObject){
1030                 chunk.html = responseObject.responseText;
1031                 Workbench.component.manager.notify(new Monk.event.chunk.ChunkHeaderRetrieved({
1032                     label: 'Text header retrieved: "' + chunk.text + '"'
1033                 }), chunk);
1034             },
1035             failure: function(response, options){
1036                 Ext.Msg.alert('Data', 'Unable to get work Header.<br/><b>Error</b>: ' +chunk.text+ " response: "+ response.statusText);
1037             }
1038         });
1039     }
1040 
1041 }
1042 
1043 
1044 
1045 
1046 
1047 /**
1048  * Retrieve the rendered bibliographic information of a chunk into the specified chunk object.
1049  * The chunk is expected to have the following members: id, text, chunkType.
1050  * @param {Object} chunk The chunk object (with members)
1051  * @config {String} id The chunk id
1052  * @config {String} corpus The collection id
1053  * @config {String} text The chunk label
1054  * @config {String} chunkType The type of the chunk
1055  * @throws {Ext.Msg} MessageBox error
1056  */
1057 Monk.data.chunk.retrieveChunkBib = function(chunk){
1058 	if (chunk.id=="collections-root") {
1059 		var chunkHierarchy = Monk.data.collection.getMetaDataHierarchy();
1060 		chunk.html = "<h2>"+chunk.text+"</h2>"+"<ul><li>number of collections: "+chunkHierarchy.length+"</li></ul>"
1061 		Workbench.component.manager.notify(new Monk.event.chunk.ChunkContentsRetrieved({
1062 			label: 'Text chunk retrieved: "' + chunk.text + '"'
1063 		//uri: responseObject.responseXML.documentURI
1064 		}), chunk);
1065 	}
1066 	else if (chunk.chunkType == "collection") {
1067 		var chunkHierarchy = Monk.data.collection.getMetaDataHierarchy();
1068 		for (var i=0;i<chunkHierarchy.length;i++) {
1069 			if (chunkHierarchy[i].id==chunk.id) {
1070 				chunk.html = "<h2>"+chunkHierarchy[i].text+"</h2>"+"<ul><li>number of works: "+chunkHierarchy[i].children.length+"</li></ul>"
1071 				Workbench.component.manager.notify(new Monk.event.chunk.ChunkContentsRetrieved({
1072 					label: 'Text chunk retrieved: "' + chunk.text + '"'
1073 				//uri: responseObject.responseXML.documentURI
1074 				}), chunk);
1075 				break;
1076 			}
1077 		}
1078 	}
1079 	else {
1080 		var conn = new Ext.ux.ErrorHandlingConnection({listeners: listeners});
1081 		conn.request({
1082 			url: Monk.data.PROXY_URL + 'get/CorpusManager.getDocumentMetadata',
1083 			method: 'GET',
1084 			params: {
1085 				id: chunk.id
1086 			},
1087 			success: function(responseObject){
1088 				chunk.html = responseObject.responseText;
1089 				Workbench.component.manager.notify(new Monk.event.chunk.ChunkBibRetrieved({
1090 					label: 'chunk bib retrieved: "' + chunk.text + '"'
1091 				}), chunk);
1092 			},
1093 			failure: function(response, options){
1094 				Ext.Msg.alert('Data', 'Unable to get work BIB.<br/><b>Error</b>: '+chunk.text+' response: ' + response.statusText);
1095 			}
1096 		});
1097 	}
1098 
1099 }
1100 
1101 
1102 /**
1103  * Retrieve the rendered contents of a chunk into the specified chunk object.
1104  * The chunk is expected to have the following members: id, text, chunkType.
1105  * @param {Object} chunk The chunk object (with members)
1106  * @config {String} id The chunk id
1107  * @config {String} corpus The collection id
1108  * @config {String} text The chunk label
1109  * @config {String} chunkType The type of the chunk
1110  * @throws {Ext.Msg} MessageBox error
1111  */
1112 Monk.data.chunk.retrieveChunkContents = function(chunk){
1113 	if (chunk.id=="collections-root") {
1114 		var chunkHierarchy = Monk.data.collection.getMetaDataHierarchy();
1115 		chunk.html = "<h2>"+chunk.text+"</h2>"+"<ul><li>number of collections: "+chunkHierarchy.length+"</li></ul>"
1116 		Workbench.component.manager.notify(new Monk.event.chunk.ChunkContentsRetrieved({
1117 			label: 'Text chunk retrieved: "' + chunk.text + '"'
1118 		//uri: responseObject.responseXML.documentURI
1119 		}), chunk);
1120 	}
1121 	else if (chunk.chunkType == "collection") {
1122 		var chunkHierarchy = Monk.data.collection.getMetaDataHierarchy();
1123 		for (var i=0;i<chunkHierarchy.length;i++) {
1124 			if (chunkHierarchy[i].id==chunk.id) {
1125 				chunk.html = "<h2>"+chunkHierarchy[i].text+"</h2>"+"<ul><li>number of works: "+chunkHierarchy[i].children.length+"</li></ul>"
1126 				Workbench.component.manager.notify(new Monk.event.chunk.ChunkContentsRetrieved({
1127 					label: 'Text chunk retrieved: "' + chunk.text + '"'
1128 				//uri: responseObject.responseXML.documentURI
1129 				}), chunk);
1130 				break;
1131 			}
1132 		}
1133 	}
1134 	else {
1135 		var conn = new Ext.ux.ErrorHandlingConnection({listeners: listeners});
1136 		conn.request({
1137 			url: Monk.data.PROXY_URL + 'get/CorpusManager.getWork',
1138 			method: 'GET',
1139 			params: {
1140 				corpus: chunk.corpus,
1141 				id: chunk.id
1142 			},
1143 			success: function(responseObject){
1144 				chunk.html = responseObject.responseText;
1145 				Workbench.component.manager.notify(new Monk.event.chunk.ChunkContentsRetrieved({
1146 					label: 'Text chunk retrieved: "' + chunk.text + '"'
1147 				//uri: responseObject.responseXML.documentURI
1148 				}), chunk);
1149 			},
1150 			failure: function(response, options){
1151 				Ext.Msg.alert('Data', 'Unable to get work contents.<br/><b>Error</b>: '+chunk.text+' response: ' + response.statusText);
1152 			}
1153 		});
1154 	}
1155 
1156 }
1157 
1158 /**
1159  * Retrieve a list of chunks from a collection which contain specific features.
1160  * Possible features:
1161  * corpusCriterion - the ID of the collection to search
1162  * pubDateStart / pubDateEnd - publication dates
1163  * posCriterion -  a part of speech criterion
1164  * lemmaPatternCriterion - a pattern to search for
1165  * See: http://scribe.at.northwestern.edu:8090/monk/search-core.html
1166  * @param {Object} features An object with feature specifications
1167  */
1168 Monk.data.chunk.getChunksContainingFeature = function(features) {
1169     var conn = new Ext.ux.ErrorHandlingConnection({listeners: listeners});
1170     conn.request({
1171         url: Monk.data.PROXY_URL + 'get/CorpusManager.getWorksWithFeature',
1172         method: 'GET',
1173         params: features,
1174         success: function(responseObject) {
1175             Workbench.component.manager.notify(
1176                 new Monk.event.chunk.ChunksContainingFeatureRetrieved({
1177                     label: 'Retrieved chunks containing feature(s)'
1178                 }),
1179                 responseObject);
1180         },
1181         failure: function(response, options) {
1182             Ext.Msg.alert('Data', 'Error getting works containing feature(s).<br/><b>Error</b>: '+response.statusText);
1183         }
1184     });
1185 }
1186 
1187 /**
1188  * Retrieve a list of chunks from a workset which contain specific features.
1189  * Possible features:
1190  * String lemmaPatternCriterion; *
1191  * String spellingPatternCriterion; *
1192  * int pubDateStart=-1; *
1193  * int pubDateEnd=-1; *
1194  * String posCriterion;  *
1195  * String sort;
1196  * int start=-1; *
1197  * int end=-1; *
1198  * String worksetId;
1199  * String searchType; // search in "TEST" "TRAINING" "BOTH"
1200  * See: http://scribe.at.northwestern.edu:8090/monk/search-core.html
1201  * @param {Object} params An object with parameter specifications
1202  */
1203 Monk.data.chunk.getChunksContainingFeatureWithinWorkset = function(params) {
1204     var conn = new Ext.ux.ErrorHandlingConnection({listeners: listeners});
1205     conn.request({
1206         url: Monk.data.PROXY_URL + 'get/WorkSetsManager.getWorksWithFeature',
1207         method: 'GET',
1208         params: params,
1209         success: function(responseObject, params) {
1210             var feature = params.params.lemmaPatternCriterion || params.params.spellingPatternCriterion;
1211             Workbench.component.manager.notify(
1212                 new Monk.event.chunk.ChunksContainingFeatureWithinWorksetRetrieved({
1213                     label: 'Retrieved chunks containing feature(s) within workset: '+params.worksetId
1214                 }),
1215                 {feature: feature, response: responseObject});
1216         },
1217         failure: function(response, options) {
1218             Ext.Msg.alert('Data', 'Error getting works containing feature(s) within workset.<br/><b>Error</b>: '+response.statusText);
1219         }
1220     });
1221 }
1222 
1223 /**
1224  * Retrieve a frequencies list for a chunk into the specified chunk object.
1225  * The chunk is expected to have the following members: id, text, chunkType.
1226  * @param {Object} chunk The chunk object (with members)
1227  * @config {String} id The chunk id
1228  * @config {String} corpus The collection id
1229  * @config {String} text The chunk label
1230  * @config {String} chunkType The type of the chunk
1231  * @throws {Ext.Msg} MessageBox error
1232  */
1233 Monk.data.chunk.retrieveChunkFrequencies = function(chunks){
1234 	var documentList = [];
1235 	for (var i=0;i<chunks.length;i++) {
1236 		if (chunks[i].id == 'collections-root' || chunks[i].chunkType == 'collection') {return}
1237 		documentList.push(chunks[i].id);
1238 	}
1239 
1240 	var conn = new Ext.ux.ErrorHandlingConnection({listeners: listeners});
1241 	conn.request({
1242 		url: Monk.data.PROXY_URL + 'get/CorpusManager.getLemma',
1243 		method: 'GET',
1244 		params: {
1245 			documentList: documentList.join(',')
1246 		},
1247 		success: function(responseObject){
1248 			var entries = responseObject.responseXML.getElementsByTagName('entry');
1249 			var freqs = [];
1250 			var re = /\(.*?\)/g;
1251 			for (var i=0;i<entries.length;i++) {
1252 				var string = entries[i].getElementsByTagName('string')[0].textContent;
1253 				var pos = new Array();
1254 				while ((match = re.exec(string)) != null) {
1255 					pos.push(match[0]);
1256 				}
1257 				freqs.push({
1258 					string : string.replace(/\s+\(.*?\)/g,'').replace(/\s+/g,' '),
1259 					pos : pos.join(' '),
1260 					count : parseInt(entries[i].getElementsByTagName('long')[0].textContent)
1261 				})
1262 			}
1263 			Workbench.component.manager.notify(new Monk.event.chunk.ChunkFrequenciesRetrieved({
1264 				label: 'Text frequencies retrieved.'
1265 			}), {
1266 				ids : documentList,
1267 				freqs : freqs
1268 			});
1269 		},
1270 		failure: function(response, options){
1271 			Ext.Msg.alert('Data', 'Unable to get chunk frequencies.<br/><b>Error</b>: ' + response.statusText);
1272 		}
1273 	});
1274 
1275 }
1276 
1277 
1278 Monk.data.chunk.cloneChunkNodeWithoutChildren = function(chunkNode) {
1279 	var newNode = {}
1280 	for (p in chunkNode) {
1281 		if (p!="children") newNode[p] = chunkNode[p]
1282 	}
1283 	return newNode;
1284 }
1285 
1286 /**
1287  * Retrieves the concordance information for a feature (lemma/spelling)
1288  * for a particular work
1289  */
1290 Monk.data.chunk.getChunkConcordance = function(chunkId,params){
1291    var conn = new Ext.ux.ErrorHandlingConnection({listeners: listeners});
1292     conn.request({
1293         url: Monk.data.PROXY_URL + 'get/FeatureSearchManager.getFeature',
1294         method: 'GET',
1295         params: {
1296             feature: params.type,
1297             workTag: chunkId,
1298             value: params.term,
1299             format: "html"
1300         },
1301         success: function(responseObject){
1302             params.html = responseObject.responseText;
1303             params.id = chunkId;
1304             Workbench.component.manager.notify(new Monk.event.chunk.ChunkConcordanceRetrieved({
1305                 label: 'chunk concordance retrieved: "' + chunkId + '"'
1306             }), params);
1307         },
1308         failure: function(response, options){
1309             Ext.Msg.alert('Concordance', 'Unable to get work concordance.<br/><b>Error</b>: ' + response.statusText);
1310         }
1311     });
1312 }
1313 
1314 /**
1315  * Retrieve the rendered contents of a chunk (with a specific terms highlighted)
1316  * into the specified chunk object.
1317  * @param {Object} chunk The chunk object (with members)
1318  * @config {String} id The chunk id
1319  * @config {String} text The chunk label
1320  * @config {String} chunkType The type of the chunk
1321  * @param {String} feature The feature type
1322  * @param {Array} terms A list of terms to search for
1323  * @throws {Ext.Msg} MessageBox error
1324  */
1325 /*Monk.data.chunk.retrieveChunkContentsWithFeatures = function(chunk, feature, terms){
1326     var conn = new Ext.ux.ErrorHandlingConnection({listeners: listeners});
1327     var delim = '|';
1328     for (var i = 0; i < terms.length; i++) {
1329         if (terms[i].search('|') != -1) delim = ':';
1330     }
1331     terms = terms.join(delim);
1332     var matches = chunk.id.match(/^(\w+)\.(.+)$/);
1333     if (matches) {
1334         conn.request({
1335             url: Monk.data.PROXY_URL + 'get/CollectionManager.renderChunkWithFeatures',
1336             method: 'GET',
1337             params: {
1338                 collection: matches[1],
1339                 chunkId: matches[2],
1340                 chunk: chunk.chunkType,
1341                 feature: feature,
1342                 terms: terms,
1343                 delim: delim
1344             },
1345             success: function(responseObject) {
1346                 chunk.html = responseObject.responseText.replace(/<\/?canvas>/g,'');
1347                 chunk.html = chunk.html.replace(/<text.*?>/g,'<div id="chunkText">');
1348                 chunk.html = chunk.html.replace(/<\/text>/g,'</div>');
1349                 Workbench.component.manager.notify(
1350                     new Monk.event.chunk.ChunkContentsRetrievedWithFeatures({
1351                         label: 'Text chunk with features retrieved: "'+chunk.text+'"'
1352                         // temporarily removed since response is text/html
1353                         //uri: responseObject.responseXML.documentURI
1354                     }),
1355                     chunk);
1356             },
1357             failure: function(response, options) {
1358                 Ext.Msg.alert('Status', 'Unable to retrieve works with feature(s).<br/><b>Error</b>: '+response.statusText);
1359             }
1360         });
1361     }
1362     else {
1363         Ext.Msg.alert('Status', 'Invalid text chunk requested (unable to find proper id).');
1364     }
1365 }*/
1366 
1367 Workbench.namespace('Monk.data.toolset');
1368 
1369 function processToolsetConfig(toolsetConfig) {
1370     var newToolsetConfig = toolsetConfig;
1371     newToolsetConfig.toolSetConfig = Monk.utils.json2string.encode(toolsetConfig);
1372     delete newToolsetConfig.tools;
1373     return newToolsetConfig;
1374 }
1375 
1376 /**
1377  * Create a new toolset.
1378  * @param {Object} toolsetConfig Contains the toolset configuration
1379  * @config {String} toolSetLabel A label for the toolset
1380  * @config {Integer} worksetId The workset to associate the toolset with
1381  * @config {String} pathToIcon A relative path to an icon for the toolset
1382  * @config {Boolean} shareable True to make the toolset public
1383  * @config {String} description A description of the toolset
1384  * @config {Object} tools An object containing the tools
1385  */
1386 Monk.data.toolset.createToolset = function(toolsetConfig) {
1387     var toolsetParams = processToolsetConfig(toolsetConfig);
1388     var conn = new Ext.ux.ErrorHandlingConnection({listeners: listeners});
1389     conn.request({
1390         url: Monk.data.PROXY_URL + 'get/ProjectManager.createToolSet',
1391         method: 'GET',
1392         params: toolsetParams,
1393         success: function(responseObject) {
1394             var toolsetId = Ext.decode(responseObject.responseText).toolSetId;
1395 
1396             var toolsetJson = Ext.decode(toolsetConfig.toolSetConfig);
1397 
1398             Workbench.component.manager.notify(
1399                 new Monk.event.toolset.ToolsetCreated({
1400                     label: 'Toolset created.'
1401                 }),
1402                 {toolsetId: toolsetId, toolsetJson: toolsetJson});
1403         },
1404         failure: function(response, options) {
1405             Ext.Msg.alert('Data', 'Error creating toolset.<br/><b>Error</b>: '+response.statusText);
1406         }
1407     });
1408 }
1409 
1410 /**
1411  * Save a toolset.
1412  * @param {Integer} toolsetId The toolset ID to update
1413  * @param {Object} toolsetConfig Contains the toolset configuration
1414  * @config {String} toolSetLabel A label for the toolset
1415  * @config {Integer} worksetId The workset ID to associate the toolset with
1416  * @config {String} pathToIcon A relative path to an icon for the toolset
1417  * @config {Boolean} shareable True to make the toolset public
1418  * @config {String} description A description of the toolset
1419  * @config {Object} tools An object containing the tools
1420  */
1421 Monk.data.toolset.saveToolset = function(toolsetId, toolsetConfig) {
1422     var toolsetParams = processToolsetConfig(toolsetConfig);
1423     toolsetParams.toolSetId = toolsetId;
1424     var conn = new Ext.ux.ErrorHandlingConnection({listeners: listeners});
1425     conn.request({
1426         url: Monk.data.PROXY_URL + 'get/ProjectManager.updateToolSet',
1427         method: 'GET',
1428         params: toolsetParams,
1429         success: function(responseObject) {
1430             var toolsetJson = Ext.decode(toolsetConfig.toolSetConfig);
1431 
1432             Workbench.component.manager.notify(
1433                 new Monk.event.toolset.ToolsetSaved({
1434                     label: 'Toolset saved.'
1435                 }),
1436                 {toolsetId: toolsetId, toolsetJson: toolsetJson});
1437         },
1438         failure: function(response, options) {
1439             Ext.Msg.alert('Data', 'Error saving toolset.<br/><b>Error</b>: '+response.statusText);
1440         }
1441     });
1442 }
1443 
1444 /**
1445  * Delete a toolset.
1446  * @param {Integer} toolsetId The toolset to delete
1447  */
1448 Monk.data.toolset.deleteToolset = function(toolsetId) {
1449     var conn = new Ext.ux.ErrorHandlingConnection({listeners: listeners});
1450     conn.request({
1451         url: Monk.data.PROXY_URL + 'get/ProjectManager.removeToolSet',
1452         method: 'GET',
1453         params: {toolSetId: toolsetId},
1454         success: function(responseObject) {
1455             Workbench.component.manager.notify(
1456                 new Monk.event.toolset.ToolsetDeleted({
1457                     label: 'Toolset deleted.'
1458                 }),
1459                 {toolsetId: toolsetId, response: responseObject});
1460         },
1461         failure: function(response, options) {
1462             Ext.Msg.alert('Data', 'Error deleting toolset.<br/><b>Error</b>: '+response.statusText);
1463         }
1464     });
1465 }
1466 
1467 /**
1468  * Get a toolset.
1469  * @param {Integer} toolsetId The toolset to get
1470  */
1471 Monk.data.toolset.getToolset = function(toolsetId) {
1472     var conn = new Ext.ux.ErrorHandlingConnection({listeners: listeners});
1473     conn.request({
1474         url: Monk.data.PROXY_URL + 'get/ProjectManager.getToolSet',
1475         method: 'GET',
1476         params: {toolSetId: toolsetId},
1477         success: function(responseObject) {
1478             Workbench.component.manager.notify(
1479                 new Monk.event.toolset.ToolsetRetrieved({
1480                     label: 'Toolset retrieved.'
1481                 }),
1482                 {toolsetId: toolsetId, json: responseObject.responseText});
1483         },
1484         failure: function(response, options) {
1485             Ext.Msg.alert('Data', 'Error retrieving toolset.<br/><b>Error</b>: '+response.statusText);
1486         }
1487     });
1488 }
1489 
1490 /**
1491  * Get toolsets associated with a workset.
1492  * @param {Integer} worksetId The workset to get toolsets for
1493  */
1494 Monk.data.toolset.getToolsetsForWorkset = function(worksetId) {
1495     var conn = new Ext.ux.ErrorHandlingConnection({listeners: listeners});
1496     conn.request({
1497         url: Monk.data.PROXY_URL + 'get/ProjectManager.getToolSets',
1498         method: 'GET',
1499         params: {worksetId: worksetId},
1500         success: function(responseObject) {
1501             Workbench.component.manager.notify(
1502                 new Monk.event.toolset.ToolsetsForWorksetRetrieved({
1503                     label: 'Toolsets retrieved.'
1504                 }),
1505                 {worksetId: worksetId, json: responseObject.responseText});
1506         },
1507         failure: function(response, options) {
1508             Ext.Msg.alert('Data', 'Error retrieving toolsets.<br/><b>Error</b>: '+response.statusText);
1509         }
1510     });
1511 }
1512 
1513 
1514 Workbench.namespace('Monk.data.corpus');
1515 /*
1516  * Get list of authors associated with a corpus
1517  * @param {String} corpusId
1518  * @returns a hashmap of th corpusId and the authors
1519  */
1520 Monk.data.corpus.getAuthors = function(corpusId){
1521     var conn = new Ext.ux.ErrorHandlingConnection({listeners: listeners});
1522     if(corpusId==null){
1523     	corpusId="all";
1524     }
1525     conn.request({
1526         url: Monk.data.PROXY_URL + 'get/CorpusManager.getAuthors',
1527         method: 'GET',
1528         params: {corpusId: corpusId},
1529         success: function(responseObject) {
1530             Workbench.component.manager.notify(
1531                 new Monk.event.corpus.AuthorsRetrieved({
1532                     label: 'AuthorList retrieved.'
1533                 }),{corpusId: corpusId, authors: responseObject});
1534 
1535         },
1536         failure: function(response, options) {
1537             Ext.Msg.alert('Corpus ', 'Error retrieving author list<br/><b>Error</b>: '+response.statusText);
1538         }
1539     });
1540 
1541 }
1542 
1543 /*
1544  * Get list of collections
1545  * @returns collection list
1546  */
1547 Monk.data.corpus.getCollections = function(){
1548     var conn = new Ext.ux.ErrorHandlingConnection({listeners: listeners});
1549    conn.request({
1550         url: Monk.data.PROXY_URL + 'get/CorpusManager.getCorpora',
1551         method: 'GET',
1552         success: function(responseObject) {
1553             Workbench.component.manager.notify(
1554                 new Monk.event.corpus.CollectionsRetrieved({
1555                     label: 'Collection list retrieved.'
1556                 }),{collections: responseObject});
1557 
1558         },
1559         failure: function(response, options) {
1560             Ext.Msg.alert('Corpus ', 'Error retrieving collection list<br/><b>Error</b>: '+response.statusText);
1561         }
1562     });
1563 
1564 }
1565 
1566 Workbench.namespace("Monk.chart");
1567 
1568 Monk.chart.getFeatureComparison = function(type,featureType,queryParam){
1569     var conn = new Ext.ux.ErrorHandlingConnection({listeners: listeners});
1570     conn.request({
1571         url: Monk.data.PROXY_URL + 'get/AnalyticsManager.getFeatureGenderComparisonChart',
1572         method: 'GET',
1573         params: queryParam,
1574         success: function(responseObject) {
1575               Workbench.component.manager.notify(new Monk.event.chart.FeatureComparisonChartRetrieved({
1576                     label: 'compare features: '
1577        }), {"featureType":featureType,"imgUrl":responseObject.responseText});
1578 
1579         },
1580         failure: function(response, options) {
1581             Ext.Msg.alert('Chart ', 'Error retrieving chart <br/><b>Error</b>: '+response.statusText);
1582         }
1583     });
1584 
1585 }
1586 
1587