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) 2010-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 Visualization static class to programmatically retrieve visualization-ready data.  Also allows
 22  * persistence of various visualization types.
 23  */
 24 LABKEY.Query.Visualization = new function() {
 25 
 26     function formatParams(config)
 27     {
 28         var params = {};
 29 
 30         if (config.filters && config.filters.length)
 31         {
 32             params['filters'] = config.filters;
 33         }
 34 
 35         if (config.dateMeasures !== undefined)
 36             params.dateMeasures = config.dateMeasures;
 37 
 38         if (config.allColumns !== undefined)
 39             params.allColumns = config.allColumns;
 40 
 41         if (config.showHidden !== undefined)
 42             params.showHidden = config.showHidden;
 43 
 44         return params;
 45     }
 46 
 47     // Create a thin wrapper around the success callback capable of converting the persisted (string/JSON) visualization
 48     // configuration into a native JavaScript object before handing it off to the caller:
 49     function getVisualizatonConfigConverterCallback(successCallback, scope)
 50     {
 51         return function(data, response, options)
 52         {
 53             //ensure data is JSON before trying to decode
 54             var json = null;
 55             if (data && data.getResponseHeader && data.getResponseHeader('Content-Type')
 56                     && data.getResponseHeader('Content-Type').indexOf('application/json') >= 0)
 57             {
 58                 json = LABKEY.Utils.decode(data.responseText);
 59                 if (json.visualizationConfig)
 60                     json.visualizationConfig = LABKEY.Utils.decode(json.visualizationConfig);
 61             }
 62 
 63             if(successCallback)
 64                 successCallback.call(scope || this, json, response, options);
 65         }
 66     }
 67 
 68     /**
 69      * This is used internally to automatically parse returned JSON and call another success function. It is based off of
 70      * LABKEY.Utils.getCallbackWrapper, however, it will call the createMeasureFn function before calling the success function.
 71      * @param createMeasureFn
 72      * @param fn
 73      * @param scope
 74      */
 75     function getSuccessCallbackWrapper(createMeasureFn, fn, scope)
 76     {
 77         return function(response, options)
 78         {
 79             //ensure response is JSON before trying to decode
 80             var json = null;
 81             var measures = null;
 82             if (response && response.getResponseHeader && response.getResponseHeader('Content-Type')
 83                     && response.getResponseHeader('Content-Type').indexOf('application/json') >= 0)
 84             {
 85                 json = LABKEY.Utils.decode(response.responseText);
 86                 measures = createMeasureFn(json);
 87             }
 88 
 89             if(fn)
 90                 fn.call(scope || this, measures, response);
 91         };
 92     }
 93 
 94     /*-- public methods --*/
 95     /** @scope LABKEY.Query.Visualization */
 96     return {
 97         getTypes : function(config) {
 98 
 99             function createTypes(json)
100             {
101                 if (json.types && json.types.length)
102                 {
103                     // for now just return the raw object array
104                     return json.types;
105                 }
106                 return [];
107             }
108 
109 
110             LABKEY.Ajax.request(
111             {
112                 url : LABKEY.ActionURL.buildURL("visualization", "getVisualizationTypes"),
113                 method : 'GET',
114                 success: getSuccessCallbackWrapper(createTypes, LABKEY.Utils.getOnSuccess(config), config.scope),
115                 failure: LABKEY.Utils.getCallbackWrapper(LABKEY.Utils.getOnFailure(config), config.scope, true)
116             });
117         },
118 
119         /**
120          * Returns the set of plottable measures found in the current container.
121          * @param config An object which contains the following configuration properties.
122          * @param {Array} config.filters An array of {@link LABKEY.Query.Visualization.Filter} objects.
123          * @param {Boolean} [config.dateMeasures] Indicates whether date measures should be returned instead of numeric measures.
124          * Defaults to false.
125          * @param {Function} config.success Function called when execution succeeds. Will be called with one argument:
126          * <ul><li><b>measures</b>: an array of {@link LABKEY.Query.Visualization.Measure} objects.</li>
127          * @param {Function} [config.failure] Function called when execution fails.  Called with the following parameters:
128          * <ul>
129          * <li><b>errorInfo:</b> an object containing detailed error information (may be null)</li>
130          * <li><b>response:</b> The XMLHttpResponse object</li>
131          * </ul>
132          */
133         getMeasures : function(config) {
134 
135             function createMeasures(json)
136             {
137                 var measures = [];
138                 if (json.measures && json.measures.length)
139                 {
140                     for (var i=0; i < json.measures.length; i++)
141                         measures.push(new LABKEY.Query.Visualization.Measure(json.measures[i]));
142                 }
143                 return measures;
144             }
145 
146             var params = formatParams(config);
147 
148             LABKEY.Ajax.request(
149             {
150                 url : config.endpoint || LABKEY.ActionURL.buildURL("visualization", "getMeasures"),
151                 method : 'GET',
152                 success: getSuccessCallbackWrapper(createMeasures, LABKEY.Utils.getOnSuccess(config), config.scope),
153                 failure: LABKEY.Utils.getCallbackWrapper(LABKEY.Utils.getOnFailure(config), config.scope, true),
154                 params : params
155             });
156         },
157 
158         /**
159          * Returns a resultset suitable for visualization based on requested measures and dimensions.
160          * @param config An object which contains the following configuration properties.
161          * @param {Array} config.measures An array of objects with the following structure:
162          * <ul>
163          *      <li><b>measure</b>: Generally an augmented {@link LABKEY.Query.Visualization.Measure}, but can be any object
164          *          with the following properties:
165          *          <ul>
166          *              <li><b>schemaName</b>: The name of the schema containing the query that contains this measure.</li>
167          *              <li><b>queryName</b>: The name of the query containing this measure.</li>
168          *              <li><b>name</b>: The  name of the column containing this measure.</li>
169          *              <li><b>type</b>: The data type of this measure.</li>
170          *              <li><b>isDemographic</b>: Boolean (default false). Indicates whether the measure is Demographic data.</li>
171          *              <li><b>alias</b>: String. </li>
172          *              <li><b>values</b>: Optional.  If provided, results will be filtered to include only the provided values.</li>
173          *              <li><b>allowNullResults</b>: Optional, defaults to true.  If true, this measure will be joined to other measures via an outer join, which will allow results
174          *                  from other measures at timepoints not present for this measure (possibly resulting in null/blank values for this measure).  If false, other measures will be inner joined
175          *                  to this measure, which will produce a dataset without null values for this measure, but which may not include all data from joined measures.</li>
176          *              <li><b>aggregate</b>: See {@link LABKEY.Query.Visualization.Aggregate}.  Required if a 'dimension' property is specified, ignored otherwise.  Indicates
177          *                                    what data should be returned if pivoting by dimension results in multiple underlying values
178          *                                    per series data point.</li>
179          *          </ul>
180          *      </li>
181          *      <li><b>dateOptions</b>: Optional if this measure's axis.timeAxis property is true, ignored otherwise.  Has the following child properties.
182          *                      Either zeroDateCol or ZeroDayVisitTag may be specified, but not both.
183          *          <ul>
184          *              <li><b>dateCol</b>: A measure object (with properties for name, queryName, and
185          *                                              schemaName) of type date specifying the measure date.</li>
186          *              <li><b>zeroDateCol</b>: A measure object (with properties for name, queryName, and
187          *                                              schemaName) of type date specifiying the zero date, used to align data points in terms of days, weeks, or months.</li>
188          *              <li><b>zeroDayVisitTag</b>: String. A VisitTag that will be used to find the ParticipantVisit used to align data points.  </li>
189          *              <li><b>interval</b>: See {@link LABKEY.Query.Visualization.Interval}.  The type of interval that should be
190          *                                              calculated between the measure date and the zero date (if zeroDateCol is specified)
191          *                                              or zero day (if zeroDayVisitTag is specified).</li>
192          *              <li><b>useProtocolDay</b>: Boolean (default true). If true, zeroDayVisitTag uses ParticipantVisit.ProtocolDay to calculate offsets;
193          *                                              if false ParticipantVisit.Day is used.</li>
194          *          </ul>
195          *      </li>
196          *      <li><b>time</b>:
197          *          String: "date" indicates this measure is date-based. "time" indicates it is time-based.
198          *      </li>
199          *      <li><b>dimension</b>:  Used to pivot a resultset into multiple series.  Generally an augmented
200          *          {@link LABKEY.Query.Visualization.Dimension}, but can be any object with the following properties:
201          *          <ul>
202          *              <li><b>name</b>: The name of this dimension.</li>
203          *              <li><b>schemaName</b>: The name of the schema containing the query that contains this dimension.</li>
204          *              <li><b>queryName</b>: The name of the query containing this dimension.</li>
205          *              <li><b>type</b>: The data type of this dimension.</li>
206          *              <li><b>values</b>: Optional.  If provided, results will be filtered to include only the named series.</li>
207          *          </ul>
208          *      </li>
209          * </ul>
210          * @param {Array} [config.sorts] Generally an array of augmented {@link LABKEY.Query.Visualization.Dimension} or {@link LABKEY.Query.Visualization.Measure}
211          * objects, but can be an array of any objects with the following properties:
212          * <ul>
213          *  <li><b>name</b>: The name of this dimension.</li>
214          *  <li><b>schemaName</b>: The name of the schema containing the query that contains this dimension.</li>
215          *  <li><b>queryName</b>: The name of the query containing this dimension.</li>
216          *  <li><b>values</b>: Optional.  If provided, results will be filtered to include only the specified values.</li>
217          * </ul>
218          * @param {Boolean} [config.metaDataOnly] Default false. If true, response will no include the actual data rows, just metadata.
219          * @param {Boolean} [config.joinToFirst] Default false. If true, all measures will be joined to the first measure in the array instead of to the previous measure.
220 
221          * @param {Function} config.success Function called when execution succeeds. Will be called with three arguments:
222          * <ul>
223          *  <li><b>data</b>: the parsed response data ({@link LABKEY.Query.SelectRowsResults})</li>
224          *  <li><b>request</b>: the XMLHttpRequest object</li>
225          *  <li><b>options</b>: a request options object ({@link LABKEY.Query.SelectRowsOptions})</li>
226          * </ul>
227          * @param {Function} [config.failure] Function called when execution fails.  Called with the following parameters:
228          * <ul>
229          * <li><b>errorInfo:</b> an object containing detailed error information (may be null)</li>
230          * <li><b>response:</b> The XMLHttpResponse object</li>
231          * </ul>
232          */
233         getData : function(config) {
234 
235             var params = {
236                 measures : [],
237                 sorts : config.sorts,
238 
239                 // @deprecated -- created for issue 11627
240                 // The filterUrl and filterQuery are used in tandem to apply a filter from a URL. Use the filterArray
241                 // option on each measure to apply filters.
242                 filterQuery: config.filterQuery, // e.g. 'study.Lab Results'
243                 filterUrl: config.filterUrl, // e.g. '/labkey/study/StudyFolder/dataset.view?Dataset.Column%7Egt=value'
244 
245                 limit   : config.limit,
246                 groupBys: config.groupBys,
247                 metaDataOnly: config.metaDataOnly,
248 
249                 // specify that all source queries should join back to the first measure
250                 joinToFirst: config.joinToFirst === true
251             };
252 
253             // clone measures
254             var measure, filters, m, f, asURL, fa;
255             for (m=0; m < config.measures.length; m++)
256             {
257                 var c = config.measures[m];
258 
259                 measure = {
260                     measure: c.measure,
261                     time: c.time
262                 };
263 
264                 if (c.dimension)
265                 {
266                     measure.dimension = c.dimension;
267                 }
268 
269                 if (c.dateOptions)
270                 {
271                     measure.dateOptions = c.dateOptions;
272                 }
273 
274                 if (c.filterArray)
275                 {
276                     measure.filterArray = c.filterArray;
277 
278                     // assume it is an array of LABKEY.Filter instances, convert each filter to it's URL parameter equivalent
279                     filters = [];
280                     for (f=0; f < measure.filterArray.length; f++)
281                     {
282                         fa = measure.filterArray[f];
283                         if (fa && fa.getURLParameterName)
284                         {
285                             asURL = encodeURIComponent(fa.getURLParameterName()) + "=" + encodeURIComponent(fa.getURLParameterValue());
286                             filters.push(asURL);
287                         }
288                     }
289                     measure['filterArray'] = filters;
290                 }
291 
292                 params.measures.push(measure);
293             }
294 
295             // issue 21418: support for parameterized queries
296             var urlParams = {};
297             if (config.parameters)
298             {
299                 for (var propName in config.parameters)
300                 {
301                     if (config.parameters.hasOwnProperty(propName))
302                         urlParams['visualization.param.' + propName] = config.parameters[propName];
303                 }
304             }
305 
306             var endpoint = config.endpoint || LABKEY.ActionURL.buildURL("visualization", "getData", config.containerPath);
307             endpoint += '?' + LABKEY.ActionURL.queryString(urlParams);
308 
309             LABKEY.Ajax.request(
310             {
311                 url : endpoint,
312                 method : 'POST',
313                 success: LABKEY.Utils.getCallbackWrapper(LABKEY.Utils.getOnSuccess(config), config.scope, false),
314                 failure: LABKEY.Utils.getCallbackWrapper(LABKEY.Utils.getOnFailure(config), config.scope, true),
315                 jsonData : params,
316                 headers : {
317                     'Content-Type' : 'application/json'
318                 }
319             });
320         },
321 
322         /**
323          * Saves a visualization for future use.  Saved visualizations appear in the study 'views' webpart.  If the
324          * visualization is scoped to a specific query, it will also appear in the views menu for that query.
325          * @param config An object which contains the following configuration properties.
326          * @param {String} config.name The name this visualization should be saved under.
327          * @param {String} config.type The type of visualization being saved.  Should be an instance of {@link LABKEY.Query.Visualization.Type}.
328          * @param {Object} config.visualizationConfig An arbitrarily complex JavaScript object that contains all information required to
329          * recreate the report.
330          * @param {Boolean} [config.replace] Whether this 'save' call should replace an existing report with the same name.
331          * If false, the call to 'save' will fail if another report with the same name exists.
332          * @param {String} [config.description] A description of the saved report.
333          * @param {String} [config.shared] Boolean indicating whether this report is viewable by all users with read
334          * permissions to the visualization's folder.  If false, only the creating user can see the visualization.  Defaults to true.
335          * @param {Boolean} [config.thumbnailType] String indicating whether a thumbnail should be auto-generated ('AUTO'), no thumbnail
336          * should be saved ('NONE'), or the existing custom thumbnail should be kept ('CUSTOM')
337          * @param {Boolean} [config.iconType] String indicating whether a icon should be auto-generated ('AUTO'), no icon
338          * should be saved ('NONE'), or the existing custom icon should be kept ('CUSTOM')
339          * @param {String} [config.svg] String svg to be used to generate a thumbnail
340          * @param {String} [config.schemaName] Optional, but required if config.queryName is provided.  Allows the visualization to
341          * be scoped to a particular query.  If scoped, this visualization will appear in the 'views' menu for that query.
342          * @param {String} [config.queryName] Optional, but required if config.schemaName is provided.  Allows the visualization to
343          * be scoped to a particular query.  If scoped, this visualization will appear in the 'views' menu for that query.
344          * @param {Function} config.success Function called when execution succeeds. Will be called with one arguments:
345          * <ul>
346          *  <li><b>result</b>: an object with two properties:
347          *      <ul>
348          *          <li><b>name</b>: the name of the saved visualization</li>
349          *          <li><b>visualizationId</b>: a unique integer identifier for this saved visualization</li>
350          *      </ul>
351          *  <li><b>request</b>: the XMLHttpRequest object</li>
352          *  <li><b>options</b>: a request options object</li>
353          *  </ul>
354          * @param {Function} [config.failure] Function called when execution fails.  Called with the following parameters:
355          * <ul>
356          * <li><b>errorInfo:</b> an object containing detailed error information (may be null)</li>
357          * <li><b>response:</b> The XMLHttpResponse object</li>
358          * </ul>
359          */
360         save : function(config)
361         {
362             var params = {
363                 name : config.name,
364                 description : config.description,
365                 json : LABKEY.Utils.encode(config.visualizationConfig),
366                 replace: config.replace,
367                 shared: config.shared,
368                 thumbnailType: config.thumbnailType,
369                 iconType: config.iconType,
370                 svg : config.svg,
371                 type : config.type,
372                 schemaName: config.schemaName,
373                 queryName: config.queryName
374             };
375 
376             LABKEY.Ajax.request(
377             {
378                 url : LABKEY.ActionURL.buildURL("visualization", "saveVisualization"),
379                 method : 'POST',
380                 success: LABKEY.Utils.getCallbackWrapper(LABKEY.Utils.getOnSuccess(config), config.scope, false),
381                 failure: LABKEY.Utils.getCallbackWrapper(LABKEY.Utils.getOnFailure(config), config.scope, true),
382                 jsonData : params,
383                 headers : {
384                     'Content-Type' : 'application/json'
385                 }
386             });
387         },
388 
389         /**
390          * Retrieves a saved visualization.  See {@link LABKEY.Query.Visualization.save}.
391          * @param config An object which contains the following configuration properties.
392          * @param {String} config.name The name this visualization to be retrieved.
393          * @param {String} [config.schemaName] Optional, but required if config.queryName is provided.  Limits the search for
394          * the visualization to a specific schema and query.  Note that visualization names are unique within a container
395          * (regardless of schema and query), so these additional optional parameters are only useful in a small number of circumstances.
396          * @param {String} [config.queryName] Optional, but required if config.schemaName is provided.  Limits the search for
397          * the visualization to a specific schema and query.  Note that visualization names are unique within a container
398          * (regardless of schema and query), so these additional optional parameters are only useful in a small number of circumstances.
399          * @param {Function} config.success Function called when execution succeeds. Will be called with one arguments:
400          * <ul>
401          *  <li><b>result</b>: an object with two properties:
402          *      <ul>
403          *          <li><b>name</b>: The name of the saved visualization</li>
404          *          <li><b>description</b>: The description of the saved visualization</li>
405          *          <li><b>type</b>: The visualization type</li>
406          *          <li><b>schemaName</b>: The schema to which this visualization has been scoped, if any</li>
407          *          <li><b>queryName</b>: The query to which this visualization has been scoped, if any</li>
408          *          <li><b>visualizationConfig</b>: The configuration object provided to {@link LABKEY.Query.Visualization.save}</li>
409          *      </ul>
410          *  </li>
411          *  <li><b>request</b>: the XMLHttpRequest object</li>
412          *  <li><b>options</b>: a request options object</li>
413          * </ul>
414          * @param {Function} [config.failure] Function called when execution fails.  Called with the following parameters:
415          * <ul>
416          * <li><b>errorInfo:</b> an object containing detailed error information (may be null)</li>
417          * <li><b>response:</b> The XMLHttpResponse object</li>
418          * </ul>
419          */
420         get : function(config)
421         {
422             var params = {
423                 reportId: config.reportId,
424                 name : config.name,
425                 schemaName: config.schemaName,
426                 queryName: config.queryName
427             };
428 
429             // Get the standard callback function (last in the success callback chain):
430             var successCallback = LABKEY.Utils.getOnSuccess(config);
431 
432             // wrap the callback to convert the visualizationConfig property from a JSON string into a native javascript object:
433             successCallback = getVisualizatonConfigConverterCallback(successCallback, config.scope);
434 
435             LABKEY.Ajax.request(
436             {
437                 url : LABKEY.ActionURL.buildURL("visualization", "getVisualization"),
438                 method : 'POST',
439                 initialConfig: config,
440                 success: successCallback,
441                 failure: LABKEY.Utils.getCallbackWrapper(LABKEY.Utils.getOnFailure(config), config.scope, true),
442                 jsonData : params,
443                 headers : {
444                     'Content-Type' : 'application/json'
445                 }
446             });
447         },
448 
449         /**
450          * Retrieves a saved visualization based on identifying parameters found on the current URL.  Method returns true or false,
451          * depending on whether the URL contains a saved visualization identifier.  If true, the success or failure callback
452          * function will be called just as with {@link LABKEY.Query.Visualization.get}.  If false, no callbacks will be called.
453          * This method allows callers to use a single method to retrieve saved visualizations, regardless of how they are identified on the URL.
454          * @param config An object which contains the following configuration properties.
455          * @param {Function} config.success Function called when the saved visualization was successfully retrieved. See {@link LABKEY.Query.Visualization.get} for details.
456          * @param {Function} [config.failure] Function called when the saved visualization could not be retrieved.  See {@link LABKEY.Query.Visualization.get} for details.
457          * @return Boolean indicating whether the current URL includes parameters that identify a saved visualization.
458          */
459         getFromUrl : function(config)
460         {
461             var params = config || {};
462 
463             params.success = LABKEY.Utils.getOnSuccess(config);
464             params.failure = LABKEY.Utils.getOnFailure(config);
465 
466             var urlParams = LABKEY.ActionURL.getParameters();
467             var valid = false;
468             if (params.reportId)
469             {
470                 valid = true;
471             }
472             else
473             {
474                 if (urlParams.name)
475                 {
476                     params.name = urlParams.name;
477                     params.schemaName = urlParams.schemaName;
478                     params.queryName = urlParams.queryName;
479                     valid = true;
480                 }
481             }
482 
483             if (valid)
484                 LABKEY.Query.Visualization.get(params);
485             return valid;
486         },
487 
488         getDataFilterFromURL : function()
489         {
490             var urlParams = LABKEY.ActionURL.getParameters();
491             return urlParams['filterUrl'];
492         }
493     };
494 };
495 
496 /**
497  * @deprecated Use {@link LABKEY.Query.Visualization}
498  */
499 LABKEY.Visualization = LABKEY.Query.Visualization;
500 
501 /**
502  * @namespace Visualization Measures are plottable data elements (columns).  They may be of numeric or date types.
503  */
504 LABKEY.Query.Visualization.Measure = function(config) {
505 
506     LABKEY.Utils.apply(this, config);
507 };
508 
509 /**
510  * Returns the name of the query associated with this measure.
511  */
512 LABKEY.Query.Visualization.Measure.prototype.getQueryName = function() {
513     return this.queryName;
514 };
515 /**
516  * Returns the name of the schema associated with this measure.
517  */
518 LABKEY.Query.Visualization.Measure.prototype.getSchemaName = function() {
519     return this.schemaName;
520 };
521 /**
522  * Returns whether this measure is part of a user-defined query (versus a built-in/system-provided query).
523  */
524 LABKEY.Query.Visualization.Measure.prototype.isUserDefined = function() {
525     return this.isUserDefined;
526 };
527 /**
528  * Returns the column name of this measure.
529  */
530 LABKEY.Query.Visualization.Measure.prototype.getName = function() {
531     return this.name;
532 };
533 /**
534  * Returns the label of this measure.
535  */
536 LABKEY.Query.Visualization.Measure.prototype.getLabel = function() {
537     return this.label;
538 };
539 /**
540  * Returns the data types of this measure.
541  */
542 LABKEY.Query.Visualization.Measure.prototype.getType = function() {
543     return this.type;
544 };
545 /**
546  * Returns a description of this measure.
547  */
548 LABKEY.Query.Visualization.Measure.prototype.getDescription = function() {
549     return this.description;
550 };
551 /**
552  * Returns the set of available {@link LABKEY.Query.Visualization.Dimension} objects for this measure.
553  * @param config An object which contains the following configuration properties.
554  * @param config.includeDemographics {Boolean} Applies only to measures from study datsets.
555  * Indicates whether dimensions from demographic datasets should be included
556  * in the returned set.  If false, only dimensions from the measure's query will be returned.
557  * @param {Function} config.success Function called when execution succeeds. Will be called with one argument:
558  <ul>
559  <li><b>values</b>: an array of unique dimension values</li>
560  </ul>
561  * @param {Function} [config.failure] Function called when execution fails.  Called with the following parameters:
562  * <ul>
563  * <li><b>errorInfo:</b> an object containing detailed error information (may be null)</li>
564  * <li><b>response:</b> The XMLHttpResponse object</li>
565  * </ul>
566  */
567 LABKEY.Query.Visualization.Measure.prototype.getDimensions = function(config) {
568 
569     var params = {queryName: this.queryName, schemaName: this.schemaName};
570 
571     if (config.includeDemographics)
572         params['includeDemographics'] = config.includeDemographics;
573 
574     function createDimensions(json)
575     {
576         var dimensions = [];
577         if (json.dimensions && json.dimensions.length)
578         {
579             for (var i=0; i < json.dimensions.length; i++)
580                 dimensions.push(new LABKEY.Query.Visualization.Dimension(json.dimensions[i]));
581         }
582         return dimensions;
583     }
584 
585     LABKEY.Ajax.request(
586             {
587                 url : LABKEY.ActionURL.buildURL("visualization", "getDimensions"),
588                 method : 'GET',
589                 params : params,
590                 success: getSuccessCallbackWrapper(createDimensions, LABKEY.Utils.getOnSuccess(config), config.scope),
591                 failure: LABKEY.Utils.getCallbackWrapper(LABKEY.Utils.getOnFailure(config), config.scope, true)
592             });
593 };
594 
595 /**
596  * @deprecated Use {@link LABKEY.Query.Visualization.Measure}
597  */
598 LABKEY.Visualization.Measure = LABKEY.Query.Visualization.Measure;
599 
600 /**
601  * @namespace Visualization Dimensions are data elements (columns) on which {@link LABKEY.Query.Visualization.Measure} objects
602  *  can be pivoted or transformed.  For example, the 'Analyte Name' dimension may be used to pivit a single 'Result' measure
603  * into one series per Analyte.
604  */
605 LABKEY.Query.Visualization.Dimension = function(config) {
606     LABKEY.Utils.apply(this, config);
607 };
608 /**
609  * Returns the name of the query associated with this dimension.
610  */
611 LABKEY.Query.Visualization.Dimension.getQueryName = function() {
612     return this.queryName;
613 };
614 /**
615  * Returns the name of the schema assocated with this dimension.
616  */
617 LABKEY.Query.Visualization.Dimension.getSchemaName = function() {
618     return this.schemaName;
619 };
620 
621 /**
622  * Returns whether this dimension is part of a user-defined query (versus a built-in/system-provided query).
623  */
624 LABKEY.Query.Visualization.Dimension.isUserDefined = function() {
625     return this.isUserDefined;
626 };
627 
628 /**
629  * Returns the column name of this dimension.
630  */
631 LABKEY.Query.Visualization.Dimension.getName = function() {
632     return this.name;
633 };
634 
635 /**
636  * Returns the label of this dimension.
637  */
638 LABKEY.Query.Visualization.Dimension.getLabel = function() {
639     return this.label;
640 };
641 
642 /**
643  * Returns the data types of this dimension.
644  */
645 LABKEY.Query.Visualization.Dimension.getType = function() {
646     return this.type;
647 };
648 
649 /**
650  * Returns a description of this dimension.
651  */
652 LABKEY.Query.Visualization.Dimension.getDescription = function() {
653     return this.description;
654 };
655 
656 /**
657  * Returns the set of available unique values for this dimension.
658  * @param config An object which contains the following configuration properties.
659  * @param {Function} config.success Function called when execution succeeds. Will be called with one argument:
660  <ul>
661  <li><b>values</b>: an array of unique dimension values</li>
662  </ul>
663  * @param {Function} [config.failure] Function called when execution fails.  Called with the following parameters:
664  * <ul>
665  * <li><b>errorInfo:</b> an object containing detailed error information (may be null)</li>
666  * <li><b>response:</b> The XMLHttpResponse object</li>
667  * </ul>
668  */
669 LABKEY.Query.Visualization.Dimension.getValues = function(config) {
670 
671     var params = {queryName: this.queryName, schemaName: this.schemaName, name: this.name};
672     function createValues(json)
673     {
674         if (json.success && json.values)
675             return json.values;
676         return [];
677     }
678 
679     LABKEY.Ajax.request(
680             {
681                 url : LABKEY.ActionURL.buildURL("visualization", "getDimensionValues"),
682                 method : 'GET',
683                 params : params,
684                 success: getSuccessCallbackWrapper(createValues, LABKEY.Utils.getOnSuccess(config), config.scope),
685                 failure: LABKEY.Utils.getCallbackWrapper(LABKEY.Utils.getOnFailure(config), config.scope, true)
686             });
687 };
688 
689 /**
690  * @deprecated Use {@link LABKEY.Query.Visualization.Dimension}
691  */
692 LABKEY.Visualization.Dimension = LABKEY.Query.Visualization.Dimension;
693 
694 /**
695  * @namespace Visualization Helper class to allow filtering of the measures returned by the
696  * {@link LABKEY.Query.Visualization.getMeasures} method.
697  */
698 LABKEY.Query.Visualization.Filter = new function()
699 {
700     function getURLParameterValue(config)
701     {
702         var params = [config.schemaName];
703 
704         if (config.queryName)
705             params.push(config.queryName);
706         else
707             params.push('~');
708         
709         if (config.queryType)
710             params.push(config.queryType);
711 
712         return params.join('|');
713     }
714 
715     /** @scope LABKEY.Query.Visualization.Filter */
716     return {
717         /**
718          * @namespace Visualization Possible query types for measure filters.  See {@link LABKEY.Query.Visualization.Filter}.
719         */
720         QueryType : {
721             /** Return only queries that are built-in to the server */
722             BUILT_IN : 'builtIn',
723             /** Return only queries that are custom (user defined) */
724             CUSTOM : 'custom',
725             /** Return only datasets */
726             DATASETS : 'datasets',
727             /** Return all queries (both built-in and custom) */
728             ALL : 'all'
729         },
730 
731         /**
732          * Creates a new filter object for use in {@link LABKEY.Query.Visualization.getMeasures}.
733          * @param config An object which contains the following configuration properties.
734          * @param {String} config.schemaName Required.  Only measures from the specified schema will be returned.
735          * @param {String} [config.queryName] If specified, only measures from the specified query will be returned.
736          * @param {Object} [config.queryType] If specified, only measures from the specified query types will be returned
737          * Valid values for queryType are:  {@link LABKEY.Query.Visualization.Filter.QueryType}.ALL, {@link LABKEY.Query.Visualization.Filter.QueryType}.BUILT_IN,
738          * and {@link LABKEY.Query.Visualization.Filter.QueryType}.CUSTOM.  By default, all queries will be returned.
739          */
740         create : function(config)
741         {
742             if (!config.schemaName)
743                 throw new Error("Coding Error!", "You must supply a value for schemaName in your configuration object!");
744             else
745                 return getURLParameterValue(config);
746         }
747     };
748 };
749 
750 /**
751  * @deprecated Use {@link LABKEY.Query.Visualization.Filter}
752  */
753 LABKEY.Visualization.Filter = LABKEY.Query.Visualization.Filter;
754 
755 /**
756  * @namespace Visualization Possible aggregates when pivoting a resultset by a dimension.  See  {@link LABKEY.Query.Visualization.getData}.
757  */
758 LABKEY.Query.Visualization.Aggregate = {
759     /** Calculates a sum/total. */
760     SUM: "SUM",
761     /** Calculates an average. */
762     AVG: "AVG",
763     /** Returns the total number of data points. */
764     COUNT: "COUNT",
765     /** Returns the minimum value. */
766     MIN: "MIN",
767     /** Returns the maximum value. */
768     MAX: "MAX"
769 };
770 
771 /**
772  * @deprecated Use {@link LABKEY.Query.Visualization.Aggregate}
773  */
774 LABKEY.Visualization.Aggregate = LABKEY.Query.Visualization.Aggregate;
775 
776 /**
777  * @namespace Visualization Possible intervals for aligning series in time plots.  See  {@link LABKEY.Query.Visualization.getData}.
778  */
779 LABKEY.Query.Visualization.Interval = {
780     /** Align by the number of days since the zero date. */
781     DAY : "DAY",
782     /** Align by the number of weeks since the zero date. */
783     WEEK : "WEEK",
784     /** Align by the number of months since the zero date. */
785     MONTH : "MONTH",
786     /** Align by the number of years since the zero date. */
787     YEAR: "YEAR"
788 };
789 
790 /**
791  * @deprecated Use {@link LABKEY.Query.Visualization.Interval}
792  */
793 LABKEY.Visualization.Interval = LABKEY.Query.Visualization.Interval;
794 
795 /**
796  * @namespace Visualization A predefined set of visualization types, for use in the config.type property in the
797  * {@link LABKEY.Query.Visualization.save} method.
798  */
799 LABKEY.Query.Visualization.Type = {
800     /**
801      * Plots data over time, aligning different series based on configurable start dates.
802      */
803     TimeChart : 'ReportService.TimeChartReport',
804     /**
805      * Plot types that are not study specific (i.e. Bar, Box, Pie, and Scatter).
806      */
807     GenericChart : 'ReportService.GenericChartReport'
808 };
809 
810 /**
811  * @deprecated Use {@link LABKEY.Query.Visualization.Type}
812  */
813 LABKEY.Visualization.Type = LABKEY.Query.Visualization.Type;
814