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 Assay static class to retrieve read-only assay definitions.
 22  *            <p>Additional Documentation:
 23  *              <ul>
 24  *                  <li><a href="https://www.labkey.org/wiki/home/Documentation/page.view?name=createDatasetViaAssay">LabKey Assays</a></li>
 25  *              </ul>
 26  *           </p>
 27  * @see LABKEY.Experiment
 28  */
 29 LABKEY.Assay = new function()
 30 {
 31     function getAssays(config)
 32     {
 33         //check for old-style separate arguments
 34         if(arguments.length > 1) {
 35             config = {
 36                 success: arguments[0],
 37                 failure: arguments[1],
 38                 parameters: arguments[2],
 39                 containerPath: arguments[3]
 40             };
 41         }
 42 
 43         moveParameter(config, "id");
 44         moveParameter(config, "type");
 45         moveParameter(config, "name");
 46 
 47         LABKEY.Ajax.request({
 48             url : LABKEY.ActionURL.buildURL("assay", "assayList", config.containerPath),
 49             method : 'POST',
 50             success: LABKEY.Utils.getOnSuccess(config),
 51             failure: LABKEY.Utils.getOnFailure(config),
 52             scope: config.scope || this,
 53             jsonData : config.parameters,
 54             headers : {
 55                 'Content-Type' : 'application/json'
 56             }
 57         });
 58     }
 59 
 60     function getSuccessCallbackWrapper(successCallback, scope)
 61     {
 62         return LABKEY.Utils.getCallbackWrapper(function(data, response){
 63             if(successCallback)
 64                 successCallback.call(this, data.definitions, response);
 65         }, (scope || this));
 66     }
 67 
 68     function moveParameter(config, param)
 69     {
 70         if (!config.parameters) config.parameters = {};
 71         if (config[param])
 72         {
 73             config.parameters[param] = config[param];
 74             delete config[param];
 75         }
 76     }
 77 
 78     /** @scope LABKEY.Assay */
 79     return {
 80 
 81 	/**
 82 	* Gets all assays.
 83 	* @param {Function} config.success Required. Function called when the
 84 			"getAll" function executes successfully.  Will be called with the argument: 
 85 			{@link LABKEY.Assay.AssayDesign[]}.
 86     * @param {Object} config An object which contains the following configuration properties.
 87 	* @param {Object} [config.scope] The scope to be used for the success and failure callbacks
 88     * @param {Function} [config.failure] Function called when execution of the "getAll" function fails.
 89 	* @param {String} [config.containerPath] The container path in which the requested Assays are defined.
 90 	*       If not supplied, the current container path will be used.
 91 	* @example Example:
 92 <pre name="code" class="xml">
 93 <script type="text/javascript">
 94 	function successHandler(assayArray) 
 95 	{ 
 96 		var html = ''; 
 97 		for (var defIndex = 0; defIndex < assayArray.length; defIndex ++) 
 98 		{ 
 99 			var definition = assayArray[defIndex ]; 
100 			html += '<b>' + definition.type + '</b>: ' 
101 				+ definition.name + '<br>'; 
102 			for (var domain in definition.domains) 
103 			{ 
104 				html += '   ' + domain + '<br>'; 
105 				var properties = definition.domains[domain]; 
106 				for (var propertyIndex = 0; propertyIndex 
107 					< properties.length; propertyIndex++) 
108 				{ 
109 					var property = properties[propertyIndex]; 
110 					html += '      ' + property.name + 
111 						' - ' + property.typeName + '<br>'; 
112 				} 
113 			} 
114 		} 
115 		document.getElementById('testDiv').innerHTML = html; 
116 	} 
117 
118 	function errorHandler(error) 
119 	{ 
120 		alert('An error occurred retrieving data.'); 
121 	}
122 	
123 	LABKEY.Assay.getAll({success: successHandler, failure: errorHandler});
124 </script>
125 <div id='testDiv'>Loading...</div>
126 </pre>
127 	  * @see LABKEY.Assay.AssayDesign
128 	  */
129         getAll : function(config)
130         {
131             if(arguments.length > 1) {
132                 config = {
133                     success: arguments[0],
134                     failure: arguments[1],
135                     parameters: {},
136                     containerPath: arguments[2]
137                 };
138             }
139 
140             config.success = getSuccessCallbackWrapper(LABKEY.Utils.getOnSuccess(config), config.scope);
141             getAssays(config);
142         },
143 	  /**
144 	  * Gets an assay by name.
145 	  * @param {Function(LABKEY.Assay.AssayDesign[])} config.success Function called when the "getByName" function executes successfully.
146       * @param {Object} config An object which contains the following configuration properties.
147 	  * @param {Function} [config.failure] Function called when execution of the "getByName" function fails.
148 	  * @param {Object} [config.scope] The scope to be used for the success and failure callbacks
149       * @param {String} [config.name] String name of the assay.
150 	  * @param {String} [config.containerPath] The container path in which the requested Assay is defined.
151 	  *       If not supplied, the current container path will be used.
152 	  * @see LABKEY.Assay.AssayDesign
153 	  */
154         getByName : function(config)
155         {
156             if(arguments.length > 1) {
157                 config = {
158                     success: arguments[0],
159                     failure: arguments[1],
160                     parameters: { name: arguments[2] },
161                     containerPath: arguments[3]
162                 };
163             }
164 
165             moveParameter(config, "name");
166             config.success = getSuccessCallbackWrapper(LABKEY.Utils.getOnSuccess(config), config.scope);
167             getAssays(config);
168         },
169 
170 	  /**
171 	  * Gets an assay by type.
172 	  * @param {Function(LABKEY.Assay.AssayDesign[])} config.success Function called
173 				when the "getByType" function executes successfully.
174       * @param {Object} config An object which contains the following configuration properties.
175 	  * @param {Function} [config.failure] Function called when execution of the "getByType" function fails.
176 	  * @param {Object} [config.scope] The scope to be used for the success and failure callbacks
177       * @param {String} config.type String name of the assay type.  "ELISpot", for example.
178 	  * @param {String} [config.containerPath] The container path in which the requested Assays are defined.
179 	  *       If not supplied, the current container path will be used.
180  	  * @see LABKEY.Assay.AssayDesign
181 	  */
182         getByType : function(config)
183         {
184             if(arguments.length > 1) {
185                 config = {
186                     success: arguments[0],
187                     failure: arguments[1],
188                     parameters: { type: arguments[2] },
189                     containerPath: arguments[3]
190                 };
191             }
192 
193             moveParameter(config, "type");
194             config.success = getSuccessCallbackWrapper(LABKEY.Utils.getOnSuccess(config), config.scope);
195             getAssays(config);
196         },
197 
198 	 /**
199 	 * Gets an assay by its ID.
200 	 * @param {Function(LABKEY.Assay.AssayDesign[])} config.success Function called
201 				when the "getById" function executes successfully.
202      * @param {Object} config An object which contains the following configuration properties.
203 	 * @param {Function} [config.failure] Function called when execution of the "getById" function fails.
204 	 * @param {Object} [config.scope] The scope to be used for the success and failure callbacks
205      * @param {Integer} config.id Unique integer ID for the assay.
206 	 * @param {String} [config.containerPath] The container path in which the requested Assay is defined.
207 	 *       If not supplied, the current container path will be used.
208 	 * @see LABKEY.Assay.AssayDesign
209 	 */
210         getById : function(config)
211         {
212             if(arguments.length > 1) {
213                 config = {
214                     success: arguments[0],
215                     failure: arguments[1],
216                     parameters: { id: arguments[2] },
217                     containerPath: arguments[3]
218                 };
219             }
220 
221             moveParameter(config, "id");
222             config.success = getSuccessCallbackWrapper(LABKEY.Utils.getOnSuccess(config), config.scope);
223             getAssays(config);
224         },
225 
226 
227         /**
228         * Select NAb assay data from an assay folder.
229         * @param {Object} config An object which contains the following configuration properties.
230          * @param {String} config.assayName  The name of the NAb assay design for which runs are to be retrieved.
231          * @param {Boolean} [config.includeStats]  Whether or not statistics (standard deviation, max, min, etc.) should
232          * be returned with calculations and well data.
233          * @param {Boolean} [config.includeWells]  Whether well-level data should be included in the response.
234          * @param {Boolean} [config.calculateNeut]  Whether neutralization should be calculated on the server.
235          * @param {Boolean} [config.includeFitParameters]  Whether the parameters used in the neutralization curve fitting calculation
236          * should be included in the response.
237         * @param {Function} config.success
238                 Function called when the "getNAbRuns" function executes successfully.
239                 This function will be called with the following arguments:
240                 <ul>
241                     <li>runs: an array of NAb run objects</li>
242                     <li>options: the options used for the AJAX request</li>
243                     <li>responseObj: the XMLHttpResponseObject instance used to make the AJAX request</li>
244                 </ul>
245         * @param {Function} [config.failure] Function called when execution of the "getNAbRuns" function fails.
246          *       This function will be called with the following arguments:
247  				<ul>
248  				    <li><b>errorInfo:</b> an object describing the error with the following fields:
249                          <ul>
250                              <li><b>exception:</b> the exception message</li>
251                              <li><b>exceptionClass:</b> the Java class of the exception thrown on the server</li>
252                              <li><b>stackTrace:</b> the Java stack trace at the point when the exception occurred</li>
253                          </ul>
254                      </li>
255                      <li><b>responseObj:</b> the XMLHttpResponseObject instance used to make the AJAX request</li>
256  				    <li><b>options:</b> the options used for the AJAX request</li>
257  				</ul>
258         *
259         * @param {Array} [config.filterArray] Array of objects created by {@link LABKEY.Filter.create}.
260         * @param {String} [config.sort]  String description of the sort.  It includes the column names
261         *       listed in the URL of a sorted data region (with an optional minus prefix to indicate
262         *       descending order). In the case of a multi-column sort, up to three column names can be
263         *       included, separated by commas.
264         * @param {String} [config.containerPath] The path to the container in which the schema and query are defined,
265         *       if different than the current container. If not supplied, the current container's path will be used.
266         * @param {Integer} [config.maxRows] The maximum number of runs to return from the server (defaults to 100).
267         *        If you want to return all possible rows, set this config property to -1.
268         * @param {Integer} [config.offset] The index of the first row to return from the server (defaults to 0).
269         *        Use this along with the maxRows config property to request pages of data.
270         * @param {Integer} [config.timeout] The maximum number of milliseconds to allow for this operation before
271         *       generating a timeout error (defaults to 30000).
272         */
273         getNAbRuns : function(config)
274         {
275             var dataObject = {};
276 
277             LABKEY.Utils.merge(dataObject, config);
278             if (config.sort)
279                 dataObject['query.sort'] = config.sort;
280             if(config.offset)
281                 dataObject['query.offset'] = config.offset;
282             if(config.maxRows)
283             {
284                 if(config.maxRows < 0)
285                     dataObject['query.showRows'] = "all";
286                 else
287                     dataObject['query.maxRows'] = config.maxRows;
288             }
289 
290             LABKEY.Filter.appendFilterParams(dataObject, config.filterArray);
291 
292             var successCallback = LABKEY.Utils.getOnSuccess(config);
293 
294             var requestConfig = {
295                 url : LABKEY.ActionURL.buildURL('nabassay', 'getNabRuns', config.containerPath),
296                 method : 'GET',
297                 success: LABKEY.Utils.getCallbackWrapper(function(data, response){
298                     if(successCallback)
299                         successCallback.call(config.scope, data.runs);
300                 }, this),
301                 failure: LABKEY.Utils.getCallbackWrapper(LABKEY.Utils.getOnFailure(config), config.scope, true),
302                 params : dataObject
303             };
304 
305             if (LABKEY.Utils.isDefined(config.timeout))
306                 requestConfig.timeout = config.timeout;
307 
308             LABKEY.Ajax.request(requestConfig);
309         },
310 
311 
312         /**
313         * Select detailed NAb information for runs with summary data that has been copied to a study folder.  Note that this
314          * method must be executed against the study folder containing the copied NAb summary data.
315          * @param {Object} config An object which contains the following configuration properties.
316          * @param {Array} config.objectIds The object Ids for the NAb data rows that have been copied to the study.
317          * @param {Boolean} [config.includeStats]  Whether or not statistics (standard deviation, max, min, etc.) should
318          * be returned with calculations and well data.
319          * @param {Boolean} [config.includeWells]  Whether well-level data should be included in the response.
320          * @param {Boolean} [config.calculateNeut]  Whether neutralization should be calculated on the server.
321          * @param {Boolean} [config.includeFitParameters]  Whether the parameters used in the neutralization curve fitting calculation
322          * should be included in the response.
323          * @param {String} [config.containerPath] The path to the study container containing the NAb summary,
324          *       if different than the current container. If not supplied, the current container's path will be used.
325          * @param {Function} config.success
326                 Function called when the "getStudyNabRuns" function executes successfully.
327                 This function will be called with the following arguments:
328                 <ul>
329                     <li>runs: an array of NAb run objects</li>
330                 </ul>
331         * @param {Function} [config.failure] Function called when execution of the "getStudyNabRuns" function fails.
332          *       This function will be called with the following arguments:
333  				<ul>
334  				    <li><b>errorInfo:</b> an object describing the error with the following fields:
335                          <ul>
336                              <li><b>exception:</b> the exception message</li>
337                              <li><b>exceptionClass:</b> the Java class of the exception thrown on the server</li>
338                              <li><b>stackTrace:</b> the Java stack trace at the point when the exception occurred</li>
339                          </ul>
340                      </li>
341                      <li><b>responseObj:</b> the XMLHttpResponseObject instance used to make the AJAX request</li>
342  				    <li><b>options:</b> the options used for the AJAX request</li>
343  				</ul>
344         * @param {Integer} [config.timeout] The maximum number of milliseconds to allow for this operation before
345         *       generating a timeout error (defaults to 30000).
346         */
347         getStudyNabRuns : function(config)
348         {
349             var dataObject = {};
350 
351             LABKEY.Utils.merge(dataObject, config);
352 
353             var successCallback = LABKEY.Utils.getOnSuccess(config);
354 
355             var requestConfig = {
356                 url : LABKEY.ActionURL.buildURL('nabassay', 'getStudyNabRuns', config.containerPath),
357                 method : 'GET',
358                 success: LABKEY.Utils.getCallbackWrapper(function(data, response){
359                     if(successCallback)
360                         successCallback.call(config.scope, data.runs);
361                 }, this),
362                 failure: LABKEY.Utils.getCallbackWrapper(LABKEY.Utils.getOnFailure(config) || LABKEY.Utils.displayAjaxErrorResponse, config.scope, true),
363                 params : dataObject
364             };
365 
366             if (LABKEY.Utils.isDefined(config.timeout))
367                 requestConfig.timeout = config.timeout;
368 
369             LABKEY.Ajax.request(requestConfig);
370         },
371 
372         /**
373         * Retrieve the URL of an image that contains a graph of dilution curves for NAb results that have been copied to a study.
374          * Note that this method must be executed against the study folder containing the copied NAb summary data.
375          * @param {Object} config An object which contains the following configuration properties.
376          * @param {Array} config.objectIds The object Ids for the NAb data rows that have been copied to the study.
377          * This method will ignore requests to graph any object IDs that the current user does not have permission to view.
378          * @param {String} config.captionColumn The data column that should be used for per-specimen captions.  If the column
379          * doesn't exist, or if it has a null value, a sensible default will be chosen, generally specimen ID or participant/visit.
380          * @param {String} [config.chartTitle] The desired title for the chart. Defaults to no title.
381          * @param {String} [config.fitType] Allowable values are FIVE_PARAMETER, FOUR_PARAMETER, and POLYNOMIAL.
382          * Defaults to FIVE_PARAMETER.
383          * @param {String} [config.height] Desired height of the graph image in pixels. Defaults to 300.
384          * @param {String} [config.width] Desired width of the graph image in pixels. Defaults to 425.
385          * @param {String} [config.containerPath] The path to the study container containing the NAb summary data,
386          *       if different than the current container. If not supplied, the current container's path will be used.
387          * @param {Function} config.success
388                 Function called when the "getStudyNabGraphURL" function executes successfully.
389                 This function will be called with the following arguments:
390                 <ul>
391                     <li>result: an object with the following properties:
392                         <ul>
393                          <li>url: a string URL of the dilution curve graph.</li>
394                          <li>objectIds: an array containing the IDs of the samples that were successfully graphed.</li>
395                         </ul>
396                     </li>
397                     <li>responseObj: the XMLHttpResponseObject instance used to make the AJAX request</li>
398                     <li>options: the options used for the AJAX request</li>
399                 </ul>
400         * @param {Function} [config.failure] Function called when execution of the "getStudyNabGraphURL" function fails.
401          *       This function will be called with the following arguments:
402  				<ul>
403  				    <li><b>errorInfo:</b> an object describing the error with the following fields:
404                          <ul>
405                              <li><b>exception:</b> the exception message</li>
406                              <li><b>exceptionClass:</b> the Java class of the exception thrown on the server</li>
407                              <li><b>stackTrace:</b> the Java stack trace at the point when the exception occurred</li>
408                          </ul>
409                      </li>
410                      <li><b>responseObj:</b> the XMLHttpResponseObject instance used to make the AJAX request</li>
411  				    <li><b>options:</b> the options used for the AJAX request</li>
412  				</ul>
413         * @param {Integer} [config.timeout] The maximum number of milliseconds to allow for this operation before
414         *       generating a timeout error (defaults to 30000).
415         * @example Example:
416 <pre name="code" class="xml">
417 <script type="text/javascript">
418     function showGraph(data)
419     {
420         var el = document.getElementById("graphDiv");
421         if (data.objectIds && data.objectIds.length > 0)
422             el.innerHTML = '<img src=\"' + data.url + '\">';
423         else
424             el.innerHTML = 'No graph available.  Insufficient permissions, ' +
425                            'or no matching results were found.';
426     }
427 
428     function initiateGraph(ids)
429     {
430         LABKEY.Assay.getStudyNabGraphURL({
431             objectIds: ids,
432             success: showGraph,
433             captionColumn: 'VirusName',
434             chartTitle: 'My NAb Chart',
435             height: 500,
436             width: 700,
437             fitType: 'FOUR_PARAMETER'
438         });
439     }
440 
441     Ext.onReady(initiateGraph([185, 165]));
442 </script>
443 <div id="graphDiv">
444 </pre>
445 
446         */
447         getStudyNabGraphURL : function(config)
448         {
449             var parameters = {};
450 
451             LABKEY.Utils.applyTranslated(parameters, config, {objectIds: 'id'}, true, false);
452 
453             var requestConfig = {
454                 url : LABKEY.ActionURL.buildURL('nabassay', 'getStudyNabGraphURL', config.containerPath),
455                 method : 'GET',
456                 success: LABKEY.Utils.getCallbackWrapper(LABKEY.Utils.getOnSuccess(config), config.scope, false),
457                 failure: LABKEY.Utils.getCallbackWrapper(LABKEY.Utils.getOnFailure(config) || LABKEY.Utils.displayAjaxErrorResponse, config.scope, true),
458                 params : parameters
459             };
460 
461             if(LABKEY.Utils.isDefined(config.timeout))
462                 requestConfig.timeout = config.timeout;
463 
464             LABKEY.Ajax.request(requestConfig);
465         },
466 
467         /**
468          * Create an assay run and import results.
469          *
470          * @param {Number} config.assayId The assay protocol id.
471          * @param {String} [config.containerPath] The path to the container in which the assay run will be imported,
472          *       if different than the current container. If not supplied, the current container's path will be used.
473          * @param {String} [config.name] The name of a run to create. If not provided, the run will be given the same name as the uploaded file or "[Untitled]".
474          * @param {String} [config.comments] Run comments.
475          * @param {Object} [config.properties] JSON formatted run properties.
476          * @param {Number} [config.batchId] The id of an existing {LABKEY.Exp.RunGroup} to add this run into.
477          * @param {Object} [config.batchProperties] JSON formatted batch properties.
478          * Only used if batchId is not provided when creating a new batch.
479          * @param {Array} config.files Array of <a href='https://developer.mozilla.org/en-US/docs/DOM/File'><code>File</code></a> objects
480          * or form file input elements to import.
481          * @param {Function} config.success The success callback function will be called with the following arguments:
482          * <ul>
483          *     <li><b>json</b>: The success response object contains two properties:
484          *         <ul>
485          *             <li><b>success</b>: true</li>
486          *             <li><b>successurl</b>: The url to browse the newly imported assay run.</li>
487          *             <li><b>assayId</b>: The assay id.</li>
488          *             <li><b>batchId</b>: The previously existing or newly created batch id.</li>
489          *             <li><b>runId</b>: The newly created run id.</li>
490          *         </ul>
491          *     </li>
492          *     <li><b>response</b>: The XMLHttpResponseObject used to submit the request.</li>
493          * </ul>
494          * @param {Function} config.failure The error callback function will be called with the following arguments:
495          * <ul>
496          *     <li><b>errorInfo:</b> an object describing the error with the following fields:
497          *         <ul>
498          *             <li><b>exception:</b> the exception message</li>
499          *             <li><b>exceptionClass:</b> the Java class of the exception thrown on the server</li>
500          *             <li><b>stackTrace:</b> the Java stack trace at the point when the exception occurred</li>
501          *         </ul>
502          *     </li>
503          * <li><b>response:</b> the XMLHttpResponseObject used to submit the request.</li>
504          *
505          * @example Here is an example of retrieving one or more File objects from a form <code><input></code>
506          * element and submitting them together to create a new run.
507          * <input id='myfiles' type='file' multiple>
508          * <a href='#' onclick='doSubmit()'>Submit</a>
509          * <script>
510          *     function doSubmit() {
511          *         LABKEY.Assay.importRun({
512          *             assayId: 3,
513          *             name: "new run",
514          *             properties: {
515          *                 "Run Field": "value"
516          *             },
517          *             batchProperties: {
518          *                 "Batch Field": "value"
519          *             },
520          *             files: [ document.getElementById('myfiles') ],
521          *             success: function (json, response) {
522          *                 window.location = json.successurl;
523          *             },
524          *             failure: error (json, response) {
525          *             }
526          *         });
527          *     }
528          * </script>
529          *
530          * @example Alternatively, you may use an HTML form to submit the multipart/form-data without using the JavaScript API.
531          * <form action='./assay.importRun.api' method='POST' enctype='multipart/form-data'>
532          *     <input name='assayId' type='text' />
533          *     <input name='name' type='text' />
534          *     <input name='file' type='file' />
535          *     <input name='submit' type='submit' />
536          * </form>
537          */
538         importRun : function (config)
539         {
540             if (!window.FormData)
541                 throw new Error("modern browser required");
542 
543             var files = [];
544             if (config.files) {
545                 for (var i = 0; i < config.files.length; i++) {
546                     var f = config.files[i];
547                     if (f instanceof window.File) {
548                         files.push(f);
549                     }
550                     else if (f.tagName == "INPUT") {
551                         for (var j = 0; j < f.files.length; j++) {
552                             files.push(f.files[j]);
553                         }
554                     }
555                 }
556             }
557 
558             if (files.length == 0)
559                 throw new Error("At least one file is required");
560 
561             var formData = new FormData();
562             formData.append("assayId", config.assayId);
563             formData.append("name", config.name);
564             formData.append("comment", config.comment);
565             if (config.batchId)
566                 formData.append("batchId", config.batchId);
567 
568             if (config.properties) {
569                 for (var key in config.properties)
570                     formData.append("properties['" + key + "']", config.properties[key]);
571             }
572 
573             if (config.batchProperties) {
574                 for (var key in config.batchProperties)
575                     formData.append("batchProperties['" + key + "']", config.batchProperties[key]);
576             }
577 
578             formData.append("file", files[0]);
579             for (var i = 1; i < files.length; i++) {
580                 formData.append("file" + i, files[i]);
581             }
582 
583             LABKEY.Ajax.request({
584                 method: 'POST',
585                 url: LABKEY.ActionURL.buildURL("assay", "importRun.api", config.containerPath),
586                 success: LABKEY.Utils.getCallbackWrapper(LABKEY.Utils.getOnSuccess(config), config.scope, false),
587                 failure: LABKEY.Utils.getCallbackWrapper(LABKEY.Utils.getOnFailure(config), config.scope, true),
588                 form: formData
589             });
590         }
591     };
592 };
593 
594 
595 /**
596 * @name LABKEY.Assay.AssayDesign
597 * @class  Static class to describe the shape and fields of an assay.  Each of the {@link LABKEY.Assay}
598             'get' methods passes its success callback function an array of AssayDesigns.
599  *            <p>Additional Documentation:
600  *              <ul>
601  *                  <li><a href="https://www.labkey.org/wiki/home/Documentation/page.view?name=createDatasetViaAssay">LabKey Assays</a></li>
602  *              </ul>
603  *           </p>
604 */
605 
606 /**#@+
607 * @memberOf LABKEY.Assay.AssayDesign#
608 * @field
609 */
610 
611 /**
612 * @name LABKEY.Assay.AssayDesign#name
613 * @description   The name of the assay.
614 * @type String
615 */
616 
617 /**
618 * @name id
619 * @description The unique ID of the assay.
620 * @type Integer
621 */
622 
623 /**
624 * @name importController
625 * @description The name of the controller used for data import
626 * @type String
627 */
628 
629 /**
630 * @name importAction
631 * @description The name of the action used for data import
632 * @type String
633 */
634 
635 /**
636 * @name containerPath
637 * @description The path to the container in which this assay design is saved
638 * @type String
639 */
640 
641 /**
642 * @name type
643 * @description The name of the assay type.  Example:  "ELISpot"
644 * @type String
645 */
646 
647 /**
648 * @name projectLevel
649 * @description  Indicates whether this is a project-level assay.
650 * @type Boolean
651 */
652 
653 /**
654 * @name LABKEY.Assay.AssayDesign#description
655 * @description  Contains the assay description.
656 * @type String
657 */
658 
659 /**
660 * @name plateTemplate
661 * @description  Contains the plate template name if the assay is plate-based.  Undefined otherwise.
662 * @type String
663 */
664 
665 /**
666 * @name domains
667 * @description Map containing name/value pairs.  Typically contains three entries for three domains (batch, run and results).
668   * Each domain is associated with an array of objects that each describe a domain field.
669  *  Each field object has the following properties:
670   *        <ul>
671   *           <li><b>name: </b>The name of the domain field. (string)</li>
672   *           <li><b>typeName: </b> The name of the type of the domain field. (Human readable.) (string)</li>
673   *           <li><b>typeURI: </b> The URI that uniquely identifies the domain field type. (Not human readable.) (string)</li>
674   *           <li><b>label: </b> The domain field label. (string)</li>
675   *           <li><b>description: </b> The domain field description. (string)</li>
676   *           <li><b>formatString: </b> The format string applied to the domain field. (string)</li>
677   *           <li><b>required: </b> Indicates whether a value is required for this domain field. (boolean)</li>
678   *           <li><b>lookup.container: </b> If this domain field is a lookup, lookup.container holds the
679              String path to the lookup container or null if the lookup in the
680              same container.  Undefined otherwise.(string)</li>
681   *           <li><b>lookup.schema: </b> If this domain field object is a lookup, lookup.schema holds the
682             String name of the lookup schema.  Undefined otherwise.(string)</li>
683   *           <li><b>lookup.table: </b> If this domain field object is a lookup, lookup.table holds the String
684             name of the lookup query.  Undefined otherwise. (string)</li>
685   *           <li><b>lookup.keyColumn: </b> The primary key field in target table (string)</li>
686   *           <li><b>lookup.displayColumn: </b>The display column in target table (string)</li>
687   *       </ul>
688 * @type Object
689 */
690 
691 /**#@-*/
692 
693