浏览代码

HPCC-8861 Refactor LogicalFiles

To match ESPWorkunits:
Make ESPLogicalFile a singleton.
Make DFUWUQueryWidget + LFDetailsWidget a MVC
Implement missing actions.

Signed-off-by: Gordon Smith <gordon.smith@lexisnexis.com>
Gordon Smith 12 年之前
父节点
当前提交
398950485b

+ 1 - 2
esp/eclwatch/ws_XSLT/dfu.xslt

@@ -82,7 +82,7 @@
             <xsl:text disable-output-escaping="yes"><![CDATA[
           function DFUFilePopup(query, filename, cluster, replicate, roxiecluster, browsedata, PosId) {
             function detailsDFUFile() {
-              document.location.href='/esp/files/stub.htm?Widget=LFDetailsWidget&Name=' + escape(filename) + '&Cluster=' + cluster;
+              document.location.href='/WsDfu/DFUInfo?Name='+ escape(filename) + '&Cluster=' + cluster;
                       }
                         function browseDFUData() {
                           document.location.href='/WsDfu/DFUGetDataColumns?OpenLogicalName='+filename;
@@ -663,7 +663,6 @@
             <xsl:value-of select="$popup"/>
           </xsl:variable>
           <img id="mn{position()}" class="menu1" src="/esp/files/img/menu1.png" onclick="{$popup}"></img>
-          <a href="javascript:go('/esp/files/stub.htm?Widget=LFDetailsWidget&amp;Cluster={ClusterName}&amp;Name={Name}')">Details</a>
       </td>
             <td>
               <xsl:if test="isZipfile=1">

+ 1 - 1
esp/eclwatch/ws_XSLT/dfu_workunits.xslt

@@ -397,7 +397,7 @@
                 </xsl:if>
             </td>
             <td>
-                <a href="javascript:go('/esp/files/stub.htm?Widget=DFUWUDetailsWidget&amp;Wuid={ID}')">
+                <a href="javascript:go('/FileSpray/GetDFUWorkunit?wuid={ID}')">
                     <xsl:choose>
                         <xsl:when test="State=2 or State=3"><b><xsl:value-of select="ID"/></b></xsl:when>
                         <xsl:otherwise><xsl:value-of select="ID"/></xsl:otherwise>

+ 1 - 1
esp/eclwatch/ws_XSLT/workunits.xslt

@@ -474,7 +474,7 @@
          <td>
             <xsl:choose>
                 <xsl:when test="not(string-length($archived))">
-                    <a href="javascript:go('/esp/files/stub.htm?Widget=WUDetailsWidget&amp;Wuid={Wuid}')">
+                    <a href="javascript:go('/WsWorkunits/WUInfo?Wuid={Wuid}&amp;&amp;IncludeExceptions=0&amp;IncludeGraphs=0&amp;IncludeSourceFiles=0&amp;IncludeResults=0&amp;IncludeVariables=0&amp;IncludeTimers=0&amp;IncludeDebugValues=0&amp;IncludeApplicationValues=0&amp;IncludeWorkflows&amp;SuppressResultSchemas=1')">
                        <xsl:value-of select="Wuid"/>
                     </a>
                 </xsl:when>

+ 4 - 0
esp/files/css/hpcc.css

@@ -229,6 +229,10 @@ margin-left:-20px;
     text-align: left;
 }
 
+.dijitToasterContainer {
+    width: 64em;
+}
+
 .smallForm .dijitValidationTextBoxLabel {
     width: auto;
 }

+ 140 - 95
esp/files/scripts/DFUWUQueryWidget.js

@@ -15,7 +15,10 @@
 ############################################################################## */
 define([
     "dojo/_base/declare",
+    "dojo/_base/lang",
+    "dojo/_base/array",
     "dojo/dom",
+    "dojo/dom-form",
     "dojo/data/ObjectStore",
     "dojo/date",
     "dojo/on",
@@ -30,7 +33,9 @@ define([
 
     "hpcc/_TabContainerWidget",
     "hpcc/WsDfu",
+    "hpcc/ESPLogicalFile",
     "hpcc/LFDetailsWidget",
+    "hpcc/SFDetailsWidget",
 
     "dojo/text!../templates/DFUWUQueryWidget.html",
 
@@ -41,13 +46,19 @@ define([
     "dijit/form/DateTextBox",
     "dijit/form/TimeTextBox",
     "dijit/form/Button",
+    "dijit/form/DropDownButton",
     "dijit/form/Select",
     "dijit/Toolbar",
-    "dijit/TooltipDialog"
-], function (declare, dom, ObjectStore, date, on,
+    "dijit/TooltipDialog",
+
+    "dojox/layout/TableContainer",
+
+    "hpcc/TargetSelectWidget"
+
+], function (declare, lang, arrayUtil, dom, domForm, ObjectStore, date, on,
                 _TemplatedMixin, _WidgetsInTemplateMixin, registry,
                 EnhancedGrid, Pagination, IndirectSelection,
-                _TabContainerWidget, WsDfu, LFDetailsWidget,
+                _TabContainerWidget, WsDfu, ESPLogicalFile, LFDetailsWidget, SFDetailsWidget,
                 template) {
     return declare("DFUWUQueryWidget", [_TabContainerWidget, _TemplatedMixin, _WidgetsInTemplateMixin], {
         templateString: template,
@@ -64,6 +75,7 @@ define([
             this.workunitsTab = registry.byId(this.id + "_Workunits");
             this.workunitsGrid = registry.byId(this.id + "WorkunitsGrid");
             this.legacyPane = registry.byId(this.id + "_Legacy");
+            this.clusterTargetSelect = registry.byId(this.id + "ClusterTargetSelect");
         },
 
         startup: function (args) {
@@ -73,13 +85,15 @@ define([
         },
 
         //  Hitched actions  ---
+        _onRefresh: function (event) {
+            this.refreshGrid();
+        },
+
         _onOpen: function (event) {
             var selections = this.workunitsGrid.selection.getSelected();
             var firstTab = null;
             for (var i = selections.length - 1; i >= 0; --i) {
-                var tab = this.ensurePane(this.id + "_" + selections[i].Name, {
-                    Name: selections[i].Name
-                });
+                var tab = this.ensurePane(this.id + "_" + selections[i].Name, selections[i]);
                 if (i == 0) {
                     firstTab = tab;
                 }
@@ -89,9 +103,9 @@ define([
             }
         },
         _onDelete: function (event) {
-            if (confirm('Delete selected workunits?')) {
+            if (confirm('Delete selected files?')) {
                 var context = this;
-                WsDfu.WUAction(this.workunitsGrid.selection.getSelected(), "Delete", {
+                WsDfu.DFUArrayAction(this.workunitsGrid.selection.getSelected(), "Delete", {
                     load: function (response) {
                         context.workunitsGrid.rowSelectCell.toggleAllSelection(false);
                         context.refreshGrid(response);
@@ -99,7 +113,21 @@ define([
                 });
             }
         },
-        _onAddToSuperfile: function (event) {
+        _onAddToSuperfileOk: function (event) {
+            var context = this;
+            var formData = domForm.toObject(this.id + "AddToSuperfileForm");
+            WsDfu.AddtoSuperfile(this.workunitsGrid.selection.getSelected(), formData.Superfile, formData.ExistingFile, {
+                load: function (response) {
+                    context.workunitsGrid.rowSelectCell.toggleAllSelection(false);
+                    context.refreshGrid(response);
+                }
+            });
+            var d = registry.byId(this.id + "AddtoDropDown");
+            registry.byId(this.id + "AddtoDropDown").closeDropDown();
+        },
+        _onAddToSuperfileCancel: function (event) {
+            var d = registry.byId(this.id + "AddtoDropDown");
+            registry.byId(this.id + "AddtoDropDown").closeDropDown();
         },
         _onFilterApply: function (event) {
             this.workunitsGrid.rowSelectCell.toggleAllSelection(false);
@@ -107,36 +135,40 @@ define([
         },
         _onFilterClear: function(event) {
             this.workunitsGrid.rowSelectCell.toggleAllSelection(false);
-            dom.byId(this.id + "Owner").value = "";
-            dom.byId(this.id + "Jobname").value = "";
-            dom.byId(this.id + "Cluster").value = "";
-            dom.byId(this.id + "State").value = "";
-            dom.byId(this.id + "ECL").value = "";
-            dom.byId(this.id + "LogicalFile").value = "";
-            dom.byId(this.id + "LogicalFileSearchType").value = "";
-            dom.byId(this.id + "FromDate").value = "";
-            dom.byId(this.id + "FromTime").value = "";
-            dom.byId(this.id + "ToDate").value = "";
-            dom.byId(this.id + "LastNDays").value = "";
+            var context = this;
+            arrayUtil.forEach(registry.byId(this.id + "FilterForm").getDescendants(), function (item, idx) {
+                if (item.id == context.id + "ClusterTargetSelect") {
+                    item.setValue("");
+                } else {
+                    item.set('value', null);
+                }
+            });
             this.refreshGrid();
         },
 
+        getISOString: function (dateField, timeField) {
+            var d = registry.byId(this.id + dateField).attr("value");
+            var t = registry.byId(this.id + timeField).attr("value");
+            if (d) {
+                if (t) {
+                    d.setHours(t.getHours());
+                    d.setMinutes(t.getMinutes());
+                    d.setSeconds(t.getSeconds());
+                }
+                return d.toISOString();
+            }
+            return "";
+        },
+
         getFilter: function () {
-            var retVal = {
-                Owner: dom.byId(this.id + "Owner").value,
-                Jobname: dom.byId(this.id + "Jobname").value,
-                Cluster: dom.byId(this.id + "Cluster").value,
-                State: dom.byId(this.id + "State").value,
-                ECL: dom.byId(this.id + "ECL").value,
-                LogicalFile: dom.byId(this.id + "LogicalFile").value,
+            var retVal = domForm.toObject(this.id + "FilterForm");
+            lang.mixin(retVal, {
+                ClusterName: this.clusterTargetSelect.getValue(),
                 StartDate: this.getISOString("FromDate", "FromTime"),
-                EndDate: this.getISOString("ToDate", "ToTime"),
-                LastNDays: dom.byId(this.id + "LastNDays").value
-            };
+                EndDate: this.getISOString("ToDate", "ToTime")
+            });
             if (retVal.StartDate != "" && retVal.EndDate != "") {
-                retVal["DateRB"] = "0";
-            } else if (retVal.LastNDays != "") {
-                retVal["DateRB"] = "0";
+            } else if (retVal.LastNDays) {
                 var now = new Date();
                 retVal.StartDate = date.add(now, "day", dom.byId(this.id + "LastNDays").value * -1).toISOString();
                 retVal.EndDate = now.toISOString();
@@ -164,7 +196,11 @@ define([
                 return;
             this.initalized = true;
 
-            this.selectChild(this.workunitsTab, true);
+            this.selectChild(this.legacyPane, true);
+            this.clusterTargetSelect.init({
+                Groups: true,
+                includeBlank: true
+            });
         },
 
         initTab: function() {
@@ -181,7 +217,7 @@ define([
                     }
                 } else {
                     if (!currSel.initalized) {
-                        currSel.init(currSel.params);
+                        currSel.init(currSel._hpccParams);
                     }
                 }
             }
@@ -189,50 +225,49 @@ define([
 
         initWorkunitsGrid: function() {
             this.workunitsGrid.setStructure([
-			    {
-			        name: "C",
-			        field: "isZipfile",
-			        width: "16px",
-			        formatter: function (compressed) {
-			            if (compressed == true) {
-			                return "C";
-			            }
-			            return "";
-			        }
-			    },
-			    {
-			        name: "K",
-			        field: "isKeyFile",
-			        width: "16px",
-			        formatter: function (keyfile) {
-			            if (keyfile == true) {
-			                return "K";
-			            }
-			            return "";
-			        }
-			    },
-			    {
-			        name: "S",
-			        field: "isSuperfile",
-			        width: "16px",
-			        formatter: function (superfile) {
-			            if (superfile == true) {
-			                return "S";
-			            }
-			            return "";
-			        }
-			    },
-			    { name: "Logical Name", field: "Name", width: "32" },
-			    { name: "Owner", field: "Owner", width: "8" },
-			    { name: "Description", field: "Description", width: "12" },
-			    { name: "Cluster", field: "ClusterName", width: "12" },
-			    { name: "Records", field: "RecordCount", width: "8" },
-			    { name: "Size", field: "Totalsize", width: "8" },
-			    { name: "Parts", field: "Parts", width: "4" },
-			    { name: "Modified (UTC/GMT)", field: "Modified", width: "12" }
+                {
+                    name: "C",
+                    field: "isZipfile",
+                    width: "16px",
+                    formatter: function (compressed) {
+                        if (compressed == true) {
+                            return "C";
+                        }
+                        return "";
+                    }
+                },
+                {
+                    name: "K",
+                    field: "IsKeyFile",
+                    width: "16px",
+                    formatter: function (keyfile) {
+                        if (keyfile == true) {
+                            return "K";
+                        }
+                        return "";
+                    }
+                },
+                {
+                    name: "S",
+                    field: "isSuperfile",
+                    width: "16px",
+                    formatter: function (superfile) {
+                        if (superfile == true) {
+                            return "S";
+                        }
+                        return "";
+                    }
+                },
+                { name: "Logical Name", field: "Name", width: "32" },
+                { name: "Owner", field: "Owner", width: "8" },
+                { name: "Description", field: "Description", width: "12" },
+                { name: "Cluster", field: "ClusterName", width: "12" },
+                { name: "Records", field: "RecordCount", width: "8" },
+                { name: "Size", field: "Totalsize", width: "8" },
+                { name: "Parts", field: "Parts", width: "4" },
+                { name: "Modified (UTC/GMT)", field: "Modified", width: "12" }
             ]);
-            var store = new WsDfu.DFUQuery();
-            var objStore = new ObjectStore({ objectStore: store });
+            var objStore = ESPLogicalFile.CreateLFQueryObjectStore();
             this.workunitsGrid.setStore(objStore);
             this.workunitsGrid.setQuery(this.getFilter());
 
@@ -241,8 +276,7 @@ define([
                 if (context.onRowDblClick) {
                     var idx = evt.rowIndex;
                     var item = this.getItem(idx);
-                    var Name = this.store.getValue(item, "Name");
-                    context.onRowDblClick(Name);
+                    context.onRowDblClick(item);
                 }
             }, true);
 
@@ -273,7 +307,7 @@ define([
 
             registry.byId(this.id + "Open").set("disabled", !hasSelection);
             registry.byId(this.id + "Delete").set("disabled", !hasSelection);
-            registry.byId(this.id + "AddToSuperfile").set("disabled", !hasSelection);
+            registry.byId(this.id + "AddtoDropDown").set("disabled", !hasSelection);
         },
 
         ensurePane: function (id, params) {
@@ -284,26 +318,37 @@ define([
             var retVal = this.tabMap[id];
             if (!retVal) {
                 var context = this;
-                retVal = new LFDetailsWidget({
-                    id: id,
-                    title: params.Name,
-                    closable: true,
-                    onClose: function () {
-                        delete context.tabMap[id];
-                        return true;
-                    },
-                    params: params
-                });
+                if (params.isSuperfile) {
+                    retVal = new SFDetailsWidget.fixCircularDependency({
+                        id: id,
+                        title: params.Name,
+                        closable: false,
+                        onClose: function () {
+                            delete context.tabMap[id];
+                            return true;
+                        },
+                        _hpccParams: params
+                    });
+                } else {
+                    retVal = new LFDetailsWidget.fixCircularDependency({
+                        id: id,
+                        title: params.Name,
+                        closable: false,
+                        onClose: function () {
+                            delete context.tabMap[id];
+                            return true;
+                        },
+                        _hpccParams: params
+                    });
+                }
                 this.tabMap[id] = retVal;
                 this.addChild(retVal, 2);
             }
             return retVal;
         },
 
-        onRowDblClick: function (name) {
-            var wuTab = this.ensurePane(this.id + "_" + name, {
-                Name: name
-            });
+        onRowDblClick: function (item) {
+            var wuTab = this.ensurePane(this.id + "_" + item.Name, item);
             this.selectChild(wuTab);
         }
     });

+ 182 - 28
esp/files/scripts/ESPLogicalFile.js

@@ -15,72 +15,206 @@
 ############################################################################## */
 define([
     "dojo/_base/declare",
+    "dojo/_base/array",
     "dojo/_base/lang",
+    "dojo/_base/Deferred",
+    "dojo/data/ObjectStore",
+    "dojo/store/util/QueryResults",
+    "dojo/store/Observable",
+    "dojo/Stateful",
 
     "hpcc/WsDfu",
+    "hpcc/FileSpray",
+    "hpcc/ESPUtil",
     "hpcc/ESPResult"
-], function (declare, lang,
-        WsDfu, ESPResult) {
-    return declare(null, {
-        cluster: "",
-        logicalName: "",
-        result: [],
+], function (declare, arrayUtil, lang, Deferred, ObjectStore, QueryResults, Observable, Stateful,
+        WsDfu, FileSpray, ESPUtil, ESPResult) {
 
+    var _logicalFiles = {};
+
+    var Store = declare(null, {
+        idProperty: "Name",
+
+        _watched: {},
+
+        constructor: function (options) {
+            declare.safeMixin(this, options);
+        },
+
+        getIdentity: function (object) {
+            return object[this.idProperty];
+        },
+
+        get: function (name) {
+            if (!_logicalFiles[name]) {
+                _logicalFiles[name] = new LogicalFile({
+                    Name: name
+                });
+            }
+            return _logicalFiles[name];
+        },
+
+        remove: function (item) {
+            if (_logicalFiles[this.getIdentity(item)]) {
+                _logicalFiles[this.getIdentity(item)].stopMonitor();
+                delete _logicalFiles[this.getIdentity(item)];
+            }
+        },
+
+        query: function (query, options) {
+            var request = {};
+            lang.mixin(request, options.query);
+            if (options.start)
+                request['PageStartFrom'] = options.start;
+            if (options.count)
+                request['Count'] = options.count;
+            if (options.sort) {
+                request['Sortby'] = options.sort[0].attribute;
+                request['Descending'] = options.sort[0].descending;
+            }
+
+            var results = WsDfu.DFUQuery({
+                request: request
+            });
+
+            var deferredResults = new Deferred();
+            deferredResults.total = results.then(function (response) {
+                if (lang.exists("DFUQueryResponse.NumFiles", response)) {
+                    return response.DFUQueryResponse.NumFiles;
+                }
+                return 0;
+            });
+            var context = this;
+            Deferred.when(results, function (response) {
+                var logicalFiles = [];
+                for (key in context._watched) {
+                    context._watched[key].unwatch();
+                }
+                this._watched = {};
+                if (lang.exists("DFUQueryResponse.DFULogicalFiles.DFULogicalFile", response)) {
+                    arrayUtil.forEach(response.DFUQueryResponse.DFULogicalFiles.DFULogicalFile, function (item, index) {
+                        var logicalFile = context.get(item.Name);
+                        logicalFile.updateData(item);
+                        logicalFiles.push(logicalFile);
+                        context._watched[logicalFile.Name] = logicalFile.watch("changedCount", function (name, oldValue, newValue) {
+                            if (oldValue !== newValue) {
+                                context.notify(logicalFile, logicalFile.Name);
+                            }
+                        });
+                    });
+                }
+                deferredResults.resolve(logicalFiles);
+            });
+
+            return QueryResults(deferredResults);
+        }
+    });
+
+    var LogicalFile = declare([ESPUtil.Singleton], {
+        _FileDetailSetter: function(FileDetail) {
+            this.FileDetail = FileDetail;
+            this.result = ESPResult.Get(FileDetail);
+        },
+        _DirSetter: function (Dir) {
+            this.set("Directory", Dir);
+        },
         constructor: function (args) {
+            this.inherited(arguments);
             declare.safeMixin(this, args);
-            this.DFUInfoResponse = {
-                Name: this.logicalName,
-                Cluster: this.cluster
-            };
+            this.logicalFile = this;
         },
         save: function (description, args) {
             //WsDfu/DFUInfo?FileName=progguide%3A%3Aexampledata%3A%3Akeys%3A%3Apeople.lastname.firstname&UpdateDescription=true&FileDesc=%C2%A0123&Save+Description=Save+Description
             var context = this;
             WsDfu.DFUInfo({
                 request: {
-                    FileName: this.logicalName,
-                    Cluster: this.cluster,
+                    FileName: this.Name,
+                    Cluster: this.Cluster,
                     UpdateDescription: true,
                     FileDesc: description
                 },
                 load: function (response) {
-                    if (response.DFUInfoResponse) {
-                        context.processDFUInfoResponse(response.DFUInfoResponse, args);
+                    if (lang.exists("DFUInfoResponse.FileDetail", response)) {
+                        context.updateData(response.DFUInfoResponse.FileDetail);
+                        if (args && args.onGetAll) {
+                            args.onGetAll(response.DFUInfoResponse.FileDetail);
+                        }
                     }
-                },
-                error: function (e) {
                 }
             });
         },
+        doDelete: function (params) {
+            var context = this;
+            WsDfu.DFUArrayAction([this], "Delete", {
+                load: function (response) {
+                    context.refresh();
+                }
+            });
+        },
+        despray: function (params) {
+            var context = this;
+            lang.mixin(params.request, {
+                sourceLogicalName: this.Name
+            });
+            return FileSpray.Despray(params);
+        },
+        copy: function (params) {
+            var context = this;
+            lang.mixin(params.request, {
+                sourceLogicalName: this.Name
+            });
+            return FileSpray.Copy(params);
+        },
+        rename: function (params) {
+            var context = this;
+            lang.mixin(params.request, {
+                srcname: this.Name
+            });
+            return FileSpray.Rename(params).then(function (response) {
+                context.set("Name", params.request.dstname);  //TODO - need to monitor DFUWorkunit for success (After ESPDFUWorkunit has been updated to proper singleton).
+                context.refresh();
+                return response;
+            });
+        },
+        removeSubfiles: function (subfiles, removeSuperfile) {
+            var context = this;
+            return WsDfu.SuperfileAction("remove", this.Name, subfiles, removeSuperfile).then(function (response) {
+                context.refresh();
+                return response;
+            });
+        },
+        refresh: function (full) {
+            this.getInfo();
+        },
         getInfo: function (args) {
             //WsDfu/DFUInfo?Name=progguide::exampledata::keys::people.state.city.zip.lastname.firstname.payload&Cluster=hthor__myeclagent HTTP/1.1
             var context = this;
             WsDfu.DFUInfo({
                 request:{
-                    Name: this.logicalName,
-                    Cluster: this.cluster
+                    Name: this.Name,
+                    Cluster: this.Cluster
                 },
                 load: function (response) {
-                    if (response.DFUInfoResponse) {
-                        context.processDFUInfoResponse(response.DFUInfoResponse, args);
+                    if (lang.exists("DFUInfoResponse.FileDetail", response)) {
+                        context.updateData(response.DFUInfoResponse.FileDetail);
+                        if (args && args.onGetAll) {
+                            args.onGetAll(response.DFUInfoResponse.FileDetail);
+                        }
                     }
                 }
             });
         },
-        processDFUInfoResponse: function(dfuInfoResponse, args) {
-            var fileDetail = dfuInfoResponse.FileDetail;
-            this.DFUInfoResponse = fileDetail;
-            this.result = ESPResult.Get(fileDetail);
-
-            if (args.onGetAll) {
-                args.onGetAll(fileDetail);
+        updateData: function (data) {
+            this.inherited(arguments);
+            if (!this.result) {
+                this.result = ESPResult.Get(data);
             }
         },
         fetchStructure: function (format, onFetchStructure) {
             var context = this;
             WsDfu.DFUDefFile({
                 request: {
-                    Name: this.logicalName,
+                    Name: this.Name,
                     Format: format
                 },
                 load: function (response) {
@@ -95,4 +229,24 @@ define([
             this.fetchStructure("xml", onFetchXML);
         }
     });
+
+    return {
+        Create: function (params) {
+            retVal = new LogicalFile(params);
+            retVal.create();
+            return retVal;
+        },
+
+        Get: function (name) {
+            var store = new Store();
+            return store.get(name);
+        },
+
+        CreateLFQueryObjectStore: function (options) {
+            var store = new Store(options);
+            store = Observable(store);
+            var objStore = new ObjectStore({ objectStore: store });
+            return objStore;
+        }
+    };
 });

+ 8 - 4
esp/files/scripts/ESPRequest.js

@@ -64,16 +64,20 @@ define([
             });
 
             var handleAs = params.handleAs ? params.handleAs : "json";
+            var postfix = "";
+            if (handleAs === "json") {
+                postfix = ".json";
+            }
             var method = params.method ? params.method : "get";
 
             var retVal = null;
             if (this.isCrossSite()) {
-                retVal = script.get(this.getBaseURL(service) + "/" + action + ".json", {
+                retVal = script.get(this.getBaseURL(service) + "/" + action + postfix, {
                     query: params.request,
                     jsonp: "jsonp"
                 });
             } else {
-                retVal = request.post(this.getBaseURL(service) + "/" + action + ".json", {
+                retVal = request.post(this.getBaseURL(service) + "/" + action + postfix, {
                     data: params.request,
                     handleAs: handleAs
                 });
@@ -193,11 +197,11 @@ define([
     });
 
     return {
-        flattenArray: function (target, arrayName) {
+        flattenArray: function (target, arrayName, arrayID) {
             if (lang.exists(arrayName + ".length", target)) {
                 var tmp = {};
                 for (var i = 0; i < target[arrayName].length; ++i) {
-                    tmp[arrayName + "_i" + i] = target[arrayName][i].Wuid;
+                    tmp[arrayName + "_i" + i] = target[arrayName][i][arrayID];
                 }
                 delete target[arrayName];
                 return lang.mixin(target, tmp);

+ 110 - 0
esp/files/scripts/ESPUtil.js

@@ -0,0 +1,110 @@
+/*##############################################################################
+#    HPCC SYSTEMS software Copyright (C) 2012 HPCC Systems.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License");
+#    you may not use this file except in compliance with the License.
+#    You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+############################################################################## */
+define([
+    "dojo/_base/declare",
+    "dojo/Stateful"
+], function (declare, Stateful) {
+
+    var SingletonData = declare([Stateful], {
+        //  Attributes  ---
+        _hasCompletedSetter: function (hasCompleted) {
+            if (this.hasCompleted !== hasCompleted) {
+                this.hasCompleted = hasCompleted;
+                if (!this.hasCompleted) {
+                    if (this.startMonitor) {
+                        this.startMonitor();
+                    }
+                } else {
+                    if (this.stopMonitor) {
+                        this.stopMonitor();
+                    }
+                }
+            }
+        },
+
+        //  Methods  ---
+        constructor: function (args) {
+            this.changedCount = 0;
+            this._changedCache = {};
+        },
+        getData: function () {
+            if (this instanceof SingletonData) {
+                return (SingletonData)(this);
+            }
+            return {};
+        },
+        updateData: function (response) {
+            var changed = false;
+            for (var key in response) {
+                var json = dojo.toJson(response[key]);
+                if (this._changedCache[key] !== json) {
+                    this._changedCache[key] = json;
+                    this.set(key, response[key]);
+                    changed = true;
+                }
+            }
+            if (changed) {
+                this.set("changedCount", this.get("changedCount") + 1);
+            }
+        }
+    });
+
+    var Monitor = declare(null, {
+        startMonitor: function () {
+            this._timerTickCount = 0;
+            this._timer = 1000;
+            this.onMonitor();
+        },
+        stopMonitor: function () {
+            this._timerTickCount = 0;
+            this._timer = 0;
+        },
+        onMonitor: function () {
+            this._timerTickCount++;
+
+            if (this.hasCompleted) {
+                this.stopMonitor();
+                return;
+            } else {
+                if (this._timerTickCount < 5 && this._timerTickCount % 1 === 0) {
+                    this.refresh();
+                } else if (this._timerTickCount < 30 && this._timerTickCount % 5 === 0) {
+                    this.refresh();
+                } else if (this._timerTickCount < 60 && this._timerTickCount % 10 === 0) {
+                    this.refresh();
+                } else if (this._timerTickCount < 120 && this._timerTickCount % 30 === 0) {
+                    this.refresh();
+                } else if (this._timerTickCount % 60 === 0) {
+                    this.refresh();
+                }
+            }
+
+            var context = this;
+            if (this._timer) {
+                setTimeout(function () {
+                    context.onMonitor();
+                }, this._timer);
+            } else {
+                this._timerTickCount = 0;
+            }
+        }
+    });
+
+    return {
+        Singleton: SingletonData,
+        Monitor: Monitor
+    };
+});

+ 26 - 90
esp/files/scripts/ESPWorkunit.js

@@ -21,14 +21,14 @@ define([
     "dojo/data/ObjectStore",
     "dojo/store/util/QueryResults",
     "dojo/store/Observable",
-    "dojo/Stateful",
 
-    "hpcc/ESPResult",
-    "hpcc/WsWorkunits"
-], function (declare, arrayUtil, lang, Deferred, ObjectStore, QueryResults, Observable, Stateful,
-    ESPResult, WsWorkunits) {
+    "hpcc/WsWorkunits",
+    "hpcc/ESPUtil",
+    "hpcc/ESPResult"
+], function (declare, arrayUtil, lang, Deferred, ObjectStore, QueryResults, Observable,
+    WsWorkunits, ESPUtil, ESPResult) {
 
-    _workunits = {};
+    var _workunits = {};
 
     var Store = declare(null, {
         idProperty: "Wuid",
@@ -48,13 +48,14 @@ define([
                 _workunits[id] = new Workunit({
                     Wuid: id
                 });
+                _workunits[id].refresh();
             }
             return _workunits[id];
         },
 
         remove: function (item) {
             if (_workunits[this.getIdentity(item)]) {
-                _workunits[this.getIdentity(item)].killTimer();
+                _workunits[this.getIdentity(item)].stopMonitor();
                 delete _workunits[this.getIdentity(item)];
             }
         },
@@ -108,17 +109,19 @@ define([
         }
     });
 
-    var WorkunitData = declare([Stateful], {
-        Wuid: "",
-        changedCount: 0,
-
-        _StateIDSetter: function (StateID) {
-            if (this.StateID !== StateID) {
-                this.StateID = StateID;
-                var actionEx = lang.exists("ActionEx", this) ? this.ActionEx : null;
-                this.set("hasCompleted", WsWorkunits.isComplete(this.StateID, actionEx));
+    var Workunit = declare([ESPUtil.Singleton, ESPUtil.Monitor], {
+        //  Asserts  ---
+        _assertHasWuid: function () {
+            if (!this.Wuid) {
+                throw new Error("Wuid cannot be empty.");
             }
         },
+        //  Attributes  ---
+        _StateIDSetter: function (StateID) {
+            this.StateID = StateID;
+            var actionEx = lang.exists("ActionEx", this) ? this.ActionEx : null;
+            this.set("hasCompleted", WsWorkunits.isComplete(this.StateID, actionEx));
+        },
         _VariablesSetter: function (Variables) {
             var variables = [];
             for (var i = 0; i < Variables.ECLResult.length; ++i) {
@@ -142,7 +145,7 @@ define([
             }
             this.set("sourceFiles", sourceFiles);
         },
-        _TimersSetter: function(Timers) {
+        _TimersSetter: function (Timers) {
             var timers = [];
             for (var i = 0; i < Timers.ECLTimer.length; ++i) {
                 var timeParts = Timers.ECLTimer[i].Value.split(":");
@@ -158,42 +161,10 @@ define([
             }
             this.set("timers", timers);
         },
-        _GraphsSetter: function(Graphs) {
+        _GraphsSetter: function (Graphs) {
             this.set("graphs", Graphs.ECLGraph);
         },
-        getData: function () {
-            if (this instanceof WorkunitData) {
-               return (WorkunitData)(this);
-            }
-            return {};
-        },
-        updateData: function (response) {
-            var changed = false;
-            for (var key in response) {
-                if (this.get(key) !== response[key]) {
-                    changed = true;
-                    this.set(key, response[key]);
-                }
-            }
-            if (changed) {
-                this.set("changedCount", this.get("changedCount") + 1);
-            }
-        }
-    });
-
-    var Workunit = declare([WorkunitData], {
-        results: [],
-
-        graphs: [],
-
-        timers: [],
-
-        _assertHasWuid: function () {
-            if (!this.Wuid) {
-                throw new Error("Wuid cannot be empty.");
-            }
-        },
-
+        //  ---  ---  ---
         onCreate: function () {
         },
         onUpdate: function () {
@@ -208,51 +179,12 @@ define([
         isComplete: function () {
             return this.hasCompleted;
         },
-        setTimer: function (timer) {
-            this._timerTickCount = 0;
-            this._timer = timer;
-            this.onTimer();
-        },
-        killTimer: function () {
-            this._timerTickCount = 0;
-            this._timer = 0;
-        },
-        onTimer: function () {
-            this._timerTickCount++;
-
-            if (this.hasCompleted) {
-                this.killTimer();
-                return;
-            } else {
-                if (this._timerTickCount < 5 && this._timerTickCount % 1 === 0) {
-                    this.refresh();
-                } else if (this._timerTickCount < 30 && this._timerTickCount % 5 === 0) {
-                    this.refresh();
-                } else if (this._timerTickCount < 60 && this._timerTickCount % 10 === 0) {
-                    this.refresh();
-                } else if (this._timerTickCount < 120 && this._timerTickCount % 30 === 0) {
-                    this.refresh();
-                } else if (this._timerTickCount % 60 === 0) {
-                    this.refresh();
-                }
-            }
-
-            var context = this;
-            if (this._timer) {
-                setTimeout(function () {
-                    context.onTimer();
-                }, this._timer);
-            } else {
-                this_timerTickCount = 0;
-            }
-        },
         monitor: function (callback) {
             if (this.hasCompleted) {
                 if (callback) {
                     callback(this);
                 }
             } else {
-                this.setTimer(1000);
                 var context = this;
                 this.watch("changedCount", function (name, oldValue, newValue) {
                     if (oldValue !== newValue && newValue) {
@@ -429,6 +361,10 @@ define([
                 },
                 load: function (response) {
                     if (lang.exists("WUInfoResponse.Workunit", response)) {
+                        if (!args.onGetText && lang.exists("WUInfoResponse.Workunit.Query", response)) {
+                            //  A truncated version of ECL just causes issues  ---
+                            delete response.WUInfoResponse.Workunit.Query;
+                        }
                         context.updateData(response.WUInfoResponse.Workunit);
 
                         if (args.onGetText && lang.exists("Query.Text", context)) {

+ 13 - 3
esp/files/scripts/FileSpray.js

@@ -25,10 +25,11 @@ define([
     
     "dojox/xml/parser",    
 
+    "hpcc/ESPBase",
     "hpcc/ESPRequest"
 ], function (declare, lang, Deferred, QueryResults, JsonRest, Memory, Cache, Observable,
     parser,
-    ESPRequest) {
+    ESPBase, ESPRequest) {
     var GetDFUWorkunits = declare(null, {
         idProperty: "ID",
 
@@ -78,15 +79,24 @@ define([
     return {
         GetDFUWorkunits: GetDFUWorkunits,
 
+        Despray: function (params) {
+            return ESPRequest.send("FileSpray", "Despray", params);
+        },
+        Copy: function (params) {
+            return ESPRequest.send("FileSpray", "Copy", params);
+        },
+        Rename: function (params) {
+            return ESPRequest.send("FileSpray", "Rename", params);
+        },
         GetDFUWorkunit: function (params) {
-            ESPRequest.send("FileSpray", "GetDFUWorkunit", params);
+            return ESPRequest.send("FileSpray", "GetDFUWorkunit", params);
         },
 
         DFUWUFile: function (params) {
             lang.mixin(params, {
                 handleAs: "text"
             });
-            ESPRequest.send("FileSpray", "DFUWUFile", params);
+            return ESPRequest.send("FileSpray", "DFUWUFile", params);
         }
     };
 });

+ 1 - 0
esp/files/scripts/GetDFUWorkunitsWidget.js

@@ -217,6 +217,7 @@ define([
             if (this.initalized)
                 return;
             this.initalized = true;
+            this.tabContainer.selectChild(this.legacyPane);
         },
 
         initWorkunitsGrid: function() {

+ 132 - 46
esp/files/scripts/LFDetailsWidget.js

@@ -14,10 +14,15 @@
 #	limitations under the License.
 ############################################################################## */
 define([
+    "exports",
     "dojo/_base/declare",
     "dojo/_base/lang",
+    "dojo/_base/array",
     "dojo/dom",
+    "dojo/dom-attr",
     "dojo/dom-class",
+    "dojo/dom-form",
+    "dojo/query",
 
     "dijit/_TemplatedMixin",
     "dijit/_WidgetsInTemplateMixin",
@@ -26,8 +31,11 @@ define([
     "dijit/layout/ContentPane",
     "dijit/Toolbar",
     "dijit/TooltipDialog",
+    "dijit/form/Form",
     "dijit/form/SimpleTextarea",
+    "dijit/form/TextBox",
     "dijit/form/Button",
+    "dijit/form/DropDownButton",
     "dijit/TitlePane",
     "dijit/registry",
 
@@ -37,20 +45,23 @@ define([
     "hpcc/FilePartsWidget",
     "hpcc/WUDetailsWidget",
     "hpcc/DFUWUDetailsWidget",
-
+    "hpcc/TargetSelectWidget",
     "hpcc/ESPLogicalFile",
 
-    "dojo/text!../templates/LFDetailsWidget.html"
-], function (declare, lang, dom, domClass, 
-                _TemplatedMixin, _WidgetsInTemplateMixin, BorderContainer, TabContainer, ContentPane, Toolbar, TooltipDialog, SimpleTextarea, Button, TitlePane, registry,
-                _TabContainerWidget, ResultWidget, EclSourceWidget, FilePartsWidget, WUDetailsWidget, DFUWUDetailsWidget,
+    "dojo/text!../templates/LFDetailsWidget.html",
+
+    "dijit/TooltipDialog"
+], function (exports, declare, lang, arrayUtil, dom, domAttr, domClass, domForm, query,
+                _TemplatedMixin, _WidgetsInTemplateMixin, BorderContainer, TabContainer, ContentPane, Toolbar, TooltipDialog, Form, SimpleTextarea, TextBox, Button, DropDownButton, TitlePane, registry,
+                _TabContainerWidget, ResultWidget, EclSourceWidget, FilePartsWidget, WUDetailsWidget, DFUWUDetailsWidget, TargetSelectWidget,
                 ESPLogicalFile,
                 template) {
-    return declare("LFDetailsWidget", [_TabContainerWidget, _TemplatedMixin, _WidgetsInTemplateMixin], {
+    exports.fixCircularDependency = declare("LFDetailsWidget", [_TabContainerWidget, _TemplatedMixin, _WidgetsInTemplateMixin], {
         templateString: template,
         baseClass: "LFDetailsWidget",
         borderContainer: null,
         tabContainer: null,
+        summaryWidget: null,
         contentWidget: null,
         contentWidgetLoaded: false,
         sourceWidget: null,
@@ -83,45 +94,50 @@ define([
             this.workunitWidget = registry.byId(this.id + "_Workunit");
             this.dfuWorkunitWidget = registry.byId(this.id + "_DFUWorkunit");
             this.legacyPane = registry.byId(this.id + "_Legacy");
+            this.copyTargetSelect = registry.byId(this.id + "CopyTargetSelect");
+            this.desprayTargetSelect = registry.byId(this.id + "DesprayTargetSelect");
         },
 
         //  Hitched actions  ---
-        _onSave: function (event) {
-            var context = this;
-            this.logicalFile.save(dom.byId(context.id + "Description").value, {
-                onGetAll: function (response) {
-                    context.refreshFileDetails(response);
-                }
-            });
+        _onRefresh: function (event) {
+            this.logicalFile.refresh();
         },
-        _onRename: function (event) {
+        _onSave: function (event) {
             var context = this;
-            this.logicalFile.rename(dom.byId(this.id + "LogicalFileName2").innerHTML, {
-                onGetAll: function (response) {
-                    context.refreshFileDetails(response);
-                }
-            });
+            this.logicalFile.save(dom.byId(context.id + "Description").value);
         },
         _onDelete: function (event) {
             this.logicalFile.doDelete({
                 load: function (response) {
-                    //TODO
                 }
             });
         },
-        _onCopy: function (event) {
+        _onCopyOk: function (event) {
             this.logicalFile.copy({
-                load: function (response) {
-                    //TODO
-                }
+                request: domForm.toObject(this.id + "CopyDialog")
             });
+            registry.byId(this.id + "CopyDropDown").closeDropDown();
         },
-        _onDespray: function (event) {
-            var context = this;
+        _onCopyCancel: function (event) {
+            registry.byId(this.id + "CopyDropDown").closeDropDown();
+        },
+        _onDesprayOk: function (event) {
             this.logicalFile.despray({
-                load: function (response) {
-                }
+                request: domForm.toObject(this.id + "DesprayDialog")
+            });
+            registry.byId(this.id + "DesprayDropDown").closeDropDown();
+        },
+        _onDesprayCancel: function (event) {
+            registry.byId(this.id + "DesprayDropDown").closeDropDown();
+        },
+        _onRenameOk: function (event) {
+            this.logicalFile.rename({
+                request: domForm.toObject(this.id + "RenameDialog")
             });
+            registry.byId(this.id + "RenameDropDown").closeDropDown();
+        },
+        _onRenameCancel: function (event) {
+            registry.byId(this.id + "RenameDropDown").closeDropDown();
         },
 
         //  Implementation  ---
@@ -130,16 +146,31 @@ define([
                 return;
             this.initalized = true;
 
+            var context = this;
             if (params.Name) {
-                dom.byId(this.id + "LogicalFileName").innerHTML = params.Name;
+                //dom.byId(this.id + "Name").innerHTML = params.Name;
                 //dom.byId(this.id + "LogicalFileName2").value = params.Name;
-                this.logicalFile = new ESPLogicalFile({
-                    cluster: params.Cluster,
-                    logicalName: params.Name
+                this.logicalFile = ESPLogicalFile.Get(params.Name);
+                var data = this.logicalFile.getData();
+                for (key in data) {
+                    this.updateInput(key, null, data[key]);
+                }
+                this.logicalFile.watch(function (name, oldValue, newValue) {
+                    context.updateInput(name, oldValue, newValue);
                 });
-                this.refreshPage();
+                this.logicalFile.refresh();
             }
             this.selectChild(this.summaryWidget, true);
+            this.copyTargetSelect.init({
+                Groups: true
+            });
+            this.desprayTargetSelect.init({
+                DropZones: true,
+                callback: function (value, item) {
+                    context.updateInput("DesprayTargetIPAddress", null, item.machine.Netaddress);
+                    context.updateInput("DesprayTargetPath", null, item.machine.Directory + "/" + context.logicalFile.Filename);
+                }
+            });
         },
 
         initTab: function() {
@@ -152,7 +183,7 @@ define([
             } else if (currSel.id == this.sourceWidget.id && !this.sourceWidgetLoaded) {
                 this.sourceWidgetLoaded = true;
                 this.sourceWidget.init({
-                    ECL: this.logicalFile.DFUInfoResponse.Ecl
+                    ECL: this.logicalFile.Ecl
                 });
             } else if (currSel.id == this.defWidget.id && !this.defWidgetLoaded) {
                 var context = this;
@@ -173,22 +204,22 @@ define([
             } else if (currSel.id == this.filePartsWidget.id && !this.filePartsWidgetLoaded) {
                 this.filePartsWidgetLoaded = true;
                 this.filePartsWidget.init({
-                    fileParts: lang.exists("logicalFile.DFUInfoResponse.DFUFileParts.DFUPart", this) ? this.logicalFile.DFUInfoResponse.DFUFileParts.DFUPart : []
+                    fileParts: lang.exists("logicalFile.DFUFileParts.DFUPart", this) ? this.logicalFile.DFUFileParts.DFUPart : []
                 });
             } else if (this.workunitWidget && currSel.id == this.workunitWidget.id && !this.workunitWidgetLoaded) {
                 this.workunitWidgetLoaded = true;
                 this.workunitWidget.init({
-                    Wuid: this.logicalFile.DFUInfoResponse.Wuid
+                    Wuid: this.logicalFile.Wuid
                 });
             } else if (this.dfuWorkunitWidget && currSel.id == this.dfuWorkunitWidget.id && !this.workunitWidgetLoaded) {
                 this.dfuWorkunitWidgetLoaded = true;
                 this.dfuWorkunitWidget.init({
-                    Wuid: this.logicalFile.DFUInfoResponse.Wuid
+                    Wuid: this.logicalFile.Wuid
                 });
             } else if (currSel.id == this.legacyPane.id && !this.legacyPaneLoaded) {
                 this.legacyPaneLoaded = true;
                 this.legacyPane.set("content", dojo.create("iframe", {
-                    src: "/WsDfu/DFUInfo?Name=" + this.logicalFile.logicalName,//+ "&Cluster=" + this.logicalFile.cluster,
+                    src: "/WsDfu/DFUInfo?Name=" + this.logicalFile.Name,//+ "&Cluster=" + this.logicalFile.cluster,
                     style: "border: 0; width: 100%; height: 100%"
                 }));
             }
@@ -201,15 +232,69 @@ define([
             return true;
         },*/
 
-        refreshPage: function () {
-            var context = this;
-            this.logicalFile.getInfo({
-                onGetAll: function (response) {
-                    context.refreshFileDetails(response);
+        updateInput: function (name, oldValue, newValue) {
+            var domElem = dom.byId(this.id + name);
+            if (domElem) {
+                switch(domElem.tagName) {
+                    case "SPAN":
+                    case "DIV":
+                        domAttr.set(this.id + name, "innerHTML", newValue)
+                        break;
+                    case "INPUT":
+                    case "TEXTAREA":
+                        domAttr.set(this.id + name, "value", newValue)
+                        break;
+                    default:
+                        alert(domElem.tagName);
                 }
-            });
+            }
+            if (name === "Wuid") {
+                if (!newValue) {
+                    this.removeChild(this.workunitWidget);
+                    this.workunitWidget = null;
+                    this.removeChild(this.dfuWorkunitWidget);
+                    this.dfuWorkunitWidget = null;
+                } else if (this.workunitWidget && newValue[0] === "D") {
+                    this.removeChild(this.workunitWidget);
+                    this.workunitWidget = null;
+                } else if (this.dfuWorkunitWidget) {
+                    this.removeChild(this.dfuWorkunitWidget);
+                    this.dfuWorkunitWidget = null;
+                }
+            }
+            if (name === "Name") {
+                this.updateInput("RenameSourceName", oldValue, newValue);
+                this.updateInput("RenameTargetName", oldValue, newValue);
+                this.updateInput("DespraySourceName", oldValue, newValue);
+                this.updateInput("CopySourceName", oldValue, newValue);
+                this.updateInput("CopyTargetName", oldValue, newValue);
+            }
+
+            /*
+            var widget = registry.byId(this.id + name);
+            if (widget) {
+                if (widget.has("innerHTML")) {
+                    widget.set("innerHTML", newValue);
+                } else {
+                    widget.set("value", newValue);
+                }
+            } else {
+                var element = dom.byId(this.id + name);
+                if (element) {
+                    if (element.innerHTML) {
+                        element.innerHTML = newValue;
+                    } else {
+                        element.value = newValue;
+                    }
+                }
+            }
+            if (name === "Filename") {
+                registry.byId(this.id + "_Summary").set("title", newValue);
+            }
+            */
         },
         refreshFileDetails: function (fileDetails) {
+            /*
             if (fileDetails.Wuid && fileDetails.Wuid[0] == "D" && this.workunitWidget) {
                 this.removeChild(this.workunitWidget);
                 this.workunitWidget = null;
@@ -226,12 +311,13 @@ define([
             dom.byId(this.id + "JobName").innerHTML = fileDetails.JobName;
             dom.byId(this.id + "Wuid").innerHTML = fileDetails.Wuid;
             dom.byId(this.id + "Modification").innerHTML = fileDetails.Modified + " (UTC/GMT)";
-            dom.byId(this.id + "Directory").innerHTML = fileDetails.Dir;
+            dom.byId(this.id + "Dir").innerHTML = fileDetails.Dir;
             dom.byId(this.id + "RecordSize").innerHTML = fileDetails.RecordSize;
             dom.byId(this.id + "Count").innerHTML = fileDetails.RecordCount;
             this.contentWidget.set("title", "Content " + "(" + fileDetails.RecordCount + ")");
             dom.byId(this.id + "Filesize").innerHTML = fileDetails.Filesize;
-            dom.byId(this.id + "Pathmask").innerHTML = fileDetails.PathMask;
+            dom.byId(this.id + "PathMask").innerHTML = fileDetails.PathMask;
+            */
         }
 
     });

+ 1 - 1
esp/files/scripts/ResultsWidget.js

@@ -57,7 +57,7 @@ define([
             var retVal = this.tabMap[id];
             if (!retVal) {
                 if (lang.exists("Name", params) && lang.exists("Cluster", params)) {
-                    retVal = new LFDetailsWidget({
+                    retVal = new LFDetailsWidget.fixCircularDependency({
                         id: id,
                         title: params.Name,
                         params: params

+ 272 - 0
esp/files/scripts/SFDetailsWidget.js

@@ -0,0 +1,272 @@
+/*##############################################################################
+#	HPCC SYSTEMS software Copyright (C) 2012 HPCC Systems.
+#
+#	Licensed under the Apache License, Version 2.0 (the "License");
+#	you may not use this file except in compliance with the License.
+#	You may obtain a copy of the License at
+#
+#	   http://www.apache.org/licenses/LICENSE-2.0
+#
+#	Unless required by applicable law or agreed to in writing, software
+#	distributed under the License is distributed on an "AS IS" BASIS,
+#	WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#	See the License for the specific language governing permissions and
+#	limitations under the License.
+############################################################################## */
+define([
+    "exports",
+    "dojo/_base/declare",
+    "dojo/_base/lang",
+    "dojo/_base/array",
+    "dojo/dom",
+    "dojo/dom-attr",
+    "dojo/dom-class",
+    "dojo/dom-form",
+    "dojo/query",
+    "dojo/store/Memory",
+    "dojo/data/ObjectStore",
+
+    "dijit/_TemplatedMixin",
+    "dijit/_WidgetsInTemplateMixin",
+    "dijit/layout/BorderContainer",
+    "dijit/layout/TabContainer",
+    "dijit/layout/ContentPane",
+    "dijit/Toolbar",
+    "dijit/TooltipDialog",
+    "dijit/form/Form",
+    "dijit/form/SimpleTextarea",
+    "dijit/form/TextBox",
+    "dijit/form/Button",
+    "dijit/form/DropDownButton",
+    "dijit/TitlePane",
+    "dijit/registry",
+
+    "hpcc/_TabContainerWidget",
+    "hpcc/ResultWidget",
+    "hpcc/ECLSourceWidget",
+    "hpcc/FilePartsWidget",
+    "hpcc/WUDetailsWidget",
+    "hpcc/DFUWUDetailsWidget",
+    "hpcc/TargetSelectWidget",
+    "hpcc/ESPLogicalFile",
+
+    "dojo/text!../templates/SFDetailsWidget.html",
+
+    "dojox/grid/EnhancedGrid",
+    "dojox/grid/enhanced/plugins/Pagination",
+    "dojox/grid/enhanced/plugins/IndirectSelection",
+
+    "dijit/TooltipDialog"
+], function (exports, declare, lang, arrayUtil, dom, domAttr, domClass, domForm, query, Memory, ObjectStore,
+                _TemplatedMixin, _WidgetsInTemplateMixin, BorderContainer, TabContainer, ContentPane, Toolbar, TooltipDialog, Form, SimpleTextarea, TextBox, Button, DropDownButton, TitlePane, registry,
+                _TabContainerWidget, ResultWidget, EclSourceWidget, FilePartsWidget, WUDetailsWidget, DFUWUDetailsWidget, TargetSelectWidget,
+                ESPLogicalFile,
+                template) {
+    exports.fixCircularDependency = declare("SFDetailsWidget", [_TabContainerWidget, _TemplatedMixin, _WidgetsInTemplateMixin], {
+        templateString: template,
+        baseClass: "SFDetailsWidget",
+        borderContainer: null,
+        tabContainer: null,
+        summaryWidget: null,
+        legacyPane: null,
+        legacyPaneLoaded: false,
+        subfilesGrid: null,
+
+        logicalFile: null,
+        prevState: "",
+        initalized: false,
+
+        postCreate: function (args) {
+            this.inherited(arguments);
+            this.summaryWidget = registry.byId(this.id + "_Summary");
+            this.legacyPane = registry.byId(this.id + "_Legacy");
+            this.subfilesGrid = registry.byId(this.id + "SubfilesGrid");
+        },
+
+        startup: function (args) {
+            this.inherited(arguments);
+            this.initSubfilesGrid();
+        },
+
+        //  Hitched actions  ---
+        _onRefresh: function (event) {
+            this.logicalFile.refresh();
+        },
+        _onSave: function (event) {
+            var context = this;
+            this.logicalFile.save(dom.byId(context.id + "Description").value);
+        },
+        _onDelete: function (event) {
+            if (confirm('Delete Superfile?')) {
+                this.logicalFile.removeSubfiles(this.subfilesGrid.store.objectStore.data, true);
+            }
+        },
+        _onRemove: function (event) {
+            this.logicalFile.removeSubfiles(this.subfilesGrid.selection.getSelected());
+        },
+        _onCopyOk: function (event) {
+            this.logicalFile.copy({
+                request: domForm.toObject(this.id + "CopyDialog")
+            });
+            registry.byId(this.id + "CopyDropDown").closeDropDown();
+        },
+        _onCopyCancel: function (event) {
+            registry.byId(this.id + "CopyDropDown").closeDropDown();
+        },
+        _onDesprayOk: function (event) {
+            this.logicalFile.despray({
+                request: domForm.toObject(this.id + "DesprayDialog")
+            });
+            registry.byId(this.id + "DesprayDropDown").closeDropDown();
+        },
+        _onDesprayCancel: function (event) {
+            registry.byId(this.id + "DesprayDropDown").closeDropDown();
+        },
+        _onRenameOk: function (event) {
+            this.logicalFile.rename({
+                request: domForm.toObject(this.id + "RenameDialog")
+            });
+            registry.byId(this.id + "RenameDropDown").closeDropDown();
+        },
+        _onRenameCancel: function (event) {
+            registry.byId(this.id + "RenameDropDown").closeDropDown();
+        },
+
+        //  Implementation  ---
+        init: function (params) {
+            if (this.initalized)
+                return;
+            this.initalized = true;
+
+            var context = this;
+            if (params.Name) {
+                //dom.byId(this.id + "Name").innerHTML = params.Name;
+                //dom.byId(this.id + "LogicalFileName2").value = params.Name;
+                this.logicalFile = ESPLogicalFile.Get(params.Name);
+                var data = this.logicalFile.getData();
+                for (key in data) {
+                    this.updateInput(key, null, data[key]);
+                }
+                this.logicalFile.watch(function (name, oldValue, newValue) {
+                    context.updateInput(name, oldValue, newValue);
+                });
+                this.logicalFile.refresh();
+            }
+            this.selectChild(this.summaryWidget, true);
+            this.subfilesGrid.startup();
+        },
+
+        initSubfilesGrid: function () {
+            this.subfilesGrid.setStructure([
+                {
+                    name: "C",
+                    field: "isZipfile",
+                    width: "16px",
+                    formatter: function (compressed) {
+                        if (compressed == true) {
+                            return "C";
+                        }
+                        return "";
+                    }
+                },
+                {
+                    name: "K",
+                    field: "IsKeyFile",
+                    width: "16px",
+                    formatter: function (keyfile) {
+                        if (keyfile == true) {
+                            return "K";
+                        }
+                        return "";
+                    }
+                },
+                {
+                    name: "S",
+                    field: "isSuperfile",
+                    width: "16px",
+                    formatter: function (superfile) {
+                        if (superfile == true) {
+                            return "S";
+                        }
+                        return "";
+                    }
+                },
+                { name: "Logical Name", field: "Name", width: "32" },
+                { name: "Owner", field: "Owner", width: "8" },
+                { name: "Description", field: "Description", width: "12" },
+                { name: "Cluster", field: "ClusterName", width: "12" },
+                { name: "Records", field: "RecordCount", width: "8" },
+                { name: "Size", field: "Totalsize", width: "8" },
+                { name: "Parts", field: "Parts", width: "4" },
+                { name: "Modified (UTC/GMT)", field: "Modified", width: "12" }
+            ]);
+            /*
+            var objStore = ESPLogicalFile.CreateLFQueryObjectStore();
+            this.subfilesGrid.setStore(objStore);
+            this.subfilesGrid.setQuery(this.getFilter());
+
+            var context = this;
+            this.subfilesGrid.on("RowDblClick", function (evt) {
+                if (context.onRowDblClick) {
+                    var idx = evt.rowIndex;
+                    var item = this.getItem(idx);
+                    context.onRowDblClick(item);
+                }
+            }, true);
+
+            dojo.connect(this.subfilesGrid.selection, 'onSelected', function (idx) {
+                context.refreshActionState();
+            });
+            dojo.connect(this.subfilesGrid.selection, 'onDeselected', function (idx) {
+                context.refreshActionState();
+            });
+            */
+            this.subfilesGrid.startup();
+        },
+
+        initTab: function () {
+            var currSel = this.getSelectedChild();
+            if (currSel.id == this.legacyPane.id && !this.legacyPaneLoaded) {
+                this.legacyPaneLoaded = true;
+                this.legacyPane.set("content", dojo.create("iframe", {
+                    src: "/WsDfu/DFUInfo?Name=" + this.logicalFile.Name,//+ "&Cluster=" + this.logicalFile.cluster,
+                    style: "border: 0; width: 100%; height: 100%"
+                }));
+            }
+        },
+
+        showMessage: function (msg) {
+        },
+
+        updateInput: function (name, oldValue, newValue) {
+            var domElem = dom.byId(this.id + name);
+            if (domElem) {
+                switch(domElem.tagName) {
+                    case "SPAN":
+                    case "DIV":
+                        domAttr.set(this.id + name, "innerHTML", newValue)
+                        break;
+                    case "INPUT":
+                    case "TEXTAREA":
+                        domAttr.set(this.id + name, "value", newValue)
+                        break;
+                    default:
+                        alert(domElem.tagName);
+                }
+            }
+            if (name === "subfiles") {
+                var data = [];
+                arrayUtil.forEach(newValue.Item, function (item, idx) {
+                    data.push(ESPLogicalFile.Get(item));
+                });
+
+                this.subfilesGrid.rowSelectCell.toggleAllSelection(false);
+                var dataStore = new ObjectStore({ objectStore: new Memory({ data: data }) });
+                this.subfilesGrid.setStore(dataStore);
+                this.subfilesGrid.setQuery({
+                    Name: "*"
+                });
+            }
+        }
+    });
+});

+ 97 - 4
esp/files/scripts/TargetSelectWidget.js

@@ -16,6 +16,7 @@
 require([
     "dojo/_base/declare",
     "dojo/_base/lang",
+    "dojo/_base/array",
     "dojo/dom",
 
     "dijit/layout/_LayoutWidget",
@@ -27,7 +28,7 @@ require([
     "hpcc/WsTopology",
 
     "dojo/text!./templates/TargetSelectWidget.html"
-], function (declare, lang, dom,
+], function (declare, lang, arrayUtil, dom,
     _LayoutWidget, _TemplatedMixin, _WidgetsInTemplateMixin, Select, registry,
     WsTopology,
     template) {
@@ -68,15 +69,42 @@ require([
             if (params.Target) {
                 this._value = params.Target;
             }
-            this.loadTargets();
+            if (params.includeBlank) {
+                this.includeBlank = params.includeBlank;
+            }
+            if (params.Groups === true) {
+                this.loadGroups();
+            } else if (params.DropZones === true) {
+                this.loadDropZones();
+            } else {
+                this.loadTargets();
+            }
+            if (params.callback) {
+                this.callback = params.callback;
+            }
+            if (params.callback) {
+                this.callback = params.callback;
+            }
+            if (params.includeBlank) {
+            }
         },
 
         onChange: function (target) {
             this._value = target;
+            this._valueItem = null;
+            var context = this;
+            var idx = arrayUtil.forEach(this.targetSelectControl.options, function(item, idx) {
+                if (item.value === context._value) {
+                    context._valueItem = item;
+                }
+            });
+            if (this.callback) {
+                this.callback(this._value, this._valueItem);
+            }
         },
 
         setValue: function (target) {
-            if (target && this._value != target) {
+            if (target !== null && this._value != target) {
                 this._value = target;
                 this.targetSelectControl.set("value", target);
             }
@@ -86,6 +114,65 @@ require([
             return this._value;
         },
 
+        loadDropZones: function () {
+            var context = this;
+            WsTopology.TpServiceQuery({
+                load: function (response) {
+                    if (lang.exists("TpServiceQueryResponse.ServiceList.TpDropZones.TpDropZone", response)) {
+                        var targetData = response.TpServiceQueryResponse.ServiceList.TpDropZones.TpDropZone;
+                        context.targetSelectControl.options = [];
+                        if (context.includeBlank) {
+                            context.targetSelectControl.options.push({
+                                label: "",
+                                value: ""
+                            });
+                        }
+                        for (var i = 0; i < targetData.length; ++i) {
+                            context.targetSelectControl.options.push({
+                                label: targetData[i].Name,
+                                value: targetData[i].Name,
+                                machine: targetData[i].TpMachines.TpMachine[0]
+                            });
+                        }
+
+                        if (context._value == "") {
+                            context._value = context.targetSelectControl.options[0].value;
+                        }
+                        context.targetSelectControl.set("value", context._value);
+                    }
+                }
+            });
+        },
+
+        loadGroups: function () {
+            var context = this;
+            WsTopology.TpGroupQuery({
+                load: function (response) {
+                    if (lang.exists("TpGroupQueryResponse.TpGroups.TpGroup", response)) {
+                        var targetData = response.TpGroupQueryResponse.TpGroups.TpGroup;
+                        context.targetSelectControl.options = [];
+                        if (context.includeBlank) {
+                            context.targetSelectControl.options.push({
+                                label: "",
+                                value: ""
+                            });
+                        }
+                        for (var i = 0; i < targetData.length; ++i) {
+                            context.targetSelectControl.options.push({
+                                label: targetData[i].Name,
+                                value: targetData[i].Name
+                            });
+                        }
+
+                        if (context._value == "") {
+                            context._value = context.targetSelectControl.options[0].value;
+                        }
+                        context.targetSelectControl.set("value", context._value);
+                    }
+                }
+            });
+        },
+
         loadTargets: function () {
             var context = this;
             WsTopology.TpTargetClusterQuery({
@@ -93,6 +180,12 @@ require([
                     if (lang.exists("TpTargetClusterQueryResponse.TpTargetClusters.TpTargetCluster", response)) {
                         var targetData = response.TpTargetClusterQueryResponse.TpTargetClusters.TpTargetCluster;
                         context.targetSelectControl.options = [];
+                        if (context.includeBlank) {
+                            context.targetSelectControl.options.push({
+                                label: "",
+                                value: ""
+                            });
+                        }
                         var has_hthor = false;
                         for (var i = 0; i < targetData.length; ++i) {
                             context.targetSelectControl.options.push({
@@ -104,7 +197,7 @@ require([
                             }
                         }
 
-                        if (context._value == "") {
+                        if (!context.includeBlank && context._value == "") {
                             if (has_hthor) {
                                 context.setValue("hthor");
                             } else {

+ 17 - 17
esp/files/scripts/WUDetailsWidget.js

@@ -16,6 +16,7 @@
 define([
     "dojo/_base/declare",
     "dojo/dom",
+    "dojo/dom-attr",
     "dojo/dom-class",
     "dojo/query",
     "dojo/store/Memory",
@@ -47,7 +48,7 @@ define([
     "dijit/Toolbar",
     "dijit/TooltipDialog",
     "dijit/TitlePane"
-], function (declare, dom, domClass, query, Memory, ObjectStore,
+], function (declare, dom, domAttr, domClass, query, Memory, ObjectStore,
                 _TemplatedMixin, _WidgetsInTemplateMixin, registry,
                 _TabContainerWidget, ESPWorkunit, EclSourceWidget, TargetSelectWidget, SampleSelectWidget, GraphsWidget, ResultsWidget, InfoGridWidget, LogsWidget, TimingPageWidget, ECLPlaygroundWidget,
                 template) {
@@ -55,7 +56,6 @@ define([
         templateString: template,
         baseClass: "WUDetailsWidget",
         summaryWidget: null,
-        summaryForm: null,
         resultsWidget: null,
         resultsWidgetLoaded: false,
         filesWidget: null,
@@ -92,7 +92,6 @@ define([
             this.xmlWidget = registry.byId(this.id + "_XML");
             this.legacyPane = registry.byId(this.id + "_Legacy");
 
-            this.summaryForm = registry.byId(this.id + "SummaryForm");
             this.infoGridWidget = registry.byId(this.id + "InfoContainer");
         },
 
@@ -150,7 +149,7 @@ define([
                 this.wu.watch(function (name, oldValue, newValue) {
                     context.updateInput(name, oldValue, newValue);
                 });
-                this.wu.refresh();
+                //this.wu.refresh(true);
             }
             this.infoGridWidget.init(params);
             this.selectChild(this.summaryWidget, true);
@@ -234,18 +233,19 @@ define([
         },
 
         updateInput: function (name, oldValue, newValue) {
-            var input = query("input[id=" + this.id + name + "]", this.summaryForm)[0];
-            if (input) {
-                var dijitInput = registry.byId(this.id + name);
-                if (dijitInput) {
-                    dijitInput.set("value", newValue);
-                } else {
-                    input.value = newValue;
-                }
-            } else {
-                var div = query("div[id=" + this.id + name + "]", this.summaryForm)[0];
-                if (div) {
-                    div.innerHTML = newValue;
+            var domElem = dom.byId(this.id + name);
+            if (domElem) {
+                switch (domElem.tagName) {
+                    case "SPAN":
+                    case "DIV":
+                        domAttr.set(this.id + name, "innerHTML", newValue)
+                        break;
+                    case "INPUT":
+                    case "TEXTAREA":
+                        domAttr.set(this.id + name, "value", newValue)
+                        break;
+                    default:
+                        alert(domElem.tagName);
                 }
             }
             if (name === "Protected") {
@@ -309,7 +309,7 @@ define([
                         tooltip += "\n";
                     tooltip += newValue[i].Name;
                     if (newValue[i].Time)
-                        tooltip += " " + response[i].Time;
+                        tooltip += " " + newValue[i].Time;
                 }
                 this.graphsWidget.set("tooltip", tooltip);
             } else if (name === "StateID") {

+ 103 - 4
esp/files/scripts/WsDfu.js

@@ -18,10 +18,11 @@ define([
     "dojo/_base/declare",
     "dojo/_base/lang",
     "dojo/_base/Deferred",
+    "dojo/_base/array",
     "dojo/store/util/QueryResults",
 
     "hpcc/ESPRequest"
-], function (declare, lang, Deferred, QueryResults,
+], function (declare, lang, Deferred, arrayUtil, QueryResults,
     ESPRequest) {
     var DFUQuery = declare(null, {
         idProperty: "Name",
@@ -68,17 +69,115 @@ define([
     });
 
     return {
-        DFUQuery: DFUQuery,
+        DFUArrayAction: function (logicalFiles, actionType, callback) {
+            arrayUtil.forEach(logicalFiles, function (item, idx) {
+                item.qualifiedName = item.Name + "@" + item.ClusterName;
+            });
+            var request = {
+                LogicalFiles: logicalFiles,
+                Type: actionType
+            };
+            ESPRequest.flattenArray(request, "LogicalFiles", "qualifiedName");
+
+            return ESPRequest.send("WsDfu", "DFUArrayAction", {
+                request: request,
+                load: function (response) {
+                    if (lang.exists("DFUArrayActionResponse.DFUArrayActionResult", response)) {
+                        dojo.publish("hpcc/brToaster", {
+                            message: response.DFUArrayActionResponse.DFUArrayActionResult,
+                            type: "error",
+                            duration: -1
+                        });
+                    }
+
+                    if (callback && callback.load) {
+                        callback.load(response);
+                    }
+                },
+                error: function (err) {
+                    if (callback && callback.error) {
+                        callback.error(err);
+                    }
+                }
+            });
+        },
+
+        SuperfileAction: function (action, superfile, subfiles, removeSuperfile, callback) {
+            var request = {
+                action: action,
+                superfile: superfile,
+                subfiles: subfiles,
+                removeSuperfile: removeSuperfile
+            };
+            ESPRequest.flattenArray(request, "subfiles", "Name");
+
+            return ESPRequest.send("WsDfu", "SuperfileAction", {
+                request: request,
+                load: function (response) {
+                    if (lang.exists("SuperfileActionResponse", response)) {
+                        dojo.publish("hpcc/brToaster", {
+                            message: response.AddtoSuperfileResponse.Subfiles,
+                            type: "error",
+                            duration: -1
+                        });
+                    }
+
+                    if (callback && callback.load) {
+                        callback.load(response);
+                    }
+                },
+                error: function (err) {
+                    if (callback && callback.error) {
+                        callback.error(err);
+                    }
+                }
+            });
+        },
+
+        AddtoSuperfile: function (logicalFiles, superfile, existingFile, callback) {
+            var request = {
+                names: logicalFiles,
+                Superfile: superfile,
+                ExistingFile: existingFile ? 1 : 0
+            };
+            ESPRequest.flattenArray(request, "names", "Name");
+
+            return ESPRequest.send("WsDfu", "AddtoSuperfile", {
+                request: request,
+                load: function (response) {
+                    if (lang.exists("AddtoSuperfileResponse.Subfiles", response)) {
+                        dojo.publish("hpcc/brToaster", {
+                            message: response.AddtoSuperfileResponse.Subfiles,
+                            type: "error",
+                            duration: -1
+                        });
+                    }
+
+                    if (callback && callback.load) {
+                        callback.load(response);
+                    }
+                },
+                error: function (err) {
+                    if (callback && callback.error) {
+                        callback.error(err);
+                    }
+                }
+            });
+        },
+
+        DFUQuery: function (params) {
+            return ESPRequest.send("WsDfu", "DFUQuery", params);
+        },
 
         DFUInfo: function (params) {
-            ESPRequest.send("WsDfu", "DFUInfo", params);
+            return ESPRequest.send("WsDfu", "DFUInfo", params);
         },
 
         DFUDefFile: function (params) {
             lang.mixin(params, {
                 handleAs: "text"
             });
-            ESPRequest.send("WsDfu", "DFUDefFile", params);
+            return ESPRequest.send("WsDfu", "DFUDefFile", params);
         }
     };
 });

+ 9 - 0
esp/files/scripts/WsTopology.js

@@ -69,8 +69,17 @@ define([
     return {
         TpServiceQuery: TpServiceQuery,
 
+        TpServiceQuery: function (params) {
+            lang.mixin(params.request, {
+                Type: "ALLSERVICES"
+            });
+            ESPRequest.send("WsTopology", "TpServiceQuery", params);
+        },
         TpTargetClusterQuery: function (params) {
             ESPRequest.send("WsTopology", "TpTargetClusterQuery", params);
+        },
+        TpGroupQuery: function (params) {
+            ESPRequest.send("WsTopology", "TpGroupQuery", params);
         }
     };
 });

+ 13 - 7
esp/files/scripts/WsWorkunits.js

@@ -71,19 +71,25 @@ define([
                 Wuids: wuids,
                 ActionType: actionType
             };
-            ESPRequest.flattenArray(request, "Wuids");
+            ESPRequest.flattenArray(request, "Wuids", "Wuid");
 
             return ESPRequest.send("WsWorkunits", "WUAction", {
                 request: request,
                 load: function (response) {
                     if (lang.exists("WUActionResponse.ActionResults.WUActionResult", response)) {
                         arrayUtil.forEach(response.WUActionResponse.ActionResults.WUActionResult, function (item, index) {
-                            var isError = item.Result.indexOf("Failed:") === 0;
-                            dojo.publish("hpcc/brToaster", {
-                                message: "<h4>" + item.Action + " "+ item.Wuid + "</h4>" + "<p>" + item.Result + "</p>",
-                                type: isError ? "error" : "message",
-                                duration: isError ? -1 : 5
-                            });
+                            if (item.Result.indexOf("Failed:") === 0) {
+                                dojo.publish("hpcc/brToaster", {
+                                    message: "<h4>" + item.Action + " " + item.Wuid + "</h4>" + "<p>" + item.Result + "</p>",
+                                    type: "error",
+                                    duration: -1
+                                });
+                            } else {
+                                dojo.publish("hpcc/brToaster", {
+                                    message: "<h4>" + item.Action + " " + item.Wuid + "</h4>" + "<p>" + item.Result + "</p>",
+                                    type: "message"
+                                });
+                            }
                         });
                     }
 

+ 8 - 5
esp/files/scripts/_TabContainerWidget.js

@@ -1,6 +1,6 @@
 define([
-	"dojo/_base/declare", // declare
-	"dojo/_base/lang", // lang.mixin
+    "dojo/_base/declare", // declare
+    "dojo/_base/lang", // lang.mixin
     "dojo/dom",
     "dojo/hash",
     "dojo/router",
@@ -165,8 +165,11 @@ define([
         },
 
         removeChild: function (child) {
-            this._tabContainer.removeChild(child);
-            child.destroyRecursive();
+            var context = this;
+            setTimeout(function () {
+                context._tabContainer.removeChild(child);
+                //child.destroyRecursive();
+            }, 100);
         },
 
         removeAllChildren: function() {
@@ -185,7 +188,7 @@ define([
             if (currSel != child) {
                 var nodeExists = dom.byId(child);
                 if (nodeExists) {
-                    this._tabContainer.selectChild(nodeExists);
+                    this._tabContainer.selectChild(child);
                 }
             } else {
                 this.onNewTabSelection({

+ 1 - 1
esp/files/stub.htm

@@ -26,13 +26,13 @@
     <script src="CodeMirror2/mode/ecl/ecl.js"></script>
     <script src="CodeMirror2/mode/xml/xml.js"></script>
     <link href="css/ecl.css" rel="stylesheet">
-    <link href="css/hpcc.css" rel="stylesheet">
     <link href="dijit/themes/claro/claro.css" media="screen" rel="stylesheet">
     <link href="dojox/grid/resources/Grid.css" rel="stylesheet">
     <link href="dojox/grid/enhanced/resources/claro/EnhancedGrid.css" rel="stylesheet">
     <link href="dojox/grid/resources/claroGrid.css" rel="stylesheet">
     <link href="dojox/widget/Toaster/Toaster.css" rel="stylesheet">
     <link href="dojox/widget/Standby/Standby.css" rel="stylesheet">
+    <link href="css/hpcc.css" rel="stylesheet">
     <!-- load dojo and provide config via dojoConfig global -->
     <script>
         var dojoConfig = (function () {

+ 3 - 2
esp/files/stub.js

@@ -34,10 +34,11 @@ define([
         require(
             ["hpcc/" + params.Widget],
             function (WidgetClass) {
-                var widget = new WidgetClass({
+                var params = {
                     id: "stub",
                     "class": "hpccApp"
-                });
+                };
+                var widget = WidgetClass.fixCircularDependency ? new WidgetClass.fixCircularDependency(params) : new WidgetClass(params);
 
                 var standbyBackground = new Standby({
                     /*

+ 32 - 46
esp/files/templates/DFUWUQueryWidget.html

@@ -3,59 +3,45 @@
         <div id="${id}TabContainer" data-dojo-props="region: 'center', tabPosition: 'top'" style="width: 100%; height: 100%" data-dojo-type="dijit.layout.TabContainer">
             <div id="${id}_Workunits" style="width: 100%; height: 100%" data-dojo-props='title:"Logical Files"' data-dojo-type="dijit.layout.BorderContainer">
                 <div id="${id}Toolbar" class="topPanel" data-dojo-props="region: 'top'" data-dojo-type="dijit.Toolbar">
+                    <div id="${id}Refresh" data-dojo-attach-event="onClick:_onRefresh" data-dojo-props="iconClass:'iconRefresh'" data-dojo-type="dijit.form.Button">Refresh</div>
+                    <span data-dojo-type="dijit.ToolbarSeparator"></span>
                     <div id="${id}Open" data-dojo-attach-event="onClick:_onOpen" data-dojo-type="dijit.form.Button">Open</div>
                     <div id="${id}Delete" data-dojo-attach-event="onClick:_onDelete" data-dojo-type="dijit.form.Button">Delete</div>
-                    <div id="${id}AddToSuperfile" data-dojo-attach-event="onClick:_onAddToSuperfile" data-dojo-type="dijit.form.Button">Add To Superfile</div>
+                    <div id="${id}AddtoDropDown" data-dojo-type="dijit.form.DropDownButton">
+                        <span>Add To Superfile</span>
+                        <div data-dojo-type="dijit.TooltipDialog">
+                            <div id="${id}AddToSuperfileForm" style="width:480px" data-dojo-type="dijit.form.Form" >
+                                <div data-dojo-props="cols:1" data-dojo-type="dojox.layout.TableContainer">
+                                    <input id="${id}AddToSuperfileTargetName" name="Superfile" title="Logical&nbsp;File:" style="width:100%;" data-dojo-props="trim: true" data-dojo-type="dijit.form.TextBox" />
+                                    <input id="${id}AddToSuperfileTargetAppend" name="ExistingFile" title="Append:" data-dojo-type="dijit/form/CheckBox" />
+                                </div>
+                                <button data-dojo-attach-event="onClick:_onAddToSuperfileOk" data-dojo-type="dijit.form.Button">Add</button>
+                                <button data-dojo-attach-event="onClick:_onAddToSuperfileCancel" data-dojo-type="dijit.form.Button">Cancel</button>
+                            </div>
+                        </div>
+                    </div>
                     <span data-dojo-type="dijit.ToolbarSeparator"></span>
                     <div data-dojo-type="dijit.form.DropDownButton">
                         <span>Filter</span>
-                        <div data-dojo-type="dijit.TooltipDialog" class="toolTip">
-                            <form class="smallForm">
-                                <ul>
-                                    <li>
-                                        <label for="${id}LogicalFile">Name:</label>
-                                        <input id="${id}LogicalFile" data-dojo-props="trim: true, placeHolder:'*::somefile*'" data-dojo-type="dijit.form.TextBox"/>
-                                    </li>
-                                    <li> 
-                                        <label for="${id}Owner">Owner:</label>
-                                        <input id="${id}Owner" data-dojo-props="trim: true, placeHolder:'jsmi*'" data-dojo-type="dijit.form.TextBox"/>
-                                    </li>
-                                    <li> 
-                                        <label for="${id}Jobname">Job Name:</label>
-                                        <input id="${id}Jobname" data-dojo-props="trim: true, placeHolder:'log_analysis_1'" data-dojo-type="dijit.form.TextBox"/>
-                                    </li>
-                                    <li> 
-                                        <label for="${id}Cluster">Cluster:</label>
-                                        <input id="${id}Cluster" data-dojo-props="trim: true, placeHolder:'r?x*'" data-dojo-type="dijit.form.TextBox"/>
-                                    </li>
-                                    <li> 
-                                        <label for="${id}State">State:</label>
-                                        <input id="${id}State" data-dojo-props="trim: true, placeHolder:'failed'" data-dojo-type="dijit.form.TextBox"/>
-                                    </li>
-                                    <li> 
-                                        <label for="${id}ECL">ECL Text:</label>
-                                        <input id="${id}ECL" data-dojo-props="trim: true, placeHolder:':=dataset'" data-dojo-type="dijit.form.TextBox"/>
-                                    </li>
-                                    <li> 
-                                        <label for="${id}FromDate">From:</label>
-                                        <img src="img/cal.gif" alt="calendar"/>
-                                        <input id="${id}FromDate" data-dojo-props="trim: true, placeHolder:'7/28/2012'" data-dojo-type="dijit.form.DateTextBox"/>
-                                        <input id="${id}FromTime" class="smallCombo" data-dojo-props="trim: true, placeHolder:'7:30 AM'" data-dojo-type="dijit.form.TimeTextBox"/>
-                                    </li>
-                                    <li> 
-                                        <label for="${id}ToDate">To:</label>
-                                        <img src="img/cal.gif" alt="calendar"/>
-                                        <input id="${id}ToDate" data-dojo-props="trim: true, placeHolder:'7/28/2012'" data-dojo-type="dijit.form.DateTextBox"/>
-                                        <input id="${id}ToTime" class="smallCombo" data-dojo-props="trim: true, placeHolder:'7:30 PM'" data-dojo-type="dijit.form.TimeTextBox"/>
-                                    </li>
-                                    <li> 
-                                        <label for="${id}LastNDays">Last N Days:</label>
-                                        <input id="${id}LastNDays" data-dojo-props="trim: true, placeHolder:'2'" data-dojo-type="dijit.form.TextBox"/>
-                                    </li>
-                                </ul>
+                        <div data-dojo-type="dijit.TooltipDialog">
+                            <div id="${id}FilterForm" style="width:480px" data-dojo-type="dijit.form.Form">
+                                <div data-dojo-props="cols:2" data-dojo-type="dojox.layout.TableContainer">
+                                    <input id="${id}Name" title="Name:" name="LogicalName" colspan="2" style="width:100%;" data-dojo-props="trim: true, placeHolder:'*::somefile*'" data-dojo-type="dijit.form.TextBox" />
+                                    <input id="${id}Description" title="Description:" name="Description" colspan="2" style="width:100%;" data-dojo-props="trim: true, placeHolder:'Some*Description'" data-dojo-type="dijit.form.TextBox" />
+                                    <input id="${id}Owner" title="Owner:" name="Owner" colspan="2" data-dojo-props="trim: true, placeHolder:'JSmith'" data-dojo-type="dijit.form.TextBox" />
+                                    <div id="${id}ClusterTargetSelect" title="Cluster:" name="ClusterName" colspan="2" style="display: inline-block; vertical-align: middle" data-dojo-type="TargetSelectWidget">
+                                    </div>
+                                    <input id="${id}FromSize" title="From&nbsp;Size:" colspan="2" data-dojo-props="trim: true, placeHolder:'4096'" data-dojo-type="dijit.form.TextBox" />
+                                    <input id="${id}ToSize" title="To&nbsp;Size:" colspan="2" data-dojo-props="trim: true, placeHolder:'16777216'" data-dojo-type="dijit.form.TextBox" />
+                                    <input id="${id}FromDate" title="From&nbsp;Date:" data-dojo-props="trim: true, placeHolder:'7/28/2012'" data-dojo-type="dijit.form.DateTextBox" />
+                                    <input id="${id}FromTime" title="Time:" data-dojo-props="trim: true, placeHolder:'7:30 AM'" data-dojo-type="dijit.form.TimeTextBox" />
+                                    <input id="${id}ToDate" title="To&nbsp;Date:" data-dojo-props="trim: true, placeHolder:'7/28/2012'" data-dojo-type="dijit.form.DateTextBox" />
+                                    <input id="${id}ToTime" title="Time:" data-dojo-props="trim: true, placeHolder:'7:30 PM'" data-dojo-type="dijit.form.TimeTextBox" />
+                                    <input id="${id}LastNDays" title="Last&nbsp;N&nbsp;Days:" colspan="2" data-dojo-props="trim: true, placeHolder:'2'" data-dojo-type="dijit.form.TextBox" />
+                                </div>
                                 <button data-dojo-attach-event="onClick:_onFilterApply" data-dojo-type="dijit.form.Button">Apply</button>
                                 <button data-dojo-attach-event="onClick:_onFilterClear" data-dojo-type="dijit.form.Button">Clear</button>
-                            </form>
+                            </div>
                         </div>
                     </div>
                     <span data-dojo-type="dijit.ToolbarSeparator"></span>

+ 166 - 31
esp/files/templates/LFDetailsWidget.html

@@ -3,64 +3,199 @@
         <div id="${id}TabContainer" data-dojo-props="region: 'center', tabPosition: 'top'" style="width: 100%; height: 100%" data-dojo-type="dijit.layout.TabContainer">
             <div id="${id}_Summary" style="width: 100%; height: 100%" data-dojo-props='title:"Summary", iconClass:"iconLogicalFile"' data-dojo-type="dijit.layout.BorderContainer">
                 <div id="${id}Toolbar" class="topPanel" data-dojo-props="region: 'top'" data-dojo-type="dijit.Toolbar">
+                    <div id="${id}Refresh" data-dojo-attach-event="onClick:_onRefresh" data-dojo-props="iconClass:'iconRefresh'" data-dojo-type="dijit.form.Button">Refresh</div>
+                    <span data-dojo-type="dijit.ToolbarSeparator"></span>
                     <div id="${id}Save" data-dojo-attach-event="onClick:_onSave" data-dojo-type="dijit.form.Button">Save</div>
                     <div id="${id}Delete" data-dojo-attach-event="onClick:_onDelete" data-dojo-type="dijit.form.Button">Delete</div>
                     <span data-dojo-type="dijit.ToolbarSeparator"></span>
-                    <div id="${id}Clone" data-dojo-attach-event="onClick:_onCopy" data-dojo-type="dijit.form.Button">Copy</div>
-                    <div id="${id}Restart" data-dojo-attach-event="onClick:_onDespray" data-dojo-type="dijit.form.Button">Despray</div>
-                    <span data-dojo-type="dijit.ToolbarSeparator"></span>
+                    <div id="${id}CopyDropDown" class="smallForm" data-dojo-type="dijit.form.DropDownButton">
+                        <span>Copy</span>
+                        <div data-dojo-type="dijit.TooltipDialog" class="toolTip">
+                            <form id="${id}CopyDialog" class="smallForm">
+                                <fieldset>
+                                    <legend>Source</legend>
+                                    <ul>
+                                        <li>
+                                            <label for="${id}CopySourceName">Logical File: </label>
+                                            <div id="${id}CopySourceName"></div>
+                                        </li>
+                                    </ul>
+                                </fieldset>
+                                <fieldset>
+                                    <legend>Target</legend>
+                                    <ul>
+                                        <li>
+                                            <label for="${id}CopyTargetSelect">Group:</label>
+                                            <div id="${id}CopyTargetSelect" name="destGroup" style="display: inline-block; vertical-align: middle" data-dojo-type="TargetSelectWidget">
+                                            </div>
+                                        </li>
+                                        <li>
+                                            <label for="${id}CopyTargetName">Logical File: </label>
+                                            <input id="${id}CopyTargetName" name="destLogicalName" data-dojo-props="trim: true" data-dojo-type="dijit.form.TextBox" />
+                                        </li>
+                                        <li>
+                                            <label for="${id}CopyTargetWrap">Wrap: </label>
+                                            <input id="${id}CopyTargetWrap" name="Wrap" data-dojo-type="dijit/form/CheckBox" />
+                                        </li>
+                                        <li>
+                                            <label for="${id}CopyTargetNoSplit">No Split: </label>
+                                            <input id="${id}CopyTargetNoSplit" name="nosplit" data-dojo-type="dijit/form/CheckBox" />
+                                        </li>
+                                        <li>
+                                            <label for="${id}CopyTargetOverwrite">Overwrite: </label>
+                                            <input id="${id}CopyTargetOverwrite" name="overwrite" data-dojo-type="dijit/form/CheckBox" />
+                                        </li>
+                                        <li>
+                                            <label for="${id}CopyTargetCompress">Compress: </label>
+                                            <input id="${id}CopyTargetCompress" name="compress" data-dojo-type="dijit/form/CheckBox" />
+                                        </li>
+                                        <li>
+                                            <label for="${id}CopyTargetRetainSuperfileStructure">Retain Superfile Structure: </label>
+                                            <input id="${id}CopyTargetRetainSuperfileStructure" name="superCopy" data-dojo-type="dijit/form/CheckBox" />
+                                        </li>
+                                    </ul>
+                                </fieldset>
+                                <button data-dojo-attach-event="onClick:_onCopyOk" data-dojo-type="dijit.form.Button">Copy</button>
+                                <button data-dojo-attach-event="onClick:_onCopyCancel" data-dojo-type="dijit.form.Button">Cancel</button>
+                            </form>
+                        </div>
+                    </div>
+                    <div id="${id}RenameDropDown" class="smallForm" data-dojo-type="dijit.form.DropDownButton">
+                        <span>Rename</span>
+                        <div data-dojo-type="dijit.TooltipDialog" class="toolTip">
+                            <form id="${id}RenameDialog" class="smallForm">
+                                <fieldset>
+                                    <legend>Source</legend>
+                                    <ul>
+                                        <li>
+                                            <label for="${id}RenameSourceName">Logical File: </label>
+                                            <div id="${id}RenameSourceName"></div>
+                                        </li>
+                                    </ul>
+                                </fieldset>
+                                <fieldset>
+                                    <legend>Target</legend>
+                                    <ul>
+                                        <li>
+                                            <label for="${id}RenameTargetName">Logical File: </label>
+                                            <input id="${id}RenameTargetName" name="dstname" data-dojo-props="trim: true" data-dojo-type="dijit.form.TextBox" />
+                                        </li>
+                                        <li>
+                                            <label for="${id}RenameTargetOverwrite">Overwrite: </label>
+                                            <input id="${id}RenameTargetOverwrite" name="overwrite" data-dojo-type="dijit/form/CheckBox" />
+                                        </li>
+                                    </ul>
+                                </fieldset>
+                                <button data-dojo-attach-event="onClick:_onRenameOk" data-dojo-type="dijit.form.Button">Rename</button>
+                                <button data-dojo-attach-event="onClick:_onRenameCancel" data-dojo-type="dijit.form.Button">Cancel</button>
+                            </form>
+                        </div>
+                    </div>
+                    <div id="${id}DesprayDropDown" class="smallForm" data-dojo-type="dijit.form.DropDownButton">
+                        <span>Despray</span>
+                        <div data-dojo-type="dijit.TooltipDialog" class="toolTip">
+                            <form id="${id}DesprayDialog" class="smallForm">
+                                <fieldset>
+                                    <legend>Source</legend>
+                                    <ul>
+                                        <li>
+                                            <label for="${id}DespraySourceName">Logical File: </label>
+                                            <div id="${id}DespraySourceName"></div>
+                                        </li>
+                                    </ul>
+                                </fieldset>
+                                <fieldset>
+                                    <legend>Target</legend>
+                                    <ul>
+                                        <li>
+                                            <label for="${id}DesprayTargetSelect">Drop Zone:</label>
+                                            <div id="${id}DesprayTargetSelect" style="display: inline-block; vertical-align: middle" data-dojo-type="TargetSelectWidget">
+                                            </div>
+                                        </li>
+                                        <li>
+                                            <label for="${id}DesprayTargetIPAddress">IP Address</label>
+                                            <input id="${id}DesprayTargetIPAddress" name="destIP" data-dojo-props="trim: true" data-dojo-type="dijit.form.TextBox" />
+                                        </li>
+                                        <li>
+                                            <label for="${id}DesprayTargetPath">Path: </label>
+                                            <input id="${id}DesprayTargetPath" name="destPath" data-dojo-props="trim: true" data-dojo-type="dijit.form.TextBox" />
+                                        </li>
+                                        <li>
+                                            <label for="${id}DesprayTargetSplitPrefix">Split Prefix: </label>
+                                            <input id="${id}DesprayTargetSplitPrefix" name="splitprefix" data-dojo-props="trim: true" data-dojo-type="dijit.form.TextBox" />
+                                        </li>
+                                        <li>
+                                            <label for="${id}DesprayTargetUseSingleConnection">Use Single Connection: </label>
+                                            <input id="${id}DesprayTargetUseSingleConnection" name="SingleConnection" data-dojo-type="dijit/form/CheckBox" />
+                                        </li>
+                                        <li>
+                                            <label for="${id}DesprayTargetOverwrite">Overwrite?</label>
+                                            <input id="${id}DesprayTargetOverwrite" name="overwrite" data-dojo-type="dijit/form/CheckBox" />
+                                        </li>
+                                    </ul>
+                                </fieldset>
+                                <button data-dojo-attach-event="onClick:_onDesprayOk" data-dojo-type="dijit.form.Button">Despray</button>
+                                <button data-dojo-attach-event="onClick:_onDesprayCancel" data-dojo-type="dijit.form.Button">Cancel</button>
+                            </form>
+                        </div>
+                    </div>
                 </div>
                 <div data-dojo-props="region: 'center'" data-dojo-type="dijit.layout.ContentPane">
                     <h2>
-                        <img class="iconAlign" src="img/folder_table.png" />&nbsp<span id="${id}LogicalFileName" class="bold">LogicalFileName</span>
+                        <img class="iconAlign" src="img/folder_table.png" />&nbsp<span id="${id}Name" class="bold"></span>
                     </h2>
-                    <form>
+                    <form id="${id}SummaryForm">
                         <ul>
-                            <li> 
+                            <li>
                                 <label for="${id}Wuid">Workunit: </label>
                                 <div id="${id}Wuid"></div>
                             </li>
-                            <li> 
-                                <label for="Owner">Owner: </label>
+                            <li>
+                                <label for="${id}Owner">Owner: </label>
                                 <div id="${id}Owner"></div>
                             </li>
-                            <li> 
-                                <label for="Description">Description: </label>
-                                <div>
-                                    <input id="${id}Description" data-dojo-props="trim:true" data-dojo-type="dijit.form.SimpleTextarea" cols="55"
-                                    rows="4"/>
-                                </div>
-                            </li>                           
                             <li>
-                                <label for="Job Name">Job Name:</label>
-                                <div id="${id}JobName"></div>                         
+                                <label for="${id}ClusterName">Cluster Name: </label>
+                                <div id="${id}ClusterName"></div>
+                            </li>
+                            <li>
+                                <label for="${id}Description">Description: </label>
+                                <input id="${id}Description" data-dojo-props="trim:true" data-dojo-type="dijit.form.SimpleTextarea" cols="55" rows="4" />
                             </li>
                             <li>
-                                <label for="Filesize">Filesize:</label>
-                                <div id="${id}Filesize"></div>                         
+                                <label for="${id}JobName">Job Name:</label>
+                                <div id="${id}JobName"></div>
                             </li>
                             <li>
-                                <label for="Modification">Modification:</label>
-                                <div id="${id}Modification"></div>                         
+                                <label for="${id}Filesize">File Size:</label>
+                                <div id="${id}Filesize"></div>
                             </li>
                             <li>
-                                <label for="Directory">Directory:</label>
-                                <div id="${id}Directory"></div>                         
+                                <label for="${id}ActualSize">Actual Size:</label>
+                                <div id="${id}ActualSize"></div>
                             </li>
                             <li>
-                                <label for="Pathmask">Pathmask:</label>
-                                <div id="${id}Pathmask"></div>                         
+                                <label for="${id}Modification">Modification:</label>
+                                <div id="${id}Modification"></div>
                             </li>
                             <li>
-                                <label for="Record Size">Record Size:</label>
-                                <div id="${id}RecordSize"></div>                         
+                                <label for="${id}Directory">Directory:</label>
+                                <div id="${id}Directory"></div>
                             </li>
                             <li>
-                                <label for="Record Count">Record Count:</label>
-                                <div id="${id}Count"></div>                         
-                            </li> 
+                                <label for="${id}PathMask">Path Mask:</label>
+                                <div id="${id}PathMask"></div>
+                            </li>
+                            <li>
+                                <label for="${id}RecordSize">Record Size:</label>
+                                <div id="${id}RecordSize"></div>
+                            </li>
+                            <li>
+                                <label for="${id}Count">Record Count:</label>
+                                <div id="${id}Count"></div>
+                            </li>
                         </ul>
-                    </form>                                   
+                    </form>
                 </div>
             </div>
             <div id="${id}_Content" title="Contents" data-dojo-type="ResultWidget">

+ 46 - 0
esp/files/templates/SFDetailsWidget.html

@@ -0,0 +1,46 @@
+<div class="${baseClass}">
+    <div id="${id}BorderContainer" class="${baseClass}BorderContainer" style="width: 100%; height: 100%" data-dojo-type="dijit.layout.BorderContainer">
+        <div id="${id}TabContainer" data-dojo-props="region: 'center', tabPosition: 'top'" style="width: 100%; height: 100%" data-dojo-type="dijit.layout.TabContainer">
+            <div id="${id}_Summary" style="width: 100%; height: 100%" data-dojo-props='title:"Summary", iconClass:"iconLogicalFile"' data-dojo-type="dijit.layout.BorderContainer">
+                <div id="${id}Toolbar" class="topPanel" data-dojo-props="region: 'top'" data-dojo-type="dijit.Toolbar">
+                    <div id="${id}Refresh" data-dojo-attach-event="onClick:_onRefresh" data-dojo-props="iconClass:'iconRefresh'" data-dojo-type="dijit.form.Button">Refresh</div>
+                    <span data-dojo-type="dijit.ToolbarSeparator"></span>
+                    <div id="${id}Save" data-dojo-attach-event="onClick:_onSave" data-dojo-type="dijit.form.Button">Save</div>
+                    <div id="${id}Delete" data-dojo-attach-event="onClick:_onDelete" data-dojo-type="dijit.form.Button">Delete</div>
+                 </div>
+                <div data-dojo-props="region: 'center'" data-dojo-type="dijit.layout.ContentPane">
+                    <h2>
+                        <img class="iconAlign" src="img/folder_table.png" />&nbsp<span id="${id}Name" class="bold"></span>
+                    </h2>
+                    <form id="${id}SummaryForm">
+                        <ul>
+                            <li>
+                                <label for="${id}Description">Description: </label>
+                                <input id="${id}Description" data-dojo-props="trim:true" data-dojo-type="dijit.form.SimpleTextarea" cols="55" rows="4" />
+                            </li>
+                            <li>
+                                <label for="${id}Filesize">File Size:</label>
+                                <div id="${id}Filesize"></div>
+                            </li>
+                        </ul>
+                    </form>
+                </div>
+                <div style="width: 100%; height: 66%" data-dojo-props="region: 'bottom', splitter: true, minSize: 120" data-dojo-type="dijit.layout.BorderContainer">
+                    <div class="topPanel" data-dojo-props="region: 'top'" data-dojo-type="dijit.Toolbar">
+                        <div id="${id}Remove" data-dojo-attach-event="onClick:_onRemove" data-dojo-type="dijit.form.Button">Remove</div>
+                    </div>
+                    <div id="${id}SubfilesGrid" style="width: 100%; height: 100%" data-dojo-props="region: 'center', plugins: { pagination: { pageSizes: [25, 50, 100, 'All'], defaultPageSize: 50, description: true, sizeSwitch: true, pageStepper: true, gotoButton: true, maxPageStep: 4, position: 'bottom' }
+                        , indirectSelection: {
+                            headerSelector: true,
+                            width: '20px',
+                            styles: 'text-align: center;'
+                        }
+                    }" data-dojo-type="dojox.grid.EnhancedGrid">
+                    </div>
+                </div>
+            </div>
+            <div id="${id}_Legacy" title="Legacy Web Page" data-dojo-type="dijit.layout.ContentPane">
+            </div>
+        </div>
+    </div>
+</div>

+ 1 - 1
esp/files/templates/WUQueryWidget.html

@@ -18,7 +18,7 @@
                     <div id="${id}Deschedule" data-dojo-attach-event="onClick:_onDeschedule" data-dojo-type="dijit.form.Button">Deschedule</div>
                     <span data-dojo-type="dijit.ToolbarSeparator"></span>
                     <img id="${id}IconFilter" src="img/noFilter.png" class="iconNoFilter"/>
-                    <div data-dojo-type="dijit.form.DropDownButton" id="${id}FilterDropDown">
+                    <div id="${id}FilterDropDown" data-dojo-type="dijit.form.DropDownButton">
                         <span>Filter</span>
                         <div data-dojo-type="dijit.TooltipDialog" class="toolTip">
                             <form id="${id}filterForm" class="smallForm">