|
@@ -2,7 +2,7 @@ import { ECLEditor } from "@hpcc-js/codemirror";
|
|
|
import { Workunit } from "@hpcc-js/comms";
|
|
|
import { SplitPanel } from "@hpcc-js/phosphor";
|
|
|
import { DirectoryTree } from "@hpcc-js/tree";
|
|
|
-import { xml2json, XMLNode } from "@hpcc-js/util";
|
|
|
+import { xml2json } from "@hpcc-js/util";
|
|
|
import "dijit/form/Button";
|
|
|
import "dijit/layout/BorderContainer";
|
|
|
import "dijit/layout/ContentPane";
|
|
@@ -24,99 +24,6 @@ class DirectoryTreeEx extends DirectoryTree {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-interface TreeNode {
|
|
|
- label: string;
|
|
|
- content?: string;
|
|
|
- selected?: boolean;
|
|
|
- children?: TreeNode[];
|
|
|
-}
|
|
|
-
|
|
|
-interface FlatNode {
|
|
|
- path: string;
|
|
|
- data: TreeNode;
|
|
|
-}
|
|
|
-
|
|
|
-type TreeNodeMap = { [path: string]: TreeNode };
|
|
|
-
|
|
|
-function flattenizeNode(node: XMLNode, pathStr: string, pathToQuery: string, flatMap: TreeNodeMap, flatNodes: FlatNode[]): TreeNode | undefined {
|
|
|
- if (typeof pathStr === "undefined") {
|
|
|
- pathStr = "";
|
|
|
- }
|
|
|
- const hasName = node.name;
|
|
|
- const hasMoneyName = node["$"] && node["$"].name;
|
|
|
- const childrenUndefined = typeof node.children() === "undefined";
|
|
|
- const ret: TreeNode = {
|
|
|
- label: hasMoneyName ? node["$"].name : ""
|
|
|
- };
|
|
|
-
|
|
|
- if (!hasMoneyName && hasName) {
|
|
|
- ret.label = node.name;
|
|
|
- }
|
|
|
- const pathToNode = (pathStr.length > 0 ? pathStr + "." : "") + ret.label;
|
|
|
- if (!childrenUndefined && (node.children().length > 0 || !node.content)) {
|
|
|
- if (node.children().length === 1 && node.children()[0].name === "Text") {
|
|
|
- ret.content = node.children()[0].content.trim();
|
|
|
- if (pathToNode === pathToQuery) {
|
|
|
- ret.selected = true;
|
|
|
- }
|
|
|
- } else {
|
|
|
- ret.children = node.children().map(function (n) {
|
|
|
- return flattenizeNode(n, pathToNode, pathToQuery, flatMap, flatNodes);
|
|
|
- }).filter(function (n) {
|
|
|
- return n;
|
|
|
- });
|
|
|
- }
|
|
|
- } else if (typeof node.content === "string" && node.content.trim()) {
|
|
|
- ret.content = node.content.trim();
|
|
|
- ret.selected = pathToNode === pathToQuery;
|
|
|
- } else {
|
|
|
- if (node.name && node.name !== "Query") {
|
|
|
- return {
|
|
|
- label: node.name,
|
|
|
- content: JSON.stringify(node, undefined, 4)
|
|
|
- };
|
|
|
- }
|
|
|
- return undefined;
|
|
|
- }
|
|
|
- let path = (pathStr.length > 0 ? pathStr + "." : "") + ret.label;
|
|
|
- if (node["$"] && node["$"].name === "" && node["$"].key === "") {
|
|
|
- // new root folder
|
|
|
- path = "";
|
|
|
- }
|
|
|
- if (ret.label === "Query" && (!node.children || node.children().length === 0)) {
|
|
|
- // skipping 'Query' metadata
|
|
|
- } else {
|
|
|
- if (typeof flatMap[path] === "undefined") {
|
|
|
- flatMap[path] = ret;
|
|
|
- flatNodes.push({
|
|
|
- data: ret,
|
|
|
- path
|
|
|
- });
|
|
|
- } else {
|
|
|
- if (flatMap[path].label !== ret.label || ret.content) {
|
|
|
- flatNodes.push({
|
|
|
- data: ret,
|
|
|
- path
|
|
|
- });
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- let pathSegmentArr = path.split(".").slice(0, -1);
|
|
|
- while (pathSegmentArr.length > 0) {
|
|
|
- const ancestorPath = pathSegmentArr.join(".");
|
|
|
- if (!flatMap[ancestorPath]) {
|
|
|
- flatMap[ancestorPath] = { label: ancestorPath, children: [] };
|
|
|
- flatNodes.push({
|
|
|
- data: flatMap[ancestorPath],
|
|
|
- path: ancestorPath
|
|
|
- });
|
|
|
- }
|
|
|
- pathSegmentArr = pathSegmentArr.slice(0, -1);
|
|
|
- }
|
|
|
- }
|
|
|
- return undefined;
|
|
|
-}
|
|
|
-
|
|
|
type _Widget = {
|
|
|
id: string;
|
|
|
widget: any;
|
|
@@ -215,88 +122,217 @@ export class ECLArchiveWidget {
|
|
|
.render()
|
|
|
;
|
|
|
|
|
|
- wu.fetchArchive().then(function (archiveResp) {
|
|
|
- const json = xml2json(archiveResp);
|
|
|
- let pathToQuery = "";
|
|
|
- try {
|
|
|
- pathToQuery = json.children()[0].children().find(function (n) {
|
|
|
- return n.name && n.name.toLowerCase() === "query";
|
|
|
- })["$"].attributePath;
|
|
|
- } catch (e) { }
|
|
|
- const data: { label: string, children: any[] } = {
|
|
|
- label: "",
|
|
|
- children: []
|
|
|
- };
|
|
|
- if (json.children() && json.children()[0]) {
|
|
|
- const flatMap: TreeNodeMap = {};
|
|
|
- const flatNodes: FlatNode[] = [];
|
|
|
+ wu.fetchArchive().then(function (archiveXML) {
|
|
|
+ renderArchive(archiveXML);
|
|
|
+ });
|
|
|
+ }
|
|
|
|
|
|
- // 1) Flatten and normalize the nodes
|
|
|
- json.children()[0].children().forEach(function (n) {
|
|
|
- flattenizeNode(n, "", pathToQuery, flatMap, flatNodes);
|
|
|
- });
|
|
|
+ });
|
|
|
|
|
|
- // 2) Sort the flattened nodes
|
|
|
- flatNodes.sort(function (a, b) {
|
|
|
- const aIsFolder = !!a.data.children;
|
|
|
- const bIsFolder = !!b.data.children;
|
|
|
- if ((aIsFolder && bIsFolder) || (!aIsFolder && !bIsFolder)) {
|
|
|
- return a.path.toLowerCase() > b.path.toLowerCase() ? 1 : -1;
|
|
|
- } else {
|
|
|
- return aIsFolder && !bIsFolder ? -1 : 1;
|
|
|
- }
|
|
|
- });
|
|
|
+ function renderArchive(archiveXmlStr) {
|
|
|
+ const archive = xml2json(archiveXmlStr);
|
|
|
+
|
|
|
+ const data = {
|
|
|
+ label: ".",
|
|
|
+ children: []
|
|
|
+ };
|
|
|
+ let queryPath = "";
|
|
|
+
|
|
|
+ function walk(node, pathArr) {
|
|
|
+ if (node && typeof node === "object") {
|
|
|
+ // 1 === Move empty string nodes to root
|
|
|
+ const hasRootIndicator = node["$"]
|
|
|
+ && node["$"].key === ""
|
|
|
+ && node["$"].name === ""
|
|
|
+ ;
|
|
|
+ // 2 === Move $.flags to './Libraries'
|
|
|
+ const hasFlagsIndicator = node["$"]
|
|
|
+ && typeof node["$"].flags !== "undefined"
|
|
|
+ ;
|
|
|
+ // 3 === "./AdditionalFiles" should be renamed to "./Resources"
|
|
|
+ const hasResourceIndicator = node.name === "AdditionalFiles"
|
|
|
+ ;
|
|
|
+ // 4 === node._children.length === 1 && node._children[0].name === Text
|
|
|
+ const hasRedundantTextNode = node._children.length === 1
|
|
|
+ && node._children[0].name === "Text"
|
|
|
+ ;
|
|
|
+ // 5 === Archive folder contents goes to root
|
|
|
+ const hasArchiveIndicator = node._children
|
|
|
+ && node._children.length > 0
|
|
|
+ && node.name === "Archive"
|
|
|
+ ;
|
|
|
+ // 6 === Has query path
|
|
|
+ const hasQueryPath = node.name === "Query";
|
|
|
|
|
|
- // 3) Push sorted nodes into tree data shape
|
|
|
- flatNodes.forEach(function (flatNode, i) {
|
|
|
- const pathArr = flatNode.path.split(".").filter(function (n) {
|
|
|
- return n !== "";
|
|
|
+ // 7 === Check for base64 encoded
|
|
|
+ const hasABase64Indicator = node["$"]
|
|
|
+ && node["$"]["xsi:type"] === "SOAP-ENC:base64"
|
|
|
+ ;
|
|
|
+ let skipPathConcat = false;
|
|
|
+
|
|
|
+ if (hasRootIndicator || hasArchiveIndicator) { // 1 & 5
|
|
|
+ pathArr = [];
|
|
|
+ skipPathConcat = true;
|
|
|
+ } else if (hasFlagsIndicator) { // 2
|
|
|
+ pathArr = ["Libraries"];
|
|
|
+ if (hasRedundantTextNode) { // 4
|
|
|
+ node.content = node._children[0].content;
|
|
|
+ node._children = [];
|
|
|
+ }
|
|
|
+ skipPathConcat = true;
|
|
|
+ } else if (hasResourceIndicator) { // 3
|
|
|
+ pathArr = ["Resources"];
|
|
|
+ skipPathConcat = true;
|
|
|
+ }
|
|
|
+ if (node["$"]) {
|
|
|
+ let label = getNodeLabel(node);
|
|
|
+ if (hasQueryPath) { // 6
|
|
|
+ if (node["$"].attributePath) {
|
|
|
+ queryPath = node["$"].attributePath;
|
|
|
+ } else if (node.content && node.content.trim() && label.trim() === "") {
|
|
|
+ label = "<unnamed query>";
|
|
|
+ queryPath = label;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (node.content && node.content.trim()) {
|
|
|
+ let _data = data;
|
|
|
+ pathArr.forEach(function(pathSegmentName, i) {
|
|
|
+ const found = _data.children.find(function(_n) {
|
|
|
+ return _n.label === pathSegmentName;
|
|
|
+ });
|
|
|
+ if (found) {
|
|
|
+ _data = found;
|
|
|
+ } else {
|
|
|
+ const _temp = {
|
|
|
+ label: pathSegmentName,
|
|
|
+ path: pathArr.slice(0, i).join("."),
|
|
|
+ children: []
|
|
|
+ };
|
|
|
+ _data.children.push(_temp);
|
|
|
+ _data = _temp;
|
|
|
+ }
|
|
|
+ })
|
|
|
+ ;
|
|
|
+ let content = node.content.trim();
|
|
|
+ content = hasABase64Indicator ? atob(content) : content;
|
|
|
+ const path = pathArr.join(".");
|
|
|
+ _data.children.push({
|
|
|
+ label,
|
|
|
+ path,
|
|
|
+ content,
|
|
|
+ fullPath: path.length === 0 ? label : path + "." + label,
|
|
|
+ selected: false
|
|
|
});
|
|
|
- if (pathArr.length === 0) {
|
|
|
- data.children = data.children.concat(flatNode.data.children);
|
|
|
+ } else if (node._children) {
|
|
|
+ if (skipPathConcat) {
|
|
|
+ node._children.forEach(function(_node, i) {
|
|
|
+ walk(_node, [...pathArr]);
|
|
|
+ });
|
|
|
} else {
|
|
|
- const parentPath = pathArr.slice(0, -1).join(".");
|
|
|
- if (!parentPath) {
|
|
|
- data.children.push(flatNode.data);
|
|
|
- } else {
|
|
|
- if (!flatNode.data.content || !flatNode.data.content.trim()) {
|
|
|
- flatNode.data.label = pathArr.slice(-1).join("");
|
|
|
+ if (label.indexOf(".") !== -1) {
|
|
|
+ const name = getNodeName(node);
|
|
|
+ if (name.indexOf(".") !== -1) {
|
|
|
+ name.split(".").forEach(function(seg, i, arr) {
|
|
|
+ if (i < arr.length - 1) {
|
|
|
+ pathArr.push(seg);
|
|
|
+ }
|
|
|
+ label = seg;
|
|
|
+ });
|
|
|
}
|
|
|
- if (!flatMap[parentPath].children) {
|
|
|
- flatMap[parentPath].children = [];
|
|
|
- }
|
|
|
- flatMap[parentPath].children.push(flatNode.data);
|
|
|
}
|
|
|
+ node._children.forEach(function(_node, i) {
|
|
|
+ walk(_node, [...pathArr].concat([label]));
|
|
|
+ });
|
|
|
}
|
|
|
- });
|
|
|
+ }
|
|
|
}
|
|
|
- context.directoryTree
|
|
|
- .data({})
|
|
|
- .render(function () {
|
|
|
- context.directoryTree
|
|
|
- .data(data)
|
|
|
- .iconSize(16)
|
|
|
- .rowItemPadding(2)
|
|
|
- .textFileIcon("fa fa-file-code-o")
|
|
|
- .render()
|
|
|
- ;
|
|
|
- context.directoryTree.rowClick = function (contentStr) {
|
|
|
- context.editor.text(contentStr);
|
|
|
- };
|
|
|
- if (!context.archiveViewer.isDOMHidden()) {
|
|
|
- const rw = context.directoryTree.calcWidth() + 20;
|
|
|
- const pw = context.archiveViewer.width();
|
|
|
- const ratio = rw / pw;
|
|
|
- context.archiveViewer.relativeSizes([ratio, 1 - ratio]).resize().render();
|
|
|
- }
|
|
|
- });
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ walk((archive as any)._children[0], []);
|
|
|
|
|
|
+ recursiveSort(data);
|
|
|
+
|
|
|
+ let firstTierLabels = data.children.map(function(n) {
|
|
|
+ return n.label;
|
|
|
+ });
|
|
|
+
|
|
|
+ const librariesIdx = firstTierLabels.indexOf("Libraries");
|
|
|
+ if (librariesIdx !== -1) {
|
|
|
+ const lib = data.children[librariesIdx];
|
|
|
+ data.children.splice(librariesIdx, 1);
|
|
|
+ data.children.push(lib);
|
|
|
+ firstTierLabels = data.children.map(function(n) {
|
|
|
+ return n.label;
|
|
|
+ });
|
|
|
+ }
|
|
|
+ const resourcesIdx = firstTierLabels.indexOf("Resources");
|
|
|
+ if (resourcesIdx !== -1) {
|
|
|
+ const res = data.children[resourcesIdx];
|
|
|
+ data.children.splice(resourcesIdx, 1);
|
|
|
+ data.children.push(res);
|
|
|
+ }
|
|
|
+ context.directoryTree
|
|
|
+ .data({})
|
|
|
+ .render(function () {
|
|
|
+ context.directoryTree
|
|
|
+ .data(data)
|
|
|
+ .iconSize(16)
|
|
|
+ .rowItemPadding(2)
|
|
|
+ .textFileIcon("fa fa-file-code-o")
|
|
|
+ .render()
|
|
|
+ ;
|
|
|
context.directoryTree.rowClick = function (contentStr) {
|
|
|
context.editor.text(contentStr);
|
|
|
};
|
|
|
+ if (!context.archiveViewer.isDOMHidden()) {
|
|
|
+ const rw = context.directoryTree.calcWidth() + 20;
|
|
|
+ const pw = context.archiveViewer.width();
|
|
|
+ const ratio = rw / pw;
|
|
|
+ context.archiveViewer.relativeSizes([ratio, 1 - ratio]).resize().render();
|
|
|
+ }
|
|
|
});
|
|
|
+
|
|
|
+ context.directoryTree.rowClick = function (contentStr) {
|
|
|
+ context.editor.text(contentStr);
|
|
|
+ };
|
|
|
+
|
|
|
+ function recursiveSort(n) {
|
|
|
+ if (n.fullPath === queryPath) {
|
|
|
+ n.selected = true;
|
|
|
+ n.iconClass = "fa fa-code";
|
|
|
+ }
|
|
|
+ if (n && n.children) {
|
|
|
+ n.children.sort(function(a, b) {
|
|
|
+ const aContent = a.content && a.content.trim();
|
|
|
+ const bContent = b.content && b.content.trim();
|
|
|
+ if (aContent && !bContent) {
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ if (!aContent && bContent) {
|
|
|
+ return 1;
|
|
|
+ }
|
|
|
+ return a.label.toLowerCase() < b.label.toLowerCase() ? -1 : 1;
|
|
|
+ });
|
|
|
+ n.children.forEach(recursiveSort);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ function getNodeName(node) {
|
|
|
+ return node["$"].name || node["$"].key;
|
|
|
}
|
|
|
+ function getNodeLabel(node) {
|
|
|
+ let label = getNodeName(node);
|
|
|
+ label = label ? label : "";
|
|
|
+ if (!label && node["$"].resourcePath) {
|
|
|
+ label = node["$"].resourcePath.split("/").pop();
|
|
|
+ } else if (!label && node["$"].originalFilename) {
|
|
|
+ label = node["$"].originalFilename.split("\\").pop();
|
|
|
+ } else if (!label && node["$"].originalFilename) {
|
|
|
+ label = node["$"].originalFilename.split("\\").pop();
|
|
|
+ }
|
|
|
+ return label;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- });
|
|
|
}
|
|
|
}
|