1 /**
  2  * @fileOverview
  3  * @author <a href="https://www.labkey.org">LabKey</a> (<a href="mailto:info@labkey.com">info@labkey.com</a>)
  4  * @license Copyright (c) 2008-2016 LabKey Corporation
  5  * <p/>
  6  * Licensed under the Apache License, Version 2.0 (the "License");
  7  * you may not use this file except in compliance with the License.
  8  * You may obtain a copy of the License at
  9  * <p/>
 10  * http://www.apache.org/licenses/LICENSE-2.0
 11  * <p/>
 12  * Unless required by applicable law or agreed to in writing, software
 13  * distributed under the License is distributed on an "AS IS" BASIS,
 14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 15  * See the License for the specific language governing permissions and
 16  * limitations under the License.
 17  * <p/>
 18  */
 19 
 20 /**
 21  * @namespace The Experiment static class allows you to create hidden run groups and other experiment-related functionality.
 22  *            <p>Additional Documentation:
 23  *              <ul>
 24  *                  <li><a href='https://www.labkey.org/Documentation/wiki-page.view?name=moduleassay'>LabKey File-Based Assays</a></li>
 25  *                  <li><a href="https://www.labkey.org/Documentation/wiki-page.view?name=experiment">LabKey Experiment</a></li>
 26  *              </ul>
 27  *           </p>
 28  */
 29 LABKEY.Experiment = new function()
 30 {
 31     function getSuccessCallbackWrapper(createExpFn, fn, scope)
 32     {
 33         return function(response, options)
 34         {
 35             //ensure response is JSON before trying to decode
 36             var json = null;
 37             var experiment = null;
 38             if (response && response.getResponseHeader && response.getResponseHeader('Content-Type')
 39                     && response.getResponseHeader('Content-Type').indexOf('application/json') >= 0)
 40             {
 41                 json = LABKEY.Utils.decode(response.responseText);
 42                 experiment = createExpFn(json);
 43             }
 44 
 45             if(fn)
 46                 fn.call(scope || this, experiment, response);
 47         };
 48     }
 49 
 50     function _saveBatches(config, createExps)
 51     {
 52         LABKEY.Ajax.request({
 53             url: LABKEY.ActionURL.buildURL("assay", "saveAssayBatch", LABKEY.ActionURL.getContainer()),
 54             method: 'POST',
 55             jsonData: {
 56                 assayId: config.assayId,
 57                 assayName: config.assayName,
 58                 providerName: config.providerName,
 59                 batches: config.batches
 60             },
 61             success: getSuccessCallbackWrapper(createExps, config.success, config.scope),
 62             failure: LABKEY.Utils.getCallbackWrapper(config.failure, config.scope, true),
 63             scope: config.scope,
 64             headers: {
 65                 'Content-Type' : 'application/json'
 66             }
 67         });
 68     }
 69 
 70     // normalize the different config object passed in for saveBatch and saveBatches into one config
 71     // appropriate for _saveBatches call above
 72     function getSaveBatchesConfig(config)
 73     {
 74         var wrapConfig = {};
 75 
 76         if (config.batches)
 77         {
 78             wrapConfig.batches = config.batches;
 79         }
 80         else
 81         {
 82             wrapConfig.batches=[];
 83             wrapConfig.batches.push(config.batch);
 84         }
 85         wrapConfig.assayId = config.assayId;
 86         wrapConfig.assayName = config.assayName;
 87         wrapConfig.providerName = config.providerName;
 88         wrapConfig.scope = config.scope;
 89         wrapConfig.success = LABKEY.Utils.getOnSuccess(config);
 90         wrapConfig.failure = LABKEY.Utils.getOnFailure(config);
 91         return wrapConfig;
 92     }
 93 
 94     /** @scope LABKEY.Experiment */
 95     return {
 96 
 97         /**
 98          * Create or recycle an existing run group. Run groups are the basis for some operations, like comparing
 99          * MS2 runs to one another.
100          * @param config A configuration object with the following properties:
101          * @param {function} config.success A reference to a function to call with the API results. This
102          * function will be passed the following parameters:
103          * <ul>
104          * <li><b>runGroup:</b> a {@link LABKEY.Exp.RunGroup} object containing properties about the run group</li>
105          * <li><b>response:</b> The XMLHttpResponse object</li>
106          * </ul>
107          * @param {Integer[]} [config.runIds] An array of integer ids for the runs to be members of the group. Either
108          * runIds or selectionKey must be specified.
109          * @param {string} [config.selectionKey] The DataRegion's selectionKey to be used to resolve the runs to be
110          * members of the group. Either runIds or selectionKey must be specified.
111          * @param {function} [config.failure] A reference to a function to call when an error occurs. This
112          * function will be passed the following parameters:
113          * <ul>
114          * <li><b>errorInfo:</b> an object containing detailed error information (may be null)</li>
115          * <li><b>response:</b> The XMLHttpResponse object</li>
116          * </ul>
117          * @param {string} [config.containerPath] An alternate container path to get permissions from. If not specified,
118          * the current container path will be used.
119          * @param {object} [config.scope] A scoping object for the success and error callback functions (default to this).
120          * @static
121          */
122         createHiddenRunGroup : function (config)
123         {
124             function createExp(json)
125             {
126                 return new LABKEY.Exp.RunGroup(json);
127             }
128 
129             var jsonData = {};
130             if (config.runIds && config.selectionKey)
131             {
132                 throw "Only one of runIds or selectionKey config parameter is allowed for a single call.";
133             }
134             else if (config.runIds)
135             {
136                 jsonData.runIds = config.runIds;
137             }
138             else if (config.selectionKey)
139             {
140                 jsonData.selectionKey = config.selectionKey;
141             }
142             else
143             {
144                 throw "Either the runIds or the selectionKey config parameter is required.";
145             }
146             LABKEY.Ajax.request(
147             {
148                 url : LABKEY.ActionURL.buildURL("experiment", "createHiddenRunGroup", config.containerPath),
149                 method : 'POST',
150                 jsonData : jsonData,
151                 success: getSuccessCallbackWrapper(createExp, LABKEY.Utils.getOnSuccess(config), config.scope),
152                 failure: LABKEY.Utils.getCallbackWrapper(LABKEY.Utils.getOnFailure(config), config.scope, true),
153                 headers :
154                 {
155                     'Content-Type' : 'application/json'
156                 }
157             });
158         },
159 
160         /**
161          * Loads a batch from the server.
162          * @param config An object that contains the following configuration parameters
163          * @param {Number} config.assayId The assay protocol id.
164          * @param {Number} config.batchId The batch id.
165          * @param {function} config.success The function to call when the function finishes successfully.
166          * This function will be called with a the parameters:
167          * <ul>
168          * <li><b>batch</b> A new {@link LABKEY.Exp.RunGroup} object.
169          * <li><b>response</b> The original response
170          * </ul>
171          * @param {function} [config.failure] The function to call if this function encounters an error.
172          * This function will be called with the following parameters:
173          * <ul>
174          * <li><b>response</b> The original response
175          * </ul>
176          * @param {object} [config.scope] A scoping object for the success and error callback functions (default to this).
177          * @see The <a href='https://www.labkey.org/Documentation/wiki-page.view?name=moduleassay'>Module Assay</a> documentation for more information.
178          * @static
179          */
180         loadBatch : function (config)
181         {
182             function createExp(json)
183             {
184                 return new LABKEY.Exp.RunGroup(json.batch);
185             }
186 
187             LABKEY.Ajax.request({
188                 url: LABKEY.ActionURL.buildURL("assay", "getAssayBatch", LABKEY.ActionURL.getContainer()),
189                 method: 'POST',
190                 success: getSuccessCallbackWrapper(createExp, LABKEY.Utils.getOnSuccess(config), config.scope),
191                 failure: LABKEY.Utils.getCallbackWrapper(LABKEY.Utils.getOnFailure(config), config.scope, true),
192                 scope: config.scope,
193                 jsonData : {
194                     assayId: config.assayId,
195                     assayName: config.assayName,
196                     providerName: config.providerName,
197                     batchId: config.batchId
198                 },
199                 headers : {
200                     'Content-Type' : 'application/json'
201                 }
202             });
203         },
204 
205         /**
206          * Loads batches from the server.
207          * @param config An object that contains the following configuration parameters
208          * @param {Number} config.assayId The assay protocol id.
209          * @param {Number} config.batchIds The list of batch ids.
210          * @param {function} config.success The function to call when the function finishes successfully.
211          * This function will be called with a the parameters:
212          * <ul>
213          * <li><b>batches</b> The list of {@link LABKEY.Exp.RunGroup} objects.
214          * <li><b>response</b> The original response
215          * </ul>
216          * @param {function} [config.failure] The function to call if this function encounters an error.
217          * This function will be called with the following parameters:
218          * <ul>
219          * <li><b>response</b> The original response
220          * </ul>
221          * @param {object} [config.scope] A scoping object for the success and error callback functions (default to this).
222          * @see The <a href='https://www.labkey.org/Documentation/wiki-page.view?name=moduleassay'>Module Assay</a> documentation for more information.
223          * @static
224          */
225         loadBatches : function (config)
226         {
227             function createExp(json)
228             {
229                 var batches = [];
230                 if (json.batches) {
231                     for (var i = 0; i < json.batches.length; i++) {
232                         batches.push(new LABKEY.Exp.RunGroup(json.batches[i]));
233                     }
234                 }
235                 return batches;
236             }
237 
238             LABKEY.Ajax.request({
239                 url: LABKEY.ActionURL.buildURL("assay", "getAssayBatches", LABKEY.ActionURL.getContainer()),
240                 method: 'POST',
241                 success: getSuccessCallbackWrapper(createExp, LABKEY.Utils.getOnSuccess(config), config.scope),
242                 failure: LABKEY.Utils.getCallbackWrapper(LABKEY.Utils.getOnFailure(config), config.scope, true),
243                 scope: config.scope,
244                 jsonData : {
245                     assayId: config.assayId,
246                     assayName: config.assayName,
247                     providerName: config.providerName,
248                     batchIds: config.batchIds
249                 },
250                 headers : {
251                     'Content-Type' : 'application/json'
252                 }
253             });
254         },
255 
256         /**
257          * Saves a modified batch.
258          * Runs within the batch may refer to existing data and material objects, either inputs or outputs, by ID or LSID.
259          * Runs may also define new data and materials objects by not specifying an ID or LSID in their properties.
260          * @param config An object that contains the following configuration parameters
261          * @param {Number} config.assayId The assay protocol id.
262          * @param {LABKEY.Exp.RunGroup} config.batch The modified batch object.
263          * @param {function} config.success The function to call when the function finishes successfully.
264          * This function will be called with the following parameters:
265          * <ul>
266          * <li><b>batch</b> A new {@link LABKEY.Exp.RunGroup} object.  Some values (such as IDs and LSIDs) will be filled in by the server.
267          * <li><b>response</b> The original response
268          * </ul>
269          * @param {function} [config.failure] The function to call if this function encounters an error.
270          * This function will be called with the following parameters:
271          * <ul>
272          * <li><b>response</b> The original response
273          * </ul>
274          * @see The <a href='https://www.labkey.org/Documentation/wiki-page.view?name=moduleassay'>Module Assay</a> documentation for more information.
275          * @static
276          */
277         saveBatch : function (config)
278         {
279             _saveBatches(getSaveBatchesConfig(config), function(json) {
280                 if (json.batches) {
281                     return new LABKEY.Exp.RunGroup(json.batches[0])
282                 }
283              });
284         },
285 
286         /**
287          * Saves an array of modified batches.
288          * Runs within the batches may refer to existing data and material objects, either inputs or outputs, by ID or LSID.
289          * Runs may also define new data and materials objects by not specifying an ID or LSID in their properties.
290          * @param config An object that contains the following configuration parameters
291          * @param {Number} config.assayId The assay protocol id.
292          * @param {LABKEY.Exp.RunGroup[]} config.batches The modified batch objects.
293          * @param {function} config.success The function to call when the function finishes successfully.
294          * This function will be called with the following parameters:
295          * <ul>
296          * <li><b>batches</b> An array of new {@link LABKEY.Exp.RunGroup} objects.  Some values (such as IDs and LSIDs) will be filled in by the server.
297          * <li><b>response</b> The original response
298          * </ul>
299          * @param {function} [config.failure] The function to call if this function encounters an error.
300          * This function will be called with the following parameters:
301          * <ul>
302          * <li><b>response</b> The original response
303          * </ul>
304          * @see The <a href='https://www.labkey.org/Documentation/wiki-page.view?name=moduleassay'>Module Assay</a> documentation for more information.
305          * @static
306          */
307         saveBatches : function (config)
308         {
309             _saveBatches(getSaveBatchesConfig(config), function(json){
310                 var batches = [];
311                 if (json.batches) {
312                     for (var i = 0; i < json.batches.length; i++) {
313                         batches.push(new LABKEY.Exp.RunGroup(json.batches[i]));
314                     }
315                 }
316                 return batches;
317             });
318         },
319 
320         /**
321          * Saves materials.
322          * @deprecated Use LABKEY.Query.insertRows({schemaName: 'Samples', queryName: '<sample set name>', ...});
323          *
324          * @param config An object that contains the following configuration parameters
325          * @param config.name name of the sample set
326          * @param config.materials An array of LABKEY.Exp.Material objects to be saved.
327          * @param {function} config.success The function to call when the function finishes successfully.
328          * This function will be called with the following parameters:
329          * <ul>
330          * <li><b>batch</b> A new {@link LABKEY.Exp.RunGroup} object.  Some values will be filled in by the server.
331          * <li><b>response</b> The original response
332          * </ul>
333          * @param {function} [config.failure] The function to call if this function encounters an error.
334          * This function will be called with the following parameters:
335          * <ul>
336          * <li><b>response</b> The original response
337          * </ul>
338          * @param {object} [config.scope] A scoping object for the success and error callback functions (default to this).
339          * @static
340          */
341         saveMaterials : function (config)
342         {
343             LABKEY.Query.insertRows({
344                 schemaName: 'Samples',
345                 queryName: config.name,
346                 rows: config.materials,
347                 success: LABKEY.Utils.getOnSuccess(config),
348                 failure: LABKEY.Utils.getOnFailure(config),
349                 scope: config.scope
350             });
351         },
352 
353         /**
354          * Get parent/child relationships of an ExpData or ExpMaterial.
355          * @param config
356          * @param config.rowId The row id of the seed ExpData or ExpMaterial.  Either rowId or lsid is required.
357          * @param config.lsid The LSID of the seed ExpData or ExpMaterial.  Either rowId or lsid is required.
358          * @param {Number} [config.depth] An optional depth argument.  Defaults to include all.
359          * @param {Boolean} [config.parents] Include parents in the lineage response.  Defaults to true.
360          * @param {Boolean} [config.children] Include children in the lineage response.  Defaults to true.
361          * @param {String} [config.expType] Optional experiment type to filter response -- either "Data", "Material", or "ExperimentRun".  Defaults to include all.
362          * @param {String} [config.cpasType] Optional LSID of a SampleSet or DataClass to filter the response.  Defaults to include all.
363          * @static
364          */
365         lineage : function (config)
366         {
367             var params = {};
368             if (config.veryNewHotness !== undefined)
369                 params.veryNewHotness = config.veryNewHotness;
370             if (config.rowId)
371                 params.rowId = config.rowId;
372             else if (config.lsid)
373                 params.lsid = config.lsid;
374 
375             if (config.hasOwnProperty('parents'))
376                 params.parents = config.parents;
377             if (config.hasOwnProperty('children'))
378                 params.children = config.children;
379             if (config.hasOwnProperty('depth'))
380                 params.depth = config.depth;
381 
382             if (config.expType)
383                 params.expType = config.expType;
384             if (config.cpasType)
385                 params.cpasType = config.cpasType;
386 
387             LABKEY.Ajax.request({
388                 method: 'GET',
389                 url: LABKEY.ActionURL.buildURL("experiment", "lineage.api"),
390                 params: params,
391                 success: LABKEY.Utils.getCallbackWrapper(LABKEY.Utils.getOnSuccess(config), config.scope),
392                 failure: LABKEY.Utils.getCallbackWrapper(LABKEY.Utils.getOnFailure(config), config.scope, true),
393                 scope: config.scope
394             });
395         }
396     };
397 
398 };
399 
400 if (typeof LABKEY.Exp == "undefined")
401     LABKEY.Exp = {};
402 
403 /**
404  * This constructor isn't called directly, but is used by derived classes.
405  * @class The experiment object base class describes basic
406  * characteristics of a protocol or an experimental run.  Many experiment classes (such as {@link LABKEY.Exp.Run},
407  * {@link LABKEY.Exp.Data} and {@link LABKEY.Exp.Material}) are subclasses
408  * of ExpObject, so they provide the fields defined by this object (e.g., name, lsid, etc).
409  * In a Java representation of these same classes, ExpObject is an abstract class.
410  *            <p>Additional Documentation:
411  *              <ul>
412  *                  <li><a href="https://www.labkey.org/Documentation/wiki-page.view?name=experiment">LabKey Experiment</a></li>
413  *              </ul>
414  *           </p>
415  * @memberOf LABKEY.Exp
416  *
417  * @param {Object} [config] Configuration object.
418  *
419  * @param {String} config.lsid The LSID of the ExpObject.
420  * @param {String} config.name The name of the ExpObject.
421  * @param {number} config.id The id of the ExpObject.
422  * @param {number} config.rowId The id of the ExpObject (alias of id property)
423  * @param {String} config.comment User editable comment.
424  * @param {Date} config.created When the ExpObject was created.
425  * @param {String} config.createdBy The person who created the ExpObject.
426  * @param {Date} config.modified When the ExpObject was last modified.
427  * @param {String} config.modifiedBy The person who last modified the ExpObject.
428  * @param {Object} config.properties Map of property descriptor names to values. Most types, such as strings and
429  * numbers, are just stored as simple properties. Properties of type FileLink will be returned by the server in the
430  * same format as {@link LABKEY.Exp.Data} objects (missing many properties such as id and createdBy if they exist on disk but
431  * have no row with metadata in the database). FileLink values are accepted from the client in the same way, or a simple value of the
432  * following three types:  the data's RowId, the data's LSID, or the full path on the server's file system.
433  */
434 LABKEY.Exp.ExpObject = function (config) {
435     config = config || {};
436     this.lsid = config.lsid;
437     this.name = config.name;
438     this.id = config.id || config.rowId;
439     this.rowId = this.id;
440     this.comment = config.comment;
441     this.created = config.created;
442     this.createdBy = config.createdBy;
443     this.modified = config.modified;
444     this.modifiedBy = config.modifiedBy;
445     this.properties = config.properties || {};
446 };
447 
448 /**
449  * Constructs a new experiment run object.
450  * @class The Exp.Run class describes an experiment run.  An experiment run is an application of an experimental
451  * protocol to concrete inputs, producing concrete outputs. In object-oriented terminology, a protocol would be a class
452  * while a run would be an instance.
453  *            <p>Additional Documentation:
454  *              <ul>
455  *                  <li><a href="https://www.labkey.org/Documentation/wiki-page.view?name=experiment">LabKey Experiment</a></li>
456  *              </ul>
457  *           </p>
458  * @extends LABKEY.Exp.ExpObject
459  * @memberOf LABKEY.Exp
460  *
461  * @param {Object} [config] The configuration object.  Inherits the config properties of {@link LABKEY.Exp.ExpObject}.
462  * @param {Object[]} config.dataInputs Array of {@link LABKEY.Exp.Data} objects that are the inputs to this run. Datas typically represent a file on the server's file system.
463  * @param {Object[]} config.dataOutputs Array of {@link LABKEY.Exp.Data} objects that are the outputs from this run. Datas typically represent a file on the server's file system.
464  * @param {Object[]} config.dataRows Array of Objects where each Object corresponds to a row in the results domain.
465  * @param {Object[]} config.materialInputs Array of {@link LABKEY.Exp.Material} objects that are material/sample inputs to the run.
466  * @param {Object[]} config.materialOutputs Array of {@link LABKEY.Exp.Material} objects that are material/sample outputs from the run.
467  *
468  * @see LABKEY.Exp.Data#getContent
469  *
470  * @example
471  * var result = // ... result of uploading a new assay results file
472  * var data = new LABKEY.Exp.Data(result);
473  *
474  * var run = new LABKEY.Exp.Run();
475  * run.name = data.name;
476  * run.properties = { "MyRunProperty" : 3 };
477  * run.dataInputs = [ data ];
478  *
479  * data.getContent({
480  *   format: 'jsonTSV',
481  *   success: function (content, format) {
482  *     data.content = content;
483  *     var sheet = content.sheets[0];
484  *     var filedata = sheet.data;
485  *
486  *     // transform the file content into the dataRows array used by the run
487  *     run.dataRows = [];
488  *     for (var i = 1; i < filedata.length; i++) {
489  *       var row = filedata[i];
490  *       run.dataRows.push({
491  *         "SampleId": row[0],
492  *         "DataValue": row[1],
493  *         // ... other columns
494  *       });
495  *     }
496  *
497  *     var batch = // ... the LABKEY.Exp.RunGroup object
498  *     batch.runs.push(run);
499  *   },
500  *   failure: function (error, format) {
501  *     alert("error: " + error);
502  *   }
503  * });
504  */
505 LABKEY.Exp.Run = function (config) {
506     LABKEY.Exp.ExpObject.call(this, config);
507     config = config || {};
508 
509     this.experiments = config.experiments || [];
510     this.protocol = config.protocol;
511     this.filePathRoot = config.filePathRoot;
512 
513     this.dataInputs = [];
514     if (config.dataInputs) {
515         for (var i = 0; i < config.dataInputs.length; i++) {
516             this.dataInputs.push(new LABKEY.Exp.Data(config.dataInputs[i]));
517         }
518     }
519 
520     this.dataOutputs = config.dataOutputs || [];
521     this.dataRows = config.dataRows || [];
522     this.materialInputs = config.materialInputs || [];
523     this.materialOutputs = config.materialOutputs || [];
524     this.objectProperties = config.objectProperties || {};
525 };
526 LABKEY.Exp.Run.prototype = new LABKEY.Exp.ExpObject;
527 LABKEY.Exp.Run.prototype.constructor = LABKEY.Exp.Run;
528 
529 /**
530  * Deletes the run from the database.
531  * @param config An object that contains the following configuration parameters
532  * @param {Function} config.success A reference to a function to call with the API results. This
533  * function will be passed the following parameters:
534  * <ul>
535  * <li><b>data:</b> a simple object with one property called 'success' which will be set to true.</li>
536  * <li><b>response:</b> The XMLHttpResponse object</li>
537  * </ul>
538  * @param {Function} [config.failure] A reference to a function to call when an error occurs. This
539  * function will be passed the following parameters:
540  * <ul>
541  * <li><b>errorInfo:</b> an object containing detailed error information (may be null)</li>
542  * <li><b>response:</b> The XMLHttpResponse object</li>
543  * </ul>
544  */
545 LABKEY.Exp.Run.prototype.deleteRun = function(config)
546 {
547     LABKEY.Ajax.request(
548     {
549         url : LABKEY.ActionURL.buildURL("experiment", "deleteRun"),
550         method : 'POST',
551         params : { runId : this.id },
552         success: LABKEY.Utils.getCallbackWrapper(LABKEY.Utils.getOnSuccess(config), this, false),
553         failure: LABKEY.Utils.getCallbackWrapper(LABKEY.Utils.getOnFailure(config), this, true)
554     });
555 };
556 
557 
558 
559 /**
560  * The Protocol constructor is private.
561  * @class Experiment protocol.
562  * @extends LABKEY.Exp.ExpObject
563  * @memberOf LABKEY.Exp
564  *
565  * @param {Object} [config] private constructor argument.  Inherits config properties of {@link LABKEY.Exp.ExpObject}.
566  *
567  * @ignore hide from JsDoc for now
568  */
569 LABKEY.Exp.Protocol = function (config) {
570     LABKEY.Exp.ExpObject.call(this, config);
571     config = config || {};
572 
573     this.instrument = config.instrument;
574     this.software = config.software;
575     this.contact = config.contact;
576     this.childProtocols = config.childProtocols || [];
577     this.steps = config.steps || [];
578     this.applicationType = config.applicationType;
579     this.description = config.description;
580     this.runs = [];
581     if (config.runs) {
582         for (var i = 0; i < config.runs.length; i++) {
583             this.runs.push(new LABKEY.Exp.Run(config.runs[i]));
584         }
585     }
586 };
587 LABKEY.Exp.Protocol.prototype = new LABKEY.Exp.ExpObject;
588 LABKEY.Exp.Protocol.prototype.constructor = LABKEY.Exp.Protocol;
589 
590 /**
591  * The RunGroup constructor is private.  To retrieve a batch RunGroup
592  * from the server, see {@link LABKEY.Experiment.loadBatch}.
593  * @class An experiment run group contains an array of
594  * {@link LABKEY.Exp.Run}s.  If all runs have the same assay protocol, the run group
595  * is considered a batch.  To add runs to a batch, insert new {@link LABKEY.Exp.Run}
596  * instances into to the 'runs' Array and save the batch.
597  * <p>
598  * Use {@link LABKEY.Experiment.loadBatch} and {@link LABKEY.Experiment.saveBatch} to
599  * load and save a RunGroup.
600  * </p>
601  *            <p>Additional Documentation:
602  *              <ul>
603  *                  <li><a href='https://www.labkey.org/Documentation/wiki-page.view?name=moduleassay'>LabKey File-Based Assays</a></li>
604  *                  <li><a href="https://www.labkey.org/Documentation/wiki-page.view?name=experiment">LabKey Experiment</a></li>
605  *              </ul>
606  *           </p>
607  * @extends LABKEY.Exp.ExpObject
608  * @memberOf LABKEY.Exp
609  *
610  * @param {Object} [config] Private configuration object. Inherits config properties of {@link LABKEY.Exp.ExpObject}.
611  * @param {LABKEY.Exp.Run[]} config.runs Array of {@link LABKEY.Exp.Run}s in this run group.
612  * @param {Boolean} config.hidden Determines whether the RunGroup is hidden.
613  */
614 LABKEY.Exp.RunGroup = function (config) {
615     LABKEY.Exp.ExpObject.call(this, config);
616     config = config || {};
617 
618     this.batchProtocolId = config.batchProtocolId || 0;
619     this.runs = [];
620     if (config.runs) {
621         for (var i = 0; i < config.runs.length; i++) {
622             this.runs.push(new LABKEY.Exp.Run(config.runs[i]));
623         }
624     }
625     //this.protocols = config.protocols || [];
626     //this.batchProtocol = config.batchProtocol;
627     this.hidden = config.hidden;
628 };
629 LABKEY.Exp.RunGroup.prototype = new LABKEY.Exp.ExpObject;
630 LABKEY.Exp.RunGroup.prototype.constructor = LABKEY.Exp.RunGroup;
631 
632 /**
633  * The ProtocolApplication constructor is private.
634  * @class Experiment ProtocolApplication.
635  * @extends LABKEY.Exp.ExpObject
636  * @memberOf LABKEY.Exp
637  *
638  * @param {Object} [config] Private configuration object. Inherits config properties of {@link LABKEY.Exp.ExpObject}.
639  *
640  * @ignore hide from JsDoc for now
641  */
642 LABKEY.Exp.ProtocolApplication = function (config) {
643     LABKEY.Exp.ExpObject.call(this, config);
644     config = config || {};
645 
646 };
647 LABKEY.Exp.ProtocolApplication.prototype = new LABKEY.Exp.ExpObject;
648 LABKEY.Exp.ProtocolApplication.prototype.constructor = LABKEY.Exp.ProtocolApplication;
649 
650 /**
651  * @class The SampleSet class describes a collection of experimental samples, which are
652  * also known as materials (see {@link LABKEY.Exp.Material}). This class defines the set of fields that
653  * you you wish to attach to all samples in the group. These fields supply characteristics of the sample
654  * (e.g., its volume, number of cells, color, etc.).
655  *            <p>Additional Documentation:
656  *              <ul>
657  *                  <li><a href="https://www.labkey.org/Documentation/wiki-page.view?name=experiment">LabKey Experiment</a></li>
658  *              </ul>
659  *           </p>
660  * @extends LABKEY.Exp.ExpObject
661  * @memberOf LABKEY.Exp
662  *
663  * @param {Object} [config] Describes the SampleSet's properties.  Inherits the config properties of {@link LABKEY.Exp.ExpObject}.
664  * @param {Object[]} config.samples Array of {@link LABKEY.Exp.Material} config objects.
665  * @param {String} config.description Description of the SampleSet
666  */
667 
668 LABKEY.Exp.SampleSet = function (config) {
669     LABKEY.Exp.ExpObject.call(this, config);
670     config = config || {};
671     this.samples = config.samples;
672     this.description = config.description;
673 };
674 LABKEY.Exp.SampleSet.prototype = new LABKEY.Exp.ExpObject;
675 LABKEY.Exp.SampleSet.prototype.constructor = LABKEY.Exp.SampleSet;
676 
677 /**
678  * Get a domain design for the SampleSet.
679  *
680  * @param {Function} config.success Required. Function called if the
681  *	"getDomain" function executes successfully. Will be called with the argument {@link LABKEY.Domain.DomainDesign},
682  *    which describes the fields of a domain.
683  * @param {Function} [config.failure] Function called if execution of the "getDomain" function fails.
684  * @param {String} [config.containerPath] The container path in which the requested Domain is defined.
685  *       If not supplied, the current container path will be used.
686  *
687  * @ignore hide from JsDoc for now
688  *
689  * @example
690  * <script type="text/javascript">
691  *   Ext.onReady(function() {
692  *     var ss = new LABKEY.Exp.SampleSet({name: 'MySampleSet'});
693  *     ss.getDomain({
694  *       success : function (domain) {
695  *         console.log(domain);
696  *       }
697  *     });
698  *   }
699  * </script>
700  */
701 LABKEY.Exp.SampleSet.prototype.getDomain = function (config)
702 {
703     LABKEY.Domain.get(LABKEY.Utils.getOnSuccess(config), LABKEY.Utils.getOnFailure(config), "Samples", this.name, config.containerPath);
704 };
705 
706 /**
707  * Create a new Sample Set definition.
708  * @param {Function} config.success Required callback function.
709  * @param {Function} [config.failure] Failure callback function.
710  * @param {LABKEY.Domain.DomainDesign} config.domainDesign The domain design to save.
711  * @param {Object} [config.options] Set of extra options used when creating the SampleSet:
712  * <ul>
713  *   <li>idCols: Optional. Array of indexes into the domain design fields.  If the domain design contains a 'Name' field, no idCols are allowed.  Either a 'Name' field must be present or at least one idCol must be supplied..
714  *   <li>parentCol: Optional. Index of the parent id column.
715  * </ul>
716  * @param {String} [config.containerPath] The container path in which to create the domain.
717  * @static
718  *
719  * @ignore hide from JsDoc for now
720  *
721  * @example
722  * var domainDesign = {
723  *   name: "BoyHowdy",
724  *   description: "A client api created sample set",
725  *   fields: [{
726  *     name: 'TestName',
727  *     label: 'The First Field',
728  *     rangeURI: 'http://www.w3.org/2001/XMLSchema#string'
729  *   },{
730  *     name: 'Num',
731  *     rangeURI: 'http://www.w3.org/2001/XMLSchema#int'
732  *   },{
733  *     name: 'Parent',
734  *     rangeURI: 'http://www.w3.org/2001/XMLSchema#string'
735  *   }]
736  * };
737  *
738  * LABKEY.Exp.SampleSet.create({
739  *   success: function () { alert("success!"); },
740  *   failure: function () { alert("failure!"); },
741  *   domainDesign: domainDesign,
742  *   options: { idCols: [0, 1], parentCol: 2 }
743  * });
744  */
745 LABKEY.Exp.SampleSet.create = function (config)
746 {
747     LABKEY.Domain.create(LABKEY.Utils.getOnSuccess(config), LABKEY.Utils.getOnFailure(config), "SampleSet", config.domainDesign, config.options, config.containerPath);
748 };
749 
750 /**
751  * DataClass represents a set of ExpData objects that share a set of properties.
752  *
753  * @class DataClass describes a collection of Data objects.
754  * This class defines the set of fields that you you wish to attach to all datas in the group.
755  * Within the DataClass, each Data has a unique name.
756  *
757  * @extends LABKEY.Exp.ExpObject
758  * @memberOf LABKEY.Exp
759  *
760  * @param config
761  * @param {String} config.description Description of the DataClass.
762  * @param {String} [config.nameExpression] Optional name expression used to generate unique names for ExpData inserted into the DataClass.
763  * @param {Object} [config.sampleSet] The optional SampleSet the DataClass is associated with.  With the following properties:
764  * @param {Integer} [config.sampleSet.id] The row id of the SampleSet.
765  * @param {String} [config.sampleSet.name] The name of the SampleSet.
766  * @constructor
767  */
768 LABKEY.Exp.DataClass = function (config)
769 {
770     "use strict";
771 
772     LABKEY.Exp.ExpObject.call(this, config);
773     config = config || {};
774     this.data = config.data;
775     this.description = config.description;
776     this.sampleSet = config.sampleSet;
777 };
778 LABKEY.Exp.DataClass.prototype = new LABKEY.Exp.ExpObject;
779 LABKEY.Exp.DataClass.prototype.constructor = LABKEY.Exp.DataClass;
780 
781 LABKEY.Exp.DataClass.prototype.getDomain = function (config)
782 {
783     "use strict";
784     LABKEY.Domain.get({
785         success: LABKEY.Utils.getOnSuccess(config),
786         failure: LABKEY.Utils.getOnFailure(config),
787         schemaName: "exp.data",
788         queryName: this.name,
789         containerPath: config.containerPath
790     });
791 };
792 
793 LABKEY.Exp.DataClass.create = function (config)
794 {
795     "use strict";
796     LABKEY.Domain.create({
797         success: LABKEY.Utils.getOnSuccess(config),
798         failure: LABKEY.Utils.getOnFailure(config),
799         type: "dataclass",
800         domainDesign: config.domainDesign,
801         options: config.options,
802         containerPath: config.containerPath
803     });
804 };
805 
806 /**
807  * The ChildObject constructor is private.
808  * @class Experiment Child
809  * @extends LABKEY.Exp.ExpObject
810  * @memberOf LABKEY.Exp
811  *
812  * @param {Object} [config] Private configuration object. Inherits the config properties of {@link LABKEY.Exp.ExpObject}.
813  *
814  * @ignore hide from JsDoc for now
815  */
816 LABKEY.Exp.ChildObject = function (config) {
817     LABKEY.Exp.ExpObject.call(this, config);
818     config = config || {};
819     // property holder
820 };
821 LABKEY.Exp.ChildObject.prototype = new LABKEY.Exp.ExpObject;
822 LABKEY.Exp.ChildObject.prototype.constructor = LABKEY.Exp.ChildObject;
823 
824 /**
825  * The ProtocolOutput constructor is private.
826  * @class Experiment Protocol Output.  Base class for {@link LABKEY.Exp.Data}
827  * and {@link LABKEY.Exp.Material}.
828  * @extends LABKEY.Exp.ExpObject
829  * @memberOf LABKEY.Exp
830  * @param {Object} [config] Private configuration object. Inherits config properties of {@link LABKEY.Exp.ExpObject}.
831  * @ignore hide from JsDoc for now
832  */
833 LABKEY.Exp.ProtocolOutput = function (config) {
834     LABKEY.Exp.ExpObject.call(this, config);
835     config = config || {};
836 
837     this.sourceProtocol = config.sourceProtocol;
838     this.run = config.run;
839     this.targetApplications = config.targetApplications;
840     this.sourceApplications = config.sourceApplications;
841     this.sucessorRuns = config.sucessorRuns;
842     this.cpasType = config.cpasType;
843 };
844 LABKEY.Exp.ProtocolOutput.prototype = new LABKEY.Exp.ExpObject;
845 LABKEY.Exp.ProtocolOutput.prototype.constructor = LABKEY.Exp.ProtocolOutput;
846 
847 /**
848  * Constructs a new experiment material object.
849  * @class The Exp.Material class describes an experiment material.  "Material" is a synonym for both
850  * "sample" and "specimen."  Thus, for example, the input to an assay could be called a material.
851  * The fields of this class are inherited from the {@link LABKEY.Exp.ExpObject} object and
852  * the private LABKEY.Exp.ProtocolOutput object.
853  *            <p>Additional Documentation:
854  *              <ul>
855  *                  <li><a href="https://www.labkey.org/Documentation/wiki-page.view?name=experiment">LabKey Experiment</a></li>
856  *              </ul>
857  *           </p>
858  * @extends LABKEY.Exp.ProtocolOutput
859  * @extends LABKEY.Exp.ExpObject
860  * @memberOf LABKEY.Exp
861  *
862  * @param {Object} [config] Configuration object.  Inherits the config properties of {@link LABKEY.Exp.ExpObject}.
863  * @param {Object} [config.sampleSet] The SampleSet the material belongs to.  With the following properties:
864  * @param {Integer} [config.sampleSet.id] The row id of the SampleSet.
865  * @param {String} [config.sampleSet.name] The name of the SampleSet.
866  */
867 LABKEY.Exp.Material = function (config) {
868     LABKEY.Exp.ProtocolOutput.call(this, config);
869     config = config || {};
870 
871     this.sampleSet = config.sampleSet;
872 };
873 LABKEY.Exp.Material.prototype = new LABKEY.Exp.ProtocolOutput;
874 LABKEY.Exp.Material.prototype.constructor = LABKEY.Exp.Material;
875 
876 /**
877  * The Data constructor is private.
878  * To create a LABKEY.Exp.Data object, upload a file using to the "assayFileUpload" action of
879  * the "assay" controller.
880  *
881  * @class The Experiment Data class describes the data input or output of a {@link LABKEY.Exp.Run}.  This typically
882  * corresponds to an assay results file uploaded to the LabKey server.
883  * <p>
884  * To create a LABKEY.Exp.Data object, upload a file using to the "assayFileUpload" action of
885  * the "assay" controller.
886  * </p>
887  *            <p>Additional Documentation:
888  *              <ul>
889  *                  <li><a href='https://www.labkey.org/Documentation/wiki-page.view?name=moduleassay'>LabKey File-Based Assays</a></li>
890  *                  <li><a href="https://www.labkey.org/Documentation/wiki-page.view?name=experiment">LabKey Experiment</a></li>
891  *              </ul>
892  *           </p>
893  *
894  * @extends LABKEY.Exp.ProtocolOutput
895  * @extends LABKEY.Exp.ExpObject
896  * @memberOf LABKEY.Exp
897  *
898  * @param {Object} [config] Private configuration object.  Inherits the config properties of {@link LABKEY.Exp.ExpObject}.
899  * @param {String} config.dataFileURL The local file url of the uploaded file.
900  * @param {Object} [config.dataClass] The DataClass the data belongs to.  With the following properties:
901  * @param {Integer} [config.dataClass.id] The row id of the DataClass.
902  * @param {String} [config.dataClass.name] The name of the DataClass.
903  *
904  * @example
905  * // To perform a file upload over HTTP:
906  * <form id="upload-run-form" enctype="multipart/form-data" method="POST">
907  *   <div id="upload-run-button"></div>
908  * </form>
909  * <script type="text/javascript">
910  *    LABKEY.Utils.requiresScript("FileUploadField.js");
911  *    // Optional - specify a protocolId so that the Exp.Data object is assigned the related LSID namespace.
912  *    var url = LABKEY.ActionURL.buildURL("assay", "assayFileUpload", LABKEY.ActionURL.getContainer(), { protocolId: 50 });
913  *    Ext.onReady(function() {
914  *       var form = new Ext.form.BasicForm(
915  *       Ext.get("upload-run-form"), {
916  *          fileUpload: true,
917  *          frame: false,
918  *          url: url,
919  *          listeners: {
920  *             actioncomplete : function (form, action) {
921  *                alert('Upload successful!');
922  *                var data = new LABKEY.Exp.Data(action.result);
923  *
924  *                // now add the data as a dataInput to a LABKEY.Exp.Run
925  *                var run = new LABKEY.Exp.Run();
926  *                run.name = data.name;
927  *                run.dataInputs = [ data ];
928  *
929  *                // add the new run to a LABKEY.Exp.Batch object and
930  *                // fetch the parsed file contents from the data object
931  *                // using the LABKEY.Exp.Data#getContent() method.
932  *             },
933  *             actionfailed: function (form, action) {
934  *                alert('Upload failed!');
935  *             }
936  *          }
937  *       });
938  *
939  *       var uploadField = new Ext.form.FileUploadField({
940  *          id: "upload-run-field",
941  *          renderTo: "upload-run-button",
942  *          buttonText: "Upload Data...",
943  *          buttonOnly: true,
944  *          buttonCfg: { cls: "labkey-button" },
945  *          listeners: {
946  *             "fileselected": function (fb, v) {
947  *                form.submit();
948  *             }
949  *          }
950  *       });
951  *    });
952  * </script>
953  *
954  * // Or, to upload the contents of a JavaScript string as a file:
955  * <script type="text/javascript">
956  * Ext.onReady(function() {
957  *    LABKEY.Ajax.request({
958  *      url: LABKEY.ActionURL.buildURL("assay", "assayFileUpload"),
959  *      params: { fileName: 'test.txt', fileContent: 'Some text!' },
960  *      success: function(response, options) {
961  *         var data = new LABKEY.Exp.Data(Ext.util.JSON.decode(response.responseText));
962  *
963  *         // now add the data as a dataInput to a LABKEY.Exp.Run
964  *         var run = new LABKEY.Exp.Run();
965  *         run.name = data.name;
966  *         run.dataInputs = [ data ];
967  *
968  *         // add the new run to a LABKEY.Exp.Batch object here
969  *      }
970  *    });
971  *  });
972  *
973  * </script>
974  */
975 LABKEY.Exp.Data = function (config) {
976     LABKEY.Exp.ProtocolOutput.call(this, config);
977     config = config || {};
978 
979     this.dataType = config.dataType;
980     this.dataFileURL = config.dataFileURL;
981     this.dataClass = config.dataClass;
982     if (config.pipelinePath)
983         this.pipelinePath = config.pipelinePath;
984     if (config.role)
985         this.role = config.role;
986 };
987 LABKEY.Exp.Data.prototype = new LABKEY.Exp.ProtocolOutput;
988 LABKEY.Exp.Data.prototype.constructor = LABKEY.Exp.Data;
989 
990 /**
991  * Retrieves the contents of the data object from the server.
992  * @param config An object that contains the following configuration parameters
993  * @param {object} [config.scope] A scoping object for the success and error callback functions (default to this).
994  * @param {function} config.success The function to call when the function finishes successfully.
995  * This function will be called with the parameters:
996  * <ul>
997  * <li><b>content</b> The type of the content varies based on the format requested.
998  * <li><b>format</b> The format used in the request
999  * <li><b>response</b> The original response
1000  * </ul>
1001  * @param {function} [config.failure] The function to call if this function encounters an error.
1002  * This function will be called with the following parameters:
1003  * <ul>
1004  * <li><b>errorInfo:</b> An object with a property called "exception," which contains the error message.</li>
1005  * <li><b>format</b> The format used in the request
1006  * <li><b>response</b> The original response
1007  * </ul>
1008  * @param {String} [config.format] How to format the content. Defaults to plaintext, supported for text/* MIME types,
1009  * including .html, .xml, .tsv, .txt, and .csv. Use 'jsonTSV' to get a JSON version of the .xls, .tsv, .or .csv
1010  * files, the structure of which matches the argument to convertToExcel in {@link LABKEY.Utils}.
1011  * <ul>
1012  * <li><b>fileName:</b> the name of the file</li>
1013  * <li><b>sheets:</b> an array of the sheets in the file. Text file types will have a single sheet named 'flat'.
1014  * <ul><li><b>name:</b> the name of the sheet</li>
1015  *     <li><b>values:</b> two-dimensional array of all the cells in the worksheet. First array index is row, second is column</li>
1016  * </ul>
1017  * </ul>
1018  * <br/>Use 'jsonTSVExtended' to get include metadata in the 2D array of cells.
1019  * Text file types will not supply additional metadata but populate the 'value' attribute in the map.
1020  * Excel files will include:
1021  * <ul>
1022  * <li><b>value:</b> the string, boolean, date, or number in the cell</li>
1023  * <li><b>timeOnly:</b> whether the date part should be ignored for dates</li>
1024  * <li><b>formatString:</b> the Java format string to be used to render the value for dates and numbers</li>
1025  * <li><b>formattedValue:</b> the formatted string for that value for all value types</li>
1026  * <li><b>error:</b> true if this cell has an error</li>
1027  * <li><b>formula:</b> if the cell's value is specified by a formula, the text of the formula</li>
1028  * </ul>
1029  * <br/>Use 'jsonTSVIgnoreTypes' to always return string values for all cells, regardless of type.
1030  * <br/>
1031  * An example of the results for a request for 'jsonTsv' format:
1032  * <pre>
1033  * {
1034 "sheets": [
1035     {
1036         "name": "Sheet1",
1037         "data": [
1038             [
1039                 "StringColumn",
1040                 "DateColumn"
1041             ],
1042             [
1043                 "Hello",
1044                 "16 May 2009 17:00:00"
1045             ],
1046             [
1047                 "world",
1048                 "12/21/2008 08:45AM"
1049             ]
1050         ]
1051     },
1052     {
1053         "name": "Sheet2",
1054         "data": [
1055             ["NumberColumn"],
1056             [55.44],
1057             [100.34],
1058             [-1]
1059         ]
1060     },
1061     {
1062         "name": "Sheet3",
1063         "data": []
1064     }
1065 ],
1066 "fileName": "SimpleExcelFile.xls"
1067 }</pre>
1068  <br/>
1069  An example of the same file in the 'jsonTSVExtended' format:
1070  <pre>
1071  * {
1072 "sheets": [
1073     {
1074         "name": "Sheet1",
1075         "data": [
1076             [
1077                 {
1078                     "value": "StringColumn",
1079                     "formattedValue": "StringColumn"
1080                 },
1081                 {
1082                     "value": "DateColumn",
1083                     "formattedValue": "DateColumn"
1084                 }
1085             ],
1086             [
1087                 {
1088                     "value": "Hello",
1089                     "formattedValue": "Hello"
1090                 },
1091                 {
1092                     "formatString": "MMMM d, yyyy",
1093                     "value": "16 May 2009 17:00:00",
1094                     "timeOnly": false,
1095                     "formattedValue": "May 17, 2009"
1096                 }
1097             ],
1098             [
1099                 {
1100                     "value": "world",
1101                     "formattedValue": "world"
1102                 },
1103                  {
1104                      "formatString": "M/d/yy h:mm a",
1105                      "value": "21 Dec 2008 19:31:00",
1106                      "timeOnly": false,
1107                      "formattedValue": "12/21/08 7:31 PM"
1108                  }
1109             ]
1110         ]
1111     },
1112     {
1113         "name": "Sheet2",
1114         "data": [
1115             [{
1116                 "value": "NumberColumn",
1117                 "formattedValue": "NumberColumn"
1118             }],
1119             [{
1120                 "formatString": "$#,##0.00",
1121                 "value": 55.44,
1122                 "formattedValue": "$55.44"
1123             }],
1124             [{
1125                 "value": 100.34,
1126                 "formattedValue": "100.34"
1127             }],
1128             [{
1129                 "value": -1,
1130                 "formattedValue": "-1"
1131             }]
1132         ]
1133     },
1134     {
1135         "name": "Sheet3",
1136         "data": []
1137     }
1138 ],
1139 "fileName": "SimpleExcelFile.xls"
1140 }
1141  </pre>
1142  *
1143  */
1144 LABKEY.Exp.Data.prototype.getContent = function(config)
1145 {
1146     if(!LABKEY.Utils.getOnSuccess(config))
1147     {
1148         alert("You must specify a callback function in config.success when calling LABKEY.Exp.Data.getContent()!");
1149         return;
1150     }
1151 
1152     function getSuccessCallbackWrapper(fn, format, scope)
1153     {
1154         return function(response)
1155         {
1156             //ensure response is JSON before trying to decode
1157             var content = null;
1158             if(response && response.getResponseHeader && response.getResponseHeader('Content-Type')
1159                     && response.getResponseHeader('Content-Type').indexOf('application/json') >= 0)
1160             {
1161                 content = LABKEY.Utils.decode(response.responseText);
1162             }
1163             else
1164             {
1165                 content = response.responseText;
1166             }
1167 
1168             if(fn)
1169                 fn.call(scope || this, content, format, response);
1170         };
1171     }
1172 
1173     LABKEY.Ajax.request(
1174     {
1175         url : LABKEY.ActionURL.buildURL("experiment", "showFile"),
1176         method : 'GET',
1177         params : { rowId : this.id, format: config.format },
1178         success: getSuccessCallbackWrapper(LABKEY.Utils.getOnSuccess(config), config.format, config.scope),
1179         failure: LABKEY.Utils.getCallbackWrapper(LABKEY.Utils.getOnFailure(config), config.scope, true)
1180     });
1181 
1182 };
1183 
1184 
1185