1 /*
  2  * Copyright (c) 2007-2016 LabKey Corporation
  3  *
  4  * Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0
  5  */
  6 
  7 // NOTE labkey.js should NOT depend on any external libraries like ExtJS
  8 
  9 if (typeof LABKEY == "undefined")
 10 {
 11     /**
 12      * @namespace Namespace used to encapsulate LabKey core API and utilities.
 13      */
 14     LABKEY = new function()
 15     {
 16         var configs = {
 17             contextPath: "",
 18             DataRegions: {},
 19             devMode: false,
 20             demoMode: false,
 21             dirty: false,
 22             isDocumentClosed: false,
 23             extJsRoot: "ext-3.4.1",
 24             extJsRoot_42: "ext-4.2.1",
 25             extThemeRoot: "labkey-ext-theme",
 26             extThemeName_42: "seattle",
 27             extThemeRoot_42: "ext-theme",
 28             fieldMarker: '@',
 29             hash: 0,
 30             imagePath: "",
 31             requestedCssFiles: {},
 32             requestedScriptFiles: [],
 33             submit: false,
 34             unloadMessage: "You will lose any changes made to this page.",
 35             verbose: false,
 36             widget: {}
 37         };
 38 
 39         // private variables not configurable
 40         var _requestedCssFiles = {};
 41 
 42         // private caching mechanism for script loading
 43         var ScriptCache = function()
 44         {
 45             var cache = {};
 46 
 47             var callbacksOnCache = function(key)
 48             {
 49                 // console.log('calling --', key);
 50                 var cbs = cache[key];
 51 
 52                 // set the cache to hit
 53                 cache[key] = true;
 54 
 55                 // Tell mothership.js to hook event callbacks
 56                 if (LABKEY.Mothership)
 57                 {
 58                     if (key.indexOf(configs.extJsRoot + "/ext-all") == 0)
 59                         LABKEY.Mothership.hookExt3();
 60 
 61                     if (key.indexOf(configs.extJsRoot_42 + "/ext-all") == 0)
 62                         LABKEY.Mothership.hookExt4();
 63                 }
 64 
 65                 // call on the callbacks who have been waiting for this resource
 66                 if (isArray(cbs))
 67                 {
 68                     var cb;
 69                     for (var c=0; c < cbs.length; c++)
 70                     {
 71                         cb = cbs[c];
 72                         handle(cb.fn, cb.scope);
 73                     }
 74                 }
 75             };
 76 
 77             var inCache = function(key)
 78             {
 79                 // console.log('hit --', key);
 80                 return cache[key] === true;
 81             };
 82 
 83             var inFlightCache = function(key)
 84             {
 85                 return isArray(cache[key]);
 86             };
 87 
 88             var loadCache = function(key, cb, s)
 89             {
 90                 // console.log('miss --', key);
 91                 // The value as an array denotes the cache resource is in flight
 92                 if (!cache[key])
 93                     cache[key] = [];
 94 
 95                 if (isFunction(cb))
 96                     cache[key].push({fn: cb, scope: s});
 97             };
 98 
 99             return {
100                 callbacksOnCache: callbacksOnCache,
101                 inCache: inCache,
102                 inFlightCache: inFlightCache,
103                 loadCache: loadCache
104             };
105         };
106 
107         // instance of scripting cache used by public methods
108         var scriptCache = new ScriptCache();
109 
110         // Public Method Definitions
111 
112         var addElemToHead = function(elemName, attributes)
113         {
114             var elem = document.createElement(elemName);
115             for (var a in attributes) {
116                 if (attributes.hasOwnProperty(a)) {
117                     elem[a] = attributes[a];
118                 }
119             }
120             return document.getElementsByTagName("head")[0].appendChild(elem);
121         };
122 
123         var addMarkup = function(html)
124         {
125             if (configs.isDocumentClosed)
126             {
127                 var elem = document.createElement("div");
128                 elem.innerHTML = html;
129                 document.body.appendChild(elem.firstChild);
130             }
131             else
132                 document.write(html);
133         };
134 
135         //private. used to append additional module context objects for AJAXd views
136         var applyModuleContext = function(ctx) {
137             for (var mn in ctx) {
138                 if (ctx.hasOwnProperty(mn)) {
139                     LABKEY.moduleContext[mn.toLowerCase()] = ctx[mn];
140                 }
141             }
142         };
143 
144         var beforeunload = function (dirtyCallback, scope, msg)
145         {
146             return function () {
147                 if (!getSubmit() && (isDirty() || (dirtyCallback && dirtyCallback.call(scope)))) {
148                     return msg || configs.unloadMessage;
149                 }
150             };
151         };
152 
153         var createElement = function(tag, innerHTML, attributes)
154         {
155             var e = document.createElement(tag);
156             if (innerHTML)
157                 e.innerHTML = innerHTML;
158             if (attributes)
159             {
160                 for (var att in attributes)
161                 {
162                     if (attributes.hasOwnProperty(att))
163                     {
164                         try
165                         {
166                             e[att] = attributes[att];
167                         }
168                         catch (x)
169                         {
170                             console.log(x); // e['style'] is read-only in old firefox
171                         }
172                     }
173                 }
174             }
175             return e;
176         };
177 
178         var getModuleContext = function(moduleName) {
179             return LABKEY.moduleContext[moduleName.toLowerCase()];
180         };
181 
182         var getModuleProperty = function(moduleName, property) {
183             var ctx = getModuleContext(moduleName);
184             if (!ctx) {
185                 return null;
186             }
187             return ctx[property];
188         };
189 
190         var getSubmit = function()
191         {
192             return configs.submit;
193         };
194 
195         // simple callback handler that will type check then call with scope
196         var handle = function(callback, scope)
197         {
198             if (isFunction(callback))
199             {
200                 callback.call(scope || this);
201             }
202         };
203 
204         // If we're in demo mode, replace each ID with an equal length string of "*".  This code should match DemoMode.id().
205         var id = function(id)
206         {
207             if (configs.demoMode)
208             {
209                 return new Array(id.length + 1 ).join("*");
210             }
211             else
212             {
213                 return id;
214             }
215         };
216 
217         var init = function(config)
218         {
219             for (var p in config)
220             {
221                 //TODO: we should be trying to seal some of these objects, or at least wrap them to make them harder to manipulate
222                 if (config.hasOwnProperty(p)) {
223                     configs[p] = config[p];
224                     LABKEY[p] = config[p];
225                 }
226             }
227             if ("Security" in LABKEY)
228                 LABKEY.Security.currentUser = LABKEY.user;
229         };
230 
231         var isArray = function(value)
232         {
233             return Object.prototype.toString.call(value) === "[object Array]";
234         };
235 
236         var isBoolean = function(value)
237         {
238             return typeof value === "boolean";
239         };
240 
241         var isDirty = function()
242         {
243             return configs.dirty;
244         };
245 
246         var isFunction = function(value)
247         {
248             return typeof value === "function";
249         };
250 
251         var isLibrary = function(file)
252         {
253             return file && (file.indexOf('.') === -1 || file.indexOf('.lib.xml') > -1);
254         };
255 
256         var loadScripts = function()
257         {
258             configs.isDocumentClosed = true;
259         };
260 
261         var loadedScripts = function()
262         {
263             for (var i=0; i < arguments.length; i++)
264             {
265                 if (isArray(arguments[i]))
266                 {
267                     for (var j=0; j < arguments[i].length; j++)
268                     {
269                         scriptCache.callbacksOnCache(arguments[i][j]);
270                     }
271                 }
272                 else
273                 {
274                     scriptCache.callbacksOnCache(arguments[i]);
275                 }
276             }
277             return true;
278         };
279 
280         var qs = function(params)
281         {
282             if (!params)
283                 return '';
284 
285             var qs = '', and = '', pv, p;
286 
287             for (p in params)
288             {
289                 if (params.hasOwnProperty(p))
290                 {
291                     pv = params[p];
292 
293                     if (pv === null || pv === undefined)
294                         pv = '';
295 
296                     if (isArray(pv))
297                     {
298                         for (var i=0; i < pv.length; i++)
299                         {
300                             qs += and + encodeURIComponent(p) + '=' + encodeURIComponent(pv[i]);
301                             and = '&';
302                         }
303                     }
304                     else
305                     {
306                         qs += and + encodeURIComponent(p) + '=' + encodeURIComponent(pv);
307                         and = '&';
308                     }
309                 }
310             }
311 
312             return qs;
313         };
314 
315         // So as not to confuse with native support for fetch()
316         var _fetch = function(url, params, success, failure)
317         {
318             var xhr = new XMLHttpRequest();
319             var _url = url + (url.indexOf('?') === -1 ? '?' : '&') + qs(params);
320 
321             xhr.onreadystatechange = function()
322             {
323                 if (xhr.readyState === 4)
324                 {
325                     var _success = (xhr.status >= 200 && xhr.status < 300) || xhr.status == 304;
326                     _success ? success(xhr) : failure(xhr);
327                 }
328             };
329 
330             xhr.open('GET', _url, true);
331 
332             xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
333             if (LABKEY.CSRF)
334                 xhr.setRequestHeader('X-LABKEY-CSRF', LABKEY.CSRF);
335 
336             xhr.send(null);
337 
338             return xhr;
339         };
340 
341         var requiresCss = function(file)
342         {
343             if (isArray(file))
344             {
345                 for (var i=0;i<file.length;i++)
346                     requiresCss(file[i]);
347                 return;
348             }
349 
350             if (file.indexOf('/') == 0)
351             {
352                 file = file.substring(1);
353             }
354 
355             var key = file,
356                 fullPath;
357 
358             if (!_requestedCssFiles[key])
359             {
360                 _requestedCssFiles[key] = true;
361 
362                 // Support both LabKey and external CSS files
363                 if (file.substr(0, 4) != "http")
364                 {
365                     // local files
366                     fullPath = configs.contextPath + "/" + file + '?' + configs.hash;
367                 }
368                 else
369                 {
370                     // external files
371                     fullPath = file;
372                 }
373 
374                 addElemToHead("link", {
375                     type: "text/css",
376                     rel: "stylesheet",
377                     href: fullPath
378                 });
379             }
380         };
381 
382         var requestedCssFiles = function()
383         {
384             var ret = (arguments.length > 0 && _requestedCssFiles[arguments[0]]) ? true : false;
385             for (var i=0; i < arguments.length ; i++)
386                 _requestedCssFiles[arguments[i]] = true;
387             return ret;
388         };
389 
390         var requiresClientAPI = function(callback, scope)
391         {
392             // backwards compat for 'immediate'
393             if (arguments.length > 0 && isBoolean(arguments[0]))
394             {
395                 callback = arguments[1];
396                 scope = arguments[2];
397             }
398 
399             requiresLib('clientapi', function()
400             {
401                 requiresExt3ClientAPI(callback, scope);
402             });
403         };
404 
405         var requiresExt3 = function(callback, scope)
406         {
407             // backwards compat for 'immediate'
408             if (arguments.length > 0 && isBoolean(arguments[0]))
409             {
410                 callback = arguments[1];
411                 scope = arguments[2];
412             }
413 
414             if (window.Ext)
415             {
416                 handle(callback, scope);
417             }
418             else
419             {
420                 requiresCss(configs.extJsRoot + '/resources/css/ext-all.css');
421                 requiresLib('Ext3', callback, scope);
422             }
423         };
424 
425         var requiresExt3ClientAPI = function(callback, scope)
426         {
427             // backwards compat for 'immediate'
428             if (arguments.length > 0 && isBoolean(arguments[0]))
429             {
430                 callback = arguments[1];
431                 scope = arguments[2];
432             }
433 
434             requiresExt3(function()
435             {
436                 requiresLib('clientapi/ext3', callback, scope);
437             });
438         };
439 
440         var requiresExt4ClientAPI = function(callback, scope)
441         {
442             // backwards compat for 'immediate'
443             if (arguments.length > 0 && isBoolean(arguments[0]))
444             {
445                 callback = arguments[1];
446                 scope = arguments[2];
447             }
448 
449             requiresExt4Sandbox(function()
450             {
451                 requiresLib('Ext4ClientApi', callback, scope);
452             });
453         };
454 
455         var requiresExt4Sandbox = function(callback, scope)
456         {
457             // backwards compat for 'immediate'
458             if (arguments.length > 0 && isBoolean(arguments[0]))
459             {
460                 callback = arguments[1];
461                 scope = arguments[2];
462             }
463 
464             if (window.Ext4)
465             {
466                 handle(callback, scope);
467             }
468             else
469             {
470                 requiresCss(configs.extThemeRoot_42 + "/" + configs.extThemeName_42 + "/ext-all.css");
471                 requiresLib('Ext4', callback, scope);
472             }
473         };
474 
475         var requiresLib = function(lib, callback, scope)
476         {
477             if (!lib)
478             {
479                 handle(callback, scope);
480                 return;
481             }
482 
483             var _lib = lib.split('.lib.xml')[0];
484 
485             // in case _lib is now empty
486             if (!_lib)
487             {
488                 handle(callback, scope);
489                 return;
490             }
491 
492             if (scriptCache.inCache(_lib))
493             {
494                 handle(callback, scope);
495                 return;
496             }
497             else if (scriptCache.inFlightCache(_lib))
498             {
499                 scriptCache.loadCache(_lib, callback, scope);
500                 return;
501             }
502             else
503             {
504                 scriptCache.loadCache(_lib, callback, scope);
505             }
506 
507             var cacheLoader = function()
508             {
509                 scriptCache.callbacksOnCache(_lib);
510             };
511 
512             _fetch('core-loadLibrary.api', {
513                 library: _lib
514             }, function(data) {
515                 // success
516                 var json = JSON.parse(data.responseText);
517                 var definition = json['libraries'][_lib];
518 
519                 if (definition)
520                 {
521                     var styles = [];
522                     var scripts = [];
523                     for (var d=0; d < definition.length; d++)
524                     {
525                         if (definition[d].indexOf('.css') > -1)
526                         {
527                             styles.push(definition[d]);
528                         }
529                         else
530                         {
531                             scripts.push(definition[d]);
532                         }
533                     }
534 
535                     LABKEY.requiresCss(styles);
536                     LABKEY.requiresScript(scripts, cacheLoader, undefined, true /* inOrder, sadly */);
537                 }
538                 else
539                 {
540                     throw new Error('Failed to retrieve library definition \"' + _lib + '\"');
541                 }
542             }, function() {
543                 // failure
544                 throw new Error('Failed to load library: \"' + _lib + '\"');
545             });
546         };
547 
548         var requiresScript = function(file, callback, scope, inOrder)
549         {
550             if (arguments.length === 0)
551             {
552                 throw "LABKEY.requiresScript() requires the 'file' parameter.";
553             }
554             else if (!file)
555             {
556                 throw "LABKEY.requiresScript() invalid 'file' argument.";
557             }
558 
559             // backwards compat for 'immediate'
560             if (arguments.length > 1 && isBoolean(arguments[1]))
561             {
562                 callback = arguments[2];
563                 scope = arguments[3];
564                 inOrder = arguments[4];
565             }
566 
567             if (isArray(file))
568             {
569                 var requestedLength = file.length;
570                 var loaded = 0;
571 
572                 if (inOrder)
573                 {
574                     var chain = function()
575                     {
576                         loaded++;
577                         if (loaded == requestedLength)
578                         {
579                             handle(callback, scope);
580                         }
581                         else if (loaded < requestedLength)
582                             requiresScript(file[loaded], chain, undefined, true);
583                     };
584 
585                     if (scriptCache.inCache(file[loaded]))
586                     {
587                         chain();
588                     }
589                     else
590                         requiresScript(file[loaded], chain, undefined, true);
591                 }
592                 else
593                 {
594                     // request all the scripts (order does not matter)
595                     var allDone = function()
596                     {
597                         loaded++;
598                         if (loaded == requestedLength)
599                         {
600                             handle(callback, scope);
601                         }
602                     };
603 
604                     for (var i = 0; i < file.length; i++)
605                     {
606                         if (scriptCache.inCache(file[i]))
607                         {
608                             allDone();
609                         }
610                         else
611                             requiresScript(file[i], allDone);
612                     }
613                 }
614                 return;
615             }
616 
617             if (isLibrary(file))
618             {
619                 if (file === 'Ext3')
620                 {
621                     requiresExt3(callback, scope);
622                 }
623                 if (file === 'Ext4')
624                 {
625                     requiresExt4Sandbox(callback, scope);
626                 }
627                 else
628                 {
629                     requiresLib(file, callback, scope);
630                 }
631                 return;
632             }
633 
634             if (file.indexOf('/') == 0)
635             {
636                 file = file.substring(1);
637             }
638 
639             if (scriptCache.inCache(file))
640             {
641                 // cache hit -- script is loaded and ready to go
642                 handle(callback, scope);
643                 return;
644             }
645             else if (scriptCache.inFlightCache(file))
646             {
647                 // cache miss -- in flight
648                 scriptCache.loadCache(file, callback, scope);
649                 return;
650             }
651             else
652             {
653                 // cache miss
654                 scriptCache.loadCache(file, callback, scope);
655             }
656 
657             // although FireFox and Safari allow scripts to use the DOM
658             // during parse time, IE does not. So if the document is
659             // closed, use the DOM to create a script element and append it
660             // to the head element. Otherwise (still parsing), use document.write()
661 
662             // Support both LabKey and external JavaScript files
663             var src = file.substr(0, 4) != "http" ? configs.contextPath + "/" + file + '?' + configs.hash : file;
664 
665             var cacheLoader = function()
666             {
667                 scriptCache.callbacksOnCache(file);
668             };
669 
670             if (configs.isDocumentClosed || callback)
671             {
672                 //create a new script element and append it to the head element
673                 var script = addElemToHead("script", {
674                     src: src,
675                     type: "text/javascript"
676                 });
677 
678                 // IE has a different way of handling <script> loads
679                 if (script.readyState)
680                 {
681                     script.onreadystatechange = function() {
682                         if (script.readyState == "loaded" || script.readyState == "complete") {
683                             script.onreadystatechange = null;
684                             cacheLoader();
685                         }
686                     };
687                 }
688                 else
689                 {
690                     script.onload = cacheLoader;
691                 }
692             }
693             else
694             {
695                 document.write('\n<script type="text/javascript" src="' + src + '"></script>\n');
696                 cacheLoader();
697             }
698         };
699 
700         var requiresVisualization = function(callback, scope)
701         {
702             requiresLib('vis/vis', callback, scope);
703         };
704 
705         var setDirty = function (dirty)
706         {
707             configs.dirty = (dirty ? true : false); // only set to boolean
708         };
709 
710         var setSubmit = function (submit)
711         {
712             configs.submit = (submit ? true : false); // only set to boolean
713         };
714 
715         var showNavTrail = function()
716         {
717             var elem = document.getElementById("navTrailAncestors");
718             if(elem)
719                 elem.style.visibility = "visible";
720             elem = document.getElementById("labkey-nav-trail-current-page");
721             if(elem)
722                 elem.style.visibility = "visible";
723         };
724 
725         return {
726 
727             /**
728              * This callback type is called 'requireCallback' and is displayed as a global symbol
729              *
730              * @callback requireCallback
731              */
732 
733             /**
734              * The DataRegion class allows you to interact with LabKey grids,
735              * including querying and modifying selection state, filters, and more.
736              * @field
737              */
738             DataRegions: configs.DataRegions,
739 
740             demoMode: configs.demoMode,
741             devMode: configs.devMode,
742             dirty: configs.dirty,
743             extJsRoot: configs.extJsRoot,
744             extJsRoot_42: configs.extJsRoot_42,
745             extThemeRoot: configs.extThemeRoot,
746             fieldMarker: configs.fieldMarker,
747             hash: configs.hash,
748             imagePath: configs.imagePath,
749             submit: configs.submit,
750             unloadMessage: configs.unloadMessage,
751             verbose: configs.verbose,
752             widget: configs.widget,
753 
754             /** @field */
755             contextPath: configs.contextPath,
756 
757             /**
758              * Appends an element to the head of the document
759              * @private
760              * @param {String} elemName First argument for docoument.createElement
761              * @param {Object} [attributes]
762              * @returns {*}
763              */
764             addElemToHead: addElemToHead,
765 
766             // TODO: Eligible for removal after util.js is migrated
767             addMarkup: addMarkup,
768             applyModuleContext: applyModuleContext,
769             beforeunload: beforeunload,
770             createElement: createElement,
771 
772             /**
773              * @function
774              * @param {String} moduleName The name of the module
775              * @returns {Object} The context object for this module.  The current view must have specifically requested
776              * the context for this module in its view XML
777              */
778             getModuleContext: getModuleContext,
779 
780             /**
781              * @function
782              * @param {String} moduleName The name of the module
783              * @param {String} property The property name to return
784              * @returns {String} The value of the module property.  Will return null if the property has not been set.
785              */
786             getModuleProperty: getModuleProperty,
787             getSubmit: getSubmit,
788             id: id,
789             init: init,
790             isDirty: isDirty,
791             loadScripts: loadScripts,
792             loadedScripts: loadedScripts,
793 
794             /**
795              * Loads a CSS file from the server.
796              * @function
797              * @param {(string|string[])} file - The path of the CSS file to load
798              * @example
799              <script type="text/javascript">
800                 LABKEY.requiresCss("myModule/myFile.css");
801              </script>
802              */
803             requiresCss: requiresCss,
804             requestedCssFiles: requestedCssFiles,
805             requiresClientAPI: requiresClientAPI,
806 
807             /**
808              * This can be added to any LABKEY page in order to load ExtJS 3.  This is the preferred method to declare Ext3 usage
809              * from wiki pages.  For HTML or JSP pages defined in a module, see our <a href="https://www.labkey.org/wiki/home/Documentation/page.view?name=ext4Development">documentation</a> on declaration of client dependencies.
810              * @function
811              * @param {boolean} [immediate=true] - True to load the script immediately; false will defer script loading until the page has been downloaded.
812              * @param {requireCallback} [callback] - Callback for when all dependencies are loaded.
813              * @param {Object} [scope] - Scope of callback.
814              * @example
815              <script type="text/javascript">
816                 LABKEY.requiresExt3(true, function() {
817                     Ext.onReady(function() {
818                         // Ext 3 is loaded and ready
819                     });
820                 });
821              </script>
822              */
823             requiresExt3: requiresExt3,
824 
825             /**
826              * This can be added to any LABKEY page in order to load the LabKey ExtJS 3 Client API.
827              * @function
828              * @param {boolean} [immediate=true] - True to load the script immediately; false will defer script loading until the page has been downloaded.
829              * @param {requireCallback} [callback] - Callback for when all dependencies are loaded.
830              * @param {Object} [scope] - Scope of callback.
831              * @example
832              <script type="text/javascript">
833                  LABKEY.requiresExt3ClientAPI(true, function() {
834                     // your code here
835                  });
836              </script>
837              */
838             requiresExt3ClientAPI: requiresExt3ClientAPI,
839 
840             /**
841              * This can be added to any LABKEY page in order to load the LabKey ExtJS 4 Client API. This primarily
842              * consists of a set of utility methods {@link LABKEY.ext4.Util} and an extended Ext.data.Store {@link LABKEY.ext4.data.Store}.
843              * It will load ExtJS 4 as a dependency.
844              * @function
845              * @param {boolean} [immediate=true] - True to load the script immediately; false will defer script loading until the page has been downloaded.
846              * @param {requireCallback} [callback] - Callback for when all dependencies are loaded.
847              * @param {Object} [scope] - Scope of callback.
848              * @example
849              <script type="text/javascript">
850                  LABKEY.requiresExt4ClientAPI(true, function() {
851                     // your code here
852                  });
853              </script>
854              */
855             requiresExt4ClientAPI: requiresExt4ClientAPI,
856 
857             /**
858              * This can be added to any LABKEY page in order to load ExtJS 4.  This is the preferred method to declare Ext4 usage
859              * from wiki pages.  For HTML or JSP pages defined in a module, see our <a href="https://www.labkey.org/wiki/home/Documentation/page.view?name=ext4Development">documentation</a> on declaration of client dependencies.
860              * @function
861              * @param {boolean} [immediate=true] - True to load the script immediately; false will defer script loading until the page has been downloaded.
862              * @param {requireCallback} [callback] - Callback for when all dependencies are loaded.
863              * @param {Object} [scope] - Scope of callback.
864              * @example
865              <script type="text/javascript">
866                  LABKEY.requiresExt4Sandbox(true, function() {
867                     Ext4.onReady(function(){
868                         // Ext4 is loaded and ready
869                     });
870                  });
871              </script>
872              */
873             requiresExt4Sandbox: requiresExt4Sandbox,
874 
875             /**
876              * Deprecated.  Use LABKEY.requiresExt3 instead.
877              * @function
878              * @private
879              */
880             requiresExtJs: requiresExt3,
881 
882             /**
883              * Loads JavaScript file(s) from the server.
884              * @function
885              * @param {(string|string[])} file - A file or Array of files to load.
886              * @param {Function} [callback] - Callback for when all dependencies are loaded.
887              * @param {Object} [scope] - Scope of callback.
888              * @param {boolean} [inOrder=false] - True to load the scripts in the order they are passed in. Default is false.
889              * @example
890              <script type="text/javascript">
891                 LABKEY.requiresScript("myModule/myScript.js", true, function() {
892                     // your script is loaded
893                 });
894              </script>
895              */
896             requiresScript: requiresScript,
897             requiresVisualization: requiresVisualization,
898             setDirty: setDirty,
899             setSubmit: setSubmit,
900             showNavTrail: showNavTrail
901         }
902     };
903 
904 }
905