aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--.vscode/launch.json2
-rw-r--r--.vscode/settings.json4
-rw-r--r--deploy/assets/env.json15
-rw-r--r--deploy/index.html4
-rw-r--r--deploy/mobile/image.html15
-rw-r--r--deploy/mobile/ink.html13
-rw-r--r--package.json86
-rw-r--r--src/.DS_Storebin6148 -> 6148 bytes
-rw-r--r--src/Utils.ts46
-rw-r--r--src/client/Server.ts138
-rw-r--r--src/client/SocketStub.ts23
-rw-r--r--src/client/documents/Documents.ts219
-rw-r--r--src/client/northstar/core/BaseObject.ts12
-rw-r--r--src/client/northstar/core/attribute/AttributeModel.ts111
-rw-r--r--src/client/northstar/core/attribute/AttributeTransformationModel.ts47
-rw-r--r--src/client/northstar/core/attribute/CalculatedAttributeModel.ts42
-rw-r--r--src/client/northstar/core/brusher/IBaseBrushable.ts13
-rw-r--r--src/client/northstar/core/brusher/IBaseBrusher.ts11
-rw-r--r--src/client/northstar/core/filter/FilterModel.ts84
-rw-r--r--src/client/northstar/core/filter/FilterOperand.ts5
-rw-r--r--src/client/northstar/core/filter/FilterType.ts6
-rw-r--r--src/client/northstar/core/filter/IBaseFilterConsumer.ts12
-rw-r--r--src/client/northstar/core/filter/IBaseFilterProvider.ts8
-rw-r--r--src/client/northstar/core/filter/ValueComparision.ts74
-rw-r--r--src/client/northstar/dash-fields/HistogramField.ts73
-rw-r--r--src/client/northstar/dash-nodes/HistogramBinPrimitiveCollection.ts238
-rw-r--r--src/client/northstar/dash-nodes/HistogramBox.scss34
-rw-r--r--src/client/northstar/dash-nodes/HistogramBox.tsx174
-rw-r--r--src/client/northstar/dash-nodes/HistogramBoxPrimitives.scss30
-rw-r--r--src/client/northstar/dash-nodes/HistogramBoxPrimitives.tsx124
-rw-r--r--src/client/northstar/dash-nodes/HistogramLabelPrimitives.scss13
-rw-r--r--src/client/northstar/dash-nodes/HistogramLabelPrimitives.tsx79
-rw-r--r--src/client/northstar/manager/Gateway.ts269
-rw-r--r--src/client/northstar/model/ModelExtensions.ts48
-rw-r--r--src/client/northstar/model/ModelHelpers.ts215
-rw-r--r--src/client/northstar/model/binRanges/AlphabeticVisualBinRange.ts52
-rw-r--r--src/client/northstar/model/binRanges/DateTimeVisualBinRange.ts105
-rw-r--r--src/client/northstar/model/binRanges/NominalVisualBinRange.ts52
-rw-r--r--src/client/northstar/model/binRanges/QuantitativeVisualBinRange.ts90
-rw-r--r--src/client/northstar/model/binRanges/VisualBinRange.ts36
-rw-r--r--src/client/northstar/model/binRanges/VisualBinRangeHelper.ts71
-rw-r--r--src/client/northstar/model/idea/MetricTypeMapping.ts30
-rw-r--r--src/client/northstar/model/idea/idea.ts8551
-rw-r--r--src/client/northstar/operations/BaseOperation.ts178
-rw-r--r--src/client/northstar/operations/HistogramOperation.ts124
-rw-r--r--src/client/northstar/utils/ArrayUtil.ts90
-rw-r--r--src/client/northstar/utils/Extensions.ts29
-rw-r--r--src/client/northstar/utils/GeometryUtil.ts129
-rw-r--r--src/client/northstar/utils/IDisposable.ts3
-rw-r--r--src/client/northstar/utils/IEquatable.ts3
-rw-r--r--src/client/northstar/utils/KeyCodes.ts137
-rw-r--r--src/client/northstar/utils/LABColor.ts90
-rw-r--r--src/client/northstar/utils/MathUtil.ts241
-rw-r--r--src/client/northstar/utils/PartialClass.ts7
-rw-r--r--src/client/northstar/utils/SizeConverter.ts99
-rw-r--r--src/client/northstar/utils/StyleContants.ts95
-rw-r--r--src/client/northstar/utils/Utils.ts75
-rw-r--r--src/client/util/DocumentManager.ts99
-rw-r--r--src/client/util/DragManager.ts190
-rw-r--r--src/client/util/RichTextRules.ts43
-rw-r--r--src/client/util/RichTextSchema.tsx23
-rw-r--r--src/client/util/Scripting.ts10
-rw-r--r--src/client/util/SelectionManager.ts15
-rw-r--r--src/client/util/TooltipTextMenu.scss7
-rw-r--r--src/client/util/TooltipTextMenu.tsx38
-rw-r--r--src/client/util/UndoManager.ts57
-rw-r--r--src/client/util/jsx-decl.d.ts1
-rw-r--r--src/client/util/type_decls.d10
-rw-r--r--src/client/views/.DS_Store (renamed from .DS_Store)bin8196 -> 8196 bytes
-rw-r--r--src/client/views/ContextMenu.scss76
-rw-r--r--src/client/views/ContextMenu.tsx33
-rw-r--r--src/client/views/DocumentDecorations.scss100
-rw-r--r--src/client/views/DocumentDecorations.tsx161
-rw-r--r--src/client/views/EditableView.scss6
-rw-r--r--src/client/views/EditableView.tsx21
-rw-r--r--src/client/views/InkingCanvas.scss58
-rw-r--r--src/client/views/InkingCanvas.tsx213
-rw-r--r--src/client/views/InkingControl.scss135
-rw-r--r--src/client/views/InkingControl.tsx74
-rw-r--r--src/client/views/InkingStroke.tsx22
-rw-r--r--src/client/views/Main.scss152
-rw-r--r--src/client/views/Main.tsx439
-rw-r--r--src/client/views/_global_variables.scss17
-rw-r--r--src/client/views/_nodeModuleOverrides.scss23
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx84
-rw-r--r--src/client/views/collections/CollectionFreeFormView.scss68
-rw-r--r--src/client/views/collections/CollectionFreeFormView.tsx322
-rw-r--r--src/client/views/collections/CollectionPDFView.scss27
-rw-r--r--src/client/views/collections/CollectionPDFView.tsx28
-rw-r--r--src/client/views/collections/CollectionSchemaView.scss165
-rw-r--r--src/client/views/collections/CollectionSchemaView.tsx298
-rw-r--r--src/client/views/collections/CollectionTreeView.scss35
-rw-r--r--src/client/views/collections/CollectionTreeView.tsx164
-rw-r--r--src/client/views/collections/CollectionVideoView.scss40
-rw-r--r--src/client/views/collections/CollectionVideoView.tsx130
-rw-r--r--src/client/views/collections/CollectionView.tsx68
-rw-r--r--src/client/views/collections/CollectionViewBase.tsx187
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss6
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx37
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.scss10
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx106
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx115
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss86
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx312
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.scss14
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx201
-rw-r--r--src/client/views/collections/collectionFreeForm/PreviewCursor.scss23
-rw-r--r--src/client/views/collections/collectionFreeForm/PreviewCursor.tsx119
-rw-r--r--src/client/views/nodes/AudioBox.scss4
-rw-r--r--src/client/views/nodes/AudioBox.tsx44
-rw-r--r--src/client/views/nodes/CollectionFreeFormDocumentView.tsx4
-rw-r--r--src/client/views/nodes/DocumentContentsView.tsx63
-rw-r--r--src/client/views/nodes/DocumentView.scss44
-rw-r--r--src/client/views/nodes/DocumentView.tsx215
-rw-r--r--src/client/views/nodes/FieldTextBox.scss14
-rw-r--r--src/client/views/nodes/FieldView.tsx40
-rw-r--r--src/client/views/nodes/FormattedTextBox.scss64
-rw-r--r--src/client/views/nodes/FormattedTextBox.tsx28
-rw-r--r--src/client/views/nodes/ImageBox.scss27
-rw-r--r--src/client/views/nodes/ImageBox.tsx23
-rw-r--r--src/client/views/nodes/KeyValueBox.scss46
-rw-r--r--src/client/views/nodes/KeyValueBox.tsx69
-rw-r--r--src/client/views/nodes/KeyValuePair.scss12
-rw-r--r--src/client/views/nodes/KeyValuePair.tsx45
-rw-r--r--src/client/views/nodes/LinkBox.scss65
-rw-r--r--src/client/views/nodes/LinkBox.tsx122
-rw-r--r--src/client/views/nodes/LinkEditor.scss43
-rw-r--r--src/client/views/nodes/LinkEditor.tsx58
-rw-r--r--src/client/views/nodes/LinkMenu.scss21
-rw-r--r--src/client/views/nodes/LinkMenu.tsx54
-rw-r--r--src/client/views/nodes/PDFBox.scss2
-rw-r--r--src/client/views/nodes/PDFBox.tsx38
-rw-r--r--src/client/views/nodes/VideoBox.scss4
-rw-r--r--src/client/views/nodes/VideoBox.tsx78
-rw-r--r--src/client/views/nodes/WebBox.scss1
-rw-r--r--src/debug/Test.tsx43
-rw-r--r--src/debug/Viewer.tsx4
-rw-r--r--src/fields/AudioField.ts31
-rw-r--r--src/fields/Document.ts119
-rw-r--r--src/fields/HtmlField.ts2
-rw-r--r--src/fields/ImageField.ts4
-rw-r--r--src/fields/KVPField.ts30
-rw-r--r--src/fields/KeyStore.ts16
-rw-r--r--src/fields/ListField.ts55
-rw-r--r--src/fields/PDFField.ts4
-rw-r--r--src/fields/TupleField.ts59
-rw-r--r--src/fields/VideoField.ts30
-rw-r--r--src/fields/WebField.ts4
-rw-r--r--src/mobile/ImageUpload.scss13
-rw-r--r--src/mobile/ImageUpload.tsx77
-rw-r--r--src/mobile/InkControls.tsx (renamed from src/fields/KVPField)0
-rw-r--r--src/server/Message.ts2
-rw-r--r--src/server/RouteStore.ts30
-rw-r--r--src/server/ServerUtil.ts46
-rw-r--r--src/server/authentication/config/passport.ts7
-rw-r--r--src/server/authentication/controllers/WorkspacesMenu.css3
-rw-r--r--src/server/authentication/controllers/WorkspacesMenu.tsx89
-rw-r--r--src/server/authentication/controllers/user.ts107
-rw-r--r--src/server/authentication/controllers/user_controller.ts264
-rw-r--r--src/server/authentication/models/current_user_utils.ts102
-rw-r--r--src/server/authentication/models/user_model.ts (renamed from src/server/authentication/models/User.ts)17
-rw-r--r--src/server/database.ts29
-rw-r--r--src/server/index.ts229
-rw-r--r--src/server/public/files/upload_e72669595eae4384a2a32196496f4f05.pdfbin0 -> 548616 bytes
-rw-r--r--src/typings/index.d.ts604
-rw-r--r--views/forgot.pug22
-rw-r--r--views/layout.pug5
-rw-r--r--views/login.pug28
-rw-r--r--views/reset.pug22
-rw-r--r--views/resources/dashlogo.pngbin0 -> 7169 bytes
-rw-r--r--views/signup.pug44
-rw-r--r--views/stylesheets/authentication.css142
-rw-r--r--webpack.config.js4
174 files changed, 18660 insertions, 2098 deletions
diff --git a/.gitignore b/.gitignore
index 2f9680a59..a499c39a3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
node_modules
+package-lock.json
dist/
.DS_Store
diff --git a/.vscode/launch.json b/.vscode/launch.json
index 074f9ddf0..fb91a1080 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -10,7 +10,7 @@
"name": "Launch Chrome against localhost",
"sourceMaps": true,
"breakOnLoad": true,
- "url": "http://localhost:1050",
+ "url": "http://localhost:1050/login",
"webRoot": "${workspaceFolder}",
},
{
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 623dae755..081b05b38 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -8,5 +8,5 @@
},
"editor.formatOnSave": true,
"typescript.format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets": false,
- "typescript.format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces": true,
-} \ No newline at end of file
+ "typescript.format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces": true
+}
diff --git a/deploy/assets/env.json b/deploy/assets/env.json
new file mode 100644
index 000000000..80963ea0d
--- /dev/null
+++ b/deploy/assets/env.json
@@ -0,0 +1,15 @@
+{
+ "IS_DARPA": false,
+ "IS_IGT": false,
+ "IS_MENU_FIXED": true,
+ "SERVER_URL": "http://localhost:1234",
+ "SERVER_API_PATH": "api",
+ "SAMPLE_SIZE": 100000,
+ "X_BINS": 15,
+ "Y_BINS": 15,
+ "SPLASH_TIME_IN_MS": 0,
+ "SHOW_FPS_COUNTER": true,
+ "SHOW_SHUTDOWN_BUTTON": false,
+ "DEGREE_OF_PARALLISM": 1,
+ "SHOW_WARNINGS": false
+} \ No newline at end of file
diff --git a/deploy/index.html b/deploy/index.html
index e0892662a..ca5c13e98 100644
--- a/deploy/index.html
+++ b/deploy/index.html
@@ -3,12 +3,14 @@
<head>
<title>Dash Web</title>
<link href="https://fonts.googleapis.com/css?family=Fjalla+One|Hind+Siliguri:300" rel="stylesheet">
+ <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"
+ integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<script src="https://cdnjs.cloudflare.com/ajax/libs/typescript/3.3.1/typescript.min.js"></script>
</head>
<body>
<div id="root"></div>
- <script src="./bundle.js"></script>
+ <script src="/bundle.js"></script>
</body>
</html> \ No newline at end of file
diff --git a/deploy/mobile/image.html b/deploy/mobile/image.html
new file mode 100644
index 000000000..6424d2a60
--- /dev/null
+++ b/deploy/mobile/image.html
@@ -0,0 +1,15 @@
+<html>
+
+<head>
+ <title>Test view</title>
+ <link href="https://fonts.googleapis.com/css?family=Fjalla+One|Hind+Siliguri:300" rel="stylesheet">
+</head>
+
+<body>
+ <div id="root">
+ <p>Capture Image: <input type="file" accept="image/*" id="capture">
+ </div>
+ <script src="../imageUpload.js"></script>
+</body>
+
+</html> \ No newline at end of file
diff --git a/deploy/mobile/ink.html b/deploy/mobile/ink.html
new file mode 100644
index 000000000..938d85794
--- /dev/null
+++ b/deploy/mobile/ink.html
@@ -0,0 +1,13 @@
+<html>
+
+<head>
+ <title>Test view</title>
+ <link href="https://fonts.googleapis.com/css?family=Fjalla+One|Hind+Siliguri:300" rel="stylesheet">
+</head>
+
+<body>
+ <div id="root"></div>
+ <script src="../inkControls.js"></script>
+</body>
+
+</html> \ No newline at end of file
diff --git a/package.json b/package.json
index f20f9d427..27b3eead1 100644
--- a/package.json
+++ b/package.json
@@ -11,25 +11,25 @@
},
"devDependencies": {
"@types/chai": "^4.1.7",
- "@types/mocha": "^5.2.5",
- "@types/react-dom": "^16.0.9",
+ "@types/mocha": "^5.2.6",
+ "@types/react-dom": "^16.8.2",
"@types/webpack-dev-middleware": "^2.0.2",
"@types/webpack-hot-middleware": "^2.16.4",
"awesome-typescript-loader": "^5.2.1",
"chai": "^4.2.0",
"copy-webpack-plugin": "^4.6.0",
- "css-loader": "^2.1.0",
+ "css-loader": "^2.1.1",
"file-loader": "^3.0.1",
"mocha": "^5.2.0",
"sass-loader": "^7.1.0",
"scss-loader": "0.0.1",
"style-loader": "^0.23.1",
"ts-node": "^7.0.1",
- "typescript": "^3.3.1",
- "webpack": "^4.29.1",
- "webpack-cli": "^3.2.1",
- "webpack-dev-middleware": "^3.5.2",
- "webpack-dev-server": "^3.1.14",
+ "typescript": "^3.3.3333",
+ "webpack": "^4.29.6",
+ "webpack-cli": "^3.2.3",
+ "webpack-dev-middleware": "^3.6.1",
+ "webpack-dev-server": "^3.2.1",
"webpack-hot-middleware": "^2.24.3"
},
"dependencies": {
@@ -37,98 +37,130 @@
"@fortawesome/fontawesome-svg-core": "^1.2.15",
"@fortawesome/free-solid-svg-icons": "^5.7.2",
"@fortawesome/react-fontawesome": "^0.1.4",
+ "@hig/flyout": "^1.0.3",
+ "@hig/theme-context": "^2.1.3",
+ "@hig/theme-data": "^2.3.3",
+ "@trendmicro/react-dropdown": "^1.3.0",
+ "@types/async": "^2.4.1",
"@types/bcrypt-nodejs": "0.0.30",
"@types/bluebird": "^3.5.25",
"@types/body-parser": "^1.17.0",
+ "@types/connect-flash": "0.0.34",
+ "@types/cookie-parser": "^1.4.1",
+ "@types/cookie-session": "^2.0.36",
+ "@types/d3-format": "^1.3.1",
"@types/express": "^4.16.1",
"@types/express-flash": "0.0.0",
"@types/express-session": "^1.15.12",
"@types/express-validator": "^3.0.0",
+ "@types/formidable": "^1.0.31",
"@types/jquery": "^3.3.29",
- "@types/jsonwebtoken": "^8.3.0",
+ "@types/jsonwebtoken": "^8.3.2",
"@types/lodash": "^4.14.121",
- "@types/mongodb": "^3.1.19",
- "@types/mongoose": "^5.3.16",
- "@types/node": "^10.12.24",
+ "@types/mobile-detect": "^1.3.4",
+ "@types/mongodb": "^3.1.22",
+ "@types/mongoose": "^5.3.21",
+ "@types/node": "^10.12.30",
+ "@types/nodemailer": "^4.6.6",
"@types/passport": "^1.0.0",
"@types/passport-local": "^1.0.33",
"@types/prosemirror-commands": "^1.0.1",
"@types/prosemirror-history": "^1.0.1",
+ "@types/prosemirror-inputrules": "^1.0.2",
"@types/prosemirror-keymap": "^1.0.1",
"@types/prosemirror-model": "^1.7.0",
"@types/prosemirror-schema-basic": "^1.0.1",
"@types/prosemirror-schema-list": "^1.0.1",
- "@types/prosemirror-state": "^1.2.1",
+ "@types/prosemirror-state": "^1.2.3",
"@types/prosemirror-transform": "^1.1.0",
- "@types/prosemirror-view": "^1.3.0",
+ "@types/prosemirror-view": "^1.3.1",
"@types/pug": "^2.0.4",
+ "@types/react": "^16.8.7",
"@types/react-color": "^2.14.1",
"@types/react-measure": "^2.0.4",
- "@types/react-table": "^6.7.21",
+ "@types/react-table": "^6.7.22",
"@types/request": "^2.48.1",
+ "@types/request-promise": "^4.1.42",
"@types/socket.io": "^2.1.2",
"@types/socket.io-client": "^1.4.32",
"@types/typescript": "^2.0.0",
"@types/uuid": "^3.4.4",
- "@types/webpack": "^4.4.24",
+ "@types/webpack": "^4.4.25",
+ "async": "^2.6.2",
"babel-runtime": "^6.26.0",
"bcrypt-nodejs": "0.0.3",
"bluebird": "^3.5.3",
"body-parser": "^1.18.3",
+ "bootstrap": "^4.3.1",
+ "class-transformer": "^0.2.0",
+ "connect-flash": "^0.1.1",
"connect-mongo": "^2.0.3",
+ "cookie-parser": "^1.4.4",
+ "cookie-session": "^2.0.0-beta.3",
+ "crypto-browserify": "^3.11.0",
+ "d3-format": "^1.3.2",
"express": "^4.16.4",
"express-flash": "0.0.2",
"express-session": "^1.15.6",
"express-validator": "^5.3.1",
"expressjs": "^1.0.1",
"flexlayout-react": "^0.3.3",
+ "formidable": "^1.2.1",
"golden-layout": "^1.5.9",
"html-to-image": "^0.1.0",
"i": "^0.3.6",
- "jsonwebtoken": "^8.4.0",
+ "jsonwebtoken": "^8.5.0",
"jsx-to-string": "^1.4.0",
"lodash": "^4.17.11",
+ "mobile-detect": "^1.4.3",
"mobx": "^5.9.0",
"mobx-react": "^5.3.5",
- "mobx-react-devtools": "^6.0.3",
+ "mobx-react-devtools": "^6.1.1",
"mongodb": "^3.1.13",
- "mongoose": "^5.4.12",
+ "mongoose": "^5.4.18",
"node-sass": "^4.11.0",
+ "nodemailer": "^5.1.1",
"nodemon": "^1.18.10",
"normalize.css": "^8.0.1",
- "npm": "^6.8.0",
+ "npm": "^6.9.0",
"passport": "^0.4.0",
"passport-local": "^1.0.0",
"prosemirror-commands": "^1.0.7",
"prosemirror-example-setup": "^1.0.1",
- "prosemirror-history": "^1.0.3",
+ "prosemirror-history": "^1.0.4",
"prosemirror-keymap": "^1.0.1",
"prosemirror-model": "^1.7.0",
"prosemirror-schema-basic": "^1.0.0",
"prosemirror-schema-list": "^1.0.2",
"prosemirror-state": "^1.2.2",
"prosemirror-transform": "^1.1.3",
- "prosemirror-view": "^1.7.1",
+ "prosemirror-view": "^1.8.3",
"pug": "^2.0.3",
"raw-loader": "^1.0.0",
- "react": "^16.5.2",
+ "react": "^16.8.4",
+ "react-bootstrap": "^1.0.0-beta.5",
+ "react-bootstrap-dropdown-menu": "^1.1.15",
"react-color": "^2.17.0",
"react-dimensions": "^1.3.1",
- "react-dom": "^16.7.0",
+ "react-dom": "^16.8.4",
"react-golden-layout": "^1.0.6",
"react-image-lightbox": "^5.1.0",
- "react-jsx-parser": "^1.13.0",
+ "react-jsx-parser": "^1.15.0",
"react-measure": "^2.2.4",
"react-mosaic": "0.0.20",
"react-pdf": "^4.0.2",
"react-pdf-highlighter": "^2.1.2",
"react-pdf-js": "^4.0.2",
+ "react-simple-dropdown": "^3.2.3",
"react-split-pane": "^0.1.85",
- "react-table": "^6.9.0",
+ "react-table": "^6.9.2",
"request": "^2.88.0",
+ "request-promise": "^4.2.4",
"socket.io": "^2.2.0",
"socket.io-client": "^2.2.0",
+ "typescript-collections": "^1.3.2",
"url-loader": "^1.1.2",
- "uuid": "^3.3.2"
+ "uuid": "^3.3.2",
+ "xoauth2": "^1.2.0"
}
}
diff --git a/src/.DS_Store b/src/.DS_Store
index 620e4ebce..90213270f 100644
--- a/src/.DS_Store
+++ b/src/.DS_Store
Binary files differ
diff --git a/src/Utils.ts b/src/Utils.ts
index d4b7da52c..a5d9bd0ca 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -37,19 +37,53 @@ export class Utils {
document.body.removeChild(textArea);
}
+ public static loggingEnabled: Boolean = false;
+ public static logFilter: number | undefined = undefined;
+ private static log(prefix: string, messageName: string, message: any, receiving: boolean) {
+ if (!this.loggingEnabled) {
+ return;
+ }
+ message = message || {};
+ if (this.logFilter !== undefined && this.logFilter !== message.type) {
+ return;
+ }
+ let idString = (message._id || message.id || "").padStart(36, ' ');
+ prefix = prefix.padEnd(16, ' ');
+ console.log(`${prefix}: ${idString}, ${receiving ? 'receiving' : 'sending'} ${messageName} with data ${JSON.stringify(message)}`);
+ }
+ private static loggingCallback(prefix: string, func: (args: any) => any, messageName: string) {
+ return (args: any) => {
+ this.log(prefix, messageName, args, true);
+ func(args);
+ }
+ };
+
public static Emit<T>(socket: Socket | SocketIOClient.Socket, message: Message<T>, args: T) {
+ this.log("Emit", message.Name, args, false);
socket.emit(message.Message, args);
}
- public static EmitCallback<T>(socket: Socket | SocketIOClient.Socket, message: Message<T>, args: T, fn: (args: any) => any) {
- socket.emit(message.Message, args, fn);
+ public static EmitCallback<T>(socket: Socket | SocketIOClient.Socket, message: Message<T>, args: T): Promise<any>;
+ public static EmitCallback<T>(socket: Socket | SocketIOClient.Socket, message: Message<T>, args: T, fn: (args: any) => any): void;
+ public static EmitCallback<T>(socket: Socket | SocketIOClient.Socket, message: Message<T>, args: T, fn?: (args: any) => any): void | Promise<any> {
+ this.log("Emit", message.Name, args, false);
+ if (fn) {
+ socket.emit(message.Message, args, this.loggingCallback('Receiving', fn, message.Name));
+ } else {
+ return new Promise<any>(res => socket.emit(message.Message, args, this.loggingCallback('Receiving', res, message.Name)));
+ }
}
- public static AddServerHandler<T>(socket: Socket, message: Message<T>, handler: (args: T) => any) {
- socket.on(message.Message, handler);
+ public static AddServerHandler<T>(socket: Socket | SocketIOClient.Socket, message: Message<T>, handler: (args: T) => any) {
+ socket.on(message.Message, this.loggingCallback('Incoming', handler, message.Name));
}
public static AddServerHandlerCallback<T>(socket: Socket, message: Message<T>, handler: (args: [T, (res: any) => any]) => any) {
- socket.on(message.Message, (arg: T, fn: (res: any) => any) => handler([arg, fn]));
+ socket.on(message.Message, (arg: T, fn: (res: any) => any) => {
+ this.log('S receiving', message.Name, arg, true);
+ handler([arg, this.loggingCallback('S sending', fn, message.Name)])
+ });
}
-} \ No newline at end of file
+}
+
+export type Without<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>; \ No newline at end of file
diff --git a/src/client/Server.ts b/src/client/Server.ts
index f0cf0bb9b..37e3c2c0d 100644
--- a/src/client/Server.ts
+++ b/src/client/Server.ts
@@ -2,62 +2,115 @@ import { Key } from "../fields/Key"
import { ObservableMap, action, reaction } from "mobx";
import { Field, FieldWaiting, FIELD_WAITING, Opt, FieldId } from "../fields/Field"
import { Document } from "../fields/Document"
-import { SocketStub } from "./SocketStub";
+import { SocketStub, FieldMap } from "./SocketStub";
import * as OpenSocket from 'socket.io-client';
import { Utils } from "./../Utils";
import { MessageStore, Types } from "./../server/Message";
export class Server {
public static ClientFieldsCached: ObservableMap<FieldId, Field | FIELD_WAITING> = new ObservableMap();
- static Socket: SocketIOClient.Socket = OpenSocket(`${window.location.protocol}//${window.location.hostname}:1234`);
+ static Socket: SocketIOClient.Socket = OpenSocket(`${window.location.protocol}//${window.location.hostname}:4321`);
static GUID: string = Utils.GenerateGuid()
// Retrieves the cached value of the field and sends a request to the server for the real value (if it's not cached).
// Call this is from within a reaction and test whether the return value is FieldWaiting.
- // 'hackTimeout' is here temporarily for simplicity when debugging things.
- public static GetField(fieldid: FieldId, callback: (field: Opt<Field>) => void): Opt<Field> | FIELD_WAITING {
- let cached = this.ClientFieldsCached.get(fieldid);
- if (!cached) {
- this.ClientFieldsCached.set(fieldid, FieldWaiting);
- SocketStub.SEND_FIELD_REQUEST(fieldid, action((field: Field | undefined) => {
- let cached = this.ClientFieldsCached.get(fieldid);
- if (cached != FieldWaiting)
- callback(cached);
- else {
- if (field) {
- this.ClientFieldsCached.set(fieldid, field);
- } else {
- this.ClientFieldsCached.delete(fieldid)
+ public static GetField(fieldid: FieldId): Promise<Opt<Field>>;
+ public static GetField(fieldid: FieldId, callback: (field: Opt<Field>) => void): void;
+ public static GetField(fieldid: FieldId, callback?: (field: Opt<Field>) => void): Promise<Opt<Field>> | void {
+ let fn = (cb: (field: Opt<Field>) => void) => {
+
+ let cached = this.ClientFieldsCached.get(fieldid);
+ if (!cached) {
+ this.ClientFieldsCached.set(fieldid, FieldWaiting);
+ SocketStub.SEND_FIELD_REQUEST(fieldid, action((field: Field | undefined) => {
+ let cached = this.ClientFieldsCached.get(fieldid);
+ if (cached != FieldWaiting)
+ cb(cached);
+ else {
+ if (field) {
+ this.ClientFieldsCached.set(fieldid, field);
+ } else {
+ this.ClientFieldsCached.delete(fieldid)
+ }
+ cb(field)
}
- callback(field)
- }
- }));
- } else if (cached != FieldWaiting) {
- setTimeout(() => callback(cached as Field), 0);
+ }));
+ } else if (cached != FieldWaiting) {
+ setTimeout(() => cb(cached as Field), 0);
+ } else {
+ reaction(() => {
+ return this.ClientFieldsCached.get(fieldid);
+ }, (field, reaction) => {
+ if (field !== "<Waiting>") {
+ reaction.dispose()
+ cb(field)
+ }
+ })
+ }
+ }
+ if (callback) {
+ fn(callback);
} else {
- reaction(() => {
- return this.ClientFieldsCached.get(fieldid);
- }, (field, reaction) => {
- if (field !== "<Waiting>") {
- callback(field)
- reaction.dispose()
- }
- })
+ return new Promise(res => fn(res));
}
- return cached;
}
- public static GetFields(fieldIds: FieldId[], callback: (fields: { [id: string]: Field }) => any) {
- SocketStub.SEND_FIELDS_REQUEST(fieldIds, (fields) => {
- for (let key in fields) {
- let field = fields[key];
- if (!this.ClientFieldsCached.has(field.Id)) {
- this.ClientFieldsCached.set(field.Id, field)
+ public static GetFields(fieldIds: FieldId[]): Promise<{ [id: string]: Field }>;
+ public static GetFields(fieldIds: FieldId[], callback: (fields: FieldMap) => any): void;
+ public static GetFields(fieldIds: FieldId[], callback?: (fields: FieldMap) => any): Promise<FieldMap> | void {
+ let fn = (cb: (fields: FieldMap) => void) => {
+
+ let neededFieldIds: FieldId[] = [];
+ let waitingFieldIds: FieldId[] = [];
+ let existingFields: { [id: string]: Field } = {};
+ for (let id of fieldIds) {
+ let field = this.ClientFieldsCached.get(id);
+ if (!field) {
+ neededFieldIds.push(id);
+ this.ClientFieldsCached.set(id, FieldWaiting);
+ } else if (field === FieldWaiting) {
+ waitingFieldIds.push(id);
+ } else {
+ existingFields[id] = field;
}
}
- callback(fields)
- });
+ SocketStub.SEND_FIELDS_REQUEST(neededFieldIds, action((fields: FieldMap) => {
+ for (let id of neededFieldIds) {
+ let field = fields[id];
+ if (field) {
+ if (!(this.ClientFieldsCached.get(field.Id) instanceof Field)) {
+ this.ClientFieldsCached.set(field.Id, field)
+ } else {
+ throw new Error("we shouldn't be trying to replace things that are already in the cache")
+ }
+ } else {
+ if (this.ClientFieldsCached.get(id) === FieldWaiting) {
+ this.ClientFieldsCached.delete(id);
+ } else {
+ throw new Error("we shouldn't be trying to replace things that are already in the cache")
+ }
+ }
+ }
+ reaction(() => {
+ return waitingFieldIds.map(id => this.ClientFieldsCached.get(id));
+ }, (cachedFields, reaction) => {
+ if (!cachedFields.some(field => !field || field === FieldWaiting)) {
+ reaction.dispose();
+ for (let field of cachedFields) {
+ let realField = field as Field;
+ existingFields[realField.Id] = realField;
+ }
+ cb({ ...fields, ...existingFields })
+ }
+ }, { fireImmediately: true })
+ }));
+ };
+ if (callback) {
+ fn(callback);
+ } else {
+ return new Promise(res => fn(res));
+ }
}
public static GetDocumentField(doc: Document, key: Key, callback?: (field: Field) => void) {
@@ -113,12 +166,17 @@ export class Server {
if (Server.ClientFieldsCached.has(field._id)) {
var f = Server.ClientFieldsCached.get(field._id);
if (f && f != FieldWaiting) {
+ // console.log("Applying : " + field._id);
f.UpdateFromServer(field.data);
f.init(() => { });
+ } else {
+ // console.log("Not applying wa : " + field._id);
}
+ } else {
+ // console.log("Not applying mi : " + field._id);
}
}
}
-Server.Socket.on(MessageStore.Foo.Message, Server.connected);
-Server.Socket.on(MessageStore.SetField.Message, Server.updateField); \ No newline at end of file
+Utils.AddServerHandler(Server.Socket, MessageStore.Foo, Server.connected);
+Utils.AddServerHandler(Server.Socket, MessageStore.SetField, Server.updateField);
diff --git a/src/client/SocketStub.ts b/src/client/SocketStub.ts
index 18df4ca0a..5045037c5 100644
--- a/src/client/SocketStub.ts
+++ b/src/client/SocketStub.ts
@@ -7,6 +7,12 @@ import { Utils } from "../Utils";
import { Server } from "./Server";
import { ServerUtils } from "../server/ServerUtil";
+
+export interface FieldMap {
+ [id: string]: Opt<Field>;
+}
+
+//TODO tfs: I think it might be cleaner to not have SocketStub deal with turning what the server gives it into Fields (in other words not call ServerUtils.FromJson), and leave that for the Server class.
export class SocketStub {
static FieldStore: ObservableMap<FieldId, Field> = new ObservableMap();
@@ -34,19 +40,26 @@ export class SocketStub {
Utils.Emit(Server.Socket, MessageStore.AddDocument, new DocumentTransfer(document.ToJson()))
}
- public static SEND_FIELD_REQUEST(fieldid: FieldId, callback: (field: Opt<Field>) => void) {
- if (fieldid) {
+ public static SEND_FIELD_REQUEST(fieldid: FieldId): Promise<Opt<Field>>;
+ public static SEND_FIELD_REQUEST(fieldid: FieldId, callback: (field: Opt<Field>) => void): void;
+ public static SEND_FIELD_REQUEST(fieldid: FieldId, callback?: (field: Opt<Field>) => void): Promise<Opt<Field>> | void {
+ let fn = function (cb: (field: Opt<Field>) => void) {
Utils.EmitCallback(Server.Socket, MessageStore.GetField, fieldid, (field: any) => {
if (field) {
- ServerUtils.FromJson(field).init(callback);
+ ServerUtils.FromJson(field).init(cb);
} else {
- callback(undefined);
+ cb(undefined);
}
})
}
+ if (callback) {
+ fn(callback);
+ } else {
+ return new Promise(res => fn(res))
+ }
}
- public static SEND_FIELDS_REQUEST(fieldIds: FieldId[], callback: (fields: { [key: string]: Field }) => any) {
+ public static SEND_FIELDS_REQUEST(fieldIds: FieldId[], callback: (fields: FieldMap) => any) {
Utils.EmitCallback(Server.Socket, MessageStore.GetFields, fieldIds, (fields: any[]) => {
let fieldMap: any = {};
for (let field of fields) {
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index fc210e9a2..0bf275df8 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -1,26 +1,35 @@
+import { AudioField } from "../../fields/AudioField";
import { Document } from "../../fields/Document";
-import { Server } from "../Server";
+import { Field } from "../../fields/Field";
+import { HtmlField } from "../../fields/HtmlField";
+import { ImageField } from "../../fields/ImageField";
+import { InkField, StrokeData } from "../../fields/InkField";
+import { Key } from "../../fields/Key";
import { KeyStore } from "../../fields/KeyStore";
-import { TextField } from "../../fields/TextField";
-import { NumberField } from "../../fields/NumberField";
import { ListField } from "../../fields/ListField";
-import { FormattedTextBox } from "../views/nodes/FormattedTextBox";
-import { ImageField } from "../../fields/ImageField";
-import { ImageBox } from "../views/nodes/ImageBox";
+import { PDFField } from "../../fields/PDFField";
+import { TextField } from "../../fields/TextField";
+import { VideoField } from "../../fields/VideoField";
import { WebField } from "../../fields/WebField";
-import { WebBox } from "../views/nodes/WebBox";
+import { HistogramField } from "../northstar/dash-fields/HistogramField";
+import { HistogramBox } from "../northstar/dash-nodes/HistogramBox";
+import { HistogramOperation } from "../northstar/operations/HistogramOperation";
+import { Server } from "../Server";
+import { CollectionPDFView } from "../views/collections/CollectionPDFView";
+import { CollectionVideoView } from "../views/collections/CollectionVideoView";
import { CollectionView, CollectionViewType } from "../views/collections/CollectionView";
-import { HtmlField } from "../../fields/HtmlField";
-import { Key } from "../../fields/Key"
-import { Field } from "../../fields/Field";
-import { KeyValueBox } from "../views/nodes/KeyValueBox"
-import { PDFField } from "../../fields/PDFField";
+import { AudioBox } from "../views/nodes/AudioBox";
+import { FormattedTextBox } from "../views/nodes/FormattedTextBox";
+import { ImageBox } from "../views/nodes/ImageBox";
+import { KeyValueBox } from "../views/nodes/KeyValueBox";
import { PDFBox } from "../views/nodes/PDFBox";
-import { CollectionPDFView } from "../views/collections/CollectionPDFView";
+import { VideoBox } from "../views/nodes/VideoBox";
+import { WebBox } from "../views/nodes/WebBox";
export interface DocumentOptions {
x?: number;
y?: number;
+ ink?: Map<string, StrokeData>;
width?: number;
height?: number;
nativeWidth?: number;
@@ -28,69 +37,97 @@ export interface DocumentOptions {
title?: string;
panx?: number;
pany?: number;
+ page?: number;
scale?: number;
layout?: string;
layoutKeys?: Key[];
viewType?: number;
+ backgroundColor?: string;
}
export namespace Documents {
let textProto: Document;
+ let histoProto: Document;
let imageProto: Document;
let webProto: Document;
let collProto: Document;
let kvpProto: Document;
+ let videoProto: Document;
+ let audioProto: Document;
let pdfProto: Document;
const textProtoId = "textProto";
+ const histoProtoId = "histoProto";
const pdfProtoId = "pdfProto";
const imageProtoId = "imageProto";
const webProtoId = "webProto";
const collProtoId = "collectionProto";
const kvpProtoId = "kvpProto";
+ const videoProtoId = "videoProto"
+ const audioProtoId = "audioProto";
- export function initProtos(mainDocId: string, callback: (mainDoc?: Document) => void) {
- Server.GetFields([collProtoId, textProtoId, imageProtoId, mainDocId], (fields) => {
+ export function initProtos(): Promise<void> {
+ return Server.GetFields([textProtoId, histoProtoId, collProtoId, imageProtoId, webProtoId, kvpProtoId]).then(fields => {
+ textProto = fields[textProtoId] as Document;
+ histoProto = fields[histoProtoId] as Document;
collProto = fields[collProtoId] as Document;
imageProto = fields[imageProtoId] as Document;
- textProto = fields[textProtoId] as Document;
webProto = fields[webProtoId] as Document;
kvpProto = fields[kvpProtoId] as Document;
- callback(fields[mainDocId] as Document)
});
}
function assignOptions(doc: Document, options: DocumentOptions): Document {
- if (options.x !== undefined) { doc.SetNumber(KeyStore.X, options.x); }
- if (options.y !== undefined) { doc.SetNumber(KeyStore.Y, options.y); }
- if (options.width !== undefined) { doc.SetNumber(KeyStore.Width, options.width); }
- if (options.height !== undefined) { doc.SetNumber(KeyStore.Height, options.height); }
if (options.nativeWidth !== undefined) { doc.SetNumber(KeyStore.NativeWidth, options.nativeWidth); }
if (options.nativeHeight !== undefined) { doc.SetNumber(KeyStore.NativeHeight, options.nativeHeight); }
if (options.title !== undefined) { doc.SetText(KeyStore.Title, options.title); }
- if (options.panx !== undefined) { doc.SetNumber(KeyStore.PanX, options.panx); }
- if (options.pany !== undefined) { doc.SetNumber(KeyStore.PanY, options.pany); }
+ if (options.page !== undefined) { doc.SetNumber(KeyStore.Page, options.page); }
if (options.scale !== undefined) { doc.SetNumber(KeyStore.Scale, options.scale); }
if (options.viewType !== undefined) { doc.SetNumber(KeyStore.ViewType, options.viewType); }
+ if (options.backgroundColor !== undefined) { doc.SetText(KeyStore.BackgroundColor, options.backgroundColor); }
+ if (options.ink !== undefined) { doc.Set(KeyStore.Ink, new InkField(options.ink)); }
if (options.layout !== undefined) { doc.SetText(KeyStore.Layout, options.layout); }
if (options.layoutKeys !== undefined) { doc.Set(KeyStore.LayoutKeys, new ListField(options.layoutKeys)); }
return doc;
}
+
+ function assignToDelegate(doc: Document, options: DocumentOptions): Document {
+ if (options.x !== undefined) { doc.SetNumber(KeyStore.X, options.x); }
+ if (options.y !== undefined) { doc.SetNumber(KeyStore.Y, options.y); }
+ if (options.width !== undefined) { doc.SetNumber(KeyStore.Width, options.width); }
+ if (options.height !== undefined) { doc.SetNumber(KeyStore.Height, options.height); }
+ if (options.panx !== undefined) { doc.SetNumber(KeyStore.PanX, options.panx); }
+ if (options.pany !== undefined) { doc.SetNumber(KeyStore.PanY, options.pany); }
+ return doc
+ }
+
function setupPrototypeOptions(protoId: string, title: string, layout: string, options: DocumentOptions): Document {
return assignOptions(new Document(protoId), { ...options, title: title, layout: layout });
}
- function SetInstanceOptions<T, U extends Field & { Data: T }>(doc: Document, options: DocumentOptions, value: T, ctor: { new(): U }, id?: string) {
+ function SetInstanceOptions<T, U extends Field & { Data: T }>(doc: Document, options: DocumentOptions, value: [T, { new(): U }] | Document, id?: string) {
var deleg = doc.MakeDelegate(id);
- deleg.SetData(KeyStore.Data, value, ctor);
+ if (value instanceof Document)
+ deleg.Set(KeyStore.Data, value)
+ else
+ deleg.SetData(KeyStore.Data, value[0], value[1]);
return assignOptions(deleg, options);
}
function GetImagePrototype(): Document {
if (!imageProto) {
imageProto = setupPrototypeOptions(imageProtoId, "IMAGE_PROTO", CollectionView.LayoutString("AnnotationsKey"),
- { x: 0, y: 0, nativeWidth: 300, width: 300, layoutKeys: [KeyStore.Data, KeyStore.Annotations] });
+ { x: 0, y: 0, nativeWidth: 300, width: 300, layoutKeys: [KeyStore.Data, KeyStore.Annotations, KeyStore.Caption] });
imageProto.SetText(KeyStore.BackgroundLayout, ImageBox.LayoutString());
+ imageProto.SetNumber(KeyStore.CurPage, 0);
}
return imageProto;
}
+ function GetHistogramPrototype(): Document {
+ if (!histoProto) {
+ histoProto = setupPrototypeOptions(histoProtoId, "HISTO PROTO", CollectionView.LayoutString("AnnotationsKey"),
+ { x: 0, y: 0, width: 300, height: 300, backgroundColor: "black", layoutKeys: [KeyStore.Data, KeyStore.Annotations, KeyStore.Caption] });
+ histoProto.SetText(KeyStore.BackgroundLayout, HistogramBox.LayoutString());
+ }
+ return histoProto;
+ }
function GetTextPrototype(): Document {
return textProto ? textProto :
textProto = setupPrototypeOptions(textProtoId, "TEXT_PROTO", FormattedTextBox.LayoutString(),
@@ -113,7 +150,7 @@ export namespace Documents {
function GetCollectionPrototype(): Document {
return collProto ? collProto :
collProto = setupPrototypeOptions(collProtoId, "COLLECTION_PROTO", CollectionView.LayoutString("DataKey"),
- { panx: 0, pany: 0, scale: 1, layoutKeys: [KeyStore.Data] });
+ { panx: 0, pany: 0, scale: 1, width: 500, height: 500, layoutKeys: [KeyStore.Data] });
}
function GetKVPPrototype(): Document {
@@ -121,56 +158,142 @@ export namespace Documents {
kvpProto = setupPrototypeOptions(kvpProtoId, "KVP_PROTO", KeyValueBox.LayoutString(),
{ x: 0, y: 0, width: 300, height: 150, layoutKeys: [KeyStore.Data] })
}
+ function GetVideoPrototype(): Document {
+ if (!videoProto) {
+ videoProto = setupPrototypeOptions(videoProtoId, "VIDEO_PROTO", CollectionVideoView.LayoutString("AnnotationsKey"),
+ { x: 0, y: 0, nativeWidth: 600, width: 300, layoutKeys: [KeyStore.Data, KeyStore.Annotations, KeyStore.Caption] });
+ videoProto.SetNumber(KeyStore.CurPage, 0);
+ videoProto.SetText(KeyStore.BackgroundLayout, VideoBox.LayoutString());
+ }
+ return videoProto;
+ }
+ function GetAudioPrototype(): Document {
+ return audioProto ? audioProto :
+ audioProto = setupPrototypeOptions(audioProtoId, "AUDIO_PROTO", AudioBox.LayoutString(),
+ { x: 0, y: 0, width: 300, height: 150, layoutKeys: [KeyStore.Data] })
+ }
+
export function ImageDocument(url: string, options: DocumentOptions = {}) {
- let doc = SetInstanceOptions(GetImagePrototype(), { ...options, layoutKeys: [KeyStore.Data, KeyStore.Annotations, KeyStore.Caption] },
- new URL(url), ImageField);
- doc.SetText(KeyStore.Caption, "my caption...");
- doc.SetText(KeyStore.BackgroundLayout, EmbeddedCaption());
- doc.SetText(KeyStore.OverlayLayout, FixedCaption());
- return doc;
+ return assignToDelegate(SetInstanceOptions(GetImagePrototype(), options, [new URL(url), ImageField]).MakeDelegate(), { ...options, layoutKeys: [KeyStore.Data, KeyStore.Annotations, KeyStore.Caption] });
+ // let doc = SetInstanceOptions(GetImagePrototype(), { ...options, layoutKeys: [KeyStore.Data, KeyStore.Annotations, KeyStore.Caption] },
+ // [new URL(url), ImageField]);
+ // doc.SetText(KeyStore.Caption, "my caption...");
+ // doc.SetText(KeyStore.BackgroundLayout, EmbeddedCaption());
+ // doc.SetText(KeyStore.OverlayLayout, FixedCaption());
+ // return doc;
+ }
+ export function VideoDocument(url: string, options: DocumentOptions = {}) {
+ return assignToDelegate(SetInstanceOptions(GetVideoPrototype(), options, [new URL(url), VideoField]), options);
+ }
+ export function AudioDocument(url: string, options: DocumentOptions = {}) {
+ return assignToDelegate(SetInstanceOptions(GetAudioPrototype(), options, [new URL(url), AudioField]), options);
+ }
+
+ export function HistogramDocument(histoOp: HistogramOperation, options: DocumentOptions = {}, id?: string, delegId?: string) {
+ return assignToDelegate(SetInstanceOptions(GetHistogramPrototype(), options, [histoOp, HistogramField], id).MakeDelegate(delegId), options);
}
export function TextDocument(options: DocumentOptions = {}) {
- return SetInstanceOptions(GetTextPrototype(), options, "", TextField);
+ return assignToDelegate(SetInstanceOptions(GetTextPrototype(), options, ["", TextField]).MakeDelegate(), options);
}
export function PdfDocument(url: string, options: DocumentOptions = {}) {
- return SetInstanceOptions(GetPdfPrototype(), options, new URL(url), PDFField);
+ return assignToDelegate(SetInstanceOptions(GetPdfPrototype(), options, [new URL(url), PDFField]).MakeDelegate(), options);
}
export function WebDocument(url: string, options: DocumentOptions = {}) {
- return SetInstanceOptions(GetWebPrototype(), options, new URL(url), WebField);
+ return assignToDelegate(SetInstanceOptions(GetWebPrototype(), options, [new URL(url), WebField]).MakeDelegate(), options);
}
export function HtmlDocument(html: string, options: DocumentOptions = {}) {
- return SetInstanceOptions(GetWebPrototype(), options, html, HtmlField);
+ return assignToDelegate(SetInstanceOptions(GetWebPrototype(), options, [html, HtmlField]).MakeDelegate(), options);
+ }
+ export function KVPDocument(document: Document, options: DocumentOptions = {}, id?: string) {
+ return assignToDelegate(SetInstanceOptions(GetKVPPrototype(), options, document, id), options)
}
- export function FreeformDocument(documents: Array<Document>, options: DocumentOptions, id?: string) {
- return SetInstanceOptions(GetCollectionPrototype(), { ...options, viewType: CollectionViewType.Freeform }, documents, ListField, id)
+ export function FreeformDocument(documents: Array<Document>, options: DocumentOptions, id?: string, makePrototype: boolean = true) {
+ if (!makePrototype) {
+ return SetInstanceOptions(GetCollectionPrototype(), { ...options, viewType: CollectionViewType.Freeform }, [documents, ListField], id)
+ }
+ return assignToDelegate(SetInstanceOptions(GetCollectionPrototype(), { ...options, viewType: CollectionViewType.Freeform }, [documents, ListField], id).MakeDelegate(), options)
}
export function SchemaDocument(documents: Array<Document>, options: DocumentOptions, id?: string) {
- return SetInstanceOptions(GetCollectionPrototype(), { ...options, viewType: CollectionViewType.Schema }, documents, ListField, id)
+ return assignToDelegate(SetInstanceOptions(GetCollectionPrototype(), { ...options, viewType: CollectionViewType.Schema }, [documents, ListField], id), options)
+ }
+ export function TreeDocument(documents: Array<Document>, options: DocumentOptions, id?: string) {
+ return assignToDelegate(SetInstanceOptions(GetCollectionPrototype(), { ...options, viewType: CollectionViewType.Tree }, [documents, ListField], id), options)
}
export function DockDocument(config: string, options: DocumentOptions, id?: string) {
- return SetInstanceOptions(GetCollectionPrototype(), { ...options, viewType: CollectionViewType.Docking }, config, TextField, id)
+ return assignToDelegate(SetInstanceOptions(GetCollectionPrototype(), { ...options, viewType: CollectionViewType.Docking }, [config, TextField], id), options)
}
- export function KVPDocument(document: Document, options: DocumentOptions = {}, id?: string) {
- var deleg = GetKVPPrototype().MakeDelegate(id);
- deleg.Set(KeyStore.Data, document);
- return assignOptions(deleg, options);
+
+ export function CaptionDocument(doc: Document) {
+ const captionDoc = doc.CreateAlias();
+ captionDoc.SetText(KeyStore.OverlayLayout, FixedCaption());
+ captionDoc.SetNumber(KeyStore.Width, doc.GetNumber(KeyStore.Width, 0));
+ captionDoc.SetNumber(KeyStore.Height, doc.GetNumber(KeyStore.Height, 0));
+ return captionDoc;
}
// example of custom display string for an image that shows a caption.
function EmbeddedCaption() {
return `<div style="height:100%">
- <div style="position:relative; margin:auto; height:85%;" >`
+ <div style="position:relative; margin:auto; height:85%; width:85%;" >`
+ ImageBox.LayoutString() +
`</div>
<div style="position:relative; height:15%; text-align:center; ">`
+ FormattedTextBox.LayoutString("CaptionKey") +
`</div>
</div>` };
- function FixedCaption() {
+ export function FixedCaption(fieldName: string = "Caption") {
return `<div style="position:absolute; height:30px; bottom:0; width:100%">
<div style="position:absolute; width:100%; height:100%; text-align:center;bottom:0;">`
- + FormattedTextBox.LayoutString("CaptionKey") +
+ + FormattedTextBox.LayoutString(fieldName + "Key") +
`</div>
</div>` };
+
+ function OuterCaption() {
+ return (`
+<div>
+ <div style="margin:auto; height:calc(100%); width:100%;">
+ {layout}
+ </div>
+ <div style="height:(100% + 25px); width:100%; position:absolute">
+ <FormattedTextBox doc={Document} DocumentViewForField={DocumentView} bindings={bindings} fieldKey={"CaptionKey"} isSelected={isSelected} select={select} selectOnLoad={SelectOnLoad} isTopMost={isTopMost}/>
+ </div>
+</div>
+ `)
+ }
+ function InnerCaption() {
+ return (`
+ <div>
+ <div style="margin:auto; height:calc(100% - 25px); width:100%;">
+ {layout}
+ </div>
+ <div style="height:25px; width:100%; position:absolute">
+ <FormattedTextBox doc={Document} DocumentViewForField={DocumentView} bindings={bindings} fieldKey={"CaptionKey"} isSelected={isSelected} select={select} selectOnLoad={SelectOnLoad} isTopMost={isTopMost}/>
+ </div>
+ </div>
+ `)
+ }
+
+ /*
+
+ this template requires an additional style setting on the collectionView-cont to make the layout relative
+
+.collectionView-cont {
+ position: relative;
+ width: 100%;
+ height: 100%;
+}
+ */
+ function Percentaption() {
+ return (`
+ <div>
+ <div style="margin:auto; height:85%; width:85%;">
+ {layout}
+ </div>
+ <div style="height:15%; width:100%; position:absolute">
+ <FormattedTextBox doc={Document} DocumentViewForField={DocumentView} bindings={bindings} fieldKey={"CaptionKey"} isSelected={isSelected} select={select} selectOnLoad={SelectOnLoad} isTopMost={isTopMost}/>
+ </div>
+ </div>
+ `)
+ }
} \ No newline at end of file
diff --git a/src/client/northstar/core/BaseObject.ts b/src/client/northstar/core/BaseObject.ts
new file mode 100644
index 000000000..e9e766e31
--- /dev/null
+++ b/src/client/northstar/core/BaseObject.ts
@@ -0,0 +1,12 @@
+import { IEquatable } from '../utils/IEquatable'
+import { IDisposable } from '../utils/IDisposable'
+
+export class BaseObject implements IEquatable, IDisposable {
+
+ public Equals(other: Object): boolean {
+ return this == other;
+ }
+
+ public Dispose(): void {
+ }
+} \ No newline at end of file
diff --git a/src/client/northstar/core/attribute/AttributeModel.ts b/src/client/northstar/core/attribute/AttributeModel.ts
new file mode 100644
index 000000000..124a5b45a
--- /dev/null
+++ b/src/client/northstar/core/attribute/AttributeModel.ts
@@ -0,0 +1,111 @@
+import { Attribute, DataType, VisualizationHint } from '../../model/idea/idea'
+import { BaseObject } from '../BaseObject'
+import { observable } from "mobx";
+
+export abstract class AttributeModel extends BaseObject {
+ public abstract get DisplayName(): string;
+ public abstract get CodeName(): string;
+ public abstract get DataType(): DataType;
+ public abstract get VisualizationHints(): VisualizationHint[];
+}
+
+export class ColumnAttributeModel extends AttributeModel {
+ public Attribute: Attribute;
+
+ constructor(attribute: Attribute) {
+ super();
+ this.Attribute = attribute;
+ }
+
+ public get DataType(): DataType {
+ return this.Attribute.dataType ? this.Attribute.dataType : DataType.Undefined;
+ }
+
+ public get DisplayName(): string {
+ return this.Attribute.displayName ? this.Attribute.displayName.ReplaceAll("_", " ") : "";
+ }
+
+ public get CodeName(): string {
+ return this.Attribute.rawName ? this.Attribute.rawName : "";
+ }
+
+ public get VisualizationHints(): VisualizationHint[] {
+ return this.Attribute.visualizationHints ? this.Attribute.visualizationHints : [];
+ }
+
+ public Equals(other: ColumnAttributeModel): boolean {
+ return this.Attribute.rawName == other.Attribute.rawName;
+ }
+}
+
+export class CodeAttributeModel extends AttributeModel {
+ private _visualizationHints: VisualizationHint[];
+
+ public CodeName: string;
+
+ @observable
+ public Code: string;
+
+ constructor(code: string, codeName: string, displayName: string, visualizationHints: VisualizationHint[]) {
+ super();
+ this.Code = code;
+ this.CodeName = codeName;
+ this.DisplayName = displayName;
+ this._visualizationHints = visualizationHints;
+ }
+
+ public get DataType(): DataType {
+ return DataType.Undefined;
+ }
+
+ @observable
+ public DisplayName: string;
+
+ public get VisualizationHints(): VisualizationHint[] {
+ return this._visualizationHints;
+ }
+
+ public Equals(other: CodeAttributeModel): boolean {
+ return this.CodeName === other.CodeName;
+ }
+
+}
+
+export class BackendAttributeModel extends AttributeModel {
+ private _dataType: DataType;
+ private _displayName: string;
+ private _codeName: string;
+ private _visualizationHints: VisualizationHint[];
+
+ public Id: string;
+
+ constructor(id: string, dataType: DataType, displayName: string, codeName: string, visualizationHints: VisualizationHint[]) {
+ super();
+ this.Id = id;
+ this._dataType = dataType;
+ this._displayName = displayName;
+ this._codeName = codeName;
+ this._visualizationHints = visualizationHints;
+ }
+
+ public get DataType(): DataType {
+ return this._dataType;
+ }
+
+ public get DisplayName(): string {
+ return this._displayName.ReplaceAll("_", " ");;
+ }
+
+ public get CodeName(): string {
+ return this._codeName;
+ }
+
+ public get VisualizationHints(): VisualizationHint[] {
+ return this._visualizationHints;
+ }
+
+ public Equals(other: BackendAttributeModel): boolean {
+ return this.Id == other.Id;
+ }
+
+} \ No newline at end of file
diff --git a/src/client/northstar/core/attribute/AttributeTransformationModel.ts b/src/client/northstar/core/attribute/AttributeTransformationModel.ts
new file mode 100644
index 000000000..cc5aa7154
--- /dev/null
+++ b/src/client/northstar/core/attribute/AttributeTransformationModel.ts
@@ -0,0 +1,47 @@
+;
+import { computed, observable } from "mobx";
+import { AggregateFunction } from "../../model/idea/idea";
+import { AttributeModel } from "./AttributeModel";
+import { IEquatable } from "../../utils/IEquatable";
+
+export class AttributeTransformationModel implements IEquatable {
+
+ @observable public AggregateFunction: AggregateFunction;
+ @observable public AttributeModel: AttributeModel;
+
+ constructor(attributeModel: AttributeModel, aggregateFunction: AggregateFunction = AggregateFunction.None) {
+ this.AttributeModel = attributeModel;
+ this.AggregateFunction = aggregateFunction;
+ }
+
+ @computed
+ public get PresentedName(): string {
+ var displayName = this.AttributeModel.DisplayName;
+ if (this.AggregateFunction === AggregateFunction.Count) {
+ return "count";
+ }
+ if (this.AggregateFunction === AggregateFunction.Avg)
+ displayName = "avg(" + displayName + ")";
+ else if (this.AggregateFunction === AggregateFunction.Max)
+ displayName = "max(" + displayName + ")";
+ else if (this.AggregateFunction === AggregateFunction.Min)
+ displayName = "min(" + displayName + ")";
+ else if (this.AggregateFunction === AggregateFunction.Sum)
+ displayName = "sum(" + displayName + ")";
+ else if (this.AggregateFunction === AggregateFunction.SumE)
+ displayName = "sumE(" + displayName + ")";
+
+ return displayName;
+ }
+
+ public clone(): AttributeTransformationModel {
+ var clone = new AttributeTransformationModel(this.AttributeModel);
+ clone.AggregateFunction = this.AggregateFunction;
+ return clone;
+ }
+
+ public Equals(other: AttributeTransformationModel): boolean {
+ return this.AggregateFunction == other.AggregateFunction &&
+ this.AttributeModel.Equals(other.AttributeModel);
+ }
+} \ No newline at end of file
diff --git a/src/client/northstar/core/attribute/CalculatedAttributeModel.ts b/src/client/northstar/core/attribute/CalculatedAttributeModel.ts
new file mode 100644
index 000000000..ab96c794d
--- /dev/null
+++ b/src/client/northstar/core/attribute/CalculatedAttributeModel.ts
@@ -0,0 +1,42 @@
+import { BackendAttributeModel, AttributeModel, CodeAttributeModel } from "./AttributeModel";
+import { DataType, VisualizationHint } from '../../model/idea/idea'
+
+export class CalculatedAttributeManager {
+ public static AllCalculatedAttributes: Array<AttributeModel> = new Array<AttributeModel>();
+
+ public static Clear() {
+ this.AllCalculatedAttributes = new Array<AttributeModel>();
+ }
+
+ public static CreateBackendAttributeModel(id: string, dataType: DataType, displayName: string, codeName: string, visualizationHints: VisualizationHint[]): BackendAttributeModel {
+ var filtered = this.AllCalculatedAttributes.filter(am => {
+ if (am instanceof BackendAttributeModel &&
+ am.Id == id) {
+ return true;
+ }
+ return false;
+ });
+ if (filtered.length > 0) {
+ return filtered[0] as BackendAttributeModel;
+ }
+ var newAttr = new BackendAttributeModel(id, dataType, displayName, codeName, visualizationHints);
+ this.AllCalculatedAttributes.push(newAttr);
+ return newAttr;
+ }
+
+ public static CreateCodeAttributeModel(code: string, codeName: string, visualizationHints: VisualizationHint[]): CodeAttributeModel {
+ var filtered = this.AllCalculatedAttributes.filter(am => {
+ if (am instanceof CodeAttributeModel &&
+ am.CodeName == codeName) {
+ return true;
+ }
+ return false;
+ });
+ if (filtered.length > 0) {
+ return filtered[0] as CodeAttributeModel;
+ }
+ var newAttr = new CodeAttributeModel(code, codeName, codeName.ReplaceAll("_", " "), visualizationHints);
+ this.AllCalculatedAttributes.push(newAttr);
+ return newAttr;
+ }
+} \ No newline at end of file
diff --git a/src/client/northstar/core/brusher/IBaseBrushable.ts b/src/client/northstar/core/brusher/IBaseBrushable.ts
new file mode 100644
index 000000000..99a36636f
--- /dev/null
+++ b/src/client/northstar/core/brusher/IBaseBrushable.ts
@@ -0,0 +1,13 @@
+import { PIXIPoint } from '../../utils/MathUtil'
+import { IEquatable } from '../../utils/IEquatable';
+import { Document } from '../../../../fields/Document'
+
+export interface IBaseBrushable<T> extends IEquatable {
+ BrusherModels: Array<Document>;
+ BrushColors: Array<number>;
+ Position: PIXIPoint;
+ Size: PIXIPoint;
+}
+export function instanceOfIBaseBrushable<T>(object: any): object is IBaseBrushable<T> {
+ return 'BrusherModels' in object;
+} \ No newline at end of file
diff --git a/src/client/northstar/core/brusher/IBaseBrusher.ts b/src/client/northstar/core/brusher/IBaseBrusher.ts
new file mode 100644
index 000000000..d7ae65464
--- /dev/null
+++ b/src/client/northstar/core/brusher/IBaseBrusher.ts
@@ -0,0 +1,11 @@
+import { PIXIPoint } from '../../utils/MathUtil'
+import { IEquatable } from '../../utils/IEquatable';
+
+
+export interface IBaseBrusher<T> extends IEquatable {
+ Position: PIXIPoint;
+ Size: PIXIPoint;
+}
+export function instanceOfIBaseBrusher<T>(object: any): object is IBaseBrusher<T> {
+ return 'BrushableModels' in object;
+} \ No newline at end of file
diff --git a/src/client/northstar/core/filter/FilterModel.ts b/src/client/northstar/core/filter/FilterModel.ts
new file mode 100644
index 000000000..aee99d2b6
--- /dev/null
+++ b/src/client/northstar/core/filter/FilterModel.ts
@@ -0,0 +1,84 @@
+import { ValueComparison } from "./ValueComparision";
+import { Utils } from "../../utils/Utils";
+import { IBaseFilterProvider } from "./IBaseFilterProvider";
+import { FilterOperand } from "./FilterOperand";
+import { KeyStore } from "../../../../fields/KeyStore";
+import { FieldWaiting } from "../../../../fields/Field";
+import { Document } from "../../../../fields/Document";
+import { HistogramField } from "../../dash-fields/HistogramField";
+
+export class FilterModel {
+ public ValueComparisons: ValueComparison[];
+ constructor() {
+ this.ValueComparisons = new Array<ValueComparison>();
+ }
+
+ public Equals(other: FilterModel): boolean {
+ if (!Utils.EqualityHelper(this, other)) return false;
+ if (!this.isSame(this.ValueComparisons, (other as FilterModel).ValueComparisons)) return false;
+ return true;
+ }
+
+ private isSame(a: ValueComparison[], b: ValueComparison[]): boolean {
+ if (a.length !== b.length) {
+ return false;
+ }
+ for (let i = 0; i < a.length; i++) {
+ let valueComp = a[i];
+ if (!valueComp.Equals(b[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public ToPythonString(): string {
+ return "(" + this.ValueComparisons.map(vc => vc.ToPythonString()).join("&&") + ")";
+ }
+
+ public static And(filters: string[]): string {
+ let ret = filters.filter(f => f !== "").join(" && ");
+ return ret;
+ }
+ public static GetFilterModelsRecursive(baseOperation: IBaseFilterProvider, visitedFilterProviders: Set<IBaseFilterProvider>, filterModels: FilterModel[], isFirst: boolean): string {
+ let ret = "";
+ visitedFilterProviders.add(baseOperation);
+ let filtered = baseOperation.FilterModels.filter(fm => fm && fm.ValueComparisons.length > 0);
+ if (!isFirst && filtered.length > 0) {
+ filterModels.push(...filtered);
+ ret = "(" + baseOperation.FilterModels.filter(fm => fm != null).map(fm => fm.ToPythonString()).join(" || ") + ")";
+ }
+ if (Utils.isBaseFilterConsumer(baseOperation) && baseOperation.Links) {
+ let children = new Array<string>();
+ let linkedGraphNodes = baseOperation.Links;
+ linkedGraphNodes.map(linkVm => {
+ let filterDoc = linkVm.Get(KeyStore.LinkedFromDocs);
+ if (filterDoc && filterDoc != FieldWaiting && filterDoc instanceof Document) {
+ let filterHistogram = filterDoc.GetT(KeyStore.Data, HistogramField);
+ if (filterHistogram && filterHistogram != FieldWaiting) {
+ if (!visitedFilterProviders.has(filterHistogram.Data)) {
+ let child = FilterModel.GetFilterModelsRecursive(filterHistogram.Data, visitedFilterProviders, filterModels, false);
+ if (child !== "") {
+ // if (linkVm.IsInverted) {
+ // child = "! " + child;
+ // }
+ children.push(child);
+ }
+ }
+ }
+ }
+ });
+
+ let childrenJoined = children.join(baseOperation.FilterOperand === FilterOperand.AND ? " && " : " || ");
+ if (children.length > 0) {
+ if (ret !== "") {
+ ret = "(" + ret + " && (" + childrenJoined + "))";
+ }
+ else {
+ ret = "(" + childrenJoined + ")";
+ }
+ }
+ }
+ return ret;
+ }
+} \ No newline at end of file
diff --git a/src/client/northstar/core/filter/FilterOperand.ts b/src/client/northstar/core/filter/FilterOperand.ts
new file mode 100644
index 000000000..2e8e8d6a0
--- /dev/null
+++ b/src/client/northstar/core/filter/FilterOperand.ts
@@ -0,0 +1,5 @@
+export enum FilterOperand
+{
+ AND,
+ OR
+} \ No newline at end of file
diff --git a/src/client/northstar/core/filter/FilterType.ts b/src/client/northstar/core/filter/FilterType.ts
new file mode 100644
index 000000000..9adbc087f
--- /dev/null
+++ b/src/client/northstar/core/filter/FilterType.ts
@@ -0,0 +1,6 @@
+export enum FilterType
+{
+ Filter,
+ Brush,
+ Slice
+} \ No newline at end of file
diff --git a/src/client/northstar/core/filter/IBaseFilterConsumer.ts b/src/client/northstar/core/filter/IBaseFilterConsumer.ts
new file mode 100644
index 000000000..93f66a154
--- /dev/null
+++ b/src/client/northstar/core/filter/IBaseFilterConsumer.ts
@@ -0,0 +1,12 @@
+import { FilterOperand } from '../filter/FilterOperand'
+import { IEquatable } from '../../utils/IEquatable'
+import { Document } from "../../../../fields/Document";
+
+export interface IBaseFilterConsumer extends IEquatable {
+ FilterOperand: FilterOperand;
+ Links: Document[];
+}
+
+export function instanceOfIBaseFilterConsumer(object: any): object is IBaseFilterConsumer {
+ return 'FilterOperand' in object;
+} \ No newline at end of file
diff --git a/src/client/northstar/core/filter/IBaseFilterProvider.ts b/src/client/northstar/core/filter/IBaseFilterProvider.ts
new file mode 100644
index 000000000..d082bfe12
--- /dev/null
+++ b/src/client/northstar/core/filter/IBaseFilterProvider.ts
@@ -0,0 +1,8 @@
+import { FilterModel } from '../filter/FilterModel'
+
+export interface IBaseFilterProvider {
+ FilterModels: Array<FilterModel>;
+}
+export function instanceOfIBaseFilterProvider(object: any): object is IBaseFilterProvider {
+ return 'FilterModels' in object;
+} \ No newline at end of file
diff --git a/src/client/northstar/core/filter/ValueComparision.ts b/src/client/northstar/core/filter/ValueComparision.ts
new file mode 100644
index 000000000..1e729d06e
--- /dev/null
+++ b/src/client/northstar/core/filter/ValueComparision.ts
@@ -0,0 +1,74 @@
+import { Predicate } from '../../model/idea/idea'
+import { Utils } from '../../utils/Utils'
+import { AttributeModel } from '../attribute/AttributeModel';
+
+export class ValueComparison {
+
+ public attributeModel: AttributeModel;
+ public Value: any;
+ public Predicate: Predicate;
+
+ public constructor(attributeModel: AttributeModel, predicate: Predicate, value: any) {
+ this.attributeModel = attributeModel;
+ this.Value = value;
+ this.Predicate = predicate;
+ }
+
+ public Equals(other: Object): boolean {
+ if (!Utils.EqualityHelper(this, other))
+ return false;
+ if (this.Predicate !== (other as ValueComparison).Predicate)
+ return false;
+ let isComplex = (typeof this.Value === "object");
+ if (!isComplex && this.Value != (other as ValueComparison).Value)
+ return false;
+ if (isComplex && !this.Value.Equals((other as ValueComparison).Value))
+ return false;
+ return true;
+ }
+
+ public ToPythonString(): string {
+ var op = "";
+ switch (this.Predicate) {
+ case Predicate.EQUALS:
+ op = "==";
+ break;
+ case Predicate.GREATER_THAN:
+ op = ">";
+ break;
+ case Predicate.GREATER_THAN_EQUAL:
+ op = ">=";
+ break;
+ case Predicate.LESS_THAN:
+ op = "<";
+ break;
+ case Predicate.LESS_THAN_EQUAL:
+ op = "<=";
+ break;
+ default:
+ op = "==";
+ break;
+ }
+
+ var val = this.Value.toString();
+ if (typeof this.Value === 'string' || this.Value instanceof String) {
+ val = "\"" + val + "\"";
+ }
+ var ret = " ";
+ var rawName = this.attributeModel.CodeName;
+ switch (this.Predicate) {
+ case Predicate.STARTS_WITH:
+ ret += rawName + " != null && " + rawName + ".StartsWith(" + val + ") ";
+ return ret;
+ case Predicate.ENDS_WITH:
+ ret += rawName + " != null && " + rawName + ".EndsWith(" + val + ") ";
+ return ret;
+ case Predicate.CONTAINS:
+ ret += rawName + " != null && " + rawName + ".Contains(" + val + ") ";
+ return ret;
+ default:
+ ret += rawName + " " + op + " " + val + " ";
+ return ret;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/client/northstar/dash-fields/HistogramField.ts b/src/client/northstar/dash-fields/HistogramField.ts
new file mode 100644
index 000000000..90be70b80
--- /dev/null
+++ b/src/client/northstar/dash-fields/HistogramField.ts
@@ -0,0 +1,73 @@
+import { action } from "mobx";
+import { ColumnAttributeModel } from "../../../client/northstar/core/attribute/AttributeModel";
+import { AttributeTransformationModel } from "../../../client/northstar/core/attribute/AttributeTransformationModel";
+import { HistogramOperation } from "../../../client/northstar/operations/HistogramOperation";
+import { BasicField } from "../../../fields/BasicField";
+import { Field, FieldId } from "../../../fields/Field";
+import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils";
+import { Types } from "../../../server/Message";
+
+
+export class HistogramField extends BasicField<HistogramOperation> {
+ constructor(data?: HistogramOperation, id?: FieldId, save: boolean = true) {
+ super(data ? data : HistogramOperation.Empty, save, id);
+ }
+
+ omitKeys(obj: any, keys: any) {
+ var dup: any = {};
+ for (var key in obj) {
+ if (keys.indexOf(key) == -1) {
+ dup[key] = obj[key];
+ }
+ }
+ return dup;
+ }
+ toString(): string {
+ return JSON.stringify(this.omitKeys(this.Data, ['Links', 'BrushLinks', 'Result', 'BrushColors', 'FilterModels', 'FilterOperand']));
+ }
+
+ Copy(): Field {
+ return new HistogramField(this.Data);
+ }
+
+ ToScriptString(): string {
+ return `new HistogramField("${this.Data}")`;
+ }
+
+
+ ToJson(): { type: Types, data: string, _id: string } {
+ return {
+ type: Types.HistogramOp,
+
+ data: this.toString(),
+ _id: this.Id
+ }
+ }
+
+ @action
+ static FromJson(id: string, data: any): HistogramField {
+ let jp = JSON.parse(data);
+ let X: AttributeTransformationModel | undefined;
+ let Y: AttributeTransformationModel | undefined;
+ let V: AttributeTransformationModel | undefined;
+
+ let schema = CurrentUserUtils.GetNorthstarSchema(jp.SchemaName);
+ if (schema) {
+ CurrentUserUtils.GetAllNorthstarColumnAttributes(schema).map(attr => {
+ if (attr.displayName == jp.X.AttributeModel.Attribute.DisplayName) {
+ X = new AttributeTransformationModel(new ColumnAttributeModel(attr), jp.X.AggregateFunction);
+ }
+ if (attr.displayName == jp.Y.AttributeModel.Attribute.DisplayName) {
+ Y = new AttributeTransformationModel(new ColumnAttributeModel(attr), jp.Y.AggregateFunction);
+ }
+ if (attr.displayName == jp.V.AttributeModel.Attribute.DisplayName) {
+ V = new AttributeTransformationModel(new ColumnAttributeModel(attr), jp.V.AggregateFunction);
+ }
+ });
+ if (X && Y && V) {
+ return new HistogramField(new HistogramOperation(jp.SchemaName, X, Y, V, jp.Normalization), id, false);
+ }
+ }
+ return new HistogramField(HistogramOperation.Empty, id, false);
+ }
+} \ No newline at end of file
diff --git a/src/client/northstar/dash-nodes/HistogramBinPrimitiveCollection.ts b/src/client/northstar/dash-nodes/HistogramBinPrimitiveCollection.ts
new file mode 100644
index 000000000..43e768c62
--- /dev/null
+++ b/src/client/northstar/dash-nodes/HistogramBinPrimitiveCollection.ts
@@ -0,0 +1,238 @@
+import React = require("react")
+import { AttributeTransformationModel } from "../../northstar/core/attribute/AttributeTransformationModel";
+import { ChartType } from '../../northstar/model/binRanges/VisualBinRange';
+import { AggregateFunction, Bin, Brush, DoubleValueAggregateResult, HistogramResult, MarginAggregateParameters, MarginAggregateResult } from "../../northstar/model/idea/idea";
+import { ModelHelpers } from "../../northstar/model/ModelHelpers";
+import { LABColor } from '../../northstar/utils/LABcolor';
+import { PIXIRectangle } from "../../northstar/utils/MathUtil";
+import { StyleConstants } from "../../northstar/utils/StyleContants";
+import { HistogramBox } from "./HistogramBox";
+import "./HistogramBoxPrimitives.scss";
+
+export class HistogramBinPrimitive {
+ constructor(init?: Partial<HistogramBinPrimitive>) {
+ Object.assign(this, init);
+ }
+ public DataValue: number = 0;
+ public Rect: PIXIRectangle = PIXIRectangle.EMPTY;
+ public MarginRect: PIXIRectangle = PIXIRectangle.EMPTY;
+ public MarginPercentage: number = 0;
+ public Color: number = StyleConstants.WARNING_COLOR;
+ public Opacity: number = 1;
+ public BrushIndex: number = 0;
+ public BarAxis: number = -1;
+}
+
+export class HistogramBinPrimitiveCollection {
+ private static TOLERANCE: number = 0.0001;
+
+ private _histoBox: HistogramBox;
+ private get histoOp() { return this._histoBox.HistoOp; }
+ private get histoResult() { return this.histoOp.Result as HistogramResult; }
+ private get sizeConverter() { return this._histoBox.SizeConverter!; }
+ public BinPrimitives: Array<HistogramBinPrimitive> = new Array<HistogramBinPrimitive>();
+ public HitGeom: PIXIRectangle = PIXIRectangle.EMPTY;
+
+ constructor(bin: Bin, histoBox: HistogramBox) {
+ this._histoBox = histoBox;
+ let brushing = this.setupBrushing(bin, this.histoOp.Normalization); // X= 0, Y = 1, V = 2
+
+ brushing.orderedBrushes.reduce((brushFactorSum, brush) => {
+ switch (histoBox.ChartType) {
+ case ChartType.VerticalBar: return this.createVerticalBarChartBinPrimitives(bin, brush, brushing.maxAxis, this.histoOp.Normalization);
+ case ChartType.HorizontalBar: return this.createHorizontalBarChartBinPrimitives(bin, brush, brushing.maxAxis, this.histoOp.Normalization);
+ case ChartType.SinglePoint: return this.createSinglePointChartBinPrimitives(bin, brush);
+ case ChartType.HeatMap: return this.createHeatmapBinPrimitives(bin, brush, brushFactorSum);
+ }
+ }, 0);
+
+ // adjust brush rects (stacking or not)
+ var allBrushIndex = ModelHelpers.AllBrushIndex(this.histoResult);
+ var filteredBinPrims = this.BinPrimitives.filter(b => b.BrushIndex != allBrushIndex && b.DataValue != 0.0);
+ filteredBinPrims.reduce((sum, fbp) => {
+ if (histoBox.ChartType == ChartType.VerticalBar) {
+ if (this.histoOp.Y.AggregateFunction == AggregateFunction.Count) {
+ fbp.Rect = new PIXIRectangle(fbp.Rect.x, fbp.Rect.y - sum, fbp.Rect.width, fbp.Rect.height);
+ fbp.MarginRect = new PIXIRectangle(fbp.MarginRect.x, fbp.MarginRect.y - sum, fbp.MarginRect.width, fbp.MarginRect.height);
+ return sum + fbp.Rect.height;
+ }
+ if (this.histoOp.Y.AggregateFunction == AggregateFunction.Avg) {
+ var w = fbp.Rect.width / 2.0;
+ fbp.Rect = new PIXIRectangle(fbp.Rect.x + sum, fbp.Rect.y, fbp.Rect.width / filteredBinPrims.length, fbp.Rect.height);
+ fbp.MarginRect = new PIXIRectangle(fbp.MarginRect.x - w + sum + (fbp.Rect.width / 2.0), fbp.MarginRect.y, fbp.MarginRect.width, fbp.MarginRect.height);
+ return sum + fbp.Rect.width;
+ }
+ }
+ else if (histoBox.ChartType == ChartType.HorizontalBar) {
+ if (this.histoOp.X.AggregateFunction == AggregateFunction.Count) {
+ fbp.Rect = new PIXIRectangle(fbp.Rect.x + sum, fbp.Rect.y, fbp.Rect.width, fbp.Rect.height);
+ fbp.MarginRect = new PIXIRectangle(fbp.MarginRect.x + sum, fbp.MarginRect.y, fbp.MarginRect.width, fbp.MarginRect.height);
+ return sum + fbp.Rect.width;
+ }
+ if (this.histoOp.X.AggregateFunction == AggregateFunction.Avg) {
+ var h = fbp.Rect.height / 2.0;
+ fbp.Rect = new PIXIRectangle(fbp.Rect.x, fbp.Rect.y + sum, fbp.Rect.width, fbp.Rect.height / filteredBinPrims.length);
+ fbp.MarginRect = new PIXIRectangle(fbp.MarginRect.x, fbp.MarginRect.y - h + sum + (fbp.Rect.height / 2.0), fbp.MarginRect.width, fbp.MarginRect.height);
+ return sum + fbp.Rect.height;
+ }
+ }
+ return 0;
+ }, 0);
+ this.BinPrimitives = this.BinPrimitives.reverse();
+ var f = this.BinPrimitives.filter(b => b.BrushIndex == allBrushIndex);
+ this.HitGeom = f.length > 0 ? f[0].Rect : PIXIRectangle.EMPTY;
+ }
+
+ private setupBrushing(bin: Bin, normalization: number) {
+ var overlapBrushIndex = ModelHelpers.OverlapBrushIndex(this.histoResult);
+ var orderedBrushes = [this.histoResult.brushes![0], this.histoResult.brushes![overlapBrushIndex]];
+ this.histoResult.brushes!.map(brush => brush.brushIndex != 0 && brush.brushIndex != overlapBrushIndex && orderedBrushes.push(brush));
+ return {
+ orderedBrushes,
+ maxAxis: orderedBrushes.reduce((prev, Brush) => {
+ let aggResult = this.getBinValue(normalization, bin, Brush.brushIndex!);
+ return aggResult != undefined && aggResult > prev ? aggResult : prev;
+ }, Number.MIN_VALUE)
+ };
+ }
+
+ private createHeatmapBinPrimitives(bin: Bin, brush: Brush, brushFactorSum: number): number {
+
+ let unNormalizedValue = this.getBinValue(2, bin, brush.brushIndex!);
+ if (unNormalizedValue == undefined)
+ return brushFactorSum;
+
+ var normalizedValue = (unNormalizedValue - this._histoBox.ValueRange[0]) / (Math.abs((this._histoBox.ValueRange[1] - this._histoBox.ValueRange[0])) < HistogramBinPrimitiveCollection.TOLERANCE ?
+ unNormalizedValue : this._histoBox.ValueRange[1] - this._histoBox.ValueRange[0]);
+
+ let allUnNormalizedValue = this.getBinValue(2, bin, ModelHelpers.AllBrushIndex(this.histoResult))
+
+ // bcz: are these calls needed?
+ let [xFrom, xTo] = this.sizeConverter.DataToScreenXAxisRange(this._histoBox.VisualBinRanges, 0, bin);
+ let [yFrom, yTo] = this.sizeConverter.DataToScreenYAxisRange(this._histoBox.VisualBinRanges, 1, bin);
+
+ var returnBrushFactorSum = brushFactorSum;
+ if (allUnNormalizedValue != undefined) {
+ var brushFactor = (unNormalizedValue / allUnNormalizedValue);
+ returnBrushFactorSum += brushFactor;
+ returnBrushFactorSum = Math.min(returnBrushFactorSum, 1.0);
+
+ var tempRect = new PIXIRectangle(xFrom, yTo, xTo - xFrom, yFrom - yTo);
+ var ratio = (tempRect.width / tempRect.height);
+ var newHeight = Math.sqrt((1.0 / ratio) * ((tempRect.width * tempRect.height) * returnBrushFactorSum));
+ var newWidth = newHeight * ratio;
+
+ xFrom = (tempRect.x + (tempRect.width - newWidth) / 2.0);
+ yTo = (tempRect.y + (tempRect.height - newHeight) / 2.0);
+ xTo = (xFrom + newWidth);
+ yFrom = (yTo + newHeight);
+ }
+ var alpha = 0.0;
+ var color = this.baseColorFromBrush(brush);
+ var lerpColor = LABColor.Lerp(
+ LABColor.FromColor(StyleConstants.MIN_VALUE_COLOR),
+ LABColor.FromColor(color),
+ (alpha + Math.pow(normalizedValue, 1.0 / 3.0) * (1.0 - alpha)));
+ var dataColor = LABColor.ToColor(lerpColor);
+
+ this.createBinPrimitive(-1, brush, PIXIRectangle.EMPTY, 0, xFrom, xTo, yFrom, yTo, dataColor, 1, unNormalizedValue);
+ return returnBrushFactorSum;
+ }
+
+ private createSinglePointChartBinPrimitives(bin: Bin, brush: Brush): number {
+ let unNormalizedValue = this.getBinValue(2, bin, brush.brushIndex!);
+ if (unNormalizedValue != undefined) {
+ let [xFrom, xTo] = this.sizeConverter.DataToScreenPointRange(0, bin, ModelHelpers.CreateAggregateKey(this.histoOp.Schema!.distinctAttributeParameters, this.histoOp.X, this.histoResult, brush.brushIndex!));
+ let [yFrom, yTo] = this.sizeConverter.DataToScreenPointRange(1, bin, ModelHelpers.CreateAggregateKey(this.histoOp.Schema!.distinctAttributeParameters, this.histoOp.Y, this.histoResult, brush.brushIndex!));
+
+ if (xFrom != undefined && yFrom != undefined && xTo != undefined && yTo != undefined)
+ this.createBinPrimitive(-1, brush, PIXIRectangle.EMPTY, 0, xFrom, xTo, yFrom, yTo, this.baseColorFromBrush(brush), 1, unNormalizedValue);
+ }
+ return 0;
+ }
+
+ private createVerticalBarChartBinPrimitives(bin: Bin, brush: Brush, binBrushMaxAxis: number, normalization: number): number {
+ let dataValue = this.getBinValue(1, bin, brush.brushIndex!);
+ if (dataValue != undefined) {
+ let [yFrom, yValue, yTo] = this.sizeConverter.DataToScreenNormalizedRange(dataValue, normalization, 1, binBrushMaxAxis);
+ let [xFrom, xTo] = this.sizeConverter.DataToScreenXAxisRange(this._histoBox.VisualBinRanges, 0, bin);
+
+ var yMarginAbsolute = this.getMargin(bin, brush, this.histoOp.Y);
+ var marginRect = new PIXIRectangle(xFrom + (xTo - xFrom) / 2.0 - 1,
+ this.sizeConverter.DataToScreenY(yValue + yMarginAbsolute), 2,
+ this.sizeConverter.DataToScreenY(yValue - yMarginAbsolute) - this.sizeConverter.DataToScreenY(yValue + yMarginAbsolute));
+
+ this.createBinPrimitive(1, brush, marginRect, 0, xFrom, xTo, yFrom, yTo,
+ this.baseColorFromBrush(brush), normalization != 0 ? 1 : 0.6 * binBrushMaxAxis / this.sizeConverter.DataRanges[1] + 0.4, dataValue);
+ }
+ return 0;
+ }
+
+ private createHorizontalBarChartBinPrimitives(bin: Bin, brush: Brush, binBrushMaxAxis: number, normalization: number): number {
+ let dataValue = this.getBinValue(0, bin, brush.brushIndex!);
+ if (dataValue != undefined) {
+ let [xFrom, xValue, xTo] = this.sizeConverter.DataToScreenNormalizedRange(dataValue, normalization, 0, binBrushMaxAxis);
+ let [yFrom, yTo] = this.sizeConverter.DataToScreenYAxisRange(this._histoBox.VisualBinRanges, 1, bin);
+
+ var xMarginAbsolute = this.sizeConverter.IsSmall ? 0 : this.getMargin(bin, brush, this.histoOp.X);
+ var marginRect = new PIXIRectangle(this.sizeConverter.DataToScreenX(xValue - xMarginAbsolute),
+ yTo + (yFrom - yTo) / 2.0 - 1,
+ this.sizeConverter.DataToScreenX(xValue + xMarginAbsolute) - this.sizeConverter.DataToScreenX(xValue - xMarginAbsolute),
+ 2.0);
+
+ this.createBinPrimitive(0, brush, marginRect, 0, xFrom, xTo, yFrom, yTo,
+ this.baseColorFromBrush(brush), normalization != 1 ? 1 : 0.6 * binBrushMaxAxis / this.sizeConverter.DataRanges[0] + 0.4, dataValue);
+ }
+ return 0;
+ }
+
+ public getBinValue(axis: number, bin: Bin, brushIndex: number) {
+ var aggregateKey = ModelHelpers.CreateAggregateKey(this.histoOp.Schema!.distinctAttributeParameters, axis == 0 ? this.histoOp.X : axis == 1 ? this.histoOp.Y : this.histoOp.V, this.histoResult, brushIndex);
+ let dataValue = ModelHelpers.GetAggregateResult(bin, aggregateKey) as DoubleValueAggregateResult;
+ return dataValue != null && dataValue.hasResult ? dataValue.result : undefined;
+ }
+
+ private getMargin(bin: Bin, brush: Brush, axis: AttributeTransformationModel) {
+ var marginParams = new MarginAggregateParameters();
+ marginParams.aggregateFunction = axis.AggregateFunction;
+ var marginAggregateKey = ModelHelpers.CreateAggregateKey(this.histoOp.Schema!.distinctAttributeParameters, axis, this.histoResult, brush.brushIndex!, marginParams);
+ var marginResult = ModelHelpers.GetAggregateResult(bin, marginAggregateKey) as MarginAggregateResult;
+ return !marginResult ? 0 : marginResult.absolutMargin!;
+ }
+
+ private createBinPrimitive(barAxis: number, brush: Brush, marginRect: PIXIRectangle,
+ marginPercentage: number, xFrom: number, xTo: number, yFrom: number, yTo: number, color: number, opacity: number, dataValue: number) {
+ var binPrimitive = new HistogramBinPrimitive(
+ {
+ Rect: new PIXIRectangle(xFrom, yTo, xTo - xFrom, yFrom - yTo),
+ MarginRect: marginRect,
+ MarginPercentage: marginPercentage,
+ BrushIndex: brush.brushIndex,
+ Color: color,
+ Opacity: opacity,
+ DataValue: dataValue,
+ BarAxis: barAxis
+ });
+ this.BinPrimitives.push(binPrimitive);
+ }
+
+ private baseColorFromBrush(brush: Brush): number {
+ let bc = StyleConstants.BRUSH_COLORS;
+ if (brush.brushIndex == ModelHelpers.RestBrushIndex(this.histoResult)) {
+ return StyleConstants.HIGHLIGHT_COLOR;
+ }
+ else if (brush.brushIndex == ModelHelpers.OverlapBrushIndex(this.histoResult)) {
+ return StyleConstants.OVERLAP_COLOR;
+ }
+ else if (brush.brushIndex == ModelHelpers.AllBrushIndex(this.histoResult)) {
+ return 0x00ff00;
+ }
+ else if (bc.length > 0) {
+ return bc[brush.brushIndex! % bc.length];
+ }
+ // else if (this.histoOp.BrushColors.length > 0) {
+ // return this.histoOp.BrushColors[brush.brushIndex! % this.histoOp.BrushColors.length];
+ // }
+ return StyleConstants.HIGHLIGHT_COLOR;
+ }
+} \ No newline at end of file
diff --git a/src/client/northstar/dash-nodes/HistogramBox.scss b/src/client/northstar/dash-nodes/HistogramBox.scss
new file mode 100644
index 000000000..b11840a65
--- /dev/null
+++ b/src/client/northstar/dash-nodes/HistogramBox.scss
@@ -0,0 +1,34 @@
+.histogrambox-container {
+ padding: 0vw;
+ position: absolute;
+ text-align: center;
+ width: 100%;
+ height: 100%;
+ background: black;
+ }
+ .histogrambox-xaxislabel {
+ position:absolute;
+ width:100%;
+ text-align: center;
+ bottom:0;
+ background: lightgray;
+ font-size: 14;
+ font-weight: bold;
+ }
+ .histogrambox-yaxislabel {
+ position:absolute;
+ height:100%;
+ width: 25px;
+ bottom:0;
+ background: lightgray;
+ }
+ .histogrambox-yaxislabel-text {
+ position:absolute;
+ transform-origin: left;
+ transform: rotate(-90deg);
+ text-align: center;
+ font-size: 14;
+ font-weight: bold;
+ bottom: calc(50% - 25px);
+ }
+ \ No newline at end of file
diff --git a/src/client/northstar/dash-nodes/HistogramBox.tsx b/src/client/northstar/dash-nodes/HistogramBox.tsx
new file mode 100644
index 000000000..49ebe5ebc
--- /dev/null
+++ b/src/client/northstar/dash-nodes/HistogramBox.tsx
@@ -0,0 +1,174 @@
+import React = require("react")
+import { action, computed, observable, reaction, runInAction, trace } from "mobx";
+import { observer } from "mobx-react";
+import Measure from "react-measure";
+import { FieldWaiting, Opt } from "../../../fields/Field";
+import { Document } from "../../../fields/Document";
+import { KeyStore } from "../../../fields/KeyStore";
+import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils";
+import { ChartType, VisualBinRange } from '../../northstar/model/binRanges/VisualBinRange';
+import { VisualBinRangeHelper } from "../../northstar/model/binRanges/VisualBinRangeHelper";
+import { AggregateBinRange, AggregateFunction, BinRange, Catalog, DoubleValueAggregateResult, HistogramResult, Result } from "../../northstar/model/idea/idea";
+import { ModelHelpers } from "../../northstar/model/ModelHelpers";
+import { HistogramOperation } from "../../northstar/operations/HistogramOperation";
+import { SizeConverter } from "../../northstar/utils/SizeConverter";
+import { DragManager } from "../../util/DragManager";
+import { FieldView, FieldViewProps } from "../../views/nodes/FieldView";
+import { AttributeTransformationModel } from "../core/attribute/AttributeTransformationModel";
+import { HistogramField } from "../dash-fields/HistogramField";
+import "../utils/Extensions";
+import "./HistogramBox.scss";
+import { HistogramBoxPrimitives } from './HistogramBoxPrimitives';
+import { HistogramLabelPrimitives } from "./HistogramLabelPrimitives";
+import { StyleConstants } from "../utils/StyleContants";
+
+
+@observer
+export class HistogramBox extends React.Component<FieldViewProps> {
+ public static LayoutString(fieldStr: string = "DataKey") { return FieldView.LayoutString(HistogramBox, fieldStr) }
+ private _dropXRef = React.createRef<HTMLDivElement>();
+ private _dropYRef = React.createRef<HTMLDivElement>();
+ private _dropXDisposer?: DragManager.DragDropDisposer;
+ private _dropYDisposer?: DragManager.DragDropDisposer;
+
+ @observable public PanelWidth: number = 100;
+ @observable public PanelHeight: number = 100;
+ @observable public HistoOp: HistogramOperation = HistogramOperation.Empty;
+ @observable public VisualBinRanges: VisualBinRange[] = [];
+ @observable public ValueRange: number[] = [];
+ @computed public get HistogramResult(): HistogramResult { return this.HistoOp.Result as HistogramResult; }
+ @observable public SizeConverter: SizeConverter = new SizeConverter();
+
+ @computed get createOperationParamsCache() { trace(); return this.HistoOp.CreateOperationParameters(); }
+ @computed get BinRanges() { return this.HistogramResult ? this.HistogramResult.binRanges : undefined; }
+ @computed get ChartType() {
+ return !this.BinRanges ? ChartType.SinglePoint : this.BinRanges[0] instanceof AggregateBinRange ?
+ (this.BinRanges[1] instanceof AggregateBinRange ? ChartType.SinglePoint : ChartType.HorizontalBar) :
+ this.BinRanges[1] instanceof AggregateBinRange ? ChartType.VerticalBar : ChartType.HeatMap;
+ }
+
+ constructor(props: FieldViewProps) {
+ super(props);
+ }
+
+ @action
+ dropX = (e: Event, de: DragManager.DropEvent) => {
+ if (de.data instanceof DragManager.DocumentDragData) {
+ let h = de.data.draggedDocuments[0].GetT(KeyStore.Data, HistogramField);
+ if (h && h != FieldWaiting) {
+ this.HistoOp.X = h.Data.X;
+ }
+ e.stopPropagation();
+ e.preventDefault();
+ }
+ }
+ @action
+ dropY = (e: Event, de: DragManager.DropEvent) => {
+ if (de.data instanceof DragManager.DocumentDragData) {
+ let h = de.data.draggedDocuments[0].GetT(KeyStore.Data, HistogramField);
+ if (h && h != FieldWaiting) {
+ this.HistoOp.Y = h.Data.X;
+ }
+ e.stopPropagation();
+ e.preventDefault();
+ }
+ }
+
+ @action
+ xLabelPointerDown = (e: React.PointerEvent) => {
+ this.HistoOp.X = new AttributeTransformationModel(this.HistoOp.X.AttributeModel, this.HistoOp.X.AggregateFunction == AggregateFunction.None ? AggregateFunction.Count : AggregateFunction.None);
+ }
+ @action
+ yLabelPointerDown = (e: React.PointerEvent) => {
+ this.HistoOp.Y = new AttributeTransformationModel(this.HistoOp.Y.AttributeModel, this.HistoOp.Y.AggregateFunction == AggregateFunction.None ? AggregateFunction.Count : AggregateFunction.None);
+ }
+
+ componentDidMount() {
+ if (this._dropXRef.current) {
+ this._dropXDisposer = DragManager.MakeDropTarget(this._dropXRef.current, { handlers: { drop: this.dropX.bind(this) } });
+ }
+ if (this._dropYRef.current) {
+ this._dropYDisposer = DragManager.MakeDropTarget(this._dropYRef.current, { handlers: { drop: this.dropY.bind(this) } });
+ }
+ reaction(() => CurrentUserUtils.NorthstarDBCatalog, (catalog?: Catalog) => this.activateHistogramOperation(catalog), { fireImmediately: true });
+ reaction(() => [this.VisualBinRanges && this.VisualBinRanges.slice()], () => this.SizeConverter.SetVisualBinRanges(this.VisualBinRanges));
+ reaction(() => [this.PanelHeight, this.PanelWidth], () => this.SizeConverter.SetIsSmall(this.PanelWidth < 40 && this.PanelHeight < 40))
+ reaction(() => this.HistogramResult ? this.HistogramResult.binRanges : undefined,
+ (binRanges: BinRange[] | undefined) => {
+ if (binRanges) {
+ this.VisualBinRanges.splice(0, this.VisualBinRanges.length, ...binRanges.map((br, ind) =>
+ VisualBinRangeHelper.GetVisualBinRange(this.HistoOp.Schema!.distinctAttributeParameters, br, this.HistogramResult!, ind ? this.HistoOp.Y : this.HistoOp.X, this.ChartType)));
+
+ let valueAggregateKey = ModelHelpers.CreateAggregateKey(this.HistoOp.Schema!.distinctAttributeParameters, this.HistoOp.V, this.HistogramResult!, ModelHelpers.AllBrushIndex(this.HistogramResult!));
+ this.ValueRange = Object.values(this.HistogramResult!.bins!).reduce((prev, cur) => {
+ let value = ModelHelpers.GetAggregateResult(cur, valueAggregateKey) as DoubleValueAggregateResult;
+ return value && value.hasResult ? [Math.min(prev[0], value.result!), Math.max(prev[1], value.result!)] : prev;
+ }, [Number.MAX_VALUE, Number.MIN_VALUE]);
+ }
+ });
+ }
+
+ componentWillUnmount() {
+ if (this._dropXDisposer)
+ this._dropXDisposer();
+ if (this._dropYDisposer)
+ this._dropYDisposer();
+ }
+
+ activateHistogramOperation(catalog?: Catalog) {
+ if (catalog) {
+ this.props.doc.GetTAsync(this.props.fieldKey, HistogramField).then((histoOp: Opt<HistogramField>) => runInAction(() => {
+ this.HistoOp = histoOp ? histoOp.Data : HistogramOperation.Empty;
+ if (this.HistoOp != HistogramOperation.Empty) {
+ reaction(() => this.props.doc.GetList(KeyStore.LinkedFromDocs, []), (docs: Document[]) => this.HistoOp.Links.splice(0, this.HistoOp.Links.length, ...docs), { fireImmediately: true });
+ reaction(() => this.props.doc.GetList(KeyStore.BrushingDocs, []).length,
+ () => {
+ let brushingDocs = this.props.doc.GetList(KeyStore.BrushingDocs, [] as Document[]);
+ let proto = this.props.doc.GetPrototype() as Document;
+ this.HistoOp.BrushLinks.splice(0, this.HistoOp.BrushLinks.length, ...brushingDocs.map((brush, i) => {
+ brush.SetNumber(KeyStore.BackgroundColor, StyleConstants.BRUSH_COLORS[i % StyleConstants.BRUSH_COLORS.length]);
+ let brushed = brush.GetList(KeyStore.BrushingDocs, [] as Document[]);
+ return { l: brush, b: brushed[0].Id == proto.Id ? brushed[1] : brushed[0] }
+ }));
+ }, { fireImmediately: true });
+ reaction(() => this.createOperationParamsCache, () => this.HistoOp.Update(), { fireImmediately: true });
+ }
+ }));
+ }
+ }
+ render() {
+ let labelY = this.HistoOp && this.HistoOp.Y ? this.HistoOp.Y.PresentedName : "<...>";
+ let labelX = this.HistoOp && this.HistoOp.X ? this.HistoOp.X.PresentedName : "<...>";
+ var h = this.props.isTopMost ? this.PanelHeight : this.props.doc.GetNumber(KeyStore.Height, 0);
+ var w = this.props.isTopMost ? this.PanelWidth : this.props.doc.GetNumber(KeyStore.Width, 0);
+ let loff = this.SizeConverter.LeftOffset;
+ let toff = this.SizeConverter.TopOffset;
+ let roff = this.SizeConverter.RightOffset;
+ let boff = this.SizeConverter.BottomOffset;
+ return (
+ <Measure onResize={(r: any) => runInAction(() => { this.PanelWidth = r.entry.width; this.PanelHeight = r.entry.height })}>
+ {({ measureRef }) =>
+ <div className="histogrambox-container" ref={measureRef} style={{ transform: `translate(${-w / 2}px, ${-h / 2}px)` }}>
+ <div className="histogrambox-yaxislabel" onPointerDown={this.yLabelPointerDown} ref={this._dropYRef} >
+ <span className="histogrambox-yaxislabel-text">
+ {labelY}
+ </span>
+ </div>
+ <div className="histogrambox-primitives" style={{
+ transform: `translate(${loff + 25}px, ${toff}px)`,
+ width: `calc(100% - ${loff + roff + 25}px)`,
+ height: `calc(100% - ${toff + boff}px)`,
+ }}>
+ <HistogramLabelPrimitives HistoBox={this} />
+ <HistogramBoxPrimitives HistoBox={this} />
+ </div>
+ <div className="histogrambox-xaxislabel" onPointerDown={this.xLabelPointerDown} ref={this._dropXRef} >
+ {labelX}
+ </div>
+ </div>
+ }
+ </Measure>
+ )
+ }
+}
+
diff --git a/src/client/northstar/dash-nodes/HistogramBoxPrimitives.scss b/src/client/northstar/dash-nodes/HistogramBoxPrimitives.scss
new file mode 100644
index 000000000..ce9edd65e
--- /dev/null
+++ b/src/client/northstar/dash-nodes/HistogramBoxPrimitives.scss
@@ -0,0 +1,30 @@
+.histogramboxprimitives-container {
+ width: 100%;
+ height: 100%;
+}
+.histogramboxprimitives-border {
+ border: 3px;
+ border-style: solid;
+ border-color: white;
+ pointer-events: none;
+ position: absolute;
+}
+.histogramboxprimitives-bar {
+ position: absolute;
+ border: 1px;
+ border-style: solid;
+ border-color: #282828;
+ pointer-events: all;
+}
+
+.histogramboxprimitives-placer {
+ position: absolute;
+ pointer-events: none;
+ width: 100%;
+ height: 100%;
+}
+.histogramboxprimitives-line {
+ position: absolute;
+ background: darkGray;
+ opacity: 0.4;
+} \ No newline at end of file
diff --git a/src/client/northstar/dash-nodes/HistogramBoxPrimitives.tsx b/src/client/northstar/dash-nodes/HistogramBoxPrimitives.tsx
new file mode 100644
index 000000000..e9adb3ce5
--- /dev/null
+++ b/src/client/northstar/dash-nodes/HistogramBoxPrimitives.tsx
@@ -0,0 +1,124 @@
+import React = require("react")
+import { computed, observable, reaction, runInAction, trace } from "mobx";
+import { observer } from "mobx-react";
+import { Utils as DashUtils } from '../../../Utils';
+import { FilterModel } from "../../northstar/core/filter/FilterModel";
+import { ModelHelpers } from "../../northstar/model/ModelHelpers";
+import { ArrayUtil } from "../../northstar/utils/ArrayUtil";
+import { LABColor } from '../../northstar/utils/LABcolor';
+import { PIXIRectangle } from "../../northstar/utils/MathUtil";
+import { StyleConstants } from "../../northstar/utils/StyleContants";
+import { HistogramBinPrimitiveCollection, HistogramBinPrimitive } from "./HistogramBinPrimitiveCollection";
+import { HistogramBox } from "./HistogramBox";
+import "./HistogramBoxPrimitives.scss";
+
+export interface HistogramPrimitivesProps {
+ HistoBox: HistogramBox;
+}
+@observer
+export class HistogramBoxPrimitives extends React.Component<HistogramPrimitivesProps> {
+ private get histoOp() { return this.props.HistoBox.HistoOp; }
+ private get renderDimension() { return this.props.HistoBox.SizeConverter.RenderDimension; }
+ @observable _selectedPrims: HistogramBinPrimitive[] = [];
+ @computed get xaxislines() { return this.renderGridLinesAndLabels(0); }
+ @computed get yaxislines() { return this.renderGridLinesAndLabels(1); }
+ @computed get selectedPrimitives() { return this._selectedPrims.map(bp => this.drawRect(bp.Rect, bp.BarAxis, undefined, "border")); }
+ @computed get binPrimitives() {
+ let histoResult = this.props.HistoBox.HistogramResult;
+ if (!histoResult || !histoResult.bins || !this.props.HistoBox.VisualBinRanges.length)
+ return (null);
+ trace();
+ let allBrushIndex = ModelHelpers.AllBrushIndex(histoResult);
+ return Object.keys(histoResult.bins).reduce((prims, key) => {
+ let drawPrims = new HistogramBinPrimitiveCollection(histoResult!.bins![key], this.props.HistoBox);
+ let toggle = this.getSelectionToggle(drawPrims.BinPrimitives, allBrushIndex,
+ ModelHelpers.GetBinFilterModel(histoResult!.bins![key], allBrushIndex, histoResult!, this.histoOp.X, this.histoOp.Y));
+ drawPrims.BinPrimitives.filter(bp => bp.DataValue && bp.BrushIndex !== allBrushIndex).map(bp =>
+ prims.push(...[{ r: bp.Rect, c: bp.Color }, { r: bp.MarginRect, c: StyleConstants.MARGIN_BARS_COLOR }].map(pair => this.drawRect(pair.r, bp.BarAxis, pair.c, "bar", toggle))));
+ return prims;
+ }, [] as JSX.Element[]);
+ }
+
+ componentDidMount() {
+ reaction(() => this.props.HistoBox.HistoOp.FilterString, () => this._selectedPrims.length = this.histoOp.FilterModels.length = 0);
+ }
+
+ private getSelectionToggle(binPrimitives: HistogramBinPrimitive[], allBrushIndex: number, filterModel: FilterModel) {
+ let allBrushPrim = ArrayUtil.FirstOrDefault(binPrimitives, bp => bp.BrushIndex == allBrushIndex);
+ return !allBrushPrim ? () => { } : () => runInAction(() => {
+ if (ArrayUtil.Contains(this.histoOp.FilterModels, filterModel)) {
+ this._selectedPrims.splice(this._selectedPrims.indexOf(allBrushPrim!), 1);
+ this.histoOp.RemoveFilterModels([filterModel]);
+ }
+ else {
+ this._selectedPrims.push(allBrushPrim!);
+ this.histoOp.AddFilterModels([filterModel]);
+ }
+ })
+ }
+
+ private renderGridLinesAndLabels(axis: number) {
+ if (!this.props.HistoBox.SizeConverter.Initialized)
+ return (null);
+ let labels = this.props.HistoBox.VisualBinRanges[axis].GetLabels();
+ return labels.reduce((prims, binLabel, i) => {
+ let r = this.props.HistoBox.SizeConverter.DataToScreenRange(binLabel.minValue!, binLabel.maxValue!, axis);
+ prims.push(this.drawLine(r.xFrom, r.yFrom, axis == 0 ? 0 : r.xTo - r.xFrom, axis == 0 ? r.yTo - r.yFrom : 0));
+ if (i == labels.length - 1)
+ prims.push(this.drawLine(axis == 0 ? r.xTo : r.xFrom, axis == 0 ? r.yFrom : r.yTo, axis == 0 ? 0 : r.xTo - r.xFrom, axis == 0 ? r.yTo - r.yFrom : 0));
+ return prims;
+ }, [] as JSX.Element[]);
+ }
+
+ drawEntity(xFrom: number, yFrom: number, entity: JSX.Element) {
+ let transXpercent = xFrom / this.renderDimension * 100;
+ let transYpercent = yFrom / this.renderDimension * 100;
+ return (<div key={DashUtils.GenerateGuid()} className={`histogramboxprimitives-placer`} style={{ transform: `translate(${transXpercent}%, ${transYpercent}%)` }}>
+ {entity}
+ </div>);
+ }
+ drawLine(xFrom: number, yFrom: number, width: number, height: number) {
+ if (height < 0) {
+ yFrom += height;
+ height = -height;
+ }
+ if (width < 0) {
+ xFrom += width;
+ width = -width;
+ }
+ let trans2Xpercent = width == 0 ? `1px` : `${(xFrom + width) / this.renderDimension * 100}%`;
+ let trans2Ypercent = height == 0 ? `1px` : `${(yFrom + height) / this.renderDimension * 100}%`;
+ let line = (<div className="histogramboxprimitives-line" style={{ width: trans2Xpercent, height: trans2Ypercent, }} />);
+ return this.drawEntity(xFrom, yFrom, line);
+ }
+ drawRect(r: PIXIRectangle, barAxis: number, color: number | undefined, classExt: string, tapHandler: () => void = () => { }) {
+ if (r.height < 0) {
+ r.y += r.height;
+ r.height = -r.height;
+ }
+ if (r.width < 0) {
+ r.x += r.width;
+ r.width = -r.width;
+ }
+ let widthPercent = r.width / this.renderDimension * 100;
+ let heightPercent = r.height / this.renderDimension * 100;
+ let rect = (<div className={`histogramboxprimitives-${classExt}`} onPointerDown={(e: React.PointerEvent) => { if (e.button == 0) tapHandler() }}
+ style={{
+ borderBottomStyle: barAxis == 1 ? "none" : "solid",
+ borderLeftStyle: barAxis == 0 ? "none" : "solid",
+ width: `${widthPercent}%`,
+ height: `${heightPercent}%`,
+ background: color ? `${LABColor.RGBtoHexString(color)}` : ""
+ }}
+ />);
+ return this.drawEntity(r.x, r.y, rect);
+ }
+ render() {
+ return <div className="histogramboxprimitives-container">
+ {this.xaxislines}
+ {this.yaxislines}
+ {this.binPrimitives}
+ {this.selectedPrimitives}
+ </div>
+ }
+} \ No newline at end of file
diff --git a/src/client/northstar/dash-nodes/HistogramLabelPrimitives.scss b/src/client/northstar/dash-nodes/HistogramLabelPrimitives.scss
new file mode 100644
index 000000000..304d33771
--- /dev/null
+++ b/src/client/northstar/dash-nodes/HistogramLabelPrimitives.scss
@@ -0,0 +1,13 @@
+
+ .histogramLabelPrimitives-gridlabel {
+ position:absolute;
+ transform-origin: left top;
+ font-size: 11;
+ color:white;
+ }
+ .histogramLabelPrimitives-placer {
+ position:absolute;
+ width:100%;
+ height:100%;
+ pointer-events: none;
+ } \ No newline at end of file
diff --git a/src/client/northstar/dash-nodes/HistogramLabelPrimitives.tsx b/src/client/northstar/dash-nodes/HistogramLabelPrimitives.tsx
new file mode 100644
index 000000000..93b237deb
--- /dev/null
+++ b/src/client/northstar/dash-nodes/HistogramLabelPrimitives.tsx
@@ -0,0 +1,79 @@
+import React = require("react")
+import { action, computed, reaction } from "mobx";
+import { observer } from "mobx-react";
+import { Utils as DashUtils } from '../../../Utils';
+import { NominalVisualBinRange } from "../model/binRanges/NominalVisualBinRange";
+import "../utils/Extensions";
+import { StyleConstants } from "../utils/StyleContants";
+import { HistogramBox } from "./HistogramBox";
+import "./HistogramLabelPrimitives.scss";
+import { HistogramPrimitivesProps } from "./HistogramBoxPrimitives";
+
+@observer
+export class HistogramLabelPrimitives extends React.Component<HistogramPrimitivesProps> {
+ componentDidMount() {
+ reaction(() => [this.props.HistoBox.PanelWidth, this.props.HistoBox.SizeConverter.LeftOffset, this.props.HistoBox.VisualBinRanges.length],
+ (fields) => HistogramLabelPrimitives.computeLabelAngle(fields[0] as number, fields[1] as number, this.props.HistoBox), { fireImmediately: true });
+ }
+
+ @action
+ static computeLabelAngle(panelWidth: number, leftOffset: number, histoBox: HistogramBox) {
+ const textWidth = 30;
+ if (panelWidth > 0 && histoBox.VisualBinRanges.length && histoBox.VisualBinRanges[0] instanceof NominalVisualBinRange) {
+ let space = (panelWidth - leftOffset * 2) / histoBox.VisualBinRanges[0].GetBins().length;
+ histoBox.SizeConverter.SetLabelAngle(Math.min(Math.PI / 2, Math.max(Math.PI / 6, textWidth / space * Math.PI / 2)));
+ } else if (histoBox.SizeConverter.LabelAngle) {
+ histoBox.SizeConverter.SetLabelAngle(0);
+ }
+ }
+ @computed get xaxislines() { return this.renderGridLinesAndLabels(0); }
+ @computed get yaxislines() { return this.renderGridLinesAndLabels(1); }
+
+ private renderGridLinesAndLabels(axis: number) {
+ let sc = this.props.HistoBox.SizeConverter;
+ let vb = this.props.HistoBox.VisualBinRanges;
+ if (!vb.length || !sc.Initialized)
+ return (null);
+ let dim = (axis == 0 ? this.props.HistoBox.PanelWidth : this.props.HistoBox.PanelHeight) / ((axis == 0 && vb[axis] instanceof NominalVisualBinRange) ?
+ (12 + 5) : // (<number>FontStyles.AxisLabel.fontSize + 5)));
+ sc.MaxLabelSizes[axis].coords[axis] + 5);
+
+ let labels = vb[axis].GetLabels();
+ return labels.reduce((prims, binLabel, i) => {
+ let r = sc.DataToScreenRange(binLabel.minValue!, binLabel.maxValue!, axis);
+ if (i % Math.ceil(labels.length / dim) === 0 && binLabel.label) {
+ const label = binLabel.label.Truncate(StyleConstants.MAX_CHAR_FOR_HISTOGRAM_LABELS, "...");
+ const textHeight = 14; const textWidth = 30;
+ let xStart = (axis === 0 ? r.xFrom + (r.xTo - r.xFrom) / 2.0 : r.xFrom - 10 - textWidth);
+ let yStart = (axis === 1 ? r.yFrom - textHeight / 2 : r.yFrom);
+
+ if (axis == 0 && vb[axis] instanceof NominalVisualBinRange) {
+ let space = (r.xTo - r.xFrom) / sc.RenderDimension * this.props.HistoBox.PanelWidth;
+ xStart += Math.max(textWidth / 2, (1 - textWidth / space) * textWidth / 2) - textHeight / 2;
+ }
+
+ let xPercent = axis == 1 ? `${xStart}px` : `${xStart / sc.RenderDimension * 100}%`
+ let yPercent = axis == 0 ? `${this.props.HistoBox.PanelHeight - sc.BottomOffset - textHeight}px` : `${yStart / sc.RenderDimension * 100}%`
+
+ prims.push(
+ <div className="histogramLabelPrimitives-placer" key={DashUtils.GenerateGuid()} style={{ transform: `translate(${xPercent}, ${yPercent})` }}>
+ <div className="histogramLabelPrimitives-gridlabel" style={{ transform: `rotate(${axis == 0 ? sc.LabelAngle : 0}rad)` }}>
+ {label}
+ </div>
+ </div>
+ )
+ }
+ return prims;
+ }, [] as JSX.Element[]);
+ }
+
+ render() {
+ let xaxislines = this.xaxislines;
+ let yaxislines = this.yaxislines;
+ return <div className="histogramLabelPrimitives-container">
+ {xaxislines}
+ {yaxislines}
+ </div>
+ }
+
+} \ No newline at end of file
diff --git a/src/client/northstar/manager/Gateway.ts b/src/client/northstar/manager/Gateway.ts
new file mode 100644
index 000000000..5ae5e4f47
--- /dev/null
+++ b/src/client/northstar/manager/Gateway.ts
@@ -0,0 +1,269 @@
+import { Catalog, OperationReference, Result, CompileResults } from "../model/idea/idea"
+import { computed, observable, action } from "mobx";
+
+export class Gateway {
+
+ private static _instance: Gateway;
+
+ private constructor() {
+ }
+
+ public static get Instance() {
+ return this._instance || (this._instance = new this());
+ }
+
+ public async GetCatalog(): Promise<Catalog> {
+ try {
+ const json = await this.MakeGetRequest("catalog");
+ const cat = Catalog.fromJS(json);
+ return cat;
+ }
+ catch (error) {
+ throw new Error("can not reach northstar's backend");
+ }
+ }
+
+ public async ClearCatalog(): Promise<void> {
+ try {
+ const json = await this.MakePostJsonRequest("Datamart/ClearAllAugmentations", {});
+ }
+ catch (error) {
+ throw new Error("can not reach northstar's backend");
+ }
+ }
+
+ public async TerminateServer(): Promise<void> {
+ try {
+ const url = Gateway.ConstructUrl("terminateServer");
+ const response = await fetch(url,
+ {
+ redirect: "follow",
+ method: "POST",
+ credentials: "include"
+ });
+ }
+ catch (error) {
+ throw new Error("can not reach northstar's backend");
+ }
+ }
+
+ public async Compile(data: any): Promise<CompileResults | undefined> {
+ const json = await this.MakePostJsonRequest("compile", data);
+ if (json != null) {
+ const cr = CompileResults.fromJS(json);
+ return cr;
+ }
+ }
+
+ public async SubmitResult(data: any): Promise<void> {
+ try {
+ console.log(data);
+ const url = Gateway.ConstructUrl("submitProblem");
+ const response = await fetch(url,
+ {
+ redirect: "follow",
+ method: "POST",
+ credentials: "include",
+ body: JSON.stringify(data)
+ });
+ }
+ catch (error) {
+ throw new Error("can not reach northstar's backend");
+ }
+ }
+
+ public async SpecifyProblem(data: any): Promise<void> {
+ try {
+ console.log(data);
+ const url = Gateway.ConstructUrl("specifyProblem");
+ const response = await fetch(url,
+ {
+ redirect: "follow",
+ method: "POST",
+ credentials: "include",
+ body: JSON.stringify(data)
+ });
+ }
+ catch (error) {
+ throw new Error("can not reach northstar's backend");
+ }
+ }
+
+ public async ExportToScript(solutionId: string): Promise<string> {
+ try {
+ const url = Gateway.ConstructUrl("exportsolution/script/" + solutionId);
+ const response = await fetch(url,
+ {
+ redirect: "follow",
+ method: "GET",
+ credentials: "include"
+ });
+ return await response.text();
+ }
+ catch (error) {
+ throw new Error("can not reach northstar's backend");
+ }
+ }
+
+
+ public async StartOperation(data: any): Promise<OperationReference | undefined> {
+ const json = await this.MakePostJsonRequest("operation", data);
+ if (json != null) {
+ const or = OperationReference.fromJS(json);
+ return or;
+ }
+ }
+
+ public async GetResult(data: any): Promise<Result | undefined> {
+ const json = await this.MakePostJsonRequest("result", data);
+ if (json != null) {
+ const res = Result.fromJS(json);
+ return res;
+ }
+ }
+
+ public async PauseOperation(data: any): Promise<void> {
+ const url = Gateway.ConstructUrl("pause");
+ await fetch(url,
+ {
+ redirect: "follow",
+ method: "POST",
+ credentials: "include",
+ body: JSON.stringify(data)
+ });
+ }
+
+ public async MakeGetRequest(endpoint: string, signal?: AbortSignal): Promise<any> {
+ const url = Gateway.ConstructUrl(endpoint);
+ const response = await fetch(url,
+ {
+ redirect: "follow",
+ method: "GET",
+ credentials: "include",
+ signal
+ });
+ const json = await response.json();
+ return json;
+ }
+
+ public async MakePostJsonRequest(endpoint: string, data: any, signal?: AbortSignal): Promise<any> {
+ const url = Gateway.ConstructUrl(endpoint);
+ const response = await fetch(url,
+ {
+ redirect: "follow",
+ method: "POST",
+ credentials: "include",
+ body: JSON.stringify(data),
+ signal
+ });
+ const json = await response.json();
+ return json;
+ }
+
+
+ public static ConstructUrl(appendix: string): string {
+ let base = Settings.Instance.ServerUrl;
+ if (base.slice(-1) == "/") {
+ base = base.slice(0, -1);
+ }
+ let url = base + "/" + Settings.Instance.ServerApiPath + "/" + appendix;
+ return url;
+ }
+}
+
+declare var ENV: any;
+
+export class Settings {
+ private _environment: any;
+
+ @observable
+ public ServerUrl: string = document.URL;
+
+ @observable
+ public ServerApiPath?: string;
+
+ @observable
+ public SampleSize?: number;
+
+ @observable
+ public XBins?: number;
+
+ @observable
+ public YBins?: number;
+
+ @observable
+ public SplashTimeInMS?: number;
+
+ @observable
+ public ShowFpsCounter?: boolean;
+
+ @observable
+ public IsMenuFixed?: boolean;
+
+ @observable
+ public ShowShutdownButton?: boolean;
+
+ @observable
+ public IsDarpa?: boolean;
+
+ @observable
+ public IsIGT?: boolean;
+
+ @observable
+ public DegreeOfParallelism?: number;
+
+ @observable
+ public ShowWarnings?: boolean;
+
+ @computed
+ public get IsDev(): boolean {
+ return ENV.IsDev;
+ }
+
+ @computed
+ public get TestDataFolderPath(): string {
+ return this.Origin + "testdata/";
+ }
+
+ @computed
+ public get Origin(): string {
+ return window.location.origin + "/";
+ }
+
+ private static _instance: Settings;
+
+ @action
+ public Update(environment: any): void {
+ /*let serverParam = new URL(document.URL).searchParams.get("serverUrl");
+ if (serverParam) {
+ if (serverParam === "debug") {
+ this.ServerUrl = `http://${window.location.hostname}:1234`;
+ }
+ else {
+ this.ServerUrl = serverParam;
+ }
+ }
+ else {
+ this.ServerUrl = environment["SERVER_URL"] ? environment["SERVER_URL"] : document.URL;
+ }*/
+ this.ServerUrl = environment["SERVER_URL"] ? environment["SERVER_URL"] : document.URL;
+ this.ServerApiPath = environment["SERVER_API_PATH"];
+ this.SampleSize = environment["SAMPLE_SIZE"];
+ this.XBins = environment["X_BINS"];
+ this.YBins = environment["Y_BINS"];
+ this.SplashTimeInMS = environment["SPLASH_TIME_IN_MS"];
+ this.ShowFpsCounter = environment["SHOW_FPS_COUNTER"];
+ this.ShowShutdownButton = environment["SHOW_SHUTDOWN_BUTTON"];
+ this.IsMenuFixed = environment["IS_MENU_FIXED"];
+ this.IsDarpa = environment["IS_DARPA"];
+ this.IsIGT = environment["IS_IGT"];
+ this.DegreeOfParallelism = environment["DEGREE_OF_PARALLISM"];
+ }
+
+ public static get Instance(): Settings {
+ if (!this._instance) {
+ this._instance = new Settings();
+ }
+ return this._instance;
+ }
+}
diff --git a/src/client/northstar/model/ModelExtensions.ts b/src/client/northstar/model/ModelExtensions.ts
new file mode 100644
index 000000000..9fcba7f1c
--- /dev/null
+++ b/src/client/northstar/model/ModelExtensions.ts
@@ -0,0 +1,48 @@
+import { AttributeParameters, Brush, MarginAggregateParameters, SingleDimensionAggregateParameters, Solution } from '../model/idea/idea'
+import { Utils } from '../utils/Utils'
+
+import { FilterModel } from '../core/filter/FilterModel'
+
+(SingleDimensionAggregateParameters as any).prototype["Equals"] = function (other: Object) {
+ if (!Utils.EqualityHelper(this, other)) return false;
+ if (!Utils.EqualityHelper((this as SingleDimensionAggregateParameters).attributeParameters!,
+ (other as SingleDimensionAggregateParameters).attributeParameters!)) return false;
+ if (!((this as SingleDimensionAggregateParameters).attributeParameters! as any)["Equals"]((other as SingleDimensionAggregateParameters).attributeParameters)) return false;
+ return true;
+}
+
+{
+ (AttributeParameters as any).prototype["Equals"] = function (other: AttributeParameters) {
+ return (<any>this).constructor.name === (<any>other).constructor.name &&
+ this.rawName === other.rawName;
+ }
+}
+
+{
+ (Solution as any).prototype["Equals"] = function (other: Object) {
+ if (!Utils.EqualityHelper(this, other)) return false;
+ if ((this as Solution).solutionId !== (other as Solution).solutionId) return false;
+ return true;
+ }
+}
+
+{
+ (MarginAggregateParameters as any).prototype["Equals"] = function (other: Object) {
+ if (!Utils.EqualityHelper(this, other)) return false;
+ if (!Utils.EqualityHelper((this as SingleDimensionAggregateParameters).attributeParameters!,
+ (other as SingleDimensionAggregateParameters).attributeParameters!)) return false;
+ if (!((this as SingleDimensionAggregateParameters).attributeParameters! as any)["Equals"]((other as SingleDimensionAggregateParameters).attributeParameters!)) return false;
+
+ if ((this as MarginAggregateParameters).aggregateFunction !== (other as MarginAggregateParameters).aggregateFunction) return false;
+ return true;
+ }
+}
+
+{
+ (Brush as any).prototype["Equals"] = function (other: Object) {
+ if (!Utils.EqualityHelper(this, other)) return false;
+ if ((this as Brush).brushEnum !== (other as Brush).brushEnum) return false;
+ if ((this as Brush).brushIndex !== (other as Brush).brushIndex) return false;
+ return true;
+ }
+} \ No newline at end of file
diff --git a/src/client/northstar/model/ModelHelpers.ts b/src/client/northstar/model/ModelHelpers.ts
new file mode 100644
index 000000000..d0711fb69
--- /dev/null
+++ b/src/client/northstar/model/ModelHelpers.ts
@@ -0,0 +1,215 @@
+
+import { action } from "mobx";
+import { AggregateFunction, AggregateKey, AggregateParameters, AttributeColumnParameters, AttributeParameters, AverageAggregateParameters, Bin, BinningParameters, Brush, BrushEnum, CountAggregateParameters, DataType, EquiWidthBinningParameters, HistogramResult, MarginAggregateParameters, SingleBinBinningParameters, SingleDimensionAggregateParameters, SumAggregateParameters, AggregateBinRange, NominalBinRange, AlphabeticBinRange, Predicate, Schema, Attribute, AttributeGroup, Exception, AttributeBackendParameters, AttributeCodeParameters } from '../model/idea/idea';
+import { ValueComparison } from "../core/filter/ValueComparision";
+import { ArrayUtil } from "../utils/ArrayUtil";
+import { AttributeModel, ColumnAttributeModel, BackendAttributeModel, CodeAttributeModel } from "../core/attribute/AttributeModel";
+import { FilterModel } from "../core/filter/FilterModel";
+import { AlphabeticVisualBinRange } from "./binRanges/AlphabeticVisualBinRange";
+import { NominalVisualBinRange } from "./binRanges/NominalVisualBinRange";
+import { VisualBinRangeHelper } from "./binRanges/VisualBinRangeHelper";
+import { AttributeTransformationModel } from "../core/attribute/AttributeTransformationModel";
+import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils";
+
+export class ModelHelpers {
+
+ public static CreateAggregateKey(distinctAttributeParameters: AttributeParameters | undefined, atm: AttributeTransformationModel, histogramResult: HistogramResult,
+ brushIndex: number, aggParameters?: SingleDimensionAggregateParameters): AggregateKey {
+ {
+ if (aggParameters == undefined) {
+ aggParameters = ModelHelpers.GetAggregateParameter(distinctAttributeParameters, atm);
+ }
+ else {
+ aggParameters.attributeParameters = ModelHelpers.GetAttributeParameters(atm.AttributeModel);
+ }
+ return new AggregateKey(
+ {
+ aggregateParameterIndex: ModelHelpers.GetAggregateParametersIndex(histogramResult, aggParameters),
+ brushIndex: brushIndex
+ });
+ }
+ }
+
+ public static GetAggregateParametersIndex(histogramResult: HistogramResult, aggParameters?: AggregateParameters): number {
+ return ArrayUtil.IndexOfWithEqual(histogramResult.aggregateParameters!, aggParameters);
+ }
+
+ public static GetAggregateParameter(distinctAttributeParameters: AttributeParameters | undefined, atm: AttributeTransformationModel): AggregateParameters | undefined {
+ var aggParam: AggregateParameters | undefined;
+ if (atm.AggregateFunction === AggregateFunction.Avg) {
+ var avg = new AverageAggregateParameters();
+ avg.attributeParameters = ModelHelpers.GetAttributeParameters(atm.AttributeModel);
+ avg.distinctAttributeParameters = distinctAttributeParameters;
+ aggParam = avg;
+ }
+ else if (atm.AggregateFunction === AggregateFunction.Count) {
+ var cnt = new CountAggregateParameters();
+ cnt.attributeParameters = ModelHelpers.GetAttributeParameters(atm.AttributeModel);
+ cnt.distinctAttributeParameters = distinctAttributeParameters;
+ aggParam = cnt;
+ }
+ else if (atm.AggregateFunction === AggregateFunction.Sum) {
+ var sum = new SumAggregateParameters();
+ sum.attributeParameters = ModelHelpers.GetAttributeParameters(atm.AttributeModel);
+ sum.distinctAttributeParameters = distinctAttributeParameters;
+ aggParam = sum;
+ }
+ return aggParam;
+ }
+
+ public static GetAggregateParametersWithMargins(distinctAttributeParameters: AttributeParameters | undefined, atms: Array<AttributeTransformationModel>): Array<AggregateParameters> {
+ var aggregateParameters = new Array<AggregateParameters>();
+ atms.forEach(agg => {
+ var aggParams = ModelHelpers.GetAggregateParameter(distinctAttributeParameters, agg);
+ if (aggParams) {
+ aggregateParameters.push(aggParams);
+
+ var margin = new MarginAggregateParameters()
+ margin.aggregateFunction = agg.AggregateFunction;
+ margin.attributeParameters = ModelHelpers.GetAttributeParameters(agg.AttributeModel);
+ margin.distinctAttributeParameters = distinctAttributeParameters;
+ aggregateParameters.push(margin);
+ }
+ });
+
+ return aggregateParameters;
+ }
+
+ public static GetBinningParameters(attr: AttributeTransformationModel, nrOfBins: number, minvalue?: number, maxvalue?: number): BinningParameters {
+ if (attr.AggregateFunction === AggregateFunction.None) {
+ return new EquiWidthBinningParameters(
+ {
+ attributeParameters: ModelHelpers.GetAttributeParameters(attr.AttributeModel),
+ requestedNrOfBins: nrOfBins,
+ minValue: minvalue,
+ maxValue: maxvalue
+ });
+ }
+ else {
+ return new SingleBinBinningParameters(
+ {
+ attributeParameters: ModelHelpers.GetAttributeParameters(attr.AttributeModel)
+ });
+ }
+ }
+
+ public static GetAttributeParametersFromAttributeModel(am: AttributeModel): AttributeParameters {
+ if (am instanceof ColumnAttributeModel) {
+ return new AttributeColumnParameters(
+ {
+ rawName: am.CodeName,
+ visualizationHints: am.VisualizationHints
+ });
+ }
+ else if (am instanceof BackendAttributeModel) {
+ return new AttributeBackendParameters(
+ {
+ rawName: am.CodeName,
+ visualizationHints: am.VisualizationHints,
+ id: (am as BackendAttributeModel).Id
+ });
+ }
+ else if (am instanceof CodeAttributeModel) {
+ return new AttributeCodeParameters(
+ {
+ rawName: am.CodeName,
+ visualizationHints: am.VisualizationHints,
+ code: (am as CodeAttributeModel).Code
+ });
+ }
+ else {
+ throw new Exception()
+ }
+ }
+
+ public static GetAttributeParameters(am: AttributeModel): AttributeParameters {
+ return this.GetAttributeParametersFromAttributeModel(am);
+ }
+
+ public static OverlapBrushIndex(histogramResult: HistogramResult): number {
+ var brush = ArrayUtil.First(histogramResult.brushes!, (b: any) => b.brushEnum === BrushEnum.Overlap);
+ return ModelHelpers.GetBrushIndex(histogramResult, brush);
+ }
+
+ public static AllBrushIndex(histogramResult: HistogramResult): number {
+ var brush = ArrayUtil.First(histogramResult.brushes!, (b: any) => b.brushEnum === BrushEnum.All);
+ return ModelHelpers.GetBrushIndex(histogramResult, brush);
+ }
+
+ public static RestBrushIndex(histogramResult: HistogramResult): number {
+ var brush = ArrayUtil.First(histogramResult.brushes!, (b: Brush) => b.brushEnum === BrushEnum.Rest);
+ return ModelHelpers.GetBrushIndex(histogramResult, brush);
+ }
+
+ public static GetBrushIndex(histogramResult: HistogramResult, brush: Brush): number {
+ return ArrayUtil.IndexOfWithEqual(histogramResult.brushes!, brush);
+ }
+
+ public static GetAggregateResult(bin: Bin, aggregateKey: AggregateKey) {
+ if (aggregateKey.aggregateParameterIndex == -1 || aggregateKey.brushIndex == -1) {
+ return null;
+ }
+ return bin.aggregateResults![aggregateKey.aggregateParameterIndex! * bin.ySize! + aggregateKey.brushIndex!];
+ }
+
+ @action
+ public static PossibleAggegationFunctions(atm: AttributeTransformationModel): Array<AggregateFunction> {
+ var ret = new Array<AggregateFunction>();
+ ret.push(AggregateFunction.None);
+ ret.push(AggregateFunction.Count);
+ if (atm.AttributeModel.DataType == DataType.Float ||
+ atm.AttributeModel.DataType == DataType.Double ||
+ atm.AttributeModel.DataType == DataType.Int) {
+ ret.push(AggregateFunction.Avg);
+ ret.push(AggregateFunction.Sum);
+ }
+ return ret;
+ }
+
+ public static GetBinFilterModel(
+ bin: Bin, brushIndex: number, histogramResult: HistogramResult,
+ xAom: AttributeTransformationModel, yAom: AttributeTransformationModel): FilterModel {
+ var dimensions: Array<AttributeTransformationModel> = [xAom, yAom];
+ var filterModel = new FilterModel();
+
+ for (var i = 0; i < histogramResult.binRanges!.length; i++) {
+ if (!(histogramResult.binRanges![i] instanceof AggregateBinRange)) {
+ var binRange = VisualBinRangeHelper.GetNonAggregateVisualBinRange(histogramResult.binRanges![i]);
+ var dataFrom = binRange.GetValueFromIndex(bin.binIndex!.indices![i]);
+ var dataTo = binRange.AddStep(dataFrom);
+
+ if (binRange instanceof NominalVisualBinRange) {
+ var tt = binRange.GetLabel(dataFrom);
+ filterModel.ValueComparisons.push(new ValueComparison(dimensions[i].AttributeModel, Predicate.EQUALS, tt));
+ }
+ else if (binRange instanceof AlphabeticVisualBinRange) {
+ filterModel.ValueComparisons.push(new ValueComparison(dimensions[i].AttributeModel, Predicate.STARTS_WITH,
+ binRange.GetLabel(dataFrom)));
+ }
+ else {
+ filterModel.ValueComparisons.push(new ValueComparison(dimensions[i].AttributeModel, Predicate.GREATER_THAN_EQUAL, dataFrom));
+ filterModel.ValueComparisons.push(new ValueComparison(dimensions[i].AttributeModel, Predicate.LESS_THAN, dataTo));
+ }
+ }
+ }
+
+ return filterModel;
+ }
+
+ public GetAllAttributes(schema: Schema) {
+ if (!schema || !schema.rootAttributeGroup) {
+ return [];
+ }
+ const recurs = (attrs: Attribute[], g: AttributeGroup) => {
+ if (g.attributes) {
+ attrs.push.apply(attrs, g.attributes);
+ if (g.attributeGroups) {
+ g.attributeGroups.forEach(ng => recurs(attrs, ng));
+ }
+ }
+ };
+ const allAttributes: Attribute[] = new Array<Attribute>();
+ recurs(allAttributes, schema.rootAttributeGroup);
+ return allAttributes;
+ }
+} \ No newline at end of file
diff --git a/src/client/northstar/model/binRanges/AlphabeticVisualBinRange.ts b/src/client/northstar/model/binRanges/AlphabeticVisualBinRange.ts
new file mode 100644
index 000000000..995bf4e0b
--- /dev/null
+++ b/src/client/northstar/model/binRanges/AlphabeticVisualBinRange.ts
@@ -0,0 +1,52 @@
+import { AlphabeticBinRange, BinLabel } from '../../model/idea/idea'
+import { VisualBinRange } from './VisualBinRange'
+
+export class AlphabeticVisualBinRange extends VisualBinRange {
+ public DataBinRange: AlphabeticBinRange;
+
+ constructor(dataBinRange: AlphabeticBinRange) {
+ super();
+ this.DataBinRange = dataBinRange;
+ }
+
+ public AddStep(value: number): number {
+ return value + 1;
+ }
+
+ public GetValueFromIndex(index: number): number {
+ return index;
+ }
+
+ public GetBins(): number[] {
+ var bins = new Array<number>();
+ var idx = 0;
+ for (var key in this.DataBinRange.labelsValue) {
+ if (this.DataBinRange.labelsValue.hasOwnProperty(key)) {
+ bins.push(idx);
+ idx++;
+ }
+ }
+ return bins;
+ }
+
+ public GetLabel(value: number): string {
+ return this.DataBinRange.prefix + this.DataBinRange.valuesLabel![value];
+ }
+
+ public GetLabels(): Array<BinLabel> {
+ var labels = new Array<BinLabel>();
+ var count = 0;
+ for (var key in this.DataBinRange.valuesLabel) {
+ if (this.DataBinRange.valuesLabel.hasOwnProperty(key)) {
+ var value = this.DataBinRange.valuesLabel[key];
+ labels.push(new BinLabel({
+ value: parseFloat(key),
+ minValue: count++,
+ maxValue: count,
+ label: this.DataBinRange.prefix + value
+ }));
+ }
+ }
+ return labels;
+ }
+} \ No newline at end of file
diff --git a/src/client/northstar/model/binRanges/DateTimeVisualBinRange.ts b/src/client/northstar/model/binRanges/DateTimeVisualBinRange.ts
new file mode 100644
index 000000000..9313fb1a7
--- /dev/null
+++ b/src/client/northstar/model/binRanges/DateTimeVisualBinRange.ts
@@ -0,0 +1,105 @@
+import { DateTimeBinRange, DateTimeStep, DateTimeStepGranularity } from '../idea/idea'
+import { VisualBinRange } from './VisualBinRange'
+
+export class DateTimeVisualBinRange extends VisualBinRange {
+ public DataBinRange: DateTimeBinRange;
+
+ constructor(dataBinRange: DateTimeBinRange) {
+ super();
+ this.DataBinRange = dataBinRange;
+ }
+
+ public AddStep(value: number): number {
+ return DateTimeVisualBinRange.AddToDateTimeTicks(value, this.DataBinRange.step!);
+ }
+
+ public GetValueFromIndex(index: number): number {
+ var v = this.DataBinRange.minValue!;
+ for (var i = 0; i < index; i++) {
+ v = this.AddStep(v);
+ }
+ return v;
+ }
+
+ public GetBins(): number[] {
+ var bins = new Array<number>();
+ for (var v: number = this.DataBinRange.minValue!;
+ v < this.DataBinRange.maxValue!;
+ v = DateTimeVisualBinRange.AddToDateTimeTicks(v, this.DataBinRange.step!)) {
+ bins.push(v);
+ }
+ return bins;
+ }
+
+ private pad(n: number, size: number) {
+ var sign = n < 0 ? '-' : '';
+ return sign + new Array(size).concat([Math.abs(n)]).join('0').slice(-size);
+ }
+
+
+ public GetLabel(value: number): string {
+ var dt = DateTimeVisualBinRange.TicksToDate(value);
+ if (this.DataBinRange.step!.dateTimeStepGranularity == DateTimeStepGranularity.Second ||
+ this.DataBinRange.step!.dateTimeStepGranularity == DateTimeStepGranularity.Minute) {
+ return ("" + this.pad(dt.getMinutes(), 2) + ":" + this.pad(dt.getSeconds(), 2));
+ //return dt.ToString("mm:ss");
+ }
+ else if (this.DataBinRange.step!.dateTimeStepGranularity == DateTimeStepGranularity.Hour) {
+ return (this.pad(dt.getHours(), 2) + ":" + this.pad(dt.getMinutes(), 2));
+ //return dt.ToString("HH:mm");
+ }
+ else if (this.DataBinRange.step!.dateTimeStepGranularity == DateTimeStepGranularity.Day) {
+ return ((dt.getMonth() + 1) + "/" + dt.getDate() + "/" + dt.getFullYear());
+ //return dt.ToString("MM/dd/yyyy");
+ }
+ else if (this.DataBinRange.step!.dateTimeStepGranularity == DateTimeStepGranularity.Month) {
+ //return dt.ToString("MM/yyyy");
+ return ((dt.getMonth() + 1) + "/" + dt.getFullYear());
+ }
+ else if (this.DataBinRange.step!.dateTimeStepGranularity == DateTimeStepGranularity.Year) {
+ return "" + dt.getFullYear();
+ }
+ return "n/a";
+ }
+
+ public static TicksToDate(ticks: number): Date {
+ var dd = new Date((ticks - 621355968000000000) / 10000);
+ dd.setMinutes(dd.getMinutes() + dd.getTimezoneOffset());
+ return dd;
+ }
+
+
+ public static DateToTicks(date: Date): number {
+ var copiedDate = new Date(date.getTime());
+ copiedDate.setMinutes(copiedDate.getMinutes() - copiedDate.getTimezoneOffset());
+ var t = copiedDate.getTime() * 10000 + 621355968000000000;
+ /*var dd = new Date((ticks - 621355968000000000) / 10000);
+ dd.setMinutes(dd.getMinutes() + dd.getTimezoneOffset());
+ return dd;*/
+ return t;
+ }
+
+ public static AddToDateTimeTicks(ticks: number, dateTimeStep: DateTimeStep): number {
+ var copiedDate = DateTimeVisualBinRange.TicksToDate(ticks);
+ var returnDate: Date = new Date(Date.now());
+ if (dateTimeStep.dateTimeStepGranularity == DateTimeStepGranularity.Second) {
+ returnDate = new Date(copiedDate.setSeconds(copiedDate.getSeconds() + dateTimeStep.dateTimeStepValue!));
+ }
+ else if (dateTimeStep.dateTimeStepGranularity == DateTimeStepGranularity.Minute) {
+ returnDate = new Date(copiedDate.setMinutes(copiedDate.getMinutes() + dateTimeStep.dateTimeStepValue!));
+ }
+ else if (dateTimeStep.dateTimeStepGranularity == DateTimeStepGranularity.Hour) {
+ returnDate = new Date(copiedDate.setHours(copiedDate.getHours() + dateTimeStep.dateTimeStepValue!));
+ }
+ else if (dateTimeStep.dateTimeStepGranularity == DateTimeStepGranularity.Day) {
+ returnDate = new Date(copiedDate.setDate(copiedDate.getDate() + dateTimeStep.dateTimeStepValue!));
+ }
+ else if (dateTimeStep.dateTimeStepGranularity == DateTimeStepGranularity.Month) {
+ returnDate = new Date(copiedDate.setMonth(copiedDate.getMonth() + dateTimeStep.dateTimeStepValue!));
+ }
+ else if (dateTimeStep.dateTimeStepGranularity == DateTimeStepGranularity.Year) {
+ returnDate = new Date(copiedDate.setFullYear(copiedDate.getFullYear() + dateTimeStep.dateTimeStepValue!));
+ }
+ return DateTimeVisualBinRange.DateToTicks(returnDate);
+ }
+} \ No newline at end of file
diff --git a/src/client/northstar/model/binRanges/NominalVisualBinRange.ts b/src/client/northstar/model/binRanges/NominalVisualBinRange.ts
new file mode 100644
index 000000000..407ff3ea6
--- /dev/null
+++ b/src/client/northstar/model/binRanges/NominalVisualBinRange.ts
@@ -0,0 +1,52 @@
+import { NominalBinRange, BinLabel } from '../../model/idea/idea'
+import { VisualBinRange } from './VisualBinRange'
+
+export class NominalVisualBinRange extends VisualBinRange {
+ public DataBinRange: NominalBinRange;
+
+ constructor(dataBinRange: NominalBinRange) {
+ super();
+ this.DataBinRange = dataBinRange;
+ }
+
+ public AddStep(value: number): number {
+ return value + 1;
+ }
+
+ public GetValueFromIndex(index: number): number {
+ return index;
+ }
+
+ public GetBins(): number[] {
+ var bins = new Array<number>();
+ var idx = 0;
+ for (var key in this.DataBinRange.labelsValue) {
+ if (this.DataBinRange.labelsValue.hasOwnProperty(key)) {
+ bins.push(idx);
+ idx++;
+ }
+ }
+ return bins;
+ }
+
+ public GetLabel(value: number): string {
+ return this.DataBinRange.valuesLabel![value];
+ }
+
+ public GetLabels(): Array<BinLabel> {
+ var labels = new Array<BinLabel>();
+ var count = 0;
+ for (var key in this.DataBinRange.valuesLabel) {
+ if (this.DataBinRange.valuesLabel.hasOwnProperty(key)) {
+ var value = this.DataBinRange.valuesLabel[key];
+ labels.push(new BinLabel({
+ value: parseFloat(key),
+ minValue: count++,
+ maxValue: count,
+ label: value
+ }));
+ }
+ }
+ return labels;
+ }
+} \ No newline at end of file
diff --git a/src/client/northstar/model/binRanges/QuantitativeVisualBinRange.ts b/src/client/northstar/model/binRanges/QuantitativeVisualBinRange.ts
new file mode 100644
index 000000000..80886416b
--- /dev/null
+++ b/src/client/northstar/model/binRanges/QuantitativeVisualBinRange.ts
@@ -0,0 +1,90 @@
+import { QuantitativeBinRange } from '../idea/idea'
+import { VisualBinRange } from './VisualBinRange';
+import { format } from "d3-format";
+
+export class QuantitativeVisualBinRange extends VisualBinRange {
+
+ public DataBinRange: QuantitativeBinRange;
+
+ constructor(dataBinRange: QuantitativeBinRange) {
+ super();
+ this.DataBinRange = dataBinRange;
+ }
+
+ public AddStep(value: number): number {
+ return value + this.DataBinRange.step!;
+ }
+
+ public GetValueFromIndex(index: number): number {
+ return this.DataBinRange.minValue! + (index * this.DataBinRange.step!);
+ }
+
+ public GetLabel(value: number): string {
+ return QuantitativeVisualBinRange.NumberFormatter(value);
+ }
+
+ public static NumberFormatter(val: number): string {
+ if (val === 0) {
+ return "0";
+ }
+ if (val < 1) {
+ /*if (val < Math.abs(0.001)) {
+ return val.toExponential(2);
+ }*/
+ return format(".3")(val);
+ }
+ return format("~s")(val);
+ }
+
+ public GetBins(): number[] {
+ let bins = new Array<number>();
+
+ for (let v: number = this.DataBinRange.minValue!; v < this.DataBinRange.maxValue!; v += this.DataBinRange.step!) {
+ bins.push(v);
+ }
+ return bins;
+ }
+
+ public static Initialize(dataMinValue: number, dataMaxValue: number, targetBinNumber: number, isIntegerRange: boolean): QuantitativeVisualBinRange {
+ let extent = QuantitativeVisualBinRange.getExtent(dataMinValue, dataMaxValue, targetBinNumber, isIntegerRange);
+ let dataBinRange = new QuantitativeBinRange();
+ dataBinRange.minValue = extent[0];
+ dataBinRange.maxValue = extent[1];
+ dataBinRange.step = extent[2];
+
+ return new QuantitativeVisualBinRange(dataBinRange);
+ }
+
+ private static getExtent(dataMin: number, dataMax: number, m: number, isIntegerRange: boolean): number[] {
+ if (dataMin === dataMax) {
+ // dataMin -= 0.1;
+ dataMax += 0.1;
+ }
+ let span = dataMax - dataMin;
+
+ let step = Math.pow(10, Math.floor(Math.log10(span / m)));
+ let err = m / span * step;
+
+ if (err <= .15) {
+ step *= 10;
+ }
+ else if (err <= .35) {
+ step *= 5;
+ }
+ else if (err <= .75) {
+ step *= 2;
+ }
+
+ if (isIntegerRange) {
+ step = Math.ceil(step);
+ }
+ let ret: number[] = new Array<number>(3);
+ let minDivStep = Math.floor(dataMin / step);
+ let maxDivStep = Math.floor(dataMax / step);
+ ret[0] = minDivStep * step; // Math.floor(Math.Round(dataMin, 8)/step)*step;
+ ret[1] = maxDivStep * step + step; // Math.floor(Math.Round(dataMax, 8)/step)*step + step;
+ ret[2] = step;
+
+ return ret;
+ }
+} \ No newline at end of file
diff --git a/src/client/northstar/model/binRanges/VisualBinRange.ts b/src/client/northstar/model/binRanges/VisualBinRange.ts
new file mode 100644
index 000000000..f53008f9a
--- /dev/null
+++ b/src/client/northstar/model/binRanges/VisualBinRange.ts
@@ -0,0 +1,36 @@
+import { BinLabel } from '../../model/idea/idea'
+
+export abstract class VisualBinRange {
+
+ constructor() {
+
+ }
+
+ public abstract AddStep(value: number): number;
+
+ public abstract GetValueFromIndex(index: number): number;
+
+ public abstract GetBins(): Array<number>;
+
+ public GetLabel(value: number): string {
+ return value.toString();
+ }
+
+ public GetLabels(): Array<BinLabel> {
+ var labels = new Array<BinLabel>();
+ var bins = this.GetBins();
+ bins.forEach(b => {
+ labels.push(new BinLabel({
+ value: b,
+ minValue: b,
+ maxValue: this.AddStep(b),
+ label: this.GetLabel(b)
+ }));
+ });
+ return labels;
+ }
+}
+
+export enum ChartType {
+ HorizontalBar = 0, VerticalBar = 1, HeatMap = 2, SinglePoint = 3
+} \ No newline at end of file
diff --git a/src/client/northstar/model/binRanges/VisualBinRangeHelper.ts b/src/client/northstar/model/binRanges/VisualBinRangeHelper.ts
new file mode 100644
index 000000000..53d585bb4
--- /dev/null
+++ b/src/client/northstar/model/binRanges/VisualBinRangeHelper.ts
@@ -0,0 +1,71 @@
+import { BinRange, NominalBinRange, QuantitativeBinRange, Exception, AlphabeticBinRange, DateTimeBinRange, AggregateBinRange, DoubleValueAggregateResult, HistogramResult, AttributeParameters } from "../idea/idea";
+import { VisualBinRange, ChartType } from "./VisualBinRange";
+import { NominalVisualBinRange } from "./NominalVisualBinRange";
+import { QuantitativeVisualBinRange } from "./QuantitativeVisualBinRange";
+import { AlphabeticVisualBinRange } from "./AlphabeticVisualBinRange";
+import { DateTimeVisualBinRange } from "./DateTimeVisualBinRange";
+import { Settings } from "../../manager/Gateway";
+import { ModelHelpers } from "../ModelHelpers";
+import { AttributeTransformationModel } from "../../core/attribute/AttributeTransformationModel";
+
+export const SETTINGS_X_BINS = 15;
+export const SETTINGS_Y_BINS = 15;
+export const SETTINGS_SAMPLE_SIZE = 100000;
+
+export class VisualBinRangeHelper {
+
+ public static GetNonAggregateVisualBinRange(dataBinRange: BinRange): VisualBinRange {
+ if (dataBinRange instanceof NominalBinRange) {
+ return new NominalVisualBinRange(dataBinRange as NominalBinRange);
+ }
+ else if (dataBinRange instanceof QuantitativeBinRange) {
+ return new QuantitativeVisualBinRange(dataBinRange as QuantitativeBinRange);
+ }
+ else if (dataBinRange instanceof AlphabeticBinRange) {
+ return new AlphabeticVisualBinRange(dataBinRange as AlphabeticBinRange);
+ }
+ else if (dataBinRange instanceof DateTimeBinRange) {
+ return new DateTimeVisualBinRange(dataBinRange as DateTimeBinRange);
+ }
+ throw new Exception()
+ }
+
+ public static GetVisualBinRange(distinctAttributeParameters: AttributeParameters | undefined, dataBinRange: BinRange, histoResult: HistogramResult, attr: AttributeTransformationModel, chartType: ChartType): VisualBinRange {
+
+ if (!(dataBinRange instanceof AggregateBinRange)) {
+ return VisualBinRangeHelper.GetNonAggregateVisualBinRange(dataBinRange);
+ }
+ else {
+ var aggregateKey = ModelHelpers.CreateAggregateKey(distinctAttributeParameters, attr, histoResult, ModelHelpers.AllBrushIndex(histoResult));
+ var minValue = Number.MAX_VALUE;
+ var maxValue = Number.MIN_VALUE;
+ for (var b = 0; b < histoResult.brushes!.length; b++) {
+ var brush = histoResult.brushes![b];
+ aggregateKey.brushIndex = brush.brushIndex;
+ for (var key in histoResult.bins) {
+ if (histoResult.bins.hasOwnProperty(key)) {
+ var bin = histoResult.bins[key];
+ var res = <DoubleValueAggregateResult>ModelHelpers.GetAggregateResult(bin, aggregateKey);
+ if (res && res.hasResult && res.result) {
+ minValue = Math.min(minValue, res.result);
+ maxValue = Math.max(maxValue, res.result);
+ }
+ }
+ }
+ };
+
+ let visualBinRange = QuantitativeVisualBinRange.Initialize(minValue, maxValue, 10, false);
+
+ if (chartType == ChartType.HorizontalBar || chartType == ChartType.VerticalBar) {
+ visualBinRange = QuantitativeVisualBinRange.Initialize(Math.min(0, minValue),
+ Math.max(0, (visualBinRange as QuantitativeVisualBinRange).DataBinRange.maxValue!),
+ SETTINGS_X_BINS, false);
+ }
+ else if (chartType == ChartType.SinglePoint) {
+ visualBinRange = QuantitativeVisualBinRange.Initialize(Math.min(0, minValue), Math.max(0, maxValue),
+ SETTINGS_X_BINS, false);
+ }
+ return visualBinRange;
+ }
+ }
+}
diff --git a/src/client/northstar/model/idea/MetricTypeMapping.ts b/src/client/northstar/model/idea/MetricTypeMapping.ts
new file mode 100644
index 000000000..11e0c871a
--- /dev/null
+++ b/src/client/northstar/model/idea/MetricTypeMapping.ts
@@ -0,0 +1,30 @@
+import { MetricType } from "./Idea";
+import { Dictionary } from 'typescript-collections';
+
+
+export class MetricTypeMapping {
+
+ public static GetMetricInterpretation(metricType: MetricType): MetricInterpretation {
+ if (metricType == MetricType.Accuracy ||
+ metricType == MetricType.F1 ||
+ metricType == MetricType.F1Macro ||
+ metricType == MetricType.F1Micro ||
+ metricType == MetricType.JaccardSimilarityScore ||
+ metricType == MetricType.ObjectDetectionAveragePrecision ||
+ metricType == MetricType.Precision ||
+ metricType == MetricType.PrecisionAtTopK ||
+ metricType == MetricType.NormalizedMutualInformation ||
+ metricType == MetricType.Recall ||
+ metricType == MetricType.RocAucMacro ||
+ metricType == MetricType.RocAuc ||
+ metricType == MetricType.RocAucMicro ||
+ metricType == MetricType.RSquared) {
+ return MetricInterpretation.HigherIsBetter;
+ }
+ return MetricInterpretation.LowerIsBetter;
+ }
+}
+
+export enum MetricInterpretation {
+ HigherIsBetter, LowerIsBetter
+} \ No newline at end of file
diff --git a/src/client/northstar/model/idea/idea.ts b/src/client/northstar/model/idea/idea.ts
new file mode 100644
index 000000000..9d9d60678
--- /dev/null
+++ b/src/client/northstar/model/idea/idea.ts
@@ -0,0 +1,8551 @@
+/* tslint:disable */
+//----------------------
+// <auto-generated>
+// Generated using the NSwag toolchain v11.19.2.0 (NJsonSchema v9.10.73.0 (Newtonsoft.Json v9.0.0.0)) (http://NSwag.org)
+// </auto-generated>
+//----------------------
+// ReSharper disable InconsistentNaming
+
+
+
+export enum AggregateFunction {
+ None = "None",
+ Sum = "Sum",
+ SumE = "SumE",
+ Count = "Count",
+ Min = "Min",
+ Max = "Max",
+ Avg = "Avg",
+}
+
+export abstract class AggregateParameters implements IAggregateParameters {
+
+ protected _discriminator: string;
+
+ constructor(data?: IAggregateParameters) {
+ if (data) {
+ for (var property in data) {
+ if (data.hasOwnProperty(property))
+ (<any>this)[property] = (<any>data)[property];
+ }
+ }
+ this._discriminator = "AggregateParameters";
+ }
+
+ init(data?: any) {
+ if (data) {
+ }
+ }
+
+ static fromJS(data: any): AggregateParameters {
+ data = typeof data === 'object' ? data : {};
+ if (data["discriminator"] === "AverageAggregateParameters") {
+ let result = new AverageAggregateParameters();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "SingleDimensionAggregateParameters") {
+ throw new Error("The abstract class 'SingleDimensionAggregateParameters' cannot be instantiated.");
+ }
+ if (data["discriminator"] === "CountAggregateParameters") {
+ let result = new CountAggregateParameters();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "KDEAggregateParameters") {
+ let result = new KDEAggregateParameters();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "MarginAggregateParameters") {
+ let result = new MarginAggregateParameters();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "MaxAggregateParameters") {
+ let result = new MaxAggregateParameters();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "MinAggregateParameters") {
+ let result = new MinAggregateParameters();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "SumAggregateParameters") {
+ let result = new SumAggregateParameters();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "SumEstimationAggregateParameters") {
+ let result = new SumEstimationAggregateParameters();
+ result.init(data);
+ return result;
+ }
+ throw new Error("The abstract class 'AggregateParameters' cannot be instantiated.");
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["discriminator"] = this._discriminator;
+ return data;
+ }
+}
+
+export interface IAggregateParameters {
+}
+
+export abstract class SingleDimensionAggregateParameters extends AggregateParameters implements ISingleDimensionAggregateParameters {
+ attributeParameters?: AttributeParameters | undefined;
+ distinctAttributeParameters?: AttributeParameters | undefined;
+
+ constructor(data?: ISingleDimensionAggregateParameters) {
+ super(data);
+ this._discriminator = "SingleDimensionAggregateParameters";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ this.attributeParameters = data["AttributeParameters"] ? AttributeParameters.fromJS(data["AttributeParameters"]) : <any>undefined;
+ this.distinctAttributeParameters = data["DistinctAttributeParameters"] ? AttributeParameters.fromJS(data["DistinctAttributeParameters"]) : <any>undefined;
+ }
+ }
+
+ static fromJS(data: any): SingleDimensionAggregateParameters {
+ data = typeof data === 'object' ? data : {};
+ if (data["discriminator"] === "AverageAggregateParameters") {
+ let result = new AverageAggregateParameters();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "CountAggregateParameters") {
+ let result = new CountAggregateParameters();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "KDEAggregateParameters") {
+ let result = new KDEAggregateParameters();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "MarginAggregateParameters") {
+ let result = new MarginAggregateParameters();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "MaxAggregateParameters") {
+ let result = new MaxAggregateParameters();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "MinAggregateParameters") {
+ let result = new MinAggregateParameters();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "SumAggregateParameters") {
+ let result = new SumAggregateParameters();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "SumEstimationAggregateParameters") {
+ let result = new SumEstimationAggregateParameters();
+ result.init(data);
+ return result;
+ }
+ throw new Error("The abstract class 'SingleDimensionAggregateParameters' cannot be instantiated.");
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["AttributeParameters"] = this.attributeParameters ? this.attributeParameters.toJSON() : <any>undefined;
+ data["DistinctAttributeParameters"] = this.distinctAttributeParameters ? this.distinctAttributeParameters.toJSON() : <any>undefined;
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface ISingleDimensionAggregateParameters extends IAggregateParameters {
+ attributeParameters?: AttributeParameters | undefined;
+ distinctAttributeParameters?: AttributeParameters | undefined;
+}
+
+export class AverageAggregateParameters extends SingleDimensionAggregateParameters implements IAverageAggregateParameters {
+
+ constructor(data?: IAverageAggregateParameters) {
+ super(data);
+ this._discriminator = "AverageAggregateParameters";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ }
+ }
+
+ static fromJS(data: any): AverageAggregateParameters {
+ data = typeof data === 'object' ? data : {};
+ let result = new AverageAggregateParameters();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IAverageAggregateParameters extends ISingleDimensionAggregateParameters {
+}
+
+export abstract class AttributeParameters implements IAttributeParameters {
+ visualizationHints?: VisualizationHint[] | undefined;
+ rawName?: string | undefined;
+
+ protected _discriminator: string;
+
+ constructor(data?: IAttributeParameters) {
+ if (data) {
+ for (var property in data) {
+ if (data.hasOwnProperty(property))
+ (<any>this)[property] = (<any>data)[property];
+ }
+ }
+ this._discriminator = "AttributeParameters";
+ }
+
+ init(data?: any) {
+ if (data) {
+ if (data["VisualizationHints"] && data["VisualizationHints"].constructor === Array) {
+ this.visualizationHints = [];
+ for (let item of data["VisualizationHints"])
+ this.visualizationHints.push(item);
+ }
+ this.rawName = data["RawName"];
+ }
+ }
+
+ static fromJS(data: any): AttributeParameters {
+ data = typeof data === 'object' ? data : {};
+ if (data["discriminator"] === "AttributeBackendParameters") {
+ let result = new AttributeBackendParameters();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "AttributeCaclculatedParameters") {
+ throw new Error("The abstract class 'AttributeCaclculatedParameters' cannot be instantiated.");
+ }
+ if (data["discriminator"] === "AttributeCodeParameters") {
+ let result = new AttributeCodeParameters();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "AttributeColumnParameters") {
+ let result = new AttributeColumnParameters();
+ result.init(data);
+ return result;
+ }
+ throw new Error("The abstract class 'AttributeParameters' cannot be instantiated.");
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["discriminator"] = this._discriminator;
+ if (this.visualizationHints && this.visualizationHints.constructor === Array) {
+ data["VisualizationHints"] = [];
+ for (let item of this.visualizationHints)
+ data["VisualizationHints"].push(item);
+ }
+ data["RawName"] = this.rawName;
+ return data;
+ }
+}
+
+export interface IAttributeParameters {
+ visualizationHints?: VisualizationHint[] | undefined;
+ rawName?: string | undefined;
+}
+
+export enum VisualizationHint {
+ TreatAsEnumeration = "TreatAsEnumeration",
+ DefaultFlipAxis = "DefaultFlipAxis",
+ Image = "Image",
+}
+
+export abstract class AttributeCaclculatedParameters extends AttributeParameters implements IAttributeCaclculatedParameters {
+
+ constructor(data?: IAttributeCaclculatedParameters) {
+ super(data);
+ this._discriminator = "AttributeCaclculatedParameters";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ }
+ }
+
+ static fromJS(data: any): AttributeCaclculatedParameters {
+ data = typeof data === 'object' ? data : {};
+ if (data["discriminator"] === "AttributeBackendParameters") {
+ let result = new AttributeBackendParameters();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "AttributeCodeParameters") {
+ let result = new AttributeCodeParameters();
+ result.init(data);
+ return result;
+ }
+ throw new Error("The abstract class 'AttributeCaclculatedParameters' cannot be instantiated.");
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IAttributeCaclculatedParameters extends IAttributeParameters {
+}
+
+export class AttributeBackendParameters extends AttributeCaclculatedParameters implements IAttributeBackendParameters {
+ id?: string | undefined;
+
+ constructor(data?: IAttributeBackendParameters) {
+ super(data);
+ this._discriminator = "AttributeBackendParameters";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ this.id = data["Id"];
+ }
+ }
+
+ static fromJS(data: any): AttributeBackendParameters {
+ data = typeof data === 'object' ? data : {};
+ let result = new AttributeBackendParameters();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["Id"] = this.id;
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IAttributeBackendParameters extends IAttributeCaclculatedParameters {
+ id?: string | undefined;
+}
+
+export class AttributeCodeParameters extends AttributeCaclculatedParameters implements IAttributeCodeParameters {
+ code?: string | undefined;
+
+ constructor(data?: IAttributeCodeParameters) {
+ super(data);
+ this._discriminator = "AttributeCodeParameters";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ this.code = data["Code"];
+ }
+ }
+
+ static fromJS(data: any): AttributeCodeParameters {
+ data = typeof data === 'object' ? data : {};
+ let result = new AttributeCodeParameters();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["Code"] = this.code;
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IAttributeCodeParameters extends IAttributeCaclculatedParameters {
+ code?: string | undefined;
+}
+
+export class AttributeColumnParameters extends AttributeParameters implements IAttributeColumnParameters {
+
+ constructor(data?: IAttributeColumnParameters) {
+ super(data);
+ this._discriminator = "AttributeColumnParameters";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ }
+ }
+
+ static fromJS(data: any): AttributeColumnParameters {
+ data = typeof data === 'object' ? data : {};
+ let result = new AttributeColumnParameters();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IAttributeColumnParameters extends IAttributeParameters {
+}
+
+export class CountAggregateParameters extends SingleDimensionAggregateParameters implements ICountAggregateParameters {
+
+ constructor(data?: ICountAggregateParameters) {
+ super(data);
+ this._discriminator = "CountAggregateParameters";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ }
+ }
+
+ static fromJS(data: any): CountAggregateParameters {
+ data = typeof data === 'object' ? data : {};
+ let result = new CountAggregateParameters();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface ICountAggregateParameters extends ISingleDimensionAggregateParameters {
+}
+
+export class KDEAggregateParameters extends SingleDimensionAggregateParameters implements IKDEAggregateParameters {
+ nrOfSamples?: number | undefined;
+
+ constructor(data?: IKDEAggregateParameters) {
+ super(data);
+ this._discriminator = "KDEAggregateParameters";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ this.nrOfSamples = data["NrOfSamples"];
+ }
+ }
+
+ static fromJS(data: any): KDEAggregateParameters {
+ data = typeof data === 'object' ? data : {};
+ let result = new KDEAggregateParameters();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["NrOfSamples"] = this.nrOfSamples;
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IKDEAggregateParameters extends ISingleDimensionAggregateParameters {
+ nrOfSamples?: number | undefined;
+}
+
+export class MarginAggregateParameters extends SingleDimensionAggregateParameters implements IMarginAggregateParameters {
+ aggregateFunction?: AggregateFunction | undefined;
+
+ constructor(data?: IMarginAggregateParameters) {
+ super(data);
+ this._discriminator = "MarginAggregateParameters";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ this.aggregateFunction = data["AggregateFunction"];
+ }
+ }
+
+ static fromJS(data: any): MarginAggregateParameters {
+ data = typeof data === 'object' ? data : {};
+ let result = new MarginAggregateParameters();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["AggregateFunction"] = this.aggregateFunction;
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IMarginAggregateParameters extends ISingleDimensionAggregateParameters {
+ aggregateFunction?: AggregateFunction | undefined;
+}
+
+export class MaxAggregateParameters extends SingleDimensionAggregateParameters implements IMaxAggregateParameters {
+
+ constructor(data?: IMaxAggregateParameters) {
+ super(data);
+ this._discriminator = "MaxAggregateParameters";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ }
+ }
+
+ static fromJS(data: any): MaxAggregateParameters {
+ data = typeof data === 'object' ? data : {};
+ let result = new MaxAggregateParameters();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IMaxAggregateParameters extends ISingleDimensionAggregateParameters {
+}
+
+export class MinAggregateParameters extends SingleDimensionAggregateParameters implements IMinAggregateParameters {
+
+ constructor(data?: IMinAggregateParameters) {
+ super(data);
+ this._discriminator = "MinAggregateParameters";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ }
+ }
+
+ static fromJS(data: any): MinAggregateParameters {
+ data = typeof data === 'object' ? data : {};
+ let result = new MinAggregateParameters();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IMinAggregateParameters extends ISingleDimensionAggregateParameters {
+}
+
+export class SumAggregateParameters extends SingleDimensionAggregateParameters implements ISumAggregateParameters {
+
+ constructor(data?: ISumAggregateParameters) {
+ super(data);
+ this._discriminator = "SumAggregateParameters";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ }
+ }
+
+ static fromJS(data: any): SumAggregateParameters {
+ data = typeof data === 'object' ? data : {};
+ let result = new SumAggregateParameters();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface ISumAggregateParameters extends ISingleDimensionAggregateParameters {
+}
+
+export class SumEstimationAggregateParameters extends SingleDimensionAggregateParameters implements ISumEstimationAggregateParameters {
+
+ constructor(data?: ISumEstimationAggregateParameters) {
+ super(data);
+ this._discriminator = "SumEstimationAggregateParameters";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ }
+ }
+
+ static fromJS(data: any): SumEstimationAggregateParameters {
+ data = typeof data === 'object' ? data : {};
+ let result = new SumEstimationAggregateParameters();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface ISumEstimationAggregateParameters extends ISingleDimensionAggregateParameters {
+}
+
+export enum OrderingFunction {
+ None = 0,
+ SortUp = 1,
+ SortDown = 2,
+}
+
+export abstract class BinningParameters implements IBinningParameters {
+ attributeParameters?: AttributeParameters | undefined;
+
+ protected _discriminator: string;
+
+ constructor(data?: IBinningParameters) {
+ if (data) {
+ for (var property in data) {
+ if (data.hasOwnProperty(property))
+ (<any>this)[property] = (<any>data)[property];
+ }
+ }
+ this._discriminator = "BinningParameters";
+ }
+
+ init(data?: any) {
+ if (data) {
+ this.attributeParameters = data["AttributeParameters"] ? AttributeParameters.fromJS(data["AttributeParameters"]) : <any>undefined;
+ }
+ }
+
+ static fromJS(data: any): BinningParameters {
+ data = typeof data === 'object' ? data : {};
+ if (data["discriminator"] === "EquiWidthBinningParameters") {
+ let result = new EquiWidthBinningParameters();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "SingleBinBinningParameters") {
+ let result = new SingleBinBinningParameters();
+ result.init(data);
+ return result;
+ }
+ throw new Error("The abstract class 'BinningParameters' cannot be instantiated.");
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["discriminator"] = this._discriminator;
+ data["AttributeParameters"] = this.attributeParameters ? this.attributeParameters.toJSON() : <any>undefined;
+ return data;
+ }
+}
+
+export interface IBinningParameters {
+ attributeParameters?: AttributeParameters | undefined;
+}
+
+export class EquiWidthBinningParameters extends BinningParameters implements IEquiWidthBinningParameters {
+ minValue?: number | undefined;
+ maxValue?: number | undefined;
+ requestedNrOfBins?: number | undefined;
+ referenceValue?: number | undefined;
+ step?: number | undefined;
+
+ constructor(data?: IEquiWidthBinningParameters) {
+ super(data);
+ this._discriminator = "EquiWidthBinningParameters";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ this.minValue = data["MinValue"];
+ this.maxValue = data["MaxValue"];
+ this.requestedNrOfBins = data["RequestedNrOfBins"];
+ this.referenceValue = data["ReferenceValue"];
+ this.step = data["Step"];
+ }
+ }
+
+ static fromJS(data: any): EquiWidthBinningParameters {
+ data = typeof data === 'object' ? data : {};
+ let result = new EquiWidthBinningParameters();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["MinValue"] = this.minValue;
+ data["MaxValue"] = this.maxValue;
+ data["RequestedNrOfBins"] = this.requestedNrOfBins;
+ data["ReferenceValue"] = this.referenceValue;
+ data["Step"] = this.step;
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IEquiWidthBinningParameters extends IBinningParameters {
+ minValue?: number | undefined;
+ maxValue?: number | undefined;
+ requestedNrOfBins?: number | undefined;
+ referenceValue?: number | undefined;
+ step?: number | undefined;
+}
+
+export class SingleBinBinningParameters extends BinningParameters implements ISingleBinBinningParameters {
+
+ constructor(data?: ISingleBinBinningParameters) {
+ super(data);
+ this._discriminator = "SingleBinBinningParameters";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ }
+ }
+
+ static fromJS(data: any): SingleBinBinningParameters {
+ data = typeof data === 'object' ? data : {};
+ let result = new SingleBinBinningParameters();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface ISingleBinBinningParameters extends IBinningParameters {
+}
+
+export class Attribute implements IAttribute {
+ displayName?: string | undefined;
+ rawName?: string | undefined;
+ description?: string | undefined;
+ dataType?: DataType | undefined;
+ visualizationHints?: VisualizationHint[] | undefined;
+ isTarget?: boolean | undefined;
+
+ constructor(data?: IAttribute) {
+ if (data) {
+ for (var property in data) {
+ if (data.hasOwnProperty(property))
+ (<any>this)[property] = (<any>data)[property];
+ }
+ }
+ }
+
+ init(data?: any) {
+ if (data) {
+ this.displayName = data["DisplayName"];
+ this.rawName = data["RawName"];
+ this.description = data["Description"];
+ this.dataType = data["DataType"];
+ if (data["VisualizationHints"] && data["VisualizationHints"].constructor === Array) {
+ this.visualizationHints = [];
+ for (let item of data["VisualizationHints"])
+ this.visualizationHints.push(item);
+ }
+ this.isTarget = data["IsTarget"];
+ }
+ }
+
+ static fromJS(data: any): Attribute {
+ data = typeof data === 'object' ? data : {};
+ let result = new Attribute();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["DisplayName"] = this.displayName;
+ data["RawName"] = this.rawName;
+ data["Description"] = this.description;
+ data["DataType"] = this.dataType;
+ if (this.visualizationHints && this.visualizationHints.constructor === Array) {
+ data["VisualizationHints"] = [];
+ for (let item of this.visualizationHints)
+ data["VisualizationHints"].push(item);
+ }
+ data["IsTarget"] = this.isTarget;
+ return data;
+ }
+}
+
+export interface IAttribute {
+ displayName?: string | undefined;
+ rawName?: string | undefined;
+ description?: string | undefined;
+ dataType?: DataType | undefined;
+ visualizationHints?: VisualizationHint[] | undefined;
+ isTarget?: boolean | undefined;
+}
+
+export enum DataType {
+ Int = "Int",
+ String = "String",
+ Float = "Float",
+ Double = "Double",
+ DateTime = "DateTime",
+ Object = "Object",
+ Undefined = "Undefined",
+}
+
+export class AttributeGroup implements IAttributeGroup {
+ name?: string | undefined;
+ attributeGroups?: AttributeGroup[] | undefined;
+ attributes?: Attribute[] | undefined;
+
+ constructor(data?: IAttributeGroup) {
+ if (data) {
+ for (var property in data) {
+ if (data.hasOwnProperty(property))
+ (<any>this)[property] = (<any>data)[property];
+ }
+ }
+ }
+
+ init(data?: any) {
+ if (data) {
+ this.name = data["Name"];
+ if (data["AttributeGroups"] && data["AttributeGroups"].constructor === Array) {
+ this.attributeGroups = [];
+ for (let item of data["AttributeGroups"])
+ this.attributeGroups.push(AttributeGroup.fromJS(item));
+ }
+ if (data["Attributes"] && data["Attributes"].constructor === Array) {
+ this.attributes = [];
+ for (let item of data["Attributes"])
+ this.attributes.push(Attribute.fromJS(item));
+ }
+ }
+ }
+
+ static fromJS(data: any): AttributeGroup {
+ data = typeof data === 'object' ? data : {};
+ let result = new AttributeGroup();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["Name"] = this.name;
+ if (this.attributeGroups && this.attributeGroups.constructor === Array) {
+ data["AttributeGroups"] = [];
+ for (let item of this.attributeGroups)
+ data["AttributeGroups"].push(item.toJSON());
+ }
+ if (this.attributes && this.attributes.constructor === Array) {
+ data["Attributes"] = [];
+ for (let item of this.attributes)
+ data["Attributes"].push(item.toJSON());
+ }
+ return data;
+ }
+}
+
+export interface IAttributeGroup {
+ name?: string | undefined;
+ attributeGroups?: AttributeGroup[] | undefined;
+ attributes?: Attribute[] | undefined;
+}
+
+export class Catalog implements ICatalog {
+ supportedOperations?: string[] | undefined;
+ schemas?: Schema[] | undefined;
+
+ constructor(data?: ICatalog) {
+ if (data) {
+ for (var property in data) {
+ if (data.hasOwnProperty(property))
+ (<any>this)[property] = (<any>data)[property];
+ }
+ }
+ }
+
+ init(data?: any) {
+ if (data) {
+ if (data["SupportedOperations"] && data["SupportedOperations"].constructor === Array) {
+ this.supportedOperations = [];
+ for (let item of data["SupportedOperations"])
+ this.supportedOperations.push(item);
+ }
+ if (data["Schemas"] && data["Schemas"].constructor === Array) {
+ this.schemas = [];
+ for (let item of data["Schemas"])
+ this.schemas.push(Schema.fromJS(item));
+ }
+ }
+ }
+
+ static fromJS(data: any): Catalog {
+ data = typeof data === 'object' ? data : {};
+ let result = new Catalog();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ if (this.supportedOperations && this.supportedOperations.constructor === Array) {
+ data["SupportedOperations"] = [];
+ for (let item of this.supportedOperations)
+ data["SupportedOperations"].push(item);
+ }
+ if (this.schemas && this.schemas.constructor === Array) {
+ data["Schemas"] = [];
+ for (let item of this.schemas)
+ data["Schemas"].push(item.toJSON());
+ }
+ return data;
+ }
+}
+
+export interface ICatalog {
+ supportedOperations?: string[] | undefined;
+ schemas?: Schema[] | undefined;
+}
+
+export class Schema implements ISchema {
+ rootAttributeGroup?: AttributeGroup | undefined;
+ displayName?: string | undefined;
+ augmentedFrom?: string | undefined;
+ rawName?: string | undefined;
+ problemDescription?: string | undefined;
+ darpaProblemDoc?: DarpaProblemDoc | undefined;
+ distinctAttributeParameters?: AttributeParameters | undefined;
+ darpaDatasetDoc?: DarpaDatasetDoc | undefined;
+ darpaDatasetLocation?: string | undefined;
+ isMultiResourceData?: boolean | undefined;
+ problemFinderRows?: ProblemFinderRows[] | undefined;
+ correlationRows?: ProblemFinderRows[] | undefined;
+
+ constructor(data?: ISchema) {
+ if (data) {
+ for (var property in data) {
+ if (data.hasOwnProperty(property))
+ (<any>this)[property] = (<any>data)[property];
+ }
+ }
+ }
+
+ init(data?: any) {
+ if (data) {
+ this.rootAttributeGroup = data["RootAttributeGroup"] ? AttributeGroup.fromJS(data["RootAttributeGroup"]) : <any>undefined;
+ this.displayName = data["DisplayName"];
+ this.augmentedFrom = data["AugmentedFrom"];
+ this.rawName = data["RawName"];
+ this.problemDescription = data["ProblemDescription"];
+ this.darpaProblemDoc = data["DarpaProblemDoc"] ? DarpaProblemDoc.fromJS(data["DarpaProblemDoc"]) : <any>undefined;
+ this.distinctAttributeParameters = data["DistinctAttributeParameters"] ? AttributeParameters.fromJS(data["DistinctAttributeParameters"]) : <any>undefined;
+ this.darpaDatasetDoc = data["DarpaDatasetDoc"] ? DarpaDatasetDoc.fromJS(data["DarpaDatasetDoc"]) : <any>undefined;
+ this.darpaDatasetLocation = data["DarpaDatasetLocation"];
+ this.isMultiResourceData = data["IsMultiResourceData"];
+ if (data["ProblemFinderRows"] && data["ProblemFinderRows"].constructor === Array) {
+ this.problemFinderRows = [];
+ for (let item of data["ProblemFinderRows"])
+ this.problemFinderRows.push(ProblemFinderRows.fromJS(item));
+ }
+ if (data["CorrelationRows"] && data["CorrelationRows"].constructor === Array) {
+ this.correlationRows = [];
+ for (let item of data["CorrelationRows"])
+ this.correlationRows.push(ProblemFinderRows.fromJS(item));
+ }
+ }
+ }
+
+ static fromJS(data: any): Schema {
+ data = typeof data === 'object' ? data : {};
+ let result = new Schema();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["RootAttributeGroup"] = this.rootAttributeGroup ? this.rootAttributeGroup.toJSON() : <any>undefined;
+ data["DisplayName"] = this.displayName;
+ data["AugmentedFrom"] = this.augmentedFrom;
+ data["RawName"] = this.rawName;
+ data["ProblemDescription"] = this.problemDescription;
+ data["DarpaProblemDoc"] = this.darpaProblemDoc ? this.darpaProblemDoc.toJSON() : <any>undefined;
+ data["DistinctAttributeParameters"] = this.distinctAttributeParameters ? this.distinctAttributeParameters.toJSON() : <any>undefined;
+ data["DarpaDatasetDoc"] = this.darpaDatasetDoc ? this.darpaDatasetDoc.toJSON() : <any>undefined;
+ data["DarpaDatasetLocation"] = this.darpaDatasetLocation;
+ data["IsMultiResourceData"] = this.isMultiResourceData;
+ if (this.problemFinderRows && this.problemFinderRows.constructor === Array) {
+ data["ProblemFinderRows"] = [];
+ for (let item of this.problemFinderRows)
+ data["ProblemFinderRows"].push(item.toJSON());
+ }
+ if (this.correlationRows && this.correlationRows.constructor === Array) {
+ data["CorrelationRows"] = [];
+ for (let item of this.correlationRows)
+ data["CorrelationRows"].push(item.toJSON());
+ }
+ return data;
+ }
+}
+
+export interface ISchema {
+ rootAttributeGroup?: AttributeGroup | undefined;
+ displayName?: string | undefined;
+ augmentedFrom?: string | undefined;
+ rawName?: string | undefined;
+ problemDescription?: string | undefined;
+ darpaProblemDoc?: DarpaProblemDoc | undefined;
+ distinctAttributeParameters?: AttributeParameters | undefined;
+ darpaDatasetDoc?: DarpaDatasetDoc | undefined;
+ darpaDatasetLocation?: string | undefined;
+ isMultiResourceData?: boolean | undefined;
+ problemFinderRows?: ProblemFinderRows[] | undefined;
+ correlationRows?: ProblemFinderRows[] | undefined;
+}
+
+export class DarpaProblemDoc implements IDarpaProblemDoc {
+ about?: ProblemAbout | undefined;
+ inputs?: ProblemInputs | undefined;
+
+ constructor(data?: IDarpaProblemDoc) {
+ if (data) {
+ for (var property in data) {
+ if (data.hasOwnProperty(property))
+ (<any>this)[property] = (<any>data)[property];
+ }
+ }
+ }
+
+ init(data?: any) {
+ if (data) {
+ this.about = data["about"] ? ProblemAbout.fromJS(data["about"]) : <any>undefined;
+ this.inputs = data["inputs"] ? ProblemInputs.fromJS(data["inputs"]) : <any>undefined;
+ }
+ }
+
+ static fromJS(data: any): DarpaProblemDoc {
+ data = typeof data === 'object' ? data : {};
+ let result = new DarpaProblemDoc();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["about"] = this.about ? this.about.toJSON() : <any>undefined;
+ data["inputs"] = this.inputs ? this.inputs.toJSON() : <any>undefined;
+ return data;
+ }
+}
+
+export interface IDarpaProblemDoc {
+ about?: ProblemAbout | undefined;
+ inputs?: ProblemInputs | undefined;
+}
+
+export class ProblemAbout implements IProblemAbout {
+ problemID?: string | undefined;
+ problemName?: string | undefined;
+ problemDescription?: string | undefined;
+ taskType?: string | undefined;
+ taskSubType?: string | undefined;
+ problemSchemaVersion?: string | undefined;
+ problemVersion?: string | undefined;
+
+ constructor(data?: IProblemAbout) {
+ if (data) {
+ for (var property in data) {
+ if (data.hasOwnProperty(property))
+ (<any>this)[property] = (<any>data)[property];
+ }
+ }
+ }
+
+ init(data?: any) {
+ if (data) {
+ this.problemID = data["problemID"];
+ this.problemName = data["problemName"];
+ this.problemDescription = data["problemDescription"];
+ this.taskType = data["taskType"];
+ this.taskSubType = data["taskSubType"];
+ this.problemSchemaVersion = data["problemSchemaVersion"];
+ this.problemVersion = data["problemVersion"];
+ }
+ }
+
+ static fromJS(data: any): ProblemAbout {
+ data = typeof data === 'object' ? data : {};
+ let result = new ProblemAbout();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["problemID"] = this.problemID;
+ data["problemName"] = this.problemName;
+ data["problemDescription"] = this.problemDescription;
+ data["taskType"] = this.taskType;
+ data["taskSubType"] = this.taskSubType;
+ data["problemSchemaVersion"] = this.problemSchemaVersion;
+ data["problemVersion"] = this.problemVersion;
+ return data;
+ }
+}
+
+export interface IProblemAbout {
+ problemID?: string | undefined;
+ problemName?: string | undefined;
+ problemDescription?: string | undefined;
+ taskType?: string | undefined;
+ taskSubType?: string | undefined;
+ problemSchemaVersion?: string | undefined;
+ problemVersion?: string | undefined;
+}
+
+export class ProblemInputs implements IProblemInputs {
+ data?: ProblemData[] | undefined;
+ performanceMetrics?: ProblemPerformanceMetric[] | undefined;
+
+ constructor(data?: IProblemInputs) {
+ if (data) {
+ for (var property in data) {
+ if (data.hasOwnProperty(property))
+ (<any>this)[property] = (<any>data)[property];
+ }
+ }
+ }
+
+ init(data?: any) {
+ if (data) {
+ if (data["data"] && data["data"].constructor === Array) {
+ this.data = [];
+ for (let item of data["data"])
+ this.data.push(ProblemData.fromJS(item));
+ }
+ if (data["performanceMetrics"] && data["performanceMetrics"].constructor === Array) {
+ this.performanceMetrics = [];
+ for (let item of data["performanceMetrics"])
+ this.performanceMetrics.push(ProblemPerformanceMetric.fromJS(item));
+ }
+ }
+ }
+
+ static fromJS(data: any): ProblemInputs {
+ data = typeof data === 'object' ? data : {};
+ let result = new ProblemInputs();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ if (this.data && this.data.constructor === Array) {
+ data["data"] = [];
+ for (let item of this.data)
+ data["data"].push(item.toJSON());
+ }
+ if (this.performanceMetrics && this.performanceMetrics.constructor === Array) {
+ data["performanceMetrics"] = [];
+ for (let item of this.performanceMetrics)
+ data["performanceMetrics"].push(item.toJSON());
+ }
+ return data;
+ }
+}
+
+export interface IProblemInputs {
+ data?: ProblemData[] | undefined;
+ performanceMetrics?: ProblemPerformanceMetric[] | undefined;
+}
+
+export class ProblemData implements IProblemData {
+ datasetID?: string | undefined;
+ targets?: ProblemTarget[] | undefined;
+
+ constructor(data?: IProblemData) {
+ if (data) {
+ for (var property in data) {
+ if (data.hasOwnProperty(property))
+ (<any>this)[property] = (<any>data)[property];
+ }
+ }
+ }
+
+ init(data?: any) {
+ if (data) {
+ this.datasetID = data["datasetID"];
+ if (data["targets"] && data["targets"].constructor === Array) {
+ this.targets = [];
+ for (let item of data["targets"])
+ this.targets.push(ProblemTarget.fromJS(item));
+ }
+ }
+ }
+
+ static fromJS(data: any): ProblemData {
+ data = typeof data === 'object' ? data : {};
+ let result = new ProblemData();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["datasetID"] = this.datasetID;
+ if (this.targets && this.targets.constructor === Array) {
+ data["targets"] = [];
+ for (let item of this.targets)
+ data["targets"].push(item.toJSON());
+ }
+ return data;
+ }
+}
+
+export interface IProblemData {
+ datasetID?: string | undefined;
+ targets?: ProblemTarget[] | undefined;
+}
+
+export class ProblemTarget implements IProblemTarget {
+ targetIndex?: number | undefined;
+ resID?: string | undefined;
+ colIndex?: number | undefined;
+ colName?: string | undefined;
+
+ constructor(data?: IProblemTarget) {
+ if (data) {
+ for (var property in data) {
+ if (data.hasOwnProperty(property))
+ (<any>this)[property] = (<any>data)[property];
+ }
+ }
+ }
+
+ init(data?: any) {
+ if (data) {
+ this.targetIndex = data["targetIndex"];
+ this.resID = data["resID"];
+ this.colIndex = data["colIndex"];
+ this.colName = data["colName"];
+ }
+ }
+
+ static fromJS(data: any): ProblemTarget {
+ data = typeof data === 'object' ? data : {};
+ let result = new ProblemTarget();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["targetIndex"] = this.targetIndex;
+ data["resID"] = this.resID;
+ data["colIndex"] = this.colIndex;
+ data["colName"] = this.colName;
+ return data;
+ }
+}
+
+export interface IProblemTarget {
+ targetIndex?: number | undefined;
+ resID?: string | undefined;
+ colIndex?: number | undefined;
+ colName?: string | undefined;
+}
+
+export class ProblemPerformanceMetric implements IProblemPerformanceMetric {
+ metric?: string | undefined;
+
+ constructor(data?: IProblemPerformanceMetric) {
+ if (data) {
+ for (var property in data) {
+ if (data.hasOwnProperty(property))
+ (<any>this)[property] = (<any>data)[property];
+ }
+ }
+ }
+
+ init(data?: any) {
+ if (data) {
+ this.metric = data["metric"];
+ }
+ }
+
+ static fromJS(data: any): ProblemPerformanceMetric {
+ data = typeof data === 'object' ? data : {};
+ let result = new ProblemPerformanceMetric();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["metric"] = this.metric;
+ return data;
+ }
+}
+
+export interface IProblemPerformanceMetric {
+ metric?: string | undefined;
+}
+
+export class DarpaDatasetDoc implements IDarpaDatasetDoc {
+ about?: DatasetAbout | undefined;
+ dataResources?: Resource[] | undefined;
+
+ constructor(data?: IDarpaDatasetDoc) {
+ if (data) {
+ for (var property in data) {
+ if (data.hasOwnProperty(property))
+ (<any>this)[property] = (<any>data)[property];
+ }
+ }
+ }
+
+ init(data?: any) {
+ if (data) {
+ this.about = data["about"] ? DatasetAbout.fromJS(data["about"]) : <any>undefined;
+ if (data["dataResources"] && data["dataResources"].constructor === Array) {
+ this.dataResources = [];
+ for (let item of data["dataResources"])
+ this.dataResources.push(Resource.fromJS(item));
+ }
+ }
+ }
+
+ static fromJS(data: any): DarpaDatasetDoc {
+ data = typeof data === 'object' ? data : {};
+ let result = new DarpaDatasetDoc();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["about"] = this.about ? this.about.toJSON() : <any>undefined;
+ if (this.dataResources && this.dataResources.constructor === Array) {
+ data["dataResources"] = [];
+ for (let item of this.dataResources)
+ data["dataResources"].push(item.toJSON());
+ }
+ return data;
+ }
+}
+
+export interface IDarpaDatasetDoc {
+ about?: DatasetAbout | undefined;
+ dataResources?: Resource[] | undefined;
+}
+
+export class DatasetAbout implements IDatasetAbout {
+ datasetID?: string | undefined;
+ datasetName?: string | undefined;
+ description?: string | undefined;
+ citation?: string | undefined;
+ license?: string | undefined;
+ source?: string | undefined;
+ sourceURI?: string | undefined;
+ approximateSize?: string | undefined;
+ datasetSchemaVersion?: string | undefined;
+ redacted?: boolean | undefined;
+ datasetVersion?: string | undefined;
+
+ constructor(data?: IDatasetAbout) {
+ if (data) {
+ for (var property in data) {
+ if (data.hasOwnProperty(property))
+ (<any>this)[property] = (<any>data)[property];
+ }
+ }
+ }
+
+ init(data?: any) {
+ if (data) {
+ this.datasetID = data["datasetID"];
+ this.datasetName = data["datasetName"];
+ this.description = data["description"];
+ this.citation = data["citation"];
+ this.license = data["license"];
+ this.source = data["source"];
+ this.sourceURI = data["sourceURI"];
+ this.approximateSize = data["approximateSize"];
+ this.datasetSchemaVersion = data["datasetSchemaVersion"];
+ this.redacted = data["redacted"];
+ this.datasetVersion = data["datasetVersion"];
+ }
+ }
+
+ static fromJS(data: any): DatasetAbout {
+ data = typeof data === 'object' ? data : {};
+ let result = new DatasetAbout();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["datasetID"] = this.datasetID;
+ data["datasetName"] = this.datasetName;
+ data["description"] = this.description;
+ data["citation"] = this.citation;
+ data["license"] = this.license;
+ data["source"] = this.source;
+ data["sourceURI"] = this.sourceURI;
+ data["approximateSize"] = this.approximateSize;
+ data["datasetSchemaVersion"] = this.datasetSchemaVersion;
+ data["redacted"] = this.redacted;
+ data["datasetVersion"] = this.datasetVersion;
+ return data;
+ }
+}
+
+export interface IDatasetAbout {
+ datasetID?: string | undefined;
+ datasetName?: string | undefined;
+ description?: string | undefined;
+ citation?: string | undefined;
+ license?: string | undefined;
+ source?: string | undefined;
+ sourceURI?: string | undefined;
+ approximateSize?: string | undefined;
+ datasetSchemaVersion?: string | undefined;
+ redacted?: boolean | undefined;
+ datasetVersion?: string | undefined;
+}
+
+export class Resource implements IResource {
+ resID?: string | undefined;
+ resPath?: string | undefined;
+ resType?: string | undefined;
+ resFormat?: string[] | undefined;
+ columns?: Column[] | undefined;
+ isCollection?: boolean | undefined;
+
+ constructor(data?: IResource) {
+ if (data) {
+ for (var property in data) {
+ if (data.hasOwnProperty(property))
+ (<any>this)[property] = (<any>data)[property];
+ }
+ }
+ }
+
+ init(data?: any) {
+ if (data) {
+ this.resID = data["resID"];
+ this.resPath = data["resPath"];
+ this.resType = data["resType"];
+ if (data["resFormat"] && data["resFormat"].constructor === Array) {
+ this.resFormat = [];
+ for (let item of data["resFormat"])
+ this.resFormat.push(item);
+ }
+ if (data["columns"] && data["columns"].constructor === Array) {
+ this.columns = [];
+ for (let item of data["columns"])
+ this.columns.push(Column.fromJS(item));
+ }
+ this.isCollection = data["isCollection"];
+ }
+ }
+
+ static fromJS(data: any): Resource {
+ data = typeof data === 'object' ? data : {};
+ let result = new Resource();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["resID"] = this.resID;
+ data["resPath"] = this.resPath;
+ data["resType"] = this.resType;
+ if (this.resFormat && this.resFormat.constructor === Array) {
+ data["resFormat"] = [];
+ for (let item of this.resFormat)
+ data["resFormat"].push(item);
+ }
+ if (this.columns && this.columns.constructor === Array) {
+ data["columns"] = [];
+ for (let item of this.columns)
+ data["columns"].push(item.toJSON());
+ }
+ data["isCollection"] = this.isCollection;
+ return data;
+ }
+}
+
+export interface IResource {
+ resID?: string | undefined;
+ resPath?: string | undefined;
+ resType?: string | undefined;
+ resFormat?: string[] | undefined;
+ columns?: Column[] | undefined;
+ isCollection?: boolean | undefined;
+}
+
+export class Column implements IColumn {
+ colIndex?: number | undefined;
+ colDescription?: string | undefined;
+ colName?: string | undefined;
+ colType?: string | undefined;
+ role?: string[] | undefined;
+ refersTo?: Reference | undefined;
+
+ constructor(data?: IColumn) {
+ if (data) {
+ for (var property in data) {
+ if (data.hasOwnProperty(property))
+ (<any>this)[property] = (<any>data)[property];
+ }
+ }
+ }
+
+ init(data?: any) {
+ if (data) {
+ this.colIndex = data["colIndex"];
+ this.colDescription = data["colDescription"];
+ this.colName = data["colName"];
+ this.colType = data["colType"];
+ if (data["role"] && data["role"].constructor === Array) {
+ this.role = [];
+ for (let item of data["role"])
+ this.role.push(item);
+ }
+ this.refersTo = data["refersTo"] ? Reference.fromJS(data["refersTo"]) : <any>undefined;
+ }
+ }
+
+ static fromJS(data: any): Column {
+ data = typeof data === 'object' ? data : {};
+ let result = new Column();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["colIndex"] = this.colIndex;
+ data["colDescription"] = this.colDescription;
+ data["colName"] = this.colName;
+ data["colType"] = this.colType;
+ if (this.role && this.role.constructor === Array) {
+ data["role"] = [];
+ for (let item of this.role)
+ data["role"].push(item);
+ }
+ data["refersTo"] = this.refersTo ? this.refersTo.toJSON() : <any>undefined;
+ return data;
+ }
+}
+
+export interface IColumn {
+ colIndex?: number | undefined;
+ colDescription?: string | undefined;
+ colName?: string | undefined;
+ colType?: string | undefined;
+ role?: string[] | undefined;
+ refersTo?: Reference | undefined;
+}
+
+export class Reference implements IReference {
+ resID?: string | undefined;
+
+ constructor(data?: IReference) {
+ if (data) {
+ for (var property in data) {
+ if (data.hasOwnProperty(property))
+ (<any>this)[property] = (<any>data)[property];
+ }
+ }
+ }
+
+ init(data?: any) {
+ if (data) {
+ this.resID = data["resID"];
+ }
+ }
+
+ static fromJS(data: any): Reference {
+ data = typeof data === 'object' ? data : {};
+ let result = new Reference();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["resID"] = this.resID;
+ return data;
+ }
+}
+
+export interface IReference {
+ resID?: string | undefined;
+}
+
+export class ProblemFinderRows implements IProblemFinderRows {
+ label?: string | undefined;
+ type?: string | undefined;
+ features?: Feature[] | undefined;
+
+ constructor(data?: IProblemFinderRows) {
+ if (data) {
+ for (var property in data) {
+ if (data.hasOwnProperty(property))
+ (<any>this)[property] = (<any>data)[property];
+ }
+ }
+ }
+
+ init(data?: any) {
+ if (data) {
+ this.label = data["label"];
+ this.type = data["type"];
+ if (data["features"] && data["features"].constructor === Array) {
+ this.features = [];
+ for (let item of data["features"])
+ this.features.push(Feature.fromJS(item));
+ }
+ }
+ }
+
+ static fromJS(data: any): ProblemFinderRows {
+ data = typeof data === 'object' ? data : {};
+ let result = new ProblemFinderRows();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["label"] = this.label;
+ data["type"] = this.type;
+ if (this.features && this.features.constructor === Array) {
+ data["features"] = [];
+ for (let item of this.features)
+ data["features"].push(item.toJSON());
+ }
+ return data;
+ }
+}
+
+export interface IProblemFinderRows {
+ label?: string | undefined;
+ type?: string | undefined;
+ features?: Feature[] | undefined;
+}
+
+export class Feature implements IFeature {
+ name?: string | undefined;
+ selected?: boolean | undefined;
+ value?: number | undefined;
+
+ constructor(data?: IFeature) {
+ if (data) {
+ for (var property in data) {
+ if (data.hasOwnProperty(property))
+ (<any>this)[property] = (<any>data)[property];
+ }
+ }
+ }
+
+ init(data?: any) {
+ if (data) {
+ this.name = data["name"];
+ this.selected = data["selected"];
+ this.value = data["value"];
+ }
+ }
+
+ static fromJS(data: any): Feature {
+ data = typeof data === 'object' ? data : {};
+ let result = new Feature();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["name"] = this.name;
+ data["selected"] = this.selected;
+ data["value"] = this.value;
+ return data;
+ }
+}
+
+export interface IFeature {
+ name?: string | undefined;
+ selected?: boolean | undefined;
+ value?: number | undefined;
+}
+
+export enum DataType2 {
+ Int = 0,
+ String = 1,
+ Float = 2,
+ Double = 3,
+ DateTime = 4,
+ Object = 5,
+ Undefined = 6,
+}
+
+export abstract class DataTypeExtensions implements IDataTypeExtensions {
+
+ constructor(data?: IDataTypeExtensions) {
+ if (data) {
+ for (var property in data) {
+ if (data.hasOwnProperty(property))
+ (<any>this)[property] = (<any>data)[property];
+ }
+ }
+ }
+
+ init(data?: any) {
+ if (data) {
+ }
+ }
+
+ static fromJS(data: any): DataTypeExtensions {
+ data = typeof data === 'object' ? data : {};
+ throw new Error("The abstract class 'DataTypeExtensions' cannot be instantiated.");
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ return data;
+ }
+}
+
+export interface IDataTypeExtensions {
+}
+
+export class ResObject implements IResObject {
+ columnName?: string | undefined;
+
+ constructor(data?: IResObject) {
+ if (data) {
+ for (var property in data) {
+ if (data.hasOwnProperty(property))
+ (<any>this)[property] = (<any>data)[property];
+ }
+ }
+ }
+
+ init(data?: any) {
+ if (data) {
+ this.columnName = data["columnName"];
+ }
+ }
+
+ static fromJS(data: any): ResObject {
+ data = typeof data === 'object' ? data : {};
+ let result = new ResObject();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["columnName"] = this.columnName;
+ return data;
+ }
+}
+
+export interface IResObject {
+ columnName?: string | undefined;
+}
+
+export class Exception implements IException {
+ message?: string | undefined;
+ innerException?: Exception | undefined;
+ stackTrace?: string | undefined;
+ source?: string | undefined;
+
+ constructor(data?: IException) {
+ if (data) {
+ for (var property in data) {
+ if (data.hasOwnProperty(property))
+ (<any>this)[property] = (<any>data)[property];
+ }
+ }
+ }
+
+ init(data?: any) {
+ if (data) {
+ this.message = data["Message"];
+ this.innerException = data["InnerException"] ? Exception.fromJS(data["InnerException"]) : <any>undefined;
+ this.stackTrace = data["StackTrace"];
+ this.source = data["Source"];
+ }
+ }
+
+ static fromJS(data: any): Exception {
+ data = typeof data === 'object' ? data : {};
+ let result = new Exception();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["Message"] = this.message;
+ data["InnerException"] = this.innerException ? this.innerException.toJSON() : <any>undefined;
+ data["StackTrace"] = this.stackTrace;
+ data["Source"] = this.source;
+ return data;
+ }
+}
+
+export interface IException {
+ message?: string | undefined;
+ innerException?: Exception | undefined;
+ stackTrace?: string | undefined;
+ source?: string | undefined;
+}
+
+export class IDEAException extends Exception implements IIDEAException {
+
+ constructor(data?: IIDEAException) {
+ super(data);
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ }
+ }
+
+ static fromJS(data: any): IDEAException {
+ data = typeof data === 'object' ? data : {};
+ let result = new IDEAException();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IIDEAException extends IException {
+}
+
+export class CodeParameters implements ICodeParameters {
+ attributeCodeParameters?: AttributeCodeParameters[] | undefined;
+ adapterName?: string | undefined;
+
+ constructor(data?: ICodeParameters) {
+ if (data) {
+ for (var property in data) {
+ if (data.hasOwnProperty(property))
+ (<any>this)[property] = (<any>data)[property];
+ }
+ }
+ }
+
+ init(data?: any) {
+ if (data) {
+ if (data["AttributeCodeParameters"] && data["AttributeCodeParameters"].constructor === Array) {
+ this.attributeCodeParameters = [];
+ for (let item of data["AttributeCodeParameters"])
+ this.attributeCodeParameters.push(AttributeCodeParameters.fromJS(item));
+ }
+ this.adapterName = data["AdapterName"];
+ }
+ }
+
+ static fromJS(data: any): CodeParameters {
+ data = typeof data === 'object' ? data : {};
+ let result = new CodeParameters();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ if (this.attributeCodeParameters && this.attributeCodeParameters.constructor === Array) {
+ data["AttributeCodeParameters"] = [];
+ for (let item of this.attributeCodeParameters)
+ data["AttributeCodeParameters"].push(item.toJSON());
+ }
+ data["AdapterName"] = this.adapterName;
+ return data;
+ }
+}
+
+export interface ICodeParameters {
+ attributeCodeParameters?: AttributeCodeParameters[] | undefined;
+ adapterName?: string | undefined;
+}
+
+export class CompileResult implements ICompileResult {
+ compileSuccess?: boolean | undefined;
+ compileMessage?: string | undefined;
+ dataType?: DataType | undefined;
+ replaceAttributeParameters?: AttributeParameters[] | undefined;
+
+ constructor(data?: ICompileResult) {
+ if (data) {
+ for (var property in data) {
+ if (data.hasOwnProperty(property))
+ (<any>this)[property] = (<any>data)[property];
+ }
+ }
+ }
+
+ init(data?: any) {
+ if (data) {
+ this.compileSuccess = data["CompileSuccess"];
+ this.compileMessage = data["CompileMessage"];
+ this.dataType = data["DataType"];
+ if (data["ReplaceAttributeParameters"] && data["ReplaceAttributeParameters"].constructor === Array) {
+ this.replaceAttributeParameters = [];
+ for (let item of data["ReplaceAttributeParameters"])
+ this.replaceAttributeParameters.push(AttributeParameters.fromJS(item));
+ }
+ }
+ }
+
+ static fromJS(data: any): CompileResult {
+ data = typeof data === 'object' ? data : {};
+ let result = new CompileResult();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["CompileSuccess"] = this.compileSuccess;
+ data["CompileMessage"] = this.compileMessage;
+ data["DataType"] = this.dataType;
+ if (this.replaceAttributeParameters && this.replaceAttributeParameters.constructor === Array) {
+ data["ReplaceAttributeParameters"] = [];
+ for (let item of this.replaceAttributeParameters)
+ data["ReplaceAttributeParameters"].push(item.toJSON());
+ }
+ return data;
+ }
+}
+
+export interface ICompileResult {
+ compileSuccess?: boolean | undefined;
+ compileMessage?: string | undefined;
+ dataType?: DataType | undefined;
+ replaceAttributeParameters?: AttributeParameters[] | undefined;
+}
+
+export class CompileResults implements ICompileResults {
+ rawNameToCompileResult?: { [key: string]: CompileResult; } | undefined;
+
+ constructor(data?: ICompileResults) {
+ if (data) {
+ for (var property in data) {
+ if (data.hasOwnProperty(property))
+ (<any>this)[property] = (<any>data)[property];
+ }
+ }
+ }
+
+ init(data?: any) {
+ if (data) {
+ if (data["RawNameToCompileResult"]) {
+ this.rawNameToCompileResult = {};
+ for (let key in data["RawNameToCompileResult"]) {
+ if (data["RawNameToCompileResult"].hasOwnProperty(key))
+ this.rawNameToCompileResult[key] = data["RawNameToCompileResult"][key] ? CompileResult.fromJS(data["RawNameToCompileResult"][key]) : new CompileResult();
+ }
+ }
+ }
+ }
+
+ static fromJS(data: any): CompileResults {
+ data = typeof data === 'object' ? data : {};
+ let result = new CompileResults();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ if (this.rawNameToCompileResult) {
+ data["RawNameToCompileResult"] = {};
+ for (let key in this.rawNameToCompileResult) {
+ if (this.rawNameToCompileResult.hasOwnProperty(key))
+ data["RawNameToCompileResult"][key] = this.rawNameToCompileResult[key];
+ }
+ }
+ return data;
+ }
+}
+
+export interface ICompileResults {
+ rawNameToCompileResult?: { [key: string]: CompileResult; } | undefined;
+}
+
+export abstract class UniqueJson implements IUniqueJson {
+
+ constructor(data?: IUniqueJson) {
+ if (data) {
+ for (var property in data) {
+ if (data.hasOwnProperty(property))
+ (<any>this)[property] = (<any>data)[property];
+ }
+ }
+ }
+
+ init(data?: any) {
+ if (data) {
+ }
+ }
+
+ static fromJS(data: any): UniqueJson {
+ data = typeof data === 'object' ? data : {};
+ throw new Error("The abstract class 'UniqueJson' cannot be instantiated.");
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ return data;
+ }
+}
+
+export interface IUniqueJson {
+}
+
+export abstract class OperationParameters extends UniqueJson implements IOperationParameters {
+ isCachable?: boolean | undefined;
+
+ protected _discriminator: string;
+
+ constructor(data?: IOperationParameters) {
+ super(data);
+ this._discriminator = "OperationParameters";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ this.isCachable = data["IsCachable"];
+ }
+ }
+
+ static fromJS(data: any): OperationParameters {
+ data = typeof data === 'object' ? data : {};
+ if (data["discriminator"] === "DataOperationParameters") {
+ throw new Error("The abstract class 'DataOperationParameters' cannot be instantiated.");
+ }
+ if (data["discriminator"] === "ExampleOperationParameters") {
+ let result = new ExampleOperationParameters();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "HistogramOperationParameters") {
+ let result = new HistogramOperationParameters();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "DistOperationParameters") {
+ throw new Error("The abstract class 'DistOperationParameters' cannot be instantiated.");
+ }
+ if (data["discriminator"] === "OptimizerOperationParameters") {
+ let result = new OptimizerOperationParameters();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "RawDataOperationParameters") {
+ let result = new RawDataOperationParameters();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "RecommenderOperationParameters") {
+ let result = new RecommenderOperationParameters();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "CDFOperationParameters") {
+ let result = new CDFOperationParameters();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "TestDistOperationParameters") {
+ throw new Error("The abstract class 'TestDistOperationParameters' cannot be instantiated.");
+ }
+ if (data["discriminator"] === "ChiSquaredTestOperationParameters") {
+ let result = new ChiSquaredTestOperationParameters();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "HypothesisTestParameters") {
+ throw new Error("The abstract class 'HypothesisTestParameters' cannot be instantiated.");
+ }
+ if (data["discriminator"] === "CorrelationTestOperationParameters") {
+ let result = new CorrelationTestOperationParameters();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "EmpiricalDistOperationParameters") {
+ let result = new EmpiricalDistOperationParameters();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "KSTestOperationParameters") {
+ let result = new KSTestOperationParameters();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "NewModelOperationParameters") {
+ let result = new NewModelOperationParameters();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "ModelOperationParameters") {
+ throw new Error("The abstract class 'ModelOperationParameters' cannot be instantiated.");
+ }
+ if (data["discriminator"] === "RootMeanSquareTestOperationParameters") {
+ let result = new RootMeanSquareTestOperationParameters();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "TTestOperationParameters") {
+ let result = new TTestOperationParameters();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "FeatureImportanceOperationParameters") {
+ let result = new FeatureImportanceOperationParameters();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "SampleOperationParameters") {
+ let result = new SampleOperationParameters();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "AddComparisonParameters") {
+ let result = new AddComparisonParameters();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "GetModelStateParameters") {
+ let result = new GetModelStateParameters();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "FrequentItemsetOperationParameters") {
+ let result = new FrequentItemsetOperationParameters();
+ result.init(data);
+ return result;
+ }
+ throw new Error("The abstract class 'OperationParameters' cannot be instantiated.");
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["discriminator"] = this._discriminator;
+ data["IsCachable"] = this.isCachable;
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IOperationParameters extends IUniqueJson {
+ isCachable?: boolean | undefined;
+}
+
+export abstract class DataOperationParameters extends OperationParameters implements IDataOperationParameters {
+ sampleStreamBlockSize?: number | undefined;
+ adapterName?: string | undefined;
+ isCachable?: boolean | undefined;
+
+ constructor(data?: IDataOperationParameters) {
+ super(data);
+ this._discriminator = "DataOperationParameters";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ this.sampleStreamBlockSize = data["SampleStreamBlockSize"];
+ this.adapterName = data["AdapterName"];
+ this.isCachable = data["IsCachable"];
+ }
+ }
+
+ static fromJS(data: any): DataOperationParameters {
+ data = typeof data === 'object' ? data : {};
+ if (data["discriminator"] === "ExampleOperationParameters") {
+ let result = new ExampleOperationParameters();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "HistogramOperationParameters") {
+ let result = new HistogramOperationParameters();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "DistOperationParameters") {
+ throw new Error("The abstract class 'DistOperationParameters' cannot be instantiated.");
+ }
+ if (data["discriminator"] === "OptimizerOperationParameters") {
+ let result = new OptimizerOperationParameters();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "RawDataOperationParameters") {
+ let result = new RawDataOperationParameters();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "RecommenderOperationParameters") {
+ let result = new RecommenderOperationParameters();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "CDFOperationParameters") {
+ let result = new CDFOperationParameters();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "TestDistOperationParameters") {
+ throw new Error("The abstract class 'TestDistOperationParameters' cannot be instantiated.");
+ }
+ if (data["discriminator"] === "EmpiricalDistOperationParameters") {
+ let result = new EmpiricalDistOperationParameters();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "FeatureImportanceOperationParameters") {
+ let result = new FeatureImportanceOperationParameters();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "SampleOperationParameters") {
+ let result = new SampleOperationParameters();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "FrequentItemsetOperationParameters") {
+ let result = new FrequentItemsetOperationParameters();
+ result.init(data);
+ return result;
+ }
+ throw new Error("The abstract class 'DataOperationParameters' cannot be instantiated.");
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["SampleStreamBlockSize"] = this.sampleStreamBlockSize;
+ data["AdapterName"] = this.adapterName;
+ data["IsCachable"] = this.isCachable;
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IDataOperationParameters extends IOperationParameters {
+ sampleStreamBlockSize?: number | undefined;
+ adapterName?: string | undefined;
+ isCachable?: boolean | undefined;
+}
+
+export class ExampleOperationParameters extends DataOperationParameters implements IExampleOperationParameters {
+ filter?: string | undefined;
+ attributeParameters?: AttributeParameters[] | undefined;
+ attributeCodeParameters?: AttributeCaclculatedParameters[] | undefined;
+ dummyValue?: number | undefined;
+ exampleType?: string | undefined;
+
+ constructor(data?: IExampleOperationParameters) {
+ super(data);
+ this._discriminator = "ExampleOperationParameters";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ this.filter = data["Filter"];
+ if (data["AttributeParameters"] && data["AttributeParameters"].constructor === Array) {
+ this.attributeParameters = [];
+ for (let item of data["AttributeParameters"])
+ this.attributeParameters.push(AttributeParameters.fromJS(item));
+ }
+ if (data["AttributeCodeParameters"] && data["AttributeCodeParameters"].constructor === Array) {
+ this.attributeCodeParameters = [];
+ for (let item of data["AttributeCodeParameters"])
+ this.attributeCodeParameters.push(AttributeCaclculatedParameters.fromJS(item));
+ }
+ this.dummyValue = data["DummyValue"];
+ this.exampleType = data["ExampleType"];
+ }
+ }
+
+ static fromJS(data: any): ExampleOperationParameters {
+ data = typeof data === 'object' ? data : {};
+ let result = new ExampleOperationParameters();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["Filter"] = this.filter;
+ if (this.attributeParameters && this.attributeParameters.constructor === Array) {
+ data["AttributeParameters"] = [];
+ for (let item of this.attributeParameters)
+ data["AttributeParameters"].push(item.toJSON());
+ }
+ if (this.attributeCodeParameters && this.attributeCodeParameters.constructor === Array) {
+ data["AttributeCodeParameters"] = [];
+ for (let item of this.attributeCodeParameters)
+ data["AttributeCodeParameters"].push(item.toJSON());
+ }
+ data["DummyValue"] = this.dummyValue;
+ data["ExampleType"] = this.exampleType;
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IExampleOperationParameters extends IDataOperationParameters {
+ filter?: string | undefined;
+ attributeParameters?: AttributeParameters[] | undefined;
+ attributeCodeParameters?: AttributeCaclculatedParameters[] | undefined;
+ dummyValue?: number | undefined;
+ exampleType?: string | undefined;
+}
+
+export abstract class DistOperationParameters extends DataOperationParameters implements IDistOperationParameters {
+ filter?: string | undefined;
+ attributeCalculatedParameters?: AttributeCaclculatedParameters[] | undefined;
+
+ constructor(data?: IDistOperationParameters) {
+ super(data);
+ this._discriminator = "DistOperationParameters";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ this.filter = data["Filter"];
+ if (data["AttributeCalculatedParameters"] && data["AttributeCalculatedParameters"].constructor === Array) {
+ this.attributeCalculatedParameters = [];
+ for (let item of data["AttributeCalculatedParameters"])
+ this.attributeCalculatedParameters.push(AttributeCaclculatedParameters.fromJS(item));
+ }
+ }
+ }
+
+ static fromJS(data: any): DistOperationParameters {
+ data = typeof data === 'object' ? data : {};
+ if (data["discriminator"] === "HistogramOperationParameters") {
+ let result = new HistogramOperationParameters();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "OptimizerOperationParameters") {
+ let result = new OptimizerOperationParameters();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "RawDataOperationParameters") {
+ let result = new RawDataOperationParameters();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "CDFOperationParameters") {
+ let result = new CDFOperationParameters();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "TestDistOperationParameters") {
+ throw new Error("The abstract class 'TestDistOperationParameters' cannot be instantiated.");
+ }
+ if (data["discriminator"] === "EmpiricalDistOperationParameters") {
+ let result = new EmpiricalDistOperationParameters();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "FeatureImportanceOperationParameters") {
+ let result = new FeatureImportanceOperationParameters();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "SampleOperationParameters") {
+ let result = new SampleOperationParameters();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "FrequentItemsetOperationParameters") {
+ let result = new FrequentItemsetOperationParameters();
+ result.init(data);
+ return result;
+ }
+ throw new Error("The abstract class 'DistOperationParameters' cannot be instantiated.");
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["Filter"] = this.filter;
+ if (this.attributeCalculatedParameters && this.attributeCalculatedParameters.constructor === Array) {
+ data["AttributeCalculatedParameters"] = [];
+ for (let item of this.attributeCalculatedParameters)
+ data["AttributeCalculatedParameters"].push(item.toJSON());
+ }
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IDistOperationParameters extends IDataOperationParameters {
+ filter?: string | undefined;
+ attributeCalculatedParameters?: AttributeCaclculatedParameters[] | undefined;
+}
+
+export class HistogramOperationParameters extends DistOperationParameters implements IHistogramOperationParameters {
+ sortPerBinAggregateParameter?: AggregateParameters | undefined;
+ binningParameters?: BinningParameters[] | undefined;
+ perBinAggregateParameters?: AggregateParameters[] | undefined;
+ globalAggregateParameters?: AggregateParameters[] | undefined;
+ brushes?: string[] | undefined;
+ enableBrushComputation?: boolean | undefined;
+ degreeOfParallism?: number | undefined;
+
+ constructor(data?: IHistogramOperationParameters) {
+ super(data);
+ this._discriminator = "HistogramOperationParameters";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ this.sortPerBinAggregateParameter = data["SortPerBinAggregateParameter"] ? AggregateParameters.fromJS(data["SortPerBinAggregateParameter"]) : <any>undefined;
+ if (data["BinningParameters"] && data["BinningParameters"].constructor === Array) {
+ this.binningParameters = [];
+ for (let item of data["BinningParameters"])
+ this.binningParameters.push(BinningParameters.fromJS(item));
+ }
+ if (data["PerBinAggregateParameters"] && data["PerBinAggregateParameters"].constructor === Array) {
+ this.perBinAggregateParameters = [];
+ for (let item of data["PerBinAggregateParameters"])
+ this.perBinAggregateParameters.push(AggregateParameters.fromJS(item));
+ }
+ if (data["GlobalAggregateParameters"] && data["GlobalAggregateParameters"].constructor === Array) {
+ this.globalAggregateParameters = [];
+ for (let item of data["GlobalAggregateParameters"])
+ this.globalAggregateParameters.push(AggregateParameters.fromJS(item));
+ }
+ if (data["Brushes"] && data["Brushes"].constructor === Array) {
+ this.brushes = [];
+ for (let item of data["Brushes"])
+ this.brushes.push(item);
+ }
+ this.enableBrushComputation = data["EnableBrushComputation"];
+ this.degreeOfParallism = data["DegreeOfParallism"];
+ }
+ }
+
+ static fromJS(data: any): HistogramOperationParameters {
+ data = typeof data === 'object' ? data : {};
+ let result = new HistogramOperationParameters();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["SortPerBinAggregateParameter"] = this.sortPerBinAggregateParameter ? this.sortPerBinAggregateParameter.toJSON() : <any>undefined;
+ if (this.binningParameters && this.binningParameters.constructor === Array) {
+ data["BinningParameters"] = [];
+ for (let item of this.binningParameters)
+ data["BinningParameters"].push(item.toJSON());
+ }
+ if (this.perBinAggregateParameters && this.perBinAggregateParameters.constructor === Array) {
+ data["PerBinAggregateParameters"] = [];
+ for (let item of this.perBinAggregateParameters)
+ data["PerBinAggregateParameters"].push(item.toJSON());
+ }
+ if (this.globalAggregateParameters && this.globalAggregateParameters.constructor === Array) {
+ data["GlobalAggregateParameters"] = [];
+ for (let item of this.globalAggregateParameters)
+ data["GlobalAggregateParameters"].push(item.toJSON());
+ }
+ if (this.brushes && this.brushes.constructor === Array) {
+ data["Brushes"] = [];
+ for (let item of this.brushes)
+ data["Brushes"].push(item);
+ }
+ data["EnableBrushComputation"] = this.enableBrushComputation;
+ data["DegreeOfParallism"] = this.degreeOfParallism;
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IHistogramOperationParameters extends IDistOperationParameters {
+ sortPerBinAggregateParameter?: AggregateParameters | undefined;
+ binningParameters?: BinningParameters[] | undefined;
+ perBinAggregateParameters?: AggregateParameters[] | undefined;
+ globalAggregateParameters?: AggregateParameters[] | undefined;
+ brushes?: string[] | undefined;
+ enableBrushComputation?: boolean | undefined;
+ degreeOfParallism?: number | undefined;
+}
+
+export class OptimizerOperationParameters extends DistOperationParameters implements IOptimizerOperationParameters {
+ taskType?: TaskType | undefined;
+ taskSubType?: TaskSubType | undefined;
+ metricType?: MetricType | undefined;
+ labelAttribute?: AttributeParameters | undefined;
+ featureAttributes?: AttributeParameters[] | undefined;
+ requiredPrimitives?: string[] | undefined;
+ pythonFilter?: string | undefined;
+ trainFilter?: string | undefined;
+ pythonTrainFilter?: string | undefined;
+ testFilter?: string | undefined;
+ pythonTestFilter?: string | undefined;
+
+ constructor(data?: IOptimizerOperationParameters) {
+ super(data);
+ this._discriminator = "OptimizerOperationParameters";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ this.taskType = data["TaskType"];
+ this.taskSubType = data["TaskSubType"];
+ this.metricType = data["MetricType"];
+ this.labelAttribute = data["LabelAttribute"] ? AttributeParameters.fromJS(data["LabelAttribute"]) : <any>undefined;
+ if (data["FeatureAttributes"] && data["FeatureAttributes"].constructor === Array) {
+ this.featureAttributes = [];
+ for (let item of data["FeatureAttributes"])
+ this.featureAttributes.push(AttributeParameters.fromJS(item));
+ }
+ if (data["RequiredPrimitives"] && data["RequiredPrimitives"].constructor === Array) {
+ this.requiredPrimitives = [];
+ for (let item of data["RequiredPrimitives"])
+ this.requiredPrimitives.push(item);
+ }
+ this.pythonFilter = data["PythonFilter"];
+ this.trainFilter = data["TrainFilter"];
+ this.pythonTrainFilter = data["PythonTrainFilter"];
+ this.testFilter = data["TestFilter"];
+ this.pythonTestFilter = data["PythonTestFilter"];
+ }
+ }
+
+ static fromJS(data: any): OptimizerOperationParameters {
+ data = typeof data === 'object' ? data : {};
+ let result = new OptimizerOperationParameters();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["TaskType"] = this.taskType;
+ data["TaskSubType"] = this.taskSubType;
+ data["MetricType"] = this.metricType;
+ data["LabelAttribute"] = this.labelAttribute ? this.labelAttribute.toJSON() : <any>undefined;
+ if (this.featureAttributes && this.featureAttributes.constructor === Array) {
+ data["FeatureAttributes"] = [];
+ for (let item of this.featureAttributes)
+ data["FeatureAttributes"].push(item.toJSON());
+ }
+ if (this.requiredPrimitives && this.requiredPrimitives.constructor === Array) {
+ data["RequiredPrimitives"] = [];
+ for (let item of this.requiredPrimitives)
+ data["RequiredPrimitives"].push(item);
+ }
+ data["PythonFilter"] = this.pythonFilter;
+ data["TrainFilter"] = this.trainFilter;
+ data["PythonTrainFilter"] = this.pythonTrainFilter;
+ data["TestFilter"] = this.testFilter;
+ data["PythonTestFilter"] = this.pythonTestFilter;
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IOptimizerOperationParameters extends IDistOperationParameters {
+ taskType?: TaskType | undefined;
+ taskSubType?: TaskSubType | undefined;
+ metricType?: MetricType | undefined;
+ labelAttribute?: AttributeParameters | undefined;
+ featureAttributes?: AttributeParameters[] | undefined;
+ requiredPrimitives?: string[] | undefined;
+ pythonFilter?: string | undefined;
+ trainFilter?: string | undefined;
+ pythonTrainFilter?: string | undefined;
+ testFilter?: string | undefined;
+ pythonTestFilter?: string | undefined;
+}
+
+export enum TaskType {
+ Undefined = 0,
+ Classification = 1,
+ Regression = 2,
+ Clustering = 3,
+ LinkPrediction = 4,
+ VertexNomination = 5,
+ CommunityDetection = 6,
+ GraphClustering = 7,
+ GraphMatching = 8,
+ TimeSeriesForecasting = 9,
+ CollaborativeFiltering = 10,
+}
+
+export enum TaskSubType {
+ Undefined = 0,
+ None = 1,
+ Binary = 2,
+ Multiclass = 3,
+ Multilabel = 4,
+ Univariate = 5,
+ Multivariate = 6,
+ Overlapping = 7,
+ Nonoverlapping = 8,
+}
+
+export enum MetricType {
+ MetricUndefined = 0,
+ Accuracy = 1,
+ Precision = 2,
+ Recall = 3,
+ F1 = 4,
+ F1Micro = 5,
+ F1Macro = 6,
+ RocAuc = 7,
+ RocAucMicro = 8,
+ RocAucMacro = 9,
+ MeanSquaredError = 10,
+ RootMeanSquaredError = 11,
+ RootMeanSquaredErrorAvg = 12,
+ MeanAbsoluteError = 13,
+ RSquared = 14,
+ NormalizedMutualInformation = 15,
+ JaccardSimilarityScore = 16,
+ PrecisionAtTopK = 17,
+ ObjectDetectionAveragePrecision = 18,
+ Loss = 100,
+}
+
+export class RawDataOperationParameters extends DistOperationParameters implements IRawDataOperationParameters {
+ sortUpRawName?: string | undefined;
+ sortDownRawName?: string | undefined;
+ numRecords?: number | undefined;
+ binningParameters?: BinningParameters[] | undefined;
+ brushes?: string[] | undefined;
+ enableBrushComputation?: boolean | undefined;
+
+ constructor(data?: IRawDataOperationParameters) {
+ super(data);
+ this._discriminator = "RawDataOperationParameters";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ this.sortUpRawName = data["SortUpRawName"];
+ this.sortDownRawName = data["SortDownRawName"];
+ this.numRecords = data["NumRecords"];
+ if (data["BinningParameters"] && data["BinningParameters"].constructor === Array) {
+ this.binningParameters = [];
+ for (let item of data["BinningParameters"])
+ this.binningParameters.push(BinningParameters.fromJS(item));
+ }
+ if (data["Brushes"] && data["Brushes"].constructor === Array) {
+ this.brushes = [];
+ for (let item of data["Brushes"])
+ this.brushes.push(item);
+ }
+ this.enableBrushComputation = data["EnableBrushComputation"];
+ }
+ }
+
+ static fromJS(data: any): RawDataOperationParameters {
+ data = typeof data === 'object' ? data : {};
+ let result = new RawDataOperationParameters();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["SortUpRawName"] = this.sortUpRawName;
+ data["SortDownRawName"] = this.sortDownRawName;
+ data["NumRecords"] = this.numRecords;
+ if (this.binningParameters && this.binningParameters.constructor === Array) {
+ data["BinningParameters"] = [];
+ for (let item of this.binningParameters)
+ data["BinningParameters"].push(item.toJSON());
+ }
+ if (this.brushes && this.brushes.constructor === Array) {
+ data["Brushes"] = [];
+ for (let item of this.brushes)
+ data["Brushes"].push(item);
+ }
+ data["EnableBrushComputation"] = this.enableBrushComputation;
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IRawDataOperationParameters extends IDistOperationParameters {
+ sortUpRawName?: string | undefined;
+ sortDownRawName?: string | undefined;
+ numRecords?: number | undefined;
+ binningParameters?: BinningParameters[] | undefined;
+ brushes?: string[] | undefined;
+ enableBrushComputation?: boolean | undefined;
+}
+
+export class RecommenderOperationParameters extends DataOperationParameters implements IRecommenderOperationParameters {
+ target?: HistogramOperationParameters | undefined;
+ budget?: number | undefined;
+ modelId?: ModelId | undefined;
+ includeAttributeParameters?: AttributeParameters[] | undefined;
+ excludeAttributeParameters?: AttributeParameters[] | undefined;
+ riskControlType?: RiskControlType | undefined;
+
+ constructor(data?: IRecommenderOperationParameters) {
+ super(data);
+ this._discriminator = "RecommenderOperationParameters";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ this.target = data["Target"] ? HistogramOperationParameters.fromJS(data["Target"]) : <any>undefined;
+ this.budget = data["Budget"];
+ this.modelId = data["ModelId"] ? ModelId.fromJS(data["ModelId"]) : <any>undefined;
+ if (data["IncludeAttributeParameters"] && data["IncludeAttributeParameters"].constructor === Array) {
+ this.includeAttributeParameters = [];
+ for (let item of data["IncludeAttributeParameters"])
+ this.includeAttributeParameters.push(AttributeParameters.fromJS(item));
+ }
+ if (data["ExcludeAttributeParameters"] && data["ExcludeAttributeParameters"].constructor === Array) {
+ this.excludeAttributeParameters = [];
+ for (let item of data["ExcludeAttributeParameters"])
+ this.excludeAttributeParameters.push(AttributeParameters.fromJS(item));
+ }
+ this.riskControlType = data["RiskControlType"];
+ }
+ }
+
+ static fromJS(data: any): RecommenderOperationParameters {
+ data = typeof data === 'object' ? data : {};
+ let result = new RecommenderOperationParameters();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["Target"] = this.target ? this.target.toJSON() : <any>undefined;
+ data["Budget"] = this.budget;
+ data["ModelId"] = this.modelId ? this.modelId.toJSON() : <any>undefined;
+ if (this.includeAttributeParameters && this.includeAttributeParameters.constructor === Array) {
+ data["IncludeAttributeParameters"] = [];
+ for (let item of this.includeAttributeParameters)
+ data["IncludeAttributeParameters"].push(item.toJSON());
+ }
+ if (this.excludeAttributeParameters && this.excludeAttributeParameters.constructor === Array) {
+ data["ExcludeAttributeParameters"] = [];
+ for (let item of this.excludeAttributeParameters)
+ data["ExcludeAttributeParameters"].push(item.toJSON());
+ }
+ data["RiskControlType"] = this.riskControlType;
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IRecommenderOperationParameters extends IDataOperationParameters {
+ target?: HistogramOperationParameters | undefined;
+ budget?: number | undefined;
+ modelId?: ModelId | undefined;
+ includeAttributeParameters?: AttributeParameters[] | undefined;
+ excludeAttributeParameters?: AttributeParameters[] | undefined;
+ riskControlType?: RiskControlType | undefined;
+}
+
+export class ModelId implements IModelId {
+ value?: string | undefined;
+
+ constructor(data?: IModelId) {
+ if (data) {
+ for (var property in data) {
+ if (data.hasOwnProperty(property))
+ (<any>this)[property] = (<any>data)[property];
+ }
+ }
+ }
+
+ init(data?: any) {
+ if (data) {
+ this.value = data["Value"];
+ }
+ }
+
+ static fromJS(data: any): ModelId {
+ data = typeof data === 'object' ? data : {};
+ let result = new ModelId();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["Value"] = this.value;
+ return data;
+ }
+}
+
+export interface IModelId {
+ value?: string | undefined;
+}
+
+export enum RiskControlType {
+ PCER = 0,
+ Bonferroni = 1,
+ AdaBonferroni = 2,
+ HolmBonferroni = 3,
+ BHFDR = 4,
+ SeqFDR = 5,
+ AlphaFDR = 6,
+ BestFootForward = 7,
+ BetaFarsighted = 8,
+ BetaFarsightedWithSupport = 9,
+ GammaFixed = 10,
+ DeltaHopeful = 11,
+ EpsilonHybrid = 12,
+ EpsilonHybridWithoutSupport = 13,
+ PsiSupport = 14,
+ ZetaDynamic = 15,
+ Unknown = 16,
+}
+
+export abstract class TestDistOperationParameters extends DistOperationParameters implements ITestDistOperationParameters {
+ attributeParameters?: AttributeParameters[] | undefined;
+
+ constructor(data?: ITestDistOperationParameters) {
+ super(data);
+ this._discriminator = "TestDistOperationParameters";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ if (data["AttributeParameters"] && data["AttributeParameters"].constructor === Array) {
+ this.attributeParameters = [];
+ for (let item of data["AttributeParameters"])
+ this.attributeParameters.push(AttributeParameters.fromJS(item));
+ }
+ }
+ }
+
+ static fromJS(data: any): TestDistOperationParameters {
+ data = typeof data === 'object' ? data : {};
+ if (data["discriminator"] === "CDFOperationParameters") {
+ let result = new CDFOperationParameters();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "EmpiricalDistOperationParameters") {
+ let result = new EmpiricalDistOperationParameters();
+ result.init(data);
+ return result;
+ }
+ throw new Error("The abstract class 'TestDistOperationParameters' cannot be instantiated.");
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ if (this.attributeParameters && this.attributeParameters.constructor === Array) {
+ data["AttributeParameters"] = [];
+ for (let item of this.attributeParameters)
+ data["AttributeParameters"].push(item.toJSON());
+ }
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface ITestDistOperationParameters extends IDistOperationParameters {
+ attributeParameters?: AttributeParameters[] | undefined;
+}
+
+export class CDFOperationParameters extends TestDistOperationParameters implements ICDFOperationParameters {
+
+ constructor(data?: ICDFOperationParameters) {
+ super(data);
+ this._discriminator = "CDFOperationParameters";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ }
+ }
+
+ static fromJS(data: any): CDFOperationParameters {
+ data = typeof data === 'object' ? data : {};
+ let result = new CDFOperationParameters();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface ICDFOperationParameters extends ITestDistOperationParameters {
+}
+
+export abstract class HypothesisTestParameters extends OperationParameters implements IHypothesisTestParameters {
+ childOperationParameters?: OperationParameters[] | undefined;
+ isCachable?: boolean | undefined;
+
+ constructor(data?: IHypothesisTestParameters) {
+ super(data);
+ this._discriminator = "HypothesisTestParameters";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ if (data["ChildOperationParameters"] && data["ChildOperationParameters"].constructor === Array) {
+ this.childOperationParameters = [];
+ for (let item of data["ChildOperationParameters"])
+ this.childOperationParameters.push(OperationParameters.fromJS(item));
+ }
+ this.isCachable = data["IsCachable"];
+ }
+ }
+
+ static fromJS(data: any): HypothesisTestParameters {
+ data = typeof data === 'object' ? data : {};
+ if (data["discriminator"] === "ChiSquaredTestOperationParameters") {
+ let result = new ChiSquaredTestOperationParameters();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "CorrelationTestOperationParameters") {
+ let result = new CorrelationTestOperationParameters();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "KSTestOperationParameters") {
+ let result = new KSTestOperationParameters();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "RootMeanSquareTestOperationParameters") {
+ let result = new RootMeanSquareTestOperationParameters();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "TTestOperationParameters") {
+ let result = new TTestOperationParameters();
+ result.init(data);
+ return result;
+ }
+ throw new Error("The abstract class 'HypothesisTestParameters' cannot be instantiated.");
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ if (this.childOperationParameters && this.childOperationParameters.constructor === Array) {
+ data["ChildOperationParameters"] = [];
+ for (let item of this.childOperationParameters)
+ data["ChildOperationParameters"].push(item.toJSON());
+ }
+ data["IsCachable"] = this.isCachable;
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IHypothesisTestParameters extends IOperationParameters {
+ childOperationParameters?: OperationParameters[] | undefined;
+ isCachable?: boolean | undefined;
+}
+
+export class ChiSquaredTestOperationParameters extends HypothesisTestParameters implements IChiSquaredTestOperationParameters {
+
+ constructor(data?: IChiSquaredTestOperationParameters) {
+ super(data);
+ this._discriminator = "ChiSquaredTestOperationParameters";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ }
+ }
+
+ static fromJS(data: any): ChiSquaredTestOperationParameters {
+ data = typeof data === 'object' ? data : {};
+ let result = new ChiSquaredTestOperationParameters();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IChiSquaredTestOperationParameters extends IHypothesisTestParameters {
+}
+
+export class CorrelationTestOperationParameters extends HypothesisTestParameters implements ICorrelationTestOperationParameters {
+
+ constructor(data?: ICorrelationTestOperationParameters) {
+ super(data);
+ this._discriminator = "CorrelationTestOperationParameters";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ }
+ }
+
+ static fromJS(data: any): CorrelationTestOperationParameters {
+ data = typeof data === 'object' ? data : {};
+ let result = new CorrelationTestOperationParameters();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface ICorrelationTestOperationParameters extends IHypothesisTestParameters {
+}
+
+export class SubmitProblemParameters implements ISubmitProblemParameters {
+ id?: string | undefined;
+
+ constructor(data?: ISubmitProblemParameters) {
+ if (data) {
+ for (var property in data) {
+ if (data.hasOwnProperty(property))
+ (<any>this)[property] = (<any>data)[property];
+ }
+ }
+ }
+
+ init(data?: any) {
+ if (data) {
+ this.id = data["Id"];
+ }
+ }
+
+ static fromJS(data: any): SubmitProblemParameters {
+ data = typeof data === 'object' ? data : {};
+ let result = new SubmitProblemParameters();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["Id"] = this.id;
+ return data;
+ }
+}
+
+export interface ISubmitProblemParameters {
+ id?: string | undefined;
+}
+
+export class SpecifyProblemParameters implements ISpecifyProblemParameters {
+ id?: string | undefined;
+ userComment?: string | undefined;
+ optimizerOperationParameters?: OptimizerOperationParameters | undefined;
+
+ constructor(data?: ISpecifyProblemParameters) {
+ if (data) {
+ for (var property in data) {
+ if (data.hasOwnProperty(property))
+ (<any>this)[property] = (<any>data)[property];
+ }
+ }
+ }
+
+ init(data?: any) {
+ if (data) {
+ this.id = data["Id"];
+ this.userComment = data["UserComment"];
+ this.optimizerOperationParameters = data["OptimizerOperationParameters"] ? OptimizerOperationParameters.fromJS(data["OptimizerOperationParameters"]) : <any>undefined;
+ }
+ }
+
+ static fromJS(data: any): SpecifyProblemParameters {
+ data = typeof data === 'object' ? data : {};
+ let result = new SpecifyProblemParameters();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["Id"] = this.id;
+ data["UserComment"] = this.userComment;
+ data["OptimizerOperationParameters"] = this.optimizerOperationParameters ? this.optimizerOperationParameters.toJSON() : <any>undefined;
+ return data;
+ }
+}
+
+export interface ISpecifyProblemParameters {
+ id?: string | undefined;
+ userComment?: string | undefined;
+ optimizerOperationParameters?: OptimizerOperationParameters | undefined;
+}
+
+export class EmpiricalDistOperationParameters extends TestDistOperationParameters implements IEmpiricalDistOperationParameters {
+ keepSamples?: boolean | undefined;
+
+ constructor(data?: IEmpiricalDistOperationParameters) {
+ super(data);
+ this._discriminator = "EmpiricalDistOperationParameters";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ this.keepSamples = data["KeepSamples"];
+ }
+ }
+
+ static fromJS(data: any): EmpiricalDistOperationParameters {
+ data = typeof data === 'object' ? data : {};
+ let result = new EmpiricalDistOperationParameters();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["KeepSamples"] = this.keepSamples;
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IEmpiricalDistOperationParameters extends ITestDistOperationParameters {
+ keepSamples?: boolean | undefined;
+}
+
+export class KSTestOperationParameters extends HypothesisTestParameters implements IKSTestOperationParameters {
+
+ constructor(data?: IKSTestOperationParameters) {
+ super(data);
+ this._discriminator = "KSTestOperationParameters";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ }
+ }
+
+ static fromJS(data: any): KSTestOperationParameters {
+ data = typeof data === 'object' ? data : {};
+ let result = new KSTestOperationParameters();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IKSTestOperationParameters extends IHypothesisTestParameters {
+}
+
+export abstract class ModelOperationParameters extends OperationParameters implements IModelOperationParameters {
+
+ constructor(data?: IModelOperationParameters) {
+ super(data);
+ this._discriminator = "ModelOperationParameters";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ }
+ }
+
+ static fromJS(data: any): ModelOperationParameters {
+ data = typeof data === 'object' ? data : {};
+ if (data["discriminator"] === "NewModelOperationParameters") {
+ let result = new NewModelOperationParameters();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "AddComparisonParameters") {
+ let result = new AddComparisonParameters();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "GetModelStateParameters") {
+ let result = new GetModelStateParameters();
+ result.init(data);
+ return result;
+ }
+ throw new Error("The abstract class 'ModelOperationParameters' cannot be instantiated.");
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IModelOperationParameters extends IOperationParameters {
+}
+
+export class NewModelOperationParameters extends ModelOperationParameters implements INewModelOperationParameters {
+ riskControlTypes?: RiskControlType[] | undefined;
+ alpha?: number | undefined;
+ alphaInvestParameter?: AlphaInvestParameter | undefined;
+
+ constructor(data?: INewModelOperationParameters) {
+ super(data);
+ this._discriminator = "NewModelOperationParameters";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ if (data["RiskControlTypes"] && data["RiskControlTypes"].constructor === Array) {
+ this.riskControlTypes = [];
+ for (let item of data["RiskControlTypes"])
+ this.riskControlTypes.push(item);
+ }
+ this.alpha = data["Alpha"];
+ this.alphaInvestParameter = data["AlphaInvestParameter"] ? AlphaInvestParameter.fromJS(data["AlphaInvestParameter"]) : <any>undefined;
+ }
+ }
+
+ static fromJS(data: any): NewModelOperationParameters {
+ data = typeof data === 'object' ? data : {};
+ let result = new NewModelOperationParameters();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ if (this.riskControlTypes && this.riskControlTypes.constructor === Array) {
+ data["RiskControlTypes"] = [];
+ for (let item of this.riskControlTypes)
+ data["RiskControlTypes"].push(item);
+ }
+ data["Alpha"] = this.alpha;
+ data["AlphaInvestParameter"] = this.alphaInvestParameter ? this.alphaInvestParameter.toJSON() : <any>undefined;
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface INewModelOperationParameters extends IModelOperationParameters {
+ riskControlTypes?: RiskControlType[] | undefined;
+ alpha?: number | undefined;
+ alphaInvestParameter?: AlphaInvestParameter | undefined;
+}
+
+export class AlphaInvestParameter extends UniqueJson implements IAlphaInvestParameter {
+ beta?: number | undefined;
+ gamma?: number | undefined;
+ delta?: number | undefined;
+ epsilon?: number | undefined;
+ windowSize?: number | undefined;
+ psi?: number | undefined;
+
+ constructor(data?: IAlphaInvestParameter) {
+ super(data);
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ this.beta = data["Beta"];
+ this.gamma = data["Gamma"];
+ this.delta = data["Delta"];
+ this.epsilon = data["Epsilon"];
+ this.windowSize = data["WindowSize"];
+ this.psi = data["Psi"];
+ }
+ }
+
+ static fromJS(data: any): AlphaInvestParameter {
+ data = typeof data === 'object' ? data : {};
+ let result = new AlphaInvestParameter();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["Beta"] = this.beta;
+ data["Gamma"] = this.gamma;
+ data["Delta"] = this.delta;
+ data["Epsilon"] = this.epsilon;
+ data["WindowSize"] = this.windowSize;
+ data["Psi"] = this.psi;
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IAlphaInvestParameter extends IUniqueJson {
+ beta?: number | undefined;
+ gamma?: number | undefined;
+ delta?: number | undefined;
+ epsilon?: number | undefined;
+ windowSize?: number | undefined;
+ psi?: number | undefined;
+}
+
+export class RootMeanSquareTestOperationParameters extends HypothesisTestParameters implements IRootMeanSquareTestOperationParameters {
+ seeded?: boolean | undefined;
+ pValueConvergenceThreshold?: number | undefined;
+ maxSimulationBatchCount?: number | undefined;
+ perBatchSimulationCount?: number | undefined;
+
+ constructor(data?: IRootMeanSquareTestOperationParameters) {
+ super(data);
+ this._discriminator = "RootMeanSquareTestOperationParameters";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ this.seeded = data["Seeded"];
+ this.pValueConvergenceThreshold = data["PValueConvergenceThreshold"];
+ this.maxSimulationBatchCount = data["MaxSimulationBatchCount"];
+ this.perBatchSimulationCount = data["PerBatchSimulationCount"];
+ }
+ }
+
+ static fromJS(data: any): RootMeanSquareTestOperationParameters {
+ data = typeof data === 'object' ? data : {};
+ let result = new RootMeanSquareTestOperationParameters();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["Seeded"] = this.seeded;
+ data["PValueConvergenceThreshold"] = this.pValueConvergenceThreshold;
+ data["MaxSimulationBatchCount"] = this.maxSimulationBatchCount;
+ data["PerBatchSimulationCount"] = this.perBatchSimulationCount;
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IRootMeanSquareTestOperationParameters extends IHypothesisTestParameters {
+ seeded?: boolean | undefined;
+ pValueConvergenceThreshold?: number | undefined;
+ maxSimulationBatchCount?: number | undefined;
+ perBatchSimulationCount?: number | undefined;
+}
+
+export class TTestOperationParameters extends HypothesisTestParameters implements ITTestOperationParameters {
+
+ constructor(data?: ITTestOperationParameters) {
+ super(data);
+ this._discriminator = "TTestOperationParameters";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ }
+ }
+
+ static fromJS(data: any): TTestOperationParameters {
+ data = typeof data === 'object' ? data : {};
+ let result = new TTestOperationParameters();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface ITTestOperationParameters extends IHypothesisTestParameters {
+}
+
+export enum EffectSize {
+ Small = 1,
+ Meduim = 2,
+ Large = 4,
+}
+
+export abstract class Result extends UniqueJson implements IResult {
+ progress?: number | undefined;
+
+ protected _discriminator: string;
+
+ constructor(data?: IResult) {
+ super(data);
+ this._discriminator = "Result";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ this.progress = data["Progress"];
+ }
+ }
+
+ static fromJS(data: any): Result {
+ data = typeof data === 'object' ? data : {};
+ if (data["discriminator"] === "ErrorResult") {
+ let result = new ErrorResult();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "HistogramResult") {
+ let result = new HistogramResult();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "DistResult") {
+ throw new Error("The abstract class 'DistResult' cannot be instantiated.");
+ }
+ if (data["discriminator"] === "ModelWealthResult") {
+ let result = new ModelWealthResult();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "HypothesisTestResult") {
+ throw new Error("The abstract class 'HypothesisTestResult' cannot be instantiated.");
+ }
+ if (data["discriminator"] === "ModelOperationResult") {
+ throw new Error("The abstract class 'ModelOperationResult' cannot be instantiated.");
+ }
+ if (data["discriminator"] === "RecommenderResult") {
+ let result = new RecommenderResult();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "Decision") {
+ let result = new Decision();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "OptimizerResult") {
+ let result = new OptimizerResult();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "ExampleResult") {
+ let result = new ExampleResult();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "NewModelOperationResult") {
+ let result = new NewModelOperationResult();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "AddComparisonResult") {
+ let result = new AddComparisonResult();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "GetModelStateResult") {
+ let result = new GetModelStateResult();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "FeatureImportanceResult") {
+ let result = new FeatureImportanceResult();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "RawDataResult") {
+ let result = new RawDataResult();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "SampleResult") {
+ let result = new SampleResult();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "CDFResult") {
+ let result = new CDFResult();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "ChiSquaredTestResult") {
+ let result = new ChiSquaredTestResult();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "CorrelationTestResult") {
+ let result = new CorrelationTestResult();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "EmpiricalDistResult") {
+ let result = new EmpiricalDistResult();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "KSTestResult") {
+ let result = new KSTestResult();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "RootMeanSquareTestResult") {
+ let result = new RootMeanSquareTestResult();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "TTestResult") {
+ let result = new TTestResult();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "FrequentItemsetResult") {
+ let result = new FrequentItemsetResult();
+ result.init(data);
+ return result;
+ }
+ throw new Error("The abstract class 'Result' cannot be instantiated.");
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["discriminator"] = this._discriminator;
+ data["Progress"] = this.progress;
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IResult extends IUniqueJson {
+ progress?: number | undefined;
+}
+
+export class ErrorResult extends Result implements IErrorResult {
+ message?: string | undefined;
+
+ constructor(data?: IErrorResult) {
+ super(data);
+ this._discriminator = "ErrorResult";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ this.message = data["Message"];
+ }
+ }
+
+ static fromJS(data: any): ErrorResult {
+ data = typeof data === 'object' ? data : {};
+ let result = new ErrorResult();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["Message"] = this.message;
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IErrorResult extends IResult {
+ message?: string | undefined;
+}
+
+export abstract class DistResult extends Result implements IDistResult {
+ sampleSize?: number | undefined;
+ populationSize?: number | undefined;
+
+ constructor(data?: IDistResult) {
+ super(data);
+ this._discriminator = "DistResult";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ this.sampleSize = data["SampleSize"];
+ this.populationSize = data["PopulationSize"];
+ }
+ }
+
+ static fromJS(data: any): DistResult {
+ data = typeof data === 'object' ? data : {};
+ if (data["discriminator"] === "HistogramResult") {
+ let result = new HistogramResult();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "RawDataResult") {
+ let result = new RawDataResult();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "SampleResult") {
+ let result = new SampleResult();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "CDFResult") {
+ let result = new CDFResult();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "EmpiricalDistResult") {
+ let result = new EmpiricalDistResult();
+ result.init(data);
+ return result;
+ }
+ throw new Error("The abstract class 'DistResult' cannot be instantiated.");
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["SampleSize"] = this.sampleSize;
+ data["PopulationSize"] = this.populationSize;
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IDistResult extends IResult {
+ sampleSize?: number | undefined;
+ populationSize?: number | undefined;
+}
+
+export class HistogramResult extends DistResult implements IHistogramResult {
+ aggregateResults?: AggregateResult[][] | undefined;
+ isEmpty?: boolean | undefined;
+ brushes?: Brush[] | undefined;
+ binRanges?: BinRange[] | undefined;
+ aggregateParameters?: AggregateParameters[] | undefined;
+ nullValueCount?: number | undefined;
+ bins?: { [key: string]: Bin; } | undefined;
+
+ constructor(data?: IHistogramResult) {
+ super(data);
+ this._discriminator = "HistogramResult";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ if (data["AggregateResults"] && data["AggregateResults"].constructor === Array) {
+ this.aggregateResults = [];
+ for (let item of data["AggregateResults"])
+ this.aggregateResults.push(item);
+ }
+ this.isEmpty = data["IsEmpty"];
+ if (data["Brushes"] && data["Brushes"].constructor === Array) {
+ this.brushes = [];
+ for (let item of data["Brushes"])
+ this.brushes.push(Brush.fromJS(item));
+ }
+ if (data["BinRanges"] && data["BinRanges"].constructor === Array) {
+ this.binRanges = [];
+ for (let item of data["BinRanges"])
+ this.binRanges.push(BinRange.fromJS(item));
+ }
+ if (data["AggregateParameters"] && data["AggregateParameters"].constructor === Array) {
+ this.aggregateParameters = [];
+ for (let item of data["AggregateParameters"])
+ this.aggregateParameters.push(AggregateParameters.fromJS(item));
+ }
+ this.nullValueCount = data["NullValueCount"];
+ if (data["Bins"]) {
+ this.bins = {};
+ for (let key in data["Bins"]) {
+ if (data["Bins"].hasOwnProperty(key))
+ this.bins[key] = data["Bins"][key] ? Bin.fromJS(data["Bins"][key]) : new Bin();
+ }
+ }
+ }
+ }
+
+ static fromJS(data: any): HistogramResult {
+ data = typeof data === 'object' ? data : {};
+ let result = new HistogramResult();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ if (this.aggregateResults && this.aggregateResults.constructor === Array) {
+ data["AggregateResults"] = [];
+ for (let item of this.aggregateResults)
+ data["AggregateResults"].push(item);
+ }
+ data["IsEmpty"] = this.isEmpty;
+ if (this.brushes && this.brushes.constructor === Array) {
+ data["Brushes"] = [];
+ for (let item of this.brushes)
+ data["Brushes"].push(item.toJSON());
+ }
+ if (this.binRanges && this.binRanges.constructor === Array) {
+ data["BinRanges"] = [];
+ for (let item of this.binRanges)
+ data["BinRanges"].push(item.toJSON());
+ }
+ if (this.aggregateParameters && this.aggregateParameters.constructor === Array) {
+ data["AggregateParameters"] = [];
+ for (let item of this.aggregateParameters)
+ data["AggregateParameters"].push(item.toJSON());
+ }
+ data["NullValueCount"] = this.nullValueCount;
+ if (this.bins) {
+ data["Bins"] = {};
+ for (let key in this.bins) {
+ if (this.bins.hasOwnProperty(key))
+ data["Bins"][key] = this.bins[key];
+ }
+ }
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IHistogramResult extends IDistResult {
+ aggregateResults?: AggregateResult[][] | undefined;
+ isEmpty?: boolean | undefined;
+ brushes?: Brush[] | undefined;
+ binRanges?: BinRange[] | undefined;
+ aggregateParameters?: AggregateParameters[] | undefined;
+ nullValueCount?: number | undefined;
+ bins?: { [key: string]: Bin; } | undefined;
+}
+
+export abstract class AggregateResult implements IAggregateResult {
+ hasResult?: boolean | undefined;
+ n?: number | undefined;
+
+ protected _discriminator: string;
+
+ constructor(data?: IAggregateResult) {
+ if (data) {
+ for (var property in data) {
+ if (data.hasOwnProperty(property))
+ (<any>this)[property] = (<any>data)[property];
+ }
+ }
+ this._discriminator = "AggregateResult";
+ }
+
+ init(data?: any) {
+ if (data) {
+ this.hasResult = data["HasResult"];
+ this.n = data["N"];
+ }
+ }
+
+ static fromJS(data: any): AggregateResult | undefined {
+ if (data === null || data === undefined) {
+ return undefined;
+ }
+ data = typeof data === 'object' ? data : {};
+ if (data["discriminator"] === "MarginAggregateResult") {
+ let result = new MarginAggregateResult();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "DoubleValueAggregateResult") {
+ let result = new DoubleValueAggregateResult();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "PointsAggregateResult") {
+ let result = new PointsAggregateResult();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "SumEstimationAggregateResult") {
+ let result = new SumEstimationAggregateResult();
+ result.init(data);
+ return result;
+ }
+ throw new Error("The abstract class 'AggregateResult' cannot be instantiated.");
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["discriminator"] = this._discriminator;
+ data["HasResult"] = this.hasResult;
+ data["N"] = this.n;
+ return data;
+ }
+}
+
+export interface IAggregateResult {
+ hasResult?: boolean | undefined;
+ n?: number | undefined;
+}
+
+export class MarginAggregateResult extends AggregateResult implements IMarginAggregateResult {
+ margin?: number | undefined;
+ absolutMargin?: number | undefined;
+ sumOfSquare?: number | undefined;
+ sampleStandardDeviation?: number | undefined;
+ mean?: number | undefined;
+ ex?: number | undefined;
+ ex2?: number | undefined;
+ variance?: number | undefined;
+
+ constructor(data?: IMarginAggregateResult) {
+ super(data);
+ this._discriminator = "MarginAggregateResult";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ this.margin = data["Margin"];
+ this.absolutMargin = data["AbsolutMargin"];
+ this.sumOfSquare = data["SumOfSquare"];
+ this.sampleStandardDeviation = data["SampleStandardDeviation"];
+ this.mean = data["Mean"];
+ this.ex = data["Ex"];
+ this.ex2 = data["Ex2"];
+ this.variance = data["Variance"];
+ }
+ }
+
+ static fromJS(data: any): MarginAggregateResult {
+ data = typeof data === 'object' ? data : {};
+ let result = new MarginAggregateResult();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["Margin"] = this.margin;
+ data["AbsolutMargin"] = this.absolutMargin;
+ data["SumOfSquare"] = this.sumOfSquare;
+ data["SampleStandardDeviation"] = this.sampleStandardDeviation;
+ data["Mean"] = this.mean;
+ data["Ex"] = this.ex;
+ data["Ex2"] = this.ex2;
+ data["Variance"] = this.variance;
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IMarginAggregateResult extends IAggregateResult {
+ margin?: number | undefined;
+ absolutMargin?: number | undefined;
+ sumOfSquare?: number | undefined;
+ sampleStandardDeviation?: number | undefined;
+ mean?: number | undefined;
+ ex?: number | undefined;
+ ex2?: number | undefined;
+ variance?: number | undefined;
+}
+
+export class DoubleValueAggregateResult extends AggregateResult implements IDoubleValueAggregateResult {
+ result?: number | undefined;
+ temporaryResult?: number | undefined;
+
+ constructor(data?: IDoubleValueAggregateResult) {
+ super(data);
+ this._discriminator = "DoubleValueAggregateResult";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ this.result = data["Result"];
+ this.temporaryResult = data["TemporaryResult"];
+ }
+ }
+
+ static fromJS(data: any): DoubleValueAggregateResult {
+ data = typeof data === 'object' ? data : {};
+ if (data["discriminator"] === "SumEstimationAggregateResult") {
+ let result = new SumEstimationAggregateResult();
+ result.init(data);
+ return result;
+ }
+ let result = new DoubleValueAggregateResult();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["Result"] = this.result;
+ data["TemporaryResult"] = this.temporaryResult;
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IDoubleValueAggregateResult extends IAggregateResult {
+ result?: number | undefined;
+ temporaryResult?: number | undefined;
+}
+
+export class PointsAggregateResult extends AggregateResult implements IPointsAggregateResult {
+ points?: Point[] | undefined;
+
+ constructor(data?: IPointsAggregateResult) {
+ super(data);
+ this._discriminator = "PointsAggregateResult";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ if (data["Points"] && data["Points"].constructor === Array) {
+ this.points = [];
+ for (let item of data["Points"])
+ this.points.push(Point.fromJS(item));
+ }
+ }
+ }
+
+ static fromJS(data: any): PointsAggregateResult {
+ data = typeof data === 'object' ? data : {};
+ let result = new PointsAggregateResult();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ if (this.points && this.points.constructor === Array) {
+ data["Points"] = [];
+ for (let item of this.points)
+ data["Points"].push(item.toJSON());
+ }
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IPointsAggregateResult extends IAggregateResult {
+ points?: Point[] | undefined;
+}
+
+export class Point implements IPoint {
+ x?: number | undefined;
+ y?: number | undefined;
+
+ constructor(data?: IPoint) {
+ if (data) {
+ for (var property in data) {
+ if (data.hasOwnProperty(property))
+ (<any>this)[property] = (<any>data)[property];
+ }
+ }
+ }
+
+ init(data?: any) {
+ if (data) {
+ this.x = data["X"];
+ this.y = data["Y"];
+ }
+ }
+
+ static fromJS(data: any): Point {
+ data = typeof data === 'object' ? data : {};
+ let result = new Point();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["X"] = this.x;
+ data["Y"] = this.y;
+ return data;
+ }
+}
+
+export interface IPoint {
+ x?: number | undefined;
+ y?: number | undefined;
+}
+
+export class SumEstimationAggregateResult extends DoubleValueAggregateResult implements ISumEstimationAggregateResult {
+ sum?: number | undefined;
+ sumEstimation?: number | undefined;
+
+ constructor(data?: ISumEstimationAggregateResult) {
+ super(data);
+ this._discriminator = "SumEstimationAggregateResult";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ this.sum = data["Sum"];
+ this.sumEstimation = data["SumEstimation"];
+ }
+ }
+
+ static fromJS(data: any): SumEstimationAggregateResult {
+ data = typeof data === 'object' ? data : {};
+ let result = new SumEstimationAggregateResult();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["Sum"] = this.sum;
+ data["SumEstimation"] = this.sumEstimation;
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface ISumEstimationAggregateResult extends IDoubleValueAggregateResult {
+ sum?: number | undefined;
+ sumEstimation?: number | undefined;
+}
+
+export class Brush implements IBrush {
+ brushIndex?: number | undefined;
+ brushEnum?: BrushEnum | undefined;
+
+ constructor(data?: IBrush) {
+ if (data) {
+ for (var property in data) {
+ if (data.hasOwnProperty(property))
+ (<any>this)[property] = (<any>data)[property];
+ }
+ }
+ }
+
+ init(data?: any) {
+ if (data) {
+ this.brushIndex = data["BrushIndex"];
+ this.brushEnum = data["BrushEnum"];
+ }
+ }
+
+ static fromJS(data: any): Brush {
+ data = typeof data === 'object' ? data : {};
+ let result = new Brush();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["BrushIndex"] = this.brushIndex;
+ data["BrushEnum"] = this.brushEnum;
+ return data;
+ }
+}
+
+export interface IBrush {
+ brushIndex?: number | undefined;
+ brushEnum?: BrushEnum | undefined;
+}
+
+export enum BrushEnum {
+ Overlap = 0,
+ Rest = 1,
+ All = 2,
+ UserSpecified = 3,
+}
+
+export abstract class BinRange implements IBinRange {
+ minValue?: number | undefined;
+ maxValue?: number | undefined;
+ targetBinNumber?: number | undefined;
+
+ protected _discriminator: string;
+
+ constructor(data?: IBinRange) {
+ if (data) {
+ for (var property in data) {
+ if (data.hasOwnProperty(property))
+ (<any>this)[property] = (<any>data)[property];
+ }
+ }
+ this._discriminator = "BinRange";
+ }
+
+ init(data?: any) {
+ if (data) {
+ this.minValue = data["MinValue"];
+ this.maxValue = data["MaxValue"];
+ this.targetBinNumber = data["TargetBinNumber"];
+ }
+ }
+
+ static fromJS(data: any): BinRange {
+ data = typeof data === 'object' ? data : {};
+ if (data["discriminator"] === "NominalBinRange") {
+ let result = new NominalBinRange();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "QuantitativeBinRange") {
+ let result = new QuantitativeBinRange();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "AggregateBinRange") {
+ let result = new AggregateBinRange();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "AlphabeticBinRange") {
+ let result = new AlphabeticBinRange();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "DateTimeBinRange") {
+ let result = new DateTimeBinRange();
+ result.init(data);
+ return result;
+ }
+ throw new Error("The abstract class 'BinRange' cannot be instantiated.");
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["discriminator"] = this._discriminator;
+ data["MinValue"] = this.minValue;
+ data["MaxValue"] = this.maxValue;
+ data["TargetBinNumber"] = this.targetBinNumber;
+ return data;
+ }
+}
+
+export interface IBinRange {
+ minValue?: number | undefined;
+ maxValue?: number | undefined;
+ targetBinNumber?: number | undefined;
+}
+
+export class NominalBinRange extends BinRange implements INominalBinRange {
+ labelsValue?: { [key: string]: number; } | undefined;
+ valuesLabel?: { [key: string]: string; } | undefined;
+
+ constructor(data?: INominalBinRange) {
+ super(data);
+ this._discriminator = "NominalBinRange";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ if (data["LabelsValue"]) {
+ this.labelsValue = {};
+ for (let key in data["LabelsValue"]) {
+ if (data["LabelsValue"].hasOwnProperty(key))
+ this.labelsValue[key] = data["LabelsValue"][key];
+ }
+ }
+ if (data["ValuesLabel"]) {
+ this.valuesLabel = {};
+ for (let key in data["ValuesLabel"]) {
+ if (data["ValuesLabel"].hasOwnProperty(key))
+ this.valuesLabel[key] = data["ValuesLabel"][key];
+ }
+ }
+ }
+ }
+
+ static fromJS(data: any): NominalBinRange {
+ data = typeof data === 'object' ? data : {};
+ let result = new NominalBinRange();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ if (this.labelsValue) {
+ data["LabelsValue"] = {};
+ for (let key in this.labelsValue) {
+ if (this.labelsValue.hasOwnProperty(key))
+ data["LabelsValue"][key] = this.labelsValue[key];
+ }
+ }
+ if (this.valuesLabel) {
+ data["ValuesLabel"] = {};
+ for (let key in this.valuesLabel) {
+ if (this.valuesLabel.hasOwnProperty(key))
+ data["ValuesLabel"][key] = this.valuesLabel[key];
+ }
+ }
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface INominalBinRange extends IBinRange {
+ labelsValue?: { [key: string]: number; } | undefined;
+ valuesLabel?: { [key: string]: string; } | undefined;
+}
+
+export class QuantitativeBinRange extends BinRange implements IQuantitativeBinRange {
+ isIntegerRange?: boolean | undefined;
+ step?: number | undefined;
+
+ constructor(data?: IQuantitativeBinRange) {
+ super(data);
+ this._discriminator = "QuantitativeBinRange";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ this.isIntegerRange = data["IsIntegerRange"];
+ this.step = data["Step"];
+ }
+ }
+
+ static fromJS(data: any): QuantitativeBinRange {
+ data = typeof data === 'object' ? data : {};
+ let result = new QuantitativeBinRange();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["IsIntegerRange"] = this.isIntegerRange;
+ data["Step"] = this.step;
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IQuantitativeBinRange extends IBinRange {
+ isIntegerRange?: boolean | undefined;
+ step?: number | undefined;
+}
+
+export class AggregateBinRange extends BinRange implements IAggregateBinRange {
+
+ constructor(data?: IAggregateBinRange) {
+ super(data);
+ this._discriminator = "AggregateBinRange";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ }
+ }
+
+ static fromJS(data: any): AggregateBinRange {
+ data = typeof data === 'object' ? data : {};
+ let result = new AggregateBinRange();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IAggregateBinRange extends IBinRange {
+}
+
+export class AlphabeticBinRange extends BinRange implements IAlphabeticBinRange {
+ prefix?: string | undefined;
+ labelsValue?: { [key: string]: number; } | undefined;
+ valuesLabel?: { [key: string]: string; } | undefined;
+
+ constructor(data?: IAlphabeticBinRange) {
+ super(data);
+ this._discriminator = "AlphabeticBinRange";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ this.prefix = data["Prefix"];
+ if (data["LabelsValue"]) {
+ this.labelsValue = {};
+ for (let key in data["LabelsValue"]) {
+ if (data["LabelsValue"].hasOwnProperty(key))
+ this.labelsValue[key] = data["LabelsValue"][key];
+ }
+ }
+ if (data["ValuesLabel"]) {
+ this.valuesLabel = {};
+ for (let key in data["ValuesLabel"]) {
+ if (data["ValuesLabel"].hasOwnProperty(key))
+ this.valuesLabel[key] = data["ValuesLabel"][key];
+ }
+ }
+ }
+ }
+
+ static fromJS(data: any): AlphabeticBinRange {
+ data = typeof data === 'object' ? data : {};
+ let result = new AlphabeticBinRange();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["Prefix"] = this.prefix;
+ if (this.labelsValue) {
+ data["LabelsValue"] = {};
+ for (let key in this.labelsValue) {
+ if (this.labelsValue.hasOwnProperty(key))
+ data["LabelsValue"][key] = this.labelsValue[key];
+ }
+ }
+ if (this.valuesLabel) {
+ data["ValuesLabel"] = {};
+ for (let key in this.valuesLabel) {
+ if (this.valuesLabel.hasOwnProperty(key))
+ data["ValuesLabel"][key] = this.valuesLabel[key];
+ }
+ }
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IAlphabeticBinRange extends IBinRange {
+ prefix?: string | undefined;
+ labelsValue?: { [key: string]: number; } | undefined;
+ valuesLabel?: { [key: string]: string; } | undefined;
+}
+
+export class DateTimeBinRange extends BinRange implements IDateTimeBinRange {
+ step?: DateTimeStep | undefined;
+
+ constructor(data?: IDateTimeBinRange) {
+ super(data);
+ this._discriminator = "DateTimeBinRange";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ this.step = data["Step"] ? DateTimeStep.fromJS(data["Step"]) : <any>undefined;
+ }
+ }
+
+ static fromJS(data: any): DateTimeBinRange {
+ data = typeof data === 'object' ? data : {};
+ let result = new DateTimeBinRange();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["Step"] = this.step ? this.step.toJSON() : <any>undefined;
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IDateTimeBinRange extends IBinRange {
+ step?: DateTimeStep | undefined;
+}
+
+export class DateTimeStep implements IDateTimeStep {
+ dateTimeStepGranularity?: DateTimeStepGranularity | undefined;
+ dateTimeStepValue?: number | undefined;
+ dateTimeStepMaxValue?: number | undefined;
+
+ constructor(data?: IDateTimeStep) {
+ if (data) {
+ for (var property in data) {
+ if (data.hasOwnProperty(property))
+ (<any>this)[property] = (<any>data)[property];
+ }
+ }
+ }
+
+ init(data?: any) {
+ if (data) {
+ this.dateTimeStepGranularity = data["DateTimeStepGranularity"];
+ this.dateTimeStepValue = data["DateTimeStepValue"];
+ this.dateTimeStepMaxValue = data["DateTimeStepMaxValue"];
+ }
+ }
+
+ static fromJS(data: any): DateTimeStep {
+ data = typeof data === 'object' ? data : {};
+ let result = new DateTimeStep();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["DateTimeStepGranularity"] = this.dateTimeStepGranularity;
+ data["DateTimeStepValue"] = this.dateTimeStepValue;
+ data["DateTimeStepMaxValue"] = this.dateTimeStepMaxValue;
+ return data;
+ }
+}
+
+export interface IDateTimeStep {
+ dateTimeStepGranularity?: DateTimeStepGranularity | undefined;
+ dateTimeStepValue?: number | undefined;
+ dateTimeStepMaxValue?: number | undefined;
+}
+
+export enum DateTimeStepGranularity {
+ Second = 0,
+ Minute = 1,
+ Hour = 2,
+ Day = 3,
+ Month = 4,
+ Year = 5,
+}
+
+export class Bin implements IBin {
+ aggregateResults?: AggregateResult[] | undefined;
+ count?: number | undefined;
+ binIndex?: BinIndex | undefined;
+ spans?: Span[] | undefined;
+ xSize?: number | undefined;
+ ySize?: number | undefined;
+
+ constructor(data?: IBin) {
+ if (data) {
+ for (var property in data) {
+ if (data.hasOwnProperty(property))
+ (<any>this)[property] = (<any>data)[property];
+ }
+ }
+ }
+
+ init(data?: any) {
+ if (data) {
+ if (data["AggregateResults"] && data["AggregateResults"].constructor === Array) {
+ this.aggregateResults = [];
+ for (let item of data["AggregateResults"]) {
+ let fromJs = AggregateResult.fromJS(item);
+ if (fromJs)
+ this.aggregateResults.push(fromJs);
+ }
+ }
+ this.count = data["Count"];
+ this.binIndex = data["BinIndex"] ? BinIndex.fromJS(data["BinIndex"]) : <any>undefined;
+ if (data["Spans"] && data["Spans"].constructor === Array) {
+ this.spans = [];
+ for (let item of data["Spans"])
+ this.spans.push(Span.fromJS(item));
+ }
+ this.xSize = data["XSize"];
+ this.ySize = data["YSize"];
+ }
+ }
+
+ static fromJS(data: any): Bin {
+ data = typeof data === 'object' ? data : {};
+ let result = new Bin();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ if (this.aggregateResults && this.aggregateResults.constructor === Array) {
+ data["AggregateResults"] = [];
+ for (let item of this.aggregateResults)
+ data["AggregateResults"].push(item.toJSON());
+ }
+ data["Count"] = this.count;
+ data["BinIndex"] = this.binIndex ? this.binIndex.toJSON() : <any>undefined;
+ if (this.spans && this.spans.constructor === Array) {
+ data["Spans"] = [];
+ for (let item of this.spans)
+ data["Spans"].push(item.toJSON());
+ }
+ data["XSize"] = this.xSize;
+ data["YSize"] = this.ySize;
+ return data;
+ }
+}
+
+export interface IBin {
+ aggregateResults?: AggregateResult[] | undefined;
+ count?: number | undefined;
+ binIndex?: BinIndex | undefined;
+ spans?: Span[] | undefined;
+ xSize?: number | undefined;
+ ySize?: number | undefined;
+}
+
+export class BinIndex implements IBinIndex {
+ indices?: number[] | undefined;
+ flatIndex?: number | undefined;
+
+ constructor(data?: IBinIndex) {
+ if (data) {
+ for (var property in data) {
+ if (data.hasOwnProperty(property))
+ (<any>this)[property] = (<any>data)[property];
+ }
+ }
+ }
+
+ init(data?: any) {
+ if (data) {
+ if (data["Indices"] && data["Indices"].constructor === Array) {
+ this.indices = [];
+ for (let item of data["Indices"])
+ this.indices.push(item);
+ }
+ this.flatIndex = data["FlatIndex"];
+ }
+ }
+
+ static fromJS(data: any): BinIndex {
+ data = typeof data === 'object' ? data : {};
+ let result = new BinIndex();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ if (this.indices && this.indices.constructor === Array) {
+ data["Indices"] = [];
+ for (let item of this.indices)
+ data["Indices"].push(item);
+ }
+ data["FlatIndex"] = this.flatIndex;
+ return data;
+ }
+}
+
+export interface IBinIndex {
+ indices?: number[] | undefined;
+ flatIndex?: number | undefined;
+}
+
+export class Span implements ISpan {
+ min?: number | undefined;
+ max?: number | undefined;
+ index?: number | undefined;
+
+ constructor(data?: ISpan) {
+ if (data) {
+ for (var property in data) {
+ if (data.hasOwnProperty(property))
+ (<any>this)[property] = (<any>data)[property];
+ }
+ }
+ }
+
+ init(data?: any) {
+ if (data) {
+ this.min = data["Min"];
+ this.max = data["Max"];
+ this.index = data["Index"];
+ }
+ }
+
+ static fromJS(data: any): Span {
+ data = typeof data === 'object' ? data : {};
+ let result = new Span();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["Min"] = this.min;
+ data["Max"] = this.max;
+ data["Index"] = this.index;
+ return data;
+ }
+}
+
+export interface ISpan {
+ min?: number | undefined;
+ max?: number | undefined;
+ index?: number | undefined;
+}
+
+export class ModelWealthResult extends Result implements IModelWealthResult {
+ wealth?: number | undefined;
+ startWealth?: number | undefined;
+
+ constructor(data?: IModelWealthResult) {
+ super(data);
+ this._discriminator = "ModelWealthResult";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ this.wealth = data["Wealth"];
+ this.startWealth = data["StartWealth"];
+ }
+ }
+
+ static fromJS(data: any): ModelWealthResult {
+ data = typeof data === 'object' ? data : {};
+ let result = new ModelWealthResult();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["Wealth"] = this.wealth;
+ data["StartWealth"] = this.startWealth;
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IModelWealthResult extends IResult {
+ wealth?: number | undefined;
+ startWealth?: number | undefined;
+}
+
+export abstract class HypothesisTestResult extends Result implements IHypothesisTestResult {
+ pValue?: number | undefined;
+ statistic?: number | undefined;
+ support?: number | undefined;
+ sampleSizes?: number[] | undefined;
+ errorMessage?: string | undefined;
+
+ constructor(data?: IHypothesisTestResult) {
+ super(data);
+ this._discriminator = "HypothesisTestResult";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ this.pValue = data["PValue"];
+ this.statistic = data["Statistic"];
+ this.support = data["Support"];
+ if (data["SampleSizes"] && data["SampleSizes"].constructor === Array) {
+ this.sampleSizes = [];
+ for (let item of data["SampleSizes"])
+ this.sampleSizes.push(item);
+ }
+ this.errorMessage = data["ErrorMessage"];
+ }
+ }
+
+ static fromJS(data: any): HypothesisTestResult {
+ data = typeof data === 'object' ? data : {};
+ if (data["discriminator"] === "ChiSquaredTestResult") {
+ let result = new ChiSquaredTestResult();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "CorrelationTestResult") {
+ let result = new CorrelationTestResult();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "KSTestResult") {
+ let result = new KSTestResult();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "RootMeanSquareTestResult") {
+ let result = new RootMeanSquareTestResult();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "TTestResult") {
+ let result = new TTestResult();
+ result.init(data);
+ return result;
+ }
+ throw new Error("The abstract class 'HypothesisTestResult' cannot be instantiated.");
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["PValue"] = this.pValue;
+ data["Statistic"] = this.statistic;
+ data["Support"] = this.support;
+ if (this.sampleSizes && this.sampleSizes.constructor === Array) {
+ data["SampleSizes"] = [];
+ for (let item of this.sampleSizes)
+ data["SampleSizes"].push(item);
+ }
+ data["ErrorMessage"] = this.errorMessage;
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IHypothesisTestResult extends IResult {
+ pValue?: number | undefined;
+ statistic?: number | undefined;
+ support?: number | undefined;
+ sampleSizes?: number[] | undefined;
+ errorMessage?: string | undefined;
+}
+
+export abstract class ModelOperationResult extends Result implements IModelOperationResult {
+ modelId?: ModelId | undefined;
+
+ constructor(data?: IModelOperationResult) {
+ super(data);
+ this._discriminator = "ModelOperationResult";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ this.modelId = data["ModelId"] ? ModelId.fromJS(data["ModelId"]) : <any>undefined;
+ }
+ }
+
+ static fromJS(data: any): ModelOperationResult {
+ data = typeof data === 'object' ? data : {};
+ if (data["discriminator"] === "NewModelOperationResult") {
+ let result = new NewModelOperationResult();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "AddComparisonResult") {
+ let result = new AddComparisonResult();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "GetModelStateResult") {
+ let result = new GetModelStateResult();
+ result.init(data);
+ return result;
+ }
+ throw new Error("The abstract class 'ModelOperationResult' cannot be instantiated.");
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["ModelId"] = this.modelId ? this.modelId.toJSON() : <any>undefined;
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IModelOperationResult extends IResult {
+ modelId?: ModelId | undefined;
+}
+
+export class RecommenderResult extends Result implements IRecommenderResult {
+ recommendedHistograms?: RecommendedHistogram[] | undefined;
+ totalCount?: number | undefined;
+
+ constructor(data?: IRecommenderResult) {
+ super(data);
+ this._discriminator = "RecommenderResult";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ if (data["RecommendedHistograms"] && data["RecommendedHistograms"].constructor === Array) {
+ this.recommendedHistograms = [];
+ for (let item of data["RecommendedHistograms"])
+ this.recommendedHistograms.push(RecommendedHistogram.fromJS(item));
+ }
+ this.totalCount = data["TotalCount"];
+ }
+ }
+
+ static fromJS(data: any): RecommenderResult {
+ data = typeof data === 'object' ? data : {};
+ let result = new RecommenderResult();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ if (this.recommendedHistograms && this.recommendedHistograms.constructor === Array) {
+ data["RecommendedHistograms"] = [];
+ for (let item of this.recommendedHistograms)
+ data["RecommendedHistograms"].push(item.toJSON());
+ }
+ data["TotalCount"] = this.totalCount;
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IRecommenderResult extends IResult {
+ recommendedHistograms?: RecommendedHistogram[] | undefined;
+ totalCount?: number | undefined;
+}
+
+export class RecommendedHistogram implements IRecommendedHistogram {
+ histogramResult?: HistogramResult | undefined;
+ selectedBinIndices?: BinIndex[] | undefined;
+ selections?: Selection[] | undefined;
+ pValue?: number | undefined;
+ significance?: boolean | undefined;
+ decision?: Decision | undefined;
+ effectSize?: number | undefined;
+ hypothesisTestResult?: HypothesisTestResult | undefined;
+ id?: string | undefined;
+ xAttribute?: Attribute | undefined;
+ yAttribute?: Attribute | undefined;
+
+ constructor(data?: IRecommendedHistogram) {
+ if (data) {
+ for (var property in data) {
+ if (data.hasOwnProperty(property))
+ (<any>this)[property] = (<any>data)[property];
+ }
+ }
+ }
+
+ init(data?: any) {
+ if (data) {
+ this.histogramResult = data["HistogramResult"] ? HistogramResult.fromJS(data["HistogramResult"]) : <any>undefined;
+ if (data["SelectedBinIndices"] && data["SelectedBinIndices"].constructor === Array) {
+ this.selectedBinIndices = [];
+ for (let item of data["SelectedBinIndices"])
+ this.selectedBinIndices.push(BinIndex.fromJS(item));
+ }
+ if (data["Selections"] && data["Selections"].constructor === Array) {
+ this.selections = [];
+ for (let item of data["Selections"])
+ this.selections.push(Selection.fromJS(item));
+ }
+ this.pValue = data["PValue"];
+ this.significance = data["Significance"];
+ this.decision = data["Decision"] ? Decision.fromJS(data["Decision"]) : <any>undefined;
+ this.effectSize = data["EffectSize"];
+ this.hypothesisTestResult = data["HypothesisTestResult"] ? HypothesisTestResult.fromJS(data["HypothesisTestResult"]) : <any>undefined;
+ this.id = data["Id"];
+ this.xAttribute = data["XAttribute"] ? Attribute.fromJS(data["XAttribute"]) : <any>undefined;
+ this.yAttribute = data["YAttribute"] ? Attribute.fromJS(data["YAttribute"]) : <any>undefined;
+ }
+ }
+
+ static fromJS(data: any): RecommendedHistogram {
+ data = typeof data === 'object' ? data : {};
+ let result = new RecommendedHistogram();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["HistogramResult"] = this.histogramResult ? this.histogramResult.toJSON() : <any>undefined;
+ if (this.selectedBinIndices && this.selectedBinIndices.constructor === Array) {
+ data["SelectedBinIndices"] = [];
+ for (let item of this.selectedBinIndices)
+ data["SelectedBinIndices"].push(item.toJSON());
+ }
+ if (this.selections && this.selections.constructor === Array) {
+ data["Selections"] = [];
+ for (let item of this.selections)
+ data["Selections"].push(item.toJSON());
+ }
+ data["PValue"] = this.pValue;
+ data["Significance"] = this.significance;
+ data["Decision"] = this.decision ? this.decision.toJSON() : <any>undefined;
+ data["EffectSize"] = this.effectSize;
+ data["HypothesisTestResult"] = this.hypothesisTestResult ? this.hypothesisTestResult.toJSON() : <any>undefined;
+ data["Id"] = this.id;
+ data["XAttribute"] = this.xAttribute ? this.xAttribute.toJSON() : <any>undefined;
+ data["YAttribute"] = this.yAttribute ? this.yAttribute.toJSON() : <any>undefined;
+ return data;
+ }
+}
+
+export interface IRecommendedHistogram {
+ histogramResult?: HistogramResult | undefined;
+ selectedBinIndices?: BinIndex[] | undefined;
+ selections?: Selection[] | undefined;
+ pValue?: number | undefined;
+ significance?: boolean | undefined;
+ decision?: Decision | undefined;
+ effectSize?: number | undefined;
+ hypothesisTestResult?: HypothesisTestResult | undefined;
+ id?: string | undefined;
+ xAttribute?: Attribute | undefined;
+ yAttribute?: Attribute | undefined;
+}
+
+export class Selection implements ISelection {
+ statements?: Statement[] | undefined;
+ filterHistogramOperationReference?: IOperationReference | undefined;
+
+ constructor(data?: ISelection) {
+ if (data) {
+ for (var property in data) {
+ if (data.hasOwnProperty(property))
+ (<any>this)[property] = (<any>data)[property];
+ }
+ }
+ }
+
+ init(data?: any) {
+ if (data) {
+ if (data["Statements"] && data["Statements"].constructor === Array) {
+ this.statements = [];
+ for (let item of data["Statements"])
+ this.statements.push(Statement.fromJS(item));
+ }
+ this.filterHistogramOperationReference = data["FilterHistogramOperationReference"] ? IOperationReference.fromJS(data["FilterHistogramOperationReference"]) : <any>undefined;
+ }
+ }
+
+ static fromJS(data: any): Selection {
+ data = typeof data === 'object' ? data : {};
+ let result = new Selection();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ if (this.statements && this.statements.constructor === Array) {
+ data["Statements"] = [];
+ for (let item of this.statements)
+ data["Statements"].push(item.toJSON());
+ }
+ data["FilterHistogramOperationReference"] = this.filterHistogramOperationReference ? this.filterHistogramOperationReference.toJSON() : <any>undefined;
+ return data;
+ }
+}
+
+export interface ISelection {
+ statements?: Statement[] | undefined;
+ filterHistogramOperationReference?: IOperationReference | undefined;
+}
+
+export class Statement implements IStatement {
+ attribute?: Attribute | undefined;
+ predicate?: Predicate | undefined;
+ value?: any | undefined;
+
+ constructor(data?: IStatement) {
+ if (data) {
+ for (var property in data) {
+ if (data.hasOwnProperty(property))
+ (<any>this)[property] = (<any>data)[property];
+ }
+ }
+ }
+
+ init(data?: any) {
+ if (data) {
+ this.attribute = data["Attribute"] ? Attribute.fromJS(data["Attribute"]) : <any>undefined;
+ this.predicate = data["Predicate"];
+ this.value = data["Value"];
+ }
+ }
+
+ static fromJS(data: any): Statement {
+ data = typeof data === 'object' ? data : {};
+ let result = new Statement();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["Attribute"] = this.attribute ? this.attribute.toJSON() : <any>undefined;
+ data["Predicate"] = this.predicate;
+ data["Value"] = this.value;
+ return data;
+ }
+}
+
+export interface IStatement {
+ attribute?: Attribute | undefined;
+ predicate?: Predicate | undefined;
+ value?: any | undefined;
+}
+
+export enum Predicate {
+ EQUALS = 0,
+ LIKE = 1,
+ GREATER_THAN = 2,
+ LESS_THAN = 3,
+ GREATER_THAN_EQUAL = 4,
+ LESS_THAN_EQUAL = 5,
+ STARTS_WITH = 6,
+ ENDS_WITH = 7,
+ CONTAINS = 8,
+}
+
+export abstract class IOperationReference implements IIOperationReference {
+ id?: string | undefined;
+
+ protected _discriminator: string;
+
+ constructor(data?: IIOperationReference) {
+ if (data) {
+ for (var property in data) {
+ if (data.hasOwnProperty(property))
+ (<any>this)[property] = (<any>data)[property];
+ }
+ }
+ this._discriminator = "IOperationReference";
+ }
+
+ init(data?: any) {
+ if (data) {
+ this.id = data["Id"];
+ }
+ }
+
+ static fromJS(data: any): IOperationReference {
+ data = typeof data === 'object' ? data : {};
+ if (data["discriminator"] === "OperationReference") {
+ let result = new OperationReference();
+ result.init(data);
+ return result;
+ }
+ throw new Error("The abstract class 'IOperationReference' cannot be instantiated.");
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["discriminator"] = this._discriminator;
+ data["Id"] = this.id;
+ return data;
+ }
+}
+
+export interface IIOperationReference {
+ id?: string | undefined;
+}
+
+export class OperationReference extends IOperationReference implements IOperationReference {
+ id?: string | undefined;
+
+ constructor(data?: IOperationReference) {
+ super(data);
+ this._discriminator = "OperationReference";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ this.id = data["Id"];
+ }
+ }
+
+ static fromJS(data: any): OperationReference {
+ data = typeof data === 'object' ? data : {};
+ let result = new OperationReference();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["Id"] = this.id;
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IOperationReference extends IIOperationReference {
+ id?: string | undefined;
+}
+
+export class Decision extends Result implements IDecision {
+ comparisonId?: ComparisonId | undefined;
+ riskControlType?: RiskControlType | undefined;
+ significance?: boolean | undefined;
+ pValue?: number | undefined;
+ lhs?: number | undefined;
+ significanceLevel?: number | undefined;
+ sampleSizeEstimate?: number | undefined;
+
+ constructor(data?: IDecision) {
+ super(data);
+ this._discriminator = "Decision";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ this.comparisonId = data["ComparisonId"] ? ComparisonId.fromJS(data["ComparisonId"]) : <any>undefined;
+ this.riskControlType = data["RiskControlType"];
+ this.significance = data["Significance"];
+ this.pValue = data["PValue"];
+ this.lhs = data["Lhs"];
+ this.significanceLevel = data["SignificanceLevel"];
+ this.sampleSizeEstimate = data["SampleSizeEstimate"];
+ }
+ }
+
+ static fromJS(data: any): Decision {
+ data = typeof data === 'object' ? data : {};
+ let result = new Decision();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["ComparisonId"] = this.comparisonId ? this.comparisonId.toJSON() : <any>undefined;
+ data["RiskControlType"] = this.riskControlType;
+ data["Significance"] = this.significance;
+ data["PValue"] = this.pValue;
+ data["Lhs"] = this.lhs;
+ data["SignificanceLevel"] = this.significanceLevel;
+ data["SampleSizeEstimate"] = this.sampleSizeEstimate;
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IDecision extends IResult {
+ comparisonId?: ComparisonId | undefined;
+ riskControlType?: RiskControlType | undefined;
+ significance?: boolean | undefined;
+ pValue?: number | undefined;
+ lhs?: number | undefined;
+ significanceLevel?: number | undefined;
+ sampleSizeEstimate?: number | undefined;
+}
+
+export class ComparisonId implements IComparisonId {
+ value?: string | undefined;
+
+ constructor(data?: IComparisonId) {
+ if (data) {
+ for (var property in data) {
+ if (data.hasOwnProperty(property))
+ (<any>this)[property] = (<any>data)[property];
+ }
+ }
+ }
+
+ init(data?: any) {
+ if (data) {
+ this.value = data["Value"];
+ }
+ }
+
+ static fromJS(data: any): ComparisonId {
+ data = typeof data === 'object' ? data : {};
+ let result = new ComparisonId();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["Value"] = this.value;
+ return data;
+ }
+}
+
+export interface IComparisonId {
+ value?: string | undefined;
+}
+
+export class OptimizerResult extends Result implements IOptimizerResult {
+ topKSolutions?: Solution[] | undefined;
+
+ constructor(data?: IOptimizerResult) {
+ super(data);
+ this._discriminator = "OptimizerResult";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ if (data["TopKSolutions"] && data["TopKSolutions"].constructor === Array) {
+ this.topKSolutions = [];
+ for (let item of data["TopKSolutions"])
+ this.topKSolutions.push(Solution.fromJS(item));
+ }
+ }
+ }
+
+ static fromJS(data: any): OptimizerResult {
+ data = typeof data === 'object' ? data : {};
+ let result = new OptimizerResult();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ if (this.topKSolutions && this.topKSolutions.constructor === Array) {
+ data["TopKSolutions"] = [];
+ for (let item of this.topKSolutions)
+ data["TopKSolutions"].push(item.toJSON());
+ }
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IOptimizerResult extends IResult {
+ topKSolutions?: Solution[] | undefined;
+}
+
+export class Solution implements ISolution {
+ solutionId?: string | undefined;
+ stepDescriptions?: StepDescription[] | undefined;
+ pipelineDescription?: PipelineDescription | undefined;
+ score?: Score | undefined;
+ naiveScore?: Score | undefined;
+
+ constructor(data?: ISolution) {
+ if (data) {
+ for (var property in data) {
+ if (data.hasOwnProperty(property))
+ (<any>this)[property] = (<any>data)[property];
+ }
+ }
+ }
+
+ init(data?: any) {
+ if (data) {
+ this.solutionId = data["SolutionId"];
+ if (data["StepDescriptions"] && data["StepDescriptions"].constructor === Array) {
+ this.stepDescriptions = [];
+ for (let item of data["StepDescriptions"])
+ this.stepDescriptions.push(StepDescription.fromJS(item));
+ }
+ this.pipelineDescription = data["PipelineDescription"] ? PipelineDescription.fromJS(data["PipelineDescription"]) : <any>undefined;
+ this.score = data["Score"] ? Score.fromJS(data["Score"]) : <any>undefined;
+ this.naiveScore = data["NaiveScore"] ? Score.fromJS(data["NaiveScore"]) : <any>undefined;
+ }
+ }
+
+ static fromJS(data: any): Solution {
+ data = typeof data === 'object' ? data : {};
+ let result = new Solution();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["SolutionId"] = this.solutionId;
+ if (this.stepDescriptions && this.stepDescriptions.constructor === Array) {
+ data["StepDescriptions"] = [];
+ for (let item of this.stepDescriptions)
+ data["StepDescriptions"].push(item.toJSON());
+ }
+ data["PipelineDescription"] = this.pipelineDescription ? this.pipelineDescription.toJSON() : <any>undefined;
+ data["Score"] = this.score ? this.score.toJSON() : <any>undefined;
+ data["NaiveScore"] = this.naiveScore ? this.naiveScore.toJSON() : <any>undefined;
+ return data;
+ }
+}
+
+export interface ISolution {
+ solutionId?: string | undefined;
+ stepDescriptions?: StepDescription[] | undefined;
+ pipelineDescription?: PipelineDescription | undefined;
+ score?: Score | undefined;
+ naiveScore?: Score | undefined;
+}
+
+export class StepDescription implements IStepDescription {
+
+ protected _discriminator: string;
+
+ constructor(data?: IStepDescription) {
+ if (data) {
+ for (var property in data) {
+ if (data.hasOwnProperty(property))
+ (<any>this)[property] = (<any>data)[property];
+ }
+ }
+ this._discriminator = "StepDescription";
+ }
+
+ init(data?: any) {
+ if (data) {
+ }
+ }
+
+ static fromJS(data: any): StepDescription {
+ data = typeof data === 'object' ? data : {};
+ if (data["discriminator"] === "SubpipelineStepDescription") {
+ let result = new SubpipelineStepDescription();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "PrimitiveStepDescription") {
+ let result = new PrimitiveStepDescription();
+ result.init(data);
+ return result;
+ }
+ let result = new StepDescription();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["discriminator"] = this._discriminator;
+ return data;
+ }
+}
+
+export interface IStepDescription {
+}
+
+export class SubpipelineStepDescription extends StepDescription implements ISubpipelineStepDescription {
+ steps?: StepDescription[] | undefined;
+
+ constructor(data?: ISubpipelineStepDescription) {
+ super(data);
+ this._discriminator = "SubpipelineStepDescription";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ if (data["Steps"] && data["Steps"].constructor === Array) {
+ this.steps = [];
+ for (let item of data["Steps"])
+ this.steps.push(StepDescription.fromJS(item));
+ }
+ }
+ }
+
+ static fromJS(data: any): SubpipelineStepDescription {
+ data = typeof data === 'object' ? data : {};
+ let result = new SubpipelineStepDescription();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ if (this.steps && this.steps.constructor === Array) {
+ data["Steps"] = [];
+ for (let item of this.steps)
+ data["Steps"].push(item.toJSON());
+ }
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface ISubpipelineStepDescription extends IStepDescription {
+ steps?: StepDescription[] | undefined;
+}
+
+export class PipelineDescription implements IPipelineDescription {
+ id?: string | undefined;
+ name?: string | undefined;
+ description?: string | undefined;
+ inputs?: PipelineDescriptionInput[] | undefined;
+ outputs?: PipelineDescriptionOutput[] | undefined;
+ steps?: PipelineDescriptionStep[] | undefined;
+
+ constructor(data?: IPipelineDescription) {
+ if (data) {
+ for (var property in data) {
+ if (data.hasOwnProperty(property))
+ (<any>this)[property] = (<any>data)[property];
+ }
+ }
+ }
+
+ init(data?: any) {
+ if (data) {
+ this.id = data["Id"];
+ this.name = data["Name"];
+ this.description = data["Description"];
+ if (data["Inputs"] && data["Inputs"].constructor === Array) {
+ this.inputs = [];
+ for (let item of data["Inputs"])
+ this.inputs.push(PipelineDescriptionInput.fromJS(item));
+ }
+ if (data["Outputs"] && data["Outputs"].constructor === Array) {
+ this.outputs = [];
+ for (let item of data["Outputs"])
+ this.outputs.push(PipelineDescriptionOutput.fromJS(item));
+ }
+ if (data["Steps"] && data["Steps"].constructor === Array) {
+ this.steps = [];
+ for (let item of data["Steps"])
+ this.steps.push(PipelineDescriptionStep.fromJS(item));
+ }
+ }
+ }
+
+ static fromJS(data: any): PipelineDescription {
+ data = typeof data === 'object' ? data : {};
+ let result = new PipelineDescription();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["Id"] = this.id;
+ data["Name"] = this.name;
+ data["Description"] = this.description;
+ if (this.inputs && this.inputs.constructor === Array) {
+ data["Inputs"] = [];
+ for (let item of this.inputs)
+ data["Inputs"].push(item.toJSON());
+ }
+ if (this.outputs && this.outputs.constructor === Array) {
+ data["Outputs"] = [];
+ for (let item of this.outputs)
+ data["Outputs"].push(item.toJSON());
+ }
+ if (this.steps && this.steps.constructor === Array) {
+ data["Steps"] = [];
+ for (let item of this.steps)
+ data["Steps"].push(item.toJSON());
+ }
+ return data;
+ }
+}
+
+export interface IPipelineDescription {
+ id?: string | undefined;
+ name?: string | undefined;
+ description?: string | undefined;
+ inputs?: PipelineDescriptionInput[] | undefined;
+ outputs?: PipelineDescriptionOutput[] | undefined;
+ steps?: PipelineDescriptionStep[] | undefined;
+}
+
+export class PipelineDescriptionInput implements IPipelineDescriptionInput {
+ name?: string | undefined;
+
+ constructor(data?: IPipelineDescriptionInput) {
+ if (data) {
+ for (var property in data) {
+ if (data.hasOwnProperty(property))
+ (<any>this)[property] = (<any>data)[property];
+ }
+ }
+ }
+
+ init(data?: any) {
+ if (data) {
+ this.name = data["Name"];
+ }
+ }
+
+ static fromJS(data: any): PipelineDescriptionInput {
+ data = typeof data === 'object' ? data : {};
+ let result = new PipelineDescriptionInput();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["Name"] = this.name;
+ return data;
+ }
+}
+
+export interface IPipelineDescriptionInput {
+ name?: string | undefined;
+}
+
+export class PipelineDescriptionOutput implements IPipelineDescriptionOutput {
+ name?: string | undefined;
+ data?: string | undefined;
+
+ constructor(data?: IPipelineDescriptionOutput) {
+ if (data) {
+ for (var property in data) {
+ if (data.hasOwnProperty(property))
+ (<any>this)[property] = (<any>data)[property];
+ }
+ }
+ }
+
+ init(data?: any) {
+ if (data) {
+ this.name = data["Name"];
+ this.data = data["Data"];
+ }
+ }
+
+ static fromJS(data: any): PipelineDescriptionOutput {
+ data = typeof data === 'object' ? data : {};
+ let result = new PipelineDescriptionOutput();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["Name"] = this.name;
+ data["Data"] = this.data;
+ return data;
+ }
+}
+
+export interface IPipelineDescriptionOutput {
+ name?: string | undefined;
+ data?: string | undefined;
+}
+
+export class PipelineDescriptionStep implements IPipelineDescriptionStep {
+ outputs?: StepOutput[] | undefined;
+
+ protected _discriminator: string;
+
+ constructor(data?: IPipelineDescriptionStep) {
+ if (data) {
+ for (var property in data) {
+ if (data.hasOwnProperty(property))
+ (<any>this)[property] = (<any>data)[property];
+ }
+ }
+ this._discriminator = "PipelineDescriptionStep";
+ }
+
+ init(data?: any) {
+ if (data) {
+ if (data["Outputs"] && data["Outputs"].constructor === Array) {
+ this.outputs = [];
+ for (let item of data["Outputs"])
+ this.outputs.push(StepOutput.fromJS(item));
+ }
+ }
+ }
+
+ static fromJS(data: any): PipelineDescriptionStep {
+ data = typeof data === 'object' ? data : {};
+ if (data["discriminator"] === "PlaceholderPipelineDescriptionStep") {
+ let result = new PlaceholderPipelineDescriptionStep();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "SubpipelinePipelineDescriptionStep") {
+ let result = new SubpipelinePipelineDescriptionStep();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "PrimitivePipelineDescriptionStep") {
+ let result = new PrimitivePipelineDescriptionStep();
+ result.init(data);
+ return result;
+ }
+ let result = new PipelineDescriptionStep();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["discriminator"] = this._discriminator;
+ if (this.outputs && this.outputs.constructor === Array) {
+ data["Outputs"] = [];
+ for (let item of this.outputs)
+ data["Outputs"].push(item.toJSON());
+ }
+ return data;
+ }
+}
+
+export interface IPipelineDescriptionStep {
+ outputs?: StepOutput[] | undefined;
+}
+
+export class StepOutput implements IStepOutput {
+ id?: string | undefined;
+
+ constructor(data?: IStepOutput) {
+ if (data) {
+ for (var property in data) {
+ if (data.hasOwnProperty(property))
+ (<any>this)[property] = (<any>data)[property];
+ }
+ }
+ }
+
+ init(data?: any) {
+ if (data) {
+ this.id = data["Id"];
+ }
+ }
+
+ static fromJS(data: any): StepOutput {
+ data = typeof data === 'object' ? data : {};
+ let result = new StepOutput();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["Id"] = this.id;
+ return data;
+ }
+}
+
+export interface IStepOutput {
+ id?: string | undefined;
+}
+
+export class PlaceholderPipelineDescriptionStep extends PipelineDescriptionStep implements IPlaceholderPipelineDescriptionStep {
+ inputs?: StepInput[] | undefined;
+
+ constructor(data?: IPlaceholderPipelineDescriptionStep) {
+ super(data);
+ this._discriminator = "PlaceholderPipelineDescriptionStep";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ if (data["Inputs"] && data["Inputs"].constructor === Array) {
+ this.inputs = [];
+ for (let item of data["Inputs"])
+ this.inputs.push(StepInput.fromJS(item));
+ }
+ }
+ }
+
+ static fromJS(data: any): PlaceholderPipelineDescriptionStep {
+ data = typeof data === 'object' ? data : {};
+ let result = new PlaceholderPipelineDescriptionStep();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ if (this.inputs && this.inputs.constructor === Array) {
+ data["Inputs"] = [];
+ for (let item of this.inputs)
+ data["Inputs"].push(item.toJSON());
+ }
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IPlaceholderPipelineDescriptionStep extends IPipelineDescriptionStep {
+ inputs?: StepInput[] | undefined;
+}
+
+export class StepInput implements IStepInput {
+ data?: string | undefined;
+
+ constructor(data?: IStepInput) {
+ if (data) {
+ for (var property in data) {
+ if (data.hasOwnProperty(property))
+ (<any>this)[property] = (<any>data)[property];
+ }
+ }
+ }
+
+ init(data?: any) {
+ if (data) {
+ this.data = data["Data"];
+ }
+ }
+
+ static fromJS(data: any): StepInput {
+ data = typeof data === 'object' ? data : {};
+ let result = new StepInput();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["Data"] = this.data;
+ return data;
+ }
+}
+
+export interface IStepInput {
+ data?: string | undefined;
+}
+
+export class SubpipelinePipelineDescriptionStep extends PipelineDescriptionStep implements ISubpipelinePipelineDescriptionStep {
+ pipelineDescription?: PipelineDescription | undefined;
+ inputs?: StepInput[] | undefined;
+
+ constructor(data?: ISubpipelinePipelineDescriptionStep) {
+ super(data);
+ this._discriminator = "SubpipelinePipelineDescriptionStep";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ this.pipelineDescription = data["PipelineDescription"] ? PipelineDescription.fromJS(data["PipelineDescription"]) : <any>undefined;
+ if (data["Inputs"] && data["Inputs"].constructor === Array) {
+ this.inputs = [];
+ for (let item of data["Inputs"])
+ this.inputs.push(StepInput.fromJS(item));
+ }
+ }
+ }
+
+ static fromJS(data: any): SubpipelinePipelineDescriptionStep {
+ data = typeof data === 'object' ? data : {};
+ let result = new SubpipelinePipelineDescriptionStep();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["PipelineDescription"] = this.pipelineDescription ? this.pipelineDescription.toJSON() : <any>undefined;
+ if (this.inputs && this.inputs.constructor === Array) {
+ data["Inputs"] = [];
+ for (let item of this.inputs)
+ data["Inputs"].push(item.toJSON());
+ }
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface ISubpipelinePipelineDescriptionStep extends IPipelineDescriptionStep {
+ pipelineDescription?: PipelineDescription | undefined;
+ inputs?: StepInput[] | undefined;
+}
+
+export class PrimitivePipelineDescriptionStep extends PipelineDescriptionStep implements IPrimitivePipelineDescriptionStep {
+ primitive?: Primitive | undefined;
+ arguments?: { [key: string]: PrimitiveStepArgument; } | undefined;
+ hyperparams?: { [key: string]: PrimitiveStepHyperparameter; } | undefined;
+
+ constructor(data?: IPrimitivePipelineDescriptionStep) {
+ super(data);
+ this._discriminator = "PrimitivePipelineDescriptionStep";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ this.primitive = data["Primitive"] ? Primitive.fromJS(data["Primitive"]) : <any>undefined;
+ if (data["Arguments"]) {
+ this.arguments = {};
+ for (let key in data["Arguments"]) {
+ if (data["Arguments"].hasOwnProperty(key))
+ this.arguments[key] = data["Arguments"][key] ? PrimitiveStepArgument.fromJS(data["Arguments"][key]) : new PrimitiveStepArgument();
+ }
+ }
+ if (data["Hyperparams"]) {
+ this.hyperparams = {};
+ for (let key in data["Hyperparams"]) {
+ if (data["Hyperparams"].hasOwnProperty(key))
+ this.hyperparams[key] = data["Hyperparams"][key] ? PrimitiveStepHyperparameter.fromJS(data["Hyperparams"][key]) : new PrimitiveStepHyperparameter();
+ }
+ }
+ }
+ }
+
+ static fromJS(data: any): PrimitivePipelineDescriptionStep {
+ data = typeof data === 'object' ? data : {};
+ let result = new PrimitivePipelineDescriptionStep();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["Primitive"] = this.primitive ? this.primitive.toJSON() : <any>undefined;
+ if (this.arguments) {
+ data["Arguments"] = {};
+ for (let key in this.arguments) {
+ if (this.arguments.hasOwnProperty(key))
+ data["Arguments"][key] = this.arguments[key];
+ }
+ }
+ if (this.hyperparams) {
+ data["Hyperparams"] = {};
+ for (let key in this.hyperparams) {
+ if (this.hyperparams.hasOwnProperty(key))
+ data["Hyperparams"][key] = this.hyperparams[key];
+ }
+ }
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IPrimitivePipelineDescriptionStep extends IPipelineDescriptionStep {
+ primitive?: Primitive | undefined;
+ arguments?: { [key: string]: PrimitiveStepArgument; } | undefined;
+ hyperparams?: { [key: string]: PrimitiveStepHyperparameter; } | undefined;
+}
+
+export class Primitive implements IPrimitive {
+ id?: string | undefined;
+ version?: string | undefined;
+ pythonPath?: string | undefined;
+ name?: string | undefined;
+ digest?: string | undefined;
+
+ constructor(data?: IPrimitive) {
+ if (data) {
+ for (var property in data) {
+ if (data.hasOwnProperty(property))
+ (<any>this)[property] = (<any>data)[property];
+ }
+ }
+ }
+
+ init(data?: any) {
+ if (data) {
+ this.id = data["Id"];
+ this.version = data["Version"];
+ this.pythonPath = data["PythonPath"];
+ this.name = data["Name"];
+ this.digest = data["Digest"];
+ }
+ }
+
+ static fromJS(data: any): Primitive {
+ data = typeof data === 'object' ? data : {};
+ let result = new Primitive();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["Id"] = this.id;
+ data["Version"] = this.version;
+ data["PythonPath"] = this.pythonPath;
+ data["Name"] = this.name;
+ data["Digest"] = this.digest;
+ return data;
+ }
+}
+
+export interface IPrimitive {
+ id?: string | undefined;
+ version?: string | undefined;
+ pythonPath?: string | undefined;
+ name?: string | undefined;
+ digest?: string | undefined;
+}
+
+export class PrimitiveStepHyperparameter implements IPrimitiveStepHyperparameter {
+
+ protected _discriminator: string;
+
+ constructor(data?: IPrimitiveStepHyperparameter) {
+ if (data) {
+ for (var property in data) {
+ if (data.hasOwnProperty(property))
+ (<any>this)[property] = (<any>data)[property];
+ }
+ }
+ this._discriminator = "PrimitiveStepHyperparameter";
+ }
+
+ init(data?: any) {
+ if (data) {
+ }
+ }
+
+ static fromJS(data: any): PrimitiveStepHyperparameter {
+ data = typeof data === 'object' ? data : {};
+ if (data["discriminator"] === "PrimitiveStepArgument") {
+ let result = new PrimitiveStepArgument();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "DataArguments") {
+ let result = new DataArguments();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "PrimitiveArgument") {
+ let result = new PrimitiveArgument();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "PrimitiveArguments") {
+ let result = new PrimitiveArguments();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "ValueArgument") {
+ let result = new ValueArgument();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "ContainerArgument") {
+ let result = new ContainerArgument();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "DataArgument") {
+ let result = new DataArgument();
+ result.init(data);
+ return result;
+ }
+ let result = new PrimitiveStepHyperparameter();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["discriminator"] = this._discriminator;
+ return data;
+ }
+}
+
+export interface IPrimitiveStepHyperparameter {
+}
+
+export class PrimitiveStepArgument extends PrimitiveStepHyperparameter implements IPrimitiveStepArgument {
+
+ protected _discriminator: string;
+
+ constructor(data?: IPrimitiveStepArgument) {
+ super(data);
+ this._discriminator = "PrimitiveStepArgument";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ }
+ }
+
+ static fromJS(data: any): PrimitiveStepArgument {
+ data = typeof data === 'object' ? data : {};
+ if (data["discriminator"] === "ContainerArgument") {
+ let result = new ContainerArgument();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "DataArgument") {
+ let result = new DataArgument();
+ result.init(data);
+ return result;
+ }
+ let result = new PrimitiveStepArgument();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["discriminator"] = this._discriminator;
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IPrimitiveStepArgument extends IPrimitiveStepHyperparameter {
+}
+
+export class DataArguments extends PrimitiveStepHyperparameter implements IDataArguments {
+ data?: string[] | undefined;
+
+ constructor(data?: IDataArguments) {
+ super(data);
+ this._discriminator = "DataArguments";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ if (data["Data"] && data["Data"].constructor === Array) {
+ this.data = [];
+ for (let item of data["Data"])
+ this.data.push(item);
+ }
+ }
+ }
+
+ static fromJS(data: any): DataArguments {
+ data = typeof data === 'object' ? data : {};
+ let result = new DataArguments();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ if (this.data && this.data.constructor === Array) {
+ data["Data"] = [];
+ for (let item of this.data)
+ data["Data"].push(item);
+ }
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IDataArguments extends IPrimitiveStepHyperparameter {
+ data?: string[] | undefined;
+}
+
+export class PrimitiveArgument extends PrimitiveStepHyperparameter implements IPrimitiveArgument {
+ data?: number | undefined;
+
+ constructor(data?: IPrimitiveArgument) {
+ super(data);
+ this._discriminator = "PrimitiveArgument";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ this.data = data["Data"];
+ }
+ }
+
+ static fromJS(data: any): PrimitiveArgument {
+ data = typeof data === 'object' ? data : {};
+ let result = new PrimitiveArgument();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["Data"] = this.data;
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IPrimitiveArgument extends IPrimitiveStepHyperparameter {
+ data?: number | undefined;
+}
+
+export class PrimitiveArguments extends PrimitiveStepHyperparameter implements IPrimitiveArguments {
+ data?: number[] | undefined;
+
+ constructor(data?: IPrimitiveArguments) {
+ super(data);
+ this._discriminator = "PrimitiveArguments";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ if (data["Data"] && data["Data"].constructor === Array) {
+ this.data = [];
+ for (let item of data["Data"])
+ this.data.push(item);
+ }
+ }
+ }
+
+ static fromJS(data: any): PrimitiveArguments {
+ data = typeof data === 'object' ? data : {};
+ let result = new PrimitiveArguments();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ if (this.data && this.data.constructor === Array) {
+ data["Data"] = [];
+ for (let item of this.data)
+ data["Data"].push(item);
+ }
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IPrimitiveArguments extends IPrimitiveStepHyperparameter {
+ data?: number[] | undefined;
+}
+
+export class ValueArgument extends PrimitiveStepHyperparameter implements IValueArgument {
+ data?: Value | undefined;
+
+ constructor(data?: IValueArgument) {
+ super(data);
+ this._discriminator = "ValueArgument";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ this.data = data["Data"] ? Value.fromJS(data["Data"]) : <any>undefined;
+ }
+ }
+
+ static fromJS(data: any): ValueArgument {
+ data = typeof data === 'object' ? data : {};
+ let result = new ValueArgument();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["Data"] = this.data ? this.data.toJSON() : <any>undefined;
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IValueArgument extends IPrimitiveStepHyperparameter {
+ data?: Value | undefined;
+}
+
+export abstract class Value implements IValue {
+
+ protected _discriminator: string;
+
+ constructor(data?: IValue) {
+ if (data) {
+ for (var property in data) {
+ if (data.hasOwnProperty(property))
+ (<any>this)[property] = (<any>data)[property];
+ }
+ }
+ this._discriminator = "Value";
+ }
+
+ init(data?: any) {
+ if (data) {
+ }
+ }
+
+ static fromJS(data: any): Value {
+ data = typeof data === 'object' ? data : {};
+ if (data["discriminator"] === "ErrorValue") {
+ let result = new ErrorValue();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "DoubleValue") {
+ let result = new DoubleValue();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "LongValue") {
+ let result = new LongValue();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "BoolValue") {
+ let result = new BoolValue();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "StringValue") {
+ let result = new StringValue();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "DatasetUriValue") {
+ let result = new DatasetUriValue();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "CsvUriValue") {
+ let result = new CsvUriValue();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "PickleUriValue") {
+ let result = new PickleUriValue();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "PickleBlobValue") {
+ let result = new PickleBlobValue();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "PlasmaIdValue") {
+ let result = new PlasmaIdValue();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "BytesValue") {
+ let result = new BytesValue();
+ result.init(data);
+ return result;
+ }
+ if (data["discriminator"] === "ListValue") {
+ let result = new ListValue();
+ result.init(data);
+ return result;
+ }
+ throw new Error("The abstract class 'Value' cannot be instantiated.");
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["discriminator"] = this._discriminator;
+ return data;
+ }
+}
+
+export interface IValue {
+}
+
+export class ErrorValue extends Value implements IErrorValue {
+ message?: string | undefined;
+
+ constructor(data?: IErrorValue) {
+ super(data);
+ this._discriminator = "ErrorValue";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ this.message = data["Message"];
+ }
+ }
+
+ static fromJS(data: any): ErrorValue {
+ data = typeof data === 'object' ? data : {};
+ let result = new ErrorValue();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["Message"] = this.message;
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IErrorValue extends IValue {
+ message?: string | undefined;
+}
+
+export class DoubleValue extends Value implements IDoubleValue {
+ value?: number | undefined;
+
+ constructor(data?: IDoubleValue) {
+ super(data);
+ this._discriminator = "DoubleValue";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ this.value = data["Value"];
+ }
+ }
+
+ static fromJS(data: any): DoubleValue {
+ data = typeof data === 'object' ? data : {};
+ let result = new DoubleValue();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["Value"] = this.value;
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IDoubleValue extends IValue {
+ value?: number | undefined;
+}
+
+export class LongValue extends Value implements ILongValue {
+ value?: number | undefined;
+
+ constructor(data?: ILongValue) {
+ super(data);
+ this._discriminator = "LongValue";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ this.value = data["Value"];
+ }
+ }
+
+ static fromJS(data: any): LongValue {
+ data = typeof data === 'object' ? data : {};
+ let result = new LongValue();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["Value"] = this.value;
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface ILongValue extends IValue {
+ value?: number | undefined;
+}
+
+export class BoolValue extends Value implements IBoolValue {
+ value?: boolean | undefined;
+
+ constructor(data?: IBoolValue) {
+ super(data);
+ this._discriminator = "BoolValue";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ this.value = data["Value"];
+ }
+ }
+
+ static fromJS(data: any): BoolValue {
+ data = typeof data === 'object' ? data : {};
+ let result = new BoolValue();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["Value"] = this.value;
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IBoolValue extends IValue {
+ value?: boolean | undefined;
+}
+
+export class StringValue extends Value implements IStringValue {
+ value?: string | undefined;
+
+ constructor(data?: IStringValue) {
+ super(data);
+ this._discriminator = "StringValue";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ this.value = data["Value"];
+ }
+ }
+
+ static fromJS(data: any): StringValue {
+ data = typeof data === 'object' ? data : {};
+ let result = new StringValue();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["Value"] = this.value;
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IStringValue extends IValue {
+ value?: string | undefined;
+}
+
+export class DatasetUriValue extends Value implements IDatasetUriValue {
+ value?: string | undefined;
+
+ constructor(data?: IDatasetUriValue) {
+ super(data);
+ this._discriminator = "DatasetUriValue";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ this.value = data["Value"];
+ }
+ }
+
+ static fromJS(data: any): DatasetUriValue {
+ data = typeof data === 'object' ? data : {};
+ let result = new DatasetUriValue();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["Value"] = this.value;
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IDatasetUriValue extends IValue {
+ value?: string | undefined;
+}
+
+export class CsvUriValue extends Value implements ICsvUriValue {
+ value?: string | undefined;
+
+ constructor(data?: ICsvUriValue) {
+ super(data);
+ this._discriminator = "CsvUriValue";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ this.value = data["Value"];
+ }
+ }
+
+ static fromJS(data: any): CsvUriValue {
+ data = typeof data === 'object' ? data : {};
+ let result = new CsvUriValue();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["Value"] = this.value;
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface ICsvUriValue extends IValue {
+ value?: string | undefined;
+}
+
+export class PickleUriValue extends Value implements IPickleUriValue {
+ value?: string | undefined;
+
+ constructor(data?: IPickleUriValue) {
+ super(data);
+ this._discriminator = "PickleUriValue";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ this.value = data["Value"];
+ }
+ }
+
+ static fromJS(data: any): PickleUriValue {
+ data = typeof data === 'object' ? data : {};
+ let result = new PickleUriValue();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["Value"] = this.value;
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IPickleUriValue extends IValue {
+ value?: string | undefined;
+}
+
+export class PickleBlobValue extends Value implements IPickleBlobValue {
+ value?: string | undefined;
+
+ constructor(data?: IPickleBlobValue) {
+ super(data);
+ this._discriminator = "PickleBlobValue";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ this.value = data["Value"];
+ }
+ }
+
+ static fromJS(data: any): PickleBlobValue {
+ data = typeof data === 'object' ? data : {};
+ let result = new PickleBlobValue();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["Value"] = this.value;
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IPickleBlobValue extends IValue {
+ value?: string | undefined;
+}
+
+export class PlasmaIdValue extends Value implements IPlasmaIdValue {
+ value?: string | undefined;
+
+ constructor(data?: IPlasmaIdValue) {
+ super(data);
+ this._discriminator = "PlasmaIdValue";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ this.value = data["Value"];
+ }
+ }
+
+ static fromJS(data: any): PlasmaIdValue {
+ data = typeof data === 'object' ? data : {};
+ let result = new PlasmaIdValue();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["Value"] = this.value;
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IPlasmaIdValue extends IValue {
+ value?: string | undefined;
+}
+
+export class BytesValue extends Value implements IBytesValue {
+ value?: string | undefined;
+
+ constructor(data?: IBytesValue) {
+ super(data);
+ this._discriminator = "BytesValue";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ this.value = data["Value"];
+ }
+ }
+
+ static fromJS(data: any): BytesValue {
+ data = typeof data === 'object' ? data : {};
+ let result = new BytesValue();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["Value"] = this.value;
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IBytesValue extends IValue {
+ value?: string | undefined;
+}
+
+export class ContainerArgument extends PrimitiveStepArgument implements IContainerArgument {
+ data?: string | undefined;
+
+ constructor(data?: IContainerArgument) {
+ super(data);
+ this._discriminator = "ContainerArgument";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ this.data = data["Data"];
+ }
+ }
+
+ static fromJS(data: any): ContainerArgument {
+ data = typeof data === 'object' ? data : {};
+ let result = new ContainerArgument();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["Data"] = this.data;
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IContainerArgument extends IPrimitiveStepArgument {
+ data?: string | undefined;
+}
+
+export class Score implements IScore {
+ metricType?: MetricType | undefined;
+ value?: number | undefined;
+
+ constructor(data?: IScore) {
+ if (data) {
+ for (var property in data) {
+ if (data.hasOwnProperty(property))
+ (<any>this)[property] = (<any>data)[property];
+ }
+ }
+ }
+
+ init(data?: any) {
+ if (data) {
+ this.metricType = data["MetricType"];
+ this.value = data["Value"];
+ }
+ }
+
+ static fromJS(data: any): Score {
+ data = typeof data === 'object' ? data : {};
+ let result = new Score();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["MetricType"] = this.metricType;
+ data["Value"] = this.value;
+ return data;
+ }
+}
+
+export interface IScore {
+ metricType?: MetricType | undefined;
+ value?: number | undefined;
+}
+
+export class ExampleResult extends Result implements IExampleResult {
+ resultValues?: { [key: string]: string; } | undefined;
+ message?: string | undefined;
+
+ constructor(data?: IExampleResult) {
+ super(data);
+ this._discriminator = "ExampleResult";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ if (data["ResultValues"]) {
+ this.resultValues = {};
+ for (let key in data["ResultValues"]) {
+ if (data["ResultValues"].hasOwnProperty(key))
+ this.resultValues[key] = data["ResultValues"][key];
+ }
+ }
+ this.message = data["Message"];
+ }
+ }
+
+ static fromJS(data: any): ExampleResult {
+ data = typeof data === 'object' ? data : {};
+ let result = new ExampleResult();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ if (this.resultValues) {
+ data["ResultValues"] = {};
+ for (let key in this.resultValues) {
+ if (this.resultValues.hasOwnProperty(key))
+ data["ResultValues"][key] = this.resultValues[key];
+ }
+ }
+ data["Message"] = this.message;
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IExampleResult extends IResult {
+ resultValues?: { [key: string]: string; } | undefined;
+ message?: string | undefined;
+}
+
+export class NewModelOperationResult extends ModelOperationResult implements INewModelOperationResult {
+
+ constructor(data?: INewModelOperationResult) {
+ super(data);
+ this._discriminator = "NewModelOperationResult";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ }
+ }
+
+ static fromJS(data: any): NewModelOperationResult {
+ data = typeof data === 'object' ? data : {};
+ let result = new NewModelOperationResult();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface INewModelOperationResult extends IModelOperationResult {
+}
+
+export class AddComparisonResult extends ModelOperationResult implements IAddComparisonResult {
+ comparisonId?: ComparisonId | undefined;
+
+ constructor(data?: IAddComparisonResult) {
+ super(data);
+ this._discriminator = "AddComparisonResult";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ this.comparisonId = data["ComparisonId"] ? ComparisonId.fromJS(data["ComparisonId"]) : <any>undefined;
+ }
+ }
+
+ static fromJS(data: any): AddComparisonResult {
+ data = typeof data === 'object' ? data : {};
+ let result = new AddComparisonResult();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["ComparisonId"] = this.comparisonId ? this.comparisonId.toJSON() : <any>undefined;
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IAddComparisonResult extends IModelOperationResult {
+ comparisonId?: ComparisonId | undefined;
+}
+
+export class GetModelStateResult extends ModelOperationResult implements IGetModelStateResult {
+ decisions?: Decision[] | undefined;
+ startingWealth?: number | undefined;
+ currentWealth?: number | undefined;
+
+ constructor(data?: IGetModelStateResult) {
+ super(data);
+ this._discriminator = "GetModelStateResult";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ if (data["Decisions"] && data["Decisions"].constructor === Array) {
+ this.decisions = [];
+ for (let item of data["Decisions"])
+ this.decisions.push(Decision.fromJS(item));
+ }
+ this.startingWealth = data["StartingWealth"];
+ this.currentWealth = data["CurrentWealth"];
+ }
+ }
+
+ static fromJS(data: any): GetModelStateResult {
+ data = typeof data === 'object' ? data : {};
+ let result = new GetModelStateResult();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ if (this.decisions && this.decisions.constructor === Array) {
+ data["Decisions"] = [];
+ for (let item of this.decisions)
+ data["Decisions"].push(item.toJSON());
+ }
+ data["StartingWealth"] = this.startingWealth;
+ data["CurrentWealth"] = this.currentWealth;
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IGetModelStateResult extends IModelOperationResult {
+ decisions?: Decision[] | undefined;
+ startingWealth?: number | undefined;
+ currentWealth?: number | undefined;
+}
+
+export class AggregateKey implements IAggregateKey {
+ aggregateParameterIndex?: number | undefined;
+ brushIndex?: number | undefined;
+
+ constructor(data?: IAggregateKey) {
+ if (data) {
+ for (var property in data) {
+ if (data.hasOwnProperty(property))
+ (<any>this)[property] = (<any>data)[property];
+ }
+ }
+ }
+
+ init(data?: any) {
+ if (data) {
+ this.aggregateParameterIndex = data["AggregateParameterIndex"];
+ this.brushIndex = data["BrushIndex"];
+ }
+ }
+
+ static fromJS(data: any): AggregateKey {
+ data = typeof data === 'object' ? data : {};
+ let result = new AggregateKey();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["AggregateParameterIndex"] = this.aggregateParameterIndex;
+ data["BrushIndex"] = this.brushIndex;
+ return data;
+ }
+}
+
+export interface IAggregateKey {
+ aggregateParameterIndex?: number | undefined;
+ brushIndex?: number | undefined;
+}
+
+export abstract class IResult implements IIResult {
+
+ constructor(data?: IIResult) {
+ if (data) {
+ for (var property in data) {
+ if (data.hasOwnProperty(property))
+ (<any>this)[property] = (<any>data)[property];
+ }
+ }
+ }
+
+ init(data?: any) {
+ if (data) {
+ }
+ }
+
+ static fromJS(data: any): IResult {
+ data = typeof data === 'object' ? data : {};
+ throw new Error("The abstract class 'IResult' cannot be instantiated.");
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ return data;
+ }
+}
+
+export interface IIResult {
+}
+
+export class DataArgument extends PrimitiveStepArgument implements IDataArgument {
+ data?: string | undefined;
+
+ constructor(data?: IDataArgument) {
+ super(data);
+ this._discriminator = "DataArgument";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ this.data = data["Data"];
+ }
+ }
+
+ static fromJS(data: any): DataArgument {
+ data = typeof data === 'object' ? data : {};
+ let result = new DataArgument();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["Data"] = this.data;
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IDataArgument extends IPrimitiveStepArgument {
+ data?: string | undefined;
+}
+
+export class ListValue extends Value implements IListValue {
+ items?: Value[] | undefined;
+
+ constructor(data?: IListValue) {
+ super(data);
+ this._discriminator = "ListValue";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ if (data["Items"] && data["Items"].constructor === Array) {
+ this.items = [];
+ for (let item of data["Items"])
+ this.items.push(Value.fromJS(item));
+ }
+ }
+ }
+
+ static fromJS(data: any): ListValue {
+ data = typeof data === 'object' ? data : {};
+ let result = new ListValue();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ if (this.items && this.items.constructor === Array) {
+ data["Items"] = [];
+ for (let item of this.items)
+ data["Items"].push(item.toJSON());
+ }
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IListValue extends IValue {
+ items?: Value[] | undefined;
+}
+
+export class Metrics implements IMetrics {
+ averageAccuracy?: number | undefined;
+ averageRSquared?: number | undefined;
+ f1Macro?: number | undefined;
+
+ constructor(data?: IMetrics) {
+ if (data) {
+ for (var property in data) {
+ if (data.hasOwnProperty(property))
+ (<any>this)[property] = (<any>data)[property];
+ }
+ }
+ }
+
+ init(data?: any) {
+ if (data) {
+ this.averageAccuracy = data["AverageAccuracy"];
+ this.averageRSquared = data["AverageRSquared"];
+ this.f1Macro = data["F1Macro"];
+ }
+ }
+
+ static fromJS(data: any): Metrics {
+ data = typeof data === 'object' ? data : {};
+ let result = new Metrics();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["AverageAccuracy"] = this.averageAccuracy;
+ data["AverageRSquared"] = this.averageRSquared;
+ data["F1Macro"] = this.f1Macro;
+ return data;
+ }
+}
+
+export interface IMetrics {
+ averageAccuracy?: number | undefined;
+ averageRSquared?: number | undefined;
+ f1Macro?: number | undefined;
+}
+
+export class FeatureImportanceOperationParameters extends DistOperationParameters implements IFeatureImportanceOperationParameters {
+ solutionId?: string | undefined;
+
+ constructor(data?: IFeatureImportanceOperationParameters) {
+ super(data);
+ this._discriminator = "FeatureImportanceOperationParameters";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ this.solutionId = data["SolutionId"];
+ }
+ }
+
+ static fromJS(data: any): FeatureImportanceOperationParameters {
+ data = typeof data === 'object' ? data : {};
+ let result = new FeatureImportanceOperationParameters();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["SolutionId"] = this.solutionId;
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IFeatureImportanceOperationParameters extends IDistOperationParameters {
+ solutionId?: string | undefined;
+}
+
+export class FeatureImportanceResult extends Result implements IFeatureImportanceResult {
+ featureImportances?: { [key: string]: TupleOfDoubleAndDouble; } | undefined;
+
+ constructor(data?: IFeatureImportanceResult) {
+ super(data);
+ this._discriminator = "FeatureImportanceResult";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ if (data["FeatureImportances"]) {
+ this.featureImportances = {};
+ for (let key in data["FeatureImportances"]) {
+ if (data["FeatureImportances"].hasOwnProperty(key))
+ this.featureImportances[key] = data["FeatureImportances"][key] ? TupleOfDoubleAndDouble.fromJS(data["FeatureImportances"][key]) : new TupleOfDoubleAndDouble();
+ }
+ }
+ }
+ }
+
+ static fromJS(data: any): FeatureImportanceResult {
+ data = typeof data === 'object' ? data : {};
+ let result = new FeatureImportanceResult();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ if (this.featureImportances) {
+ data["FeatureImportances"] = {};
+ for (let key in this.featureImportances) {
+ if (this.featureImportances.hasOwnProperty(key))
+ data["FeatureImportances"][key] = this.featureImportances[key];
+ }
+ }
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IFeatureImportanceResult extends IResult {
+ featureImportances?: { [key: string]: TupleOfDoubleAndDouble; } | undefined;
+}
+
+export class TupleOfDoubleAndDouble implements ITupleOfDoubleAndDouble {
+ item1?: number | undefined;
+ item2?: number | undefined;
+
+ constructor(data?: ITupleOfDoubleAndDouble) {
+ if (data) {
+ for (var property in data) {
+ if (data.hasOwnProperty(property))
+ (<any>this)[property] = (<any>data)[property];
+ }
+ }
+ }
+
+ init(data?: any) {
+ if (data) {
+ this.item1 = data["Item1"];
+ this.item2 = data["Item2"];
+ }
+ }
+
+ static fromJS(data: any): TupleOfDoubleAndDouble {
+ data = typeof data === 'object' ? data : {};
+ let result = new TupleOfDoubleAndDouble();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["Item1"] = this.item1;
+ data["Item2"] = this.item2;
+ return data;
+ }
+}
+
+export interface ITupleOfDoubleAndDouble {
+ item1?: number | undefined;
+ item2?: number | undefined;
+}
+
+export class PrimitiveStepDescription extends StepDescription implements IPrimitiveStepDescription {
+ hyperparams?: { [key: string]: Value; } | undefined;
+
+ constructor(data?: IPrimitiveStepDescription) {
+ super(data);
+ this._discriminator = "PrimitiveStepDescription";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ if (data["Hyperparams"]) {
+ this.hyperparams = {};
+ for (let key in data["Hyperparams"]) {
+ if (data["Hyperparams"].hasOwnProperty(key))
+ this.hyperparams[key] = data["Hyperparams"][key] ? Value.fromJS(data["Hyperparams"][key]) : <any>undefined;
+ }
+ }
+ }
+ }
+
+ static fromJS(data: any): PrimitiveStepDescription {
+ data = typeof data === 'object' ? data : {};
+ let result = new PrimitiveStepDescription();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ if (this.hyperparams) {
+ data["Hyperparams"] = {};
+ for (let key in this.hyperparams) {
+ if (this.hyperparams.hasOwnProperty(key))
+ data["Hyperparams"][key] = this.hyperparams[key];
+ }
+ }
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IPrimitiveStepDescription extends IStepDescription {
+ hyperparams?: { [key: string]: Value; } | undefined;
+}
+
+export enum ValueType {
+ VALUE_TYPE_UNDEFINED = 0,
+ RAW = 1,
+ DATASET_URI = 2,
+ CSV_URI = 3,
+ PICKLE_URI = 4,
+ PICKLE_BLOB = 5,
+ PLASMA_ID = 6,
+}
+
+export class DatamartSearchParameters implements IDatamartSearchParameters {
+ adapterName?: string | undefined;
+ queryJson?: string | undefined;
+
+ constructor(data?: IDatamartSearchParameters) {
+ if (data) {
+ for (var property in data) {
+ if (data.hasOwnProperty(property))
+ (<any>this)[property] = (<any>data)[property];
+ }
+ }
+ }
+
+ init(data?: any) {
+ if (data) {
+ this.adapterName = data["AdapterName"];
+ this.queryJson = data["QueryJson"];
+ }
+ }
+
+ static fromJS(data: any): DatamartSearchParameters {
+ data = typeof data === 'object' ? data : {};
+ let result = new DatamartSearchParameters();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["AdapterName"] = this.adapterName;
+ data["QueryJson"] = this.queryJson;
+ return data;
+ }
+}
+
+export interface IDatamartSearchParameters {
+ adapterName?: string | undefined;
+ queryJson?: string | undefined;
+}
+
+export class DatamartAugmentParameters implements IDatamartAugmentParameters {
+ adapterName?: string | undefined;
+ augmentationJson?: string | undefined;
+ numberOfSamples?: number | undefined;
+ augmentedAdapterName?: string | undefined;
+
+ constructor(data?: IDatamartAugmentParameters) {
+ if (data) {
+ for (var property in data) {
+ if (data.hasOwnProperty(property))
+ (<any>this)[property] = (<any>data)[property];
+ }
+ }
+ }
+
+ init(data?: any) {
+ if (data) {
+ this.adapterName = data["AdapterName"];
+ this.augmentationJson = data["AugmentationJson"];
+ this.numberOfSamples = data["NumberOfSamples"];
+ this.augmentedAdapterName = data["AugmentedAdapterName"];
+ }
+ }
+
+ static fromJS(data: any): DatamartAugmentParameters {
+ data = typeof data === 'object' ? data : {};
+ let result = new DatamartAugmentParameters();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["AdapterName"] = this.adapterName;
+ data["AugmentationJson"] = this.augmentationJson;
+ data["NumberOfSamples"] = this.numberOfSamples;
+ data["AugmentedAdapterName"] = this.augmentedAdapterName;
+ return data;
+ }
+}
+
+export interface IDatamartAugmentParameters {
+ adapterName?: string | undefined;
+ augmentationJson?: string | undefined;
+ numberOfSamples?: number | undefined;
+ augmentedAdapterName?: string | undefined;
+}
+
+export class RawDataResult extends DistResult implements IRawDataResult {
+ samples?: { [key: string]: any[]; } | undefined;
+ weightedWords?: { [key: string]: Word[]; } | undefined;
+
+ constructor(data?: IRawDataResult) {
+ super(data);
+ this._discriminator = "RawDataResult";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ if (data["Samples"]) {
+ this.samples = {};
+ for (let key in data["Samples"]) {
+ if (data["Samples"].hasOwnProperty(key))
+ this.samples[key] = data["Samples"][key] !== undefined ? data["Samples"][key] : [];
+ }
+ }
+ if (data["WeightedWords"]) {
+ this.weightedWords = {};
+ for (let key in data["WeightedWords"]) {
+ if (data["WeightedWords"].hasOwnProperty(key))
+ this.weightedWords[key] = data["WeightedWords"][key] ? data["WeightedWords"][key].map((i: any) => Word.fromJS(i)) : [];
+ }
+ }
+ }
+ }
+
+ static fromJS(data: any): RawDataResult {
+ data = typeof data === 'object' ? data : {};
+ let result = new RawDataResult();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ if (this.samples) {
+ data["Samples"] = {};
+ for (let key in this.samples) {
+ if (this.samples.hasOwnProperty(key))
+ data["Samples"][key] = this.samples[key];
+ }
+ }
+ if (this.weightedWords) {
+ data["WeightedWords"] = {};
+ for (let key in this.weightedWords) {
+ if (this.weightedWords.hasOwnProperty(key))
+ data["WeightedWords"][key] = this.weightedWords[key];
+ }
+ }
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IRawDataResult extends IDistResult {
+ samples?: { [key: string]: any[]; } | undefined;
+ weightedWords?: { [key: string]: Word[]; } | undefined;
+}
+
+export class Word implements IWord {
+ text?: string | undefined;
+ occurrences?: number | undefined;
+ stem?: string | undefined;
+ isWordGroup?: boolean | undefined;
+
+ constructor(data?: IWord) {
+ if (data) {
+ for (var property in data) {
+ if (data.hasOwnProperty(property))
+ (<any>this)[property] = (<any>data)[property];
+ }
+ }
+ }
+
+ init(data?: any) {
+ if (data) {
+ this.text = data["Text"];
+ this.occurrences = data["Occurrences"];
+ this.stem = data["Stem"];
+ this.isWordGroup = data["IsWordGroup"];
+ }
+ }
+
+ static fromJS(data: any): Word {
+ data = typeof data === 'object' ? data : {};
+ let result = new Word();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["Text"] = this.text;
+ data["Occurrences"] = this.occurrences;
+ data["Stem"] = this.stem;
+ data["IsWordGroup"] = this.isWordGroup;
+ return data;
+ }
+}
+
+export interface IWord {
+ text?: string | undefined;
+ occurrences?: number | undefined;
+ stem?: string | undefined;
+ isWordGroup?: boolean | undefined;
+}
+
+export class SampleOperationParameters extends DistOperationParameters implements ISampleOperationParameters {
+ numSamples?: number | undefined;
+ attributeParameters?: AttributeParameters[] | undefined;
+ brushes?: string[] | undefined;
+
+ constructor(data?: ISampleOperationParameters) {
+ super(data);
+ this._discriminator = "SampleOperationParameters";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ this.numSamples = data["NumSamples"];
+ if (data["AttributeParameters"] && data["AttributeParameters"].constructor === Array) {
+ this.attributeParameters = [];
+ for (let item of data["AttributeParameters"])
+ this.attributeParameters.push(AttributeParameters.fromJS(item));
+ }
+ if (data["Brushes"] && data["Brushes"].constructor === Array) {
+ this.brushes = [];
+ for (let item of data["Brushes"])
+ this.brushes.push(item);
+ }
+ }
+ }
+
+ static fromJS(data: any): SampleOperationParameters {
+ data = typeof data === 'object' ? data : {};
+ let result = new SampleOperationParameters();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["NumSamples"] = this.numSamples;
+ if (this.attributeParameters && this.attributeParameters.constructor === Array) {
+ data["AttributeParameters"] = [];
+ for (let item of this.attributeParameters)
+ data["AttributeParameters"].push(item.toJSON());
+ }
+ if (this.brushes && this.brushes.constructor === Array) {
+ data["Brushes"] = [];
+ for (let item of this.brushes)
+ data["Brushes"].push(item);
+ }
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface ISampleOperationParameters extends IDistOperationParameters {
+ numSamples?: number | undefined;
+ attributeParameters?: AttributeParameters[] | undefined;
+ brushes?: string[] | undefined;
+}
+
+export class SampleResult extends DistResult implements ISampleResult {
+ samples?: { [key: string]: { [key: string]: number; }; } | undefined;
+ isTruncated?: boolean | undefined;
+
+ constructor(data?: ISampleResult) {
+ super(data);
+ this._discriminator = "SampleResult";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ if (data["Samples"]) {
+ this.samples = {};
+ for (let key in data["Samples"]) {
+ if (data["Samples"].hasOwnProperty(key))
+ this.samples[key] = data["Samples"][key] !== undefined ? data["Samples"][key] : {};
+ }
+ }
+ this.isTruncated = data["IsTruncated"];
+ }
+ }
+
+ static fromJS(data: any): SampleResult {
+ data = typeof data === 'object' ? data : {};
+ let result = new SampleResult();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ if (this.samples) {
+ data["Samples"] = {};
+ for (let key in this.samples) {
+ if (this.samples.hasOwnProperty(key))
+ data["Samples"][key] = this.samples[key];
+ }
+ }
+ data["IsTruncated"] = this.isTruncated;
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface ISampleResult extends IDistResult {
+ samples?: { [key: string]: { [key: string]: number; }; } | undefined;
+ isTruncated?: boolean | undefined;
+}
+
+export class ResultParameters extends UniqueJson implements IResultParameters {
+ operationReference?: IOperationReference | undefined;
+ stopOperation?: boolean | undefined;
+
+ protected _discriminator: string;
+
+ constructor(data?: IResultParameters) {
+ super(data);
+ this._discriminator = "ResultParameters";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ this.operationReference = data["OperationReference"] ? IOperationReference.fromJS(data["OperationReference"]) : <any>undefined;
+ this.stopOperation = data["StopOperation"];
+ }
+ }
+
+ static fromJS(data: any): ResultParameters {
+ data = typeof data === 'object' ? data : {};
+ if (data["discriminator"] === "RecommenderResultParameters") {
+ let result = new RecommenderResultParameters();
+ result.init(data);
+ return result;
+ }
+ let result = new ResultParameters();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["discriminator"] = this._discriminator;
+ data["OperationReference"] = this.operationReference ? this.operationReference.toJSON() : <any>undefined;
+ data["StopOperation"] = this.stopOperation;
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IResultParameters extends IUniqueJson {
+ operationReference?: IOperationReference | undefined;
+ stopOperation?: boolean | undefined;
+}
+
+export class RecommenderResultParameters extends ResultParameters implements IRecommenderResultParameters {
+ from?: number | undefined;
+ to?: number | undefined;
+ pValueSorting?: Sorting | undefined;
+ effectSizeFilter?: EffectSize | undefined;
+
+ constructor(data?: IRecommenderResultParameters) {
+ super(data);
+ this._discriminator = "RecommenderResultParameters";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ this.from = data["From"];
+ this.to = data["To"];
+ this.pValueSorting = data["PValueSorting"];
+ this.effectSizeFilter = data["EffectSizeFilter"];
+ }
+ }
+
+ static fromJS(data: any): RecommenderResultParameters {
+ data = typeof data === 'object' ? data : {};
+ let result = new RecommenderResultParameters();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["From"] = this.from;
+ data["To"] = this.to;
+ data["PValueSorting"] = this.pValueSorting;
+ data["EffectSizeFilter"] = this.effectSizeFilter;
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IRecommenderResultParameters extends IResultParameters {
+ from?: number | undefined;
+ to?: number | undefined;
+ pValueSorting?: Sorting | undefined;
+ effectSizeFilter?: EffectSize | undefined;
+}
+
+export enum Sorting {
+ Ascending = "Ascending",
+ Descending = "Descending",
+}
+
+export class AddComparisonParameters extends ModelOperationParameters implements IAddComparisonParameters {
+ modelId?: ModelId | undefined;
+ comparisonOrder?: number | undefined;
+ childOperationParameters?: OperationParameters[] | undefined;
+ isCachable?: boolean | undefined;
+
+ constructor(data?: IAddComparisonParameters) {
+ super(data);
+ this._discriminator = "AddComparisonParameters";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ this.modelId = data["ModelId"] ? ModelId.fromJS(data["ModelId"]) : <any>undefined;
+ this.comparisonOrder = data["ComparisonOrder"];
+ if (data["ChildOperationParameters"] && data["ChildOperationParameters"].constructor === Array) {
+ this.childOperationParameters = [];
+ for (let item of data["ChildOperationParameters"])
+ this.childOperationParameters.push(OperationParameters.fromJS(item));
+ }
+ this.isCachable = data["IsCachable"];
+ }
+ }
+
+ static fromJS(data: any): AddComparisonParameters {
+ data = typeof data === 'object' ? data : {};
+ let result = new AddComparisonParameters();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["ModelId"] = this.modelId ? this.modelId.toJSON() : <any>undefined;
+ data["ComparisonOrder"] = this.comparisonOrder;
+ if (this.childOperationParameters && this.childOperationParameters.constructor === Array) {
+ data["ChildOperationParameters"] = [];
+ for (let item of this.childOperationParameters)
+ data["ChildOperationParameters"].push(item.toJSON());
+ }
+ data["IsCachable"] = this.isCachable;
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IAddComparisonParameters extends IModelOperationParameters {
+ modelId?: ModelId | undefined;
+ comparisonOrder?: number | undefined;
+ childOperationParameters?: OperationParameters[] | undefined;
+ isCachable?: boolean | undefined;
+}
+
+export class CDFResult extends DistResult implements ICDFResult {
+ cDF?: { [key: string]: number; } | undefined;
+
+ constructor(data?: ICDFResult) {
+ super(data);
+ this._discriminator = "CDFResult";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ if (data["CDF"]) {
+ this.cDF = {};
+ for (let key in data["CDF"]) {
+ if (data["CDF"].hasOwnProperty(key))
+ this.cDF[key] = data["CDF"][key];
+ }
+ }
+ }
+ }
+
+ static fromJS(data: any): CDFResult {
+ data = typeof data === 'object' ? data : {};
+ let result = new CDFResult();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ if (this.cDF) {
+ data["CDF"] = {};
+ for (let key in this.cDF) {
+ if (this.cDF.hasOwnProperty(key))
+ data["CDF"][key] = this.cDF[key];
+ }
+ }
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface ICDFResult extends IDistResult {
+ cDF?: { [key: string]: number; } | undefined;
+}
+
+export class ChiSquaredTestResult extends HypothesisTestResult implements IChiSquaredTestResult {
+ hs_aligned?: TupleOfDoubleAndDouble[] | undefined;
+
+ constructor(data?: IChiSquaredTestResult) {
+ super(data);
+ this._discriminator = "ChiSquaredTestResult";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ if (data["hs_aligned"] && data["hs_aligned"].constructor === Array) {
+ this.hs_aligned = [];
+ for (let item of data["hs_aligned"])
+ this.hs_aligned.push(TupleOfDoubleAndDouble.fromJS(item));
+ }
+ }
+ }
+
+ static fromJS(data: any): ChiSquaredTestResult {
+ data = typeof data === 'object' ? data : {};
+ let result = new ChiSquaredTestResult();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ if (this.hs_aligned && this.hs_aligned.constructor === Array) {
+ data["hs_aligned"] = [];
+ for (let item of this.hs_aligned)
+ data["hs_aligned"].push(item.toJSON());
+ }
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IChiSquaredTestResult extends IHypothesisTestResult {
+ hs_aligned?: TupleOfDoubleAndDouble[] | undefined;
+}
+
+export class CorrelationTestResult extends HypothesisTestResult implements ICorrelationTestResult {
+ degreeOfFreedom?: number | undefined;
+ sampleCorrelationCoefficient?: number | undefined;
+ distResult?: EmpiricalDistResult | undefined;
+
+ constructor(data?: ICorrelationTestResult) {
+ super(data);
+ this._discriminator = "CorrelationTestResult";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ this.degreeOfFreedom = data["DegreeOfFreedom"];
+ this.sampleCorrelationCoefficient = data["SampleCorrelationCoefficient"];
+ this.distResult = data["DistResult"] ? EmpiricalDistResult.fromJS(data["DistResult"]) : <any>undefined;
+ }
+ }
+
+ static fromJS(data: any): CorrelationTestResult {
+ data = typeof data === 'object' ? data : {};
+ let result = new CorrelationTestResult();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["DegreeOfFreedom"] = this.degreeOfFreedom;
+ data["SampleCorrelationCoefficient"] = this.sampleCorrelationCoefficient;
+ data["DistResult"] = this.distResult ? this.distResult.toJSON() : <any>undefined;
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface ICorrelationTestResult extends IHypothesisTestResult {
+ degreeOfFreedom?: number | undefined;
+ sampleCorrelationCoefficient?: number | undefined;
+ distResult?: EmpiricalDistResult | undefined;
+}
+
+export class EmpiricalDistResult extends DistResult implements IEmpiricalDistResult {
+ marginals?: AttributeParameters[] | undefined;
+ marginalDistParameters?: { [key: string]: DistParameter; } | undefined;
+ jointDistParameter?: JointDistParameter | undefined;
+
+ constructor(data?: IEmpiricalDistResult) {
+ super(data);
+ this._discriminator = "EmpiricalDistResult";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ if (data["Marginals"] && data["Marginals"].constructor === Array) {
+ this.marginals = [];
+ for (let item of data["Marginals"])
+ this.marginals.push(AttributeParameters.fromJS(item));
+ }
+ if (data["MarginalDistParameters"]) {
+ this.marginalDistParameters = {};
+ for (let key in data["MarginalDistParameters"]) {
+ if (data["MarginalDistParameters"].hasOwnProperty(key))
+ this.marginalDistParameters[key] = data["MarginalDistParameters"][key] ? DistParameter.fromJS(data["MarginalDistParameters"][key]) : new DistParameter();
+ }
+ }
+ this.jointDistParameter = data["JointDistParameter"] ? JointDistParameter.fromJS(data["JointDistParameter"]) : <any>undefined;
+ }
+ }
+
+ static fromJS(data: any): EmpiricalDistResult {
+ data = typeof data === 'object' ? data : {};
+ let result = new EmpiricalDistResult();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ if (this.marginals && this.marginals.constructor === Array) {
+ data["Marginals"] = [];
+ for (let item of this.marginals)
+ data["Marginals"].push(item.toJSON());
+ }
+ if (this.marginalDistParameters) {
+ data["MarginalDistParameters"] = {};
+ for (let key in this.marginalDistParameters) {
+ if (this.marginalDistParameters.hasOwnProperty(key))
+ data["MarginalDistParameters"][key] = this.marginalDistParameters[key];
+ }
+ }
+ data["JointDistParameter"] = this.jointDistParameter ? this.jointDistParameter.toJSON() : <any>undefined;
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IEmpiricalDistResult extends IDistResult {
+ marginals?: AttributeParameters[] | undefined;
+ marginalDistParameters?: { [key: string]: DistParameter; } | undefined;
+ jointDistParameter?: JointDistParameter | undefined;
+}
+
+export class DistParameter implements IDistParameter {
+ mean?: number | undefined;
+ moment2?: number | undefined;
+ variance?: number | undefined;
+ varianceEstimate?: number | undefined;
+ min?: number | undefined;
+ max?: number | undefined;
+
+ constructor(data?: IDistParameter) {
+ if (data) {
+ for (var property in data) {
+ if (data.hasOwnProperty(property))
+ (<any>this)[property] = (<any>data)[property];
+ }
+ }
+ }
+
+ init(data?: any) {
+ if (data) {
+ this.mean = data["Mean"];
+ this.moment2 = data["Moment2"];
+ this.variance = data["Variance"];
+ this.varianceEstimate = data["VarianceEstimate"];
+ this.min = data["Min"];
+ this.max = data["Max"];
+ }
+ }
+
+ static fromJS(data: any): DistParameter {
+ data = typeof data === 'object' ? data : {};
+ let result = new DistParameter();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["Mean"] = this.mean;
+ data["Moment2"] = this.moment2;
+ data["Variance"] = this.variance;
+ data["VarianceEstimate"] = this.varianceEstimate;
+ data["Min"] = this.min;
+ data["Max"] = this.max;
+ return data;
+ }
+}
+
+export interface IDistParameter {
+ mean?: number | undefined;
+ moment2?: number | undefined;
+ variance?: number | undefined;
+ varianceEstimate?: number | undefined;
+ min?: number | undefined;
+ max?: number | undefined;
+}
+
+export class JointDistParameter implements IJointDistParameter {
+ jointDist?: DistParameter | undefined;
+ covariance?: number | undefined;
+
+ constructor(data?: IJointDistParameter) {
+ if (data) {
+ for (var property in data) {
+ if (data.hasOwnProperty(property))
+ (<any>this)[property] = (<any>data)[property];
+ }
+ }
+ }
+
+ init(data?: any) {
+ if (data) {
+ this.jointDist = data["JointDist"] ? DistParameter.fromJS(data["JointDist"]) : <any>undefined;
+ this.covariance = data["Covariance"];
+ }
+ }
+
+ static fromJS(data: any): JointDistParameter {
+ data = typeof data === 'object' ? data : {};
+ let result = new JointDistParameter();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["JointDist"] = this.jointDist ? this.jointDist.toJSON() : <any>undefined;
+ data["Covariance"] = this.covariance;
+ return data;
+ }
+}
+
+export interface IJointDistParameter {
+ jointDist?: DistParameter | undefined;
+ covariance?: number | undefined;
+}
+
+export enum DistributionType {
+ Continuous = 0,
+ Discrete = 1,
+}
+
+export abstract class DistributionTypeExtension implements IDistributionTypeExtension {
+
+ constructor(data?: IDistributionTypeExtension) {
+ if (data) {
+ for (var property in data) {
+ if (data.hasOwnProperty(property))
+ (<any>this)[property] = (<any>data)[property];
+ }
+ }
+ }
+
+ init(data?: any) {
+ if (data) {
+ }
+ }
+
+ static fromJS(data: any): DistributionTypeExtension {
+ data = typeof data === 'object' ? data : {};
+ throw new Error("The abstract class 'DistributionTypeExtension' cannot be instantiated.");
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ return data;
+ }
+}
+
+export interface IDistributionTypeExtension {
+}
+
+export class GetModelStateParameters extends ModelOperationParameters implements IGetModelStateParameters {
+ modelId?: ModelId | undefined;
+ comparisonIds?: ComparisonId[] | undefined;
+ riskControlType?: RiskControlType | undefined;
+
+ constructor(data?: IGetModelStateParameters) {
+ super(data);
+ this._discriminator = "GetModelStateParameters";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ this.modelId = data["ModelId"] ? ModelId.fromJS(data["ModelId"]) : <any>undefined;
+ if (data["ComparisonIds"] && data["ComparisonIds"].constructor === Array) {
+ this.comparisonIds = [];
+ for (let item of data["ComparisonIds"])
+ this.comparisonIds.push(ComparisonId.fromJS(item));
+ }
+ this.riskControlType = data["RiskControlType"];
+ }
+ }
+
+ static fromJS(data: any): GetModelStateParameters {
+ data = typeof data === 'object' ? data : {};
+ let result = new GetModelStateParameters();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["ModelId"] = this.modelId ? this.modelId.toJSON() : <any>undefined;
+ if (this.comparisonIds && this.comparisonIds.constructor === Array) {
+ data["ComparisonIds"] = [];
+ for (let item of this.comparisonIds)
+ data["ComparisonIds"].push(item.toJSON());
+ }
+ data["RiskControlType"] = this.riskControlType;
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IGetModelStateParameters extends IModelOperationParameters {
+ modelId?: ModelId | undefined;
+ comparisonIds?: ComparisonId[] | undefined;
+ riskControlType?: RiskControlType | undefined;
+}
+
+export class KSTestResult extends HypothesisTestResult implements IKSTestResult {
+
+ constructor(data?: IKSTestResult) {
+ super(data);
+ this._discriminator = "KSTestResult";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ }
+ }
+
+ static fromJS(data: any): KSTestResult {
+ data = typeof data === 'object' ? data : {};
+ let result = new KSTestResult();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IKSTestResult extends IHypothesisTestResult {
+}
+
+export class ModelWealthParameters extends UniqueJson implements IModelWealthParameters {
+ modelId?: ModelId | undefined;
+ riskControlType?: RiskControlType | undefined;
+
+ constructor(data?: IModelWealthParameters) {
+ super(data);
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ this.modelId = data["ModelId"] ? ModelId.fromJS(data["ModelId"]) : <any>undefined;
+ this.riskControlType = data["RiskControlType"];
+ }
+ }
+
+ static fromJS(data: any): ModelWealthParameters {
+ data = typeof data === 'object' ? data : {};
+ let result = new ModelWealthParameters();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["ModelId"] = this.modelId ? this.modelId.toJSON() : <any>undefined;
+ data["RiskControlType"] = this.riskControlType;
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IModelWealthParameters extends IUniqueJson {
+ modelId?: ModelId | undefined;
+ riskControlType?: RiskControlType | undefined;
+}
+
+export class RootMeanSquareTestResult extends HypothesisTestResult implements IRootMeanSquareTestResult {
+ simulationCount?: number | undefined;
+ extremeSimulationCount?: number | undefined;
+
+ constructor(data?: IRootMeanSquareTestResult) {
+ super(data);
+ this._discriminator = "RootMeanSquareTestResult";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ this.simulationCount = data["SimulationCount"];
+ this.extremeSimulationCount = data["ExtremeSimulationCount"];
+ }
+ }
+
+ static fromJS(data: any): RootMeanSquareTestResult {
+ data = typeof data === 'object' ? data : {};
+ let result = new RootMeanSquareTestResult();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["SimulationCount"] = this.simulationCount;
+ data["ExtremeSimulationCount"] = this.extremeSimulationCount;
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IRootMeanSquareTestResult extends IHypothesisTestResult {
+ simulationCount?: number | undefined;
+ extremeSimulationCount?: number | undefined;
+}
+
+export class TTestResult extends HypothesisTestResult implements ITTestResult {
+ degreeOfFreedom?: number | undefined;
+ distResults?: EmpiricalDistResult[] | undefined;
+
+ constructor(data?: ITTestResult) {
+ super(data);
+ this._discriminator = "TTestResult";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ this.degreeOfFreedom = data["DegreeOfFreedom"];
+ if (data["DistResults"] && data["DistResults"].constructor === Array) {
+ this.distResults = [];
+ for (let item of data["DistResults"])
+ this.distResults.push(EmpiricalDistResult.fromJS(item));
+ }
+ }
+ }
+
+ static fromJS(data: any): TTestResult {
+ data = typeof data === 'object' ? data : {};
+ let result = new TTestResult();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["DegreeOfFreedom"] = this.degreeOfFreedom;
+ if (this.distResults && this.distResults.constructor === Array) {
+ data["DistResults"] = [];
+ for (let item of this.distResults)
+ data["DistResults"].push(item.toJSON());
+ }
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface ITTestResult extends IHypothesisTestResult {
+ degreeOfFreedom?: number | undefined;
+ distResults?: EmpiricalDistResult[] | undefined;
+}
+
+export enum Sorting2 {
+ Ascending = 0,
+ Descending = 1,
+}
+
+export class BinLabel implements IBinLabel {
+ value?: number | undefined;
+ minValue?: number | undefined;
+ maxValue?: number | undefined;
+ label?: string | undefined;
+
+ constructor(data?: IBinLabel) {
+ if (data) {
+ for (var property in data) {
+ if (data.hasOwnProperty(property))
+ (<any>this)[property] = (<any>data)[property];
+ }
+ }
+ }
+
+ init(data?: any) {
+ if (data) {
+ this.value = data["Value"];
+ this.minValue = data["MinValue"];
+ this.maxValue = data["MaxValue"];
+ this.label = data["Label"];
+ }
+ }
+
+ static fromJS(data: any): BinLabel {
+ data = typeof data === 'object' ? data : {};
+ let result = new BinLabel();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["Value"] = this.value;
+ data["MinValue"] = this.minValue;
+ data["MaxValue"] = this.maxValue;
+ data["Label"] = this.label;
+ return data;
+ }
+}
+
+export interface IBinLabel {
+ value?: number | undefined;
+ minValue?: number | undefined;
+ maxValue?: number | undefined;
+ label?: string | undefined;
+}
+
+export class PreProcessedString implements IPreProcessedString {
+ value?: string | undefined;
+ id?: number | undefined;
+ stringLookup?: { [key: string]: number; } | undefined;
+ indexLookup?: { [key: string]: string; } | undefined;
+
+ constructor(data?: IPreProcessedString) {
+ if (data) {
+ for (var property in data) {
+ if (data.hasOwnProperty(property))
+ (<any>this)[property] = (<any>data)[property];
+ }
+ }
+ }
+
+ init(data?: any) {
+ if (data) {
+ this.value = data["Value"];
+ this.id = data["Id"];
+ if (data["StringLookup"]) {
+ this.stringLookup = {};
+ for (let key in data["StringLookup"]) {
+ if (data["StringLookup"].hasOwnProperty(key))
+ this.stringLookup[key] = data["StringLookup"][key];
+ }
+ }
+ if (data["IndexLookup"]) {
+ this.indexLookup = {};
+ for (let key in data["IndexLookup"]) {
+ if (data["IndexLookup"].hasOwnProperty(key))
+ this.indexLookup[key] = data["IndexLookup"][key];
+ }
+ }
+ }
+ }
+
+ static fromJS(data: any): PreProcessedString {
+ data = typeof data === 'object' ? data : {};
+ let result = new PreProcessedString();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["Value"] = this.value;
+ data["Id"] = this.id;
+ if (this.stringLookup) {
+ data["StringLookup"] = {};
+ for (let key in this.stringLookup) {
+ if (this.stringLookup.hasOwnProperty(key))
+ data["StringLookup"][key] = this.stringLookup[key];
+ }
+ }
+ if (this.indexLookup) {
+ data["IndexLookup"] = {};
+ for (let key in this.indexLookup) {
+ if (this.indexLookup.hasOwnProperty(key))
+ data["IndexLookup"][key] = this.indexLookup[key];
+ }
+ }
+ return data;
+ }
+}
+
+export interface IPreProcessedString {
+ value?: string | undefined;
+ id?: number | undefined;
+ stringLookup?: { [key: string]: number; } | undefined;
+ indexLookup?: { [key: string]: string; } | undefined;
+}
+
+export class BitSet implements IBitSet {
+ length?: number | undefined;
+ size?: number | undefined;
+
+ constructor(data?: IBitSet) {
+ if (data) {
+ for (var property in data) {
+ if (data.hasOwnProperty(property))
+ (<any>this)[property] = (<any>data)[property];
+ }
+ }
+ }
+
+ init(data?: any) {
+ if (data) {
+ this.length = data["Length"];
+ this.size = data["Size"];
+ }
+ }
+
+ static fromJS(data: any): BitSet {
+ data = typeof data === 'object' ? data : {};
+ let result = new BitSet();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["Length"] = this.length;
+ data["Size"] = this.size;
+ return data;
+ }
+}
+
+export interface IBitSet {
+ length?: number | undefined;
+ size?: number | undefined;
+}
+
+export class DateTimeUtil implements IDateTimeUtil {
+
+ constructor(data?: IDateTimeUtil) {
+ if (data) {
+ for (var property in data) {
+ if (data.hasOwnProperty(property))
+ (<any>this)[property] = (<any>data)[property];
+ }
+ }
+ }
+
+ init(data?: any) {
+ if (data) {
+ }
+ }
+
+ static fromJS(data: any): DateTimeUtil {
+ data = typeof data === 'object' ? data : {};
+ let result = new DateTimeUtil();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ return data;
+ }
+}
+
+export interface IDateTimeUtil {
+}
+
+export class FrequentItemsetOperationParameters extends DistOperationParameters implements IFrequentItemsetOperationParameters {
+ filter?: string | undefined;
+ attributeParameters?: AttributeParameters[] | undefined;
+ attributeCodeParameters?: AttributeCaclculatedParameters[] | undefined;
+
+ constructor(data?: IFrequentItemsetOperationParameters) {
+ super(data);
+ this._discriminator = "FrequentItemsetOperationParameters";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ this.filter = data["Filter"];
+ if (data["AttributeParameters"] && data["AttributeParameters"].constructor === Array) {
+ this.attributeParameters = [];
+ for (let item of data["AttributeParameters"])
+ this.attributeParameters.push(AttributeParameters.fromJS(item));
+ }
+ if (data["AttributeCodeParameters"] && data["AttributeCodeParameters"].constructor === Array) {
+ this.attributeCodeParameters = [];
+ for (let item of data["AttributeCodeParameters"])
+ this.attributeCodeParameters.push(AttributeCaclculatedParameters.fromJS(item));
+ }
+ }
+ }
+
+ static fromJS(data: any): FrequentItemsetOperationParameters {
+ data = typeof data === 'object' ? data : {};
+ let result = new FrequentItemsetOperationParameters();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ data["Filter"] = this.filter;
+ if (this.attributeParameters && this.attributeParameters.constructor === Array) {
+ data["AttributeParameters"] = [];
+ for (let item of this.attributeParameters)
+ data["AttributeParameters"].push(item.toJSON());
+ }
+ if (this.attributeCodeParameters && this.attributeCodeParameters.constructor === Array) {
+ data["AttributeCodeParameters"] = [];
+ for (let item of this.attributeCodeParameters)
+ data["AttributeCodeParameters"].push(item.toJSON());
+ }
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IFrequentItemsetOperationParameters extends IDistOperationParameters {
+ filter?: string | undefined;
+ attributeParameters?: AttributeParameters[] | undefined;
+ attributeCodeParameters?: AttributeCaclculatedParameters[] | undefined;
+}
+
+export class FrequentItemsetResult extends Result implements IFrequentItemsetResult {
+ frequentItems?: { [key: string]: number; } | undefined;
+
+ constructor(data?: IFrequentItemsetResult) {
+ super(data);
+ this._discriminator = "FrequentItemsetResult";
+ }
+
+ init(data?: any) {
+ super.init(data);
+ if (data) {
+ if (data["FrequentItems"]) {
+ this.frequentItems = {};
+ for (let key in data["FrequentItems"]) {
+ if (data["FrequentItems"].hasOwnProperty(key))
+ this.frequentItems[key] = data["FrequentItems"][key];
+ }
+ }
+ }
+ }
+
+ static fromJS(data: any): FrequentItemsetResult {
+ data = typeof data === 'object' ? data : {};
+ let result = new FrequentItemsetResult();
+ result.init(data);
+ return result;
+ }
+
+ toJSON(data?: any) {
+ data = typeof data === 'object' ? data : {};
+ if (this.frequentItems) {
+ data["FrequentItems"] = {};
+ for (let key in this.frequentItems) {
+ if (this.frequentItems.hasOwnProperty(key))
+ data["FrequentItems"][key] = this.frequentItems[key];
+ }
+ }
+ super.toJSON(data);
+ return data;
+ }
+}
+
+export interface IFrequentItemsetResult extends IResult {
+ frequentItems?: { [key: string]: number; } | undefined;
+}
+
diff --git a/src/client/northstar/operations/BaseOperation.ts b/src/client/northstar/operations/BaseOperation.ts
new file mode 100644
index 000000000..f545b2c58
--- /dev/null
+++ b/src/client/northstar/operations/BaseOperation.ts
@@ -0,0 +1,178 @@
+import { FilterModel } from '../core/filter/FilterModel'
+import { ErrorResult, Exception, OperationParameters, OperationReference, Result, ResultParameters } from '../model/idea/idea';
+import { action, computed, observable } from "mobx";
+import { Gateway } from '../manager/Gateway';
+
+export abstract class BaseOperation {
+ private _interactionTimeoutId: number = 0;
+ private static _currentOperations: Map<number, PollPromise> = new Map<number, PollPromise>();
+ //public InteractionTimeout: EventDelegate<InteractionTimeoutEventArgs> = new EventDelegate<InteractionTimeoutEventArgs>();
+
+ @observable public Error: string = "";
+ @observable public OverridingFilters: FilterModel[] = [];
+ //@observable
+ @observable public Result?: Result = undefined;
+ @observable public ComputationStarted: boolean = false;
+ public OperationReference?: OperationReference = undefined;
+
+ private static _nextId = 0;
+ public RequestSalt: string = "";
+ public Id: number;
+
+ constructor() {
+ this.Id = BaseOperation._nextId++;
+ }
+
+ @computed
+ public get FilterString(): string {
+
+ // let filterModels: FilterModel[] = [];
+ // return FilterModel.GetFilterModelsRecursive(this, new Set<GraphNode<BaseOperationViewModel, FilterLinkViewModel>>(), filterModels, true)
+ // if (this.OverridingFilters.length > 0) {
+ // return "(" + this.OverridingFilters.filter(fm => fm != null).map(fm => fm.ToPythonString()).join(" || ") + ")";
+ // }
+ // let rdg = MainManager.Instance.MainViewModel.FilterReverseDependencyGraph;
+ // let sliceModel = this.TypedViewModel.IncomingSliceModel;
+ // if (sliceModel != null && sliceModel.Source != null && instanceOfIBaseFilterProvider(sliceModel.Source) && rdg.has(sliceModel.Source)) {
+ // let filterModels = sliceModel.Source.FilterModels.map(f => f);
+ // return FilterModel.GetFilterModelsRecursive(rdg.get(sliceModel.Source), new Set<GraphNode<BaseOperationViewModel, FilterLinkViewModel>>(), filterModels, false);
+ // }
+
+ // if (rdg.has(this.TypedViewModel)) {
+ // let filterModels = [];
+ // return FilterModel.GetFilterModelsRecursive(rdg.get(this.TypedViewModel), new Set<GraphNode<BaseOperationViewModel, FilterLinkViewModel>>(), filterModels, true)
+ // }
+ return "";
+ }
+
+
+ @action
+ public SetResult(result: Result): void {
+ this.Result = result;
+ }
+
+ public async Update(): Promise<void> {
+
+ try {
+ if (BaseOperation._currentOperations.has(this.Id)) {
+ BaseOperation._currentOperations.get(this.Id)!.Cancel();
+ if (this.OperationReference) {
+ Gateway.Instance.PauseOperation(this.OperationReference.toJSON());
+ }
+ }
+
+ let operationParameters = this.CreateOperationParameters();
+ if (this.Result)
+ this.Result.progress = 0; // bcz: used to set Result to undefined, but that causes the display to blink
+ this.Error = "";
+ let salt = Math.random().toString();
+ this.RequestSalt = salt;
+
+ if (!operationParameters) {
+ this.ComputationStarted = false;
+ return;
+ }
+
+ this.ComputationStarted = true;
+ //let start = performance.now();
+ let promise = Gateway.Instance.StartOperation(operationParameters.toJSON());
+ promise.catch(err => {
+ action(() => {
+ this.Error = err;
+ console.error(err);
+ });
+ });
+ let operationReference = await promise;
+
+
+ if (operationReference) {
+ this.OperationReference = operationReference;
+
+ let resultParameters = new ResultParameters();
+ resultParameters.operationReference = operationReference;
+
+ let pollPromise = new PollPromise(salt, operationReference);
+ BaseOperation._currentOperations.set(this.Id, pollPromise);
+
+ pollPromise.Start(async () => {
+ let result = await Gateway.Instance.GetResult(resultParameters.toJSON());
+ if (result instanceof ErrorResult) {
+ throw new Error((result as ErrorResult).message);
+ }
+ if (this.RequestSalt == pollPromise.RequestSalt) {
+ if (result && (!this.Result || this.Result.progress != result.progress)) {
+ /*if (operationViewModel.Result !== null && operationViewModel.Result !== undefined) {
+ let t1 = performance.now();
+ console.log((t1 - start) + " milliseconds.");
+ start = performance.now();
+ }*/
+ this.SetResult(result);
+ }
+
+ if (!result || result.progress! < 1) {
+ return true;
+ }
+ }
+ return false;
+ }, 100).catch((err: Error) => action(() => {
+ this.Error = err.message;
+ console.error(err.message);
+ })()
+ );
+ }
+ }
+ catch (err) {
+ console.error(err as Exception);
+ // ErrorDialog.Instance.HandleError(err, operationViewModel);
+ }
+ }
+
+ public CreateOperationParameters(): OperationParameters | undefined { return undefined; }
+
+ private interactionTimeout() {
+ // clearTimeout(this._interactionTimeoutId);
+ // this.InteractionTimeout.Fire(new InteractionTimeoutEventArgs(this.TypedViewModel, InteractionTimeoutType.Timeout));
+ }
+}
+
+export class PollPromise {
+ public RequestSalt: string;
+ public OperationReference: OperationReference;
+
+ private _notCanceled: boolean = true;
+ private _poll: undefined | (() => Promise<boolean>);
+ private _delay: number = 0;
+
+ public constructor(requestKey: string, operationReference: OperationReference) {
+ this.RequestSalt = requestKey;
+ this.OperationReference = operationReference;
+ }
+
+ public Cancel(): void {
+ this._notCanceled = false;
+ }
+
+ public Start(poll: () => Promise<boolean>, delay: number): Promise<void> {
+ this._poll = poll;
+ this._delay = delay;
+ return this.pollRecursive();
+ }
+
+ private pollRecursive = (): Promise<void> => {
+ return Promise.resolve().then(this._poll).then((flag) => {
+ this._notCanceled && flag && new Promise((res) => (setTimeout(res, this._delay)))
+ .then(this.pollRecursive);
+ });
+ }
+}
+
+
+export class InteractionTimeoutEventArgs {
+ constructor(public Sender: object, public Type: InteractionTimeoutType) {
+ }
+}
+
+export enum InteractionTimeoutType {
+ Reset = 0,
+ Timeout = 1
+}
diff --git a/src/client/northstar/operations/HistogramOperation.ts b/src/client/northstar/operations/HistogramOperation.ts
new file mode 100644
index 000000000..e63de1632
--- /dev/null
+++ b/src/client/northstar/operations/HistogramOperation.ts
@@ -0,0 +1,124 @@
+import { action, computed, observable, trace } from "mobx";
+import { Document } from "../../../fields/Document";
+import { FieldWaiting } from "../../../fields/Field";
+import { KeyStore } from "../../../fields/KeyStore";
+import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils";
+import { ColumnAttributeModel } from "../core/attribute/AttributeModel";
+import { AttributeTransformationModel } from "../core/attribute/AttributeTransformationModel";
+import { CalculatedAttributeManager } from "../core/attribute/CalculatedAttributeModel";
+import { FilterModel } from "../core/filter/FilterModel";
+import { FilterOperand } from "../core/filter/FilterOperand";
+import { IBaseFilterConsumer } from "../core/filter/IBaseFilterConsumer";
+import { IBaseFilterProvider } from "../core/filter/IBaseFilterProvider";
+import { HistogramField } from "../dash-fields/HistogramField";
+import { SETTINGS_SAMPLE_SIZE, SETTINGS_X_BINS, SETTINGS_Y_BINS } from "../model/binRanges/VisualBinRangeHelper";
+import { AggregateFunction, AggregateParameters, Attribute, AverageAggregateParameters, Bin, DataType, DoubleValueAggregateResult, HistogramOperationParameters, HistogramResult, QuantitativeBinRange } from "../model/idea/idea";
+import { ModelHelpers } from "../model/ModelHelpers";
+import { ArrayUtil } from "../utils/ArrayUtil";
+import { BaseOperation } from "./BaseOperation";
+
+export class HistogramOperation extends BaseOperation implements IBaseFilterConsumer, IBaseFilterProvider {
+ public static Empty = new HistogramOperation("-empty schema-", new AttributeTransformationModel(new ColumnAttributeModel(new Attribute())), new AttributeTransformationModel(new ColumnAttributeModel(new Attribute())), new AttributeTransformationModel(new ColumnAttributeModel(new Attribute())));
+ @observable public FilterOperand: FilterOperand = FilterOperand.AND;
+ @observable public Links: Document[] = [];
+ @observable public BrushLinks: { l: Document, b: Document }[] = [];
+ @observable public BrushColors: number[] = [];
+ @observable public FilterModels: FilterModel[] = [];
+
+ @observable public Normalization: number = -1;
+ @observable public X: AttributeTransformationModel;
+ @observable public Y: AttributeTransformationModel;
+ @observable public V: AttributeTransformationModel;
+ @observable public SchemaName: string;
+ @observable public QRange: QuantitativeBinRange | undefined;
+ @computed public get Schema() { return CurrentUserUtils.GetNorthstarSchema(this.SchemaName); }
+
+ constructor(schemaName: string, x: AttributeTransformationModel, y: AttributeTransformationModel, v: AttributeTransformationModel, normalized?: number) {
+ super();
+ this.X = x;
+ this.Y = y;
+ this.V = v;
+ this.Normalization = normalized ? normalized : -1;
+ this.SchemaName = schemaName;
+ }
+
+ Equals(other: Object): boolean {
+ throw new Error("Method not implemented.");
+ }
+
+ @action
+ public AddFilterModels(filterModels: FilterModel[]): void {
+ filterModels.filter(f => f !== null).forEach(fm => this.FilterModels.push(fm));
+ }
+ @action
+ public RemoveFilterModels(filterModels: FilterModel[]): void {
+ ArrayUtil.RemoveMany(this.FilterModels, filterModels);
+ }
+
+ @computed
+ public get FilterString(): string {
+ let filterModels: FilterModel[] = [];
+ return FilterModel.GetFilterModelsRecursive(this, new Set<IBaseFilterProvider>(), filterModels, true)
+ }
+
+ @computed
+ public get BrushString(): string[] {
+ trace();
+ let brushes: string[] = [];
+ this.BrushLinks.map(brushLink => {
+ let brushHistogram = brushLink.b.GetT(KeyStore.Data, HistogramField);
+ if (brushHistogram && brushHistogram != FieldWaiting) {
+ let filterModels: FilterModel[] = [];
+ brushes.push(FilterModel.GetFilterModelsRecursive(brushHistogram.Data, new Set<IBaseFilterProvider>(), filterModels, false));
+ }
+ });
+ return brushes;
+ }
+
+ private getAggregateParameters(histoX: AttributeTransformationModel, histoY: AttributeTransformationModel, histoValue: AttributeTransformationModel) {
+ let allAttributes = new Array<AttributeTransformationModel>(histoX, histoY, histoValue);
+ allAttributes = ArrayUtil.Distinct(allAttributes.filter(a => a.AggregateFunction !== AggregateFunction.None));
+
+ let numericDataTypes = [DataType.Int, DataType.Double, DataType.Float];
+ let perBinAggregateParameters: AggregateParameters[] = ModelHelpers.GetAggregateParametersWithMargins(this.Schema!.distinctAttributeParameters, allAttributes);
+ let globalAggregateParameters: AggregateParameters[] = [];
+ [histoX, histoY]
+ .filter(a => a.AggregateFunction === AggregateFunction.None && ArrayUtil.Contains(numericDataTypes, a.AttributeModel.DataType))
+ .forEach(a => {
+ let avg = new AverageAggregateParameters();
+ avg.attributeParameters = ModelHelpers.GetAttributeParameters(a.AttributeModel);
+ globalAggregateParameters.push(avg);
+ });
+ return [perBinAggregateParameters, globalAggregateParameters];
+ }
+
+ public CreateOperationParameters(): HistogramOperationParameters | undefined {
+ if (this.X && this.Y && this.V) {
+ let [perBinAggregateParameters, globalAggregateParameters] = this.getAggregateParameters(this.X, this.Y, this.V);
+ return new HistogramOperationParameters({
+ enableBrushComputation: true,
+ adapterName: this.SchemaName,
+ filter: this.FilterString,
+ brushes: this.BrushString,
+ binningParameters: [ModelHelpers.GetBinningParameters(this.X, SETTINGS_X_BINS, this.QRange ? this.QRange.minValue : undefined, this.QRange ? this.QRange.maxValue : undefined),
+ ModelHelpers.GetBinningParameters(this.Y, SETTINGS_Y_BINS)],
+ sampleStreamBlockSize: SETTINGS_SAMPLE_SIZE,
+ perBinAggregateParameters: perBinAggregateParameters,
+ globalAggregateParameters: globalAggregateParameters,
+ sortPerBinAggregateParameter: undefined,
+ attributeCalculatedParameters: CalculatedAttributeManager
+ .AllCalculatedAttributes.map(a => ModelHelpers.GetAttributeParametersFromAttributeModel(a)),
+ degreeOfParallism: 1, // Settings.Instance.DegreeOfParallelism,
+ isCachable: false
+ });
+ }
+ }
+
+ @action
+ public async Update(): Promise<void> {
+ this.BrushColors = this.BrushLinks.map(e => e.l.GetNumber(KeyStore.BackgroundColor, 0));
+ return super.Update();
+ }
+}
+
+
diff --git a/src/client/northstar/utils/ArrayUtil.ts b/src/client/northstar/utils/ArrayUtil.ts
new file mode 100644
index 000000000..f35c98317
--- /dev/null
+++ b/src/client/northstar/utils/ArrayUtil.ts
@@ -0,0 +1,90 @@
+import { Exception } from "../model/idea/idea";
+
+export class ArrayUtil {
+
+ public static Contains(arr1: any[], arr2: any): boolean {
+ if (arr1.length === 0) {
+ return false;
+ }
+ let isComplex = typeof arr1[0] === "object";
+ for (let i = 0; i < arr1.length; i++) {
+ if (isComplex && "Equals" in arr1[i]) {
+ if (arr1[i].Equals(arr2)) {
+ return true;
+ }
+ }
+ else {
+ if (arr1[i] === arr2) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+
+ public static RemoveMany(arr: any[], elements: Object[]) {
+ elements.forEach(e => ArrayUtil.Remove(arr, e));
+ }
+
+ public static AddMany(arr: any[], others: Object[]) {
+ arr.push(...others);
+ }
+
+ public static Clear(arr: any[]) {
+ arr.splice(0, arr.length);
+ }
+
+
+ public static Remove(arr: any[], other: Object) {
+ const index = ArrayUtil.IndexOfWithEqual(arr, other);
+ if (index === -1) {
+ return;
+ }
+ arr.splice(index, 1);
+ }
+
+
+ public static First<T>(arr: T[], predicate: (x: any) => boolean): T {
+ let filtered = arr.filter(predicate);
+ if (filtered.length > 0) {
+ return filtered[0];
+ }
+ throw new Exception()
+ }
+
+ public static FirstOrDefault<T>(arr: T[], predicate: (x: any) => boolean): T | undefined {
+ let filtered = arr.filter(predicate);
+ if (filtered.length > 0) {
+ return filtered[0];
+ }
+ return undefined;
+ }
+
+ public static Distinct(arr: any[]): any[] {
+ let ret = [];
+ for (let i = 0; i < arr.length; i++) {
+ if (!ArrayUtil.Contains(ret, arr[i])) {
+ ret.push(arr[i]);
+ }
+ }
+ return ret;
+ }
+
+ public static IndexOfWithEqual(arr: any[], other: any): number {
+ for (let i = 0; i < arr.length; i++) {
+ let isComplex = typeof arr[0] === "object";
+ if (isComplex && "Equals" in arr[i]) {
+ if (arr[i].Equals(other)) {
+ return i;
+ }
+ }
+ else {
+ if (arr[i] === other) {
+ return i;
+ }
+ }
+ }
+ return -1;
+ }
+} \ No newline at end of file
diff --git a/src/client/northstar/utils/Extensions.ts b/src/client/northstar/utils/Extensions.ts
new file mode 100644
index 000000000..7c2b7fc9d
--- /dev/null
+++ b/src/client/northstar/utils/Extensions.ts
@@ -0,0 +1,29 @@
+interface String {
+ ReplaceAll(toReplace: string, replacement: string): string;
+ Truncate(length: number, replacement: string): String;
+}
+
+String.prototype.ReplaceAll = function (toReplace: string, replacement: string): string {
+ var target = this;
+ return target.split(toReplace).join(replacement);
+}
+
+String.prototype.Truncate = function (length: number, replacement: string): String {
+ var target = this;
+ if (target.length >= length) {
+ target = target.slice(0, Math.max(0, length - replacement.length)) + replacement;
+ }
+ return target;
+}
+
+interface Math {
+ log10(val: number): number;
+}
+
+Math.log10 = function (val: number): number {
+ return Math.log(val) / Math.LN10;
+}
+
+declare interface ObjectConstructor {
+ assign(...objects: Object[]): Object;
+}
diff --git a/src/client/northstar/utils/GeometryUtil.ts b/src/client/northstar/utils/GeometryUtil.ts
new file mode 100644
index 000000000..d5f3ba631
--- /dev/null
+++ b/src/client/northstar/utils/GeometryUtil.ts
@@ -0,0 +1,129 @@
+import { MathUtil, PIXIRectangle, PIXIPoint } from "./MathUtil";
+
+
+export class GeometryUtil {
+
+ public static ComputeBoundingBox(points: { x: number, y: number }[], scale = 1, padding: number = 0): PIXIRectangle {
+ let minX: number = Number.MAX_VALUE;
+ let minY: number = Number.MAX_VALUE;
+ let maxX: number = Number.MIN_VALUE;
+ let maxY: number = Number.MIN_VALUE;
+ for (var i = 0; i < points.length; i++) {
+ if (points[i].x < minX)
+ minX = points[i].x;
+ if (points[i].y < minY)
+ minY = points[i].y;
+ if (points[i].x > maxX)
+ maxX = points[i].x;
+ if (points[i].y > maxY)
+ maxY = points[i].y;
+ }
+ return new PIXIRectangle(minX * scale - padding, minY * scale - padding, (maxX - minX) * scale + padding * 2, (maxY - minY) * scale + padding * 2);
+ }
+
+ public static RectangleOverlap(rect1: PIXIRectangle, rect2: PIXIRectangle) {
+ let x_overlap = Math.max(0, Math.min(rect1.right, rect2.right) - Math.max(rect1.left, rect2.left));
+ let y_overlap = Math.max(0, Math.min(rect1.bottom, rect2.bottom) - Math.max(rect1.top, rect2.top));
+ return x_overlap * y_overlap;
+ }
+
+ public static RotatePoints(center: { x: number, y: number }, points: { x: number, y: number }[], angle: number): PIXIPoint[] {
+ const rotate = (cx: number, cy: number, x: number, y: number, angle: number) => {
+ const radians = angle,
+ cos = Math.cos(radians),
+ sin = Math.sin(radians),
+ nx = (cos * (x - cx)) + (sin * (y - cy)) + cx,
+ ny = (cos * (y - cy)) - (sin * (x - cx)) + cy;
+ return new PIXIPoint(nx, ny);
+ }
+ return points.map(p => rotate(center.x, center.y, p.x, p.y, angle));
+ }
+
+ public static LineByLeastSquares(points: { x: number, y: number }[]): PIXIPoint[] {
+ let sum_x: number = 0;
+ let sum_y: number = 0;
+ let sum_xy: number = 0;
+ let sum_xx: number = 0;
+ let count: number = 0;
+
+ let x: number = 0;
+ let y: number = 0;
+
+
+ if (points.length === 0) {
+ return [];
+ }
+
+ for (let v = 0; v < points.length; v++) {
+ x = points[v].x;
+ y = points[v].y;
+ sum_x += x;
+ sum_y += y;
+ sum_xx += x * x;
+ sum_xy += x * y;
+ count++;
+ }
+
+ let m = (count * sum_xy - sum_x * sum_y) / (count * sum_xx - sum_x * sum_x);
+ let b = (sum_y / count) - (m * sum_x) / count;
+ let result: PIXIPoint[] = new Array<PIXIPoint>();
+
+ for (let v = 0; v < points.length; v++) {
+ x = points[v].x;
+ y = x * m + b;
+ result.push(new PIXIPoint(x, y));
+ }
+ return result;
+ }
+
+ // public static PointInsidePolygon(vs:Point[], x:number, y:number):boolean {
+ // // ray-casting algorithm based on
+ // // http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
+
+ // var inside = false;
+ // for (var i = 0, j = vs.length - 1; i < vs.length; j = i++) {
+ // var xi = vs[i].x, yi = vs[i].y;
+ // var xj = vs[j].x, yj = vs[j].y;
+
+ // var intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi);
+ // if (intersect)
+ // inside = !inside;
+ // }
+
+ // return inside;
+ // };
+
+ public static IntersectLines(x1: number, y1: number, x2: number, y2: number, x3: number, y3: number, x4: number, y4: number): boolean {
+ let a1: number, a2: number, b1: number, b2: number, c1: number, c2: number;
+ let r1: number, r2: number, r3: number, r4: number;
+ let denom: number, offset: number, num: number;
+
+ a1 = y2 - y1;
+ b1 = x1 - x2;
+ c1 = (x2 * y1) - (x1 * y2);
+ r3 = ((a1 * x3) + (b1 * y3) + c1);
+ r4 = ((a1 * x4) + (b1 * y4) + c1);
+
+ if ((r3 !== 0) && (r4 !== 0) && (MathUtil.Sign(r3) === MathUtil.Sign(r4))) {
+ return false;
+ }
+
+ a2 = y4 - y3;
+ b2 = x3 - x4;
+ c2 = (x4 * y3) - (x3 * y4);
+
+ r1 = (a2 * x1) + (b2 * y1) + c2;
+ r2 = (a2 * x2) + (b2 * y2) + c2;
+
+ if ((r1 !== 0) && (r2 !== 0) && (MathUtil.Sign(r1) === MathUtil.Sign(r2))) {
+ return false;
+ }
+
+ denom = (a1 * b2) - (a2 * b1);
+
+ if (denom === 0) {
+ return false;
+ }
+ return true;
+ }
+} \ No newline at end of file
diff --git a/src/client/northstar/utils/IDisposable.ts b/src/client/northstar/utils/IDisposable.ts
new file mode 100644
index 000000000..5e9843326
--- /dev/null
+++ b/src/client/northstar/utils/IDisposable.ts
@@ -0,0 +1,3 @@
+export interface IDisposable {
+ Dispose(): void;
+} \ No newline at end of file
diff --git a/src/client/northstar/utils/IEquatable.ts b/src/client/northstar/utils/IEquatable.ts
new file mode 100644
index 000000000..2f81c2478
--- /dev/null
+++ b/src/client/northstar/utils/IEquatable.ts
@@ -0,0 +1,3 @@
+export interface IEquatable {
+ Equals(other: Object): boolean;
+} \ No newline at end of file
diff --git a/src/client/northstar/utils/KeyCodes.ts b/src/client/northstar/utils/KeyCodes.ts
new file mode 100644
index 000000000..044569ffe
--- /dev/null
+++ b/src/client/northstar/utils/KeyCodes.ts
@@ -0,0 +1,137 @@
+/**
+ * Class contains the keycodes for keys on your keyboard.
+ *
+ * Useful for auto completion:
+ *
+ * ```
+ * switch (event.key)
+ * {
+ * case KeyCode.UP:
+ * {
+ * // Up key pressed
+ * break;
+ * }
+ * case KeyCode.DOWN:
+ * {
+ * // Down key pressed
+ * break;
+ * }
+ * case KeyCode.LEFT:
+ * {
+ * // Left key pressed
+ * break;
+ * }
+ * case KeyCode.RIGHT:
+ * {
+ * // Right key pressed
+ * break;
+ * }
+ * default:
+ * {
+ * // ignore
+ * break;
+ * }
+ * }
+ * ```
+ */
+export class KeyCodes
+{
+ public static TAB:number = 9;
+ public static CAPS_LOCK:number = 20;
+ public static SHIFT:number = 16;
+ public static CONTROL:number = 17;
+ public static SPACE:number = 32;
+ public static DOWN:number = 40;
+ public static UP:number = 38;
+ public static LEFT:number = 37;
+ public static RIGHT:number = 39;
+ public static ESCAPE:number = 27;
+ public static F1:number = 112;
+ public static F2:number = 113;
+ public static F3:number = 114;
+ public static F4:number = 115;
+ public static F5:number = 116;
+ public static F6:number = 117;
+ public static F7:number = 118;
+ public static F8:number = 119;
+ public static F9:number = 120;
+ public static F10:number = 121;
+ public static F11:number = 122;
+ public static F12:number = 123;
+ public static INSERT:number = 45;
+ public static HOME:number = 36;
+ public static PAGE_UP:number = 33;
+ public static PAGE_DOWN:number = 34;
+ public static DELETE:number = 46;
+ public static END:number = 35;
+ public static ENTER:number = 13;
+ public static BACKSPACE:number = 8;
+ public static NUMPAD_0:number = 96;
+ public static NUMPAD_1:number = 97;
+ public static NUMPAD_2:number = 98;
+ public static NUMPAD_3:number = 99;
+ public static NUMPAD_4:number = 100;
+ public static NUMPAD_5:number = 101;
+ public static NUMPAD_6:number = 102;
+ public static NUMPAD_7:number = 103;
+ public static NUMPAD_8:number = 104;
+ public static NUMPAD_9:number = 105;
+ public static NUMPAD_DIVIDE:number = 111;
+ public static NUMPAD_ADD:number = 107;
+ public static NUMPAD_ENTER:number = 13;
+ public static NUMPAD_DECIMAL:number = 110;
+ public static NUMPAD_SUBTRACT:number = 109;
+ public static NUMPAD_MULTIPLY:number = 106;
+ public static SEMICOLON:number = 186;
+ public static EQUAL:number = 187;
+ public static COMMA:number = 188;
+ public static MINUS:number = 189;
+ public static PERIOD:number = 190;
+ public static SLASH:number = 191;
+ public static BACKQUOTE:number = 192;
+ public static LEFTBRACKET:number = 219;
+ public static BACKSLASH:number = 220;
+ public static RIGHTBRACKET:number = 221;
+ public static QUOTE:number = 222;
+ public static ALT:number = 18;
+ public static COMMAND:number = 15;
+ public static NUMPAD:number = 21;
+ public static A:number = 65;
+ public static B:number = 66;
+ public static C:number = 67;
+ public static D:number = 68;
+ public static E:number = 69;
+ public static F:number = 70;
+ public static G:number = 71;
+ public static H:number = 72;
+ public static I:number = 73;
+ public static J:number = 74;
+ public static K:number = 75;
+ public static L:number = 76;
+ public static M:number = 77;
+ public static N:number = 78;
+ public static O:number = 79;
+ public static P:number = 80;
+ public static Q:number = 81;
+ public static R:number = 82;
+ public static S:number = 83;
+ public static T:number = 84;
+ public static U:number = 85;
+ public static V:number = 86;
+ public static W:number = 87;
+ public static X:number = 88;
+ public static Y:number = 89;
+ public static Z:number = 90;
+ public static NUM_0:number = 48;
+ public static NUM_1:number = 49;
+ public static NUM_2:number = 50;
+ public static NUM_3:number = 51;
+ public static NUM_4:number = 52;
+ public static NUM_5:number = 53;
+ public static NUM_6:number = 54;
+ public static NUM_7:number = 55;
+ public static NUM_8:number = 56;
+ public static NUM_9:number = 57;
+ public static SUBSTRACT:number = 189;
+ public static ADD:number = 187;
+} \ No newline at end of file
diff --git a/src/client/northstar/utils/LABColor.ts b/src/client/northstar/utils/LABColor.ts
new file mode 100644
index 000000000..72e46fb7f
--- /dev/null
+++ b/src/client/northstar/utils/LABColor.ts
@@ -0,0 +1,90 @@
+
+export class LABColor {
+ public L: number;
+ public A: number;
+ public B: number;
+
+ // constructor - takes three floats for lightness and color-opponent dimensions
+ constructor(l: number, a: number, b: number) {
+ this.L = l;
+ this.A = a;
+ this.B = b;
+ }
+
+ // static function for linear interpolation between two LABColors
+ public static Lerp(a: LABColor, b: LABColor, t: number): LABColor {
+ return new LABColor(LABColor.LerpNumber(a.L, b.L, t), LABColor.LerpNumber(a.A, b.A, t), LABColor.LerpNumber(a.B, b.B, t));
+ }
+
+ public static LerpNumber(a: number, b: number, percent: number): number {
+ return a + percent * (b - a);
+ }
+
+ static hexToRGB(hex: number, alpha: number): number[] {
+ var r = (hex & (0xff << 16)) >> 16;
+ var g = (hex & (0xff << 8)) >> 8;
+ var b = (hex & (0xff << 0)) >> 0;
+ return [r, g, b];
+ }
+ static RGBtoHex(red: number, green: number, blue: number): number {
+ return blue | (green << 8) | (red << 16);
+ }
+
+ public static RGBtoHexString(rgb: number): string {
+ let str = "#" + this.hex((rgb & (0xff << 16)) >> 16) + this.hex((rgb & (0xff << 8)) >> 8) + this.hex((rgb & (0xff << 0)) >> 0);
+ return str;
+ }
+
+ static hex(x: number): string {
+ var hexDigits = new Array
+ ("0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f");
+ return isNaN(x) ? "00" : hexDigits[(x - x % 16) / 16] + hexDigits[x % 16];
+ }
+
+ public static FromColor(c: number): LABColor {
+ var rgb = LABColor.hexToRGB(c, 0);
+ var r = LABColor.d3_rgb_xyz(rgb[0] * 255);
+ var g = LABColor.d3_rgb_xyz(rgb[1] * 255);
+ var b = LABColor.d3_rgb_xyz(rgb[2] * 255);
+
+ var x = LABColor.d3_xyz_lab((0.4124564 * r + 0.3575761 * g + 0.1804375 * b) / LABColor.d3_lab_X);
+ var y = LABColor.d3_xyz_lab((0.2126729 * r + 0.7151522 * g + 0.0721750 * b) / LABColor.d3_lab_Y);
+ var z = LABColor.d3_xyz_lab((0.0193339 * r + 0.1191920 * g + 0.9503041 * b) / LABColor.d3_lab_Z);
+ var lab = new LABColor(116 * y - 16, 500 * (x - y), 200 * (y - z));
+ return lab;
+ }
+
+ private static d3_lab_X: number = 0.950470;
+ private static d3_lab_Y: number = 1;
+ private static d3_lab_Z: number = 1.088830;
+
+ public static d3_lab_xyz(x: number): number {
+ return x > 0.206893034 ? x * x * x : (x - 4 / 29) / 7.787037;
+ }
+
+ public static d3_xyz_rgb(r: number): number {
+ return Math.round(255 * (r <= 0.00304 ? 12.92 * r : 1.055 * Math.pow(r, 1 / 2.4) - 0.055));
+ }
+
+ public static d3_rgb_xyz(r: number): number {
+ return (r /= 255) <= 0.04045 ? r / 12.92 : Math.pow((r + 0.055) / 1.055, 2.4);
+ }
+
+ public static d3_xyz_lab(x: number): number {
+ return x > 0.008856 ? Math.pow(x, 1 / 3) : 7.787037 * x + 4 / 29;
+ }
+
+ public static ToColor(lab: LABColor): number {
+ var y = (lab.L + 16) / 116;
+ var x = y + lab.A / 500;
+ var z = y - lab.B / 200;
+ x = LABColor.d3_lab_xyz(x) * LABColor.d3_lab_X;
+ y = LABColor.d3_lab_xyz(y) * LABColor.d3_lab_Y;
+ z = LABColor.d3_lab_xyz(z) * LABColor.d3_lab_Z;
+
+ return LABColor.RGBtoHex(
+ LABColor.d3_xyz_rgb(3.2404542 * x - 1.5371385 * y - 0.4985314 * z) / 255,
+ LABColor.d3_xyz_rgb(-0.9692660 * x + 1.8760108 * y + 0.0415560 * z) / 255,
+ LABColor.d3_xyz_rgb(0.0556434 * x - 0.2040259 * y + 1.0572252 * z) / 255);
+ }
+} \ No newline at end of file
diff --git a/src/client/northstar/utils/MathUtil.ts b/src/client/northstar/utils/MathUtil.ts
new file mode 100644
index 000000000..bb7e73871
--- /dev/null
+++ b/src/client/northstar/utils/MathUtil.ts
@@ -0,0 +1,241 @@
+
+
+export class PIXIPoint {
+ public get x() { return this.coords[0]; }
+ public get y() { return this.coords[1]; }
+ public set x(value: number) { this.coords[0] = value; }
+ public set y(value: number) { this.coords[1] = value; }
+ public coords: number[] = [0, 0];
+ constructor(x: number, y: number) {
+ this.coords[0] = x;
+ this.coords[1] = y;
+ }
+}
+
+export class PIXIRectangle {
+ public x: number;
+ public y: number;
+ public width: number;
+ public height: number
+ public get left() { return this.x }
+ public get right() { return this.x + this.width; }
+ public get top() { return this.y }
+ public get bottom() { return this.top + this.height }
+ public static get EMPTY() { return new PIXIRectangle(0, 0, -1, -1); }
+ constructor(x: number, y: number, width: number, height: number) {
+ this.x = x;
+ this.y = y;
+ this.width = width;
+ this.height = height;
+ }
+}
+
+export class MathUtil {
+
+ public static EPSILON: number = 0.001;
+
+ public static Sign(value: number): number {
+ return value >= 0 ? 1 : -1;
+ }
+
+ public static AddPoint(p1: PIXIPoint, p2: PIXIPoint, inline: boolean = false): PIXIPoint {
+ if (inline) {
+ p1.x += p2.x;
+ p1.y += p2.y;
+ return p1;
+ }
+ else {
+ return new PIXIPoint(p1.x + p2.x, p1.y + p2.y);
+ }
+ }
+
+ public static Perp(p1: PIXIPoint): PIXIPoint {
+ return new PIXIPoint(-p1.y, p1.x);
+ }
+
+ public static DividePoint(p1: PIXIPoint, by: number, inline: boolean = false): PIXIPoint {
+ if (inline) {
+ p1.x /= by;
+ p1.y /= by;
+ return p1;
+ }
+ else {
+ return new PIXIPoint(p1.x / by, p1.y / by);
+ }
+ }
+
+ public static MultiplyConstant(p1: PIXIPoint, by: number, inline: boolean = false) {
+ if (inline) {
+ p1.x *= by;
+ p1.y *= by;
+ return p1;
+ }
+ else {
+ return new PIXIPoint(p1.x * by, p1.y * by);
+ }
+ }
+
+ public static SubtractPoint(p1: PIXIPoint, p2: PIXIPoint, inline: boolean = false): PIXIPoint {
+ if (inline) {
+ p1.x -= p2.x;
+ p1.y -= p2.y;
+ return p1;
+ }
+ else {
+ return new PIXIPoint(p1.x - p2.x, p1.y - p2.y);
+ }
+ }
+
+ public static Area(rect: PIXIRectangle): number {
+ return rect.width * rect.height;
+ }
+
+ public static DistToLineSegment(v: PIXIPoint, w: PIXIPoint, p: PIXIPoint) {
+ // Return minimum distance between line segment vw and point p
+ var l2 = MathUtil.DistSquared(v, w); // i.e. |w-v|^2 - avoid a sqrt
+ if (l2 == 0.0) return MathUtil.Dist(p, v); // v == w case
+ // Consider the line extending the segment, parameterized as v + t (w - v).
+ // We find projection of point p onto the line.
+ // It falls where t = [(p-v) . (w-v)] / |w-v|^2
+ // We clamp t from [0,1] to handle points outside the segment vw.
+ var dot = MathUtil.Dot(
+ MathUtil.SubtractPoint(p, v),
+ MathUtil.SubtractPoint(w, v)) / l2;
+ var t = Math.max(0, Math.min(1, dot));
+ // Projection falls on the segment
+ var projection = MathUtil.AddPoint(v,
+ MathUtil.MultiplyConstant(
+ MathUtil.SubtractPoint(w, v), t));
+ return MathUtil.Dist(p, projection);
+ }
+
+ public static LineSegmentIntersection(ps1: PIXIPoint, pe1: PIXIPoint, ps2: PIXIPoint, pe2: PIXIPoint): PIXIPoint | undefined {
+ var a1 = pe1.y - ps1.y;
+ var b1 = ps1.x - pe1.x;
+
+ var a2 = pe2.y - ps2.y;
+ var b2 = ps2.x - pe2.x;
+
+ var delta = a1 * b2 - a2 * b1;
+ if (delta == 0) {
+ return undefined;
+ }
+ var c2 = a2 * ps2.x + b2 * ps2.y;
+ var c1 = a1 * ps1.x + b1 * ps1.y;
+ var invdelta = 1 / delta;
+ return new PIXIPoint((b2 * c1 - b1 * c2) * invdelta, (a1 * c2 - a2 * c1) * invdelta);
+ }
+
+ public static PointInPIXIRectangle(p: PIXIPoint, rect: PIXIRectangle): boolean {
+ if (p.x < rect.left - this.EPSILON)
+ return false;
+ if (p.x > rect.right + this.EPSILON)
+ return false;
+ if (p.y < rect.top - this.EPSILON)
+ return false;
+ if (p.y > rect.bottom + this.EPSILON)
+ return false;
+
+ return true;
+ }
+
+ public static LinePIXIRectangleIntersection(lineFrom: PIXIPoint, lineTo: PIXIPoint, rect: PIXIRectangle): Array<PIXIPoint> {
+ var r1 = new PIXIPoint(rect.left, rect.top);
+ var r2 = new PIXIPoint(rect.right, rect.top);
+ var r3 = new PIXIPoint(rect.right, rect.bottom);
+ var r4 = new PIXIPoint(rect.left, rect.bottom);
+ var ret = new Array<PIXIPoint>();
+ var dist = this.Dist(lineFrom, lineTo)
+ var inter = this.LineSegmentIntersection(lineFrom, lineTo, r1, r2);
+ if (inter != null && this.PointInPIXIRectangle(inter, rect) &&
+ this.Dist(inter, lineFrom) < dist && this.Dist(inter, lineTo) < dist)
+ ret.push(inter);
+ inter = this.LineSegmentIntersection(lineFrom, lineTo, r2, r3);
+ if (inter != null && this.PointInPIXIRectangle(inter, rect) &&
+ this.Dist(inter, lineFrom) < dist && this.Dist(inter, lineTo) < dist)
+ ret.push(inter);
+ inter = this.LineSegmentIntersection(lineFrom, lineTo, r3, r4);
+ if (inter != null && this.PointInPIXIRectangle(inter, rect) &&
+ this.Dist(inter, lineFrom) < dist && this.Dist(inter, lineTo) < dist)
+ ret.push(inter);
+ inter = this.LineSegmentIntersection(lineFrom, lineTo, r4, r1);
+ if (inter != null && this.PointInPIXIRectangle(inter, rect) &&
+ this.Dist(inter, lineFrom) < dist && this.Dist(inter, lineTo) < dist)
+ ret.push(inter);
+ return ret;
+ }
+
+ public static Intersection(rect1: PIXIRectangle, rect2: PIXIRectangle): PIXIRectangle {
+ const left = Math.max(rect1.x, rect2.x);
+ const right = Math.min(rect1.x + rect1.width, rect2.x + rect2.width);
+ const top = Math.max(rect1.y, rect2.y);
+ const bottom = Math.min(rect1.y + rect1.height, rect2.y + rect2.height);
+ return new PIXIRectangle(left, top, right - left, bottom - top);
+ }
+
+ public static Dist(p1: PIXIPoint, p2: PIXIPoint): number {
+ return Math.sqrt(MathUtil.DistSquared(p1, p2));
+ }
+
+ public static Dot(p1: PIXIPoint, p2: PIXIPoint): number {
+ return p1.x * p2.x + p1.y * p2.y
+ }
+
+ public static Normalize(p1: PIXIPoint) {
+ var d = this.Length(p1);
+ return new PIXIPoint(p1.x / d, p1.y / d);
+ }
+
+ public static Length(p1: PIXIPoint): number {
+ return Math.sqrt(p1.x * p1.x + p1.y * p1.y);
+ }
+
+ public static DistSquared(p1: PIXIPoint, p2: PIXIPoint): number {
+ const a = p1.x - p2.x;
+ const b = p1.y - p2.y;
+ return (a * a + b * b)
+ }
+
+ public static RectIntersectsRect(r1: PIXIRectangle, r2: PIXIRectangle): boolean {
+ return !(r2.x > r1.x + r1.width ||
+ r2.x + r2.width < r1.x ||
+ r2.y > r1.y + r1.height ||
+ r2.y + r2.height < r1.y);
+ }
+
+ public static ArgMin(temp: number[]): number {
+ let index = 0;
+ let value = temp[0];
+ for (let i = 1; i < temp.length; i++) {
+ if (temp[i] < value) {
+ value = temp[i];
+ index = i;
+ }
+ }
+ return index;
+ }
+
+ public static ArgMax(temp: number[]): number {
+ let index = 0;
+ let value = temp[0];
+ for (let i = 1; i < temp.length; i++) {
+ if (temp[i] > value) {
+ value = temp[i];
+ index = i;
+ }
+ }
+ return index;
+ }
+
+ public static Combinations<T>(chars: T[]) {
+ let result = new Array<T>();
+ let f = (prefix: any, chars: any) => {
+ for (let i = 0; i < chars.length; i++) {
+ result.push(prefix.concat(chars[i]));
+ f(prefix.concat(chars[i]), chars.slice(i + 1));
+ }
+ };
+ f([], chars);
+ return result;
+ }
+} \ No newline at end of file
diff --git a/src/client/northstar/utils/PartialClass.ts b/src/client/northstar/utils/PartialClass.ts
new file mode 100644
index 000000000..2f20de96f
--- /dev/null
+++ b/src/client/northstar/utils/PartialClass.ts
@@ -0,0 +1,7 @@
+
+export class PartialClass<T> {
+
+ constructor(data?: Partial<T>) {
+ Object.assign(this, data);
+ }
+} \ No newline at end of file
diff --git a/src/client/northstar/utils/SizeConverter.ts b/src/client/northstar/utils/SizeConverter.ts
new file mode 100644
index 000000000..bb91ed4a7
--- /dev/null
+++ b/src/client/northstar/utils/SizeConverter.ts
@@ -0,0 +1,99 @@
+import { PIXIPoint } from "./MathUtil";
+import { VisualBinRange } from "../model/binRanges/VisualBinRange";
+import { Bin, DoubleValueAggregateResult, AggregateKey } from "../model/idea/idea";
+import { ModelHelpers } from "../model/ModelHelpers";
+import { observable, action, computed } from "mobx";
+
+export class SizeConverter {
+ public DataMins: Array<number> = new Array<number>(2);
+ public DataMaxs: Array<number> = new Array<number>(2);
+ public DataRanges: Array<number> = new Array<number>(2);
+ public MaxLabelSizes: Array<PIXIPoint> = new Array<PIXIPoint>(2);
+ public RenderDimension: number = 300;
+
+ @observable _leftOffset: number = 40;
+ @observable _rightOffset: number = 20;
+ @observable _topOffset: number = 20;
+ @observable _bottomOffset: number = 45;
+ @observable _labelAngle: number = 0;
+ @observable _isSmall: boolean = false;
+ @observable public Initialized = 0;
+
+ @action public SetIsSmall(isSmall: boolean) { this._isSmall = isSmall; }
+ @action public SetLabelAngle(angle: number) { this._labelAngle = angle; }
+ @computed public get IsSmall() { return this._isSmall; }
+ @computed public get LabelAngle() { return this._labelAngle; }
+ @computed public get LeftOffset() { return this.IsSmall ? 5 : this._leftOffset; }
+ @computed public get RightOffset() { return this.IsSmall ? 5 : !this._labelAngle ? this._bottomOffset : Math.max(this._rightOffset, Math.cos(this._labelAngle) * (this.MaxLabelSizes[0].x + 18)); }
+ @computed public get TopOffset() { return this.IsSmall ? 5 : this._topOffset; }
+ @computed public get BottomOffset() { return this.IsSmall ? 25 : !this._labelAngle ? this._bottomOffset : Math.max(this._bottomOffset, Math.sin(this._labelAngle) * (this.MaxLabelSizes[0].x + 18)) + 18; }
+
+ public SetVisualBinRanges(visualBinRanges: Array<VisualBinRange>) {
+ this.Initialized++;
+ var xLabels = visualBinRanges[0].GetLabels();
+ var yLabels = visualBinRanges[1].GetLabels();
+ var xLabelStrings = xLabels.map(l => l.label!).sort(function (a, b) { return b.length - a.length });
+ var yLabelStrings = yLabels.map(l => l.label!).sort(function (a, b) { return b.length - a.length });
+
+ var metricsX = { width: 75 }; // RenderUtils.MeasureText(FontStyles.Default.fontFamily.toString(), 12, // FontStyles.AxisLabel.fontSize as number,
+ //xLabelStrings[0]!.slice(0, 20)) // StyleConstants.MAX_CHAR_FOR_HISTOGRAM_LABELS));
+ var metricsY = { width: 22 }; // RenderUtils.MeasureText(FontStyles.Default.fontFamily.toString(), 12, // FontStyles.AxisLabel.fontSize as number,
+ // yLabelStrings[0]!.slice(0, 20)); // StyleConstants.MAX_CHAR_FOR_HISTOGRAM_LABELS));
+ this.MaxLabelSizes[0] = new PIXIPoint(metricsX.width, 12);// FontStyles.AxisLabel.fontSize as number);
+ this.MaxLabelSizes[1] = new PIXIPoint(metricsY.width, 12); // FontStyles.AxisLabel.fontSize as number);
+
+ this._leftOffset = Math.max(10, metricsY.width + 10 + 20);
+
+ this.DataMins[0] = xLabels.map(l => l.minValue!).reduce((m, c) => Math.min(m, c), Number.MAX_VALUE);
+ this.DataMins[1] = yLabels.map(l => l.minValue!).reduce((m, c) => Math.min(m, c), Number.MAX_VALUE);
+ this.DataMaxs[0] = xLabels.map(l => l.maxValue!).reduce((m, c) => Math.max(m, c), Number.MIN_VALUE);
+ this.DataMaxs[1] = yLabels.map(l => l.maxValue!).reduce((m, c) => Math.max(m, c), Number.MIN_VALUE);
+
+ this.DataRanges[0] = this.DataMaxs[0] - this.DataMins[0];
+ this.DataRanges[1] = this.DataMaxs[1] - this.DataMins[1];
+ }
+
+ public DataToScreenNormalizedRange(dataValue: number, normalization: number, axis: number, binBrushMaxAxis: number) {
+ var value = normalization != 1 - axis || binBrushMaxAxis == 0 ? dataValue : (dataValue - 0) / (binBrushMaxAxis - 0) * this.DataRanges[axis];
+ var from = this.DataToScreenCoord(Math.min(0, value), axis);
+ var to = this.DataToScreenCoord(Math.max(0, value), axis);
+ return [from, value, to];
+ }
+
+ public DataToScreenPointRange(axis: number, bin: Bin, aggregateKey: AggregateKey) {
+ var value = ModelHelpers.GetAggregateResult(bin, aggregateKey) as DoubleValueAggregateResult;
+ if (value && value.hasResult)
+ return [this.DataToScreenCoord(value.result!, axis) - 5,
+ this.DataToScreenCoord(value.result!, axis) + 5];
+ return [undefined, undefined];
+ }
+
+ public DataToScreenXAxisRange(visualBinRanges: VisualBinRange[], index: number, bin: Bin) {
+ var value = visualBinRanges[0].GetValueFromIndex(bin.binIndex!.indices![index]);
+ return [this.DataToScreenX(value), this.DataToScreenX(visualBinRanges[index].AddStep(value))]
+ }
+ public DataToScreenYAxisRange(visualBinRanges: VisualBinRange[], index: number, bin: Bin) {
+ var value = visualBinRanges[1].GetValueFromIndex(bin.binIndex!.indices![index]);
+ return [this.DataToScreenY(value), this.DataToScreenY(visualBinRanges[index].AddStep(value))]
+ }
+
+ public DataToScreenX(x: number): number {
+ return ((x - this.DataMins[0]) / this.DataRanges[0]) * this.RenderDimension;
+ }
+ public DataToScreenY(y: number, flip: boolean = true) {
+ var retY = ((y - this.DataMins[1]) / this.DataRanges[1]) * this.RenderDimension;
+ return flip ? (this.RenderDimension) - retY : retY;
+ }
+ public DataToScreenCoord(v: number, axis: number) {
+ if (axis == 0)
+ return this.DataToScreenX(v);
+ return this.DataToScreenY(v);
+ }
+ public DataToScreenRange(minVal: number, maxVal: number, axis: number) {
+ let xFrom = this.DataToScreenX(axis === 0 ? minVal : this.DataMins[0]);
+ let xTo = this.DataToScreenX(axis === 0 ? maxVal : this.DataMaxs[0]);
+ let yFrom = this.DataToScreenY(axis === 1 ? minVal : this.DataMins[1]);
+ let yTo = this.DataToScreenY(axis === 1 ? maxVal : this.DataMaxs[1]);
+ return { xFrom, yFrom, xTo, yTo }
+ }
+} \ No newline at end of file
diff --git a/src/client/northstar/utils/StyleContants.ts b/src/client/northstar/utils/StyleContants.ts
new file mode 100644
index 000000000..ac8617e3b
--- /dev/null
+++ b/src/client/northstar/utils/StyleContants.ts
@@ -0,0 +1,95 @@
+import { PIXIPoint } from "./MathUtil";
+
+export class StyleConstants {
+
+ static DEFAULT_FONT: string = "Roboto Condensed";
+
+ static MENU_SUBMENU_WIDTH: number = 85;
+ static MENU_SUBMENU_HEIGHT: number = 400;
+ static MENU_BOX_SIZE: PIXIPoint = new PIXIPoint(80, 35);
+ static MENU_BOX_PADDING: number = 10;
+
+ static OPERATOR_MENU_LARGE: number = 35;
+ static OPERATOR_MENU_SMALL: number = 25;
+ static BRUSH_PALETTE: number[] = [0x42b43c, 0xfa217f, 0x6a9c75, 0xfb5de7, 0x25b8ea, 0x9b5bc4, 0xda9f63, 0xe23209, 0xfb899b, 0x94a6fd]
+ static GAP: number = 3;
+
+ static BACKGROUND_COLOR: number = 0xF3F3F3;
+ static TOOL_TIP_BACKGROUND_COLOR: number = 0xffffff;
+ static LIGHT_TEXT_COLOR: number = 0xffffff;
+ static LIGHT_TEXT_COLOR_STR: string = StyleConstants.HexToHexString(StyleConstants.LIGHT_TEXT_COLOR);
+ static DARK_TEXT_COLOR: number = 0x282828;
+ static HIGHLIGHT_TEXT_COLOR: number = 0xffcc00;
+ static FPS_TEXT_COLOR: number = StyleConstants.DARK_TEXT_COLOR;
+ static CORRELATION_LABEL_TEXT_COLOR_STR: string = StyleConstants.HexToHexString(StyleConstants.DARK_TEXT_COLOR);
+ static LOADING_SCREEN_TEXT_COLOR_STR: string = StyleConstants.HexToHexString(StyleConstants.DARK_TEXT_COLOR);
+ static ERROR_COLOR: number = 0x540E25;
+ static WARNING_COLOR: number = 0xE58F24;
+ static LOWER_THAN_NAIVE_COLOR: number = 0xee0000;
+ static HIGHLIGHT_COLOR: number = 0x82A8D9;
+ static HIGHLIGHT_COLOR_STR: string = StyleConstants.HexToHexString(StyleConstants.HIGHLIGHT_COLOR);
+ static OPERATOR_BACKGROUND_COLOR: number = 0x282828;
+ static LOADING_ANIMATION_COLOR: number = StyleConstants.OPERATOR_BACKGROUND_COLOR;
+ static MENU_COLOR: number = 0x282828;
+ static MENU_FONT_COLOR: number = StyleConstants.LIGHT_TEXT_COLOR;
+ static MENU_SELECTED_COLOR: number = StyleConstants.HIGHLIGHT_COLOR;
+ static MENU_SELECTED_FONT_COLOR: number = StyleConstants.LIGHT_TEXT_COLOR;
+ static BRUSH_COLOR: number = 0xff0000;
+ static DROP_ACCEPT_COLOR: number = StyleConstants.HIGHLIGHT_COLOR;
+ static SELECTED_COLOR: number = 0xffffff;
+ static SELECTED_COLOR_STR: string = StyleConstants.HexToHexString(StyleConstants.SELECTED_COLOR);
+ static PROGRESS_BACKGROUND_COLOR: number = 0x595959;
+ static GRID_LINES_COLOR: number = 0x3D3D3D;
+ static GRID_LINES_COLOR_STR: string = StyleConstants.HexToHexString(StyleConstants.GRID_LINES_COLOR);
+
+ static MAX_CHAR_FOR_HISTOGRAM_LABELS: number = 20;
+
+ static OVERLAP_COLOR: number = 0x0000ff;//0x540E25;
+ static BRUSH_COLORS: Array<number> = new Array<number>(
+ 0xFFDA7E, 0xFE8F65, 0xDA5655, 0x8F2240
+ );
+
+ static MIN_VALUE_COLOR: number = 0x373d43; //32343d, 373d43, 3b4648
+ static MARGIN_BARS_COLOR: number = 0xffffff;
+ static MARGIN_BARS_COLOR_STR: string = StyleConstants.HexToHexString(StyleConstants.MARGIN_BARS_COLOR);
+
+ static HISTOGRAM_WIDTH: number = 200;
+ static HISTOGRAM_HEIGHT: number = 150;
+ static PREDICTOR_WIDTH: number = 150;
+ static PREDICTOR_HEIGHT: number = 100;
+ static RAWDATA_WIDTH: number = 150;
+ static RAWDATA_HEIGHT: number = 100;
+ static FREQUENT_ITEM_WIDTH: number = 180;
+ static FREQUENT_ITEM_HEIGHT: number = 100;
+ static CORRELATION_WIDTH: number = 555;
+ static CORRELATION_HEIGHT: number = 390;
+ static PROBLEM_FINDER_WIDTH: number = 450;
+ static PROBLEM_FINDER_HEIGHT: number = 150;
+ static PIPELINE_OPERATOR_WIDTH: number = 300;
+ static PIPELINE_OPERATOR_HEIGHT: number = 120;
+ static SLICE_WIDTH: number = 150;
+ static SLICE_HEIGHT: number = 45;
+ static BORDER_MENU_ITEM_WIDTH: number = 50;
+ static BORDER_MENU_ITEM_HEIGHT: number = 30;
+
+
+ static SLICE_BG_COLOR: string = StyleConstants.HexToHexString(StyleConstants.OPERATOR_BACKGROUND_COLOR);
+ static SLICE_EMPTY_COLOR: number = StyleConstants.OPERATOR_BACKGROUND_COLOR;
+ static SLICE_OCCUPIED_COLOR: number = 0xffffff;
+ static SLICE_OCCUPIED_BG_COLOR: string = StyleConstants.HexToHexString(StyleConstants.OPERATOR_BACKGROUND_COLOR);
+ static SLICE_HOVER_BG_COLOR: string = StyleConstants.HexToHexString(StyleConstants.HIGHLIGHT_COLOR);
+ static SLICE_HOVER_COLOR: number = 0xffffff;
+
+ static HexToHexString(hex: number): string {
+ if (hex === undefined) {
+ return "#000000";
+ }
+ var s = hex.toString(16);
+ while (s.length < 6) {
+ s = "0" + s;
+ }
+ return "#" + s;
+ }
+
+
+}
diff --git a/src/client/northstar/utils/Utils.ts b/src/client/northstar/utils/Utils.ts
new file mode 100644
index 000000000..b35dce820
--- /dev/null
+++ b/src/client/northstar/utils/Utils.ts
@@ -0,0 +1,75 @@
+import { IBaseBrushable } from '../core/brusher/IBaseBrushable'
+import { IBaseFilterConsumer } from '../core/filter/IBaseFilterConsumer'
+import { IBaseFilterProvider } from '../core/filter/IBaseFilterProvider'
+import { AggregateFunction } from '../model/idea/idea'
+
+export class Utils {
+
+ public static EqualityHelper(a: Object, b: Object): boolean {
+ if (a === b) return true;
+ if (a === undefined && b !== undefined) return false;
+ if (a === null && b !== null) return false;
+ if (b === undefined && a !== undefined) return false;
+ if (b === null && a !== null) return false;
+ if ((<any>a).constructor.name !== (<any>b).constructor.name) return false;
+ return true;
+ }
+
+ public static LowercaseFirstLetter(str: string) {
+ return str.charAt(0).toUpperCase() + str.slice(1);
+ }
+
+ //
+ // this Type Guard tests if dropTarget is an IDropTarget. If it is, it coerces the compiler
+ // to treat the dropTarget parameter as an IDropTarget *ouside* this function scope (ie, in
+ // the scope of where this function is called from).
+ //
+
+ public static isBaseBrushable<T>(obj: Object): obj is IBaseBrushable<T> {
+ let typed = <IBaseBrushable<T>>obj;
+ return typed != null && typed.BrusherModels !== undefined;
+ }
+
+ public static isBaseFilterProvider(obj: Object): obj is IBaseFilterProvider {
+ let typed = <IBaseFilterProvider>obj;
+ return typed != null && typed.FilterModels !== undefined;
+ }
+
+ public static isBaseFilterConsumer(obj: Object): obj is IBaseFilterConsumer {
+ let typed = <IBaseFilterConsumer>obj;
+ return typed != null && typed.FilterOperand !== undefined;
+ }
+
+ public static EncodeQueryData(data: any): string {
+ const ret = [];
+ for (let d in data) {
+ ret.push(encodeURIComponent(d) + "=" + encodeURIComponent(data[d]));
+ }
+ return ret.join("&");
+ }
+
+ public static ToVegaAggregationString(agg: AggregateFunction): string {
+ if (agg === AggregateFunction.Avg) {
+ return "average";
+ }
+ else if (agg === AggregateFunction.Count) {
+ return "count";
+ }
+ else {
+ return "";
+ }
+ }
+
+ public static GetQueryVariable(variable: string) {
+ let query = window.location.search.substring(1);
+ let vars = query.split("&");
+ for (let i = 0; i < vars.length; i++) {
+ let pair = vars[i].split("=");
+ if (decodeURIComponent(pair[0]) == variable) {
+ return decodeURIComponent(pair[1]);
+ }
+ }
+ return undefined;
+ }
+}
+
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
new file mode 100644
index 000000000..bf59fbb43
--- /dev/null
+++ b/src/client/util/DocumentManager.ts
@@ -0,0 +1,99 @@
+import React = require('react')
+import { observer } from 'mobx-react';
+import { observable, action, computed } from 'mobx';
+import { Document } from "../../fields/Document"
+import { DocumentView } from '../views/nodes/DocumentView';
+import { KeyStore } from '../../fields/KeyStore';
+import { FieldWaiting } from '../../fields/Field';
+import { ListField } from '../../fields/ListField';
+
+
+export class DocumentManager {
+
+ //global holds all of the nodes (regardless of which collection they're in)
+ @observable
+ public DocumentViews: DocumentView[] = [];
+
+ // singleton instance
+ private static _instance: DocumentManager;
+
+ // create one and only one instance of NodeManager
+ public static get Instance(): DocumentManager {
+ return this._instance || (this._instance = new this());
+ }
+
+ //private constructor so no other class can create a nodemanager
+ private constructor() {
+ // this.DocumentViews = new Array<DocumentView>();
+ }
+
+ public getAllDocumentViews(collection: Document) {
+ return this.DocumentViews.filter(dv =>
+ dv.props.ContainingCollectionView && dv.props.ContainingCollectionView.props.Document == collection);
+ }
+
+ public getDocumentView(toFind: Document): DocumentView | null {
+
+ let toReturn: DocumentView | null;
+ toReturn = null;
+
+ //gets document view that is in a freeform canvas collection
+ DocumentManager.Instance.DocumentViews.map(view => {
+ let doc = view.props.Document;
+ // if (view.props.ContainingCollectionView instanceof CollectionFreeFormView) {
+
+ if (Object.is(doc, toFind)) {
+ toReturn = view;
+ return;
+ }
+ let docSrc = doc.GetT(KeyStore.Prototype, Document);
+ if (docSrc && docSrc != FieldWaiting && Object.is(docSrc, toFind)) {
+ toReturn = view;
+ }
+ })
+
+ return (toReturn);
+ }
+ public getDocumentViews(toFind: Document): DocumentView[] {
+
+ let toReturn: DocumentView[] = [];
+
+ //gets document view that is in a freeform canvas collection
+ DocumentManager.Instance.DocumentViews.map(view => {
+ let doc = view.props.Document;
+ // if (view.props.ContainingCollectionView instanceof CollectionFreeFormView) {
+
+ if (Object.is(doc, toFind)) {
+ toReturn.push(view);
+ } else {
+ let docSrc = doc.GetT(KeyStore.Prototype, Document);
+ if (docSrc && docSrc != FieldWaiting && Object.is(docSrc, toFind)) {
+ toReturn.push(view);
+ }
+ }
+ })
+
+ return (toReturn);
+ }
+
+ @computed
+ public get LinkedDocumentViews() {
+ return DocumentManager.Instance.DocumentViews.reduce((pairs, dv) => {
+ let linksList = dv.props.Document.GetT(KeyStore.LinkedToDocs, ListField);
+ if (linksList && linksList != FieldWaiting && linksList.Data.length) {
+ pairs.push(...linksList.Data.reduce((pairs, link) => {
+ if (link instanceof Document) {
+ let linkToDoc = link.GetT(KeyStore.LinkedToDocs, Document);
+ if (linkToDoc && linkToDoc != FieldWaiting) {
+ DocumentManager.Instance.getDocumentViews(linkToDoc).map(docView1 => {
+ pairs.push({ a: dv, b: docView1, l: link })
+ })
+ }
+ }
+ return pairs;
+ }, [] as { a: DocumentView, b: DocumentView, l: Document }[]));
+ }
+ return pairs;
+ }, [] as { a: DocumentView, b: DocumentView, l: Document }[]);
+ }
+} \ No newline at end of file
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 513a6ac9e..9ffe964ef 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -2,18 +2,21 @@ import { DocumentDecorations } from "../views/DocumentDecorations";
import { CollectionDockingView } from "../views/collections/CollectionDockingView";
import { Document } from "../../fields/Document"
import { action } from "mobx";
-import { DocumentView } from "../views/nodes/DocumentView";
import { ImageField } from "../../fields/ImageField";
import { KeyStore } from "../../fields/KeyStore";
+import { CollectionView } from "../views/collections/CollectionView";
+import { DocumentView } from "../views/nodes/DocumentView";
-export function setupDrag(_reference: React.RefObject<HTMLDivElement>, docFunc: () => Document) {
+export function setupDrag(_reference: React.RefObject<HTMLDivElement>, docFunc: () => Document, removeFunc: (containingCollection: CollectionView) => void = () => { }) {
let onRowMove = action((e: PointerEvent): void => {
e.stopPropagation();
e.preventDefault();
document.removeEventListener("pointermove", onRowMove);
document.removeEventListener('pointerup', onRowUp);
- DragManager.StartDrag(_reference.current!, { document: docFunc() });
+ var dragData = new DragManager.DocumentDragData([docFunc()]);
+ dragData.removeDocument = removeFunc;
+ DragManager.StartDocumentDrag([_reference.current!], dragData);
});
let onRowUp = action((e: PointerEvent): void => {
document.removeEventListener("pointermove", onRowMove);
@@ -24,7 +27,7 @@ export function setupDrag(_reference: React.RefObject<HTMLDivElement>, docFunc:
if (e.button == 0) {
e.stopPropagation();
if (e.shiftKey) {
- CollectionDockingView.Instance.StartOtherDrag(docFunc(), e);
+ CollectionDockingView.Instance.StartOtherDrag([docFunc()], e);
} else {
document.addEventListener("pointermove", onRowMove);
document.addEventListener('pointerup', onRowUp);
@@ -70,11 +73,12 @@ export namespace DragManager {
export interface DropOptions {
handlers: DropHandlers;
}
-
export class DropEvent {
constructor(readonly x: number, readonly y: number, readonly data: { [id: string]: any }) { }
}
+
+
export interface DropHandlers {
drop: (e: Event, de: DropEvent) => void;
}
@@ -96,46 +100,84 @@ export namespace DragManager {
};
}
- export function StartDrag(ele: HTMLElement, dragData: { [id: string]: any }, options?: DragOptions) {
- DocumentDecorations.Instance.Hidden = true;
+ export class DocumentDragData {
+ constructor(dragDoc: Document[]) {
+ this.draggedDocuments = dragDoc;
+ this.droppedDocuments = dragDoc;
+ }
+ draggedDocuments: Document[];
+ droppedDocuments: Document[];
+ xOffset?: number;
+ yOffset?: number;
+ aliasOnDrop?: boolean;
+ removeDocument?: (collectionDrop: CollectionView) => void;
+ [id: string]: any;
+ }
+
+ export function StartDocumentDrag(eles: HTMLElement[], dragData: DocumentDragData, options?: DragOptions) {
+ StartDrag(eles, dragData, options, (dropData: { [id: string]: any }) => dropData.droppedDocuments = dragData.aliasOnDrop ? dragData.draggedDocuments.map(d => d.CreateAlias()) : dragData.draggedDocuments);
+ }
+
+ export class LinkDragData {
+ constructor(linkSourceDoc: DocumentView) {
+ this.linkSourceDocumentView = linkSourceDoc;
+ }
+ linkSourceDocumentView: DocumentView;
+ [id: string]: any;
+ }
+ export function StartLinkDrag(ele: HTMLElement, dragData: LinkDragData, options?: DragOptions) {
+ StartDrag([ele], dragData, options);
+ }
+ function StartDrag(eles: HTMLElement[], dragData: { [id: string]: any }, options?: DragOptions, finishDrag?: (dropData: { [id: string]: any }) => void) {
if (!dragDiv) {
dragDiv = document.createElement("div");
+ dragDiv.className = "dragManager-dragDiv"
DragManager.Root().appendChild(dragDiv);
}
- const w = ele.offsetWidth, h = ele.offsetHeight;
- const rect = ele.getBoundingClientRect();
- const scaleX = rect.width / w, scaleY = rect.height / h;
- let x = rect.left, y = rect.top;
- // const offsetX = e.x - rect.left, offsetY = e.y - rect.top;
-
- let dragElement = ele.cloneNode(true) as HTMLElement;
- dragElement.style.opacity = "0.7";
- dragElement.style.position = "absolute";
- dragElement.style.bottom = "";
- dragElement.style.left = "";
- dragElement.style.transformOrigin = "0 0";
- dragElement.style.zIndex = "1000";
- dragElement.style.transform = `translate(${x}px, ${y}px) scale(${scaleX}, ${scaleY})`;
- dragElement.style.width = `${rect.width / scaleX}px`;
- dragElement.style.height = `${rect.height / scaleY}px`;
-
- // bcz: PDFs don't show up if you clone them because they contain a canvas.
- // however, PDF's have a thumbnail field that contains an image of their canvas.
- // So we replace the pdf's canvas with the image thumbnail
- const docView: DocumentView = dragData["documentView"];
- const doc: Document = docView ? docView.props.Document : dragData["document"];
- var pdfBox = dragElement.getElementsByClassName("pdfBox-cont")[0] as HTMLElement;
- let thumbnail = doc.GetT(KeyStore.Thumbnail, ImageField);
- if (pdfBox && pdfBox.childElementCount && thumbnail) {
- let img = new Image();
- img!.src = thumbnail.toString();
- img!.style.position = "absolute";
- img!.style.width = `${rect.width / scaleX}px`;
- img!.style.height = `${rect.height / scaleY}px`;
- pdfBox.replaceChild(img!, pdfBox.children[0])
- }
- dragDiv.appendChild(dragElement);
+ let scaleXs: number[] = [];
+ let scaleYs: number[] = [];
+ let xs: number[] = [];
+ let ys: number[] = [];
+
+ const docs: Document[] = dragData instanceof DocumentDragData ? dragData.draggedDocuments : [];
+ let dragElements = eles.map(ele => {
+ const w = ele.offsetWidth, h = ele.offsetHeight;
+ const rect = ele.getBoundingClientRect();
+ const scaleX = rect.width / w, scaleY = rect.height / h;
+ let x = rect.left, y = rect.top;
+ xs.push(x); ys.push(y);
+ scaleXs.push(scaleX); scaleYs.push(scaleY);
+ let dragElement = ele.cloneNode(true) as HTMLElement;
+ dragElement.style.opacity = "0.7";
+ dragElement.style.position = "absolute";
+ dragElement.style.bottom = "";
+ dragElement.style.left = "";
+ dragElement.style.transformOrigin = "0 0";
+ dragElement.style.zIndex = "1000";
+ dragElement.style.transform = `translate(${x}px, ${y}px) scale(${scaleX}, ${scaleY})`;
+ dragElement.style.width = `${rect.width / scaleX}px`;
+ dragElement.style.height = `${rect.height / scaleY}px`;
+
+ // bcz: PDFs don't show up if you clone them because they contain a canvas.
+ // however, PDF's have a thumbnail field that contains an image of their canvas.
+ // So we replace the pdf's canvas with the image thumbnail
+ if (docs.length) {
+ var pdfBox = dragElement.getElementsByClassName("pdfBox-cont")[0] as HTMLElement;
+ let thumbnail = docs[0].GetT(KeyStore.Thumbnail, ImageField);
+ if (pdfBox && pdfBox.childElementCount && thumbnail) {
+ let img = new Image();
+ img!.src = thumbnail.toString();
+ img!.style.position = "absolute";
+ img!.style.width = `${rect.width / scaleX}px`;
+ img!.style.height = `${rect.height / scaleY}px`;
+ pdfBox.replaceChild(img!, pdfBox.children[0])
+ }
+ }
+
+ dragDiv.appendChild(dragElement);
+ return dragElement;
+ });
let hideSource = false;
if (options) {
@@ -145,58 +187,64 @@ export namespace DragManager {
hideSource = options.hideSource();
}
}
- const wasHidden = ele.hidden;
- if (hideSource) {
- ele.hidden = true;
- }
+ eles.map(ele => ele.hidden = hideSource);
+
const moveHandler = (e: PointerEvent) => {
e.stopPropagation();
e.preventDefault();
- x += e.movementX;
- y += e.movementY;
+ if (dragData instanceof DocumentDragData)
+ dragData.aliasOnDrop = e.ctrlKey || e.altKey;
if (e.shiftKey) {
abortDrag();
- CollectionDockingView.Instance.StartOtherDrag(doc, { pageX: e.pageX, pageY: e.pageY, preventDefault: () => { }, button: 0 });
+ CollectionDockingView.Instance.StartOtherDrag(docs, { pageX: e.pageX, pageY: e.pageY, preventDefault: () => { }, button: 0 });
}
- dragElement.style.transform = `translate(${x}px, ${y}px) scale(${scaleX}, ${scaleY})`;
+ dragElements.map((dragElement, i) => dragElement.style.transform = `translate(${xs[i] += e.movementX}px, ${ys[i] += e.movementY}px) scale(${scaleXs[i]}, ${scaleYs[i]})`);
};
const abortDrag = () => {
document.removeEventListener("pointermove", moveHandler, true);
document.removeEventListener("pointerup", upHandler);
- dragDiv.removeChild(dragElement);
- if (hideSource && !wasHidden) {
- ele.hidden = false;
- }
+ dragElements.map(dragElement => dragDiv.removeChild(dragElement));
+ eles.map(ele => ele.hidden = false);
}
const upHandler = (e: PointerEvent) => {
abortDrag();
- FinishDrag(ele, e, dragData, options);
+ FinishDrag(eles, e, dragData, options, finishDrag);
};
document.addEventListener("pointermove", moveHandler, true);
document.addEventListener("pointerup", upHandler);
}
- function FinishDrag(dragEle: HTMLElement, e: PointerEvent, dragData: { [index: string]: any }, options?: DragOptions) {
- let parent = dragEle.parentElement;
- if (parent)
- parent.removeChild(dragEle);
+ function FinishDrag(dragEles: HTMLElement[], e: PointerEvent, dragData: { [index: string]: any }, options?: DragOptions, finishDrag?: (dragData: { [index: string]: any }) => void) {
+ let removed = dragEles.map(dragEle => {
+ let parent = dragEle.parentElement;
+ if (parent)
+ parent.removeChild(dragEle);
+ return [dragEle, parent];
+ });
const target = document.elementFromPoint(e.x, e.y);
- if (parent)
- parent.appendChild(dragEle);
- if (!target) {
- return;
- }
- target.dispatchEvent(new CustomEvent<DropEvent>("dashOnDrop", {
- bubbles: true,
- detail: {
- x: e.x,
- y: e.y,
- data: dragData
+ removed.map(r => {
+ let dragEle: HTMLElement = r[0]!;
+ let parent: HTMLElement | null = r[1];
+ if (parent)
+ parent.appendChild(dragEle);
+ });
+ if (target) {
+ if (finishDrag)
+ finishDrag(dragData);
+
+ target.dispatchEvent(new CustomEvent<DropEvent>("dashOnDrop", {
+ bubbles: true,
+ detail: {
+ x: e.x,
+ y: e.y,
+ data: dragData
+ }
+ }));
+
+ if (options) {
+ options.handlers.dragComplete({});
}
- }));
- if (options) {
- options.handlers.dragComplete({});
}
DocumentDecorations.Instance.Hidden = false;
}
diff --git a/src/client/util/RichTextRules.ts b/src/client/util/RichTextRules.ts
new file mode 100644
index 000000000..3b8396510
--- /dev/null
+++ b/src/client/util/RichTextRules.ts
@@ -0,0 +1,43 @@
+import {
+ inputRules,
+ wrappingInputRule,
+ textblockTypeInputRule,
+ smartQuotes,
+ emDash,
+ ellipsis
+} from "prosemirror-inputrules";
+import { Schema, NodeSpec, MarkSpec, DOMOutputSpecArray, NodeType } from "prosemirror-model";
+
+import { schema } from "./RichTextSchema";
+
+export const inpRules = {
+ rules: [
+ ...smartQuotes,
+ ellipsis,
+ emDash,
+
+ // > blockquote
+ wrappingInputRule(/^\s*>\s$/, schema.nodes.blockquote),
+
+ // 1. ordered list
+ wrappingInputRule(
+ /^(\d+)\.\s$/,
+ schema.nodes.ordered_list,
+ match => ({ order: +match[1] }),
+ (match, node) => node.childCount + node.attrs.order === +match[1]
+ ),
+
+ // * bullet list
+ wrappingInputRule(/^\s*([-+*])\s$/, schema.nodes.bullet_list),
+
+ // ``` code block
+ textblockTypeInputRule(/^```$/, schema.nodes.code_block),
+
+ // # heading
+ textblockTypeInputRule(
+ new RegExp("^(#{1,6})\\s$"),
+ schema.nodes.heading,
+ match => ({ level: match[1].length })
+ )
+ ]
+};
diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx
index abf448c9f..2a3c1da6e 100644
--- a/src/client/util/RichTextSchema.tsx
+++ b/src/client/util/RichTextSchema.tsx
@@ -1,12 +1,15 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { Schema, NodeSpec, MarkSpec, DOMOutputSpecArray } from "prosemirror-model"
+import { Schema, NodeSpec, MarkSpec, DOMOutputSpecArray, NodeType } from "prosemirror-model"
import { joinUp, lift, setBlockType, toggleMark, wrapIn } from 'prosemirror-commands'
import { redo, undo } from 'prosemirror-history'
-import { orderedList, bulletList, listItem } from 'prosemirror-schema-list'
+import { orderedList, bulletList, listItem, } from 'prosemirror-schema-list'
+import { EditorState, Transaction, NodeSelection, } from "prosemirror-state";
+import { EditorView, } from "prosemirror-view";
const pDOM: DOMOutputSpecArray = ["p", 0], blockquoteDOM: DOMOutputSpecArray = ["blockquote", 0], hrDOM: DOMOutputSpecArray = ["hr"],
preDOM: DOMOutputSpecArray = ["pre", ["code", 0]], brDOM: DOMOutputSpecArray = ["br"], ulDOM: DOMOutputSpecArray = ["ul", 0]
+
// :: Object
// [Specs](#model.NodeSpec) for the nodes defined in this schema.
export const nodes: { [index: string]: NodeSpec } = {
@@ -113,12 +116,22 @@ export const nodes: { [index: string]: NodeSpec } = {
content: 'list_item+',
group: 'block'
},
+ //this doesn't currently work for some reason
bullet_list: {
+ ...bulletList,
content: 'list_item+',
group: 'block',
- parseDOM: [{ tag: "ul" }, { style: "list-style-type=disc;" }],
- toDOM() { return ulDOM }
- },
+ // parseDOM: [{ tag: "ul" }, { style: 'list-style-type=disc' }],
+ // toDOM() { return ulDOM }
+ },
+ //bullet_list: {
+ // content: 'list_item+',
+ // group: 'block',
+ //active: blockActive(schema.nodes.bullet_list),
+ //enable: wrapInList(schema.nodes.bullet_list),
+ //run: wrapInList(schema.nodes.bullet_list),
+ //select: state => true,
+ // },
list_item: {
...listItem,
content: 'paragraph block*'
diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts
index 46bd1a206..4e97b9401 100644
--- a/src/client/util/Scripting.ts
+++ b/src/client/util/Scripting.ts
@@ -15,6 +15,8 @@ import { ListField } from "../../fields/ListField";
// @ts-ignore
import * as typescriptlib from '!!raw-loader!./type_decls.d'
+import { Documents } from "../documents/Documents";
+import { Key } from "../../fields/Key";
export interface ExecutableScript {
@@ -28,9 +30,9 @@ function Compile(script: string | undefined, diagnostics: Opt<any[]>, scope: { [
let func: () => Opt<Field>;
if (compiled && script) {
- let fieldTypes = [Document, NumberField, TextField, ImageField, RichTextField, ListField];
- let paramNames = ["KeyStore", ...fieldTypes.map(fn => fn.name)];
- let params: any[] = [KeyStore, ...fieldTypes]
+ let fieldTypes = [Document, NumberField, TextField, ImageField, RichTextField, ListField, Key];
+ let paramNames = ["KeyStore", "Documents", ...fieldTypes.map(fn => fn.name)];
+ let params: any[] = [KeyStore, Documents, ...fieldTypes]
for (let prop in scope) {
if (prop === "this") {
continue;
@@ -110,7 +112,7 @@ export function CompileScript(script: string, scope?: { [name: string]: any }, a
let host = new ScriptingCompilerHost;
let funcScript = `(function() {
${addReturn ? `return ${script};` : script}
- })()`
+ }).apply(this)`
host.writeFile("file.ts", funcScript);
host.writeFile('node_modules/typescript/lib/lib.d.ts', typescriptlib);
let program = ts.createProgram(["file.ts"], {}, host);
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index 1a711ae64..1354e32e1 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -1,5 +1,6 @@
import { observable, action } from "mobx";
import { DocumentView } from "../views/nodes/DocumentView";
+import { Document } from "../../fields/Document"
export namespace SelectionManager {
class Manager {
@@ -29,8 +30,18 @@ export namespace SelectionManager {
return manager.SelectedDocuments.indexOf(doc) !== -1;
}
- export function DeselectAll(): void {
- manager.SelectedDocuments = []
+ export function DeselectAll(except?: Document): void {
+ let found: DocumentView | undefined = undefined;
+ if (except) {
+ for (let i = 0; i < manager.SelectedDocuments.length; i++) {
+ let view = manager.SelectedDocuments[i];
+ if (view.props.Document == except)
+ found = view;
+ }
+ }
+ manager.SelectedDocuments.length = 0;
+ if (found)
+ manager.SelectedDocuments.push(found);
}
export function SelectedDocuments(): Array<DocumentView> {
diff --git a/src/client/util/TooltipTextMenu.scss b/src/client/util/TooltipTextMenu.scss
index fa43f5326..ea580d104 100644
--- a/src/client/util/TooltipTextMenu.scss
+++ b/src/client/util/TooltipTextMenu.scss
@@ -1,8 +1,9 @@
+@import "../views/global_variables";
.tooltipMenu {
position: absolute;
z-index: 20;
- background: rgb(19, 18, 18);
+ background: $dark-color;
border: 1px solid silver;
border-radius: 4px;
padding: 2px 10px;
@@ -31,14 +32,14 @@
bottom: -4.5px;
border: 5px solid transparent;
border-bottom-width: 0;
- border-top-color: black;
+ border-top-color: $dark-color;
}
.menuicon {
display: inline-block;
border-right: 1px solid rgba(0, 0, 0, 0.2);
//color: rgb(19, 18, 18);
- color: white;
+ color: $light-color;
line-height: 1;
padding: 0px 2px;
margin: 1px;
diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx
index 3b87fe9de..2a613ba8b 100644
--- a/src/client/util/TooltipTextMenu.tsx
+++ b/src/client/util/TooltipTextMenu.tsx
@@ -2,10 +2,10 @@ import { action, IReactionDisposer, reaction } from "mobx";
import { baseKeymap } from "prosemirror-commands";
import { history, redo, undo } from "prosemirror-history";
import { keymap } from "prosemirror-keymap";
-const { exampleSetup } = require("prosemirror-example-setup")
-import { EditorState, Transaction, } from "prosemirror-state";
+import { EditorState, Transaction, NodeSelection } from "prosemirror-state";
import { EditorView } from "prosemirror-view";
import { schema } from "./RichTextSchema";
+import { Schema, NodeType } from "prosemirror-model"
import React = require("react")
import "./TooltipTextMenu.scss";
const { toggleMark, setBlockType, wrapIn } = require("prosemirror-commands");
@@ -16,7 +16,7 @@ import {
} from '@fortawesome/free-solid-svg-icons';
-
+//appears above a selection of text in a RichTextBox to give user options such as Bold, Italics, etc.
export class TooltipTextMenu {
private tooltip: HTMLElement;
@@ -39,7 +39,8 @@ export class TooltipTextMenu {
{ command: toggleMark(schema.marks.strikethrough), dom: this.icon("S", "strikethrough") },
{ command: toggleMark(schema.marks.superscript), dom: this.icon("s", "superscript") },
{ command: toggleMark(schema.marks.subscript), dom: this.icon("s", "subscript") },
- { command: wrapInList(schema.nodes.bullet_list), dom: this.icon(":", "bullets") }
+ //this doesn't work currently - look into notion of active block
+ { command: wrapInList(schema.nodes.bullet_list), dom: this.icon(":", "bullets") },
]
items.forEach(({ dom }) => this.tooltip.appendChild(dom));
@@ -49,7 +50,9 @@ export class TooltipTextMenu {
view.focus();
items.forEach(({ command, dom }) => {
if (dom.contains(e.srcElement)) {
- command(view.state, view.dispatch, view)
+ let active = command(view.state, view.dispatch, view);
+ //uncomment this if we want the bullet button to disappear if current selection is bulleted
+ // dom.style.display = active ? "" : "none"
}
})
})
@@ -66,13 +69,25 @@ export class TooltipTextMenu {
return span;
}
- blockActive(view: EditorView) {
- const { $from, to } = view.state.selection
+ //adapted this method - use it to check if block has a tag (ie bulleting)
+ blockActive(type: NodeType<Schema<string, string>>, state: EditorState) {
+ let attrs = {};
+
+ if (state.selection instanceof NodeSelection) {
+ const sel: NodeSelection = state.selection;
+ let $from = sel.$from;
+ let to = sel.to;
+ let node = sel.node;
+
+ if (node) {
+ return node.hasMarkup(type, attrs);
+ }
- return to <= $from.end() && $from.parent.hasMarkup(schema.nodes.bulletList);
+ return to <= $from.end() && $from.parent.hasMarkup(type, attrs);
+ }
}
- //this doesn't currently work but hopefully will soon
+ //this doesn't currently work but could be used to use icons for buttons
unorderedListIcon(): HTMLSpanElement {
let span = document.createElement("span");
let icon = document.createElement("FontAwesomeIcon");
@@ -105,8 +120,6 @@ export class TooltipTextMenu {
// Otherwise, reposition it and update its content
this.tooltip.style.display = ""
let { from, to } = state.selection
- // These are in screen coordinates
- //check this - tranform
let start = view.coordsAtPos(from), end = view.coordsAtPos(to)
// The box in which the tooltip is positioned, to use as base
let box = this.tooltip.offsetParent!.getBoundingClientRect()
@@ -116,8 +129,9 @@ export class TooltipTextMenu {
this.tooltip.style.left = (left - box.left) + "px"
let width = Math.abs(start.left - end.left) / 2;
let mid = Math.min(start.left, end.left) + width;
+
//THIS WIDTH IS 15 * NUMBER OF ICONS + 15
- this.tooltip.style.width = 120 + "px";
+ this.tooltip.style.width = 122 + "px";
this.tooltip.style.bottom = (box.bottom - start.top) + "px";
}
diff --git a/src/client/util/UndoManager.ts b/src/client/util/UndoManager.ts
index 46ad558f3..6d1b2f1b8 100644
--- a/src/client/util/UndoManager.ts
+++ b/src/client/util/UndoManager.ts
@@ -1,4 +1,14 @@
import { observable, action } from "mobx";
+import 'source-map-support/register'
+import { Without } from "../../Utils";
+
+function getBatchName(target: any, key: string | symbol): string {
+ let keyName = key.toString();
+ if (target && target.constructor && target.constructor.name) {
+ return `${target.constructor.name}.${keyName}`;
+ }
+ return keyName;
+}
function propertyDecorator(target: any, key: string | symbol) {
Object.defineProperty(target, key, {
@@ -13,11 +23,11 @@ function propertyDecorator(target: any, key: string | symbol) {
writable: true,
configurable: true,
value: function (...args: any[]) {
+ let batch = UndoManager.StartBatch(getBatchName(target, key));
try {
- UndoManager.StartBatch();
return value.apply(this, args);
} finally {
- UndoManager.EndBatch();
+ batch.end();
}
}
})
@@ -32,11 +42,11 @@ export function undoBatch(target: any, key: string | symbol, descriptor?: TypedP
const oldFunction = descriptor.value;
descriptor.value = function (...args: any[]) {
+ let batch = UndoManager.StartBatch(getBatchName(target, key));
try {
- UndoManager.StartBatch()
return oldFunction.apply(this, args)
} finally {
- UndoManager.EndBatch()
+ batch.end();
}
}
@@ -70,26 +80,53 @@ export namespace UndoManager {
return redoStack.length > 0;
}
- export function StartBatch(): void {
+ let openBatches: Batch[] = [];
+ export function GetOpenBatches(): Without<Batch, 'end'>[] {
+ return openBatches;
+ }
+ export class Batch {
+ private disposed: boolean = false;
+
+ constructor(readonly batchName: string) {
+ openBatches.push(this);
+ }
+
+ private dispose = (cancel: boolean) => {
+ if (this.disposed) {
+ throw new Error("Cannot dispose an already disposed batch");
+ }
+ this.disposed = true;
+ openBatches.splice(openBatches.indexOf(this));
+ EndBatch(cancel);
+ }
+
+ end = () => { this.dispose(false); }
+ cancel = () => { this.dispose(true); }
+ }
+
+ export function StartBatch(batchName: string): Batch {
batchCounter++;
if (batchCounter > 0) {
currentBatch = [];
}
+ return new Batch(batchName);
}
- export const EndBatch = action(() => {
+ const EndBatch = action((cancel: boolean = false) => {
batchCounter--;
if (batchCounter === 0 && currentBatch && currentBatch.length) {
- undoStack.push(currentBatch);
+ if (!cancel) {
+ undoStack.push(currentBatch);
+ }
redoStack.length = 0;
currentBatch = undefined;
}
})
- export function RunInBatch(fn: () => void) {
- StartBatch();
+ export function RunInBatch(fn: () => void, batchName: string) {
+ let batch = StartBatch(batchName);
fn();
- EndBatch();
+ batch.end();
}
export const Undo = action(() => {
diff --git a/src/client/util/jsx-decl.d.ts b/src/client/util/jsx-decl.d.ts
new file mode 100644
index 000000000..532f06178
--- /dev/null
+++ b/src/client/util/jsx-decl.d.ts
@@ -0,0 +1 @@
+declare module 'react-jsx-parser';
diff --git a/src/client/util/type_decls.d b/src/client/util/type_decls.d
index 679f73f42..4f69053b1 100644
--- a/src/client/util/type_decls.d
+++ b/src/client/util/type_decls.d
@@ -174,6 +174,7 @@ declare class ListField<T> extends BasicField<T[]>{
Copy(): Field;
}
declare class Key extends Field {
+ constructor(name:string);
Name: string;
TrySetValue(value: any): boolean;
GetValue(): any;
@@ -213,3 +214,12 @@ declare class Document extends Field {
GetAllPrototypes(): Document[];
MakeDelegate(): Document;
}
+
+declare const KeyStore: {
+ [name: string]: Key;
+}
+
+// @ts-ignore
+declare const console: any;
+
+declare const Documents: any;
diff --git a/.DS_Store b/src/client/views/.DS_Store
index 60446cb09..0964d5ff3 100644
--- a/.DS_Store
+++ b/src/client/views/.DS_Store
Binary files differ
diff --git a/src/client/views/ContextMenu.scss b/src/client/views/ContextMenu.scss
index ea40c8e99..f6830d9cd 100644
--- a/src/client/views/ContextMenu.scss
+++ b/src/client/views/ContextMenu.scss
@@ -1,41 +1,55 @@
+@import "global_variables";
.contextMenu-cont {
- position: absolute;
- display: flex;
- z-index: 1000;
- box-shadow: #AAAAAA .2vw .2vw .4vw;
- flex-direction: column;
+ position: absolute;
+ display: flex;
+ z-index: 1000;
+ box-shadow: $intermediate-color 0.2vw 0.2vw 0.4vw;
+ flex-direction: column;
+}
+
+.contextMenu-item:first-child {
+ background: $intermediate-color;
+ color: $light-color;
+}
+
+.contextMenu-item:first-child::placeholder {
+ color: $light-color;
+}
+
+.contextMenu-item:first-child:hover {
+ background: $intermediate-color;
+ color: $light-color;
}
.contextMenu-item {
- width: auto;
- height: auto;
- background: #F0F8FF;
- display: flex;
- justify-content: left;
- align-items: center;
- -webkit-touch-callout: none;
- -webkit-user-select: none;
- -khtml-user-select: none;
- -moz-user-select: none;
- -ms-user-select: none;
- user-select: none;
- transition: all .1s;
- border-width: .11px;
- border-style: none;
- border-color: rgb(187, 186, 186);
- border-bottom-style: solid;
- padding: 10px;
- white-space: nowrap;
- font-size: 1.5vw;
+ width: auto;
+ height: auto;
+ background: $light-color-secondary;
+ display: flex;
+ justify-content: left;
+ align-items: center;
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -khtml-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ transition: all 0.1s;
+ border-width: 0.11px;
+ border-style: none;
+ border-color: $intermediate-color;
+ border-bottom-style: solid;
+ padding: 10px;
+ white-space: nowrap;
+ font-size: 13px;
}
.contextMenu-item:hover {
- transition: all .1s;
- background: #B0E0E6;
+ transition: all 0.1s;
+ background: $lighter-alt-accent;
}
.contextMenu-description {
- font-size: 1.5vw;
- text-align: left;
- width: 8vw;
-} \ No newline at end of file
+ text-align: left;
+ width: 8vw;
+}
diff --git a/src/client/views/ContextMenu.tsx b/src/client/views/ContextMenu.tsx
index fcb934860..cfa8ea7b7 100644
--- a/src/client/views/ContextMenu.tsx
+++ b/src/client/views/ContextMenu.tsx
@@ -13,6 +13,8 @@ export class ContextMenu extends React.Component {
@observable private _pageY: number = 0;
@observable private _display: string = "none";
@observable private _searchString: string = "";
+ // afaik displaymenu can be called before all the items are added to the menu, so can't determine in displayMenu what the height of the menu will be
+ @observable private _yRelativeToTop: boolean = true;
private ref: React.RefObject<HTMLDivElement>;
@@ -44,8 +46,13 @@ export class ContextMenu extends React.Component {
@action
displayMenu(x: number, y: number) {
- this._pageX = x
- this._pageY = y
+ //maxX and maxY will change if the UI/font size changes, but will work for any amount
+ //of items added to the menu
+ let maxX = window.innerWidth - 150;
+ let maxY = window.innerHeight - ((this._items.length + 1/*for search box*/) * 34 + 30);
+
+ this._pageX = x > maxX ? maxX : x;
+ this._pageY = y > maxY ? maxY : y;
this._searchString = "";
@@ -54,8 +61,13 @@ export class ContextMenu extends React.Component {
intersects = (x: number, y: number): boolean => {
if (this.ref.current && this._display !== "none") {
- if (x >= this._pageX && x <= this._pageX + this.ref.current.getBoundingClientRect().width) {
- if (y >= this._pageY && y <= this._pageY + this.ref.current.getBoundingClientRect().height) {
+ let menuSize = { width: this.ref.current.getBoundingClientRect().width, height: this.ref.current.getBoundingClientRect().height };
+
+ let upperLeft = { x: this._pageX, y: this._yRelativeToTop ? this._pageY : window.innerHeight - (this._pageY + menuSize.height) };
+ let bottomRight = { x: this._pageX + menuSize.width, y: this._yRelativeToTop ? this._pageY + menuSize.height : window.innerHeight - this._pageY };
+
+ if (x >= upperLeft.x && x <= bottomRight.x) {
+ if (y >= upperLeft.y && y <= bottomRight.y) {
return true;
}
}
@@ -64,14 +76,15 @@ export class ContextMenu extends React.Component {
}
render() {
+ let style = this._yRelativeToTop ? { left: this._pageX, top: this._pageY, display: this._display } :
+ { left: this._pageX, bottom: this._pageY, display: this._display };
+
+
return (
- <div className="contextMenu-cont" style={{ left: this._pageX, top: this._pageY, display: this._display }} ref={this.ref}>
+ <div className="contextMenu-cont" style={style} ref={this.ref}>
<input className="contextMenu-item" type="text" placeholder="Search . . ." value={this._searchString} onChange={this.onChange}></input>
- {this._items.filter(prop => {
- return prop.description.toLowerCase().indexOf(this._searchString.toLowerCase()) !== -1;
- }).map(prop => {
- return <ContextMenuItem {...prop} key={prop.description} />
- })}
+ {this._items.filter(prop => prop.description.toLowerCase().indexOf(this._searchString.toLowerCase()) !== -1).
+ map(prop => <ContextMenuItem {...prop} key={prop.description} />)}
</div>
)
}
diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss
index 9bafbda44..272ea9e5d 100644
--- a/src/client/views/DocumentDecorations.scss
+++ b/src/client/views/DocumentDecorations.scss
@@ -1,17 +1,18 @@
+@import "global_variables";
#documentDecorations-container {
position: absolute;
display: grid;
z-index: 1000;
- grid-template-rows: 20px 20px 1fr 20px;
- grid-template-columns: 20px 1fr 20px;
+ grid-template-rows: 20px 8px 1fr 8px;
+ grid-template-columns: 8px 1fr 8px;
pointer-events: none;
#documentDecorations-centerCont {
background: none;
}
.documentDecorations-resizer {
pointer-events: auto;
- background: lightblue;
- opacity: 0.4;
+ background: $alt-accent;
+ opacity: 0.8;
}
#documentDecorations-topLeftResizer,
#documentDecorations-bottomRightResizer {
@@ -35,4 +36,95 @@
grid-column-end: 4;
pointer-events: auto;
}
+}
+
+.documentDecorations-background {
+ background:lightblue;
+ position: absolute;
+ opacity: 0.1;
+}
+
+// position: absolute;
+// display: grid;
+// z-index: 1000;
+// grid-template-rows: 20px 1fr 20px 0px;
+// grid-template-columns: 20px 1fr 20px;
+// pointer-events: none;
+// #documentDecorations-centerCont {
+// background: none;
+// }
+// .documentDecorations-resizer {
+// pointer-events: auto;
+// background: lightblue;
+// opacity: 0.4;
+// }
+// #documentDecorations-topLeftResizer,
+// #documentDecorations-bottomRightResizer {
+// cursor: nwse-resize;
+// }
+// #documentDecorations-topRightResizer,
+// #documentDecorations-bottomLeftResizer {
+// cursor: nesw-resize;
+// }
+// #documentDecorations-topResizer,
+// #documentDecorations-bottomResizer {
+// cursor: ns-resize;
+// }
+// #documentDecorations-leftResizer,
+// #documentDecorations-rightResizer {
+// cursor: ew-resize;
+// }
+// }
+.linkFlyout {
+ grid-column: 1/4
+}
+
+.linkButton-empty:hover {
+ background: $main-accent;
+ transform: scale(1.05);
+ cursor: pointer;
+}
+
+.linkButton-nonempty:hover {
+ background: $main-accent;
+ transform: scale(1.05);
+ cursor: pointer;
+}
+
+.linkButton-empty {
+ height: 20px;
+ width: 20px;
+ margin-top: 10px;
+ border-radius: 50%;
+ opacity: 0.9;
+ pointer-events: auto;
+ background-color: $dark-color;
+ color: $light-color;
+ text-transform: uppercase;
+ letter-spacing: 2px;
+ font-size: 75%;
+ transition: transform 0.2s;
+ text-align: center;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.linkButton-nonempty {
+ height: 20px;
+ width: 20px;
+ margin-top: 10px;
+ border-radius: 50%;
+ opacity: 0.9;
+ pointer-events: auto;
+ background-color: $dark-color;
+ color: $light-color;
+ text-transform: uppercase;
+ letter-spacing: 2px;
+ font-size: 75%;
+ transition: transform 0.2s;
+ text-align: center;
+ display: flex;
+ justify-content: center;
+ align-items: center;
} \ No newline at end of file
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 84009907a..572c265f3 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -1,30 +1,40 @@
-import { observable, computed, action } from "mobx";
-import React = require("react");
-import { SelectionManager } from "../util/SelectionManager";
+import { action, computed, observable } from "mobx";
import { observer } from "mobx-react";
-import './DocumentDecorations.scss'
-import { CollectionFreeFormView } from "./collections/CollectionFreeFormView";
+import { Key } from "../../fields/Key";
//import ContentEditable from 'react-contenteditable'
import { KeyStore } from "../../fields/KeyStore";
+import { ListField } from "../../fields/ListField";
import { NumberField } from "../../fields/NumberField";
-import { Document } from "../../fields/Document";
-import { DocumentView } from "./nodes/DocumentView";
-import { Key } from "../../fields/Key";
import { TextField } from "../../fields/TextField";
-import { BasicField } from "../../fields/BasicField";
-import { Field, FieldValue } from "../../fields/Field";
+import { DragManager } from "../util/DragManager";
+import { SelectionManager } from "../util/SelectionManager";
+import { CollectionView } from "./collections/CollectionView";
+import './DocumentDecorations.scss';
+import { DocumentView } from "./nodes/DocumentView";
+import { LinkMenu } from "./nodes/LinkMenu";
+import React = require("react");
+const higflyout = require("@hig/flyout");
+export const { anchorPoints } = higflyout;
+export const Flyout = higflyout.default;
@observer
export class DocumentDecorations extends React.Component<{}, { value: string }> {
static Instance: DocumentDecorations
private _resizer = ""
private _isPointerDown = false;
- @observable private _opacity = 1;
private keyinput: React.RefObject<HTMLInputElement>;
private _documents: DocumentView[] = SelectionManager.SelectedDocuments();
+ private _resizeBorderWidth = 16;
+ private _linkBoxHeight = 30;
+ private _titleHeight = 20;
+ private _linkButton = React.createRef<HTMLDivElement>();
//@observable private _title: string = this._documents[0].props.Document.Title;
@observable private _title: string = this._documents.length > 0 ? this._documents[0].props.Document.Title : "";
@observable private _fieldKey: Key = KeyStore.Title;
+ @observable private _hidden = false;
+ @observable private _opacity = 1;
+ @observable private _dragging = false;
+
constructor(props: Readonly<{}>) {
super(props)
@@ -84,7 +94,50 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
@computed
public get Hidden() { return this._hidden; }
public set Hidden(value: boolean) { this._hidden = value; }
- private _hidden: boolean = false;
+
+ _lastDrag: number[] = [0, 0];
+ onBackgroundDown = (e: React.PointerEvent): void => {
+ document.removeEventListener("pointermove", this.onBackgroundMove);
+ document.addEventListener("pointermove", this.onBackgroundMove);
+ document.removeEventListener("pointerup", this.onBackgroundUp);
+ document.addEventListener("pointerup", this.onBackgroundUp);
+ this._lastDrag = [e.clientX, e.clientY]
+ e.stopPropagation();
+ e.preventDefault();
+ }
+
+ @action
+ onBackgroundMove = (e: PointerEvent): void => {
+ let dragDocView = SelectionManager.SelectedDocuments()[0];
+ const [left, top] = dragDocView.props.ScreenToLocalTransform().inverse().transformPoint(0, 0);
+ let dragData = new DragManager.DocumentDragData(SelectionManager.SelectedDocuments().map(dv => dv.props.Document));
+ dragData.aliasOnDrop = false;
+ dragData.xOffset = e.x - left;
+ dragData.yOffset = e.y - top;
+ dragData.removeDocument = (dropCollectionView: CollectionView) =>
+ dragData.draggedDocuments.map(d => {
+ if (dragDocView.props.RemoveDocument && dragDocView.props.ContainingCollectionView !== dropCollectionView) {
+ dragDocView.props.RemoveDocument(d);
+ }
+ });
+ this._dragging = true;
+ document.removeEventListener("pointermove", this.onBackgroundMove);
+ document.removeEventListener("pointerup", this.onBackgroundUp);
+ DragManager.StartDocumentDrag(SelectionManager.SelectedDocuments().map(docView => (docView as any)._mainCont!.current!), dragData, {
+ handlers: {
+ dragComplete: action(() => this._dragging = false),
+ },
+ hideSource: true
+ })
+ e.stopPropagation();
+ }
+
+ onBackgroundUp = (e: PointerEvent): void => {
+ document.removeEventListener("pointermove", this.onBackgroundMove);
+ document.removeEventListener("pointerup", this.onBackgroundUp);
+ e.stopPropagation();
+ e.preventDefault();
+ }
onPointerDown = (e: React.PointerEvent): void => {
e.stopPropagation();
@@ -99,6 +152,43 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
}
}
+ onLinkButtonDown = (e: React.PointerEvent): void => {
+ // if ()
+ // let linkMenu = new LinkMenu(SelectionManager.SelectedDocuments()[0]);
+ // linkMenu.Hidden = false;
+ console.log("down");
+
+ e.stopPropagation();
+ document.removeEventListener("pointermove", this.onLinkButtonMoved)
+ document.addEventListener("pointermove", this.onLinkButtonMoved);
+ document.removeEventListener("pointerup", this.onLinkButtonUp)
+ document.addEventListener("pointerup", this.onLinkButtonUp);
+
+ }
+
+ onLinkButtonUp = (e: PointerEvent): void => {
+ document.removeEventListener("pointermove", this.onLinkButtonMoved)
+ document.removeEventListener("pointerup", this.onLinkButtonUp)
+ e.stopPropagation();
+ }
+
+
+ onLinkButtonMoved = (e: PointerEvent): void => {
+ if (this._linkButton.current != null) {
+ document.removeEventListener("pointermove", this.onLinkButtonMoved)
+ document.removeEventListener("pointerup", this.onLinkButtonUp)
+ let dragData = new DragManager.LinkDragData(SelectionManager.SelectedDocuments()[0]);
+ DragManager.StartLinkDrag(this._linkButton.current, dragData, {
+ handlers: {
+ dragComplete: action(() => { }),
+ },
+ hideSource: false
+ })
+ }
+ e.stopPropagation();
+ }
+
+
onPointerMove = (e: PointerEvent): void => {
e.stopPropagation();
e.preventDefault();
@@ -199,6 +289,12 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
return this._title;
}
+ changeFlyoutContent = (): void => {
+
+ }
+ // buttonOnPointerUp = (e: React.PointerEvent): void => {
+ // e.stopPropagation();
+ // }
render() {
var bounds = this.Bounds;
// console.log(this._documents.length)
@@ -210,12 +306,37 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
console.log("DocumentDecorations: Bounds Error")
return (null);
}
- return (
+
+ let linkButton = null;
+ if (SelectionManager.SelectedDocuments().length > 0) {
+ let selFirst = SelectionManager.SelectedDocuments()[0];
+ let linkToSize = selFirst.props.Document.GetData(KeyStore.LinkedToDocs, ListField, []).length;
+ let linkFromSize = selFirst.props.Document.GetData(KeyStore.LinkedFromDocs, ListField, []).length;
+ let linkCount = linkToSize + linkFromSize;
+ linkButton = (<Flyout
+ anchorPoint={anchorPoints.RIGHT_TOP}
+ content={
+ <LinkMenu docView={selFirst} changeFlyout={this.changeFlyoutContent}>
+ </LinkMenu>
+ }>
+ <div className={"linkButton-" + (selFirst.props.Document.GetData(KeyStore.LinkedToDocs, ListField, []).length ? "nonempty" : "empty")} onPointerDown={this.onLinkButtonDown} >{linkCount}</div>
+ </Flyout>);
+ }
+ return (<div className="documentDecorations">
+ <div className="documentDecorations-background" style={{
+ width: (bounds.r - bounds.x + this._resizeBorderWidth) + "px",
+ height: (bounds.b - bounds.y + this._resizeBorderWidth) + "px",
+ left: bounds.x - this._resizeBorderWidth / 2,
+ top: bounds.y - this._resizeBorderWidth / 2,
+ pointerEvents: this._dragging ? "none" : "all",
+ zIndex: SelectionManager.SelectedDocuments().length > 1 ? 1000 : 0,
+ }} onPointerDown={this.onBackgroundDown} onContextMenu={(e: React.MouseEvent) => { e.preventDefault(); e.stopPropagation() }} >
+ </div>
<div id="documentDecorations-container" style={{
- width: (bounds.r - bounds.x + 20 + 20) + "px",
- height: (bounds.b - bounds.y + 40 + 20) + "px",
- left: bounds.x - 20,
- top: bounds.y - 20 - 20,
+ width: (bounds.r - bounds.x + this._resizeBorderWidth) + "px",
+ height: (bounds.b - bounds.y + this._resizeBorderWidth + this._linkBoxHeight + this._titleHeight) + "px",
+ left: bounds.x - this._resizeBorderWidth / 2,
+ top: bounds.y - this._resizeBorderWidth / 2 - this._titleHeight,
opacity: this._opacity
}}>
<input ref={this.keyinput} className="title" type="text" name="dynbox" value={this.getValue()} onChange={this.handleChange} onPointerDown={this.onPointerDown} onKeyPress={this.enterPressed} />
@@ -228,7 +349,11 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
<div id="documentDecorations-bottomLeftResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
<div id="documentDecorations-bottomResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
<div id="documentDecorations-bottomRightResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
- </div>
+
+ <div title="View Links" className="linkFlyout" ref={this._linkButton}>{linkButton}</div>
+
+ </div >
+ </div>
)
}
} \ No newline at end of file
diff --git a/src/client/views/EditableView.scss b/src/client/views/EditableView.scss
new file mode 100644
index 000000000..be3c5069a
--- /dev/null
+++ b/src/client/views/EditableView.scss
@@ -0,0 +1,6 @@
+.editableView-container-editing {
+ overflow-wrap: break-word;
+ word-wrap: break-word;
+ hyphens: auto;
+ max-width: 300px;
+} \ No newline at end of file
diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx
index 84b1b91c3..29bf6add7 100644
--- a/src/client/views/EditableView.tsx
+++ b/src/client/views/EditableView.tsx
@@ -1,6 +1,7 @@
import React = require('react')
import { observer } from 'mobx-react';
-import { observable, action } from 'mobx';
+import { observable, action, trace } from 'mobx';
+import "./EditableView.scss"
export interface EditableProps {
/**
@@ -15,11 +16,14 @@ export interface EditableProps {
* */
SetValue(value: string): boolean;
+ OnFillDown?(value: string): void;
+
/**
* The contents to render when not editing
*/
contents: any;
height: number
+ display?: string;
}
/**
@@ -34,8 +38,13 @@ export class EditableView extends React.Component<EditableProps> {
@action
onKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
- if (e.key == "Enter" && !e.ctrlKey) {
- if (this.props.SetValue(e.currentTarget.value)) {
+ if (e.key == "Enter") {
+ if (!e.ctrlKey) {
+ if (this.props.SetValue(e.currentTarget.value)) {
+ this.editing = false;
+ }
+ } else if (this.props.OnFillDown) {
+ this.props.OnFillDown(e.currentTarget.value);
this.editing = false;
}
} else if (e.key == "Escape") {
@@ -46,11 +55,11 @@ export class EditableView extends React.Component<EditableProps> {
render() {
if (this.editing) {
return <input defaultValue={this.props.GetValue()} onKeyDown={this.onKeyDown} autoFocus onBlur={action(() => this.editing = false)}
- style={{ display: "inline" }}></input>
+ style={{ display: this.props.display }}></input>
} else {
return (
- <div className="editableView-container-editing" style={{ display: "inline", height: "100%", maxHeight: `${this.props.height}` }}
- onClick={action(() => this.editing = true)}>
+ <div className="editableView-container-editing" style={{ display: this.props.display, height: "auto", maxHeight: `${this.props.height}` }}
+ onClick={action(() => this.editing = true)} >
{this.props.contents}
</div>
)
diff --git a/src/client/views/InkingCanvas.scss b/src/client/views/InkingCanvas.scss
index f654b194b..35c8ee942 100644
--- a/src/client/views/InkingCanvas.scss
+++ b/src/client/views/InkingCanvas.scss
@@ -1,32 +1,30 @@
-.inking-canvas {
- position: fixed;
- top: -50000px;
- left: -50000px; // z-index: 99; //overlays ink on top of everything
- svg {
- width: 100000px;
- height: 100000px;
- .highlight {
- mix-blend-mode: multiply;
- }
- }
-}
+@import "global_variables";
-.inking-control {
+.inkingCanvas-paths-ink, .inkingCanvas-paths-markers, .inkingCanvas-noSelect, .inkingCanvas-canSelect {
position: absolute;
- right: 0;
- bottom: 75px;
- text-align: right;
- .ink-panel {
- margin-top: 12px;
- &:first {
- margin-top: 0;
- }
- }
- .ink-size {
- display: flex;
- justify-content: space-between;
- input {
- width: 85%;
- }
- }
-} \ No newline at end of file
+ width: 8192px;
+ height: 8192px;
+ cursor:"crosshair";
+ pointer-events: auto;
+
+}
+.inkingCanvas-canSelect,
+.inkingCanvas-noSelect {
+ top:-50000px;
+ left:-50000px;
+ width: 100000px;
+ height: 100000px;
+}
+.inkingCanvas-noSelect {
+ pointer-events: none;
+ cursor: "arrow";
+}
+.inkingCanvas-paths-ink, .inkingCanvas-paths-markers {
+ pointer-events: none;
+ z-index: 10000; // overlays ink on top of everything
+ cursor: "arrow";
+}
+.inkingCanvas-paths-markers {
+ mix-blend-mode: multiply;
+}
+
diff --git a/src/client/views/InkingCanvas.tsx b/src/client/views/InkingCanvas.tsx
index 0d87c1239..cad4b74b1 100644
--- a/src/client/views/InkingCanvas.tsx
+++ b/src/client/views/InkingCanvas.tsx
@@ -1,117 +1,112 @@
+import { action, computed, trace, observable, runInAction } from "mobx";
import { observer } from "mobx-react";
-import { action, computed } from "mobx";
-import { InkingControl } from "./InkingControl";
-import React = require("react");
-import { Transform } from "../util/Transform";
import { Document } from "../../fields/Document";
-import { KeyStore } from "../../fields/KeyStore";
+import { FieldWaiting } from "../../fields/Field";
import { InkField, InkTool, StrokeData, StrokeMap } from "../../fields/InkField";
-import { JsxArgs } from "./nodes/DocumentView";
-import { InkingStroke } from "./InkingStroke";
-import "./InkingCanvas.scss"
-import { CollectionDockingView } from "./collections/CollectionDockingView";
+import { KeyStore } from "../../fields/KeyStore";
import { Utils } from "../../Utils";
-import { FieldWaiting } from "../../fields/Field";
-import { getMapLikeKeys } from "mobx/lib/internal";
-
+import { Transform } from "../util/Transform";
+import "./InkingCanvas.scss";
+import { InkingControl } from "./InkingControl";
+import { InkingStroke } from "./InkingStroke";
+import React = require("react");
interface InkCanvasProps {
getScreenTransform: () => Transform;
Document: Document;
+ children: () => JSX.Element[];
}
@observer
export class InkingCanvas extends React.Component<InkCanvasProps> {
+ maxCanvasDim = 8192 / 2; // 1/2 of the maximum canvas dimension for Chrome
+ @observable inkMidX: number = 0;
+ @observable inkMidY: number = 0;
+ private _currentStrokeId: string = "";
+ public static IntersectStrokeRect(stroke: StrokeData, selRect: { left: number, top: number, width: number, height: number }): boolean {
+ return stroke.pathData.reduce((inside: boolean, val) => inside ||
+ (selRect.left < val.x && selRect.left + selRect.width > val.x &&
+ selRect.top < val.y && selRect.top + selRect.height > val.y)
+ , false);
+ }
- private _isDrawing: boolean = false;
- private _idGenerator: string = "";
-
- constructor(props: Readonly<InkCanvasProps>) {
- super(props);
+ componentDidMount() {
+ this.props.Document.GetTAsync(KeyStore.Ink, InkField, ink => runInAction(() => {
+ if (ink) {
+ let bounds = Array.from(ink.Data).reduce(([mix, max, miy, may], [id, strokeData]) =>
+ strokeData.pathData.reduce(([mix, max, miy, may], p) =>
+ [Math.min(mix, p.x), Math.max(max, p.x), Math.min(miy, p.y), Math.max(may, p.y)],
+ [mix, max, miy, may]),
+ [Number.MAX_VALUE, Number.MIN_VALUE, Number.MAX_VALUE, Number.MIN_VALUE]);
+ this.inkMidX = (bounds[0] + bounds[1]) / 2;
+ this.inkMidY = (bounds[2] + bounds[3]) / 2;
+ }
+ }));
}
@computed
get inkData(): StrokeMap {
let map = this.props.Document.GetT(KeyStore.Ink, InkField);
- if (!map || map === FieldWaiting) {
- return new Map;
- }
- return new Map(map.Data);
+ return !map || map === FieldWaiting ? new Map : new Map(map.Data);
}
set inkData(value: StrokeMap) {
- this.props.Document.SetData(KeyStore.Ink, value, InkField);
- }
-
- componentDidMount() {
- document.addEventListener("mouseup", this.handleMouseUp);
- }
-
- componentWillUnmount() {
- document.removeEventListener("mouseup", this.handleMouseUp);
+ this.props.Document.SetDataOnPrototype(KeyStore.Ink, value, InkField);
}
-
@action
- handleMouseDown = (e: React.PointerEvent): void => {
- if (e.button != 0 ||
- InkingControl.Instance.selectedTool === InkTool.None) {
+ onPointerDown = (e: React.PointerEvent): void => {
+ if (e.button != 0 || e.altKey || e.ctrlKey || InkingControl.Instance.selectedTool === InkTool.None) {
return;
}
- e.stopPropagation()
- if (InkingControl.Instance.selectedTool === InkTool.Eraser) {
- return
- }
- e.stopPropagation()
- const point = this.relativeCoordinatesForEvent(e);
-
- // start the new line, saves a uuid to represent the field of the stroke
- this._idGenerator = Utils.GenerateGuid();
- let data = this.inkData;
- data.set(this._idGenerator,
- {
- pathData: [point],
+ document.addEventListener("pointermove", this.onPointerMove, true);
+ document.addEventListener("pointerup", this.onPointerUp, true);
+ e.stopPropagation();
+ e.preventDefault();
+
+ if (InkingControl.Instance.selectedTool != InkTool.Eraser) {
+ // start the new line, saves a uuid to represent the field of the stroke
+ this._currentStrokeId = Utils.GenerateGuid();
+ this.inkData.set(this._currentStrokeId, {
+ pathData: [this.relativeCoordinatesForEvent(e.clientX, e.clientY)],
color: InkingControl.Instance.selectedColor,
width: InkingControl.Instance.selectedWidth,
tool: InkingControl.Instance.selectedTool,
- page: this.props.Document.GetNumber(KeyStore.CurPage, 0)
+ page: this.props.Document.GetNumber(KeyStore.CurPage, -1)
});
- this.inkData = data;
- this._isDrawing = true;
+ }
}
@action
- handleMouseMove = (e: React.PointerEvent): void => {
- if (!this._isDrawing ||
- InkingControl.Instance.selectedTool === InkTool.None) {
- return;
+ onPointerUp = (e: PointerEvent): void => {
+ document.removeEventListener("pointermove", this.onPointerMove, true);
+ document.removeEventListener("pointerup", this.onPointerUp, true);
+ let coord = this.relativeCoordinatesForEvent(e.clientX, e.clientY);
+ if (Math.abs(coord.x - this.inkMidX) > 500 || Math.abs(coord.y - this.inkMidY) > 500) {
+ this.inkMidX = coord.x;
+ this.inkMidY = coord.y;
}
- e.stopPropagation()
- if (InkingControl.Instance.selectedTool === InkTool.Eraser) {
- return
- }
- const point = this.relativeCoordinatesForEvent(e);
-
- // add points to new line as it is being drawn
- let data = this.inkData;
- let strokeData = data.get(this._idGenerator);
- if (strokeData) {
- strokeData.pathData.push(point);
- data.set(this._idGenerator, strokeData);
- }
-
- this.inkData = data;
+ e.stopPropagation();
+ e.preventDefault();
}
@action
- handleMouseUp = (e: MouseEvent): void => {
- this._isDrawing = false;
+ onPointerMove = (e: PointerEvent): void => {
+ e.stopPropagation()
+ e.preventDefault();
+ if (InkingControl.Instance.selectedTool != InkTool.Eraser) {
+ let data = this.inkData; // add points to new line as it is being drawn
+ let strokeData = data.get(this._currentStrokeId);
+ if (strokeData) {
+ strokeData.pathData.push(this.relativeCoordinatesForEvent(e.clientX, e.clientY));
+ data.set(this._currentStrokeId, strokeData);
+ }
+ this.inkData = data;
+ }
}
- relativeCoordinatesForEvent = (e: React.MouseEvent): { x: number, y: number } => {
- let [x, y] = this.props.getScreenTransform().transformPoint(e.clientX, e.clientY);
- x += 50000
- y += 50000
+ relativeCoordinatesForEvent = (ex: number, ey: number): { x: number, y: number } => {
+ let [x, y] = this.props.getScreenTransform().transformPoint(ex, ey);
return { x, y };
}
@@ -122,49 +117,35 @@ export class InkingCanvas extends React.Component<InkCanvasProps> {
this.inkData = data;
}
- render() {
- // styling for cursor
- let canvasStyle = {};
- if (InkingControl.Instance.selectedTool === InkTool.None) {
- canvasStyle = { pointerEvents: "none" };
- } else {
- canvasStyle = { pointerEvents: "auto", cursor: "crosshair" };
- }
-
- // get data from server
- // let inkField = this.props.Document.GetT(KeyStore.Ink, InkField);
- // if (!inkField || inkField == "<Waiting>") {
- // return (<div className="inking-canvas" style={canvasStyle}
- // onMouseDown={this.handleMouseDown} onMouseMove={this.handleMouseMove} >
- // <svg>
- // </svg>
- // </div >)
- // }
-
- let lines = this.inkData;
-
- // parse data from server
- let paths: Array<JSX.Element> = []
- let curPage = this.props.Document.GetNumber(KeyStore.CurPage, 0)
- Array.from(lines).map(item => {
- let id = item[0];
- let strokeData = item[1];
- if (strokeData.page == 0 || strokeData.page == curPage)
- paths.push(<InkingStroke key={id} id={id}
- line={strokeData.pathData}
- color={strokeData.color}
- width={strokeData.width}
- tool={strokeData.tool}
- deleteCallback={this.removeLine} />)
- })
+ @computed
+ get drawnPaths() {
+ let curPage = this.props.Document.GetNumber(KeyStore.CurPage, -1)
+ let paths = Array.from(this.inkData).reduce((paths, [id, strokeData]) => {
+ if (strokeData.page == -1 || strokeData.page == curPage)
+ paths.push(<InkingStroke key={id} id={id} line={strokeData.pathData}
+ offsetX={this.maxCanvasDim - this.inkMidX}
+ offsetY={this.maxCanvasDim - this.inkMidY}
+ color={strokeData.color} width={strokeData.width}
+ tool={strokeData.tool} deleteCallback={this.removeLine} />)
+ return paths;
+ }, [] as JSX.Element[]);
+ return [<svg className={`inkingCanvas-paths-markers`} key="Markers"
+ style={{ left: `${this.inkMidX - this.maxCanvasDim}px`, top: `${this.inkMidY - this.maxCanvasDim}px` }} >
+ {paths.filter(path => path.props.tool == InkTool.Highlighter)}
+ </svg>,
+ <svg className={`inkingCanvas-paths-ink`} key="Pens"
+ style={{ left: `-${this.inkMidX - this.maxCanvasDim}px`, top: `-${this.inkMidY - this.maxCanvasDim}px` }}>
+ {paths.filter(path => path.props.tool != InkTool.Highlighter)}
+ </svg>];
+ }
+ render() {
+ let svgCanvasStyle = InkingControl.Instance.selectedTool != InkTool.None ? "canSelect" : "noSelect";
return (
-
- <div className="inking-canvas" style={canvasStyle}
- onPointerDown={this.handleMouseDown} onPointerMove={this.handleMouseMove} >
- <svg>
- {paths}
- </svg>
+ <div className="inkingCanvas" >
+ <div className={`inkingCanvas-${svgCanvasStyle}`} onPointerDown={this.onPointerDown} />
+ {this.props.children()}
+ {this.drawnPaths}
</div >
)
}
diff --git a/src/client/views/InkingControl.scss b/src/client/views/InkingControl.scss
new file mode 100644
index 000000000..0d8fd8784
--- /dev/null
+++ b/src/client/views/InkingControl.scss
@@ -0,0 +1,135 @@
+@import "global_variables";
+.inking-control {
+ position: absolute;
+ left: 70px;
+ bottom: 70px;
+ margin: 0;
+ padding: 0;
+ display: flex;
+ label,
+ input,
+ option {
+ font-size: 12px;
+ }
+ input[type="range"] {
+ -webkit-appearance: none;
+ background-color: transparent;
+ vertical-align: middle;
+ margin-top: 8px;
+ &:focus {
+ outline: none;
+ }
+ &::-webkit-slider-runnable-track {
+ width: 100%;
+ height: 3px;
+ border-radius: 1.5px;
+ cursor: pointer;
+ background: $intermediate-color;
+ }
+ &::-webkit-slider-thumb {
+ height: 12px;
+ width: 12px;
+ border: 1px solid $intermediate-color;
+ border-radius: 6px;
+ background: $light-color;
+ cursor: pointer;
+ -webkit-appearance: none;
+ margin-top: -4px;
+ }
+ &::-moz-range-track {
+ width: 100%;
+ height: 3px;
+ border-radius: 1.5px;
+ cursor: pointer;
+ background: $light-color;
+ }
+ &::-moz-range-thumb {
+ height: 12px;
+ width: 12px;
+ border: 1px solid $intermediate-color;
+ border-radius: 6px;
+ background: $light-color;
+ cursor: pointer;
+ -webkit-appearance: none;
+ margin-top: -4px;
+ }
+ }
+ input[type="text"] {
+ border: none;
+ padding: 0 0px;
+ background: transparent;
+ color: $dark-color;
+ font-size: 12px;
+ margin-top: 4px;
+ }
+ .ink-panel {
+ margin: 6px 12px 6px 0;
+ height: 30px;
+ vertical-align: middle;
+ line-height: 36px;
+ padding: 0 10px;
+ color: $intermediate-color;
+ &:first {
+ margin-top: 0;
+ }
+ }
+ .ink-tools {
+ display: flex;
+ background-color: transparent;
+ border-radius: 0;
+ padding: 0;
+ button {
+ height: 36px;
+ padding: 0px;
+ padding-bottom: 3px;
+ margin-left: 10px;
+ background-color: transparent;
+ color: $intermediate-color;
+ }
+ button:hover {
+ transform: scale(1.15);
+ }
+ }
+ .ink-size {
+ display: flex;
+ justify-content: space-between;
+ input[type="text"] {
+ width: 42px;
+ }
+ >* {
+ margin-right: 6px;
+ &:last-child {
+ margin-right: 0;
+ }
+ }
+ }
+ .ink-color {
+ display: flex;
+ position: relative;
+ padding-right: 0;
+ label {
+ margin-right: 6px;
+ }
+ .ink-color-display {
+ border-radius: 11px;
+ width: 22px;
+ height: 22px;
+ margin-top: 6px;
+ cursor: pointer;
+ text-align: center; // span {
+ // color: $light-color;
+ // font-size: 8px;
+ // user-select: none;
+ // }
+ }
+ .ink-color-picker {
+ background-color: $light-color;
+ border-radius: 5px;
+ padding: 12px;
+ position: absolute;
+ bottom: 36px;
+ left: -3px;
+ box-shadow: $intermediate-color 0.2vw 0.2vw 0.8vw;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/client/views/InkingControl.tsx b/src/client/views/InkingControl.tsx
index 929fb42a1..c1519dff8 100644
--- a/src/client/views/InkingControl.tsx
+++ b/src/client/views/InkingControl.tsx
@@ -1,16 +1,27 @@
import { observable, action, computed } from "mobx";
+
import { CirclePicker, ColorResult } from 'react-color'
import React = require("react");
-import "./InkingCanvas.scss"
import { InkTool } from "../../fields/InkField";
import { observer } from "mobx-react";
+import "./InkingControl.scss"
+import { library } from '@fortawesome/fontawesome-svg-core';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { faPen, faHighlighter, faEraser, faBan } from '@fortawesome/free-solid-svg-icons';
+import { SelectionManager } from "../util/SelectionManager";
+import { KeyStore } from "../../fields/KeyStore";
+import { TextField } from "../../fields/TextField";
+
+library.add(faPen, faHighlighter, faEraser, faBan);
@observer
export class InkingControl extends React.Component {
static Instance: InkingControl = new InkingControl({});
@observable private _selectedTool: InkTool = InkTool.None;
- @observable private _selectedColor: string = "#f44336";
+ @observable private _selectedColor: string = "rgb(244, 67, 54)";
@observable private _selectedWidth: string = "25";
+ @observable private _open: boolean = false;
+ @observable private _colorPickerDisplay: boolean = false;
constructor(props: Readonly<{}>) {
super(props);
@@ -25,6 +36,12 @@ export class InkingControl extends React.Component {
@action
switchColor = (color: ColorResult): void => {
this._selectedColor = color.hex;
+ if (SelectionManager.SelectedDocuments().length == 1) {
+ var sdoc = SelectionManager.SelectedDocuments()[0];
+ if (sdoc.props.ContainingCollectionView && sdoc.props.ContainingCollectionView) {
+ sdoc.props.Document.SetDataOnPrototype(KeyStore.BackgroundColor, color.hex, TextField);
+ }
+ }
}
@action
@@ -49,29 +66,50 @@ export class InkingControl extends React.Component {
selected = (tool: InkTool) => {
if (this._selectedTool === tool) {
- return { backgroundColor: "black", color: "white" }
+ return { color: "#61aaa3" }
}
return {}
}
+ @action
+ toggleDisplay = () => {
+ this._open = !this._open;
+ }
+
+ @action
+ toggleColorPicker = () => {
+ this._colorPickerDisplay = !this._colorPickerDisplay;
+ }
+
render() {
return (
- <div className="inking-control">
- <div className="ink-tools ink-panel">
- <button onClick={() => this.switchTool(InkTool.Pen)} style={this.selected(InkTool.Pen)}>Pen</button>
- <button onClick={() => this.switchTool(InkTool.Highlighter)} style={this.selected(InkTool.Highlighter)}>Highlighter</button>
- <button onClick={() => this.switchTool(InkTool.Eraser)} style={this.selected(InkTool.Eraser)}>Eraser</button>
- <button onClick={() => this.switchTool(InkTool.None)} style={this.selected(InkTool.None)}> None</button>
- </div>
- <div className="ink-size ink-panel">
- <label htmlFor="stroke-width">Size</label>
- <input type="range" min="1" max="100" defaultValue="25" name="stroke-width"
+ <ul className="inking-control" style={this._open ? { display: "flex" } : { display: "none" }}>
+ <li className="ink-tools ink-panel">
+ <div className="ink-tool-buttons">
+ <button onClick={() => this.switchTool(InkTool.Pen)} style={this.selected(InkTool.Pen)}><FontAwesomeIcon icon="pen" size="lg" title="Pen" /></button>
+ <button onClick={() => this.switchTool(InkTool.Highlighter)} style={this.selected(InkTool.Highlighter)}><FontAwesomeIcon icon="highlighter" size="lg" title="Highlighter" /></button>
+ <button onClick={() => this.switchTool(InkTool.Eraser)} style={this.selected(InkTool.Eraser)}><FontAwesomeIcon icon="eraser" size="lg" title="Eraser" /></button>
+ <button onClick={() => this.switchTool(InkTool.None)} style={this.selected(InkTool.None)}><FontAwesomeIcon icon="ban" size="lg" title="Pointer" /></button>
+ </div>
+ </li>
+ <li className="ink-color ink-panel">
+ <label>COLOR: </label>
+ <div className="ink-color-display" style={{ backgroundColor: this._selectedColor }}
+ onClick={() => this.toggleColorPicker()}>
+ {/* {this._colorPickerDisplay ? <span>&#9660;</span> : <span>&#9650;</span>} */}
+ </div>
+ <div className="ink-color-picker" style={this._colorPickerDisplay ? { display: "block" } : { display: "none" }}>
+ <CirclePicker onChange={this.switchColor} circleSize={22} width={"220"} />
+ </div>
+ </li>
+ <li className="ink-size ink-panel">
+ <label htmlFor="stroke-width">SIZE: </label>
+ <input type="text" min="1" max="100" value={this._selectedWidth} name="stroke-width"
+ onChange={(e: React.ChangeEvent<HTMLInputElement>) => this.switchWidth(e.target.value)} />
+ <input type="range" min="1" max="100" value={this._selectedWidth} name="stroke-width"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => this.switchWidth(e.target.value)} />
- </div>
- <div className="ink-color ink-panel">
- <CirclePicker onChange={this.switchColor} />
- </div>
- </div>
+ </li>
+ </ul >
)
}
} \ No newline at end of file
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index d724421d3..615f8af7e 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -6,6 +6,8 @@ import React = require("react");
interface StrokeProps {
+ offsetX: number;
+ offsetY: number;
id: string;
line: Array<{ x: number, y: number }>;
color: string;
@@ -21,23 +23,14 @@ export class InkingStroke extends React.Component<StrokeProps> {
@observable private _strokeColor: string = this.props.color;
@observable private _strokeWidth: string = this.props.width;
- private _canvasColor: string = "#cdcdcd";
-
- deleteStroke = (e: React.MouseEvent): void => {
+ deleteStroke = (e: React.PointerEvent): void => {
if (InkingControl.Instance.selectedTool === InkTool.Eraser && e.buttons === 1) {
this.props.deleteCallback(this.props.id);
}
}
parseData = (line: Array<{ x: number, y: number }>): string => {
- if (line.length === 0) {
- return "";
- }
- const pathData = "M " +
- line.map(p => {
- return p.x + " " + p.y;
- }).join(" L ");
- return pathData;
+ return !line.length ? "" : "M " + line.map(p => (p.x + this.props.offsetX) + " " + (p.y + this.props.offsetY)).join(" L ");
}
createStyle() {
@@ -52,15 +45,14 @@ export class InkingStroke extends React.Component<StrokeProps> {
}
}
-
render() {
let pathStyle = this.createStyle();
let pathData = this.parseData(this.props.line);
+ let pointerEvents: any = InkingControl.Instance.selectedTool == InkTool.Eraser ? "all" : "none";
return (
- <path className={(this._strokeTool === InkTool.Highlighter) ? "highlight" : ""}
- d={pathData} style={pathStyle} strokeLinejoin="round" strokeLinecap="round"
- onMouseOver={this.deleteStroke} onMouseDown={this.deleteStroke} />
+ <path d={pathData} style={{ ...pathStyle, pointerEvents: pointerEvents }} strokeLinejoin="round" strokeLinecap="round"
+ onPointerOver={this.deleteStroke} onPointerDown={this.deleteStroke} />
)
}
} \ No newline at end of file
diff --git a/src/client/views/Main.scss b/src/client/views/Main.scss
index 4334ed299..698a9c617 100644
--- a/src/client/views/Main.scss
+++ b/src/client/views/Main.scss
@@ -1,22 +1,35 @@
+@import "global_variables";
+@import "nodeModuleOverrides";
html,
body {
width: 100%;
height: 100%;
overflow: hidden;
- font-family: 'Hind Siliguri', sans-serif;
+ font-family: $sans-serif;
margin: 0;
}
+#dash-title {
+ position: absolute;
+ right: 46.5%;
+ letter-spacing: 3px;
+ top: 9px;
+ font-size: 12px;
+ color: $alt-accent;
+ z-index: 9999;
+}
+
h1 {
font-size: 50px;
position: fixed;
top: 30px;
left: 50%;
transform: translateX(-50%);
- color: black;
+ color: $dark-color;
text-shadow: -1px -1px 0 #fff, 1px -1px 0 #fff, -1px 1px 0 #fff, 1px 1px 0 #fff;
z-index: 9999;
- font-family: 'Fjalla One', sans-serif;
+ font-family: $sans-serif;
+ font-weight: 700;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
@@ -25,27 +38,148 @@ h1 {
user-select: none;
}
+.jsx-parser {
+ width:100%
+}
+
p {
margin: 0px;
padding: 0px;
}
+
::-webkit-scrollbar {
-webkit-appearance: none;
- height:5px;
- width:5px;
+ height: 5px;
+ width: 5px;
}
+
::-webkit-scrollbar-thumb {
border-radius: 2px;
- background-color: rgba(0,0,0,.5);
+ background-color: rgba(0, 0, 0, 0.5);
}
-.main-buttonDiv {
+// button stuff
+button {
+ background: $dark-color;
+ outline: none;
+ border: 0px;
+ color: $light-color;
+ text-transform: uppercase;
+ letter-spacing: 2px;
+ font-size: 75%;
+ padding: 10px;
+ transition: transform 0.2s;
+}
+
+button:hover {
+ background: $main-accent;
+ transform: scale(1.05);
+ cursor: pointer;
+}
+
+.clear-db-button {
position: absolute;
- width: 150px;
- left: 0px;
+ right: 45%;
+ bottom: 3%;
+ font-size: 50%;
+}
+
+.round-button {
+ width: 36px;
+ height: 36px;
+ border-radius: 18px;
+ font-size: 15px;
}
+
+.round-button:hover {
+ transform: scale(1.15);
+}
+
+.add-button {
+ position: relative;
+ margin-right: 10px;
+}
+
.main-undoButtons {
position: absolute;
width: 150px;
right: 0px;
}
+
+//toolbar stuff
+#toolbar {
+ position: absolute;
+ bottom: 62px;
+ left: 24px;
+ .toolbar-button {
+ display: block;
+ margin-bottom: 10px;
+ }
+}
+
+// add nodes menu. Note that the + button is actually an input label, not an actual button.
+#add-nodes-menu {
+ position: absolute;
+ bottom: 24px;
+ left: 24px;
+ label {
+ background: $dark-color;
+ color: $light-color;
+ display: inline-block;
+ border-radius: 18px;
+ font-size: 25px;
+ width: 36px;
+ height: 36px;
+ margin-right: 10px;
+ cursor: pointer;
+ transition: transform 0.2s;
+ }
+ label p {
+ padding-left: 10.5px;
+ padding-top: 3px;
+ }
+ label:hover {
+ background: $main-accent;
+ transform: scale(1.15);
+ }
+ input {
+ display: none;
+ }
+ input:not(:checked)~#add-options-content {
+ display: none;
+ }
+ input:checked~label {
+ transform: rotate(45deg);
+ transition: transform 0.5s;
+ cursor: pointer;
+ }
+}
+#main-div {
+ width:100%;
+ height:100%;
+ position:absolute;
+}
+#mainContent-div {
+ width:100%;
+ height:100%;
+ position:absolute;
+}
+#add-options-content {
+ display: table;
+ opacity: 1;
+ margin: 0;
+ padding: 0;
+ position: relative;
+ float: right;
+ bottom: 0.3em;
+ margin-bottom: -1.68em;
+}
+
+ul#add-options-list {
+ list-style: none;
+ padding: 0;
+ li {
+ display: inline-block;
+ padding: 0;
+ }
+} \ No newline at end of file
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 6e5b84075..6f66f8f38 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -1,4 +1,4 @@
-import { action, configure } from 'mobx';
+import { action, configure, observable, runInAction, trace, computed, reaction } from 'mobx';
import "normalize.css";
import * as React from 'react';
import * as ReactDOM from 'react-dom';
@@ -7,106 +7,373 @@ import { KeyStore } from '../../fields/KeyStore';
import "./Main.scss";
import { MessageStore } from '../../server/Message';
import { Utils } from '../../Utils';
+import * as request from 'request'
+import * as rp from 'request-promise'
import { Documents } from '../documents/Documents';
import { Server } from '../Server';
import { setupDrag } from '../util/DragManager';
import { Transform } from '../util/Transform';
import { UndoManager } from '../util/UndoManager';
+import { WorkspacesMenu } from '../../server/authentication/controllers/WorkspacesMenu';
import { CollectionDockingView } from './collections/CollectionDockingView';
import { ContextMenu } from './ContextMenu';
import { DocumentDecorations } from './DocumentDecorations';
import { DocumentView } from './nodes/DocumentView';
import "./Main.scss";
+import { observer } from 'mobx-react';
import { InkingControl } from './InkingControl';
-import { NumberField } from '../../fields/NumberField';
-import { TextField } from '../../fields/TextField';
+import { RouteStore } from '../../server/RouteStore';
+import { library, IconName } from '@fortawesome/fontawesome-svg-core';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { faFont } from '@fortawesome/free-solid-svg-icons';
+import { faImage } from '@fortawesome/free-solid-svg-icons';
+import { faFilePdf } from '@fortawesome/free-solid-svg-icons';
+import { faObjectGroup } from '@fortawesome/free-solid-svg-icons';
+import { faTable } from '@fortawesome/free-solid-svg-icons';
+import { faGlobeAsia } from '@fortawesome/free-solid-svg-icons';
+import { faUndoAlt } from '@fortawesome/free-solid-svg-icons';
+import { faRedoAlt } from '@fortawesome/free-solid-svg-icons';
+import { faPenNib } from '@fortawesome/free-solid-svg-icons';
+import { faFilm } from '@fortawesome/free-solid-svg-icons';
+import { faMusic } from '@fortawesome/free-solid-svg-icons';
+import { faTree } from '@fortawesome/free-solid-svg-icons';
+import Measure from 'react-measure';
+import { DashUserModel } from '../../server/authentication/models/user_model';
+import { ServerUtils } from '../../server/ServerUtil';
+import { CurrentUserUtils } from '../../server/authentication/models/current_user_utils';
+import { Field, Opt, FieldWaiting } from '../../fields/Field';
import { ListField } from '../../fields/ListField';
+import { Gateway, Settings } from '../northstar/manager/Gateway';
+import { Catalog, Schema, Attribute, AttributeGroup, AggregateFunction } from '../northstar/model/idea/idea';
+import { ArrayUtil } from '../northstar/utils/ArrayUtil';
+import '../northstar/model/ModelExtensions'
+import '../northstar/utils/Extensions'
+import { HistogramOperation } from '../northstar/operations/HistogramOperation';
+import { AttributeTransformationModel } from '../northstar/core/attribute/AttributeTransformationModel';
+import { ColumnAttributeModel } from '../northstar/core/attribute/AttributeModel';
+@observer
+export class Main extends React.Component {
+ // dummy initializations keep the compiler happy
+ @observable private mainfreeform?: Document;
+ @observable public pwidth: number = 0;
+ @observable public pheight: number = 0;
+ private _northstarSchemas: Document[] = [];
-configure({ enforceActions: "observed" }); // causes errors to be generated when modifying an observable outside of an action
-window.addEventListener("drop", (e) => e.preventDefault(), false)
-window.addEventListener("dragover", (e) => e.preventDefault(), false)
-document.addEventListener("pointerdown", action(function (e: PointerEvent) {
- if (!ContextMenu.Instance.intersects(e.pageX, e.pageY)) {
- ContextMenu.Instance.clearItems()
- }
-}), true)
-
-
-const mainDocId = "mainDoc";
-let mainContainer: Document;
-let mainfreeform: Document;
-Documents.initProtos(mainDocId, (res?: Document) => {
- if (res instanceof Document) {
- mainContainer = res;
- mainContainer.GetAsync(KeyStore.ActiveFrame, field => mainfreeform = field as Document);
- }
- else {
- mainContainer = Documents.DockDocument(JSON.stringify({ content: [{ type: 'row', content: [] }] }), { title: "main container" }, mainDocId);
-
- // bcz: strangely, we need a timeout to prevent exceptions/issues initializing GoldenLayout (the rendering engine for Main Container)
- setTimeout(() => {
- mainfreeform = Documents.FreeformDocument([], { x: 0, y: 400, title: "mini collection" });
-
- var dockingLayout = { content: [{ type: 'row', content: [CollectionDockingView.makeDocumentConfig(mainfreeform)] }] };
- mainContainer.SetText(KeyStore.Data, JSON.stringify(dockingLayout));
- mainContainer.Set(KeyStore.ActiveFrame, mainfreeform);
- }, 0);
- }
-
- let imgurl = "https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg";
- let pdfurl = "http://www.adobe.com/support/products/enterprise/knowledgecenter/media/c4611_sample_explain.pdf"
- let weburl = "https://cs.brown.edu/courses/cs166/";
- let clearDatabase = action(() => Utils.Emit(Server.Socket, MessageStore.DeleteAll, {}))
- let addTextNode = action(() => Documents.TextDocument({ width: 200, height: 200, title: "a text note" }))
- let addColNode = action(() => Documents.FreeformDocument([], { width: 200, height: 200, title: "a freeform collection" }));
- let addSchemaNode = action(() => Documents.SchemaDocument([Documents.TextDocument()], { width: 200, height: 200, title: "a schema collection" }));
- let addPDFNode = action(() => Documents.PdfDocument(pdfurl, { width: 200, height: 200, title: "a schema collection" }));
- let addImageNode = action(() => Documents.ImageDocument(imgurl, { width: 200, height: 200, title: "an image of a cat" }));
- let addWebNode = action(() => Documents.WebDocument(weburl, { width: 200, height: 200, title: "a sample web page" }));
-
- let addClick = (creator: () => Document) => action(() =>
- mainfreeform.GetList<Document>(KeyStore.Data, []).push(creator())
- );
-
- let imgRef = React.createRef<HTMLDivElement>();
- let pdfRef = React.createRef<HTMLDivElement>();
- let webRef = React.createRef<HTMLDivElement>();
- let textRef = React.createRef<HTMLDivElement>();
- let schemaRef = React.createRef<HTMLDivElement>();
- let colRef = React.createRef<HTMLDivElement>();
-
- ReactDOM.render((
- <div style={{ position: "absolute", width: "100%", height: "100%" }}>
- <DocumentView Document={mainContainer}
- AddDocument={undefined} RemoveDocument={undefined} ScreenToLocalTransform={() => Transform.Identity}
- ContentScaling={() => 1}
- PanelWidth={() => 0}
- PanelHeight={() => 0}
+ @computed private get mainContainer(): Document | undefined {
+ let doc = this.userDocument.GetT(KeyStore.ActiveWorkspace, Document);
+ return doc == FieldWaiting ? undefined : doc;
+ }
+
+ private set mainContainer(doc: Document | undefined) {
+ if (doc) {
+ this.userDocument.Set(KeyStore.ActiveWorkspace, doc);
+ }
+ }
+
+ private get userDocument(): Document {
+ return CurrentUserUtils.UserDocument;
+ }
+
+ public static Instance: Main;
+
+ constructor(props: Readonly<{}>) {
+ super(props);
+ Main.Instance = this;
+ // causes errors to be generated when modifying an observable outside of an action
+ configure({ enforceActions: "observed" });
+ if (window.location.pathname !== RouteStore.home) {
+ let pathname = window.location.pathname.split("/");
+ if (pathname.length > 1 && pathname[pathname.length - 2] == 'doc') {
+ CurrentUserUtils.MainDocId = pathname[pathname.length - 1];
+ }
+ };
+
+ CurrentUserUtils.loadCurrentUser();
+
+ library.add(faFont);
+ library.add(faImage);
+ library.add(faFilePdf);
+ library.add(faObjectGroup);
+ library.add(faTable);
+ library.add(faGlobeAsia);
+ library.add(faUndoAlt);
+ library.add(faRedoAlt);
+ library.add(faPenNib);
+ library.add(faFilm);
+ library.add(faMusic);
+ library.add(faTree);
+
+ this.initEventListeners();
+ this.initAuthenticationRouters();
+
+ this.initializeNorthstar();
+ }
+
+ onHistory = () => {
+ if (window.location.pathname !== RouteStore.home) {
+ let pathname = window.location.pathname.split("/");
+ CurrentUserUtils.MainDocId = pathname[pathname.length - 1];
+ Server.GetField(CurrentUserUtils.MainDocId, action((field: Opt<Field>) => {
+ if (field instanceof Document) {
+ this.openWorkspace(field, true);
+ }
+ }));
+ }
+ }
+
+ componentDidMount() {
+ window.onpopstate = this.onHistory;
+ }
+
+ componentWillUnmount() {
+ window.onpopstate = null;
+ }
+
+ initEventListeners = () => {
+ // window.addEventListener("pointermove", (e) => this.reportLocation(e))
+ window.addEventListener("drop", (e) => e.preventDefault(), false) // drop event handler
+ window.addEventListener("dragover", (e) => e.preventDefault(), false) // drag event handler
+ // click interactions for the context menu
+ document.addEventListener("pointerdown", action(function (e: PointerEvent) {
+ if (!ContextMenu.Instance.intersects(e.pageX, e.pageY)) {
+ ContextMenu.Instance.clearItems();
+ }
+ }), true);
+ }
+
+ initAuthenticationRouters = () => {
+ // Load the user's active workspace, or create a new one if initial session after signup
+ if (!CurrentUserUtils.MainDocId) {
+ this.userDocument.GetTAsync(KeyStore.ActiveWorkspace, Document).then(doc => {
+ if (doc) {
+ CurrentUserUtils.MainDocId = doc.Id;
+ this.openWorkspace(doc);
+ } else {
+ this.createNewWorkspace();
+ }
+ })
+ } else {
+ Server.GetField(CurrentUserUtils.MainDocId).then(field => {
+ if (field instanceof Document) {
+ this.openWorkspace(field)
+ } else {
+ this.createNewWorkspace(CurrentUserUtils.MainDocId);
+ }
+ })
+ }
+ }
+
+ @action
+ createNewWorkspace = (id?: string): void => {
+ this.userDocument.GetTAsync<ListField<Document>>(KeyStore.Workspaces, ListField).then(action((list: Opt<ListField<Document>>) => {
+ if (list) {
+ let freeformDoc = Documents.FreeformDocument([], { x: 0, y: 400, title: "mini collection" });
+ var dockingLayout = { content: [{ type: 'row', content: [CollectionDockingView.makeDocumentConfig(freeformDoc)] }] };
+ let mainDoc = Documents.DockDocument(JSON.stringify(dockingLayout), { title: `Main Container ${list.Data.length + 1}` }, id);
+ list.Data.push(mainDoc);
+ CurrentUserUtils.MainDocId = mainDoc.Id;
+ // bcz: strangely, we need a timeout to prevent exceptions/issues initializing GoldenLayout (the rendering engine for Main Container)
+ setTimeout(() => {
+ this.openWorkspace(mainDoc);
+ let pendingDocument = Documents.SchemaDocument([], { title: "New Mobile Uploads" })
+ mainDoc.Set(KeyStore.OptionalRightCollection, pendingDocument);
+ }, 0);
+ }
+ }));
+ }
+
+ @action
+ openWorkspace = (doc: Document, fromHistory = false): void => {
+ this.mainContainer = doc;
+ fromHistory || window.history.pushState(null, doc.Title, "/doc/" + doc.Id);
+ this.userDocument.GetTAsync(KeyStore.OptionalRightCollection, Document).then(col => {
+ // if there is a pending doc, and it has new data, show it (syip: we use a timeout to prevent collection docking view from being uninitialized)
+ setTimeout(() => {
+ if (col) {
+ col.GetTAsync<ListField<Document>>(KeyStore.Data, ListField, (f: Opt<ListField<Document>>) => {
+ if (f && f.Data.length > 0) {
+ CollectionDockingView.Instance.AddRightSplit(col);
+ }
+ })
+ }
+ }, 100);
+ });
+ }
+
+ @observable
+ workspacesShown: boolean = false;
+
+ areWorkspacesShown = () => {
+ return this.workspacesShown;
+ }
+ @action
+ toggleWorkspaces = () => {
+ this.workspacesShown = !this.workspacesShown;
+ }
+
+ screenToLocalTransform = () => Transform.Identity
+ pwidthFunc = () => this.pwidth;
+ pheightFunc = () => this.pheight;
+ focusDocument = (doc: Document) => { }
+ noScaling = () => 1;
+
+ @computed
+ get mainContent() {
+ return !this.mainContainer ? (null) :
+ <DocumentView Document={this.mainContainer}
+ AddDocument={undefined}
+ RemoveDocument={undefined}
+ ScreenToLocalTransform={this.screenToLocalTransform}
+ ContentScaling={this.noScaling}
+ PanelWidth={this.pwidthFunc}
+ PanelHeight={this.pheightFunc}
isTopMost={true}
SelectOnLoad={false}
- focus={() => { }}
+ focus={this.focusDocument}
ContainingCollectionView={undefined} />
- <DocumentDecorations />
- <ContextMenu />
- <div className="main-buttonDiv" style={{ bottom: '0px' }} ref={imgRef} >
- <button onPointerDown={setupDrag(imgRef, addImageNode)} onClick={addClick(addImageNode)}>Add Image</button></div>
- <div className="main-buttonDiv" style={{ bottom: '25px' }} ref={webRef} >
- <button onPointerDown={setupDrag(webRef, addWebNode)} onClick={addClick(addWebNode)}>Add Web</button></div>
- <div className="main-buttonDiv" style={{ bottom: '50px' }} ref={textRef}>
- <button onPointerDown={setupDrag(textRef, addTextNode)} onClick={addClick(addTextNode)}>Add Text</button></div>
- <div className="main-buttonDiv" style={{ bottom: '75px' }} ref={colRef}>
- <button onPointerDown={setupDrag(colRef, addColNode)} onClick={addClick(addColNode)}>Add Collection</button></div>
- <div className="main-buttonDiv" style={{ bottom: '100px' }} ref={schemaRef}>
- <button onPointerDown={setupDrag(schemaRef, addSchemaNode)} onClick={addClick(addSchemaNode)}>Add Schema</button></div>
- <div className="main-buttonDiv" style={{ bottom: '125px' }} >
- <button onClick={clearDatabase}>Clear Database</button></div>
- <div className="main-buttonDiv" style={{ bottom: '150px' }} ref={pdfRef}>
- <button onPointerDown={setupDrag(pdfRef, addPDFNode)} onClick={addClick(addPDFNode)}>Add PDF</button></div>
- <button className="main-undoButtons" style={{ bottom: '25px' }} onClick={() => UndoManager.Undo()}>Undo</button>
- <button className="main-undoButtons" style={{ bottom: '0px' }} onClick={() => UndoManager.Redo()}>Redo</button>
- <InkingControl />
- </div>),
- document.getElementById('root'));
-})
+ }
+
+ /* for the expandable add nodes menu. Not included with the miscbuttons because once it expands it expands the whole div with it, making canvas interactions limited. */
+ @computed
+ get nodesMenu() {
+ let imgurl = "https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg";
+ let pdfurl = "http://www.adobe.com/support/products/enterprise/knowledgecenter/media/c4611_sample_explain.pdf"
+ let weburl = "https://cs.brown.edu/courses/cs166/";
+ let audiourl = "http://techslides.com/demos/samples/sample.mp3";
+ let videourl = "http://techslides.com/demos/sample-videos/small.mp4";
+
+ let addTextNode = action(() => Documents.TextDocument({ width: 200, height: 200, title: "a text note" }))
+ let addColNode = action(() => Documents.FreeformDocument([], { width: 200, height: 200, title: "a freeform collection" }));
+ let addSchemaNode = action(() => Documents.SchemaDocument([], { width: 200, height: 200, title: "a schema collection" }));
+ let addTreeNode = action(() => Documents.TreeDocument(this._northstarSchemas, { width: 250, height: 400, title: "northstar schemas" }));
+ let addVideoNode = action(() => Documents.VideoDocument(videourl, { width: 200, height: 200, title: "video node" }));
+ let addPDFNode = action(() => Documents.PdfDocument(pdfurl, { width: 200, height: 200, title: "a schema collection" }));
+ let addImageNode = action(() => Documents.ImageDocument(imgurl, { width: 200, height: 200, title: "an image of a cat" }));
+ let addWebNode = action(() => Documents.WebDocument(weburl, { width: 200, height: 200, title: "a sample web page" }));
+ let addAudioNode = action(() => Documents.AudioDocument(audiourl, { width: 200, height: 200, title: "audio node" }))
+
+ let btns: [React.RefObject<HTMLDivElement>, IconName, string, () => Document][] = [
+ [React.createRef<HTMLDivElement>(), "font", "Add Textbox", addTextNode],
+ [React.createRef<HTMLDivElement>(), "image", "Add Image", addImageNode],
+ [React.createRef<HTMLDivElement>(), "file-pdf", "Add PDF", addPDFNode],
+ [React.createRef<HTMLDivElement>(), "film", "Add Video", addVideoNode],
+ [React.createRef<HTMLDivElement>(), "music", "Add Audio", addAudioNode],
+ [React.createRef<HTMLDivElement>(), "globe-asia", "Add Web Clipping", addWebNode],
+ [React.createRef<HTMLDivElement>(), "object-group", "Add Collection", addColNode],
+ [React.createRef<HTMLDivElement>(), "tree", "Add Tree", addTreeNode],
+ [React.createRef<HTMLDivElement>(), "table", "Add Schema", addSchemaNode],
+ ]
+
+ return < div id="add-nodes-menu" >
+ <input type="checkbox" id="add-menu-toggle" />
+ <label htmlFor="add-menu-toggle" title="Add Node"><p>+</p></label>
+
+ <div id="add-options-content">
+ <ul id="add-options-list">
+ {btns.map(btn =>
+ <li key={btn[1]} ><div ref={btn[0]}>
+ <button className="round-button add-button" title={btn[2]} onPointerDown={setupDrag(btn[0], btn[3])}>
+ <FontAwesomeIcon icon={btn[1]} size="sm" />
+ </button>
+ </div></li>)}
+ </ul>
+ </div>
+ </div >
+ }
+
+ /* @TODO this should really be moved into a moveable toolbar component, but for now let's put it here to meet the deadline */
+ @computed
+ get miscButtons() {
+ let workspacesRef = React.createRef<HTMLDivElement>();
+ let logoutRef = React.createRef<HTMLDivElement>();
+
+ let clearDatabase = action(() => Utils.Emit(Server.Socket, MessageStore.DeleteAll, {}))
+ return [
+ <button className="clear-db-button" key="clear-db" onClick={clearDatabase}>Clear Database</button>,
+ <div id="toolbar" key="toolbar">
+ <button className="toolbar-button round-button" title="Undo" onClick={() => UndoManager.Undo()}><FontAwesomeIcon icon="undo-alt" size="sm" /></button>
+ <button className="toolbar-button round-button" title="Redo" onClick={() => UndoManager.Redo()}><FontAwesomeIcon icon="redo-alt" size="sm" /></button>
+ <button className="toolbar-button round-button" title="Ink" onClick={() => InkingControl.Instance.toggleDisplay()}><FontAwesomeIcon icon="pen-nib" size="sm" /></button>
+ </div >,
+ <div className="main-buttonDiv" key="workspaces" style={{ top: '34px', left: '2px', position: 'absolute' }} ref={workspacesRef}>
+ <button onClick={this.toggleWorkspaces}>Workspaces</button></div>,
+ <div className="main-buttonDiv" key="logout" style={{ top: '34px', right: '1px', position: 'absolute' }} ref={logoutRef}>
+ <button onClick={() => request.get(ServerUtils.prepend(RouteStore.logout), () => { })}>Log Out</button></div>
+ ]
+ }
+
+ render() {
+ let workspaceMenu: any = null;
+ let workspaces = this.userDocument.GetT<ListField<Document>>(KeyStore.Workspaces, ListField);
+ if (workspaces && workspaces !== FieldWaiting) {
+ workspaceMenu = <WorkspacesMenu active={this.mainContainer} open={this.openWorkspace} new={this.createNewWorkspace} allWorkspaces={workspaces.Data}
+ isShown={this.areWorkspacesShown} toggle={this.toggleWorkspaces} />
+ }
+ return (
+ <div id="main-div">
+ <DocumentDecorations />
+ <Measure onResize={(r: any) => runInAction(() => {
+ this.pwidth = r.entry.width;
+ this.pheight = r.entry.height;
+ })}>
+ {({ measureRef }) =>
+ <div ref={measureRef} id="mainContent-div">
+ {this.mainContent}
+ </div>
+ }
+ </Measure>
+ <ContextMenu />
+ {this.nodesMenu}
+ {this.miscButtons}
+ {workspaceMenu}
+ <InkingControl />
+ </div>
+ );
+ }
+
+ // --------------- Northstar hooks ------------- /
+
+ @action SetNorthstarCatalog(ctlog: Catalog) {
+ CurrentUserUtils.NorthstarDBCatalog = ctlog;
+ if (ctlog && ctlog.schemas) {
+ this._northstarSchemas = ctlog.schemas.map(schema => {
+ let schemaDoc = Documents.TreeDocument([], { width: 50, height: 100, title: schema.displayName! });
+ let schemaDocuments = schemaDoc.GetList(KeyStore.Data, [] as Document[]);
+ CurrentUserUtils.GetAllNorthstarColumnAttributes(schema).map(attr => {
+ Server.GetField(attr.displayName! + ".alias", action((field: Opt<Field>) => {
+ if (field instanceof Document) {
+ schemaDocuments.push(field);
+ } else {
+ var atmod = new ColumnAttributeModel(attr);
+ let histoOp = new HistogramOperation(schema!.displayName!,
+ new AttributeTransformationModel(atmod, AggregateFunction.None),
+ new AttributeTransformationModel(atmod, AggregateFunction.Count),
+ new AttributeTransformationModel(atmod, AggregateFunction.Count));
+ schemaDocuments.push(Documents.HistogramDocument(histoOp, { width: 200, height: 200, title: attr.displayName! }, undefined, attr.displayName! + ".alias"));
+ }
+ }));
+ });
+ return schemaDoc;
+ })
+ }
+ }
+ async initializeNorthstar(): Promise<void> {
+ let envPath = "/assets/env.json";
+ const response = await fetch(envPath, {
+ redirect: "follow",
+ method: "GET",
+ credentials: "include"
+ });
+ const env = await response.json();
+ Settings.Instance.Update(env);
+ let cat = Gateway.Instance.ClearCatalog();
+ cat.then(async () => this.SetNorthstarCatalog(await Gateway.Instance.GetCatalog()));
+ }
+}
+Documents.initProtos().then(() => {
+ return CurrentUserUtils.loadCurrentUser()
+}).then(() => {
+ ReactDOM.render(<Main />, document.getElementById('root'));
+});
diff --git a/src/client/views/_global_variables.scss b/src/client/views/_global_variables.scss
new file mode 100644
index 000000000..44a819b79
--- /dev/null
+++ b/src/client/views/_global_variables.scss
@@ -0,0 +1,17 @@
+@import url("https://fonts.googleapis.com/css?family=Noto+Sans:400,700|Crimson+Text:400,400i,700");
+// colors
+$light-color: #fcfbf7;
+$light-color-secondary: rgb(241, 239, 235);
+$main-accent: #61aaa3;
+// $alt-accent: #cdd5ec;
+// $alt-accent: #cdeceb;
+$alt-accent: #59dff7;
+$lighter-alt-accent: rgb(207, 220, 240);
+$intermediate-color: #9c9396;
+$dark-color: #121721;
+// fonts
+$sans-serif: "Noto Sans", sans-serif;
+// $sans-serif: "Roboto Slab", sans-serif;
+$serif: "Crimson Text", serif;
+// misc values
+$border-radius: 0.3em;
diff --git a/src/client/views/_nodeModuleOverrides.scss b/src/client/views/_nodeModuleOverrides.scss
new file mode 100644
index 000000000..6f97e60f8
--- /dev/null
+++ b/src/client/views/_nodeModuleOverrides.scss
@@ -0,0 +1,23 @@
+// this file is for overriding all the css from installed node modules
+
+// goldenlayout stuff
+div .lm_header {
+ background: $dark-color;
+ min-height: 2em;
+}
+
+.lm_tab {
+ margin-top: 0.6em !important;
+ padding-top: 0.5em !important;
+ min-height: 1.35em;
+ padding-bottom: 0px;
+ border-radius: 5px;
+ font-family: $sans-serif !important;
+}
+
+.lm_header .lm_controls {
+ right: 1em !important;
+}
+
+// @TODO the ril__navgiation buttons in the img gallery are a lil messed up but I can't figure out
+// why. Low priority for now but it's bugging me. --Julie
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index f01c538e6..39e0dd989 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -1,7 +1,7 @@
import * as GoldenLayout from "golden-layout";
import 'golden-layout/src/css/goldenlayout-base.css';
import 'golden-layout/src/css/goldenlayout-dark-theme.css';
-import { action, observable, reaction } from "mobx";
+import { action, observable, reaction, trace } from "mobx";
import { observer } from "mobx-react";
import * as ReactDOM from 'react-dom';
import { Document } from "../../../fields/Document";
@@ -16,6 +16,9 @@ import "./CollectionDockingView.scss";
import { COLLECTION_BORDER_WIDTH } from "./CollectionView";
import React = require("react");
import { SubCollectionViewProps } from "./CollectionViewBase";
+import { ServerUtils } from "../../../server/ServerUtil";
+import { DragManager } from "../../util/DragManager";
+import { TextField } from "../../../fields/TextField";
@observer
export class CollectionDockingView extends React.Component<SubCollectionViewProps> {
@@ -36,6 +39,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
private _containerRef = React.createRef<HTMLDivElement>();
private _fullScreen: any = null;
private _flush: boolean = false;
+ private _ignoreStateChange = "";
constructor(props: SubCollectionViewProps) {
super(props);
@@ -43,8 +47,10 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
(window as any).React = React;
(window as any).ReactDOM = ReactDOM;
}
- public StartOtherDrag(dragDoc: Document, e: any) {
- this.AddRightSplit(dragDoc, true).contentItems[0].tab._dragListener.onMouseDown({ pageX: e.pageX, pageY: e.pageY, preventDefault: () => { }, button: e.button })
+ public StartOtherDrag(dragDocs: Document[], e: any) {
+ dragDocs.map(dragDoc =>
+ this.AddRightSplit(dragDoc, true).contentItems[0].tab._dragListener.
+ onMouseDown({ pageX: e.pageX, pageY: e.pageY, preventDefault: () => { }, button: 0 }));
}
@action
@@ -58,6 +64,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
docconfig.callDownwards('_$init');
this._goldenLayout._$maximiseItem(docconfig);
this._fullScreen = docconfig;
+ this._ignoreStateChange = JSON.stringify(this._goldenLayout.toConfig());
this.stateChanged();
}
@action
@@ -66,6 +73,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
this._goldenLayout._$minimiseItem(this._fullScreen);
this._goldenLayout.root.contentItems[0].removeChild(this._fullScreen);
this._fullScreen = null;
+ this._ignoreStateChange = JSON.stringify(this._goldenLayout.toConfig());
this.stateChanged();
}
}
@@ -75,7 +83,6 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
//
@action
public AddRightSplit(document: Document, minimize: boolean = false) {
- this._goldenLayout.emit('stateChanged');
let newItemStackConfig = {
type: 'stack',
content: [CollectionDockingView.makeDocumentConfig(document)]
@@ -104,7 +111,9 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
newContentItem.callDownwards('_$init');
this._goldenLayout.root.callDownwards('setSize', [this._goldenLayout.width, this._goldenLayout.height]);
this._goldenLayout.emit('stateChanged');
+ this._ignoreStateChange = JSON.stringify(this._goldenLayout.toConfig());
this.stateChanged();
+
return newContentItem;
}
@@ -144,15 +153,24 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
if (this._containerRef.current) {
reaction(
() => this.props.Document.GetText(KeyStore.Data, ""),
- () => this.setupGoldenLayout(), { fireImmediately: true });
+ () => {
+ if (!this._goldenLayout || this._ignoreStateChange != JSON.stringify(this._goldenLayout.toConfig())) {
+ setTimeout(() => this.setupGoldenLayout(), 1);
+ }
+ this._ignoreStateChange = "";
+ }, { fireImmediately: true });
window.addEventListener('resize', this.onResize); // bcz: would rather add this event to the parent node, but resize events only come from Window
}
}
componentWillUnmount: () => void = () => {
- this._goldenLayout.unbind('itemDropped', this.itemDropped);
- this._goldenLayout.unbind('tabCreated', this.tabCreated);
- this._goldenLayout.unbind('stackCreated', this.stackCreated);
+ try {
+ this._goldenLayout.unbind('itemDropped', this.itemDropped);
+ this._goldenLayout.unbind('tabCreated', this.tabCreated);
+ this._goldenLayout.unbind('stackCreated', this.stackCreated);
+ } catch (e) {
+
+ }
this._goldenLayout.destroy();
this._goldenLayout = null;
window.removeEventListener('resize', this.onResize);
@@ -174,17 +192,28 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
}
@action
onPointerDown = (e: React.PointerEvent): void => {
- if (e.button === 2 && this.props.active()) {
+ var className = (e.target as any).className;
+ if ((className == "lm_title" || className == "lm_tab lm_active") && (e.ctrlKey || e.altKey)) {
e.stopPropagation();
e.preventDefault();
- } else {
- var className = (e.target as any).className;
- if (className == "lm_drag_handle" || className == "lm_close" || className == "lm_maximise" || className == "lm_minimise" || className == "lm_close_tab") {
- this._flush = true;
- }
- if (e.buttons === 1 && this.props.active()) {
- e.stopPropagation();
- }
+ let docid = (e.target as any).DashDocId;
+ let tab = (e.target as any).parentElement as HTMLElement;
+ Server.GetField(docid, action((f: Opt<Field>) => {
+ if (f instanceof Document)
+ DragManager.StartDocumentDrag([tab], new DragManager.DocumentDragData([f as Document]),
+ {
+ handlers: {
+ dragComplete: action(() => { }),
+ },
+ hideSource: false
+ })
+ }));
+ }
+ if (className == "lm_drag_handle" || className == "lm_close" || className == "lm_maximise" || className == "lm_minimise" || className == "lm_close_tab") {
+ this._flush = true;
+ }
+ if (this.props.active()) {
+ e.stopPropagation();
}
}
@@ -198,6 +227,21 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
this.stateChanged();
}
tabCreated = (tab: any) => {
+ if (tab.hasOwnProperty("contentItem") && tab.contentItem.config.type != "stack") {
+ if (tab.titleElement[0].textContent.indexOf("-waiting") != -1) {
+ Server.GetField(tab.contentItem.config.props.documentId, action((f: Opt<Field>) => {
+ if (f != undefined && f instanceof Document) {
+ f.GetTAsync(KeyStore.Title, TextField, (tfield) => {
+ if (tfield != undefined) {
+ tab.titleElement[0].textContent = f.Title;
+ }
+ })
+ }
+ }));
+ tab.titleElement[0].DashDocId = tab.contentItem.config.props.documentId;
+ }
+ tab.titleElement[0].DashDocId = tab.contentItem.config.props.documentId;
+ }
tab.closeElement.off('click') //unbind the current click handler
.click(function () {
tab.contentItem.remove();
@@ -213,6 +257,12 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
stack.remove();
//}
}));
+ stack.header.controlsContainer.find('.lm_popout') //get the close icon
+ .off('click') //unbind the current click handler
+ .click(action(function () {
+ var url = ServerUtils.prepend("/doc/" + stack.contentItems[0].tab.contentItem.config.props.documentId);
+ let win = window.open(url, stack.contentItems[0].tab.title, "width=300,height=400");
+ }));
}
render() {
diff --git a/src/client/views/collections/CollectionFreeFormView.scss b/src/client/views/collections/CollectionFreeFormView.scss
deleted file mode 100644
index b059163ed..000000000
--- a/src/client/views/collections/CollectionFreeFormView.scss
+++ /dev/null
@@ -1,68 +0,0 @@
-.collectionfreeformview-container {
-
- .collectionfreeformview > .jsx-parser{
- position:absolute;
- height: 100%;
- width: 100%;
- }
-
- border-style: solid;
- box-sizing: border-box;
- position: relative;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- overflow: hidden;
- .collectionfreeformview {
- position: absolute;
- top: 0;
- left: 0;
- width:100%;
- height: 100%;
- }
-}
-.collectionfreeformview-overlay {
-
- .collectionfreeformview > .jsx-parser{
- position:absolute;
- height: 100%;
- }
- .formattedTextBox-cont {
- background:yellow;
- }
-
- border-style: solid;
- box-sizing: border-box;
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- overflow: hidden;
- .collectionfreeformview {
- position: absolute;
- top: 0;
- left: 0;
- width:100%;
- height: 100%;
- }
-}
-
-.border {
- border-style: solid;
- box-sizing: border-box;
- width: 100%;
- height: 100%;
-}
-
-//this is an animation for the blinking cursor!
-@keyframes blink {
- 0% {opacity: 0}
- 49%{opacity: 0}
- 50% {opacity: 1}
-}
-
-#prevCursor {
- animation: blink 1s infinite;
-} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionFreeFormView.tsx b/src/client/views/collections/CollectionFreeFormView.tsx
deleted file mode 100644
index 16002ad9f..000000000
--- a/src/client/views/collections/CollectionFreeFormView.tsx
+++ /dev/null
@@ -1,322 +0,0 @@
-import { action, computed, observable, reaction, trace } from "mobx";
-import { observer } from "mobx-react";
-import { Document } from "../../../fields/Document";
-import { FieldWaiting } from "../../../fields/Field";
-import { KeyStore } from "../../../fields/KeyStore";
-import { ListField } from "../../../fields/ListField";
-import { TextField } from "../../../fields/TextField";
-import { Documents } from "../../documents/Documents";
-import { DragManager } from "../../util/DragManager";
-import { Transform } from "../../util/Transform";
-import { undoBatch } from "../../util/UndoManager";
-import { CollectionDockingView } from "../collections/CollectionDockingView";
-import { CollectionSchemaView } from "../collections/CollectionSchemaView";
-import { CollectionView } from "../collections/CollectionView";
-import { CollectionPDFView } from "../collections/CollectionPDFView";
-import { InkingCanvas } from "../InkingCanvas";
-import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocumentView";
-import { DocumentView } from "../nodes/DocumentView";
-import { FormattedTextBox } from "../nodes/FormattedTextBox";
-import { ImageBox } from "../nodes/ImageBox";
-import { KeyValueBox } from "../nodes/KeyValueBox";
-import { PDFBox } from "../nodes/PDFBox";
-import { WebBox } from "../nodes/WebBox";
-import "./CollectionFreeFormView.scss";
-import { COLLECTION_BORDER_WIDTH } from "./CollectionView";
-import { CollectionViewBase } from "./CollectionViewBase";
-import React = require("react");
-import { render } from "pug";
-const JsxParser = require('react-jsx-parser').default;//TODO Why does this need to be imported like this?
-
-@observer
-export class CollectionFreeFormView extends CollectionViewBase {
- private _canvasRef = React.createRef<HTMLDivElement>();
- private _lastX: number = 0;
- private _lastY: number = 0;
- private _selectOnLoaded: string = ""; // id of document that should be selected once it's loaded (used for click-to-type)
-
- @observable
- private _downX: number = 0;
- @observable
- private _downY: number = 0;
-
- //determines whether the blinking cursor for indicating whether a text will be made on key down is visible
- @observable
- private _previewCursorVisible: boolean = false;
-
- @computed get panX(): number { return this.props.Document.GetNumber(KeyStore.PanX, 0) }
- @computed get panY(): number { return this.props.Document.GetNumber(KeyStore.PanY, 0) }
- @computed get scale(): number { return this.props.Document.GetNumber(KeyStore.Scale, 1); }
- @computed get isAnnotationOverlay() { return this.props.fieldKey.Id === KeyStore.Annotations.Id; } // bcz: ? Why do we need to compare Id's?
- @computed get nativeWidth() { return this.props.Document.GetNumber(KeyStore.NativeWidth, 0); }
- @computed get nativeHeight() { return this.props.Document.GetNumber(KeyStore.NativeHeight, 0); }
- @computed get zoomScaling() { return this.props.Document.GetNumber(KeyStore.Scale, 1); }
- @computed get centeringShiftX() { return !this.props.Document.GetNumber(KeyStore.NativeWidth, 0) ? this.props.panelWidth() / 2 : 0; } // shift so pan position is at center of window for non-overlay collections
- @computed get centeringShiftY() { return !this.props.Document.GetNumber(KeyStore.NativeHeight, 0) ? this.props.panelHeight() / 2 : 0; }// shift so pan position is at center of window for non-overlay collections
-
- @undoBatch
- @action
- drop = (e: Event, de: DragManager.DropEvent) => {
- super.drop(e, de);
- const docView: DocumentView = de.data["documentView"];
- let doc: Document = docView ? docView.props.Document : de.data["document"];
- let screenX = de.x - (de.data["xOffset"] as number || 0);
- let screenY = de.y - (de.data["yOffset"] as number || 0);
- const [x, y] = this.getTransform().transformPoint(screenX, screenY);
- doc.SetNumber(KeyStore.X, x);
- doc.SetNumber(KeyStore.Y, y);
- this.bringToFront(doc);
- }
-
- @action
- onPointerDown = (e: React.PointerEvent): void => {
- if (((e.button === 2 && this.props.active()) ||
- !e.defaultPrevented) && (!this.isAnnotationOverlay || this.zoomScaling != 1 || e.button == 0)) {
- document.removeEventListener("pointermove", this.onPointerMove);
- document.addEventListener("pointermove", this.onPointerMove);
- document.removeEventListener("pointerup", this.onPointerUp);
- document.addEventListener("pointerup", this.onPointerUp);
- this._lastX = e.pageX;
- this._lastY = e.pageY;
- this._downX = e.pageX;
- this._downY = e.pageY;
- }
- }
-
- @action
- onPointerUp = (e: PointerEvent): void => {
- document.removeEventListener("pointermove", this.onPointerMove);
- document.removeEventListener("pointerup", this.onPointerUp);
- e.stopPropagation();
- if (Math.abs(this._downX - e.clientX) < 3 && Math.abs(this._downY - e.clientY) < 3) {
- //show preview text cursor on tap
- this._previewCursorVisible = true;
- //select is not already selected
- if (!this.props.isSelected()) {
- this.props.select(false);
- }
- }
-
- }
-
- @action
- onPointerMove = (e: PointerEvent): void => {
- if (!e.cancelBubble && this.props.active()) {
- e.stopPropagation();
- e.preventDefault();
- let x = this.props.Document.GetNumber(KeyStore.PanX, 0);
- let y = this.props.Document.GetNumber(KeyStore.PanY, 0);
- let [dx, dy] = this.getTransform().transformDirection(e.clientX - this._lastX, e.clientY - this._lastY);
- this._previewCursorVisible = false;
- this.SetPan(x - dx, y - dy);
- }
- this._lastX = e.pageX;
- this._lastY = e.pageY;
- }
-
- @action
- onPointerWheel = (e: React.WheelEvent): void => {
- e.stopPropagation();
- e.preventDefault();
- let coefficient = 1000;
-
- if (e.ctrlKey) {
- var nativeWidth = this.props.Document.GetNumber(KeyStore.NativeWidth, 0);
- var nativeHeight = this.props.Document.GetNumber(KeyStore.NativeHeight, 0);
- const coefficient = 1000;
- let deltaScale = (1 - (e.deltaY / coefficient));
- this.props.Document.SetNumber(KeyStore.NativeWidth, nativeWidth * deltaScale);
- this.props.Document.SetNumber(KeyStore.NativeHeight, nativeHeight * deltaScale);
- e.stopPropagation();
- e.preventDefault();
- } else {
- // if (modes[e.deltaMode] == 'pixels') coefficient = 50;
- // else if (modes[e.deltaMode] == 'lines') coefficient = 1000; // This should correspond to line-height??
- let transform = this.getTransform();
-
- let deltaScale = (1 - (e.deltaY / coefficient));
- if (deltaScale * this.zoomScaling < 1 && this.isAnnotationOverlay)
- deltaScale = 1 / this.zoomScaling;
- let [x, y] = transform.transformPoint(e.clientX, e.clientY);
-
- let localTransform = this.getLocalTransform()
- localTransform = localTransform.inverse().scaleAbout(deltaScale, x, y)
- // console.log(localTransform)
-
- this.props.Document.SetNumber(KeyStore.Scale, localTransform.Scale);
- this.SetPan(-localTransform.TranslateX / localTransform.Scale, -localTransform.TranslateY / localTransform.Scale);
- }
- }
-
- @action
- private SetPan(panX: number, panY: number) {
- var x1 = this.getLocalTransform().inverse().Scale;
- var x2 = this.getTransform().inverse().Scale;
- const newPanX = Math.min((1 - 1 / x1) * this.nativeWidth, Math.max(0, panX));
- const newPanY = Math.min((1 - 1 / x1) * this.nativeHeight, Math.max(0, panY));
- this.props.Document.SetNumber(KeyStore.PanX, this.isAnnotationOverlay ? newPanX : panX);
- this.props.Document.SetNumber(KeyStore.PanY, this.isAnnotationOverlay ? newPanY : panY);
- }
-
- @action
- onDrop = (e: React.DragEvent): void => {
- var pt = this.getTransform().transformPoint(e.pageX, e.pageY);
- super.onDrop(e, { x: pt[0], y: pt[1] });
- }
-
- onDragOver = (): void => {
- }
-
- @action
- onKeyDown = (e: React.KeyboardEvent<Element>) => {
- //if not these keys, make a textbox if preview cursor is active!
- if (!e.ctrlKey && !e.altKey) {
- if (this._previewCursorVisible) {
- //make textbox and add it to this collection
- let [x, y] = this.getTransform().transformPoint(this._downX, this._downY); (this._downX, this._downY);
- let newBox = Documents.TextDocument({ width: 200, height: 100, x: x, y: y, title: "new" });
- // mark this collection so that when the text box is created we can send it the SelectOnLoad prop to focus itself
- this._selectOnLoaded = newBox.Id;
- //set text to be the typed key and get focus on text box
- this.props.CollectionView.addDocument(newBox);
- //remove cursor from screen
- this._previewCursorVisible = false;
- }
- }
- }
-
- @action
- bringToFront(doc: Document) {
- const { fieldKey: fieldKey, Document: Document } = this.props;
-
- const value: Document[] = Document.GetList<Document>(fieldKey, []).slice();
- value.sort((doc1, doc2) => {
- if (doc1 === doc) {
- return 1;
- }
- if (doc2 === doc) {
- return -1;
- }
- return doc1.GetNumber(KeyStore.ZIndex, 0) - doc2.GetNumber(KeyStore.ZIndex, 0);
- }).map((doc, index) => {
- doc.SetNumber(KeyStore.ZIndex, index + 1)
- });
- }
-
- @computed get backgroundLayout(): string | undefined {
- let field = this.props.Document.GetT(KeyStore.BackgroundLayout, TextField);
- if (field && field !== "<Waiting>") {
- return field.Data;
- }
- }
- @computed get overlayLayout(): string | undefined {
- let field = this.props.Document.GetT(KeyStore.OverlayLayout, TextField);
- if (field && field !== "<Waiting>") {
- return field.Data;
- }
- }
-
- focusDocument = (doc: Document) => {
- let x = doc.GetNumber(KeyStore.X, 0) + doc.GetNumber(KeyStore.Width, 0) / 2;
- let y = doc.GetNumber(KeyStore.Y, 0) + doc.GetNumber(KeyStore.Height, 0) / 2;
- this.SetPan(x, y);
- this.props.focus(this.props.Document);
- }
-
-
- @computed
- get views() {
- var curPage = this.props.Document.GetNumber(KeyStore.CurPage, 1);
- const lvalue = this.props.Document.GetT<ListField<Document>>(this.props.fieldKey, ListField);
- if (lvalue && lvalue != FieldWaiting) {
- return lvalue.Data.map(doc => {
- var page = doc.GetNumber(KeyStore.Page, 0);
- return (page != curPage && page != 0) ? (null) :
- (<CollectionFreeFormDocumentView key={doc.Id} Document={doc}
- AddDocument={this.props.addDocument}
- RemoveDocument={this.props.removeDocument}
- ScreenToLocalTransform={this.getTransform}
- isTopMost={false}
- SelectOnLoad={doc.Id === this._selectOnLoaded}
- ContentScaling={this.noScaling}
- PanelWidth={doc.Width}
- PanelHeight={doc.Height}
- ContainingCollectionView={this.props.CollectionView}
- focus={this.focusDocument}
- />);
- })
- }
- return null;
- }
-
- @computed
- get backgroundView() {
- return !this.backgroundLayout ? (null) :
- (<JsxParser
- components={{ FormattedTextBox, ImageBox, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, CollectionPDFView, WebBox, KeyValueBox, PDFBox }}
- bindings={this.props.bindings}
- jsx={this.backgroundLayout}
- showWarnings={true}
- onError={(test: any) => console.log(test)}
- />);
- }
- @computed
- get overlayView() {
- return !this.overlayLayout ? (null) :
- (<JsxParser
- components={{ FormattedTextBox, ImageBox, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, CollectionPDFView, WebBox, KeyValueBox, PDFBox }}
- bindings={this.props.bindings}
- jsx={this.overlayLayout}
- showWarnings={true}
- onError={(test: any) => console.log(test)}
- />);
- }
-
- getTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-COLLECTION_BORDER_WIDTH, -COLLECTION_BORDER_WIDTH).translate(-this.centeringShiftX, -this.centeringShiftY).transform(this.getLocalTransform())
- getLocalTransform = (): Transform => Transform.Identity.scale(1 / this.scale).translate(this.panX, this.panY);
- noScaling = () => 1;
-
- //when focus is lost, this will remove the preview cursor
- @action
- onBlur = (e: React.FocusEvent<HTMLDivElement>): void => {
- this._previewCursorVisible = false;
- }
-
- render() {
- //determines whether preview text cursor should be visible (ie when user taps this collection it should)
- let cursor = null;
- if (this._previewCursorVisible) {
- //get local position and place cursor there!
- let [x, y] = this.getTransform().transformPoint(this._downX, this._downY);
- cursor = <div id="prevCursor" onKeyPress={this.onKeyDown} style={{ color: "black", position: "absolute", transformOrigin: "left top", transform: `translate(${x}px, ${y}px)` }}>I</div>
- }
-
- let [dx, dy] = [this.centeringShiftX, this.centeringShiftY];
-
- const panx: number = -this.props.Document.GetNumber(KeyStore.PanX, 0);
- const pany: number = -this.props.Document.GetNumber(KeyStore.PanY, 0);
-
- return (
- <div className={`collectionfreeformview${this.isAnnotationOverlay ? "-overlay" : "-container"}`}
- onPointerDown={this.onPointerDown}
- onKeyPress={this.onKeyDown}
- onWheel={this.onPointerWheel}
- onDrop={this.onDrop.bind(this)}
- onDragOver={this.onDragOver}
- onBlur={this.onBlur}
- style={{ borderWidth: `${COLLECTION_BORDER_WIDTH}px`, }}
- tabIndex={0}
- ref={this.createDropTarget}>
- <div className="collectionfreeformview"
- style={{ transformOrigin: "left top", transform: `translate(${dx}px, ${dy}px) scale(${this.zoomScaling}, ${this.zoomScaling}) translate(${panx}px, ${pany}px)` }}
- ref={this._canvasRef}>
- {this.backgroundView}
- <InkingCanvas getScreenTransform={this.getTransform} Document={this.props.Document} />
- {cursor}
- {this.views}
- </div>
- {this.overlayView}
- </div>
- );
- }
-}
diff --git a/src/client/views/collections/CollectionPDFView.scss b/src/client/views/collections/CollectionPDFView.scss
new file mode 100644
index 000000000..0144625c1
--- /dev/null
+++ b/src/client/views/collections/CollectionPDFView.scss
@@ -0,0 +1,27 @@
+.collectionPdfView-buttonTray {
+ top : 25px;
+ left : 20px;
+ position: relative;
+ transform-origin: left top;
+ position: absolute;
+}
+.collectionPdfView-cont{
+ width: 100%;
+ height: 100%;
+ position: absolute;
+
+}
+.collectionPdfView-backward {
+ color : white;
+ top :0px;
+ left : 0px;
+ position: absolute;
+ background-color: rgba(50, 50, 50, 0.2);
+}
+.collectionPdfView-forward {
+ color : white;
+ top :0px;
+ left : 35px;
+ position: absolute;
+ background-color: rgba(50, 50, 50, 0.2);
+} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionPDFView.tsx b/src/client/views/collections/CollectionPDFView.tsx
index 7fd9f0f11..4d2daf149 100644
--- a/src/client/views/collections/CollectionPDFView.tsx
+++ b/src/client/views/collections/CollectionPDFView.tsx
@@ -1,11 +1,13 @@
-import { action, computed } from "mobx";
+import { action, computed, observable } from "mobx";
import { observer } from "mobx-react";
import { Document } from "../../../fields/Document";
import { KeyStore } from "../../../fields/KeyStore";
import { ContextMenu } from "../ContextMenu";
import { CollectionView, CollectionViewType } from "./CollectionView";
import { CollectionViewProps } from "./CollectionViewBase";
+import "./CollectionPDFView.scss"
import React = require("react");
+import { FieldId } from "../../../fields/Field";
@observer
@@ -17,24 +19,26 @@ export class CollectionPDFView extends React.Component<CollectionViewProps> {
isTopMost={isTopMost} SelectOnLoad={selectOnLoad} BackgroundView={BackgroundView} focus={focus}/>`;
}
- @action onPageBack = () => this.curPage > 1 ? this.props.Document.SetNumber(KeyStore.CurPage, this.curPage - 1) : 0;
- @action onPageForward = () => this.curPage < this.numPages ? this.props.Document.SetNumber(KeyStore.CurPage, this.curPage + 1) : 0;
+ private get curPage() { return this.props.Document.GetNumber(KeyStore.CurPage, -1); }
+ private get numPages() { return this.props.Document.GetNumber(KeyStore.NumPages, 0); }
+ @action onPageBack = () => this.curPage > 1 ? this.props.Document.SetNumber(KeyStore.CurPage, this.curPage - 1) : -1;
+ @action onPageForward = () => this.curPage < this.numPages ? this.props.Document.SetNumber(KeyStore.CurPage, this.curPage + 1) : -1;
- @computed private get curPage() { return this.props.Document.GetNumber(KeyStore.CurPage, 0); }
- @computed private get numPages() { return this.props.Document.GetNumber(KeyStore.NumPages, 0); }
- @computed private get uIButtons() {
+ private get uIButtons() {
+ let scaling = Math.min(1.8, this.props.ScreenToLocalTransform().transformDirection(1, 1)[0]);
return (
- <div className="pdfBox-buttonTray" key="tray">
- <button className="pdfButton" onClick={this.onPageBack}>{"<"}</button>
- <button className="pdfButton" onClick={this.onPageForward}>{">"}</button>
+ <div className="collectionPdfView-buttonTray" key="tray" style={{ transform: `scale(${scaling}, ${scaling})` }}>
+ <button className="collectionPdfView-backward" onClick={this.onPageBack}>{"<"}</button>
+ <button className="collectionPdfView-forward" onClick={this.onPageForward}>{">"}</button>
</div>);
}
// "inherited" CollectionView API starts here...
-
+ @observable
+ public SelectedDocs: FieldId[] = []
public active: () => boolean = () => CollectionView.Active(this);
- addDocument = (doc: Document): void => { CollectionView.AddDocument(this.props, doc); }
+ addDocument = (doc: Document, allowDuplicates: boolean): boolean => { return CollectionView.AddDocument(this.props, doc, allowDuplicates); }
removeDocument = (doc: Document): boolean => { return CollectionView.RemoveDocument(this.props, doc); }
specificContextMenu = (e: React.MouseEvent): void => {
@@ -47,7 +51,7 @@ export class CollectionPDFView extends React.Component<CollectionViewProps> {
get subView(): any { return CollectionView.SubView(this); }
render() {
- return (<div className="collectionView-cont" onContextMenu={this.specificContextMenu}>
+ return (<div className="collectionPdfView-cont" onContextMenu={this.specificContextMenu}>
{this.subView}
{this.props.isSelected() ? this.uIButtons : (null)}
</div>)
diff --git a/src/client/views/collections/CollectionSchemaView.scss b/src/client/views/collections/CollectionSchemaView.scss
index d40e6d314..c3a2e88ac 100644
--- a/src/client/views/collections/CollectionSchemaView.scss
+++ b/src/client/views/collections/CollectionSchemaView.scss
@@ -1,54 +1,141 @@
+@import "../global_variables";
+//options menu styling
+#schemaOptionsMenuBtn {
+ position: absolute;
+ height: 20px;
+ width: 20px;
+ border-radius: 50%;
+ z-index: 21;
+ right: 4px;
+ top: 4px;
+ pointer-events: auto;
+ background-color:black;
+ display:inline-block;
+ padding: 0px;
+ font-size: 100%;
+}
+#schema-options-header {
+ text-align: center;
+ padding: 0px;
+ margin: 0px;
+}
+.schema-options-subHeader {
+ color: $intermediate-color;
+ margin-bottom: 5px;
+}
+#schemaOptionsMenuBtn:hover {
+ transform: scale(1.15);
+}
+
+#preview-schema-checkbox-div {
+ margin-left: 20px;
+ font-size: 12px;
+}
+
+ #options-flyout-div {
+ text-align: left;
+ padding:0px;
+ z-index: 100;
+ font-family: $sans-serif;
+ padding-left: 5px;
+ }
+
+ #schema-col-checklist {
+ overflow: scroll;
+ text-align: left;
+ //background-color: $light-color-secondary;
+ line-height: 25px;
+ max-height: 175px;
+ font-family: $sans-serif;
+ font-size: 12px;
+ }
+
.collectionSchemaView-container {
- border-style: solid;
+ border: 1px solid $intermediate-color;
+ border-radius: $border-radius;
box-sizing: border-box;
position: absolute;
width: 100%;
height: 100%;
+
+ .collectionSchemaView-content {
+ position: absolute;
+ height: 100%;
+ width: 100%;
+ overflow: auto;
+ }
.collectionSchemaView-previewRegion {
- position: relative;
- background: black;
- float: left;
+ position: relative;
+ background: $light-color;
+ float: left;
height: 100%;
}
.collectionSchemaView-previewHandle {
position: absolute;
- height: 37px;
- width: 20px;
+ height: 15px;
+ width: 15px;
z-index: 20;
right: 0;
- top: 0;
+ top: 20px;
background: Black ;
}
.collectionSchemaView-dividerDragger{
position: relative;
background: black;
float: left;
+ height: 37px;
+ width: 20px;
+ z-index: 20;
+ right: 0;
+ top: 0;
+ background: $main-accent;
+ }
+ .collectionSchemaView-columnsHandle {
+ position: absolute;
+ height: 37px;
+ width: 20px;
+ z-index: 20;
+ left: 0;
+ bottom: 0;
+ background: $main-accent;
+ }
+ .collectionSchemaView-colDividerDragger {
+ position: relative;
+ box-sizing: border-box;
+ border-top: 1px solid $intermediate-color;
+ border-bottom: 1px solid $intermediate-color;
+ float: top;
+ width: 100%;
+ }
+ .collectionSchemaView-dividerDragger {
+ position: relative;
+ box-sizing: border-box;
+ border-left: 1px solid $intermediate-color;
+ border-right: 1px solid $intermediate-color;
+ float: left;
height: 100%;
}
.collectionSchemaView-tableContainer {
position: relative;
float: left;
- height: 100%;
+ height: 100%;
}
-
.ReactTable {
- position: absolute;
- // display: inline-block;
- // overflow: auto;
+ // position: absolute; // display: inline-block;
+ // overflow: auto;
width: 100%;
height: 100%;
- background: white;
+ background: $light-color;
box-sizing: border-box;
+ border: none !important;
.rt-table {
overflow-y: auto;
overflow-x: auto;
height: 100%;
-
display: -webkit-inline-box;
- direction: ltr;
- // direction:rtl;
+ direction: ltr; // direction:rtl;
// display:block;
}
.rt-tbody {
@@ -60,37 +147,47 @@
max-height: 44px;
}
.rt-td {
- border-width: 1;
- border-right-color: #aaa;
+ border-width: 1px;
+ border-right-color: $intermediate-color;
.imageBox-cont {
- position:relative;
- max-height:100%;
+ position: relative;
+ max-height: 100%;
}
.imageBox-cont img {
object-fit: contain;
max-width: 100%;
- height: 100%
+ height: 100%;
+ }
+ .videobox-cont {
+ object-fit: contain;
+ width: auto;
+ height: 100%;
}
- }
- .rt-tr-group {
- border-width: 1;
- border-bottom-color: #aaa
}
}
.ReactTable .rt-thead.-header {
- background:grey;
- }
- .ReactTable .rt-th, .ReactTable .rt-td {
+ background: $intermediate-color;
+ color: $light-color;
+ text-transform: uppercase;
+ letter-spacing: 2px;
+ font-size: 12px;
+ height: 30px;
+ padding-top: 4px;
+ }
+ .ReactTable .rt-th,
+ .ReactTable .rt-td {
max-height: 44;
padding: 3px 7px;
+ font-size: 13px;
+ text-align: center;
}
.ReactTable .rt-tbody .rt-tr-group:last-child {
- border-bottom: grey;
+ border-bottom: $intermediate-color;
border-bottom-style: solid;
border-bottom-width: 1;
}
.documentView-node:first-child {
- background: grey;
+ background: $light-color;
.imageBox-cont img {
object-fit: contain;
}
@@ -204,4 +301,12 @@
-webkit-flex: 1;
-ms-flex: 1;
flex: 1;
+}
+
+.-even {
+ background: $light-color !important;
+}
+
+.-odd {
+ background: $light-color-secondary !important;
} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 49f95c014..0ff6c3b40 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -1,37 +1,75 @@
import React = require("react")
-import { action, observable } from "mobx";
+import { library } from '@fortawesome/fontawesome-svg-core';
+import { faCog, faPlus } from '@fortawesome/free-solid-svg-icons';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { action, computed, observable, trace, untracked } from "mobx";
import { observer } from "mobx-react";
import Measure from "react-measure";
import ReactTable, { CellInfo, ComponentPropsGetterR, ReactTableDefaults } from "react-table";
import "react-table/react-table.css";
import { Document } from "../../../fields/Document";
-import { Field } from "../../../fields/Field";
+import { Field, Opt, FieldWaiting } from "../../../fields/Field";
+import { Key } from "../../../fields/Key";
import { KeyStore } from "../../../fields/KeyStore";
+import { ListField } from "../../../fields/ListField";
+import { Server } from "../../Server";
+import { setupDrag } from "../../util/DragManager";
import { CompileScript, ToField } from "../../util/Scripting";
import { Transform } from "../../util/Transform";
-import { ContextMenu } from "../ContextMenu";
+import { anchorPoints, Flyout } from "../DocumentDecorations";
+import '../DocumentDecorations.scss';
import { EditableView } from "../EditableView";
import { DocumentView } from "../nodes/DocumentView";
import { FieldView, FieldViewProps } from "../nodes/FieldView";
import "./CollectionSchemaView.scss";
-import { COLLECTION_BORDER_WIDTH } from "./CollectionView";
+import { CollectionView, COLLECTION_BORDER_WIDTH } from "./CollectionView";
import { CollectionViewBase } from "./CollectionViewBase";
-import { setupDrag } from "../../util/DragManager";
+import { TextField } from "../../../fields/TextField";
+
// bcz: need to add drag and drop of rows and columns. This seems like it might work for rows: https://codesandbox.io/s/l94mn1q657
@observer
+class KeyToggle extends React.Component<{ keyId: string, checked: boolean, toggle: (key: Key) => void }> {
+ @observable key: Key | undefined;
+
+ componentWillReceiveProps() {
+ Server.GetField(this.props.keyId, action((field: Opt<Field>) => {
+ if (field instanceof Key) {
+ this.key = field;
+ }
+ }))
+ }
+
+ render() {
+ if (this.key) {
+ return (<div key={this.key.Id}>
+ <input type="checkbox" checked={this.props.checked} onChange={() => this.key && this.props.toggle(this.key)} />
+ {this.key.Name}
+ </div>)
+ }
+ return (null);
+ }
+}
+
+@observer
export class CollectionSchemaView extends CollectionViewBase {
private _mainCont = React.createRef<HTMLDivElement>();
- private DIVIDER_WIDTH = 5;
+ private _startSplitPercent = 0;
+ private DIVIDER_WIDTH = 4;
+ @observable _columns: Array<Key> = [KeyStore.Title, KeyStore.Data, KeyStore.Author];
@observable _contentScaling = 1; // used to transfer the dimensions of the content pane in the DOM to the ContentScaling prop of the DocumentView
@observable _dividerX = 0;
@observable _panelWidth = 0;
@observable _panelHeight = 0;
@observable _selectedIndex = 0;
- @observable _splitPercentage: number = 50;
+ @observable _columnsPercentage = 0;
+ @observable _keys: Key[] = [];
+
+ @computed get splitPercentage() { return this.props.Document.GetNumber(KeyStore.SchemaSplitPercentage, 0); }
+
renderCell = (rowProps: CellInfo) => {
let props: FieldViewProps = {
@@ -47,11 +85,32 @@ export class CollectionSchemaView extends CollectionViewBase {
<FieldView {...props} />
)
let reference = React.createRef<HTMLDivElement>();
- let onItemDown = setupDrag(reference, () => props.doc);
+ let onItemDown = setupDrag(reference, () => props.doc, (containingCollection: CollectionView) => this.props.removeDocument(props.doc));
+ let applyToDoc = (doc: Document, value: string) => {
+ let script = CompileScript(value, { this: doc }, true);
+ if (!script.compiled) {
+ return false;
+ }
+ let field = script();
+ if (field instanceof Field) {
+ doc.Set(props.fieldKey, field);
+ return true;
+ } else {
+ let dataField = ToField(field);
+ if (dataField) {
+ doc.Set(props.fieldKey, dataField);
+ return true;
+ }
+ }
+ return false;
+ }
return (
- <div onPointerDown={onItemDown} key={props.doc.Id} ref={reference}>
- <EditableView contents={contents}
- height={36} GetValue={() => {
+ <div className="collectionSchemaView-cellContents" onPointerDown={onItemDown} style={{ height: "56px" }} key={props.doc.Id} ref={reference}>
+ <EditableView
+ display={"inline"}
+ contents={contents}
+ height={56}
+ GetValue={() => {
let field = props.doc.Get(props.fieldKey);
if (field && field instanceof Field) {
return field.ToScriptString();
@@ -59,22 +118,14 @@ export class CollectionSchemaView extends CollectionViewBase {
return field || "";
}}
SetValue={(value: string) => {
- let script = CompileScript(value, undefined, true);
- if (!script.compiled) {
- return false;
- }
- let field = script();
- if (field instanceof Field) {
- props.doc.Set(props.fieldKey, field);
- return true;
- } else {
- let dataField = ToField(field);
- if (dataField) {
- props.doc.Set(props.fieldKey, dataField);
- return true;
+ return applyToDoc(props.doc, value);
+ }}
+ OnFillDown={(value: string) => {
+ this.props.Document.GetTAsync<ListField<Document>>(this.props.fieldKey, ListField).then((val) => {
+ if (val) {
+ val.Data.forEach(doc => applyToDoc(doc, value));
}
- }
- return false;
+ })
}}>
</EditableView>
</div>
@@ -88,8 +139,8 @@ export class CollectionSchemaView extends CollectionViewBase {
}
return {
onClick: action((e: React.MouseEvent, handleOriginal: Function) => {
+ that.props.select(e.ctrlKey);
that._selectedIndex = rowInfo.index;
- this._splitPercentage += 0.05; // bcz - ugh - needed to force Measure to do its thing and call onResize
if (handleOriginal) {
handleOriginal()
@@ -102,64 +153,76 @@ export class CollectionSchemaView extends CollectionViewBase {
};
}
- _startSplitPercent = 0;
+ @computed
+ get columns() {
+ return this.props.Document.GetList<Key>(KeyStore.ColumnsKey, []);
+ }
+
+ @action
+ toggleKey = (key: Key) => {
+ this.props.Document.GetOrCreateAsync<ListField<Key>>(KeyStore.ColumnsKey, ListField,
+ (field) => {
+ const index = field.Data.indexOf(key);
+ if (index === -1) {
+ this.columns.push(key);
+ } else {
+ this.columns.splice(index, 1);
+ }
+
+ })
+ }
+
+ //toggles preview side-panel of schema
+ @action
+ toggleExpander = (event: React.ChangeEvent<HTMLInputElement>) => {
+ this._startSplitPercent = this.splitPercentage;
+ if (this._startSplitPercent == this.splitPercentage) {
+ this.props.Document.SetNumber(KeyStore.SchemaSplitPercentage, this.splitPercentage == 0 ? 33 : 0);
+ }
+ }
+
+ @computed
+ get findAllDocumentKeys(): { [id: string]: boolean } {
+ const docs = this.props.Document.GetList<Document>(this.props.fieldKey, []);
+ let keys: { [id: string]: boolean } = {}
+ if (this._optionsActivated > -1) {
+ // bcz: ugh. this is untracked since otherwise a large collection of documents will blast the server for all their fields.
+ // then as each document's fields come back, we update the documents _proxies. Each time we do this, the whole schema will be
+ // invalidated and re-rendered. This workaround will inquire all of the document fields before the options button is clicked.
+ // then by the time the options button is clicked, all of the fields should be in place. If a new field is added while this menu
+ // is displayed (unlikely) it won't show up until something else changes.
+ untracked(() => docs.map(doc => doc.GetAllPrototypes().map(proto => proto._proxies.forEach((val: any, key: string) => keys[key] = false))));
+ }
+ this.columns.forEach(key => keys[key.Id] = true)
+ return keys;
+ }
+
@action
onDividerMove = (e: PointerEvent): void => {
let nativeWidth = this._mainCont.current!.getBoundingClientRect();
- this._splitPercentage = Math.round((e.clientX - nativeWidth.left) / nativeWidth.width * 100);
+ this.props.Document.SetNumber(KeyStore.SchemaSplitPercentage, Math.max(0, 100 - Math.round((e.clientX - nativeWidth.left) / nativeWidth.width * 100)));
}
@action
onDividerUp = (e: PointerEvent): void => {
document.removeEventListener("pointermove", this.onDividerMove);
document.removeEventListener('pointerup', this.onDividerUp);
- if (this._startSplitPercent == this._splitPercentage) {
- this._splitPercentage = this._splitPercentage == 1 ? 66 : 100;
+ if (this._startSplitPercent == this.splitPercentage) {
+ this.props.Document.SetNumber(KeyStore.SchemaSplitPercentage, this.splitPercentage == 0 ? 33 : 0);
}
}
onDividerDown = (e: React.PointerEvent) => {
- this._startSplitPercent = this._splitPercentage;
+ this._startSplitPercent = this.splitPercentage;
e.stopPropagation();
e.preventDefault();
document.addEventListener("pointermove", this.onDividerMove);
document.addEventListener('pointerup', this.onDividerUp);
}
+
+ @observable _tableWidth = 0;
@action
- onExpanderMove = (e: PointerEvent): void => {
- e.stopPropagation();
- e.preventDefault();
- }
- @action
- onExpanderUp = (e: PointerEvent): void => {
- e.stopPropagation();
- e.preventDefault();
- document.removeEventListener("pointermove", this.onExpanderMove);
- document.removeEventListener('pointerup', this.onExpanderUp);
- if (this._startSplitPercent == this._splitPercentage) {
- this._splitPercentage = this._splitPercentage == 100 ? 66 : 100;
- }
+ setTableDimensions = (r: any) => {
+ this._tableWidth = r.entry.width;
}
- onExpanderDown = (e: React.PointerEvent) => {
- this._startSplitPercent = this._splitPercentage;
- e.stopPropagation();
- e.preventDefault();
- document.addEventListener("pointermove", this.onExpanderMove);
- document.addEventListener('pointerup', this.onExpanderUp);
- }
-
- onPointerDown = (e: React.PointerEvent) => {
- // if (e.button === 2 && this.active) {
- // e.stopPropagation();
- // e.preventDefault();
- // } else
- {
- if (e.buttons === 1) {
- if (this.props.isSelected()) {
- e.stopPropagation();
- }
- }
- }
- }
-
@action
setScaling = (r: any) => {
const children = this.props.Document.GetList<Document>(this.props.fieldKey, []);
@@ -175,43 +238,111 @@ export class CollectionSchemaView extends CollectionViewBase {
getTransform = (): Transform => {
return this.props.ScreenToLocalTransform().translate(- COLLECTION_BORDER_WIDTH - this.DIVIDER_WIDTH - this._dividerX, - COLLECTION_BORDER_WIDTH).scale(1 / this._contentScaling);
}
+ getPreviewTransform = (): Transform => {
+ return this.props.ScreenToLocalTransform().translate(- COLLECTION_BORDER_WIDTH - this.DIVIDER_WIDTH - this._dividerX - this._tableWidth, - COLLECTION_BORDER_WIDTH).scale(1 / this._contentScaling);
+ }
focusDocument = (doc: Document) => { }
+ onPointerDown = (e: React.PointerEvent): void => {
+ if (this.props.isSelected()) {
+ e.stopPropagation();
+ }
+ }
+
+ @action
+ addColumn = () => {
+ this.columns.push(new Key(this.newKeyName));
+ this.newKeyName = "";
+ }
+
+ @observable
+ newKeyName: string = "";
+
+ @action
+ newKeyChange = (e: React.ChangeEvent<HTMLInputElement>) => {
+ this.newKeyName = e.currentTarget.value;
+ }
+ onWheel = (e: React.WheelEvent): void => {
+ if (this.props.active())
+ e.stopPropagation();
+ }
+
+ @observable _optionsActivated: number = 0;
+ @action
+ OptionsMenuDown = (e: React.PointerEvent) => {
+ this._optionsActivated++;
+ }
+
+ @observable previewScript: string = "this";
+ @action
+ onPreviewScriptChange = (e: React.ChangeEvent<HTMLInputElement>) => {
+ this.previewScript = e.currentTarget.value;
+ }
+
render() {
- const columns = this.props.Document.GetList(KeyStore.ColumnsKey, [KeyStore.Title, KeyStore.Data, KeyStore.Author])
+ library.add(faCog);
+ library.add(faPlus);
+ const columns = this.columns;
const children = this.props.Document.GetList<Document>(this.props.fieldKey, []);
const selected = children.length > this._selectedIndex ? children[this._selectedIndex] : undefined;
+ //all the keys/columns that will be displayed in the schema
+ const allKeys = this.findAllDocumentKeys;
+ let doc: any = selected ? selected.Get(new Key(this.previewScript)) : undefined;
+
+ // let doc = CompileScript(this.previewScript, { this: selected }, true)();
let content = this._selectedIndex == -1 || !selected ? (null) : (
<Measure onResize={this.setScaling}>
{({ measureRef }) =>
<div className="collectionSchemaView-content" ref={measureRef}>
- <DocumentView Document={selected}
+ {doc instanceof Document ? <DocumentView Document={doc}
AddDocument={this.props.addDocument} RemoveDocument={this.props.removeDocument}
isTopMost={false}
SelectOnLoad={false}
- ScreenToLocalTransform={this.getTransform}
+ ScreenToLocalTransform={this.getPreviewTransform}
ContentScaling={this.getContentScaling}
PanelWidth={this.getPanelWidth}
PanelHeight={this.getPanelHeight}
ContainingCollectionView={this.props.CollectionView}
focus={this.focusDocument}
- />
+ /> : null}
+ <input value={this.previewScript} onChange={this.onPreviewScriptChange}
+ style={{ position: 'absolute', bottom: '0px' }} />
</div>
}
</Measure>
)
- let previewHandle = !this.props.active() ? (null) : (
- <div className="collectionSchemaView-previewHandle" onPointerDown={this.onExpanderDown} />);
+ let dividerDragger = this.splitPercentage == 0 ? (null) :
+ <div className="collectionSchemaView-dividerDragger" onPointerDown={this.onDividerDown} style={{ width: `${this.DIVIDER_WIDTH}px` }} />
+
+ //options button and menu
+ let optionsMenu = !this.props.active() ? (null) : (<Flyout
+ anchorPoint={anchorPoints.LEFT_TOP}
+ content={<div>
+ <div id="schema-options-header"><h5><b>Options</b></h5></div>
+ <div id="options-flyout-div">
+ <h6 className="schema-options-subHeader">Preview Window</h6>
+ <div id="preview-schema-checkbox-div"><input type="checkbox" key={"Show Preview"} checked={this.splitPercentage != 0} onChange={this.toggleExpander} /> Show Preview </div>
+ <h6 className="schema-options-subHeader" >Displayed Columns</h6>
+ <ul id="schema-col-checklist" >
+ {Array.from(Object.keys(allKeys)).map(item => {
+ return (<KeyToggle checked={allKeys[item]} key={item} keyId={item} toggle={this.toggleKey} />)
+ })}
+ </ul>
+ <input value={this.newKeyName} onChange={this.newKeyChange} />
+ <button onClick={this.addColumn}><FontAwesomeIcon style={{ color: "white" }} icon="plus" size="lg" /></button>
+ </div>
+ </div>
+ }>
+ <button id="schemaOptionsMenuBtn" onPointerDown={this.OptionsMenuDown}><FontAwesomeIcon style={{ color: "white" }} icon="cog" size="sm" /></button>
+ </Flyout>);
+
return (
- <div className="collectionSchemaView-container" onPointerDown={this.onPointerDown} ref={this._mainCont} style={{ borderWidth: `${COLLECTION_BORDER_WIDTH}px` }} >
+ <div className="collectionSchemaView-container" onPointerDown={this.onPointerDown} onWheel={this.onWheel} ref={this._mainCont} style={{ borderWidth: `${COLLECTION_BORDER_WIDTH}px` }} >
<div className="collectionSchemaView-dropTarget" onDrop={(e: React.DragEvent) => this.onDrop(e, {})} ref={this.createDropTarget}>
- <Measure onResize={action((r: any) => {
- this._dividerX = r.entry.width;
- this._panelHeight = r.entry.height;
- })}>
+ <Measure onResize={this.setTableDimensions}>
{({ measureRef }) =>
- <div ref={measureRef} className="collectionSchemaView-tableContainer" style={{ width: `${this._splitPercentage}%` }}>
+ <div className="collectionSchemaView-tableContainer" ref={measureRef} style={{ width: `calc(100% - ${this.splitPercentage}%)` }}>
<ReactTable
data={children}
pageSize={children.length}
@@ -229,14 +360,13 @@ export class CollectionSchemaView extends CollectionViewBase {
}}
getTrProps={this.getTrProps}
/>
- </div>
- }
+ </div>}
</Measure>
- <div className="collectionSchemaView-dividerDragger" onPointerDown={this.onDividerDown} style={{ width: `${this.DIVIDER_WIDTH}px` }} />
- <div className="collectionSchemaView-previewRegion" style={{ width: `calc(${100 - this._splitPercentage}% - ${this.DIVIDER_WIDTH}px)` }}>
+ {dividerDragger}
+ <div className="collectionSchemaView-previewRegion" style={{ width: `calc(${this.props.Document.GetNumber(KeyStore.SchemaSplitPercentage, 0)}% - ${this.DIVIDER_WIDTH}px)` }}>
{content}
</div>
- {previewHandle}
+ {optionsMenu}
</div>
</div >
)
diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss
index f8d580a7b..5a14aa54d 100644
--- a/src/client/views/collections/CollectionTreeView.scss
+++ b/src/client/views/collections/CollectionTreeView.scss
@@ -1,16 +1,27 @@
+@import "../global_variables";
#body {
padding: 20px;
- background: #bbbbbb;
+ padding-left: 10px;
+ padding-right: 0px;
+ background: $light-color-secondary;
+ font-size: 13px;
+ overflow: scroll;
}
ul {
list-style: none;
+ padding-left: 20px;
}
li {
margin: 5px 0;
}
+.collection-child {
+ margin-top: 10px;
+ margin-bottom: 10px;
+}
+
.no-indent {
padding-left: 0;
}
@@ -18,10 +29,17 @@ li {
.bullet {
width: 1.5em;
display: inline-block;
+ color: $intermediate-color;
+}
+
+.coll-title {
+ font-size: 24px;
+ margin-bottom: 20px;
}
.collectionTreeView-dropTarget {
- border-style: solid;
+ border: 0px solid transparent;
+ border-radius: $border-radius;
box-sizing: border-box;
height: 100%;
}
@@ -30,8 +48,17 @@ li {
display: inline-table;
}
+.docContainer:hover {
+ .delete-button {
+ display: inline;
+ width: auto;
+ }
+}
+
.delete-button {
- color: #999999;
+ color: $intermediate-color;
float: right;
- margin-left: 1em;
+ margin-left: 15px;
+ margin-top: 3px;
+ display: inline;
} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 8b06d9ac4..70790af18 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -1,16 +1,19 @@
+import { IconProp, library } from '@fortawesome/fontawesome-svg-core';
+import { faCaretDown, faCaretRight, faTrashAlt } from '@fortawesome/free-solid-svg-icons';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { action, observable, trace } from "mobx";
import { observer } from "mobx-react";
-import { CollectionViewBase } from "./CollectionViewBase";
import { Document } from "../../../fields/Document";
+import { FieldWaiting } from "../../../fields/Field";
import { KeyStore } from "../../../fields/KeyStore";
import { ListField } from "../../../fields/ListField";
-import React = require("react")
-import { TextField } from "../../../fields/TextField";
-import { observable, action } from "mobx";
-import "./CollectionTreeView.scss";
-import { EditableView } from "../EditableView";
import { setupDrag } from "../../util/DragManager";
-import { FieldWaiting } from "../../../fields/Field";
-import { COLLECTION_BORDER_WIDTH } from "./CollectionView";
+import { EditableView } from "../EditableView";
+import "./CollectionTreeView.scss";
+import { CollectionView, COLLECTION_BORDER_WIDTH } from "./CollectionView";
+import { CollectionViewBase } from "./CollectionViewBase";
+import React = require("react")
+
export interface TreeViewProps {
document: Document;
@@ -23,19 +26,19 @@ export enum BulletType {
List
}
+library.add(faTrashAlt);
+library.add(faCaretDown);
+library.add(faCaretRight);
+
@observer
/**
* Component that takes in a document prop and a boolean whether it's collapsed or not.
*/
class TreeView extends React.Component<TreeViewProps> {
- @observable
- collapsed: boolean = false;
-
- delete = () => {
- this.props.deleteDoc(this.props.document);
- }
+ @observable _collapsed: boolean = true;
+ delete = () => this.props.deleteDoc(this.props.document);
@action
remove = (document: Document) => {
@@ -46,91 +49,63 @@ class TreeView extends React.Component<TreeViewProps> {
}
renderBullet(type: BulletType) {
- let onClicked = action(() => this.collapsed = !this.collapsed);
-
+ let onClicked = action(() => this._collapsed = !this._collapsed);
+ let bullet: IconProp | undefined = undefined;
switch (type) {
- case BulletType.Collapsed:
- return <div className="bullet" onClick={onClicked}>&#9654;</div>
- case BulletType.Collapsible:
- return <div className="bullet" onClick={onClicked}>&#9660;</div>
- case BulletType.List:
- return <div className="bullet">&mdash;</div>
+ case BulletType.Collapsed: bullet = "caret-right"; break;
+ case BulletType.Collapsible: bullet = "caret-down"; break;
}
+ return <div className="bullet" onClick={onClicked}>{bullet ? <FontAwesomeIcon icon={bullet} /> : ""} </div>
}
/**
* Renders the EditableView title element for placement into the tree.
*/
renderTitle() {
- let title = this.props.document.GetT<TextField>(KeyStore.Title, TextField);
-
- // if the title hasn't loaded, immediately return the div
- if (!title || title === "<Waiting>") {
- return <div key={this.props.document.Id}></div>;
- }
-
- return <div className="docContainer"> <EditableView contents={title.Data}
- height={36} GetValue={() => {
- let title = this.props.document.GetT<TextField>(KeyStore.Title, TextField);
- if (title && title !== "<Waiting>")
- return title.Data;
- return "";
- }} SetValue={(value: string) => {
- this.props.document.SetData(KeyStore.Title, value, TextField);
- return true;
- }} />
- <div className="delete-button" onClick={this.delete}>x</div>
- </div >
+ let reference = React.createRef<HTMLDivElement>();
+ let onItemDown = setupDrag(reference, () => this.props.document, (containingCollection: CollectionView) => this.props.deleteDoc(this.props.document));
+ let editableView = (titleString: string) =>
+ (<EditableView
+ display={"inline"}
+ contents={titleString}
+ height={36}
+ GetValue={() => this.props.document.Title}
+ SetValue={(value: string) => {
+ this.props.document.SetText(KeyStore.Title, value);
+ return true;
+ }}
+ />);
+ return (
+ <div className="docContainer" ref={reference} onPointerDown={onItemDown}>
+ {editableView(this.props.document.Title)}
+ <div className="delete-button" onClick={this.delete}><FontAwesomeIcon icon="trash-alt" size="xs" /></div>
+ </div >)
}
render() {
- var children = this.props.document.GetT<ListField<Document>>(KeyStore.Data, ListField);
-
- let reference = React.createRef<HTMLDivElement>();
- let onItemDown = setupDrag(reference, () => this.props.document);
- let titleElement = this.renderTitle();
+ let bulletType = BulletType.List;
+ let childElements: JSX.Element | undefined = undefined;
- // check if this document is a collection
- if (children && children !== FieldWaiting) {
- let subView;
-
- // if uncollapsed, then add the children elements
- if (!this.collapsed) {
- // render all children elements
- let childrenElement = (children.Data.map(value =>
- <TreeView document={value} deleteDoc={this.remove} />)
- )
- subView =
- <li key={this.props.document.Id} >
- {this.renderBullet(BulletType.Collapsible)}
- {titleElement}
- <ul key={this.props.document.Id}>
- {childrenElement}
- </ul>
- </li>
- } else {
- subView = <li key={this.props.document.Id}>
- {this.renderBullet(BulletType.Collapsed)}
- {titleElement}
- </li>
+ var children = this.props.document.GetT<ListField<Document>>(KeyStore.Data, ListField);
+ if (children && children !== FieldWaiting) { // add children for a collection
+ if (!this._collapsed) {
+ bulletType = BulletType.Collapsible;
+ childElements = <ul>
+ {children.Data.map(value => <TreeView key={value.Id} document={value} deleteDoc={this.remove} />)}
+ </ul>
}
-
- return <div className="treeViewItem-container" onPointerDown={onItemDown} ref={reference}>
- {subView}
- </div>
- }
-
- // otherwise this is a normal leaf node
- else {
- return <li key={this.props.document.Id}>
- {this.renderBullet(BulletType.List)}
- {titleElement}
- </li>;
+ else bulletType = BulletType.Collapsed;
}
+ return <div className="treeViewItem-container" >
+ <li className="collection-child">
+ {this.renderBullet(bulletType)}
+ {this.renderTitle()}
+ {childElements ? childElements : (null)}
+ </li>
+ </div>
}
}
-
@observer
export class CollectionTreeView extends CollectionViewBase {
@@ -143,12 +118,6 @@ export class CollectionTreeView extends CollectionViewBase {
}
render() {
- let titleStr = "";
- let title = this.props.Document.GetT<TextField>(KeyStore.Title, TextField);
- if (title && title !== FieldWaiting) {
- titleStr = title.Data;
- }
-
var children = this.props.Document.GetT<ListField<Document>>(KeyStore.Data, ListField);
let childrenElement = !children || children === FieldWaiting ? (null) :
(children.Data.map(value =>
@@ -156,16 +125,19 @@ export class CollectionTreeView extends CollectionViewBase {
)
return (
- <div id="body" className="collectionTreeView-dropTarget" onDrop={(e: React.DragEvent) => this.onDrop(e, {})} ref={this.createDropTarget} style={{ borderWidth: `${COLLECTION_BORDER_WIDTH}px` }}>
- <h3>
- <EditableView contents={titleStr}
- height={72} GetValue={() => {
- return this.props.Document.Title;
- }} SetValue={(value: string) => {
- this.props.Document.SetData(KeyStore.Title, value, TextField);
+ <div id="body" className="collectionTreeView-dropTarget" onWheel={(e: React.WheelEvent) => e.stopPropagation()} onDrop={(e: React.DragEvent) => this.onDrop(e, {})} ref={this.createDropTarget} style={{ borderWidth: `${COLLECTION_BORDER_WIDTH}px` }}>
+ <div className="coll-title">
+ <EditableView
+ contents={this.props.Document.Title}
+ display={"inline"}
+ height={72}
+ GetValue={() => this.props.Document.Title}
+ SetValue={(value: string) => {
+ this.props.Document.SetText(KeyStore.Title, value);
return true;
}} />
- </h3>
+ </div>
+ <hr />
<ul className="no-indent">
{childrenElement}
</ul>
diff --git a/src/client/views/collections/CollectionVideoView.scss b/src/client/views/collections/CollectionVideoView.scss
new file mode 100644
index 000000000..cbb981b13
--- /dev/null
+++ b/src/client/views/collections/CollectionVideoView.scss
@@ -0,0 +1,40 @@
+
+.collectionVideoView-cont{
+ width: 100%;
+ height: 100%;
+ position: absolute;
+
+}
+.collectionVideoView-time{
+ color : white;
+ top :25px;
+ left : 25px;
+ position: absolute;
+ background-color: rgba(50, 50, 50, 0.2);
+ transform-origin: left top;
+}
+.collectionVideoView-play {
+ width: 25px;
+ height: 20px;
+ bottom: 25px;
+ left : 25px;
+ position: absolute;
+ color : white;
+ background-color: rgba(50, 50, 50, 0.2);
+ border-radius: 4px;
+ text-align: center;
+ transform-origin: left bottom;
+}
+.collectionVideoView-full {
+ width: 25px;
+ height: 20px;
+ bottom: 25px;
+ right : 25px;
+ position: absolute;
+ color : white;
+ background-color: rgba(50, 50, 50, 0.2);
+ border-radius: 4px;
+ text-align: center;
+ transform-origin: right bottom;
+
+} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionVideoView.tsx b/src/client/views/collections/CollectionVideoView.tsx
new file mode 100644
index 000000000..470a853e3
--- /dev/null
+++ b/src/client/views/collections/CollectionVideoView.tsx
@@ -0,0 +1,130 @@
+import { action, computed, observable, trace } from "mobx";
+import { observer } from "mobx-react";
+import { Document } from "../../../fields/Document";
+import { KeyStore } from "../../../fields/KeyStore";
+import { ContextMenu } from "../ContextMenu";
+import { CollectionView, CollectionViewType } from "./CollectionView";
+import { CollectionViewProps } from "./CollectionViewBase";
+import React = require("react");
+import { FieldId } from "../../../fields/Field";
+import "./CollectionVideoView.scss"
+
+
+@observer
+export class CollectionVideoView extends React.Component<CollectionViewProps> {
+ private _intervalTimer: any = undefined;
+ private _player: HTMLVideoElement | undefined = undefined;
+
+ @observable _currentTimecode: number = 0;
+ @observable _isPlaying: boolean = false;
+
+ public static LayoutString(fieldKey: string = "DataKey") {
+ return `<${CollectionVideoView.name} Document={Document}
+ ScreenToLocalTransform={ScreenToLocalTransform} fieldKey={${fieldKey}} panelWidth={PanelWidth} panelHeight={PanelHeight} isSelected={isSelected} select={select} bindings={bindings}
+ isTopMost={isTopMost} SelectOnLoad={selectOnLoad} BackgroundView={BackgroundView} focus={focus}/>`;
+ }
+ private get uIButtons() {
+ let scaling = Math.min(1.8, this.props.ScreenToLocalTransform().transformDirection(1, 1)[0]);
+ return ([
+ <div className="collectionVideoView-time" key="time" onPointerDown={this.onResetDown} style={{ transform: `scale(${scaling}, ${scaling})` }}>
+ <span>{"" + Math.round(this._currentTimecode)}</span>
+ <span style={{ fontSize: 8 }}>{" " + Math.round((this._currentTimecode - Math.trunc(this._currentTimecode)) * 100)}</span>
+ </div>,
+ <div className="collectionVideoView-play" key="play" onPointerDown={this.onPlayDown} style={{ transform: `scale(${scaling}, ${scaling})` }}>
+ {this._isPlaying ? "\"" : ">"}
+ </div>,
+ <div className="collectionVideoView-full" key="full" onPointerDown={this.onFullDown} style={{ transform: `scale(${scaling}, ${scaling})` }}>
+ F
+ </div>
+ ]);
+ }
+
+ @action
+ mainCont = (ele: HTMLDivElement | null) => {
+ if (ele) {
+ this._player = ele!.getElementsByTagName("video")[0];
+ if (this.props.Document.GetNumber(KeyStore.CurPage, -1) >= 0) {
+ this._currentTimecode = this.props.Document.GetNumber(KeyStore.CurPage, -1);
+ }
+ }
+ }
+
+ componentDidMount() {
+ this._intervalTimer = setInterval(this.updateTimecode, 1000);
+ }
+
+ componentWillUnmount() {
+ clearInterval(this._intervalTimer);
+ }
+
+ @action
+ updateTimecode = () => {
+ if (this._player) {
+ if ((this._player as any).AHackBecauseSomethingResetsTheVideoToZero != -1) {
+ this._player.currentTime = (this._player as any).AHackBecauseSomethingResetsTheVideoToZero;
+ (this._player as any).AHackBecauseSomethingResetsTheVideoToZero = -1;
+ } else {
+ this._currentTimecode = this._player.currentTime;
+ this.props.Document.SetNumber(KeyStore.CurPage, Math.round(this._currentTimecode));
+ }
+ }
+ }
+
+ @action
+ onPlayDown = () => {
+ if (this._player) {
+ if (this._player.paused) {
+ this._player.play();
+ this._isPlaying = true;
+ } else {
+ this._player.pause();
+ this._isPlaying = false;
+ }
+ }
+ }
+
+ @action
+ onFullDown = (e: React.PointerEvent) => {
+ if (this._player) {
+ this._player.requestFullscreen();
+ e.stopPropagation();
+ e.preventDefault();
+ }
+ }
+
+ @action
+ onResetDown = () => {
+ if (this._player) {
+ this._player.pause();
+ this._player.currentTime = 0;
+ }
+
+ }
+
+ // "inherited" CollectionView API starts here...
+
+ @observable
+ public SelectedDocs: FieldId[] = []
+ public active: () => boolean = () => CollectionView.Active(this);
+
+ addDocument = (doc: Document, allowDuplicates: boolean): boolean => { return CollectionView.AddDocument(this.props, doc, allowDuplicates); }
+ removeDocument = (doc: Document): boolean => { return CollectionView.RemoveDocument(this.props, doc); }
+
+ specificContextMenu = (e: React.MouseEvent): void => {
+ if (!e.isPropagationStopped() && this.props.Document.Id != "mainDoc") { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
+ ContextMenu.Instance.addItem({ description: "VideoOptions", event: () => { } });
+ }
+ }
+
+ get collectionViewType(): CollectionViewType { return CollectionViewType.Freeform; }
+ get subView(): any { return CollectionView.SubView(this); }
+
+
+ render() {
+ trace();
+ return (<div className="collectionVideoView-cont" ref={this.mainCont} onContextMenu={this.specificContextMenu}>
+ {this.subView}
+ {this.props.isSelected() ? this.uIButtons : (null)}
+ </div>)
+ }
+} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 49df04163..014aa1d8f 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -1,4 +1,4 @@
-import { action, computed } from "mobx";
+import { action, computed, observable } from "mobx";
import { observer } from "mobx-react";
import { Document } from "../../../fields/Document";
import { ListField } from "../../../fields/ListField";
@@ -7,12 +7,13 @@ import { ContextMenu } from "../ContextMenu";
import React = require("react");
import { KeyStore } from "../../../fields/KeyStore";
import { NumberField } from "../../../fields/NumberField";
-import { CollectionFreeFormView } from "./CollectionFreeFormView";
+import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormView";
import { CollectionDockingView } from "./CollectionDockingView";
import { CollectionSchemaView } from "./CollectionSchemaView";
import { CollectionViewProps } from "./CollectionViewBase";
import { CollectionTreeView } from "./CollectionTreeView";
-import { Field } from "../../../fields/Field";
+import { Field, FieldId, FieldWaiting } from "../../../fields/Field";
+import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils";
export enum CollectionViewType {
Invalid,
@@ -22,7 +23,7 @@ export enum CollectionViewType {
Tree
}
-export const COLLECTION_BORDER_WIDTH = 2;
+export const COLLECTION_BORDER_WIDTH = 1;
@observer
export class CollectionView extends React.Component<CollectionViewProps> {
@@ -33,8 +34,10 @@ export class CollectionView extends React.Component<CollectionViewProps> {
isTopMost={isTopMost} SelectOnLoad={selectOnLoad} BackgroundView={BackgroundView} focus={focus}/>`;
}
+ @observable
+ public SelectedDocs: FieldId[] = [];
public active: () => boolean = () => CollectionView.Active(this);
- addDocument = (doc: Document): void => { CollectionView.AddDocument(this.props, doc); }
+ addDocument = (doc: Document, allowDuplicates: boolean): boolean => { return CollectionView.AddDocument(this.props, doc, allowDuplicates); }
removeDocument = (doc: Document): boolean => { return CollectionView.RemoveDocument(this.props, doc); }
get subView() { return CollectionView.SubView(this); }
@@ -45,16 +48,49 @@ export class CollectionView extends React.Component<CollectionViewProps> {
return isSelected || childSelected || topMost;
}
+ static createsCycle(documentToAdd: Document, containerDocument: Document): boolean {
+ let data = documentToAdd.GetList<Document>(KeyStore.Data, []);
+ for (let i = 0; i < data.length; i++) {
+ if (CollectionView.createsCycle(data[i], containerDocument))
+ return true;
+ }
+ let annots = documentToAdd.GetList<Document>(KeyStore.Annotations, []);
+ for (let i = 0; i < annots.length; i++) {
+ if (CollectionView.createsCycle(annots[i], containerDocument))
+ return true;
+ }
+ for (let containerProto: any = containerDocument; containerProto && containerProto != FieldWaiting; containerProto = containerProto.GetPrototype()) {
+ if (containerProto.Id == documentToAdd.Id)
+ return true;
+ }
+ return false;
+ }
+
@action
- public static AddDocument(props: CollectionViewProps, doc: Document) {
- doc.SetNumber(KeyStore.Page, props.Document.GetNumber(KeyStore.CurPage, 0));
+ public static AddDocument(props: CollectionViewProps, doc: Document, allowDuplicates: boolean): boolean {
+ var curPage = props.Document.GetNumber(KeyStore.CurPage, -1);
+ doc.SetOnPrototype(KeyStore.Page, new NumberField(curPage));
+ if (curPage >= 0) {
+ doc.SetOnPrototype(KeyStore.AnnotationOn, props.Document);
+ }
if (props.Document.Get(props.fieldKey) instanceof Field) {
//TODO This won't create the field if it doesn't already exist
const value = props.Document.GetData(props.fieldKey, ListField, new Array<Document>())
- value.push(doc);
+ if (!CollectionView.createsCycle(doc, props.Document)) {
+ if (!value.some(v => v.Id == doc.Id) || allowDuplicates)
+ value.push(doc);
+ }
+ else
+ return false;
} else {
- props.Document.SetData(props.fieldKey, [doc], ListField);
+ let proto = props.Document.GetPrototype();
+ if (!proto || proto == FieldWaiting || !CollectionView.createsCycle(proto, doc)) {
+ props.Document.SetOnPrototype(props.fieldKey, new ListField([doc]));
+ }
+ else
+ return false;
}
+ return true;
}
@action
@@ -68,11 +104,16 @@ export class CollectionView extends React.Component<CollectionViewProps> {
break;
}
}
+ doc.GetTAsync(KeyStore.AnnotationOn, Document).then((annotationOn) => {
+ if (annotationOn == props.Document) {
+ doc.Set(KeyStore.AnnotationOn, undefined, true);
+ }
+ })
if (index !== -1) {
value.splice(index, 1)
- SelectionManager.DeselectAll()
+ //SelectionManager.DeselectAll()
ContextMenu.Instance.clearItems()
return true;
}
@@ -82,7 +123,7 @@ export class CollectionView extends React.Component<CollectionViewProps> {
get collectionViewType(): CollectionViewType {
let Document = this.props.Document;
let viewField = Document.GetT(KeyStore.ViewType, NumberField);
- if (viewField === "<Waiting>") {
+ if (viewField === FieldWaiting) {
return CollectionViewType.Invalid;
} else if (viewField) {
return viewField.Data;
@@ -92,11 +133,10 @@ export class CollectionView extends React.Component<CollectionViewProps> {
}
specificContextMenu = (e: React.MouseEvent): void => {
- if (!e.isPropagationStopped() && this.props.Document.Id != "mainDoc") { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
+ if (!e.isPropagationStopped() && this.props.Document.Id != CurrentUserUtils.MainDocId) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
ContextMenu.Instance.addItem({ description: "Freeform", event: () => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Freeform) })
ContextMenu.Instance.addItem({ description: "Schema", event: () => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Schema) })
ContextMenu.Instance.addItem({ description: "Treeview", event: () => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Tree) })
- ContextMenu.Instance.addItem({ description: "Docking", event: () => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Docking) })
}
}
@@ -116,4 +156,4 @@ export class CollectionView extends React.Component<CollectionViewProps> {
{this.subView}
</div>)
}
-} \ No newline at end of file
+}
diff --git a/src/client/views/collections/CollectionViewBase.tsx b/src/client/views/collections/CollectionViewBase.tsx
index 0a3b965f2..316d20c9d 100644
--- a/src/client/views/collections/CollectionViewBase.tsx
+++ b/src/client/views/collections/CollectionViewBase.tsx
@@ -1,16 +1,22 @@
-import { action } from "mobx";
+import { action, runInAction } from "mobx";
import { Document } from "../../../fields/Document";
import { ListField } from "../../../fields/ListField";
import React = require("react");
import { KeyStore } from "../../../fields/KeyStore";
-import { FieldWaiting } from "../../../fields/Field";
+import { FieldWaiting, Opt } from "../../../fields/Field";
import { undoBatch } from "../../util/UndoManager";
import { DragManager } from "../../util/DragManager";
-import { DocumentView } from "../nodes/DocumentView";
import { Documents, DocumentOptions } from "../../documents/Documents";
import { Key } from "../../../fields/Key";
import { Transform } from "../../util/Transform";
import { CollectionView } from "./CollectionView";
+import { RouteStore } from "../../../server/RouteStore";
+import { TupleField } from "../../../fields/TupleField";
+import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils";
+import { NumberField } from "../../../fields/NumberField";
+import request = require("request");
+import { ServerUtils } from "../../../server/ServerUtil";
+import { Server } from "../../Server";
export interface CollectionViewProps {
fieldKey: Key;
@@ -24,13 +30,16 @@ export interface CollectionViewProps {
panelHeight: () => number;
focus: (doc: Document) => void;
}
+
export interface SubCollectionViewProps extends CollectionViewProps {
active: () => boolean;
- addDocument: (doc: Document) => void;
+ addDocument: (doc: Document, allowDuplicates: boolean) => boolean;
removeDocument: (doc: Document) => boolean;
CollectionView: CollectionView;
}
+export type CursorEntry = TupleField<[string, string], [number, number]>;
+
export class CollectionViewBase extends React.Component<SubCollectionViewProps> {
private dropDisposer?: DragManager.DragDropDisposer;
protected createDropTarget = (ele: HTMLDivElement) => {
@@ -42,79 +51,157 @@ export class CollectionViewBase extends React.Component<SubCollectionViewProps>
}
}
+ @action
+ protected setCursorPosition(position: [number, number]) {
+ let ind;
+ let doc = this.props.Document;
+ let id = CurrentUserUtils.id;
+ let email = CurrentUserUtils.email;
+ if (id && email) {
+ let textInfo: [string, string] = [id, email];
+ doc.GetTAsync(KeyStore.Prototype, Document).then(proto => {
+ if (!proto) {
+ return;
+ }
+ proto.GetOrCreateAsync<ListField<CursorEntry>>(KeyStore.Cursors, ListField, action((field: ListField<CursorEntry>) => {
+ let cursors = field.Data;
+ if (cursors.length > 0 && (ind = cursors.findIndex(entry => entry.Data[0][0] === id)) > -1) {
+ cursors[ind].Data[1] = position;
+ } else {
+ let entry = new TupleField<[string, string], [number, number]>([textInfo, position]);
+ cursors.push(entry);
+ }
+ }))
+ })
+ }
+ }
+
@undoBatch
@action
- protected drop(e: Event, de: DragManager.DropEvent) {
- const docView: DocumentView = de.data["documentView"];
- const doc: Document = de.data["document"];
- if (docView && docView.props.ContainingCollectionView && docView.props.ContainingCollectionView !== this.props.CollectionView) {
- if (docView.props.RemoveDocument) {
- docView.props.RemoveDocument(docView.props.Document);
+ protected drop(e: Event, de: DragManager.DropEvent): boolean {
+ if (de.data instanceof DragManager.DocumentDragData) {
+ if (de.data.aliasOnDrop) {
+ [KeyStore.Width, KeyStore.Height, KeyStore.CurPage].map(key =>
+ de.data.draggedDocuments.GetTAsync(key, NumberField, (f: Opt<NumberField>) => f ? de.data.droppedDocument.SetNumber(key, f.Data) : null));
+ }
+ let added = de.data.droppedDocuments.reduce((added, d) => this.props.addDocument(d, false), true);
+ if (added && de.data.removeDocument && !de.data.aliasOnDrop) {
+ de.data.removeDocument(this.props.CollectionView);
+ }
+ e.stopPropagation();
+ return added;
+ }
+ return false;
+ }
+
+ protected getDocumentFromType(type: string, path: string, options: DocumentOptions): Opt<Document> {
+ let ctor: ((path: string, options: DocumentOptions) => Document) | undefined;
+ if (type.indexOf("image") !== -1) {
+ ctor = Documents.ImageDocument;
+ }
+ if (type.indexOf("video") !== -1) {
+ ctor = Documents.VideoDocument;
+ }
+ if (type.indexOf("audio") !== -1) {
+ ctor = Documents.AudioDocument;
+ }
+ if (type.indexOf("pdf") !== -1) {
+ ctor = Documents.PdfDocument;
+ }
+ if (type.indexOf("html") !== -1) {
+ if (path.includes('localhost')) {
+ let s = path.split('/');
+ let id = s[s.length - 1];
+ Server.GetField(id).then(field => {
+ if (field instanceof Document) {
+ let alias = field.CreateAlias();
+ alias.SetNumber(KeyStore.X, options.x || 0);
+ alias.SetNumber(KeyStore.Y, options.y || 0);
+ alias.SetNumber(KeyStore.Width, options.width || 300);
+ alias.SetNumber(KeyStore.Height, options.height || options.width || 300);
+ this.props.addDocument(alias, false);
+ }
+ })
+ return undefined;
}
- this.props.addDocument(docView.props.Document);
- } else if (doc) {
- this.props.removeDocument(doc);
- this.props.addDocument(doc);
+ ctor = Documents.WebDocument;
+ options = { height: options.width, ...options, title: path };
}
- e.stopPropagation();
+ return ctor ? ctor(path, options) : undefined;
}
@action
protected onDrop(e: React.DragEvent, options: DocumentOptions): void {
- e.stopPropagation()
- e.preventDefault()
let that = this;
let html = e.dataTransfer.getData("text/html");
let text = e.dataTransfer.getData("text/plain");
- if (html && html.indexOf("<img") != 0) {
+
+ if (text && text.startsWith("<div")) {
+ return;
+ }
+ e.stopPropagation()
+ e.preventDefault()
+
+ if (html && html.indexOf("<img") != 0 && !html.startsWith("<a")) {
+ console.log("not good");
let htmlDoc = Documents.HtmlDocument(html, { ...options, width: 300, height: 300 });
htmlDoc.SetText(KeyStore.DocumentText, text);
- this.props.addDocument(htmlDoc);
+ this.props.addDocument(htmlDoc, false);
return;
}
for (let i = 0; i < e.dataTransfer.items.length; i++) {
+ const upload = window.location.origin + RouteStore.upload;
let item = e.dataTransfer.items[i];
if (item.kind === "string" && item.type.indexOf("uri") != -1) {
- e.dataTransfer.items[i].getAsString(function (s) {
- action(() => {
- var img = Documents.ImageDocument(s, { ...options, nativeWidth: 300, width: 300, })
-
- let docs = that.props.Document.GetT(KeyStore.Data, ListField);
- if (docs != FieldWaiting) {
- if (!docs) {
- docs = new ListField<Document>();
- that.props.Document.Set(KeyStore.Data, docs)
+ e.dataTransfer.items[i].getAsString(action((s: string) => {
+ let document: Document;
+ request.head(ServerUtils.prepend(RouteStore.corsProxy + "/" + s), (err, res, body) => {
+ let type = res.headers["content-type"];
+ if (type) {
+ let doc = this.getDocumentFromType(type, s, { ...options, width: 300, nativeWidth: 300 })
+ if (doc) {
+ this.props.addDocument(doc, false);
}
- docs.Data.push(img);
}
- })()
-
- })
+ });
+ // this.props.addDocument(Documents.WebDocument(s, { ...options, width: 300, height: 300 }), false)
+ }))
}
- if (item.kind == "file" && item.type.indexOf("image")) {
- let fReader = new FileReader()
+ let type = item.type
+ if (item.kind == "file") {
let file = item.getAsFile();
-
- fReader.addEventListener("load", action("drop", () => {
- if (fReader.result) {
- let url = "" + fReader.result;
- let doc = Documents.ImageDocument(url, options)
- let docs = that.props.Document.GetT(KeyStore.Data, ListField);
- if (docs != FieldWaiting) {
- if (!docs) {
- docs = new ListField<Document>();
- that.props.Document.Set(KeyStore.Data, docs)
- }
- docs.Data.push(doc);
- }
- }
- }), false)
+ let formData = new FormData()
if (file) {
- fReader.readAsDataURL(file)
+ formData.append('file', file)
}
+
+ fetch(upload, {
+ method: 'POST',
+ body: formData
+ }).then((res: Response) => {
+ return res.json()
+ }).then(json => {
+ json.map((file: any) => {
+ let path = window.location.origin + file
+ runInAction(() => {
+ let doc = this.getDocumentFromType(type, path, { ...options, nativeWidth: 300, width: 300 })
+
+ let docs = that.props.Document.GetT(KeyStore.Data, ListField);
+ if (docs != FieldWaiting) {
+ if (!docs) {
+ docs = new ListField<Document>();
+ that.props.Document.Set(KeyStore.Data, docs)
+ }
+ if (doc) {
+ docs.Data.push(doc);
+ }
+ }
+ })
+ })
+ })
}
}
}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss
new file mode 100644
index 000000000..3b2f79be1
--- /dev/null
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss
@@ -0,0 +1,6 @@
+.collectionfreeformlinkview-linkLine {
+ stroke: black;
+ stroke-width: 3;
+ transform: translate(10000px,10000px);
+ pointer-events: all;
+} \ No newline at end of file
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
new file mode 100644
index 000000000..e84f0c5ad
--- /dev/null
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -0,0 +1,37 @@
+import { observer } from "mobx-react";
+import { Document } from "../../../../fields/Document";
+import { KeyStore } from "../../../../fields/KeyStore";
+import { Utils } from "../../../../Utils";
+import "./CollectionFreeFormLinkView.scss";
+import React = require("react");
+import v5 = require("uuid/v5");
+
+export interface CollectionFreeFormLinkViewProps {
+ A: Document;
+ B: Document;
+ LinkDocs: Document[];
+}
+
+@observer
+export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFormLinkViewProps> {
+
+ onPointerDown = (e: React.PointerEvent) => {
+ this.props.LinkDocs.map(l =>
+ console.log("Link:" + l.Title));
+ }
+ render() {
+ let l = this.props.LinkDocs;
+ let a = this.props.A;
+ let b = this.props.B;
+ let x1 = a.GetNumber(KeyStore.X, 0) + a.GetNumber(KeyStore.Width, 0) / 2;
+ let y1 = a.GetNumber(KeyStore.Y, 0) + a.GetNumber(KeyStore.Height, 0) / 2;
+ let x2 = b.GetNumber(KeyStore.X, 0) + b.GetNumber(KeyStore.Width, 0) / 2;
+ let y2 = b.GetNumber(KeyStore.Y, 0) + b.GetNumber(KeyStore.Height, 0) / 2;
+ return (
+ <line key={Utils.GenerateGuid()} className="collectionfreeformlinkview-linkLine" onPointerDown={this.onPointerDown}
+ style={{ strokeWidth: `${l.length * 5}` }}
+ x1={`${x1}`} y1={`${y1}`}
+ x2={`${x2}`} y2={`${y2}`} />
+ )
+ }
+} \ No newline at end of file
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.scss
new file mode 100644
index 000000000..4341c82f7
--- /dev/null
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.scss
@@ -0,0 +1,10 @@
+.collectionfreeformlinksview-svgCanvas{
+ transform: translate(-10000px,-10000px);
+ position: absolute;
+ width: 20000px;
+ height: 20000px;
+ pointer-events: none;
+ }
+ .collectionfreeformlinksview-container {
+ pointer-events: none;
+ } \ No newline at end of file
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
new file mode 100644
index 000000000..eb20b3100
--- /dev/null
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
@@ -0,0 +1,106 @@
+import { computed, reaction, runInAction, trace } from "mobx";
+import { observer } from "mobx-react";
+import { Document } from "../../../../fields/Document";
+import { FieldWaiting } from "../../../../fields/Field";
+import { KeyStore } from "../../../../fields/KeyStore";
+import { ListField } from "../../../../fields/ListField";
+import { Utils } from "../../../../Utils";
+import { DocumentManager } from "../../../util/DocumentManager";
+import { DocumentView } from "../../nodes/DocumentView";
+import { CollectionViewProps } from "../CollectionViewBase";
+import "./CollectionFreeFormLinksView.scss";
+import { CollectionFreeFormLinkView } from "./CollectionFreeFormLinkView";
+import React = require("react");
+import v5 = require("uuid/v5");
+
+@observer
+export class CollectionFreeFormLinksView extends React.Component<CollectionViewProps> {
+
+ componentDidMount() {
+ reaction(() => {
+ return DocumentManager.Instance.getAllDocumentViews(this.props.Document).map(dv => dv.props.Document.GetNumber(KeyStore.X, 0))
+ }, () => {
+ let views = DocumentManager.Instance.getAllDocumentViews(this.props.Document);
+ for (let i = 0; i < views.length; i++) {
+ for (let j = i + 1; j < views.length; j++) {
+ let srcDoc = views[j].props.Document;
+ let dstDoc = views[i].props.Document;
+ let x1 = srcDoc.GetNumber(KeyStore.X, 0);
+ let x1w = srcDoc.GetNumber(KeyStore.Width, -1);
+ let x2 = dstDoc.GetNumber(KeyStore.X, 0);
+ let x2w = dstDoc.GetNumber(KeyStore.Width, -1);
+ if (x1w < 0 || x2w < 0)
+ continue;
+ dstDoc.GetTAsync(KeyStore.Prototype, Document).then((protoDest) =>
+ srcDoc.GetTAsync(KeyStore.Prototype, Document).then((protoSrc) => runInAction(() => {
+ let dstTarg = (protoDest ? protoDest : dstDoc);
+ let srcTarg = (protoSrc ? protoSrc : srcDoc);
+ let findBrush = (field: ListField<Document>) => field.Data.findIndex(brush => {
+ let bdocs = brush.GetList(KeyStore.BrushingDocs, [] as Document[]);
+ return (bdocs.length == 0 || (bdocs[0] == dstTarg && bdocs[1] == srcTarg) || (bdocs[0] == srcTarg && bdocs[1] == dstTarg))
+ });
+ let brushAction = (field: ListField<Document>) => {
+ let found = findBrush(field);
+ if (found != -1)
+ field.Data.splice(found, 1);
+ };
+ if (Math.abs(x1 + x1w - x2) < 20 || Math.abs(x2 + x2w - x1) < 20) {
+ let linkDoc: Document = new Document();
+ linkDoc.SetText(KeyStore.Title, "Histogram Brush");
+ linkDoc.SetText(KeyStore.LinkDescription, "Brush between " + srcTarg.Title + " and " + dstTarg.Title);
+ linkDoc.SetData(KeyStore.BrushingDocs, [dstTarg, srcTarg], ListField);
+
+ brushAction = brushAction = (field: ListField<Document>) => (findBrush(field) == -1) && field.Data.push(linkDoc);
+ }
+ dstTarg.GetOrCreateAsync(KeyStore.BrushingDocs, ListField, brushAction);
+ srcTarg.GetOrCreateAsync(KeyStore.BrushingDocs, ListField, brushAction);
+ }
+ )))
+ }
+ }
+ })
+ }
+ documentAnchors(view: DocumentView) {
+ let equalViews = [view];
+ let containerDoc = view.props.Document.GetT(KeyStore.AnnotationOn, Document);
+ if (containerDoc && containerDoc != FieldWaiting && containerDoc instanceof Document) {
+ equalViews = DocumentManager.Instance.getDocumentViews(containerDoc.GetPrototype() as Document)
+ }
+ return equalViews.filter(sv => sv.props.ContainingCollectionView && sv.props.ContainingCollectionView.props.Document == this.props.Document);
+ }
+
+ @computed
+ get uniqueConnections() {
+ let connections = DocumentManager.Instance.LinkedDocumentViews.reduce((drawnPairs, connection) => {
+ let srcViews = this.documentAnchors(connection.a);
+ let targetViews = this.documentAnchors(connection.b);
+ let possiblePairs: { a: Document, b: Document, }[] = [];
+ srcViews.map(sv => targetViews.map(tv => possiblePairs.push({ a: sv.props.Document, b: tv.props.Document })));
+ possiblePairs.map(possiblePair => {
+ if (!drawnPairs.reduce((found, drawnPair) => {
+ let match = (possiblePair.a == drawnPair.a && possiblePair.b == drawnPair.b);
+ if (match) {
+ if (!drawnPair.l.reduce((found, link) => found || link.Id == connection.l.Id, false))
+ drawnPair.l.push(connection.l);
+ }
+ return match || found;
+ }, false)) {
+ drawnPairs.push({ a: possiblePair.a, b: possiblePair.b, l: [connection.l] as Document[] });
+ }
+ })
+ return drawnPairs
+ }, [] as { a: Document, b: Document, l: Document[] }[]);
+ return connections.map(c => <CollectionFreeFormLinkView key={Utils.GenerateGuid()} A={c.a} B={c.b} LinkDocs={c.l} />);
+ }
+
+ render() {
+ return (
+ <div className="collectionfreeformlinksview-container">
+ <svg className="collectionfreeformlinksview-svgCanvas">
+ {this.uniqueConnections}
+ </svg>
+ {this.props.children}
+ </div>
+ );
+ }
+} \ No newline at end of file
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
new file mode 100644
index 000000000..19382e66f
--- /dev/null
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
@@ -0,0 +1,115 @@
+import { action, computed, observable } from "mobx";
+import { observer } from "mobx-react";
+import { Document } from "../../../../fields/Document";
+import { FieldWaiting } from "../../../../fields/Field";
+import { KeyStore } from "../../../../fields/KeyStore";
+import { TextField } from "../../../../fields/TextField";
+import { DragManager } from "../../../util/DragManager";
+import { Transform } from "../../../util/Transform";
+import { undoBatch } from "../../../util/UndoManager";
+import { InkingCanvas } from "../../InkingCanvas";
+import { CollectionFreeFormDocumentView } from "../../nodes/CollectionFreeFormDocumentView";
+import { DocumentContentsView } from "../../nodes/DocumentContentsView";
+import { DocumentViewProps } from "../../nodes/DocumentView";
+import { COLLECTION_BORDER_WIDTH } from "../CollectionView";
+import { CollectionViewBase, CollectionViewProps, CursorEntry } from "../CollectionViewBase";
+import { CollectionFreeFormLinksView } from "./CollectionFreeFormLinksView";
+import "./CollectionFreeFormView.scss";
+import { MarqueeView } from "./MarqueeView";
+import React = require("react");
+import v5 = require("uuid/v5");
+import { CurrentUserUtils } from "../../../../server/authentication/models/current_user_utils";
+
+@observer
+export class CollectionFreeFormRemoteCursors extends React.Component<CollectionViewProps> {
+ protected getCursors(): CursorEntry[] {
+ let doc = this.props.Document;
+ let id = CurrentUserUtils.id;
+ let cursors = doc.GetList<CursorEntry>(KeyStore.Cursors, []);
+ let notMe = cursors.filter(entry => entry.Data[0][0] !== id);
+ return id ? notMe : [];
+ }
+
+ private crosshairs?: HTMLCanvasElement;
+ drawCrosshairs = (backgroundColor: string) => {
+ if (this.crosshairs) {
+ let c = this.crosshairs;
+ let ctx = c.getContext('2d');
+ if (ctx) {
+ ctx.fillStyle = backgroundColor;
+ ctx.fillRect(0, 0, 20, 20);
+
+ ctx.fillStyle = "black";
+ ctx.lineWidth = 0.5;
+
+ ctx.beginPath();
+
+ ctx.moveTo(10, 0);
+ ctx.lineTo(10, 8);
+
+ ctx.moveTo(10, 20);
+ ctx.lineTo(10, 12);
+
+ ctx.moveTo(0, 10);
+ ctx.lineTo(8, 10);
+
+ ctx.moveTo(20, 10);
+ ctx.lineTo(12, 10);
+
+ ctx.stroke();
+
+ // ctx.font = "10px Arial";
+ // ctx.fillText(CurrentUserUtils.email[0].toUpperCase(), 10, 10);
+ }
+ }
+ }
+ @computed
+ get sharedCursors() {
+ return this.getCursors().map(entry => {
+ if (entry.Data.length > 0) {
+ let id = entry.Data[0][0];
+ let email = entry.Data[0][1];
+ let point = entry.Data[1];
+ this.drawCrosshairs("#" + v5(id, v5.URL).substring(0, 6).toUpperCase() + "22")
+ return (
+ <div
+ key={id}
+ style={{
+ position: "absolute",
+ transform: `translate(${point[0] - 10}px, ${point[1] - 10}px)`,
+ zIndex: 10000,
+ transformOrigin: 'center center',
+ }}
+ >
+ <canvas
+ ref={(el) => { if (el) this.crosshairs = el }}
+ width={20}
+ height={20}
+ style={{
+ position: 'absolute',
+ width: "20px",
+ height: "20px",
+ opacity: 0.5,
+ borderRadius: "50%",
+ border: "2px solid black"
+ }}
+ />
+ <p
+ style={{
+ fontSize: 14,
+ color: "black",
+ // fontStyle: "italic",
+ marginLeft: -12,
+ marginTop: 4
+ }}
+ >{email[0].toUpperCase()}</p>
+ </div>
+ );
+ }
+ })
+ }
+
+ render() {
+ return this.sharedCursors;
+ }
+} \ No newline at end of file
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
new file mode 100644
index 000000000..81d21d89a
--- /dev/null
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
@@ -0,0 +1,86 @@
+@import "../../global_variables";
+
+.collectionfreeformview {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ transform-origin: left top;
+}
+.collectionfreeformview-container {
+ .collectionfreeformview > .jsx-parser {
+ position: absolute;
+ height: 100%;
+ width: 100%;
+ }
+
+ //nested freeform views
+ // .collectionfreeformview-container {
+ // background-image: linear-gradient(to right, $light-color-secondary 1px, transparent 1px),
+ // linear-gradient(to bottom, $light-color-secondary 1px, transparent 1px);
+ // background-size: 30px 30px;
+ // }
+
+ box-shadow: $intermediate-color 0.2vw 0.2vw 0.8vw;
+ border: 0px solid $light-color-secondary;
+ border-radius: $border-radius;
+ box-sizing: border-box;
+ position: relative;
+ overflow: hidden;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+}
+.collectionfreeformview-overlay {
+ .collectionfreeformview > .jsx-parser {
+ position: absolute;
+ height: 100%;
+ }
+ .formattedTextBox-cont {
+ background: $light-color-secondary;
+ }
+
+ opacity: 0.99;
+ border: 0px solid transparent;
+ border-radius: $border-radius;
+ box-sizing: border-box;
+ position:relative;
+ overflow: hidden;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ .collectionfreeformview {
+ .formattedTextBox-cont {
+ background:yellow;
+ }
+ }
+}
+
+// selection border...?
+.border {
+ border-style: solid;
+ box-sizing: border-box;
+ width: 98%;
+ height: 98%;
+ border-radius: $border-radius;
+}
+
+//this is an animation for the blinking cursor!
+@keyframes blink {
+ 0% {
+ opacity: 0;
+ }
+ 49% {
+ opacity: 0;
+ }
+ 50% {
+ opacity: 1;
+ }
+}
+
+#prevCursor {
+ animation: blink 1s infinite;
+}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
new file mode 100644
index 000000000..c5178f69d
--- /dev/null
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -0,0 +1,312 @@
+import { action, computed, observable, trace } from "mobx";
+import { observer } from "mobx-react";
+import { Document } from "../../../../fields/Document";
+import { FieldWaiting } from "../../../../fields/Field";
+import { KeyStore } from "../../../../fields/KeyStore";
+import { TextField } from "../../../../fields/TextField";
+import { DragManager } from "../../../util/DragManager";
+import { Transform } from "../../../util/Transform";
+import { undoBatch } from "../../../util/UndoManager";
+import { InkingCanvas } from "../../InkingCanvas";
+import { CollectionFreeFormDocumentView } from "../../nodes/CollectionFreeFormDocumentView";
+import { DocumentContentsView } from "../../nodes/DocumentContentsView";
+import { DocumentViewProps } from "../../nodes/DocumentView";
+import { COLLECTION_BORDER_WIDTH } from "../CollectionView";
+import { CollectionViewBase } from "../CollectionViewBase";
+import { CollectionFreeFormLinksView } from "./CollectionFreeFormLinksView";
+import "./CollectionFreeFormView.scss";
+import { MarqueeView } from "./MarqueeView";
+import React = require("react");
+import v5 = require("uuid/v5");
+import { CollectionFreeFormRemoteCursors } from "./CollectionFreeFormRemoteCursors";
+import { PreviewCursor } from "./PreviewCursor";
+
+@observer
+export class CollectionFreeFormView extends CollectionViewBase {
+ public _canvasRef = React.createRef<HTMLDivElement>();
+ private _selectOnLoaded: string = ""; // id of document that should be selected once it's loaded (used for click-to-type)
+
+ public addLiveTextBox = (newBox: Document) => {
+ // mark this collection so that when the text box is created we can send it the SelectOnLoad prop to focus itself and receive text input
+ this._selectOnLoaded = newBox.Id;
+ this.addDocument(newBox, false);
+ }
+
+ public addDocument = (newBox: Document, allowDuplicates: boolean) => {
+ let added = this.props.addDocument(newBox, false);
+ this.bringToFront(newBox);
+ return added;
+ }
+
+ public selectDocuments = (docs: Document[]) => {
+ this.props.CollectionView.SelectedDocs.length = 0;
+ docs.map(d => this.props.CollectionView.SelectedDocs.push(d.Id));
+ }
+
+ public getActiveDocuments = () => {
+ var curPage = this.props.Document.GetNumber(KeyStore.CurPage, -1);
+ return this.props.Document.GetList(this.props.fieldKey, [] as Document[]).reduce((active, doc) => {
+ var page = doc.GetNumber(KeyStore.Page, -1);
+ if (page == curPage || page == -1) {
+ active.push(doc);
+ }
+ return active;
+ }, [] as Document[]);
+ }
+
+ @observable public DownX: number = 0;
+ @observable public DownY: number = 0;
+ @observable private _lastX: number = 0;
+ @observable private _lastY: number = 0;
+
+ @computed get panX(): number { return this.props.Document.GetNumber(KeyStore.PanX, 0) }
+ @computed get panY(): number { return this.props.Document.GetNumber(KeyStore.PanY, 0) }
+ @computed get scale(): number { return this.props.Document.GetNumber(KeyStore.Scale, 1); }
+ @computed get isAnnotationOverlay() { return this.props.fieldKey && this.props.fieldKey.Id === KeyStore.Annotations.Id; } // bcz: ? Why do we need to compare Id's?
+ @computed get nativeWidth() { return this.props.Document.GetNumber(KeyStore.NativeWidth, 0); }
+ @computed get nativeHeight() { return this.props.Document.GetNumber(KeyStore.NativeHeight, 0); }
+ @computed get zoomScaling() { return this.props.Document.GetNumber(KeyStore.Scale, 1); }
+ @computed get centeringShiftX() { return !this.props.Document.GetNumber(KeyStore.NativeWidth, 0) ? this.props.panelWidth() / 2 : 0; } // shift so pan position is at center of window for non-overlay collections
+ @computed get centeringShiftY() { return !this.props.Document.GetNumber(KeyStore.NativeHeight, 0) ? this.props.panelHeight() / 2 : 0; }// shift so pan position is at center of window for non-overlay collections
+
+ @undoBatch
+ @action
+ drop = (e: Event, de: DragManager.DropEvent) => {
+ if (super.drop(e, de)) {
+ if (de.data instanceof DragManager.DocumentDragData) {
+ let screenX = de.x - (de.data.xOffset as number || 0);
+ let screenY = de.y - (de.data.yOffset as number || 0);
+ const [x, y] = this.getTransform().transformPoint(screenX, screenY);
+ let dragDoc = de.data.draggedDocuments[0];
+ let dragX = dragDoc.GetNumber(KeyStore.X, 0);
+ let dragY = dragDoc.GetNumber(KeyStore.Y, 0);
+ de.data.draggedDocuments.map(d => {
+ let docX = d.GetNumber(KeyStore.X, 0);
+ let docY = d.GetNumber(KeyStore.Y, 0);
+ d.SetNumber(KeyStore.X, x + (docX - dragX));
+ d.SetNumber(KeyStore.Y, y + (docY - dragY));
+ if (!d.GetNumber(KeyStore.Width, 0)) {
+ d.SetNumber(KeyStore.Width, 300);
+ d.SetNumber(KeyStore.Height, 300);
+ }
+ this.bringToFront(d);
+ })
+ }
+ return true;
+ }
+ return false;
+ }
+
+
+ @action
+ cleanupInteractions = () => {
+ document.removeEventListener("pointermove", this.onPointerMove);
+ document.removeEventListener("pointerup", this.onPointerUp);
+ }
+
+ @action
+ onPointerDown = (e: React.PointerEvent): void => {
+ if (((e.button === 2 && (!this.isAnnotationOverlay || this.zoomScaling != 1)) || e.button == 0) && this.props.active()) {
+ document.removeEventListener("pointermove", this.onPointerMove);
+ document.addEventListener("pointermove", this.onPointerMove);
+ document.removeEventListener("pointerup", this.onPointerUp);
+ document.addEventListener("pointerup", this.onPointerUp);
+ this._lastX = this.DownX = e.pageX;
+ this._lastY = this.DownY = e.pageY;
+ if (this.props.isSelected())
+ e.stopPropagation();
+ }
+ }
+
+ @action
+ onPointerUp = (e: PointerEvent): void => {
+ e.stopPropagation();
+
+ this.cleanupInteractions();
+ }
+
+ @action
+ onPointerMove = (e: PointerEvent): void => {
+ if (!e.cancelBubble && this.props.active()) {
+ if ((!this.isAnnotationOverlay || this.zoomScaling != 1) && !e.shiftKey) {
+ let x = this.props.Document.GetNumber(KeyStore.PanX, 0);
+ let y = this.props.Document.GetNumber(KeyStore.PanY, 0);
+ let [dx, dy] = this.getTransform().transformDirection(e.clientX - this._lastX, e.clientY - this._lastY);
+ this.SetPan(x - dx, y - dy);
+ this._lastX = e.pageX;
+ this._lastY = e.pageY;
+ e.stopPropagation();
+ e.preventDefault();
+ }
+ }
+ }
+
+ @action
+ onPointerWheel = (e: React.WheelEvent): void => {
+ this.props.select(false);
+ e.stopPropagation();
+ let coefficient = 1000;
+
+ if (e.ctrlKey) {
+ var nativeWidth = this.props.Document.GetNumber(KeyStore.NativeWidth, 0);
+ var nativeHeight = this.props.Document.GetNumber(KeyStore.NativeHeight, 0);
+ const coefficient = 1000;
+ let deltaScale = (1 - (e.deltaY / coefficient));
+ this.props.Document.SetNumber(KeyStore.NativeWidth, nativeWidth * deltaScale);
+ this.props.Document.SetNumber(KeyStore.NativeHeight, nativeHeight * deltaScale);
+ e.stopPropagation();
+ e.preventDefault();
+ } else {
+ // if (modes[e.deltaMode] == 'pixels') coefficient = 50;
+ // else if (modes[e.deltaMode] == 'lines') coefficient = 1000; // This should correspond to line-height??
+ let transform = this.getTransform();
+
+ let deltaScale = (1 - (e.deltaY / coefficient));
+ if (deltaScale * this.zoomScaling < 1 && this.isAnnotationOverlay)
+ deltaScale = 1 / this.zoomScaling;
+ let [x, y] = transform.transformPoint(e.clientX, e.clientY);
+
+ let localTransform = this.getLocalTransform()
+ localTransform = localTransform.inverse().scaleAbout(deltaScale, x, y)
+ // console.log(localTransform)
+
+ this.props.Document.SetNumber(KeyStore.Scale, localTransform.Scale);
+ this.SetPan(-localTransform.TranslateX / localTransform.Scale, -localTransform.TranslateY / localTransform.Scale);
+ }
+ }
+
+ @action
+ private SetPan(panX: number, panY: number) {
+ var x1 = this.getLocalTransform().inverse().Scale;
+ const newPanX = Math.min((1 - 1 / x1) * this.nativeWidth, Math.max(0, panX));
+ const newPanY = Math.min((1 - 1 / x1) * this.nativeHeight, Math.max(0, panY));
+ this.props.Document.SetNumber(KeyStore.PanX, this.isAnnotationOverlay ? newPanX : panX);
+ this.props.Document.SetNumber(KeyStore.PanY, this.isAnnotationOverlay ? newPanY : panY);
+ }
+
+ @action
+ onDrop = (e: React.DragEvent): void => {
+ var pt = this.getTransform().transformPoint(e.pageX, e.pageY);
+ super.onDrop(e, { x: pt[0], y: pt[1] });
+ }
+
+ onDragOver = (): void => {
+ }
+
+ @action
+ bringToFront(doc: Document) {
+ const { fieldKey: fieldKey, Document: Document } = this.props;
+
+ const value: Document[] = Document.GetList<Document>(fieldKey, []).slice();
+ value.sort((doc1, doc2) => {
+ if (doc1 === doc) {
+ return 1;
+ }
+ if (doc2 === doc) {
+ return -1;
+ }
+ return doc1.GetNumber(KeyStore.ZIndex, 0) - doc2.GetNumber(KeyStore.ZIndex, 0);
+ }).map((doc, index) => {
+ doc.SetNumber(KeyStore.ZIndex, index + 1)
+ });
+ }
+
+ @computed get backgroundLayout(): string | undefined {
+ let field = this.props.Document.GetT(KeyStore.BackgroundLayout, TextField);
+ if (field && field !== FieldWaiting) {
+ return field.Data;
+ }
+ }
+ @computed get overlayLayout(): string | undefined {
+ let field = this.props.Document.GetT(KeyStore.OverlayLayout, TextField);
+ if (field && field !== FieldWaiting) {
+ return field.Data;
+ }
+ }
+
+ focusDocument = (doc: Document) => {
+ let x = doc.GetNumber(KeyStore.X, 0) + doc.GetNumber(KeyStore.Width, 0) / 2;
+ let y = doc.GetNumber(KeyStore.Y, 0) + doc.GetNumber(KeyStore.Height, 0) / 2;
+ this.SetPan(x, y);
+ this.props.focus(this.props.Document);
+ }
+
+ getDocumentViewProps(document: Document): DocumentViewProps {
+ return {
+ Document: document,
+ AddDocument: this.props.addDocument,
+ RemoveDocument: this.props.removeDocument,
+ ScreenToLocalTransform: this.getTransform,
+ isTopMost: false,
+ SelectOnLoad: document.Id == this._selectOnLoaded,
+ PanelWidth: document.Width,
+ PanelHeight: document.Height,
+ ContentScaling: this.noScaling,
+ ContainingCollectionView: this.props.CollectionView,
+ focus: this.focusDocument
+ }
+ }
+
+ @computed
+ get views() {
+ var curPage = this.props.Document.GetNumber(KeyStore.CurPage, -1);
+ return this.props.Document.GetList(this.props.fieldKey, [] as Document[]).filter(doc => doc).reduce((prev, doc) => {
+ var page = doc.GetNumber(KeyStore.Page, -1);
+ if (page == curPage || page == -1)
+ prev.push(<CollectionFreeFormDocumentView key={doc.Id} {...this.getDocumentViewProps(doc)} />);
+ return prev;
+ }, [] as JSX.Element[])
+ }
+
+ @computed
+ get backgroundView() {
+ return !this.backgroundLayout ? (null) :
+ (<DocumentContentsView {...this.getDocumentViewProps(this.props.Document)}
+ layoutKey={KeyStore.BackgroundLayout} isTopMost={this.props.isTopMost} isSelected={() => false} select={() => { }} />);
+ }
+ @computed
+ get overlayView() {
+ return !this.overlayLayout ? (null) :
+ (<DocumentContentsView {...this.getDocumentViewProps(this.props.Document)}
+ layoutKey={KeyStore.OverlayLayout} isTopMost={this.props.isTopMost} isSelected={() => false} select={() => { }} />);
+ }
+
+ getTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-COLLECTION_BORDER_WIDTH, -COLLECTION_BORDER_WIDTH).translate(-this.centeringShiftX, -this.centeringShiftY).transform(this.getLocalTransform())
+ getContainerTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-COLLECTION_BORDER_WIDTH, -COLLECTION_BORDER_WIDTH)
+ getLocalTransform = (): Transform => Transform.Identity.scale(1 / this.scale).translate(this.panX, this.panY);
+ noScaling = () => 1;
+ childViews = () => this.views;
+
+ render() {
+ let [dx, dy] = [this.centeringShiftX, this.centeringShiftY];
+
+ const panx: number = -this.props.Document.GetNumber(KeyStore.PanX, 0);
+ const pany: number = -this.props.Document.GetNumber(KeyStore.PanY, 0);
+
+ return (
+ <div className={`collectionfreeformview${this.isAnnotationOverlay ? "-overlay" : "-container"}`}
+ onPointerDown={this.onPointerDown} onPointerMove={(e) => super.setCursorPosition(this.getTransform().transformPoint(e.clientX, e.clientY))}
+ onDrop={this.onDrop.bind(this)} onDragOver={this.onDragOver} onWheel={this.onPointerWheel}
+ style={{ borderWidth: `${COLLECTION_BORDER_WIDTH}px` }} ref={this.createDropTarget}>
+ <MarqueeView container={this} activeDocuments={this.getActiveDocuments} selectDocuments={this.selectDocuments}
+ addDocument={this.addDocument} removeDocument={this.props.removeDocument}
+ getContainerTransform={this.getContainerTransform} getTransform={this.getTransform}>
+ <PreviewCursor container={this} addLiveTextDocument={this.addLiveTextBox}
+ getContainerTransform={this.getContainerTransform} getTransform={this.getTransform} >
+ <div className="collectionfreeformview" ref={this._canvasRef}
+ style={{ transform: `translate(${dx}px, ${dy}px) scale(${this.zoomScaling}, ${this.zoomScaling}) translate(${panx}px, ${pany}px)` }}>
+ {this.backgroundView}
+ <CollectionFreeFormLinksView {...this.props}>
+ <InkingCanvas getScreenTransform={this.getTransform} Document={this.props.Document} >
+ {this.childViews}
+ </InkingCanvas>
+ </CollectionFreeFormLinksView>
+ <CollectionFreeFormRemoteCursors {...this.props} />
+ </div>
+ {this.overlayView}
+ </PreviewCursor>
+ </MarqueeView>
+ </div>
+ );
+ }
+} \ No newline at end of file
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.scss b/src/client/views/collections/collectionFreeForm/MarqueeView.scss
new file mode 100644
index 000000000..1ee3b244b
--- /dev/null
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.scss
@@ -0,0 +1,14 @@
+
+.marqueeView {
+ position: absolute;
+ width:100%;
+ height:100%;
+}
+.marquee {
+ border-style: dashed;
+ box-sizing: border-box;
+ position: absolute;
+ border-width: 1px;
+ border-color: black;
+ pointer-events: none;
+} \ No newline at end of file
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
new file mode 100644
index 000000000..e2239c8be
--- /dev/null
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -0,0 +1,201 @@
+import { action, computed, observable, trace } from "mobx";
+import { observer } from "mobx-react";
+import { Document } from "../../../../fields/Document";
+import { FieldWaiting } from "../../../../fields/Field";
+import { InkField, StrokeData } from "../../../../fields/InkField";
+import { KeyStore } from "../../../../fields/KeyStore";
+import { Documents } from "../../../documents/Documents";
+import { SelectionManager } from "../../../util/SelectionManager";
+import { Transform } from "../../../util/Transform";
+import { InkingCanvas } from "../../InkingCanvas";
+import { CollectionFreeFormView } from "./CollectionFreeFormView";
+import "./MarqueeView.scss";
+import { PreviewCursor } from "./PreviewCursor";
+import React = require("react");
+
+interface MarqueeViewProps {
+ getContainerTransform: () => Transform;
+ getTransform: () => Transform;
+ container: CollectionFreeFormView;
+ addDocument: (doc: Document, allowDuplicates: false) => boolean;
+ activeDocuments: () => Document[];
+ selectDocuments: (docs: Document[]) => void;
+ removeDocument: (doc: Document) => boolean;
+}
+
+@observer
+export class MarqueeView extends React.Component<MarqueeViewProps>
+{
+ @observable _lastX: number = 0;
+ @observable _lastY: number = 0;
+ @observable _downX: number = 0;
+ @observable _downY: number = 0;
+ @observable _used: boolean = false;
+ @observable _visible: boolean = false;
+ static DRAG_THRESHOLD = 4;
+
+ @action
+ cleanupInteractions = (all: boolean = false) => {
+ if (all) {
+ document.removeEventListener("pointermove", this.onPointerMove, true)
+ document.removeEventListener("pointerup", this.onPointerUp, true);
+ } else {
+ this._used = true;
+ }
+ document.removeEventListener("keydown", this.marqueeCommand, true);
+ this._visible = false;
+ }
+
+ @action
+ onPointerDown = (e: React.PointerEvent): void => {
+ if (e.buttons == 1 && !e.altKey && !e.metaKey && this.props.container.props.active()) {
+ this._downX = this._lastX = e.pageX;
+ this._downY = this._lastY = e.pageY;
+ this._used = false;
+ document.addEventListener("pointermove", this.onPointerMove, true)
+ document.addEventListener("pointerup", this.onPointerUp, true);
+ document.addEventListener("keydown", this.marqueeCommand, true);
+ }
+ }
+
+ @action
+ onPointerMove = (e: PointerEvent): void => {
+ this._lastX = e.pageX;
+ this._lastY = e.pageY;
+ if (!e.cancelBubble) {
+ if (!this._used && e.buttons == 1 && !e.altKey && !e.metaKey &&
+ (Math.abs(this._lastX - this._downX) > MarqueeView.DRAG_THRESHOLD || Math.abs(this._lastY - this._downY) > MarqueeView.DRAG_THRESHOLD)) {
+ this._visible = true;
+ }
+ e.stopPropagation();
+ e.preventDefault();
+ }
+ }
+
+ @action
+ onPointerUp = (e: PointerEvent): void => {
+ this.cleanupInteractions(true);
+ this._visible = false;
+ let mselect = this.marqueeSelect();
+ if (!e.shiftKey) {
+ SelectionManager.DeselectAll(mselect.length ? undefined : this.props.container.props.Document);
+ }
+ this.props.selectDocuments(mselect.length ? mselect : [this.props.container.props.Document]);
+ }
+
+ intersectRect(r1: { left: number, top: number, width: number, height: number },
+ r2: { left: number, top: number, width: number, height: number }) {
+ return !(r2.left > r1.left + r1.width || r2.left + r2.width < r1.left || r2.top > r1.top + r1.height || r2.top + r2.height < r1.top);
+ }
+
+ @computed
+ get Bounds() {
+ let left = this._downX < this._lastX ? this._downX : this._lastX;
+ let top = this._downY < this._lastY ? this._downY : this._lastY;
+ let topLeft = this.props.getTransform().transformPoint(left, top);
+ let size = this.props.getTransform().transformDirection(this._lastX - this._downX, this._lastY - this._downY);
+ return { left: topLeft[0], top: topLeft[1], width: Math.abs(size[0]), height: Math.abs(size[1]) }
+ }
+
+ @action
+ marqueeCommand = (e: KeyboardEvent) => {
+ if (e.key == "Backspace" || e.key == "Delete") {
+ this.marqueeSelect().map(d => this.props.removeDocument(d));
+ let ink = this.props.container.props.Document.GetT(KeyStore.Ink, InkField);
+ if (ink && ink != FieldWaiting) {
+ this.marqueeInkDelete(ink.Data);
+ }
+ this.cleanupInteractions();
+ }
+ if (e.key == "c") {
+ let bounds = this.Bounds;
+ let selected = this.marqueeSelect().map(d => {
+ this.props.removeDocument(d);
+ d.SetNumber(KeyStore.X, d.GetNumber(KeyStore.X, 0) - bounds.left - bounds.width / 2);
+ d.SetNumber(KeyStore.Y, d.GetNumber(KeyStore.Y, 0) - bounds.top - bounds.height / 2);
+ d.SetNumber(KeyStore.Page, -1);
+ d.SetText(KeyStore.Title, "" + d.GetNumber(KeyStore.Width, 0) + " " + d.GetNumber(KeyStore.Height, 0));
+ return d;
+ });
+ let ink = this.props.container.props.Document.GetT(KeyStore.Ink, InkField);
+ let inkData = ink && ink != FieldWaiting ? ink.Data : undefined;
+ //setTimeout(() => {
+ let newCollection = Documents.FreeformDocument(selected, {
+ x: bounds.left,
+ y: bounds.top,
+ panx: 0,
+ pany: 0,
+ width: bounds.width,
+ height: bounds.height,
+ backgroundColor: "Transparent",
+ ink: inkData ? this.marqueeInkSelect(inkData) : undefined,
+ title: "a nested collection"
+ });
+ this.props.addDocument(newCollection, false);
+ this.marqueeInkDelete(inkData);
+ // }, 100);
+ this.cleanupInteractions();
+ }
+ }
+ @action
+ marqueeInkSelect(ink: Map<any, any>) {
+ let idata = new Map();
+ let centerShiftX = 0 - (this.Bounds.left + this.Bounds.width / 2); // moves each point by the offset that shifts the selection's center to the origin.
+ let centerShiftY = 0 - (this.Bounds.top + this.Bounds.height / 2);
+ ink.forEach((value: StrokeData, key: string, map: any) => {
+ if (InkingCanvas.IntersectStrokeRect(value, this.Bounds)) {
+ idata.set(key,
+ {
+ pathData: value.pathData.map(val => { return { x: val.x + centerShiftX, y: val.y + centerShiftY } }),
+ color: value.color,
+ width: value.width,
+ tool: value.tool,
+ page: -1
+ });
+ }
+ });
+ return idata;
+ }
+
+ @action
+ marqueeInkDelete(ink?: Map<any, any>) {
+ // bcz: this appears to work but when you restart all the deleted strokes come back -- InkField isn't observing its changes so they aren't written to the DB.
+ // ink.forEach((value: StrokeData, key: string, map: any) =>
+ // InkingCanvas.IntersectStrokeRect(value, this.Bounds) && ink.delete(key));
+
+ if (ink) {
+ let idata = new Map();
+ ink.forEach((value: StrokeData, key: string, map: any) =>
+ !InkingCanvas.IntersectStrokeRect(value, this.Bounds) && idata.set(key, value));
+ this.props.container.props.Document.SetDataOnPrototype(KeyStore.Ink, idata, InkField);
+ }
+ }
+
+ marqueeSelect() {
+ let selRect = this.Bounds;
+ let selection: Document[] = [];
+ this.props.activeDocuments().map(doc => {
+ var x = doc.GetNumber(KeyStore.X, 0);
+ var y = doc.GetNumber(KeyStore.Y, 0);
+ var w = doc.GetNumber(KeyStore.Width, 0);
+ var h = doc.GetNumber(KeyStore.Height, 0);
+ if (this.intersectRect({ left: x, top: y, width: w, height: h }, selRect))
+ selection.push(doc)
+ })
+ return selection;
+ }
+
+ @computed
+ get marqueeDiv() {
+ let p = this.props.getContainerTransform().transformPoint(this._downX < this._lastX ? this._downX : this._lastX, this._downY < this._lastY ? this._downY : this._lastY);
+ let v = this.props.getContainerTransform().transformDirection(this._lastX - this._downX, this._lastY - this._downY);
+ return <div className="marquee" style={{ transform: `translate(${p[0]}px, ${p[1]}px)`, width: `${Math.abs(v[0])}`, height: `${Math.abs(v[1])}` }} />
+ }
+
+ render() {
+ return <div className="marqueeView" onPointerDown={this.onPointerDown}>
+ {this.props.children}
+ {!this._visible ? (null) : this.marqueeDiv}
+ </div>;
+ }
+} \ No newline at end of file
diff --git a/src/client/views/collections/collectionFreeForm/PreviewCursor.scss b/src/client/views/collections/collectionFreeForm/PreviewCursor.scss
new file mode 100644
index 000000000..21210be2b
--- /dev/null
+++ b/src/client/views/collections/collectionFreeForm/PreviewCursor.scss
@@ -0,0 +1,23 @@
+
+.previewCursor {
+ color: black;
+ position: absolute;
+ transform-origin: left top;
+ pointer-events: none;
+}
+.previewCursorView {
+ position: absolute;
+ width:100%;
+ height:100%;
+}
+
+//this is an animation for the blinking cursor!
+// @keyframes blink {
+// 0% {opacity: 0}
+// 49%{opacity: 0}
+// 50% {opacity: 1}
+// }
+
+// #previewCursor {
+// animation: blink 1s infinite;
+// } \ No newline at end of file
diff --git a/src/client/views/collections/collectionFreeForm/PreviewCursor.tsx b/src/client/views/collections/collectionFreeForm/PreviewCursor.tsx
new file mode 100644
index 000000000..93c98f7b0
--- /dev/null
+++ b/src/client/views/collections/collectionFreeForm/PreviewCursor.tsx
@@ -0,0 +1,119 @@
+import { action, observable, trace, computed, reaction } from "mobx";
+import { observer } from "mobx-react";
+import { Document } from "../../../../fields/Document";
+import { Documents } from "../../../documents/Documents";
+import { Transform } from "../../../util/Transform";
+import { CollectionFreeFormView } from "./CollectionFreeFormView";
+import "./PreviewCursor.scss";
+import React = require("react");
+import { interfaceDeclaration } from "babel-types";
+
+
+export interface PreviewCursorProps {
+ getTransform: () => Transform;
+ getContainerTransform: () => Transform;
+ container: CollectionFreeFormView;
+ addLiveTextDocument: (doc: Document) => void;
+}
+
+@observer
+export class PreviewCursor extends React.Component<PreviewCursorProps> {
+ @observable _lastX: number = 0;
+ @observable _lastY: number = 0;
+ @observable public _visible: boolean = false;
+ @observable public DownX: number = 0;
+ @observable public DownY: number = 0;
+ _showOnUp: boolean = false;
+
+ @action
+ cleanupInteractions = () => {
+ document.removeEventListener("pointerup", this.onPointerUp, true);
+ document.removeEventListener("pointermove", this.onPointerMove, true);
+ }
+
+ @action
+ onPointerDown = (e: React.PointerEvent) => {
+ if (e.button == 0 && this.props.container.props.active()) {
+ document.removeEventListener("keypress", this.onKeyPress, false);
+ this._showOnUp = true;
+ this.DownX = e.pageX;
+ this.DownY = e.pageY;
+ document.addEventListener("pointerup", this.onPointerUp, true);
+ document.addEventListener("pointermove", this.onPointerMove, true);
+ }
+ }
+ @action
+ onPointerMove = (e: PointerEvent): void => {
+ if (Math.abs(this.DownX - e.clientX) > 4 || Math.abs(this.DownY - e.clientY) > 4) {
+ this._showOnUp = false;
+ this._visible = false;
+ }
+ }
+
+ @action
+ onPointerUp = (e: PointerEvent): void => {
+ if (this._showOnUp) {
+ document.addEventListener("keypress", this.onKeyPress, false);
+ this._lastX = this.DownX;
+ this._lastY = this.DownY;
+ this._visible = true;
+ }
+ this.cleanupInteractions();
+ }
+
+ @action
+ onKeyPress = (e: KeyboardEvent) => {
+ // Mixing events between React and Native is finicky. In FormattedTextBox, we set the
+ // DASHFormattedTextBoxHandled flag when a text box consumes a key press so that we can ignore
+ // the keyPress here.
+ //if not these keys, make a textbox if preview cursor is active!
+ if (!e.ctrlKey && !e.altKey && !e.defaultPrevented && !(e as any).DASHFormattedTextBoxHandled) {
+ //make textbox and add it to this collection
+ let [x, y] = this.props.getTransform().transformPoint(this._lastX, this._lastY);
+ let newBox = Documents.TextDocument({ width: 200, height: 100, x: x, y: y, title: "typed text" });
+ this.props.addLiveTextDocument(newBox);
+ document.removeEventListener("keypress", this.onKeyPress, false);
+ this._visible = false;
+ e.stopPropagation();
+ }
+ }
+
+ getPoint = () => this.props.getContainerTransform().transformPoint(this._lastX, this._lastY);
+ getVisible = () => this._visible;
+ setVisible = (v: boolean) => {
+ this._visible = v;
+ document.removeEventListener("keypress", this.onKeyPress, false);
+ }
+ render() {
+ return (
+ <div className="previewCursorView" onPointerDown={this.onPointerDown}>
+ {this.props.children}
+ <PreviewCursorPrompt setVisible={this.setVisible} getPoint={this.getPoint} getVisible={this.getVisible} />
+ </div>
+ )
+ }
+}
+
+export interface PromptProps {
+ getPoint: () => number[];
+ getVisible: () => boolean;
+ setVisible: (v: boolean) => void;
+}
+
+@observer
+export class PreviewCursorPrompt extends React.Component<PromptProps> {
+ private _promptRef = React.createRef<HTMLDivElement>();
+
+ //when focus is lost, this will remove the preview cursor
+ @action onBlur = (): void => this.props.setVisible(false);
+
+ render() {
+ let p = this.props.getPoint();
+ if (this.props.getVisible() && this._promptRef.current)
+ this._promptRef.current.focus();
+ return <div className="previewCursor" id="previewCursor" onBlur={this.onBlur} tabIndex={0} ref={this._promptRef}
+ style={{ transform: `translate(${p[0]}px, ${p[1]}px)`, opacity: this.props.getVisible() ? 1 : 0 }}>
+ I
+ </div >;
+ }
+} \ No newline at end of file
diff --git a/src/client/views/nodes/AudioBox.scss b/src/client/views/nodes/AudioBox.scss
new file mode 100644
index 000000000..704cdc31c
--- /dev/null
+++ b/src/client/views/nodes/AudioBox.scss
@@ -0,0 +1,4 @@
+.audiobox-cont{
+ height: 100%;
+ width: 100%;
+} \ No newline at end of file
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
new file mode 100644
index 000000000..6daf15f5f
--- /dev/null
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -0,0 +1,44 @@
+import React = require("react")
+import { FieldViewProps, FieldView } from './FieldView';
+import { FieldWaiting } from '../../../fields/Field';
+import { observer } from "mobx-react"
+import { ContextMenu } from "../../views/ContextMenu";
+import { observable, action } from 'mobx';
+import { KeyStore } from '../../../fields/KeyStore';
+import { AudioField } from "../../../fields/AudioField";
+import "./AudioBox.scss"
+import { NumberField } from "../../../fields/NumberField";
+
+@observer
+export class AudioBox extends React.Component<FieldViewProps> {
+
+ public static LayoutString() { return FieldView.LayoutString(AudioBox) }
+
+ constructor(props: FieldViewProps) {
+ super(props);
+ }
+
+
+
+ componentDidMount() {
+ }
+
+ componentWillUnmount() {
+ }
+
+
+ render() {
+ let field = this.props.doc.Get(this.props.fieldKey)
+ let path = field == FieldWaiting ? "http://techslides.com/demos/samples/sample.mp3" :
+ field instanceof AudioField ? field.Data.href : "http://techslides.com/demos/samples/sample.mp3";
+
+ return (
+ <div>
+ <audio controls className="audiobox-cont">
+ <source src={path} type="audio/mpeg" />
+ Not supported.
+ </audio>
+ </div>
+ )
+ }
+} \ No newline at end of file
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 50dc5a619..d52b662bd 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -1,4 +1,4 @@
-import { computed, trace } from "mobx";
+import { computed } from "mobx";
import { observer } from "mobx-react";
import { KeyStore } from "../../../fields/KeyStore";
import { NumberField } from "../../../fields/NumberField";
@@ -6,6 +6,7 @@ import { Transform } from "../../util/Transform";
import { DocumentView, DocumentViewProps } from "./DocumentView";
import "./DocumentView.scss";
import React = require("react");
+import { thisExpression } from "babel-types";
@observer
@@ -73,6 +74,7 @@ export class CollectionFreeFormDocumentView extends React.Component<DocumentView
<div className="collectionFreeFormDocumentView-container" ref={this._mainCont} style={{
transformOrigin: "left top",
transform: this.transform,
+ pointerEvents: "all",
width: this.width,
height: this.height,
position: "absolute",
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
new file mode 100644
index 000000000..77551649c
--- /dev/null
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -0,0 +1,63 @@
+import { computed } from "mobx";
+import { observer } from "mobx-react";
+import { FieldWaiting } from "../../../fields/Field";
+import { Key } from "../../../fields/Key";
+import { KeyStore } from "../../../fields/KeyStore";
+import { ListField } from "../../../fields/ListField";
+import { CollectionDockingView } from "../collections/CollectionDockingView";
+import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView";
+import { CollectionPDFView } from "../collections/CollectionPDFView";
+import { CollectionSchemaView } from "../collections/CollectionSchemaView";
+import { CollectionVideoView } from "../collections/CollectionVideoView";
+import { CollectionView } from "../collections/CollectionView";
+import { AudioBox } from "./AudioBox";
+import { DocumentViewProps, JsxBindings } from "./DocumentView";
+import "./DocumentView.scss";
+import { FormattedTextBox } from "./FormattedTextBox";
+import { ImageBox } from "./ImageBox";
+import { KeyValueBox } from "./KeyValueBox";
+import { PDFBox } from "./PDFBox";
+import { VideoBox } from "./VideoBox";
+import { WebBox } from "./WebBox";
+import { HistogramBox } from "../../northstar/dash-nodes/HistogramBox";
+import React = require("react");
+const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this?
+
+
+@observer
+export class DocumentContentsView extends React.Component<DocumentViewProps & {
+ isSelected: () => boolean,
+ select: (ctrl: boolean) => void,
+ layoutKey: Key
+}> {
+ @computed get layout(): string { return this.props.Document.GetText(this.props.layoutKey, "<p>Error loading layout data</p>"); }
+ @computed get layoutKeys(): Key[] { return this.props.Document.GetData(KeyStore.LayoutKeys, ListField, new Array<Key>()); }
+ @computed get layoutFields(): Key[] { return this.props.Document.GetData(KeyStore.LayoutFields, ListField, new Array<Key>()); }
+
+
+ CreateBindings(): JsxBindings {
+ let bindings: JsxBindings = { ...this.props, };
+ for (const key of this.layoutKeys) {
+ bindings[key.Name + "Key"] = key; // this maps string values of the form <keyname>Key to an actual key Kestore.keyname e.g, "DataKey" => KeyStore.Data
+ }
+ for (const key of this.layoutFields) {
+ let field = this.props.Document.Get(key);
+ bindings[key.Name] = field && field != FieldWaiting ? field.GetValue() : field;
+ }
+ return bindings;
+ }
+
+ render() {
+ let lkeys = this.props.Document.GetT(KeyStore.LayoutKeys, ListField);
+ if (!lkeys || lkeys === FieldWaiting) {
+ return <p>Error loading layout keys</p>;
+ }
+ return <JsxParser
+ components={{ FormattedTextBox, ImageBox, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, CollectionPDFView, CollectionVideoView, WebBox, KeyValueBox, PDFBox, VideoBox, AudioBox, HistogramBox }}
+ bindings={this.CreateBindings()}
+ jsx={this.layout}
+ showWarnings={true}
+ onError={(test: any) => { console.log(test) }}
+ />
+ }
+} \ No newline at end of file
diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss
index ab913897b..85a115f1c 100644
--- a/src/client/views/nodes/DocumentView.scss
+++ b/src/client/views/nodes/DocumentView.scss
@@ -1,23 +1,23 @@
+@import "../global_variables";
.documentView-node {
- position: absolute;
- background: #cdcdcd;
- //overflow: hidden;
- &.minimized {
- width: 30px;
- height: 30px;
- }
- .top {
- background: #232323;
- height: 20px;
- cursor: pointer;
- }
- .content {
- padding: 20px 20px;
- height: auto;
- box-sizing: border-box;
- }
- .scroll-box {
- overflow-y: scroll;
- height: calc(100% - 20px);
- }
-} \ No newline at end of file
+ position: absolute;
+ background: $light-color; //overflow: hidden;
+ &.minimized {
+ width: 30px;
+ height: 30px;
+ }
+ .top {
+ background: #232323;
+ height: 20px;
+ cursor: pointer;
+ }
+ .content {
+ padding: 20px 20px;
+ height: auto;
+ box-sizing: border-box;
+ }
+ .scroll-box {
+ overflow-y: scroll;
+ height: calc(100% - 20px);
+ }
+}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 41e93df35..1195128dc 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1,34 +1,30 @@
-import { action, computed } from "mobx";
+import { action, computed, IReactionDisposer, reaction, runInAction } from "mobx";
import { observer } from "mobx-react";
import { Document } from "../../../fields/Document";
-import { Field, FieldWaiting, Opt } from "../../../fields/Field";
+import { Field, Opt, FieldWaiting } from "../../../fields/Field";
import { Key } from "../../../fields/Key";
import { KeyStore } from "../../../fields/KeyStore";
import { ListField } from "../../../fields/ListField";
+import { TextField } from "../../../fields/TextField";
+import { Utils } from "../../../Utils";
+import { Documents } from "../../documents/Documents";
+import { DocumentManager } from "../../util/DocumentManager";
import { DragManager } from "../../util/DragManager";
import { SelectionManager } from "../../util/SelectionManager";
import { Transform } from "../../util/Transform";
import { CollectionDockingView } from "../collections/CollectionDockingView";
-import { CollectionFreeFormView } from "../collections/CollectionFreeFormView";
-import { CollectionSchemaView } from "../collections/CollectionSchemaView";
import { CollectionView, CollectionViewType } from "../collections/CollectionView";
-import { CollectionPDFView } from "../collections/CollectionPDFView";
import { ContextMenu } from "../ContextMenu";
-import { FormattedTextBox } from "../nodes/FormattedTextBox";
-import { ImageBox } from "../nodes/ImageBox";
-import { Documents } from "../../documents/Documents"
-import { KeyValueBox } from "./KeyValueBox"
-import { WebBox } from "../nodes/WebBox";
-import { PDFBox } from "../nodes/PDFBox";
+import { DocumentContentsView } from "./DocumentContentsView";
import "./DocumentView.scss";
import React = require("react");
-const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this?
+import { ServerUtils } from "../../../server/ServerUtil";
export interface DocumentViewProps {
ContainingCollectionView: Opt<CollectionView>;
Document: Document;
- AddDocument?: (doc: Document) => void;
+ AddDocument?: (doc: Document, allowDuplicates: boolean) => boolean;
RemoveDocument?: (doc: Document) => boolean;
ScreenToLocalTransform: () => Transform;
isTopMost: boolean;
@@ -80,12 +76,23 @@ export function FakeJsxArgs(keys: string[], fields: string[] = []): JsxArgs {
return args;
}
+export interface JsxBindings {
+ Document: Document;
+ isSelected: () => boolean;
+ select: (isCtrlPressed: boolean) => void;
+ isTopMost: boolean;
+ SelectOnLoad: boolean;
+ [prop: string]: any;
+}
+
+
+
@observer
export class DocumentView extends React.Component<DocumentViewProps> {
private _mainCont = React.createRef<HTMLDivElement>();
- private _documentBindings: any = null;
private _downX: number = 0;
private _downY: number = 0;
+ private _reactionDisposer: Opt<IReactionDisposer>;
@computed get active(): boolean { return SelectionManager.IsSelected(this) || !this.props.ContainingCollectionView || this.props.ContainingCollectionView.active(); }
@computed get topMost(): boolean { return !this.props.ContainingCollectionView || this.props.ContainingCollectionView.collectionViewType == CollectionViewType.Docking; }
@computed get layout(): string { return this.props.Document.GetText(KeyStore.Layout, "<p>Error loading layout data</p>"); }
@@ -95,15 +102,15 @@ export class DocumentView extends React.Component<DocumentViewProps> {
onPointerDown = (e: React.PointerEvent): void => {
this._downX = e.clientX;
this._downY = e.clientY;
- if (e.shiftKey && e.buttons === 1) {
- CollectionDockingView.Instance.StartOtherDrag(this.props.Document, e);
+ if (e.shiftKey && e.buttons === 2) {
+ if (this.props.isTopMost) {
+ this.startDragging(e.pageX, e.pageY, e.altKey || e.ctrlKey);
+ }
+ else CollectionDockingView.Instance.StartOtherDrag([this.props.Document], e);
e.stopPropagation();
} else {
if (this.active && !e.isDefaultPrevented()) {
e.stopPropagation();
- if (e.buttons === 2) {
- e.preventDefault();
- }
document.removeEventListener("pointermove", this.onPointerMove)
document.addEventListener("pointermove", this.onPointerMove);
document.removeEventListener("pointerup", this.onPointerUp)
@@ -111,25 +118,71 @@ export class DocumentView extends React.Component<DocumentViewProps> {
}
}
}
+
+ private dropDisposer?: DragManager.DragDropDisposer;
+
+ componentDidMount() {
+ if (this._mainCont.current) {
+ this.dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, { handlers: { drop: this.drop.bind(this) } });
+ }
+ runInAction(() => DocumentManager.Instance.DocumentViews.push(this))
+ this._reactionDisposer = reaction(
+ () => this.props.ContainingCollectionView && this.props.ContainingCollectionView.SelectedDocs.slice(),
+ () => {
+ if (this.props.ContainingCollectionView && this.props.ContainingCollectionView.SelectedDocs.indexOf(this.props.Document.Id) != -1)
+ SelectionManager.SelectDoc(this, true);
+ });
+ }
+
+ componentDidUpdate() {
+ if (this.dropDisposer) {
+ this.dropDisposer();
+ }
+ if (this._mainCont.current) {
+ this.dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, { handlers: { drop: this.drop.bind(this) } });
+ }
+ }
+
+ componentWillUnmount() {
+ if (this.dropDisposer) {
+ this.dropDisposer();
+ }
+ runInAction(() => DocumentManager.Instance.DocumentViews.splice(DocumentManager.Instance.DocumentViews.indexOf(this), 1))
+ if (this._reactionDisposer) {
+ this._reactionDisposer();
+ }
+ }
+
+ startDragging(x: number, y: number, dropAliasOfDraggedDoc: boolean) {
+ if (this._mainCont.current) {
+ const [left, top] = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0);
+ let dragData = new DragManager.DocumentDragData([this.props.Document]);
+ dragData.aliasOnDrop = dropAliasOfDraggedDoc;
+ dragData.xOffset = x - left;
+ dragData.yOffset = y - top;
+ dragData.removeDocument = (dropCollectionView: CollectionView) => {
+ if (this.props.RemoveDocument && this.props.ContainingCollectionView !== dropCollectionView) {
+ this.props.RemoveDocument(this.props.Document);
+ }
+ }
+ DragManager.StartDocumentDrag([this._mainCont.current], dragData, {
+ handlers: {
+ dragComplete: action(() => { }),
+ },
+ hideSource: !dropAliasOfDraggedDoc
+ })
+ }
+ }
+
onPointerMove = (e: PointerEvent): void => {
if (e.cancelBubble) {
return;
}
if (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3) {
document.removeEventListener("pointermove", this.onPointerMove)
- document.removeEventListener("pointerup", this.onPointerUp)
- if (this._mainCont.current != null && !this.topMost) {
- const [left, top] = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0);
- let dragData: { [id: string]: any } = {};
- dragData["documentView"] = this;
- dragData["xOffset"] = e.x - left;
- dragData["yOffset"] = e.y - top;
- DragManager.StartDrag(this._mainCont.current, dragData, {
- handlers: {
- dragComplete: action(() => { }),
- },
- hideSource: true
- })
+ document.removeEventListener("pointerup", this.onPointerUp);
+ if (!this.topMost || e.buttons == 2 || e.altKey) {
+ this.startDragging(e.x, e.y, e.ctrlKey || e.altKey);
}
}
e.stopPropagation();
@@ -143,6 +196,9 @@ export class DocumentView extends React.Component<DocumentViewProps> {
SelectionManager.SelectDoc(this, e.ctrlKey);
}
}
+ stopPropogation = (e: React.SyntheticEvent) => {
+ e.stopPropagation();
+ }
deleteClicked = (): void => {
if (this.props.RemoveDocument) {
@@ -152,7 +208,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
fieldsClicked = (e: React.MouseEvent): void => {
if (this.props.AddDocument) {
- this.props.AddDocument(Documents.KVPDocument(this.props.Document));
+ this.props.AddDocument(Documents.KVPDocument(this.props.Document, { width: 300, height: 300 }), false);
}
}
fullScreenClicked = (e: React.MouseEvent): void => {
@@ -170,6 +226,48 @@ export class DocumentView extends React.Component<DocumentViewProps> {
}
@action
+ drop = (e: Event, de: DragManager.DropEvent) => {
+ if (de.data instanceof DragManager.LinkDragData) {
+ let sourceDoc: Document = de.data.linkSourceDocumentView.props.Document;
+ let destDoc: Document = this.props.Document;
+ if (this.props.isTopMost) {
+ return;
+ }
+ let linkDoc: Document = new Document();
+
+ destDoc.GetTAsync(KeyStore.Prototype, Document).then((protoDest) =>
+ sourceDoc.GetTAsync(KeyStore.Prototype, Document).then((protoSrc) => runInAction(() => {
+ linkDoc.Set(KeyStore.Title, new TextField("New Link"));
+ linkDoc.Set(KeyStore.LinkDescription, new TextField(""));
+ linkDoc.Set(KeyStore.LinkTags, new TextField("Default"));
+
+ let dstTarg = (protoDest ? protoDest : destDoc);
+ let srcTarg = (protoSrc ? protoSrc : sourceDoc);
+ linkDoc.Set(KeyStore.LinkedToDocs, dstTarg);
+ linkDoc.Set(KeyStore.LinkedFromDocs, srcTarg);
+ dstTarg.GetOrCreateAsync(KeyStore.LinkedFromDocs, ListField, field => { (field as ListField<Document>).Data.push(linkDoc) })
+ srcTarg.GetOrCreateAsync(KeyStore.LinkedToDocs, ListField, field => { (field as ListField<Document>).Data.push(linkDoc) })
+ }))
+ )
+ e.stopPropagation();
+ }
+ }
+
+ onDrop = (e: React.DragEvent) => {
+ if (e.isDefaultPrevented()) {
+ return;
+ }
+ let text = e.dataTransfer.getData("text/plain");
+ if (text && text.startsWith("<div")) {
+ let oldLayout = this.props.Document.GetText(KeyStore.Layout, "");
+ let layout = text.replace("{layout}", oldLayout);
+ this.props.Document.SetText(KeyStore.Layout, layout);
+ e.stopPropagation();
+ e.preventDefault();
+ }
+ }
+
+ @action
onContextMenu = (e: React.MouseEvent): void => {
e.stopPropagation();
let moved = Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3;
@@ -183,6 +281,18 @@ export class DocumentView extends React.Component<DocumentViewProps> {
ContextMenu.Instance.addItem({ description: "Fields", event: this.fieldsClicked })
ContextMenu.Instance.addItem({ description: "Center", event: () => this.props.focus(this.props.Document) })
ContextMenu.Instance.addItem({ description: "Open Right", event: () => CollectionDockingView.Instance.AddRightSplit(this.props.Document) })
+ ContextMenu.Instance.addItem({
+ description: "Copy URL",
+ event: () => {
+ Utils.CopyText(ServerUtils.prepend("/doc/" + this.props.Document.Id));
+ }
+ });
+ ContextMenu.Instance.addItem({
+ description: "Copy ID",
+ event: () => {
+ Utils.CopyText(this.props.Document.Id);
+ }
+ });
//ContextMenu.Instance.addItem({ description: "Docking", event: () => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Docking) })
ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15)
if (!this.topMost) {
@@ -194,15 +304,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15)
SelectionManager.SelectDoc(this, e.ctrlKey);
}
- @computed get mainContent() {
- return <JsxParser
- components={{ FormattedTextBox, ImageBox, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, CollectionPDFView, WebBox, KeyValueBox, PDFBox }}
- bindings={this._documentBindings}
- jsx={this.layout}
- showWarnings={true}
- onError={(test: any) => { console.log(test) }}
- />
- }
+
isSelected = () => {
return SelectionManager.IsSelected(this);
@@ -213,40 +315,27 @@ export class DocumentView extends React.Component<DocumentViewProps> {
}
render() {
- if (!this.props.Document) return <div></div>
- let lkeys = this.props.Document.GetT(KeyStore.LayoutKeys, ListField);
- if (!lkeys || lkeys === "<Waiting>") {
- return <p>Error loading layout keys</p>;
- }
- this._documentBindings = {
- ...this.props,
- isSelected: this.isSelected,
- select: this.select,
- focus: this.props.focus
- };
- for (const key of this.layoutKeys) {
- this._documentBindings[key.Name + "Key"] = key; // this maps string values of the form <keyname>Key to an actual key Kestore.keyname e.g, "DataKey" => KeyStore.Data
- }
- for (const key of this.layoutFields) {
- let field = this.props.Document.Get(key);
- this._documentBindings[key.Name] = field && field != FieldWaiting ? field.GetValue() : field;
- }
- this._documentBindings.bindings = this._documentBindings;
+ if (!this.props.Document) {
+ return (null);
+ }
var scaling = this.props.ContentScaling();
var nativeWidth = this.props.Document.GetNumber(KeyStore.NativeWidth, 0);
var nativeHeight = this.props.Document.GetNumber(KeyStore.NativeHeight, 0);
+ var backgroundcolor = this.props.Document.GetText(KeyStore.BackgroundColor, "");
return (
<div className="documentView-node" ref={this._mainCont}
style={{
+ background: backgroundcolor,
width: nativeWidth > 0 ? nativeWidth.toString() + "px" : "100%",
height: nativeHeight > 0 ? nativeHeight.toString() + "px" : "100%",
transformOrigin: "left top",
transform: `scale(${scaling} , ${scaling})`
}}
+ onDrop={this.onDrop}
onContextMenu={this.onContextMenu}
onPointerDown={this.onPointerDown} >
- {this.mainContent}
- </div>
+ <DocumentContentsView {...this.props} isSelected={this.isSelected} select={this.select} layoutKey={KeyStore.Layout} />
+ </div >
)
}
} \ No newline at end of file
diff --git a/src/client/views/nodes/FieldTextBox.scss b/src/client/views/nodes/FieldTextBox.scss
index b6ce2fabc..d2cd61b0d 100644
--- a/src/client/views/nodes/FieldTextBox.scss
+++ b/src/client/views/nodes/FieldTextBox.scss
@@ -1,14 +1,14 @@
.ProseMirror {
- margin-top: -1em;
- width: 100%;
- height: 100%;
+ margin-top: -1em;
+ width: 100%;
+ height: 100%;
}
.ProseMirror:focus {
- outline: none !important
+ outline: none !important;
}
.fieldTextBox-cont {
- background: white;
- padding: 1vw;
-} \ No newline at end of file
+ background: white;
+ padding: 1vw;
+}
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 9e63006d1..4e83ec7b9 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -7,11 +7,19 @@ import { TextField } from "../../../fields/TextField";
import { NumberField } from "../../../fields/NumberField";
import { RichTextField } from "../../../fields/RichTextField";
import { ImageField } from "../../../fields/ImageField";
-import { WebField } from "../../../fields/WebField";
+import { VideoField } from "../../../fields/VideoField"
import { Key } from "../../../fields/Key";
import { FormattedTextBox } from "./FormattedTextBox";
import { ImageBox } from "./ImageBox";
import { WebBox } from "./WebBox";
+import { VideoBox } from "./VideoBox";
+import { AudioBox } from "./AudioBox";
+import { AudioField } from "../../../fields/AudioField";
+import { ListField } from "../../../fields/ListField";
+import { DocumentContentsView } from "./DocumentContentsView";
+import { Transform } from "../../util/Transform";
+import { KeyStore } from "../../../fields/KeyStore";
+
//
// these properties get assigned through the render() method of the DocumentView when it creates this node.
@@ -53,8 +61,34 @@ export class FieldView extends React.Component<FieldViewProps> {
else if (field instanceof ImageField) {
return <ImageBox {...this.props} />
}
- else if (field instanceof WebField) {
- return <WebBox {...this.props} />
+ else if (field instanceof VideoField) {
+ return <VideoBox {...this.props} />
+ }
+ else if (field instanceof AudioField) {
+ return <AudioBox {...this.props} />
+ }
+ else if (field instanceof Document) {
+ return (<DocumentContentsView Document={field}
+ AddDocument={undefined}
+ RemoveDocument={undefined}
+ ScreenToLocalTransform={() => Transform.Identity}
+ ContentScaling={() => 1}
+ PanelWidth={() => 100}
+ PanelHeight={() => 100}
+ isTopMost={true}
+ SelectOnLoad={false}
+ focus={() => { }}
+ isSelected={() => false}
+ select={() => false}
+ layoutKey={KeyStore.Layout}
+ ContainingCollectionView={undefined} />)
+ }
+ else if (field instanceof ListField) {
+ return (<div>
+ {(field as ListField<Field>).Data.map(f => {
+ return f instanceof Document ? f.Title : f.GetValue().toString();
+ }).join(", ")}
+ </div>)
}
// bcz: this belongs here, but it doesn't render well so taking it out for now
// else if (field instanceof HtmlField) {
diff --git a/src/client/views/nodes/FormattedTextBox.scss b/src/client/views/nodes/FormattedTextBox.scss
index ab5849f09..32da2632e 100644
--- a/src/client/views/nodes/FormattedTextBox.scss
+++ b/src/client/views/nodes/FormattedTextBox.scss
@@ -1,38 +1,46 @@
+@import "../global_variables";
.ProseMirror {
- width: 100%;
- height: auto;
- min-height: 100%
+ width: 100%;
+ height: auto;
+ min-height: 100%;
+ font-family: $serif;
}
.ProseMirror:focus {
- outline: none !important
+ outline: none !important;
}
.formattedTextBox-cont {
- background: white;
- padding: 1;
- border-width: 1px;
- border-radius: 2px;
- border-color:black;
- box-sizing: border-box;
- background: white;
- border-style:solid;
- overflow-y: scroll;
- overflow-x: hidden;
- color: initial;
- height: 100%;
+ background: $light-color-secondary;
+ padding: 0.9em;
+ border-width: 0px;
+ border-radius: $border-radius;
+ border-color: $intermediate-color;
+ box-sizing: border-box;
+ border-style: solid;
+ overflow-y: scroll;
+ overflow-x: hidden;
+ color: initial;
+ height: 100%;
}
.menuicon {
- display: inline-block;
- border-right: 1px solid rgba(0, 0, 0, 0.2);
- color: #888;
- line-height: 1;
- padding: 0 7px;
- margin: 1px;
- cursor: pointer;
- text-align: center;
- min-width: 1.4em;
- }
- .strong, .heading { font-weight: bold; }
- .em { font-style: italic; } \ No newline at end of file
+ display: inline-block;
+ border-right: 1px solid rgba(0, 0, 0, 0.2);
+ color: #888;
+ line-height: 1;
+ padding: 0 7px;
+ margin: 1px;
+ cursor: pointer;
+ text-align: center;
+ min-width: 1.4em;
+}
+
+.strong,
+.heading {
+ font-weight: bold;
+}
+
+.em {
+ font-style: italic;
+}
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index a6cee9957..512ad7d70 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -14,6 +14,9 @@ import { Plugin } from 'prosemirror-state'
import { Decoration, DecorationSet } from 'prosemirror-view'
import { TooltipTextMenu } from "../../util/TooltipTextMenu"
import { ContextMenu } from "../../views/ContextMenu";
+import { inpRules } from "../../util/RichTextRules";
+const { buildMenuItems } = require("prosemirror-example-setup");
+const { menuBar } = require("prosemirror-menu");
@@ -31,7 +34,7 @@ import { ContextMenu } from "../../views/ContextMenu";
// and 'doc' property to the document that is being rendered
//
// When rendered() by React, this extracts the TextController from the Document stored at the
-// specified Key and assigns it to an HTML input node. When changes are made tot his node,
+// specified Key and assigns it to an HTML input node. When changes are made to this node,
// this will edit the document and assign the new value to that field.
//]
export class FormattedTextBox extends React.Component<FieldViewProps> {
@@ -52,7 +55,9 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
if (this._editorView) {
const state = this._editorView.state.apply(tx);
this._editorView.updateState(state);
- this.props.doc.SetData(this.props.fieldKey, JSON.stringify(state.toJSON()), RichTextField);
+ const { doc, fieldKey } = this.props;
+ doc.SetDataOnPrototype(fieldKey, JSON.stringify(state.toJSON()), RichTextField);
+ // doc.SetData(fieldKey, JSON.stringify(state.toJSON()), RichTextField);
}
}
@@ -60,6 +65,7 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
let state: EditorState;
const config = {
schema,
+ inpRules, //these currently don't do anything, but could eventually be helpful
plugins: [
history(),
keymap({ "Mod-z": undo, "Mod-y": redo }),
@@ -69,7 +75,7 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
};
let field = this.props.doc.GetT(this.props.fieldKey, RichTextField);
- if (field && field != FieldWaiting) {
+ if (field && field != FieldWaiting && field.Data) {
state = EditorState.fromJSON(config, JSON.parse(field.Data));
} else {
state = EditorState.create(config);
@@ -110,10 +116,12 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
@action
onChange(e: React.ChangeEvent<HTMLInputElement>) {
- this.props.doc.SetData(this.props.fieldKey, e.target.value, RichTextField);
+ const { fieldKey, doc } = this.props;
+ doc.SetOnPrototype(fieldKey, new RichTextField(e.target.value))
+ // doc.SetData(fieldKey, e.target.value, RichTextField);
}
onPointerDown = (e: React.PointerEvent): void => {
- if (e.buttons === 1 && this.props.isSelected()) {
+ if (e.buttons === 1 && this.props.isSelected() && !e.altKey) {
e.stopPropagation();
}
}
@@ -150,11 +158,19 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
}
})
}
-
+ onKeyPress(e: React.KeyboardEvent) {
+ e.stopPropagation();
+ // stop propagation doesn't seem to stop propagation of native keyboard events.
+ // so we set a flag on the native event that marks that the event's been handled.
+ // (e.nativeEvent as any).DASHFormattedTextBoxHandled = true;
+ }
render() {
return (<div className="formattedTextBox-cont"
+ onKeyDown={this.onKeyPress}
+ onKeyPress={this.onKeyPress}
onPointerDown={this.onPointerDown}
onContextMenu={this.specificContextMenu}
+ // tfs: do we need this event handler
onWheel={this.onPointerWheel}
ref={this._ref} />)
}
diff --git a/src/client/views/nodes/ImageBox.scss b/src/client/views/nodes/ImageBox.scss
index ea459b911..487038841 100644
--- a/src/client/views/nodes/ImageBox.scss
+++ b/src/client/views/nodes/ImageBox.scss
@@ -1,22 +1,21 @@
-
.imageBox-cont {
- padding: 0vw;
- position: relative;
- text-align: center;
- width: 100%;
- height: auto;
- max-width: 100%;
- max-height: 100%
+ padding: 0vw;
+ position: relative;
+ text-align: center;
+ width: 100%;
+ height: auto;
+ max-width: 100%;
+ max-height: 100%;
}
.imageBox-cont img {
- object-fit: contain;
height: 100%;
+ width:100%;
}
.imageBox-button {
- padding : 0vw;
- border: none;
- width : 100%;
- height: 100%;
-} \ No newline at end of file
+ padding: 0vw;
+ border: none;
+ width: 100%;
+ height: 100%;
+}
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 30910fb1f..60d1f7214 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -1,5 +1,5 @@
-import { action, observable } from 'mobx';
+import { action, observable, trace } from 'mobx';
import { observer } from "mobx-react";
import Lightbox from 'react-image-lightbox';
import 'react-image-lightbox/style.css'; // This only needs to be imported once in your app
@@ -10,6 +10,7 @@ import { ContextMenu } from "../../views/ContextMenu";
import { FieldView, FieldViewProps } from './FieldView';
import "./ImageBox.scss";
import React = require("react")
+import { Utils } from '../../../Utils';
@observer
export class ImageBox extends React.Component<FieldViewProps> {
@@ -49,7 +50,7 @@ export class ImageBox extends React.Component<FieldViewProps> {
onPointerDown = (e: React.PointerEvent): void => {
if (Date.now() - this._lastTap < 300) {
- if (e.buttons === 1 && this.props.isSelected()) {
+ if (e.buttons === 1) {
e.stopPropagation();
this._downX = e.clientX;
this._downY = e.clientY;
@@ -70,8 +71,8 @@ export class ImageBox extends React.Component<FieldViewProps> {
}
lightbox = (path: string) => {
- const images = [path, "http://www.cs.brown.edu/~bcz/face.gif"];
- if (this._isOpen && this.props.isSelected()) {
+ const images = [path];
+ if (this._isOpen) {
return (<Lightbox
mainSrc={images[this._photoIndex]}
nextSrc={images[(this._photoIndex + 1) % images.length]}
@@ -89,12 +90,16 @@ export class ImageBox extends React.Component<FieldViewProps> {
}
}
- //REPLACE THIS WITH CAPABILITIES SPECIFIC TO THIS TYPE OF NODE
- imageCapability = (e: React.MouseEvent): void => {
- }
-
specificContextMenu = (e: React.MouseEvent): void => {
- ContextMenu.Instance.addItem({ description: "Image Capability", event: this.imageCapability });
+ let field = this.props.doc.GetT(this.props.fieldKey, ImageField);
+ if (field && field !== FieldWaiting) {
+ let url = field.Data.href;
+ ContextMenu.Instance.addItem({
+ description: "Copy path", event: () => {
+ Utils.CopyText(url)
+ }
+ });
+ }
}
render() {
diff --git a/src/client/views/nodes/KeyValueBox.scss b/src/client/views/nodes/KeyValueBox.scss
index 1295266e5..63ae75424 100644
--- a/src/client/views/nodes/KeyValueBox.scss
+++ b/src/client/views/nodes/KeyValueBox.scss
@@ -1,31 +1,57 @@
+@import "../global_variables";
.keyValueBox-cont {
- overflow-y:scroll;
+ overflow-y: scroll;
height: 100%;
- border: black;
- border-width: 1px;
- border-style: solid;
+ background-color: $light-color;
+ border: 1px solid $intermediate-color;
+ border-radius: $border-radius;
box-sizing: border-box;
display: inline-block;
.imageBox-cont img {
- max-height:45px;
+ max-height: 45px;
height: auto;
}
+ td {
+ padding: 6px 8px;
+ border-right: 1px solid $intermediate-color;
+ border-top: 1px solid $intermediate-color;
+ &:last-child {
+ border-right: none;
+ }
+ }
}
+
.keyValueBox-table {
position: relative;
+ border-collapse: collapse;
}
+
.keyValueBox-header {
- background:gray;
+ background: $intermediate-color;
+ color: $light-color;
+ text-transform: uppercase;
+ letter-spacing: 2px;
+ font-size: 12px;
+ height: 30px;
+ padding-top: 4px;
+ th {
+ font-weight: normal;
+ &:first-child {
+ border-right: 1px solid $light-color;
+ }
+ }
}
+
.keyValueBox-evenRow {
- background: white;
+ background: $light-color;
.formattedTextBox-cont {
- background: white;
+ background: $light-color;
}
}
+
.keyValueBox-oddRow {
- background: lightGray;
+ background: $light-color-secondary;
.formattedTextBox-cont {
- background: lightgray;
+ background: $light-color-secondary;
}
} \ No newline at end of file
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index ac8c949a9..283c1f732 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -2,17 +2,62 @@
import { observer } from "mobx-react";
import 'react-image-lightbox/style.css'; // This only needs to be imported once in your app
import { Document } from '../../../fields/Document';
-import { FieldWaiting } from '../../../fields/Field';
+import { FieldWaiting, Field } from '../../../fields/Field';
import { KeyStore } from '../../../fields/KeyStore';
import { FieldView, FieldViewProps } from './FieldView';
import "./KeyValueBox.scss";
import { KeyValuePair } from "./KeyValuePair";
import React = require("react")
+import { CompileScript, ToField } from "../../util/Scripting";
+import { Key } from '../../../fields/Key';
+import { observable, action } from "mobx";
@observer
export class KeyValueBox extends React.Component<FieldViewProps> {
public static LayoutString(fieldStr: string = "DataKey") { return FieldView.LayoutString(KeyValueBox, fieldStr) }
+ @observable private _keyInput: string = "";
+ @observable private _valueInput: string = "";
+
+
+ constructor(props: FieldViewProps) {
+ super(props);
+ }
+
+
+
+ shouldComponentUpdate() {
+ return false;
+ }
+
+ @action
+ onEnterKey = (e: React.KeyboardEvent): void => {
+ if (e.key == 'Enter') {
+ if (this._keyInput && this._valueInput) {
+ let doc = this.props.doc.GetT(KeyStore.Data, Document);
+ if (!doc || doc == FieldWaiting) {
+ return
+ }
+ let realDoc = doc;
+
+ let script = CompileScript(this._valueInput, undefined, true);
+ if (!script.compiled) {
+ return;
+ }
+ let field = script();
+ if (field instanceof Field) {
+ realDoc.Set(new Key(this._keyInput), field);
+ } else {
+ let dataField = ToField(field);
+ if (dataField) {
+ realDoc.Set(new Key(this._keyInput), dataField);
+ }
+ }
+ this._keyInput = ""
+ this._valueInput = ""
+ }
+ }
+ }
onPointerDown = (e: React.PointerEvent): void => {
if (e.buttons === 1 && this.props.isSelected()) {
@@ -33,7 +78,7 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
let ids: { [key: string]: string } = {};
let protos = doc.GetAllPrototypes();
for (const proto of protos) {
- proto._proxies.forEach((val, key) => {
+ proto._proxies.forEach((val: any, key: string) => {
if (!(key in ids)) {
ids[key] = key;
}
@@ -48,9 +93,26 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
return rows;
}
+ @action
+ keyChanged = (e: React.ChangeEvent<HTMLInputElement>) => {
+ this._keyInput = e.currentTarget.value;
+ }
+
+ @action
+ valueChanged = (e: React.ChangeEvent<HTMLInputElement>) => {
+ this._valueInput = e.currentTarget.value;
+ }
- render() {
+ newKeyValue = () => {
+ return (
+ <tr>
+ <td><input type="text" value={this._keyInput} placeholder="Key" onChange={this.keyChanged} /></td>
+ <td><input type="text" value={this._valueInput} placeholder="Value" onChange={this.valueChanged} onKeyPress={this.onEnterKey} /></td>
+ </tr>
+ )
+ }
+ render() {
return (<div className="keyValueBox-cont" onWheel={this.onPointerWheel}>
<table className="keyValueBox-table">
<tbody>
@@ -59,6 +121,7 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
<th>Fields</th>
</tr>
{this.createTable()}
+ {this.newKeyValue()}
</tbody>
</table>
</div>)
diff --git a/src/client/views/nodes/KeyValuePair.scss b/src/client/views/nodes/KeyValuePair.scss
new file mode 100644
index 000000000..64e871e1c
--- /dev/null
+++ b/src/client/views/nodes/KeyValuePair.scss
@@ -0,0 +1,12 @@
+@import "../global_variables";
+
+.container{
+ display: flex;
+ flex-direction: row;
+ flex-wrap: nowrap;
+ justify-content: space-between;
+}
+
+.delete{
+ color: red;
+} \ No newline at end of file
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index a97e98313..7ed5ee272 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -1,5 +1,6 @@
import 'react-image-lightbox/style.css'; // This only needs to be imported once in your app
import "./KeyValueBox.scss";
+import "./KeyValuePair.scss";
import React = require("react")
import { FieldViewProps, FieldView } from './FieldView';
import { Opt, Field } from '../../../fields/Field';
@@ -8,6 +9,8 @@ import { observable, action } from 'mobx';
import { Document } from '../../../fields/Document';
import { Key } from '../../../fields/Key';
import { Server } from "../../Server"
+import { EditableView } from "../EditableView";
+import { CompileScript, ToField } from "../../util/Scripting";
// Represents one row in a key value plane
@@ -48,10 +51,48 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
bindings: {},
selectOnLoad: false,
}
+ let contents = (
+ <FieldView {...props} />
+ );
return (
<tr className={this.props.rowStyle}>
- <td>{this.key.Name}</td>
- <td><FieldView {...props} /></td>
+ {/* <button>X</button> */}
+ <td>
+ <div className="container">
+ <div>{this.key.Name}</div>
+ <button className="delete" onClick={() => {
+ let field = props.doc.Get(props.fieldKey);
+ if (field && field instanceof Field) {
+ props.doc.Set(props.fieldKey, undefined);
+ }
+ }}>X</button>
+ </div>
+ </td>
+ <td><EditableView contents={contents} height={36} GetValue={() => {
+ let field = props.doc.Get(props.fieldKey);
+ if (field && field instanceof Field) {
+ return field.ToScriptString();
+ }
+ return field || "";
+ }}
+ SetValue={(value: string) => {
+ let script = CompileScript(value, undefined, true);
+ if (!script.compiled) {
+ return false;
+ }
+ let field = script();
+ if (field instanceof Field) {
+ props.doc.Set(props.fieldKey, field);
+ return true;
+ } else {
+ let dataField = ToField(field);
+ if (dataField) {
+ props.doc.Set(props.fieldKey, dataField);
+ return true;
+ }
+ }
+ return false;
+ }}></EditableView></td>
</tr>
)
}
diff --git a/src/client/views/nodes/LinkBox.scss b/src/client/views/nodes/LinkBox.scss
new file mode 100644
index 000000000..5d5f782d2
--- /dev/null
+++ b/src/client/views/nodes/LinkBox.scss
@@ -0,0 +1,65 @@
+@import "../global_variables";
+.link-container {
+ width: 100%;
+ height: 35px;
+ display: flex;
+ flex-direction: row;
+ border-top: 0.5px solid #bababa;
+}
+
+.info-container {
+ width: 55%;
+ padding-top: 5px;
+ padding-left: 5px;
+ display: flex;
+ flex-direction: column
+}
+
+.link-name {
+ font-size: 11px;
+}
+
+.doc-name {
+ font-size: 8px;
+}
+
+.button-container {
+ width: 45%;
+ display: flex;
+ flex-direction: row;
+}
+
+.button {
+ height: 20px;
+ width: 20px;
+ margin: 8px 4px;
+ border-radius: 50%;
+ opacity: 0.9;
+ pointer-events: auto;
+ background-color: $dark-color;
+ color: $light-color;
+ text-transform: uppercase;
+ letter-spacing: 2px;
+ font-size: 60%;
+ transition: transform 0.2s;
+}
+
+.button:hover {
+ background: $main-accent;
+ cursor: pointer;
+}
+
+.fa-icon-view {
+ margin-left: 3px;
+ margin-top: 5px;
+}
+
+.fa-icon-edit {
+ margin-left: 5px;
+ margin-top: 5px;
+}
+
+.fa-icon-delete {
+ margin-left: 6px;
+ margin-top: 5px;
+} \ No newline at end of file
diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx
new file mode 100644
index 000000000..e81f8fec7
--- /dev/null
+++ b/src/client/views/nodes/LinkBox.tsx
@@ -0,0 +1,122 @@
+import { observable, computed, action } from "mobx";
+import React = require("react");
+import { SelectionManager } from "../../util/SelectionManager";
+import { observer } from "mobx-react";
+import './LinkBox.scss'
+import { KeyStore } from '../../../fields/KeyStore'
+import { props } from "bluebird";
+import { DocumentView } from "./DocumentView";
+import { Document } from "../../../fields/Document";
+import { ListField } from "../../../fields/ListField";
+import { DocumentManager } from "../../util/DocumentManager";
+import { LinkEditor } from "./LinkEditor";
+import { CollectionDockingView } from "../collections/CollectionDockingView";
+import { library } from '@fortawesome/fontawesome-svg-core';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { faEye } from '@fortawesome/free-solid-svg-icons';
+import { faEdit } from '@fortawesome/free-solid-svg-icons';
+import { faTimes } from '@fortawesome/free-solid-svg-icons';
+import { undoBatch } from "../../util/UndoManager";
+import { FieldWaiting } from "../../../fields/Field";
+import { NumberField } from "../../../fields/NumberField";
+
+
+library.add(faEye);
+library.add(faEdit);
+library.add(faTimes);
+
+interface Props {
+ linkDoc: Document;
+ linkName: String;
+ pairedDoc: Document;
+ type: String;
+ showEditor: () => void
+}
+
+@observer
+export class LinkBox extends React.Component<Props> {
+
+ @undoBatch
+ onViewButtonPressed = (e: React.PointerEvent): void => {
+ e.stopPropagation();
+ let docView = DocumentManager.Instance.getDocumentView(this.props.pairedDoc);
+ if (docView) {
+ docView.props.focus(docView.props.Document);
+ } else {
+ this.props.pairedDoc.GetAsync(KeyStore.AnnotationOn, (contextDoc: any) => {
+ if (!contextDoc) {
+ CollectionDockingView.Instance.AddRightSplit(this.props.pairedDoc.MakeDelegate());
+ } else if (contextDoc instanceof Document) {
+ this.props.pairedDoc.GetTAsync(KeyStore.Page, NumberField).then((pfield: any) => {
+ contextDoc.GetTAsync(KeyStore.CurPage, NumberField).then((cfield: any) => {
+ if (pfield != cfield)
+ contextDoc.SetNumber(KeyStore.CurPage, pfield.Data);
+ let contextView = DocumentManager.Instance.getDocumentView(contextDoc);
+ if (contextView) {
+ contextView.props.focus(contextDoc);
+ } else {
+ CollectionDockingView.Instance.AddRightSplit(contextDoc);
+ }
+ })
+ });
+ }
+ });
+ }
+ }
+
+ onEditButtonPressed = (e: React.PointerEvent): void => {
+ console.log("edit down");
+ e.stopPropagation();
+
+ this.props.showEditor();
+ }
+
+ onDeleteButtonPressed = (e: React.PointerEvent): void => {
+ console.log("delete down");
+ e.stopPropagation();
+ this.props.linkDoc.GetTAsync(KeyStore.LinkedFromDocs, Document, field => {
+ if (field) {
+ field.GetTAsync<ListField<Document>>(KeyStore.LinkedToDocs, ListField, field => {
+ if (field) {
+ field.Data.splice(field.Data.indexOf(this.props.linkDoc));
+ }
+ })
+ }
+ });
+ this.props.linkDoc.GetTAsync(KeyStore.LinkedToDocs, Document, field => {
+ if (field) {
+ field.GetTAsync<ListField<Document>>(KeyStore.LinkedFromDocs, ListField, field => {
+ if (field) {
+ field.Data.splice(field.Data.indexOf(this.props.linkDoc));
+ }
+ })
+ }
+ });
+ }
+
+ render() {
+
+ return (
+ //<LinkEditor linkBox={this} linkDoc={this.props.linkDoc} />
+ <div className="link-container">
+ <div className="info-container" onPointerDown={this.onViewButtonPressed}>
+ <div className="link-name">
+ <p>{this.props.linkName}</p>
+ </div>
+ <div className="doc-name">
+ <p>{this.props.type}{this.props.pairedDoc.Title}</p>
+ </div>
+ </div>
+
+ <div className="button-container">
+ <div title="Follow Link" className="button" onPointerDown={this.onViewButtonPressed}>
+ <FontAwesomeIcon className="fa-icon-view" icon="eye" size="sm" /></div>
+ <div title="Edit Link" className="button" onPointerDown={this.onEditButtonPressed}>
+ <FontAwesomeIcon className="fa-icon-edit" icon="edit" size="sm" /></div>
+ <div title="Delete Link" className="button" onPointerDown={this.onDeleteButtonPressed}>
+ <FontAwesomeIcon className="fa-icon-delete" icon="times" size="sm" /></div>
+ </div>
+ </div>
+ )
+ }
+} \ No newline at end of file
diff --git a/src/client/views/nodes/LinkEditor.scss b/src/client/views/nodes/LinkEditor.scss
new file mode 100644
index 000000000..fb0c69cff
--- /dev/null
+++ b/src/client/views/nodes/LinkEditor.scss
@@ -0,0 +1,43 @@
+@import "../global_variables";
+.edit-container {
+ width: 100%;
+ height: auto;
+ display: flex;
+ flex-direction: column;
+}
+
+.name-input {
+ margin-bottom: 10px;
+ padding: 5px;
+ font-size: 12px;
+ border: 1px solid #bababa;
+}
+
+.description-input {
+ font-size: 11px;
+ padding: 5px;
+ margin-bottom: 10px;
+ border: 1px solid #bababa;
+}
+
+.save-button {
+ width: 50px;
+ height: 20px;
+ pointer-events: auto;
+ background-color: $dark-color;
+ color: $light-color;
+ text-transform: uppercase;
+ letter-spacing: 2px;
+ padding: 2px;
+ font-size: 10px;
+ margin: 0 auto;
+ transition: transform 0.2s;
+ text-align: center;
+ line-height: 20px;
+}
+
+.save-button:hover {
+ background: $main-accent;
+ transform: scale(1.05);
+ cursor: pointer;
+} \ No newline at end of file
diff --git a/src/client/views/nodes/LinkEditor.tsx b/src/client/views/nodes/LinkEditor.tsx
new file mode 100644
index 000000000..3f7b4bf2d
--- /dev/null
+++ b/src/client/views/nodes/LinkEditor.tsx
@@ -0,0 +1,58 @@
+import { observable, computed, action } from "mobx";
+import React = require("react");
+import { SelectionManager } from "../../util/SelectionManager";
+import { observer } from "mobx-react";
+import './LinkEditor.scss'
+import { KeyStore } from '../../../fields/KeyStore'
+import { props } from "bluebird";
+import { DocumentView } from "./DocumentView";
+import { Document } from "../../../fields/Document";
+import { TextField } from "../../../fields/TextField";
+import { link } from "fs";
+
+interface Props {
+ linkDoc: Document;
+ showLinks: () => void;
+}
+
+@observer
+export class LinkEditor extends React.Component<Props> {
+
+ @observable private _nameInput: string = this.props.linkDoc.GetText(KeyStore.Title, "");
+ @observable private _descriptionInput: string = this.props.linkDoc.GetText(KeyStore.LinkDescription, "");
+
+
+ onSaveButtonPressed = (e: React.PointerEvent): void => {
+ console.log("view down");
+ e.stopPropagation();
+
+ this.props.linkDoc.SetData(KeyStore.Title, this._nameInput, TextField);
+ this.props.linkDoc.SetData(KeyStore.LinkDescription, this._descriptionInput, TextField);
+
+ this.props.showLinks();
+ }
+
+
+
+ render() {
+
+ return (
+ <div className="edit-container">
+ <input onChange={this.onNameChanged} className="name-input" type="text" value={this._nameInput} placeholder="Name . . ."></input>
+ <textarea onChange={this.onDescriptionChanged} className="description-input" value={this._descriptionInput} placeholder="Description . . ."></textarea>
+ <div className="save-button" onPointerDown={this.onSaveButtonPressed}>SAVE</div>
+ </div>
+
+ )
+ }
+
+ @action
+ onNameChanged = (e: React.ChangeEvent<HTMLInputElement>) => {
+ this._nameInput = e.target.value;
+ }
+
+ @action
+ onDescriptionChanged = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
+ this._descriptionInput = e.target.value;
+ }
+} \ No newline at end of file
diff --git a/src/client/views/nodes/LinkMenu.scss b/src/client/views/nodes/LinkMenu.scss
new file mode 100644
index 000000000..dedcce6ef
--- /dev/null
+++ b/src/client/views/nodes/LinkMenu.scss
@@ -0,0 +1,21 @@
+#linkMenu-container {
+ width: 100%;
+ height: auto;
+ display: flex;
+ flex-direction: column;
+}
+
+#linkMenu-searchBar {
+ width: 100%;
+ padding: 5px;
+ margin-bottom: 10px;
+ font-size: 12px;
+ border: 1px solid #bababa;
+}
+
+#linkMenu-list {
+ margin-top: 5px;
+ width: 100%;
+ height: 100px;
+ overflow-y: scroll;
+} \ No newline at end of file
diff --git a/src/client/views/nodes/LinkMenu.tsx b/src/client/views/nodes/LinkMenu.tsx
new file mode 100644
index 000000000..5eeb40772
--- /dev/null
+++ b/src/client/views/nodes/LinkMenu.tsx
@@ -0,0 +1,54 @@
+import { action, observable } from "mobx";
+import { observer } from "mobx-react";
+import { Document } from "../../../fields/Document";
+import { FieldWaiting } from "../../../fields/Field";
+import { Key } from "../../../fields/Key";
+import { KeyStore } from '../../../fields/KeyStore';
+import { ListField } from "../../../fields/ListField";
+import { DocumentView } from "./DocumentView";
+import { LinkBox } from "./LinkBox";
+import { LinkEditor } from "./LinkEditor";
+import './LinkMenu.scss';
+import React = require("react");
+
+interface Props {
+ docView: DocumentView;
+ changeFlyout: () => void
+}
+
+@observer
+export class LinkMenu extends React.Component<Props> {
+
+ @observable private _editingLink?: Document;
+
+ renderLinkItems(links: Document[], key: Key, type: string) {
+ return links.map(link => {
+ let doc = link.GetT(key, Document);
+ if (doc && doc != FieldWaiting) {
+ return <LinkBox key={doc.Id} linkDoc={link} linkName={link.Title} pairedDoc={doc} showEditor={action(() => this._editingLink = link)} type={type} />
+ }
+ })
+ }
+
+ render() {
+ //get list of links from document
+ let linkFrom: Document[] = this.props.docView.props.Document.GetData(KeyStore.LinkedFromDocs, ListField, []);
+ let linkTo: Document[] = this.props.docView.props.Document.GetData(KeyStore.LinkedToDocs, ListField, []);
+ if (this._editingLink === undefined) {
+ return (
+ <div id="linkMenu-container">
+ <input id="linkMenu-searchBar" type="text" placeholder="Search..."></input>
+ <div id="linkMenu-list">
+ {this.renderLinkItems(linkTo, KeyStore.LinkedToDocs, "Destination: ")}
+ {this.renderLinkItems(linkFrom, KeyStore.LinkedFromDocs, "Source: ")}
+ </div>
+ </div>
+ )
+ } else {
+ return (
+ <LinkEditor linkDoc={this._editingLink} showLinks={action(() => this._editingLink = undefined)}></LinkEditor>
+ )
+ }
+
+ }
+} \ No newline at end of file
diff --git a/src/client/views/nodes/PDFBox.scss b/src/client/views/nodes/PDFBox.scss
index 9f92410d4..ad947afd5 100644
--- a/src/client/views/nodes/PDFBox.scss
+++ b/src/client/views/nodes/PDFBox.scss
@@ -11,5 +11,5 @@
}
.pdfBox-contentContainer {
position: absolute;
- transform-origin: "left top";
+ transform-origin: left top;
} \ No newline at end of file
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 70a70c7c8..28a1f9757 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -1,5 +1,5 @@
import * as htmlToImage from "html-to-image";
-import { action, computed, observable, reaction, IReactionDisposer } from 'mobx';
+import { action, computed, observable, reaction, IReactionDisposer, trace, keys } from 'mobx';
import { observer } from "mobx-react";
import 'react-image-lightbox/style.css';
import Measure from "react-measure";
@@ -17,6 +17,8 @@ import "./ImageBox.scss";
import "./PDFBox.scss";
import { Sticky } from './Sticky'; //you should look at sticky and annotation, because they are used here
import React = require("react")
+import { RouteStore } from "../../../server/RouteStore";
+import { NumberField } from "../../../fields/NumberField";
/** ALSO LOOK AT: Annotation.tsx, Sticky.tsx
* This method renders PDF and puts all kinds of functionalities such as annotation, highlighting,
@@ -59,7 +61,6 @@ export class PDFBox extends React.Component<FieldViewProps> {
//very useful for keeping track of X and y position throughout the PDF Canvas
private initX: number = 0;
private initY: number = 0;
- private initPage: boolean = false;
//checks if tool is on
private _toolOn: boolean = false; //checks if tool is on
@@ -86,18 +87,16 @@ export class PDFBox extends React.Component<FieldViewProps> {
@observable private _interactive: boolean = false;
@observable private _loaded: boolean = false;
- @computed private get curPage() { return this.props.doc.GetNumber(KeyStore.CurPage, 0); }
+ @computed private get curPage() { return this.props.doc.GetNumber(KeyStore.CurPage, 1); }
+ @computed private get thumbnailPage() { return this.props.doc.GetNumber(KeyStore.ThumbnailPage, -1); }
componentDidMount() {
this._reactionDisposer = reaction(
- () => this.curPage,
+ () => [this.curPage, this.thumbnailPage],
() => {
- if (this.curPage && this.initPage) {
+ if (this.curPage > 0 && this.thumbnailPage > 0 && this.curPage != this.thumbnailPage) {
this.saveThumbnail();
this._interactive = true;
- } else {
- if (this.curPage)
- this.initPage = true;
}
},
{ fireImmediately: true });
@@ -383,6 +382,7 @@ export class PDFBox extends React.Component<FieldViewProps> {
{ width: me.props.doc.GetNumber(KeyStore.NativeWidth, 0), height: me.props.doc.GetNumber(KeyStore.NativeHeight, 0), quality: 0.5 })
.then(function (dataUrl: string) {
me.props.doc.SetData(KeyStore.Thumbnail, new URL(dataUrl), ImageField);
+ me.props.doc.SetNumber(KeyStore.ThumbnailPage, me.props.doc.GetNumber(KeyStore.CurPage, -1));
})
.catch(function (error: any) {
console.error('oops, something went wrong!', error);
@@ -423,7 +423,9 @@ export class PDFBox extends React.Component<FieldViewProps> {
// so this design is flawed.
var nativeWidth = this.props.doc.GetNumber(KeyStore.NativeWidth, 0);
if (!this.props.doc.GetNumber(KeyStore.NativeHeight, 0)) {
- this.props.doc.SetNumber(KeyStore.NativeHeight, nativeWidth * r.entry.height / r.entry.width);
+ var nativeHeight = nativeWidth * r.entry.height / r.entry.width;
+ this.props.doc.SetNumber(KeyStore.Height, nativeHeight / nativeWidth * this.props.doc.GetNumber(KeyStore.Width, 0));
+ this.props.doc.SetNumber(KeyStore.NativeHeight, nativeHeight);
}
if (!this.props.doc.GetT(KeyStore.Thumbnail, ImageField)) {
this.saveThumbnail();
@@ -433,13 +435,11 @@ export class PDFBox extends React.Component<FieldViewProps> {
@computed
get pdfContent() {
let page = this.curPage;
- if (page == 0)
- page = 1;
const renderHeight = 2400;
let pdfUrl = this.props.doc.GetT(this.props.fieldKey, PDFField);
let xf = this.props.doc.GetNumber(KeyStore.NativeHeight, 0) / renderHeight;
return <div className="pdfBox-contentContainer" key="container" style={{ transform: `scale(${xf}, ${xf})` }}>
- <Document file={window.origin + "/corsProxy/" + `${pdfUrl}`}>
+ <Document file={window.origin + RouteStore.corsProxy + `/${pdfUrl}`}>
<Measure onResize={this.setScaling}>
{({ measureRef }) =>
<div className="pdfBox-page" ref={measureRef}>
@@ -461,19 +461,17 @@ export class PDFBox extends React.Component<FieldViewProps> {
return [
this._pageInfo.area.filter(() => this._pageInfo.area).map((element: any) => element),
this._currAnno.map((element: any) => element),
- <div key="pdfBox-contentShell">
- {this.pdfContent}
- {proxy}
- </div>
+ this.pdfContent,
+ proxy
];
}
@computed
get imageProxyRenderer() {
- let field = this.props.doc.Get(KeyStore.Thumbnail);
- if (field) {
- let path = field == FieldWaiting ? "https://image.flaticon.com/icons/svg/66/66163.svg" :
- field instanceof ImageField ? field.Data.href : "http://cs.brown.edu/people/bcz/prairie.jpg";
+ let thumbField = this.props.doc.Get(KeyStore.Thumbnail);
+ if (thumbField) {
+ let path = thumbField == FieldWaiting || this.thumbnailPage != this.curPage ? "https://image.flaticon.com/icons/svg/66/66163.svg" :
+ thumbField instanceof ImageField ? thumbField.Data.href : "http://cs.brown.edu/people/bcz/prairie.jpg";
return <img src={path} width="100%" />;
}
return (null);
diff --git a/src/client/views/nodes/VideoBox.scss b/src/client/views/nodes/VideoBox.scss
new file mode 100644
index 000000000..76bbeb37c
--- /dev/null
+++ b/src/client/views/nodes/VideoBox.scss
@@ -0,0 +1,4 @@
+.videobox-cont{
+ width: 100%;
+ height: Auto;
+} \ No newline at end of file
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
new file mode 100644
index 000000000..7c0db83a8
--- /dev/null
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -0,0 +1,78 @@
+import React = require("react")
+import { observer } from "mobx-react";
+import { FieldWaiting, Opt } from '../../../fields/Field';
+import { VideoField } from '../../../fields/VideoField';
+import { FieldView, FieldViewProps } from './FieldView';
+import "./VideoBox.scss";
+import Measure from "react-measure";
+import { action, trace, observable, IReactionDisposer, computed, reaction } from "mobx";
+import { KeyStore } from "../../../fields/KeyStore";
+import { number } from "prop-types";
+
+@observer
+export class VideoBox extends React.Component<FieldViewProps> {
+
+ private _reactionDisposer: Opt<IReactionDisposer>;
+ private _videoRef = React.createRef<HTMLVideoElement>()
+ public static LayoutString() { return FieldView.LayoutString(VideoBox) }
+
+ constructor(props: FieldViewProps) {
+ super(props);
+ }
+
+ @computed private get curPage() { return this.props.doc.GetNumber(KeyStore.CurPage, -1); }
+
+
+ _loaded: boolean = false;
+
+ @action
+ setScaling = (r: any) => {
+ if (this._loaded) {
+ // bcz: the nativeHeight should really be set when the document is imported.
+ // also, the native dimensions could be different for different pages of the PDF
+ // so this design is flawed.
+ var nativeWidth = this.props.doc.GetNumber(KeyStore.NativeWidth, 0);
+ var nativeHeight = this.props.doc.GetNumber(KeyStore.NativeHeight, 0);
+ var newNativeHeight = nativeWidth * r.entry.height / r.entry.width;
+ if (!nativeHeight && newNativeHeight != nativeHeight && !isNaN(newNativeHeight)) {
+ this.props.doc.SetNumber(KeyStore.Height, newNativeHeight / nativeWidth * this.props.doc.GetNumber(KeyStore.Width, 0));
+ this.props.doc.SetNumber(KeyStore.NativeHeight, newNativeHeight);
+ }
+ } else {
+ this._loaded = true;
+ }
+ }
+
+ get player(): HTMLVideoElement | undefined {
+ return this._videoRef.current ? this._videoRef.current.getElementsByTagName("video")[0] : undefined;
+ }
+
+ @action
+ setVideoRef = (vref: HTMLVideoElement | null) => {
+ if (this.curPage >= 0 && vref) {
+ vref!.currentTime = this.curPage;
+ (vref! as any).AHackBecauseSomethingResetsTheVideoToZero = this.curPage;
+ }
+ }
+
+ render() {
+ let field = this.props.doc.GetT(this.props.fieldKey, VideoField);
+ if (!field || field === FieldWaiting) {
+ return <div>Loading</div>
+ }
+ let path = field.Data.href;
+ trace();
+ return (
+ <Measure onResize={this.setScaling}>
+ {({ measureRef }) =>
+ <div style={{ width: "100%", height: "auto" }} ref={measureRef}>
+ <video className="videobox-cont" onClick={() => { }} ref={this.setVideoRef}>
+ <source src={path} type="video/mp4" />
+ Not supported.
+ </video>
+ </div>
+ }
+ </Measure>
+ )
+ }
+} \ No newline at end of file
diff --git a/src/client/views/nodes/WebBox.scss b/src/client/views/nodes/WebBox.scss
index e72b3c4da..a535b2638 100644
--- a/src/client/views/nodes/WebBox.scss
+++ b/src/client/views/nodes/WebBox.scss
@@ -4,6 +4,7 @@
position: absolute;
width: 100%;
height: 100%;
+ overflow: scroll;
}
.webBox-button {
diff --git a/src/debug/Test.tsx b/src/debug/Test.tsx
index 7bc70615f..c8de33f41 100644
--- a/src/debug/Test.tsx
+++ b/src/debug/Test.tsx
@@ -1,46 +1,29 @@
import * as React from 'react';
import * as ReactDOM from 'react-dom';
+import JsxParser from 'react-jsx-parser'
-class TestInternal extends React.Component {
- onContextMenu = (e: React.MouseEvent) => {
- console.log("Internal");
- e.stopPropagation();
- }
-
- onPointerDown = (e: React.MouseEvent) => {
- console.log("pointer down")
- e.preventDefault();
- }
-
- render() {
- return <div onContextMenu={this.onContextMenu} onPointerDown={this.onPointerDown}
- onPointerUp={this.onPointerDown}>Hello world</div>
- }
-}
-
-class TestChild extends React.Component {
- onContextMenu = () => {
- console.log("Child");
- }
-
+class Hello extends React.Component<{ firstName: string, lastName: string }> {
render() {
- return <div onContextMenu={this.onContextMenu}><TestInternal /></div>
+ return <div>Hello {this.props.firstName} {this.props.lastName}</div>
}
}
-class TestParent extends React.Component {
- onContextMenu = () => {
- console.log("Parent");
- }
-
+class Test extends React.Component {
render() {
- return <div onContextMenu={this.onContextMenu}><TestChild /></div>
+ let jsx = "<Hello {...props}/>";
+ let bindings = {
+ props: {
+ firstName: "First",
+ lastName: "Last"
+ }
+ }
+ return <JsxParser jsx={jsx} bindings={bindings} components={{ Hello }}></JsxParser>
}
}
ReactDOM.render((
<div style={{ position: "absolute", width: "100%", height: "100%" }}>
- <TestParent />
+ <Test />
</div>),
document.getElementById('root')
); \ No newline at end of file
diff --git a/src/debug/Viewer.tsx b/src/debug/Viewer.tsx
index 780e9f8f2..7fdd77bf3 100644
--- a/src/debug/Viewer.tsx
+++ b/src/debug/Viewer.tsx
@@ -137,11 +137,13 @@ class DebugViewer extends React.Component<{ fieldId: string }> {
content = (<FieldViewer field={this.field} />)
} else if (this.field instanceof Key) {
content = (<KeyViewer field={this.field} />)
+ } else {
+ content = (<span>Unrecognized field type</span>)
}
} else if (this.error) {
content = <span>Field <b>{this.props.fieldId}</b> not found <button onClick={() => this.update()}>Refresh</button></span>
} else {
- content = <>Field loading</>
+ content = <span>Field loading: {this.props.fieldId}</span>
}
return content;
}
diff --git a/src/fields/AudioField.ts b/src/fields/AudioField.ts
new file mode 100644
index 000000000..8864471ae
--- /dev/null
+++ b/src/fields/AudioField.ts
@@ -0,0 +1,31 @@
+import { BasicField } from "./BasicField";
+import { Field, FieldId } from "./Field";
+import { Types } from "../server/Message";
+
+export class AudioField extends BasicField<URL> {
+ constructor(data: URL | undefined = undefined, id?: FieldId, save: boolean = true) {
+ super(data == undefined ? new URL("http://techslides.com/demos/samples/sample.mp3") : data, save, id);
+ }
+
+ toString(): string {
+ return this.Data.href;
+ }
+
+
+ ToScriptString(): string {
+ return `new AudioField("${this.Data}")`;
+ }
+
+ Copy(): Field {
+ return new AudioField(this.Data);
+ }
+
+ ToJson(): { type: Types, data: string, _id: string } {
+ return {
+ type: Types.Audio,
+ data: this.Data.href,
+ _id: this.Id
+ }
+ }
+
+} \ No newline at end of file
diff --git a/src/fields/Document.ts b/src/fields/Document.ts
index 2e873439c..85ff6ddcb 100644
--- a/src/fields/Document.ts
+++ b/src/fields/Document.ts
@@ -1,8 +1,8 @@
import { Key } from "./Key"
import { KeyStore } from "./KeyStore";
-import { Field, Cast, FieldWaiting, FieldValue, FieldId } from "./Field"
+import { Field, Cast, FieldWaiting, FieldValue, FieldId, Opt } from "./Field"
import { NumberField } from "./NumberField";
-import { ObservableMap, computed, action } from "mobx";
+import { ObservableMap, computed, action, runInAction } from "mobx";
import { TextField } from "./TextField";
import { ListField } from "./ListField";
import { Server } from "../client/Server";
@@ -11,6 +11,7 @@ import { UndoManager } from "../client/util/UndoManager";
import { HtmlField } from "./HtmlField";
export class Document extends Field {
+ //TODO tfs: We should probably store FieldWaiting in fields when we request it from the server so that we don't set up multiple server gets for the same document and field
public fields: ObservableMap<string, { key: Key, field: Field }> = new ObservableMap();
public _proxies: ObservableMap<string, FieldId> = new ObservableMap();
@@ -34,8 +35,23 @@ export class Document extends Field {
public Scale = () => { return this.GetNumber(KeyStore.Scale, 1) }
@computed
- public get Title() {
- return this.GetText(KeyStore.Title, "<untitled>");
+ public get Title(): string {
+ let title = this.Get(KeyStore.Title, true);
+ if (title)
+ if (title != FieldWaiting && title instanceof TextField)
+ return title.Data;
+ else return "-waiting-";
+ let parTitle = this.GetT(KeyStore.Title, TextField);
+ if (parTitle)
+ if (parTitle != FieldWaiting)
+ return parTitle.Data + ".alias";
+ else return "-waiting-.alias";
+ return "-untitled-";
+ }
+
+ @computed
+ public get Fields() {
+ return this.fields;
}
/**
@@ -118,14 +134,39 @@ export class Document extends Field {
* Note: The callback will not be called if there is no associated field.
* @returns `true` if the field exists on the document and `callback` will be called, and `false` otherwise
*/
- GetAsync(key: Key, callback: (field: Field) => void): boolean {
- //TODO: This should probably check if this.fields contains the key before calling Server.GetDocumentField
- //This currently doesn't deal with prototypes
- if (this._proxies.has(key.Id)) {
+ GetAsync(key: Key, callback: (field: Opt<Field>) => void): void {
+ //TODO: This currently doesn't deal with prototypes
+ let field = this.fields.get(key.Id);
+ if (field && field.field) {
+ callback(field.field);
+ } else if (this._proxies.has(key.Id)) {
Server.GetDocumentField(this, key, callback);
- return true;
+ } else if (this._proxies.has(KeyStore.Prototype.Id)) {
+ this.GetTAsync(KeyStore.Prototype, Document, proto => {
+ if (proto) {
+ proto.GetAsync(key, callback);
+ } else {
+ callback(undefined);
+ }
+ })
+ } else {
+ callback(undefined);
+ }
+ }
+
+ GetTAsync<T extends Field>(key: Key, ctor: { new(): T }): Promise<Opt<T>>;
+ GetTAsync<T extends Field>(key: Key, ctor: { new(): T }, callback: (field: Opt<T>) => void): void;
+ GetTAsync<T extends Field>(key: Key, ctor: { new(): T }, callback?: (field: Opt<T>) => void): Promise<Opt<T>> | void {
+ let fn = (cb: (field: Opt<T>) => void) => {
+ return this.GetAsync(key, (field) => {
+ cb(Cast(field, ctor));
+ });
+ }
+ if (callback) {
+ fn(callback);
+ } else {
+ return new Promise(res => fn(res));
}
- return false;
}
/**
@@ -201,36 +242,54 @@ export class Document extends Field {
}
@action
- Set(key: Key, field: Field | undefined): void {
+ Set(key: Key, field: Field | undefined, setOnPrototype = false): void {
let old = this.fields.get(key.Id);
let oldField = old ? old.field : undefined;
- if (field) {
- this.fields.set(key.Id, { key, field });
- this._proxies.set(key.Id, field.Id)
- // Server.AddDocumentField(this, key, field);
- } else {
- this.fields.delete(key.Id);
- this._proxies.delete(key.Id)
- // Server.DeleteDocumentField(this, key);
+ if (setOnPrototype) {
+ this.SetOnPrototype(key, field)
+ }
+ else {
+ if (field) {
+ this.fields.set(key.Id, { key, field });
+ this._proxies.set(key.Id, field.Id)
+ // Server.AddDocumentField(this, key, field);
+ } else {
+ this.fields.delete(key.Id);
+ this._proxies.delete(key.Id)
+ // Server.DeleteDocumentField(this, key);
+ }
+ Server.UpdateField(this);
}
if (oldField || field) {
UndoManager.AddEvent({
- undo: () => this.Set(key, oldField),
- redo: () => this.Set(key, field)
+ undo: () => this.Set(key, oldField, setOnPrototype),
+ redo: () => this.Set(key, field, setOnPrototype)
})
}
- Server.UpdateField(this);
}
@action
- SetData<T, U extends Field & { Data: T }>(key: Key, value: T, ctor: { new(): U }, replaceWrongType = true) {
+ SetOnPrototype(key: Key, field: Field | undefined): void {
+ this.GetTAsync(KeyStore.Prototype, Document, (f: Opt<Document>) => {
+ f && f.Set(key, field)
+ })
+ }
+
+ @action
+ SetDataOnPrototype<T, U extends Field & { Data: T }>(key: Key, value: T, ctor: { new(): U }, replaceWrongType = true) {
+ this.GetTAsync(KeyStore.Prototype, Document, (f: Opt<Document>) => {
+ f && f.SetData(key, value, ctor)
+ })
+ }
+ @action
+ SetData<T, U extends Field & { Data: T }>(key: Key, value: T, ctor: { new(data: T): U }, replaceWrongType = true) {
let field = this.Get(key, true);
if (field instanceof ctor) {
field.Data = value;
} else if (!field || replaceWrongType) {
- let newField = new ctor();
- newField.Data = value;
+ let newField = new ctor(value);
+ // newField.Data = value;
this.Set(key, newField);
}
}
@@ -259,6 +318,15 @@ export class Document extends Field {
return protos;
}
+ CreateAlias(id?: string): Document {
+ let alias = new Document(id)
+ this.GetTAsync(KeyStore.Prototype, Document, (f: Opt<Document>) => {
+ f && alias.Set(KeyStore.Prototype, f)
+ })
+
+ return alias
+ }
+
MakeDelegate(id?: string): Document {
let delegate = new Document(id);
@@ -275,6 +343,7 @@ export class Document extends Field {
throw new Error("Method not implemented.");
}
GetValue() {
+ return this.Title;
var title = (this._proxies.has(KeyStore.Title.Id) ? "???" : this.Title) + "(" + this.Id + ")";
return title;
//throw new Error("Method not implemented.");
diff --git a/src/fields/HtmlField.ts b/src/fields/HtmlField.ts
index a07326095..7cbdf7e58 100644
--- a/src/fields/HtmlField.ts
+++ b/src/fields/HtmlField.ts
@@ -15,7 +15,7 @@ export class HtmlField extends BasicField<string> {
return new HtmlField(this.Data);
}
- ToJson(): { _id: string; type: Types; data: any; } {
+ ToJson(): { _id: string; type: Types; data: string; } {
return {
type: Types.Html,
data: this.Data,
diff --git a/src/fields/ImageField.ts b/src/fields/ImageField.ts
index be8d73e68..a9ece7d7b 100644
--- a/src/fields/ImageField.ts
+++ b/src/fields/ImageField.ts
@@ -19,10 +19,10 @@ export class ImageField extends BasicField<URL> {
return new ImageField(this.Data);
}
- ToJson(): { type: Types, data: URL, _id: string } {
+ ToJson(): { type: Types, data: string, _id: string } {
return {
type: Types.Image,
- data: this.Data,
+ data: this.Data.href,
_id: this.Id
}
}
diff --git a/src/fields/KVPField.ts b/src/fields/KVPField.ts
deleted file mode 100644
index a7ecc0768..000000000
--- a/src/fields/KVPField.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-import { BasicField } from "./BasicField"
-import { FieldId } from "./Field";
-import { Types } from "../server/Message";
-import { Document } from "./Document"
-
-export class KVPField extends BasicField<Document> {
- constructor(data: Document | undefined = undefined, id?: FieldId, save: boolean = true) {
- super(data == undefined ? new Document() : data, save, id);
- }
-
- toString(): string {
- return this.Data.Title;
- }
-
- ToScriptString(): string {
- return `new KVPField("${this.Data}")`;
- }
-
- Copy() {
- return new KVPField(this.Data);
- }
-
- ToJson(): { type: Types, data: Document, _id: string } {
- return {
- type: Types.Text,
- data: this.Data,
- _id: this.Id
- }
- }
-} \ No newline at end of file
diff --git a/src/fields/KeyStore.ts b/src/fields/KeyStore.ts
index f67257093..1f039e592 100644
--- a/src/fields/KeyStore.ts
+++ b/src/fields/KeyStore.ts
@@ -19,16 +19,30 @@ export namespace KeyStore {
export const Annotations = new Key("Annotations");
export const ViewType = new Key("ViewType");
export const Layout = new Key("Layout");
+ export const BackgroundColor = new Key("BackgroundColor");
export const BackgroundLayout = new Key("BackgroundLayout");
export const OverlayLayout = new Key("OverlayLayout");
export const LayoutKeys = new Key("LayoutKeys");
export const LayoutFields = new Key("LayoutFields");
export const ColumnsKey = new Key("SchemaColumns");
+ export const SchemaSplitPercentage = new Key("SchemaSplitPercentage");
export const Caption = new Key("Caption");
- export const ActiveFrame = new Key("ActiveFrame");
+ export const ActiveWorkspace = new Key("ActiveWorkspace");
export const DocumentText = new Key("DocumentText");
+ export const BrushingDocs = new Key("BrushingDocs");
+ export const LinkedToDocs = new Key("LinkedToDocs");
+ export const LinkedFromDocs = new Key("LinkedFromDocs");
+ export const LinkDescription = new Key("LinkDescription");
+ export const LinkTags = new Key("LinkTag");
export const Thumbnail = new Key("Thumbnail");
+ export const ThumbnailPage = new Key("ThumbnailPage");
export const CurPage = new Key("CurPage");
+ export const AnnotationOn = new Key("AnnotationOn");
export const NumPages = new Key("NumPages");
export const Ink = new Key("Ink");
+ export const Cursors = new Key("Cursors");
+ export const OptionalRightCollection = new Key("OptionalRightCollection");
+ export const Archives = new Key("Archives");
+ export const Updated = new Key("Updated");
+ export const Workspaces = new Key("Workspaces");
}
diff --git a/src/fields/ListField.ts b/src/fields/ListField.ts
index ce32da0a6..c4008bd12 100644
--- a/src/fields/ListField.ts
+++ b/src/fields/ListField.ts
@@ -4,6 +4,7 @@ import { UndoManager } from "../client/util/UndoManager";
import { Types } from "../server/Message";
import { BasicField } from "./BasicField";
import { Field, FieldId } from "./Field";
+import { FieldMap } from "../client/SocketStub";
export class ListField<T extends Field> extends BasicField<T[]> {
private _proxies: string[] = []
@@ -16,8 +17,13 @@ export class ListField<T extends Field> extends BasicField<T[]> {
this.observeList();
}
+ private _processingServerUpdate: boolean = false;
+
private observeDisposer: Lambda | undefined;
private observeList(): void {
+ if (this.observeDisposer) {
+ this.observeDisposer()
+ }
this.observeDisposer = observe(this.Data as IObservableArray<T>, (change: IArrayChange<T> | IArraySplice<T>) => {
this.updateProxies()
if (change.type == "splice") {
@@ -31,14 +37,12 @@ export class ListField<T extends Field> extends BasicField<T[]> {
redo: () => this.Data[change.index] = change.newValue
})
}
- Server.UpdateField(this);
+ if (!this._processingServerUpdate)
+ Server.UpdateField(this);
});
}
protected setData(value: T[]) {
- if (this.observeDisposer) {
- this.observeDisposer()
- }
this.data = observable(value);
this.updateProxies();
this.observeList();
@@ -68,31 +72,30 @@ export class ListField<T extends Field> extends BasicField<T[]> {
}
init(callback: (field: Field) => any) {
- Server.GetFields(this._proxies, action((fields: { [index: string]: Field }) => {
- if (!this.arraysEqual(this._proxies, this.Data.map(field => field.Id))) {
+ Server.GetFields(this._proxies, action((fields: FieldMap) => {
+ if (!this.arraysEqual(this._proxies, this.data.map(field => field.Id))) {
var dataids = this.data.map(d => d.Id);
- var added = this.data.length == this._proxies.length - 1;
+ var proxies = this._proxies.map(p => p);
+ var added = this.data.length < this._proxies.length;
var deleted = this.data.length > this._proxies.length;
for (let i = 0; i < dataids.length && added; i++)
- added = this._proxies.indexOf(dataids[i]) != -1;
+ added = proxies.indexOf(dataids[i]) != -1;
for (let i = 0; i < this._proxies.length && deleted; i++)
- deleted = dataids.indexOf(this._proxies[i]) != -1;
- if (added) { // if only 1 items was added
- for (let i = 0; i < this._proxies.length; i++)
- if (dataids.indexOf(this._proxies[i]) === -1)
- this.Data.splice(i, 0, fields[this._proxies[i]] as T);
- } else if (deleted) { // if only items were deleted
- for (let i = this.data.length - 1; i >= 0; i--) {
- if (this._proxies.indexOf(this.data[i].Id) === -1) {
- this.Data.splice(i, 1);
- }
- }
- } else // otherwise, just rebuild the whole list
- this.data = this._proxies.map(id => fields[id] as T)
- observe(this.Data, () => {
- this.updateProxies()
- Server.UpdateField(this);
- })
+ deleted = dataids.indexOf(proxies[i]) != -1;
+
+ this._processingServerUpdate = true;
+ for (let i = 0; i < proxies.length && added; i++) {
+ if (dataids.indexOf(proxies[i]) === -1)
+ this.Data.splice(i, 0, fields[proxies[i]] as T);
+ }
+ for (let i = dataids.length - 1; i >= 0 && deleted; i--) {
+ if (proxies.indexOf(dataids[i]) === -1)
+ this.Data.splice(i, 1);
+ }
+ if (!added && !deleted) {// otherwise, just rebuild the whole list
+ this.setData(proxies.map(id => fields[id] as T));
+ }
+ this._processingServerUpdate = false;
}
callback(this);
}))
@@ -109,7 +112,7 @@ export class ListField<T extends Field> extends BasicField<T[]> {
ToJson(): { type: Types, data: string[], _id: string } {
return {
type: Types.List,
- data: this._proxies,
+ data: this._proxies || [],
_id: this.Id
}
}
diff --git a/src/fields/PDFField.ts b/src/fields/PDFField.ts
index f3a009001..b6625387e 100644
--- a/src/fields/PDFField.ts
+++ b/src/fields/PDFField.ts
@@ -22,10 +22,10 @@ export class PDFField extends BasicField<URL> {
return `new PDFField("${this.Data}")`;
}
- ToJson(): { type: Types, data: URL, _id: string } {
+ ToJson(): { type: Types, data: string, _id: string } {
return {
type: Types.PDF,
- data: this.Data,
+ data: this.Data.href,
_id: this.Id
}
}
diff --git a/src/fields/TupleField.ts b/src/fields/TupleField.ts
new file mode 100644
index 000000000..e2162c751
--- /dev/null
+++ b/src/fields/TupleField.ts
@@ -0,0 +1,59 @@
+import { action, IArrayChange, IArraySplice, IObservableArray, observe, observable, Lambda } from "mobx";
+import { Server } from "../client/Server";
+import { UndoManager } from "../client/util/UndoManager";
+import { Types } from "../server/Message";
+import { BasicField } from "./BasicField";
+import { Field, FieldId } from "./Field";
+
+export class TupleField<T, U> extends BasicField<[T, U]> {
+ constructor(data: [T, U], id?: FieldId, save: boolean = true) {
+ super(data, save, id);
+ if (save) {
+ Server.UpdateField(this);
+ }
+ this.observeTuple();
+ }
+
+ private observeDisposer: Lambda | undefined;
+ private observeTuple(): void {
+ this.observeDisposer = observe(this.Data as (T | U)[] as IObservableArray<T | U>, (change: IArrayChange<T | U> | IArraySplice<T | U>) => {
+ if (change.type === "update") {
+ UndoManager.AddEvent({
+ undo: () => this.Data[change.index] = change.oldValue,
+ redo: () => this.Data[change.index] = change.newValue
+ })
+ Server.UpdateField(this);
+ } else {
+ throw new Error("Why are you messing with the length of a tuple, huh?");
+ }
+ });
+ }
+
+ protected setData(value: [T, U]) {
+ if (this.observeDisposer) {
+ this.observeDisposer()
+ }
+ this.data = observable(value) as (T | U)[] as [T, U];
+ this.observeTuple();
+ }
+
+ UpdateFromServer(values: [T, U]) {
+ this.setData(values);
+ }
+
+ ToScriptString(): string {
+ return `new TupleField([${this.Data[0], this.Data[1]}])`;
+ }
+
+ Copy(): Field {
+ return new TupleField<T, U>(this.Data);
+ }
+
+ ToJson(): { type: Types, data: [T, U], _id: string } {
+ return {
+ type: Types.Tuple,
+ data: this.Data,
+ _id: this.Id
+ }
+ }
+} \ No newline at end of file
diff --git a/src/fields/VideoField.ts b/src/fields/VideoField.ts
new file mode 100644
index 000000000..626e4ec83
--- /dev/null
+++ b/src/fields/VideoField.ts
@@ -0,0 +1,30 @@
+import { BasicField } from "./BasicField";
+import { Field, FieldId } from "./Field";
+import { Types } from "../server/Message";
+
+export class VideoField extends BasicField<URL> {
+ constructor(data: URL | undefined = undefined, id?: FieldId, save: boolean = true) {
+ super(data == undefined ? new URL("http://techslides.com/demos/sample-videos/small.mp4") : data, save, id);
+ }
+
+ toString(): string {
+ return this.Data.href;
+ }
+
+ ToScriptString(): string {
+ return `new VideoField("${this.Data}")`;
+ }
+
+ Copy(): Field {
+ return new VideoField(this.Data);
+ }
+
+ ToJson(): { type: Types, data: string, _id: string } {
+ return {
+ type: Types.Video,
+ data: this.Data.href,
+ _id: this.Id
+ }
+ }
+
+} \ No newline at end of file
diff --git a/src/fields/WebField.ts b/src/fields/WebField.ts
index 8f945d686..6c4de5000 100644
--- a/src/fields/WebField.ts
+++ b/src/fields/WebField.ts
@@ -19,10 +19,10 @@ export class WebField extends BasicField<URL> {
return new WebField(this.Data);
}
- ToJson(): { type: Types, data: URL, _id: string } {
+ ToJson(): { type: Types, data: string, _id: string } {
return {
type: Types.Web,
- data: this.Data,
+ data: this.Data.href,
_id: this.Id
}
}
diff --git a/src/mobile/ImageUpload.scss b/src/mobile/ImageUpload.scss
new file mode 100644
index 000000000..d0b7d4e41
--- /dev/null
+++ b/src/mobile/ImageUpload.scss
@@ -0,0 +1,13 @@
+.imgupload_cont {
+ height: 100vh;
+ width: 100vw;
+ align-content: center;
+ .button_file {
+ text-align: center;
+ height: 50%;
+ width: 50%;
+ background-color: paleturquoise;
+ color: grey;
+ font-size: 3em;
+ }
+} \ No newline at end of file
diff --git a/src/mobile/ImageUpload.tsx b/src/mobile/ImageUpload.tsx
new file mode 100644
index 000000000..ae48dd2c6
--- /dev/null
+++ b/src/mobile/ImageUpload.tsx
@@ -0,0 +1,77 @@
+import * as ReactDOM from 'react-dom';
+import * as rp from 'request-promise';
+import { Documents } from '../client/documents/Documents';
+import { Server } from '../client/Server';
+import { Document } from '../fields/Document';
+import { KeyStore } from '../fields/KeyStore';
+import { ListField } from '../fields/ListField';
+import { RouteStore } from '../server/RouteStore';
+import { ServerUtils } from '../server/ServerUtil';
+import "./ImageUpload.scss";
+import React = require('react');
+
+
+
+
+// const onPointerDown = (e: React.TouchEvent) => {
+// let imgInput = document.getElementById("input_image_file");
+// if (imgInput) {
+// imgInput.click();
+// }
+// }
+
+const onFileLoad = (file: any) => {
+ let imgPrev = document.getElementById("img_preview")
+ if (imgPrev) {
+ let files: File[] = file.target.files;
+ if (files.length != 0) {
+ console.log(files[0]);
+ let formData = new FormData();
+ formData.append("file", files[0]);
+
+ const upload = window.location.origin + "/upload"
+ fetch(upload, {
+ method: 'POST',
+ body: formData
+ }).then((res: Response) => {
+ return res.json()
+ }).then(json => {
+ json.map((file: any) => {
+ let path = window.location.origin + file
+ var doc: Document = Documents.ImageDocument(path, { nativeWidth: 200, width: 200 })
+
+ rp.get(ServerUtils.prepend(RouteStore.getUserDocumentId)).then(res => {
+ if (res) {
+ return Server.GetField(res);
+ }
+ throw new Error("No user id returned");
+ }).then(field => {
+ if (field instanceof Document) {
+ return field.GetTAsync(KeyStore.OptionalRightCollection, Document)
+ }
+ }).then(pending => {
+ if (pending) {
+ pending.GetOrCreateAsync(KeyStore.Data, ListField, list => {
+ list.Data.push(doc);
+ })
+ }
+ });
+
+ // console.log(window.location.origin + file[0])
+
+ //imgPrev.setAttribute("src", window.location.origin + files[0].name)
+ })
+ })
+ }
+ }
+}
+
+ReactDOM.render((
+ <div className="imgupload_cont">
+ {/* <button className = "button_file" = {onPointerDown}> Open Image </button> */}
+ <input type="file" accept="image/*" onChange={onFileLoad} className="input_file" id="input_image_file"></input>
+ <img id="img_preview" src=""></img>
+ <div id="message" />
+ </div>),
+ document.getElementById('root')
+); \ No newline at end of file
diff --git a/src/fields/KVPField b/src/mobile/InkControls.tsx
index e69de29bb..e69de29bb 100644
--- a/src/fields/KVPField
+++ b/src/mobile/InkControls.tsx
diff --git a/src/server/Message.ts b/src/server/Message.ts
index 5e97a5edf..05ae0f19a 100644
--- a/src/server/Message.ts
+++ b/src/server/Message.ts
@@ -45,7 +45,7 @@ export class GetFieldArgs {
}
export enum Types {
- Number, List, Key, Image, Web, Document, Text, RichText, DocumentReference, Html, Ink, PDF
+ Number, List, Key, Image, Web, Document, Text, RichText, DocumentReference, Html, Video, Audio, Ink, PDF, Tuple, HistogramOp
}
export class DocumentTransfer implements Transferable {
diff --git a/src/server/RouteStore.ts b/src/server/RouteStore.ts
new file mode 100644
index 000000000..fdf5b6a5c
--- /dev/null
+++ b/src/server/RouteStore.ts
@@ -0,0 +1,30 @@
+// PREPEND ALL ROUTES WITH FORWARD SLASHES!
+
+export enum RouteStore {
+ // GENERAL
+ root = "/",
+ home = "/home",
+ corsProxy = "/corsProxy",
+ delete = "/delete",
+ deleteAll = "/deleteAll",
+
+ // UPLOAD AND STATIC FILE SERVING
+ public = "/public",
+ upload = "/upload",
+ images = "/images",
+
+ // USER AND WORKSPACES
+ getCurrUser = "/getCurrentUser",
+ getUserDocumentId = "/getUserDocumentId",
+ updateCursor = "/updateCursor",
+
+ openDocumentWithId = "/doc/:docId",
+
+ // AUTHENTICATION
+ signup = "/signup",
+ login = "/login",
+ logout = "/logout",
+ forgot = "/forgotpassword",
+ reset = "/reset/:token",
+
+} \ No newline at end of file
diff --git a/src/server/ServerUtil.ts b/src/server/ServerUtil.ts
index 3b9d14891..98a7a1451 100644
--- a/src/server/ServerUtil.ts
+++ b/src/server/ServerUtil.ts
@@ -1,19 +1,29 @@
-import {HtmlField} from '../fields/HtmlField';
-import {InkField} from '../fields/InkField';
-import {PDFField} from '../fields/PDFField';
-import {WebField} from '../fields/WebField';
-import {Utils} from '../Utils';
-import {Document} from './../fields/Document';
-import {Field} from './../fields/Field';
-import {ImageField} from './../fields/ImageField';
-import {Key} from './../fields/Key';
-import {ListField} from './../fields/ListField';
-import {NumberField} from './../fields/NumberField';
-import {RichTextField} from './../fields/RichTextField';
-import {TextField} from './../fields/TextField';
-import {Types} from './Message';
+
+import { Field } from './../fields/Field';
+import { TextField } from './../fields/TextField';
+import { NumberField } from './../fields/NumberField';
+import { RichTextField } from './../fields/RichTextField';
+import { Key } from './../fields/Key';
+import { ImageField } from './../fields/ImageField';
+import { ListField } from './../fields/ListField';
+import { Document } from './../fields/Document';
+import { Server } from './../client/Server';
+import { Types } from './Message';
+import { Utils } from '../Utils';
+import { HtmlField } from '../fields/HtmlField';
+import { WebField } from '../fields/WebField';
+import { AudioField } from '../fields/AudioField';
+import { VideoField } from '../fields/VideoField';
+import { InkField } from '../fields/InkField';
+import { PDFField } from '../fields/PDFField';
+import { TupleField } from '../fields/TupleField';
+import { HistogramField } from '../client/northstar/dash-fields/HistogramField';
+
+
export class ServerUtils {
+ public static prepend(extension: string): string { return window.location.origin + extension; }
+
public static FromJson(json: any): Field {
let obj = json
let data: any = obj.data
@@ -40,10 +50,18 @@ export class ServerUtils {
return new Key(data, id, false)
case Types.Image:
return new ImageField(new URL(data), id, false)
+ case Types.HistogramOp:
+ return HistogramField.FromJson(id, data);
case Types.PDF:
return new PDFField(new URL(data), id, false)
case Types.List:
return ListField.FromJson(id, data)
+ case Types.Audio:
+ return new AudioField(new URL(data), id, false)
+ case Types.Video:
+ return new VideoField(new URL(data), id, false)
+ case Types.Tuple:
+ return new TupleField(data, id, false);
case Types.Ink:
return InkField.FromJson(id, data);
case Types.Document:
diff --git a/src/server/authentication/config/passport.ts b/src/server/authentication/config/passport.ts
index 05f6c3133..b6fe15655 100644
--- a/src/server/authentication/config/passport.ts
+++ b/src/server/authentication/config/passport.ts
@@ -2,8 +2,9 @@ import * as passport from 'passport'
import * as passportLocal from 'passport-local';
import * as mongodb from 'mongodb';
import * as _ from "lodash";
-import { default as User } from '../models/User';
+import { default as User } from '../models/user_model';
import { Request, Response, NextFunction } from "express";
+import { RouteStore } from '../../RouteStore';
const LocalStrategy = passportLocal.Strategy;
@@ -18,7 +19,7 @@ passport.deserializeUser<any, any>((id, done) => {
});
// AUTHENTICATE JUST WITH EMAIL AND PASSWORD
-passport.use(new LocalStrategy({ usernameField: 'email' }, (email, password, done) => {
+passport.use(new LocalStrategy({ usernameField: 'email', passReqToCallback: true }, (req, email, password, done) => {
User.findOne({ email: email.toLowerCase() }, (error: any, user: any) => {
if (error) return done(error);
if (!user) return done(undefined, false, { message: "Invalid email or password" }) // invalid email
@@ -35,7 +36,7 @@ export let isAuthenticated = (req: Request, res: Response, next: NextFunction) =
if (req.isAuthenticated()) {
return next();
}
- return res.redirect("/login");
+ return res.redirect(RouteStore.login);
}
export let isAuthorized = (req: Request, res: Response, next: NextFunction) => {
diff --git a/src/server/authentication/controllers/WorkspacesMenu.css b/src/server/authentication/controllers/WorkspacesMenu.css
new file mode 100644
index 000000000..b89039965
--- /dev/null
+++ b/src/server/authentication/controllers/WorkspacesMenu.css
@@ -0,0 +1,3 @@
+.ids:hover {
+ color: darkblue;
+} \ No newline at end of file
diff --git a/src/server/authentication/controllers/WorkspacesMenu.tsx b/src/server/authentication/controllers/WorkspacesMenu.tsx
new file mode 100644
index 000000000..8e14cf98e
--- /dev/null
+++ b/src/server/authentication/controllers/WorkspacesMenu.tsx
@@ -0,0 +1,89 @@
+import * as React from 'react';
+import { observable, action, configure, reaction, computed, ObservableMap, runInAction } from 'mobx';
+import { observer } from "mobx-react";
+import './WorkspacesMenu.css'
+import { Document } from '../../../fields/Document';
+import { EditableView } from '../../../client/views/EditableView';
+import { KeyStore } from '../../../fields/KeyStore';
+
+export interface WorkspaceMenuProps {
+ active: Document | undefined;
+ open: (workspace: Document) => void;
+ new: () => void;
+ allWorkspaces: Document[];
+ isShown: () => boolean;
+ toggle: () => void;
+}
+
+@observer
+export class WorkspacesMenu extends React.Component<WorkspaceMenuProps> {
+ constructor(props: WorkspaceMenuProps) {
+ super(props);
+ this.addNewWorkspace = this.addNewWorkspace.bind(this);
+ }
+
+ @action
+ addNewWorkspace() {
+ this.props.new();
+ this.props.toggle();
+ }
+
+ render() {
+ return (
+ <div
+ style={{
+ width: "auto",
+ maxHeight: '200px',
+ overflow: 'scroll',
+ borderRadius: 5,
+ position: "absolute",
+ top: 78,
+ left: this.props.isShown() ? 11 : -500,
+ background: "white",
+ border: "black solid 2px",
+ transition: "all 1s ease",
+ zIndex: 15,
+ padding: 10,
+ paddingRight: 12,
+ }}>
+ <img
+ src="https://bit.ly/2IBBkxk"
+ style={{
+ width: 20,
+ height: 20,
+ marginTop: 3,
+ marginLeft: 3,
+ marginBottom: 3,
+ cursor: "grab"
+ }}
+ onClick={this.addNewWorkspace}
+ />
+ {this.props.allWorkspaces.map((s, i) =>
+ <div
+ key={s.Id}
+ onContextMenu={(e) => {
+ e.preventDefault();
+ this.props.open(s);
+ }}
+ style={{
+ marginTop: 10,
+ color: s === this.props.active ? "red" : "black"
+ }}
+ >
+ <span>{i + 1} - </span>
+ <EditableView
+ display={"inline"}
+ GetValue={() => { return s.Title }}
+ SetValue={(title: string): boolean => {
+ s.SetText(KeyStore.Title, title);
+ return true;
+ }}
+ contents={s.Title}
+ height={20}
+ />
+ </div>
+ )}
+ </div>
+ );
+ }
+} \ No newline at end of file
diff --git a/src/server/authentication/controllers/user.ts b/src/server/authentication/controllers/user.ts
deleted file mode 100644
index f74ff9039..000000000
--- a/src/server/authentication/controllers/user.ts
+++ /dev/null
@@ -1,107 +0,0 @@
-import { default as User, UserModel, AuthToken } from "../models/User";
-import { Request, Response, NextFunction } from "express";
-import * as passport from "passport";
-import { IVerifyOptions } from "passport-local";
-import "../config/passport";
-import * as request from "express-validator";
-const flash = require("express-flash");
-import * as session from "express-session";
-import * as pug from 'pug';
-
-/**
- * GET /signup
- * Signup page.
- */
-export let getSignup = (req: Request, res: Response) => {
- if (req.user) {
- return res.redirect("/");
- }
- res.render("signup.pug", {
- title: "Sign Up"
- });
-};
-
-/**
- * POST /signup
- * Create a new local account.
- */
-export let postSignup = (req: Request, res: Response, next: NextFunction) => {
- req.assert("email", "Email is not valid").isEmail();
- req.assert("password", "Password must be at least 4 characters long").len({ min: 4 });
- req.assert("confirmPassword", "Passwords do not match").equals(req.body.password);
- req.sanitize("email").normalizeEmail({ gmail_remove_dots: false });
-
- const errors = req.validationErrors();
-
- if (errors) {
- req.flash("errors", "Unable to facilitate sign up. Please try again.");
- return res.redirect("/signup");
- }
-
- const user = new User({
- email: req.body.email,
- password: req.body.password
- });
-
- User.findOne({ email: req.body.email }, (err, existingUser) => {
- if (err) { return next(err); }
- if (existingUser) {
- req.flash("errors", "Account with that email address already exists.");
- return res.redirect("/signup");
- }
- user.save((err) => {
- if (err) { return next(err); }
- req.logIn(user, (err) => {
- if (err) {
- return next(err);
- }
- res.redirect("/");
- });
- });
- });
-};
-
-
-/**
- * GET /login
- * Login page.
- */
-export let getLogin = (req: Request, res: Response) => {
- if (req.user) {
- return res.redirect("/");
- }
- res.send("<p>dear lord please render</p>");
- // res.render("account/login", {
- // title: "Login"
- // });
-};
-
-/**
- * POST /login
- * Sign in using email and password.
- */
-export let postLogin = (req: Request, res: Response, next: NextFunction) => {
- req.assert("email", "Email is not valid").isEmail();
- req.assert("password", "Password cannot be blank").notEmpty();
- req.sanitize("email").normalizeEmail({ gmail_remove_dots: false });
-
- const errors = req.validationErrors();
-
- if (errors) {
- req.flash("errors", "Unable to login at this time. Please try again.");
- return res.redirect("/login");
- }
-
- passport.authenticate("local", (err: Error, user: UserModel, info: IVerifyOptions) => {
- if (err) { return next(err); }
- if (!user) {
- req.flash("errors", info.message);
- return res.redirect("/login");
- }
- req.logIn(user, (err) => {
- if (err) { return next(err); }
- req.flash("success", "Success! You are logged in.");
- res.redirect("/");
- });
- })(req, res, next);
-}; \ No newline at end of file
diff --git a/src/server/authentication/controllers/user_controller.ts b/src/server/authentication/controllers/user_controller.ts
new file mode 100644
index 000000000..e365b8dce
--- /dev/null
+++ b/src/server/authentication/controllers/user_controller.ts
@@ -0,0 +1,264 @@
+import { default as User, DashUserModel, AuthToken } from "../models/user_model";
+import { Request, Response, NextFunction } from "express";
+import * as passport from "passport";
+import { IVerifyOptions } from "passport-local";
+import "../config/passport";
+import * as request from "express-validator";
+const flash = require("express-flash");
+import * as session from "express-session";
+import * as pug from 'pug';
+import * as async from 'async';
+import * as nodemailer from 'nodemailer';
+import c = require("crypto");
+import { RouteStore } from "../../RouteStore";
+import { Utils } from "../../../Utils";
+
+/**
+ * GET /signup
+ * Directs user to the signup page
+ * modeled by signup.pug in views
+ */
+export let getSignup = (req: Request, res: Response) => {
+ if (req.user) {
+ let user = req.user;
+ return res.redirect(RouteStore.home);
+ }
+ res.render("signup.pug", {
+ title: "Sign Up",
+ user: req.user,
+ });
+};
+
+/**
+ * POST /signup
+ * Create a new local account.
+ */
+export let postSignup = (req: Request, res: Response, next: NextFunction) => {
+ req.assert("email", "Email is not valid").isEmail();
+ req.assert("password", "Password must be at least 4 characters long").len({ min: 4 });
+ req.assert("confirmPassword", "Passwords do not match").equals(req.body.password);
+ req.sanitize("email").normalizeEmail({ gmail_remove_dots: false });
+
+ const errors = req.validationErrors();
+
+ if (errors) {
+ res.render("signup.pug", {
+ title: "Sign Up",
+ user: req.user,
+ });
+ return res.redirect(RouteStore.signup);
+ }
+
+ const email = req.body.email;
+ const password = req.body.password;
+
+ const user = new User({
+ email,
+ password,
+ userDocumentId: Utils.GenerateGuid()
+ });
+
+ User.findOne({ email }, (err, existingUser) => {
+ if (err) { return next(err); }
+ if (existingUser) {
+ return res.redirect(RouteStore.login);
+ }
+ user.save((err) => {
+ if (err) { return next(err); }
+ req.logIn(user, (err) => {
+ if (err) {
+ return next(err);
+ }
+ res.redirect(RouteStore.home);
+ });
+ });
+ });
+
+};
+
+
+/**
+ * GET /login
+ * Login page.
+ */
+export let getLogin = (req: Request, res: Response) => {
+ if (req.user) {
+ return res.redirect(RouteStore.home);
+ }
+ res.render("login.pug", {
+ title: "Log In",
+ user: req.user
+ });
+};
+
+/**
+ * POST /login
+ * Sign in using email and password.
+ * On failure, redirect to signup page
+ */
+export let postLogin = (req: Request, res: Response, next: NextFunction) => {
+ req.assert("email", "Email is not valid").isEmail();
+ req.assert("password", "Password cannot be blank").notEmpty();
+ req.sanitize("email").normalizeEmail({ gmail_remove_dots: false });
+
+ const errors = req.validationErrors();
+
+ if (errors) {
+ req.flash("errors", "Unable to login at this time. Please try again.");
+ return res.redirect(RouteStore.signup);
+ }
+
+ passport.authenticate("local", (err: Error, user: DashUserModel, info: IVerifyOptions) => {
+ if (err) { return next(err); }
+ if (!user) {
+ return res.redirect(RouteStore.signup);
+ }
+ req.logIn(user, (err) => {
+ if (err) { return next(err); }
+ res.redirect(RouteStore.home);
+ });
+ })(req, res, next);
+};
+
+/**
+ * GET /logout
+ * Invokes the logout function on the request
+ * and destroys the user's current session.
+ */
+export let getLogout = (req: Request, res: Response) => {
+ req.logout();
+ const sess = req.session;
+ if (sess) {
+ sess.destroy((err) => { if (err) { console.log(err); } });
+ }
+ res.redirect(RouteStore.login);
+}
+
+export let getForgot = function (req: Request, res: Response) {
+ res.render("forgot.pug", {
+ title: "Recover Password",
+ user: req.user,
+ });
+}
+
+export let postForgot = function (req: Request, res: Response, next: NextFunction) {
+ const email = req.body.email;
+ async.waterfall([
+ function (done: any) {
+ let token: string;
+ c.randomBytes(20, function (err: any, buffer: Buffer) {
+ if (err) {
+ done(null);
+ return;
+ }
+ done(null, buffer.toString('hex'));
+ })
+ },
+ function (token: string, done: any) {
+ User.findOne({ email }, function (err, user: DashUserModel) {
+ if (!user) {
+ // NO ACCOUNT WITH SUBMITTED EMAIL
+ return res.redirect(RouteStore.forgot);
+ }
+ user.passwordResetToken = token;
+ user.passwordResetExpires = new Date(Date.now() + 3600000); // 1 HOUR
+ user.save(function (err: any) {
+ done(null, token, user);
+ });
+ });
+ },
+ function (token: Uint16Array, user: DashUserModel, done: any) {
+ const smtpTransport = nodemailer.createTransport({
+ service: 'Gmail',
+ auth: {
+ user: 'brownptcdash@gmail.com',
+ pass: 'browngfx1'
+ }
+ });
+ const mailOptions = {
+ to: user.email,
+ from: 'brownptcdash@gmail.com',
+ subject: 'Dash Password Reset',
+ text: 'You are receiving this because you (or someone else) have requested the reset of the password for your account.\n\n' +
+ 'Please click on the following link, or paste this into your browser to complete the process:\n\n' +
+ 'http://' + req.headers.host + '/reset/' + token + '\n\n' +
+ 'If you did not request this, please ignore this email and your password will remain unchanged.\n'
+ };
+ smtpTransport.sendMail(mailOptions, function (err) {
+ // req.flash('info', 'An e-mail has been sent to ' + user.email + ' with further instructions.');
+ done(null, err, 'done');
+ });
+ }
+ ], function (err) {
+ if (err) return next(err);
+ res.redirect(RouteStore.forgot);
+ })
+}
+
+export let getReset = function (req: Request, res: Response) {
+ User.findOne({ passwordResetToken: req.params.token, passwordResetExpires: { $gt: Date.now() } }, function (err, user: DashUserModel) {
+ if (!user || err) {
+ return res.redirect(RouteStore.forgot);
+ }
+ res.render("reset.pug", {
+ title: "Reset Password",
+ user: req.user,
+ });
+ });
+}
+
+export let postReset = function (req: Request, res: Response) {
+ async.waterfall([
+ function (done: any) {
+ User.findOne({ passwordResetToken: req.params.token, passwordResetExpires: { $gt: Date.now() } }, function (err, user: DashUserModel) {
+ if (!user || err) {
+ return res.redirect('back');
+ }
+
+ req.assert("password", "Password must be at least 4 characters long").len({ min: 4 });
+ req.assert("confirmPassword", "Passwords do not match").equals(req.body.password);
+
+ if (req.validationErrors()) {
+ return res.redirect('back');
+ }
+
+ user.password = req.body.password;
+ user.passwordResetToken = undefined;
+ user.passwordResetExpires = undefined;
+
+ user.save(function (err) {
+ if (err) {
+ return res.redirect(RouteStore.login);
+ }
+ req.logIn(user, function (err) {
+ if (err) {
+ return;
+ }
+ });
+ done(null, user);
+ });
+ });
+ },
+ function (user: DashUserModel, done: any) {
+ const smtpTransport = nodemailer.createTransport({
+ service: 'Gmail',
+ auth: {
+ user: 'brownptcdash@gmail.com',
+ pass: 'browngfx1'
+ }
+ });
+ const mailOptions = {
+ to: user.email,
+ from: 'brownptcdash@gmail.com',
+ subject: 'Your password has been changed',
+ text: 'Hello,\n\n' +
+ 'This is a confirmation that the password for your account ' + user.email + ' has just been changed.\n'
+ };
+ smtpTransport.sendMail(mailOptions, function (err) {
+ done(null, err);
+ });
+ }
+ ], function (err) {
+ res.redirect(RouteStore.login);
+ });
+} \ No newline at end of file
diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts
new file mode 100644
index 000000000..0ac85b446
--- /dev/null
+++ b/src/server/authentication/models/current_user_utils.ts
@@ -0,0 +1,102 @@
+import { DashUserModel } from "./user_model";
+import * as rp from 'request-promise';
+import { RouteStore } from "../../RouteStore";
+import { ServerUtils } from "../../ServerUtil";
+import { Server } from "../../../client/Server";
+import { Document } from "../../../fields/Document";
+import { KeyStore } from "../../../fields/KeyStore";
+import { ListField } from "../../../fields/ListField";
+import { Documents } from "../../../client/documents/Documents";
+import { Schema, Attribute, AttributeGroup, Catalog } from "../../../client/northstar/model/idea/idea";
+import { observable, computed, action } from "mobx";
+import { ArrayUtil } from "../../../client/northstar/utils/ArrayUtil";
+
+export class CurrentUserUtils {
+ private static curr_email: string;
+ private static curr_id: string;
+ private static user_document: Document;
+ //TODO tfs: these should be temporary...
+ private static mainDocId: string | undefined;
+ @observable private static catalog?: Catalog;
+
+ public static get email(): string {
+ return this.curr_email;
+ }
+
+ public static get id(): string {
+ return this.curr_id;
+ }
+
+ public static get UserDocument(): Document {
+ return this.user_document;
+ }
+
+ public static get MainDocId(): string | undefined {
+ return this.mainDocId;
+ }
+
+ public static set MainDocId(id: string | undefined) {
+ this.mainDocId = id;
+ }
+
+ @computed public static get NorthstarDBCatalog(): Catalog | undefined {
+ return this.catalog;
+ }
+ public static set NorthstarDBCatalog(ctlog: Catalog | undefined) {
+ this.catalog = ctlog;
+ }
+ public static GetNorthstarSchema(name: string): Schema | undefined {
+ return !this.catalog || !this.catalog.schemas ? undefined :
+ ArrayUtil.FirstOrDefault<Schema>(this.catalog.schemas, (s: Schema) => s.displayName === name);
+ }
+ public static GetAllNorthstarColumnAttributes(schema: Schema) {
+ if (!schema || !schema.rootAttributeGroup) {
+ return [];
+ }
+ const recurs = (attrs: Attribute[], g: AttributeGroup) => {
+ if (g.attributes) {
+ attrs.push.apply(attrs, g.attributes);
+ if (g.attributeGroups) {
+ g.attributeGroups.forEach(ng => recurs(attrs, ng));
+ }
+ }
+ };
+ const allAttributes: Attribute[] = new Array<Attribute>();
+ recurs(allAttributes, schema.rootAttributeGroup);
+ return allAttributes;
+ }
+
+ private static createUserDocument(id: string): Document {
+ let doc = new Document(id);
+
+ doc.Set(KeyStore.Workspaces, new ListField<Document>());
+ doc.Set(KeyStore.OptionalRightCollection, Documents.SchemaDocument([], { title: "Pending documents" }))
+ return doc;
+ }
+
+ public static loadCurrentUser(): Promise<any> {
+ let userPromise = rp.get(ServerUtils.prepend(RouteStore.getCurrUser)).then((response) => {
+ if (response) {
+ let obj = JSON.parse(response);
+ CurrentUserUtils.curr_id = obj.id as string;
+ CurrentUserUtils.curr_email = obj.email as string;
+ } else {
+ throw new Error("There should be a user! Why does Dash think there isn't one?")
+ }
+ });
+ let userDocPromise = rp.get(ServerUtils.prepend(RouteStore.getUserDocumentId)).then(id => {
+ if (id) {
+ return Server.GetField(id).then(field => {
+ if (field instanceof Document) {
+ this.user_document = field;
+ } else {
+ this.user_document = this.createUserDocument(id);
+ }
+ })
+ } else {
+ throw new Error("There should be a user id! Why does Dash think there isn't one?")
+ }
+ });
+ return Promise.all([userPromise, userDocPromise]);
+ }
+} \ No newline at end of file
diff --git a/src/server/authentication/models/User.ts b/src/server/authentication/models/user_model.ts
index 9752c4260..81580aad5 100644
--- a/src/server/authentication/models/User.ts
+++ b/src/server/authentication/models/user_model.ts
@@ -1,6 +1,5 @@
//@ts-ignore
import * as bcrypt from "bcrypt-nodejs";
-import * as crypto from "crypto";
//@ts-ignore
import * as mongoose from "mongoose";
var url = 'mongodb://localhost:27017/Dash'
@@ -16,12 +15,13 @@ mongoose.connection.on('error', function (error) {
mongoose.connection.on('disconnected', function () {
console.log('connection closed');
});
-export type UserModel = mongoose.Document & {
+export type DashUserModel = mongoose.Document & {
email: string,
password: string,
- passwordResetToken: string,
- passwordResetExpires: Date,
- tokens: AuthToken[],
+ passwordResetToken: string | undefined,
+ passwordResetExpires: Date | undefined,
+
+ userDocumentId: string;
profile: {
name: string,
@@ -47,10 +47,11 @@ const userSchema = new mongoose.Schema({
passwordResetToken: String,
passwordResetExpires: Date,
+ userDocumentId: String,
+
facebook: String,
twitter: String,
google: String,
- tokens: Array,
profile: {
name: String,
@@ -65,7 +66,7 @@ const userSchema = new mongoose.Schema({
* Password hash middleware.
*/
userSchema.pre("save", function save(next) {
- const user = this as UserModel;
+ const user = this as DashUserModel;
if (!user.isModified("password")) { return next(); }
bcrypt.genSalt(10, (err, salt) => {
if (err) { return next(err); }
@@ -77,7 +78,7 @@ userSchema.pre("save", function save(next) {
});
});
-const comparePassword: comparePasswordFunction = function (this: UserModel, candidatePassword, cb) {
+const comparePassword: comparePasswordFunction = function (this: DashUserModel, candidatePassword, cb) {
bcrypt.compare(candidatePassword, this.password, (err: mongoose.Error, isMatch: boolean) => {
cb(err, isMatch);
});
diff --git a/src/server/database.ts b/src/server/database.ts
index 07c5819ab..a42d29aac 100644
--- a/src/server/database.ts
+++ b/src/server/database.ts
@@ -16,11 +16,20 @@ export class Database {
})
}
- public update(id: string, value: any) {
+ public update(id: string, value: any, callback: () => void) {
if (this.db) {
let collection = this.db.collection('documents');
- collection.update({ _id: id }, { $set: value }, {
+ collection.updateOne({ _id: id }, { $set: value }, {
upsert: true
+ }, (err, res) => {
+ if (err) {
+ console.log(err.message);
+ console.log(err.errmsg);
+ }
+ if (res) {
+ console.log(JSON.stringify(res.result));
+ }
+ callback()
});
}
}
@@ -32,11 +41,13 @@ export class Database {
}
}
- public deleteAll() {
- if (this.db) {
- let collection = this.db.collection('documents');
- collection.deleteMany({});
- }
+ public deleteAll(collectionName: string = 'documents'): Promise<any> {
+ return new Promise(res => {
+ if (this.db) {
+ let collection = this.db.collection(collectionName);
+ collection.deleteMany({}, res);
+ }
+ })
}
public insert(kvpairs: any) {
@@ -70,6 +81,10 @@ export class Database {
let collection = this.db.collection('documents');
let cursor = collection.find({ _id: { "$in": ids } })
cursor.toArray((err, docs) => {
+ if (err) {
+ console.log(err.message);
+ console.log(err.errmsg);
+ }
fn(docs);
})
};
diff --git a/src/server/index.ts b/src/server/index.ts
index 4c2e09661..17d7432e0 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -4,64 +4,66 @@ import * as webpack from 'webpack'
import * as wdm from 'webpack-dev-middleware';
import * as whm from 'webpack-hot-middleware';
import * as path from 'path'
+import * as formidable from 'formidable'
import * as passport from 'passport';
-import { MessageStore, Message, SetFieldArgs, GetFieldArgs, Transferable } from "./Message";
+import { MessageStore, Transferable } from "./Message";
import { Client } from './Client';
import { Socket } from 'socket.io';
import { Utils } from '../Utils';
import { ObservableMap } from 'mobx';
import { FieldId, Field } from '../fields/Field';
import { Database } from './database';
-import { ServerUtils } from './ServerUtil';
-import { ObjectID } from 'mongodb';
-import { Document } from '../fields/Document';
import * as io from 'socket.io'
-import * as passportConfig from './authentication/config/passport';
-import { getLogin, postLogin, getSignup, postSignup } from './authentication/controllers/user';
+import { getLogin, postLogin, getSignup, postSignup, getLogout, postReset, getForgot, postForgot, getReset } from './authentication/controllers/user_controller';
const config = require('../../webpack.config');
const compiler = webpack(config);
const port = 1050; // default port to listen
-const serverPort = 1234;
+const serverPort = 4321;
import * as expressValidator from 'express-validator';
import expressFlash = require('express-flash');
+import flash = require('connect-flash');
import * as bodyParser from 'body-parser';
import * as session from 'express-session';
+import * as cookieParser from 'cookie-parser';
+import * as mobileDetect from 'mobile-detect';
import c = require("crypto");
const MongoStore = require('connect-mongo')(session);
const mongoose = require('mongoose');
-const bluebird = require('bluebird');
-import { performance } from 'perf_hooks'
+import { DashUserModel } from './authentication/models/user_model';
import * as fs from 'fs';
import * as request from 'request'
+import { RouteStore } from './RouteStore';
+import { exec } from 'child_process'
const download = (url: string, dest: fs.PathLike) => {
request.get(url).pipe(fs.createWriteStream(dest));
}
const mongoUrl = 'mongodb://localhost:27017/Dash';
-// mongoose.Promise = bluebird;
-mongoose.connect(mongoUrl)//.then(
-// () => { /** ready to use. The `mongoose.connect()` promise resolves to undefined. */ },
-// ).catch((err: any) => {
-// console.log("MongoDB connection error. Please make sure MongoDB is running. " + err);
-// process.exit();
-// });
+mongoose.connect(mongoUrl)
mongoose.connection.on('connected', function () {
console.log("connected");
})
-app.use(bodyParser.json());
-app.use(bodyParser.urlencoded({ extended: true }));
-app.use(expressValidator());
-app.use(expressFlash());
-app.use(require('express-session')({
- secret: `${c.randomBytes(64)}`,
+// SESSION MANAGEMENT AND AUTHENTICATION MIDDLEWARE
+// ORDER OF IMPORTS MATTERS
+
+app.use(cookieParser());
+app.use(session({
+ secret: "64d6866242d3b5a5503c675b32c9605e4e90478e9b77bcf2bc",
resave: true,
+ cookie: { maxAge: 7 * 24 * 60 * 60 * 1000 },
saveUninitialized: true,
store: new MongoStore({
url: 'mongodb://localhost:27017/Dash'
})
}));
+
+app.use(flash());
+app.use(expressFlash());
+app.use(bodyParser.json());
+app.use(bodyParser.urlencoded({ extended: true }));
+app.use(expressValidator());
app.use(passport.initialize());
app.use(passport.session());
app.use((req, res, next) => {
@@ -69,29 +71,163 @@ app.use((req, res, next) => {
next();
});
-app.get("/signup", getSignup);
-app.post("/signup", postSignup);
-app.get("/login", getLogin);
-app.post("/login", postLogin);
+app.get("/hello", (req, res) => {
+ res.send("<p>Hello</p>");
+})
+
+enum Method {
+ GET,
+ POST
+}
+
+/**
+ * Please invoke this function when adding a new route to Dash's server.
+ * It ensures that any requests leading to or containing user-sensitive information
+ * does not execute unless Passport authentication detects a user logged in.
+ * @param method whether or not the request is a GET or a POST
+ * @param handler the action to invoke, recieving a DashUserModel and, as expected, the Express.Request and Express.Response
+ * @param onRejection an optional callback invoked on return if no user is found to be logged in
+ * @param subscribers the forward slash prepended path names (reference and add to RouteStore.ts) that will all invoke the given @param handler
+ */
+function addSecureRoute(method: Method,
+ handler: (user: DashUserModel, res: express.Response, req: express.Request) => void,
+ onRejection: (res: express.Response) => any = (res) => res.redirect(RouteStore.logout),
+ ...subscribers: string[]
+) {
+ let abstracted = (req: express.Request, res: express.Response) => {
+ const dashUser: DashUserModel = req.user;
+ if (!dashUser) return onRejection(res);
+ handler(dashUser, res, req);
+ }
+ subscribers.forEach(route => {
+ switch (method) {
+ case Method.GET:
+ app.get(route, abstracted);
+ break;
+ case Method.POST:
+ app.post(route, abstracted);
+ break;
+ }
+ });
+}
+
+// STATIC FILE SERVING
let FieldStore: ObservableMap<FieldId, Field> = new ObservableMap();
-// define a route handler for the default home page
-app.get("/", (req, res) => {
- res.sendFile(path.join(__dirname, '../../deploy/index.html'));
+app.use(express.static(__dirname + RouteStore.public));
+app.use(RouteStore.images, express.static(__dirname + RouteStore.public))
+
+app.get("/pull", (req, res) => {
+ exec('"C:\\Program Files\\Git\\git-bash.exe" -c "git pull"', (err, stdout, stderr) => {
+ if (err) {
+ res.send(err.message);
+ return;
+ }
+ res.redirect("/");
+ })
});
-app.get("/hello", (req, res) => {
- res.send("<p>Hello</p>");
-})
+// GETTERS
+
+// anyone attempting to navigate to localhost at this port will
+// first have to login
+addSecureRoute(
+ Method.GET,
+ (user, res) => res.redirect(RouteStore.home),
+ undefined,
+ RouteStore.root
+);
+
+addSecureRoute(
+ Method.GET,
+ (user, res, req) => {
+ let detector = new mobileDetect(req.headers['user-agent'] || "");
+ if (detector.mobile() != null) {
+ res.sendFile(path.join(__dirname, '../../deploy/mobile/image.html'));
+ } else {
+ res.sendFile(path.join(__dirname, '../../deploy/index.html'));
+ }
+ },
+ undefined,
+ RouteStore.home,
+ RouteStore.openDocumentWithId
+);
+
+addSecureRoute(
+ Method.GET,
+ (user, res) => res.send(user.userDocumentId || ""),
+ undefined,
+ RouteStore.getUserDocumentId,
+);
+
+addSecureRoute(
+ Method.GET,
+ (user, res) => {
+ res.send(JSON.stringify({
+ id: user.id,
+ email: user.email
+ }));
+ },
+ undefined,
+ RouteStore.getCurrUser
+);
+
+// SETTERS
+
+addSecureRoute(
+ Method.POST,
+ (user, res, req) => {
+ let form = new formidable.IncomingForm()
+ form.uploadDir = __dirname + "/public/files/"
+ form.keepExtensions = true
+ // let path = req.body.path;
+ console.log("upload")
+ form.parse(req, (err, fields, files) => {
+ console.log("parsing")
+ let names: any[] = [];
+ for (const name in files) {
+ let file = files[name];
+ names.push(`/files/` + path.basename(file.path));
+ }
+ res.send(names);
+ });
+ },
+ undefined,
+ RouteStore.upload
+);
+
+// AUTHENTICATION
+
+// Sign Up
+app.get(RouteStore.signup, getSignup);
+app.post(RouteStore.signup, postSignup);
+
+// Log In
+app.get(RouteStore.login, getLogin);
+app.post(RouteStore.login, postLogin);
-app.use("/corsProxy", (req, res) => {
+// Log Out
+app.get(RouteStore.logout, getLogout);
+
+// FORGOT PASSWORD EMAIL HANDLING
+app.get(RouteStore.forgot, getForgot)
+app.post(RouteStore.forgot, postForgot)
+
+// RESET PASSWORD EMAIL HANDLING
+app.get(RouteStore.reset, getReset);
+app.post(RouteStore.reset, postReset);
+
+app.use(RouteStore.corsProxy, (req, res) => {
req.pipe(request(req.url.substring(1))).pipe(res);
});
-app.get("/delete", (req, res) => {
- deleteAll();
- res.redirect("/");
+app.get(RouteStore.delete, (req, res) => {
+ deleteFields().then(() => res.redirect(RouteStore.home));
+});
+
+app.get(RouteStore.deleteAll, (req, res) => {
+ deleteAll().then(() => res.redirect(RouteStore.home));
});
app.use(wdm(compiler, {
@@ -120,21 +256,25 @@ server.on("connection", function (socket: Socket) {
Utils.AddServerHandler(socket, MessageStore.SetField, (args) => setField(socket, args))
Utils.AddServerHandlerCallback(socket, MessageStore.GetField, getField)
Utils.AddServerHandlerCallback(socket, MessageStore.GetFields, getFields)
- Utils.AddServerHandler(socket, MessageStore.DeleteAll, deleteAll)
+ Utils.AddServerHandler(socket, MessageStore.DeleteAll, deleteFields)
})
+function deleteFields() {
+ return Database.Instance.deleteAll();
+}
+
function deleteAll() {
- Database.Instance.deleteAll();
+ return Database.Instance.deleteAll().then(() => {
+ return Database.Instance.deleteAll('sessions')
+ }).then(() => {
+ return Database.Instance.deleteAll('users')
+ });
}
function barReceived(guid: String) {
clients[guid.toString()] = new Client(guid.toString());
}
-function addDocument(document: Document) {
-
-}
-
function getField([id, callback]: [string, (result: any) => void]) {
Database.Instance.getDocument(id, (result: any) => {
if (result) {
@@ -151,8 +291,9 @@ function getFields([ids, callback]: [string[], (result: any) => void]) {
}
function setField(socket: Socket, newValue: Transferable) {
- Database.Instance.update(newValue._id, newValue)
- socket.broadcast.emit(MessageStore.SetField.Message, newValue)
+ Database.Instance.update(newValue._id, newValue, () => {
+ socket.broadcast.emit(MessageStore.SetField.Message, newValue);
+ })
}
server.listen(serverPort);
diff --git a/src/server/public/files/upload_e72669595eae4384a2a32196496f4f05.pdf b/src/server/public/files/upload_e72669595eae4384a2a32196496f4f05.pdf
new file mode 100644
index 000000000..8e58bfddd
--- /dev/null
+++ b/src/server/public/files/upload_e72669595eae4384a2a32196496f4f05.pdf
Binary files differ
diff --git a/src/typings/index.d.ts b/src/typings/index.d.ts
index e4a66f7f2..7939ae8be 100644
--- a/src/typings/index.d.ts
+++ b/src/typings/index.d.ts
@@ -1,322 +1,322 @@
/// <reference types="node" />
declare module '@react-pdf/renderer' {
- import * as React from 'react';
-
- namespace ReactPDF {
- interface Style {
- [property: string]: any;
- }
- interface Styles {
- [key: string]: Style;
- }
- type Orientation = 'portrait' | 'landscape';
-
- interface DocumentProps {
- title?: string;
- author?: string;
- subject?: string;
- keywords?: string;
- creator?: string;
- producer?: string;
- onRender?: () => any;
- }
-
- /**
- * This component represent the PDF document itself. It must be the root
- * of your tree element structure, and under no circumstances should it be
- * used as children of another react-pdf component. In addition, it should
- * only have childs of type <Page />.
- */
- class Document extends React.Component<DocumentProps> {}
-
- interface NodeProps {
- style?: Style | Style[];
- /**
- * Render component in all wrapped pages.
- * @see https://react-pdf.org/advanced#fixed-components
- */
- fixed?: boolean;
- /**
- * Force the wrapping algorithm to start a new page when rendering the
- * element.
- * @see https://react-pdf.org/advanced#page-breaks
- */
- break?: boolean;
- }
-
- interface PageProps extends NodeProps {
- /**
- * Enable page wrapping for this page.
- * @see https://react-pdf.org/components#page-wrapping
- */
- wrap?: boolean;
- debug?: boolean;
- size?: string | [number, number] | {width: number; height: number};
- orientation?: Orientation;
- ruler?: boolean;
- rulerSteps?: number;
- verticalRuler?: boolean;
- verticalRulerSteps?: number;
- horizontalRuler?: boolean;
- horizontalRulerSteps?: number;
- ref?: Page;
- }
-
- /**
- * Represents single page inside the PDF document, or a subset of them if
- * using the wrapping feature. A <Document /> can contain as many pages as
- * you want, but ensure not rendering a page inside any component besides
- * Document.
- */
- class Page extends React.Component<PageProps> {}
-
- interface ViewProps extends NodeProps {
- /**
- * Enable/disable page wrapping for element.
- * @see https://react-pdf.org/components#page-wrapping
- */
- wrap?: boolean;
- debug?: boolean;
- render?: (props: {pageNumber: number}) => React.ReactNode;
- children?: React.ReactNode;
- }
-
+ import * as React from 'react';
+
+ namespace ReactPDF {
+ interface Style {
+ [property: string]: any;
+ }
+ interface Styles {
+ [key: string]: Style;
+ }
+ type Orientation = 'portrait' | 'landscape';
+
+ interface DocumentProps {
+ title?: string;
+ author?: string;
+ subject?: string;
+ keywords?: string;
+ creator?: string;
+ producer?: string;
+ onRender?: () => any;
+ }
+
+ /**
+ * This component represent the PDF document itself. It must be the root
+ * of your tree element structure, and under no circumstances should it be
+ * used as children of another react-pdf component. In addition, it should
+ * only have childs of type <Page />.
+ */
+ class Document extends React.Component<DocumentProps> { }
+
+ interface NodeProps {
+ style?: Style | Style[];
/**
- * The most fundamental component for building a UI and is designed to be
- * nested inside other views and can have 0 to many children.
+ * Render component in all wrapped pages.
+ * @see https://react-pdf.org/advanced#fixed-components
*/
- class View extends React.Component<ViewProps> {}
-
- interface ImageProps extends NodeProps {
- debug?: boolean;
- src: string | {data: Buffer; format: 'png' | 'jpg'};
- cache?: boolean;
- }
-
+ fixed?: boolean;
/**
- * A React component for displaying network or local (Node only) JPG or
- * PNG images, as well as base64 encoded image strings.
+ * Force the wrapping algorithm to start a new page when rendering the
+ * element.
+ * @see https://react-pdf.org/advanced#page-breaks
*/
- class Image extends React.Component<ImageProps> {}
-
- interface TextProps extends NodeProps {
- /**
- * Enable/disable page wrapping for element.
- * @see https://react-pdf.org/components#page-wrapping
- */
- wrap?: boolean;
- debug?: boolean;
- render?: (
- props: {pageNumber: number; totalPages: number},
- ) => React.ReactNode;
- children?: React.ReactNode;
- /**
- * How much hyphenated breaks should be avoided.
- */
- hyphenationCallback?: number;
- }
-
+ break?: boolean;
+ }
+
+ interface PageProps extends NodeProps {
/**
- * A React component for displaying text. Text supports nesting of other
- * Text or Link components to create inline styling.
+ * Enable page wrapping for this page.
+ * @see https://react-pdf.org/components#page-wrapping
*/
- class Text extends React.Component<TextProps> {}
-
- interface LinkProps extends NodeProps {
- /**
- * Enable/disable page wrapping for element.
- * @see https://react-pdf.org/components#page-wrapping
- */
- wrap?: boolean;
- debug?: boolean;
- src: string;
- children?: React.ReactNode;
- }
-
+ wrap?: boolean;
+ debug?: boolean;
+ size?: string | [number, number] | { width: number; height: number };
+ orientation?: Orientation;
+ ruler?: boolean;
+ rulerSteps?: number;
+ verticalRuler?: boolean;
+ verticalRulerSteps?: number;
+ horizontalRuler?: boolean;
+ horizontalRulerSteps?: number;
+ ref?: Page;
+ }
+
+ /**
+ * Represents single page inside the PDF document, or a subset of them if
+ * using the wrapping feature. A <Document /> can contain as many pages as
+ * you want, but ensure not rendering a page inside any component besides
+ * Document.
+ */
+ class Page extends React.Component<PageProps> { }
+
+ interface ViewProps extends NodeProps {
/**
- * A React component for displaying an hyperlink. Link’s can be nested
- * inside a Text component, or being inside any other valid primitive.
+ * Enable/disable page wrapping for element.
+ * @see https://react-pdf.org/components#page-wrapping
*/
- class Link extends React.Component<LinkProps> {}
-
- interface NoteProps extends NodeProps {
- children: string;
- }
-
- class Note extends React.Component<NoteProps> {}
-
- interface BlobProviderParams {
- blob: Blob | null;
- url: string | null;
- loading: boolean;
- error: Error | null;
- }
- interface BlobProviderProps {
- document: React.ReactElement<DocumentProps>;
- children: (params: BlobProviderParams) => React.ReactNode;
- }
-
+ wrap?: boolean;
+ debug?: boolean;
+ render?: (props: { pageNumber: number }) => React.ReactNode;
+ children?: React.ReactNode;
+ }
+
+ /**
+ * The most fundamental component for building a UI and is designed to be
+ * nested inside other views and can have 0 to many children.
+ */
+ class View extends React.Component<ViewProps> { }
+
+ interface ImageProps extends NodeProps {
+ debug?: boolean;
+ src: string | { data: Buffer; format: 'png' | 'jpg' };
+ cache?: boolean;
+ }
+
+ /**
+ * A React component for displaying network or local (Node only) JPG or
+ * PNG images, as well as base64 encoded image strings.
+ */
+ class Image extends React.Component<ImageProps> { }
+
+ interface TextProps extends NodeProps {
/**
- * Easy and declarative way of getting document's blob data without
- * showing it on screen.
- * @see https://react-pdf.org/advanced#on-the-fly-rendering
- * @platform web
+ * Enable/disable page wrapping for element.
+ * @see https://react-pdf.org/components#page-wrapping
*/
- class BlobProvider extends React.Component<BlobProviderProps> {}
-
- interface PDFViewerProps {
- width?: number;
- height?: number;
- style?: Style | Style[];
- className?: string;
- children?: React.ReactElement<DocumentProps>;
- }
-
+ wrap?: boolean;
+ debug?: boolean;
+ render?: (
+ props: { pageNumber: number; totalPages: number },
+ ) => React.ReactNode;
+ children?: React.ReactNode;
/**
- * Iframe PDF viewer for client-side generated documents.
- * @platform web
+ * How much hyphenated breaks should be avoided.
*/
- class PDFViewer extends React.Component<PDFViewerProps> {}
-
- interface PDFDownloadLinkProps {
- document: React.ReactElement<DocumentProps>;
- fileName?: string;
- style?: Style | Style[];
- className?: string;
- children?:
- | React.ReactNode
- | ((params: BlobProviderParams) => React.ReactNode);
- }
-
+ hyphenationCallback?: number;
+ }
+
+ /**
+ * A React component for displaying text. Text supports nesting of other
+ * Text or Link components to create inline styling.
+ */
+ class Text extends React.Component<TextProps> { }
+
+ interface LinkProps extends NodeProps {
/**
- * Anchor tag to enable generate and download PDF documents on the fly.
- * @see https://react-pdf.org/advanced#on-the-fly-rendering
- * @platform web
+ * Enable/disable page wrapping for element.
+ * @see https://react-pdf.org/components#page-wrapping
*/
- class PDFDownloadLink extends React.Component<PDFDownloadLinkProps> {}
-
- interface EmojiSource {
- url: string;
- format: string;
- }
- interface RegisteredFont {
- src: string;
- loaded: boolean;
- loading: boolean;
- data: any;
- [key: string]: any;
- }
- type HyphenationCallback = (
- words: string[],
- glyphString: {[key: string]: any},
- ) => string[];
-
- const Font: {
- register: (
- src: string,
- options: {family: string; [key: string]: any},
- ) => void;
- getEmojiSource: () => EmojiSource;
- getRegisteredFonts: () => string[];
- registerEmojiSource: (emojiSource: EmojiSource) => void;
- registerHyphenationCallback: (
- hyphenationCallback: HyphenationCallback,
- ) => void;
- getHyphenationCallback: () => HyphenationCallback;
- getFont: (fontFamily: string) => RegisteredFont | undefined;
- load: (
- fontFamily: string,
- document: React.ReactElement<DocumentProps>,
- ) => Promise<void>;
- clear: () => void;
- reset: () => void;
- };
-
- const StyleSheet: {
- hairlineWidth: number;
- create: <TStyles>(styles: TStyles) => TStyles;
- resolve: (
- style: Style,
- container: {
- width: number;
- height: number;
- orientation: Orientation;
- },
- ) => Style;
- flatten: (...styles: Style[]) => Style;
- absoluteFillObject: {
- position: 'absolute';
- left: 0;
- right: 0;
- top: 0;
- bottom: 0;
- };
- };
-
- const version: any;
-
- const PDFRenderer: any;
-
- const createInstance: (
- element: {
- type: string;
- props: {[key: string]: any};
- },
- root?: any,
- ) => any;
-
- const pdf: (
+ wrap?: boolean;
+ debug?: boolean;
+ src: string;
+ children?: React.ReactNode;
+ }
+
+ /**
+ * A React component for displaying an hyperlink. Link’s can be nested
+ * inside a Text component, or being inside any other valid primitive.
+ */
+ class Link extends React.Component<LinkProps> { }
+
+ interface NoteProps extends NodeProps {
+ children: string;
+ }
+
+ class Note extends React.Component<NoteProps> { }
+
+ interface BlobProviderParams {
+ blob: Blob | null;
+ url: string | null;
+ loading: boolean;
+ error: Error | null;
+ }
+ interface BlobProviderProps {
+ document: React.ReactElement<DocumentProps>;
+ children: (params: BlobProviderParams) => React.ReactNode;
+ }
+
+ /**
+ * Easy and declarative way of getting document's blob data without
+ * showing it on screen.
+ * @see https://react-pdf.org/advanced#on-the-fly-rendering
+ * @platform web
+ */
+ class BlobProvider extends React.Component<BlobProviderProps> { }
+
+ interface PDFViewerProps {
+ width?: number;
+ height?: number;
+ style?: Style | Style[];
+ className?: string;
+ children?: React.ReactElement<DocumentProps>;
+ }
+
+ /**
+ * Iframe PDF viewer for client-side generated documents.
+ * @platform web
+ */
+ class PDFViewer extends React.Component<PDFViewerProps> { }
+
+ interface PDFDownloadLinkProps {
+ document: React.ReactElement<DocumentProps>;
+ fileName?: string;
+ style?: Style | Style[];
+ className?: string;
+ children?:
+ | React.ReactNode
+ | ((params: BlobProviderParams) => React.ReactNode);
+ }
+
+ /**
+ * Anchor tag to enable generate and download PDF documents on the fly.
+ * @see https://react-pdf.org/advanced#on-the-fly-rendering
+ * @platform web
+ */
+ class PDFDownloadLink extends React.Component<PDFDownloadLinkProps> { }
+
+ interface EmojiSource {
+ url: string;
+ format: string;
+ }
+ interface RegisteredFont {
+ src: string;
+ loaded: boolean;
+ loading: boolean;
+ data: any;
+ [key: string]: any;
+ }
+ type HyphenationCallback = (
+ words: string[],
+ glyphString: { [key: string]: any },
+ ) => string[];
+
+ const Font: {
+ register: (
+ src: string,
+ options: { family: string;[key: string]: any },
+ ) => void;
+ getEmojiSource: () => EmojiSource;
+ getRegisteredFonts: () => string[];
+ registerEmojiSource: (emojiSource: EmojiSource) => void;
+ registerHyphenationCallback: (
+ hyphenationCallback: HyphenationCallback,
+ ) => void;
+ getHyphenationCallback: () => HyphenationCallback;
+ getFont: (fontFamily: string) => RegisteredFont | undefined;
+ load: (
+ fontFamily: string,
document: React.ReactElement<DocumentProps>,
- ) => {
- isDirty: () => boolean;
- updateContainer: (document: React.ReactElement<any>) => void;
- toBuffer: () => NodeJS.ReadableStream;
- toBlob: () => Blob;
- toString: () => string;
+ ) => Promise<void>;
+ clear: () => void;
+ reset: () => void;
+ };
+
+ const StyleSheet: {
+ hairlineWidth: number;
+ create: <TStyles>(styles: TStyles) => TStyles;
+ resolve: (
+ style: Style,
+ container: {
+ width: number;
+ height: number;
+ orientation: Orientation;
+ },
+ ) => Style;
+ flatten: (...styles: Style[]) => Style;
+ absoluteFillObject: {
+ position: 'absolute';
+ left: 0;
+ right: 0;
+ top: 0;
+ bottom: 0;
};
-
- const renderToStream: (
- document: React.ReactElement<DocumentProps>,
- ) => NodeJS.ReadableStream;
-
- const renderToFile: (
- document: React.ReactElement<DocumentProps>,
- filePath: string,
- callback?: (output: NodeJS.ReadableStream, filePath: string) => any,
- ) => Promise<NodeJS.ReadableStream>;
-
- const render: typeof renderToFile;
- }
-
- const Document: typeof ReactPDF.Document;
- const Page: typeof ReactPDF.Page;
- const View: typeof ReactPDF.View;
- const Image: typeof ReactPDF.Image;
- const Text: typeof ReactPDF.Text;
- const Link: typeof ReactPDF.Link;
- const Note: typeof ReactPDF.Note;
- const Font: typeof ReactPDF.Font;
- const StyleSheet: typeof ReactPDF.StyleSheet;
- const createInstance: typeof ReactPDF.createInstance;
- const PDFRenderer: typeof ReactPDF.PDFRenderer;
- const version: typeof ReactPDF.version;
- const pdf: typeof ReactPDF.pdf;
-
- export default ReactPDF;
- export {
- Document,
- Page,
- View,
- Image,
- Text,
- Link,
- Note,
- Font,
- StyleSheet,
- createInstance,
- PDFRenderer,
- version,
- pdf,
};
- } \ No newline at end of file
+
+ const version: any;
+
+ const PDFRenderer: any;
+
+ const createInstance: (
+ element: {
+ type: string;
+ props: { [key: string]: any };
+ },
+ root?: any,
+ ) => any;
+
+ const pdf: (
+ document: React.ReactElement<DocumentProps>,
+ ) => {
+ isDirty: () => boolean;
+ updateContainer: (document: React.ReactElement<any>) => void;
+ toBuffer: () => NodeJS.ReadableStream;
+ toBlob: () => Blob;
+ toString: () => string;
+ };
+
+ const renderToStream: (
+ document: React.ReactElement<DocumentProps>,
+ ) => NodeJS.ReadableStream;
+
+ const renderToFile: (
+ document: React.ReactElement<DocumentProps>,
+ filePath: string,
+ callback?: (output: NodeJS.ReadableStream, filePath: string) => any,
+ ) => Promise<NodeJS.ReadableStream>;
+
+ const render: typeof renderToFile;
+ }
+
+ const Document: typeof ReactPDF.Document;
+ const Page: typeof ReactPDF.Page;
+ const View: typeof ReactPDF.View;
+ const Image: typeof ReactPDF.Image;
+ const Text: typeof ReactPDF.Text;
+ const Link: typeof ReactPDF.Link;
+ const Note: typeof ReactPDF.Note;
+ const Font: typeof ReactPDF.Font;
+ const StyleSheet: typeof ReactPDF.StyleSheet;
+ const createInstance: typeof ReactPDF.createInstance;
+ const PDFRenderer: typeof ReactPDF.PDFRenderer;
+ const version: typeof ReactPDF.version;
+ const pdf: typeof ReactPDF.pdf;
+
+ export default ReactPDF;
+ export {
+ Document,
+ Page,
+ View,
+ Image,
+ Text,
+ Link,
+ Note,
+ Font,
+ StyleSheet,
+ createInstance,
+ PDFRenderer,
+ version,
+ pdf,
+ };
+} \ No newline at end of file
diff --git a/views/forgot.pug b/views/forgot.pug
new file mode 100644
index 000000000..4036b49db
--- /dev/null
+++ b/views/forgot.pug
@@ -0,0 +1,22 @@
+
+extends ./layout
+
+block content
+ style
+ include ./stylesheets/authentication.css
+ form.form-horizontal(id='forgot-form', method='POST')
+ input(type='hidden', name='_csrf', value=_csrf)
+ .overlay(id='overlay_forgot')
+ a(href="/login")
+ img(id='to_login', src="https://bit.ly/2U6ouZk", alt="")
+ .inner.forgot
+ h3.auth_header Recover Password
+ .form-group
+ //- label.col-sm-3.control-label(for='email', id='email_label') Email
+ .col-sm-7
+ input.form-control(type='email', name='email', id='email', placeholder='Email', autofocus, required)
+ .form-group
+ .col-sm-offset-3.col-sm-7
+ button.btn.btn-success(id='submit', type='submit')
+ i.fa.fa-user-plus
+ | Submit \ No newline at end of file
diff --git a/views/layout.pug b/views/layout.pug
index fb22ae770..b11291eb9 100644
--- a/views/layout.pug
+++ b/views/layout.pug
@@ -8,10 +8,7 @@ html(lang='')
meta(name='description', content='')
meta(name='theme-color' content='#4DA5F4')
meta(name='csrf-token', content=_csrf)
- link(rel='shortcut icon', href='/images/favicon.png')
link(rel='stylesheet', href='/css/main.css')
body
-
- .container
- block content \ No newline at end of file
+ block content \ No newline at end of file
diff --git a/views/login.pug b/views/login.pug
new file mode 100644
index 000000000..9bc40a495
--- /dev/null
+++ b/views/login.pug
@@ -0,0 +1,28 @@
+
+extends ./layout
+
+block content
+ style
+ include ./stylesheets/authentication.css
+ form.form-horizontal(id='login-form', method='POST')
+ input(type='hidden', name='_csrf', value=_csrf)
+ .overlay(id='overlay_login')
+ a(href="/signup")
+ img(id='new_user', src="https://bit.ly/2EuqPb4", alt="")
+ a(href="/forgot")
+ img(id='forgot', src="https://bit.ly/2XjHpSo", alt="")
+ .inner.login
+ h3.auth_header Log In
+ .form-group
+ //- label.col-sm-3.control-label(for='email', id='email_label') Email
+ .col-sm-7
+ input.form-control(type='email', name='email', id='email', placeholder='Email', autofocus, required)
+ .form-group
+ //- label.col-sm-3.control-label(for='password') Password
+ .col-sm-7
+ input.form-control(type='password', name='password', id='password', placeholder='Password', required)
+ .form-group
+ .col-sm-offset-3.col-sm-7
+ button.btn.btn-success(id='submit', type='submit')
+ i.fa.fa-user-plus
+ | Submit \ No newline at end of file
diff --git a/views/reset.pug b/views/reset.pug
new file mode 100644
index 000000000..8b6fa952b
--- /dev/null
+++ b/views/reset.pug
@@ -0,0 +1,22 @@
+
+extends ./layout
+
+block content
+ style
+ include ./stylesheets/authentication.css
+ form.form-horizontal(id='reset-form', method='POST')
+ input(type='hidden', name='_csrf', value=_csrf)
+ .overlay(id='overlay_reset')
+ .inner.reset
+ h3.auth_header Reset Password
+ .form-group
+ .col-sm-7
+ input.form-control(type='password', name='password', id='password', placeholder='Password', required)
+ .form-group
+ .col-sm-7
+ input.form-control(type='password', name='confirmPassword', id='confirmPassword', placeholder='Confirm Password', required)
+ .form-group
+ .col-sm-offset-3.col-sm-7
+ button.btn.btn-success(type='submit')
+ i.fa.fa-user-plus
+ | Reset \ No newline at end of file
diff --git a/views/resources/dashlogo.png b/views/resources/dashlogo.png
new file mode 100644
index 000000000..3ba4e111b
--- /dev/null
+++ b/views/resources/dashlogo.png
Binary files differ
diff --git a/views/signup.pug b/views/signup.pug
index a23f334af..11b02a5eb 100644
--- a/views/signup.pug
+++ b/views/signup.pug
@@ -2,24 +2,26 @@
extends ./layout
block content
- .page-header
- h3 Sign up
- form.form-horizontal(id='signup-form', method='POST')
- input(type='hidden', name='_csrf', value=_csrf)
- .form-group
- label.col-sm-3.control-label(for='email') Email
- .col-sm-7
- input.form-control(type='email', name='email', id='email', placeholder='Email', autofocus, required)
- .form-group
- label.col-sm-3.control-label(for='password') Password
- .col-sm-7
- input.form-control(type='password', name='password', id='password', placeholder='Password', required)
- .form-group
- label.col-sm-3.control-label(for='confirmPassword') Confirm Password
- .col-sm-7
- input.form-control(type='password', name='confirmPassword', id='confirmPassword', placeholder='Confirm Password', required)
- .form-group
- .col-sm-offset-3.col-sm-7
- button.btn.btn-success(type='submit')
- i.fa.fa-user-plus
- | Signup \ No newline at end of file
+ style
+ include ./stylesheets/authentication.css
+ form.form-horizontal(id='signup-form', method='POST')
+ input(type='hidden', name='_csrf', value=_csrf)
+ .overlay(id='overlay_signup')
+ a(href="/login")
+ img(id='to_login', src="https://bit.ly/2U6ouZk", alt="")
+ .inner.signup
+ h3.auth_header Create An Account
+ .form-group
+ .col-sm-7
+ input.form-control(type='email', name='email', id='email', placeholder='Email', autofocus, required)
+ .form-group
+ .col-sm-7
+ input.form-control(type='password', name='password', id='password', placeholder='Password', required)
+ .form-group
+ .col-sm-7
+ input.form-control(type='password', name='confirmPassword', id='confirmPassword', placeholder='Confirm Password', required)
+ .form-group
+ .col-sm-offset-3.col-sm-7
+ button.btn.btn-success(type='submit')
+ i.fa.fa-user-plus
+ | Sign Up \ No newline at end of file
diff --git a/views/stylesheets/authentication.css b/views/stylesheets/authentication.css
new file mode 100644
index 000000000..dea0474e4
--- /dev/null
+++ b/views/stylesheets/authentication.css
@@ -0,0 +1,142 @@
+#email_label {
+ color: blue;
+ margin-top: 10px;
+}
+
+h3,
+label {
+ font-family: Arial, Helvetica, sans-serif;
+}
+
+body {
+ /* background-color: #ccbbcc; */
+ background-color: #251f1f;
+ /* background-image: url(https://bit.ly/2XibZvI);
+ background-repeat: no-repeat;
+ background-size: cover; */
+}
+
+#logo {
+ width: 100px;
+ height: 100px;
+ position: absolute;
+}
+
+.auth_header {
+ text-align: left;
+}
+
+.login,
+.reset {
+ height: 220px;
+}
+
+.forgot {
+ height: 175px;
+}
+
+.signup {
+ height: 273px;
+}
+
+.btn {
+ width: 224px;
+ height: 35px;
+ font-family: Arial, Helvetica, sans-serif;
+ font-size: 14px;
+ font-style: oblique;
+}
+
+#overlay_signup,
+#overlay_reset,
+#overlay_workspaces {
+ height: 345px;
+}
+
+.workspace-header {
+ margin-left: 20px;
+}
+
+.select-workspace {
+ margin-top: 15px;
+ margin-left: 20px;
+}
+
+#overlay_workspaces {
+ overflow-y: scroll;
+ text-align: left;
+}
+
+.workspaceId {
+ list-style-type: none;
+ font-family: Arial, Helvetica, sans-serif;
+ margin-left: -20px;
+ cursor: grab;
+ padding-bottom: 15px;
+}
+
+.workspaceId:hover {
+ color: red;
+}
+
+#overlay_login {
+ height: 300px;
+}
+
+#overlay_forgot {
+ height: 250px;
+}
+
+#new_user,
+#to_login {
+ right: 15px;
+}
+
+#new_user,
+#to_login,
+#forgot {
+ top: 15px;
+ width: 20px;
+ height: 20px;
+ position: absolute;
+}
+
+#forgot {
+ left: 15px;
+}
+
+.overlay {
+ border: 2px solid yellow;
+ text-align: center;
+ position: absolute;
+ margin: auto;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ width: 400px;
+ background-color: white;
+ border-radius: 8px;
+ box-shadow: 10px 10px 10px #00000099;
+}
+
+.inner {
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ width: 230px;
+ margin: auto;
+}
+
+.form-control {
+ width: 200px;
+ margin-bottom: 15px;
+ height: 30px;
+ outline: none;
+ padding-left: 10px;
+ padding-right: 10px;
+ font-family: Arial, Helvetica, sans-serif;
+ font-size: 16px;
+} \ No newline at end of file
diff --git a/webpack.config.js b/webpack.config.js
index 815e2b477..5ba9dd4b5 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -8,6 +8,8 @@ module.exports = {
bundle: ["./src/client/views/Main.tsx", 'webpack-hot-middleware/client?reload=true'],
viewer: ["./src/debug/Viewer.tsx", 'webpack-hot-middleware/client?reload=true'],
test: ["./src/debug/Test.tsx", 'webpack-hot-middleware/client?reload=true'],
+ inkControls: ["./src/mobile/InkControls.tsx", 'webpack-hot-middleware/client?reload=true'],
+ imageUpload: ["./src/mobile/ImageUpload.tsx", 'webpack-hot-middleware/client?reload=true'],
},
devtool: "source-map",
node: {
@@ -75,7 +77,7 @@ module.exports = {
compress: false,
host: "localhost",
contentBase: path.join(__dirname, 'deploy'),
- port: 1234,
+ port: 4321,
hot: true,
https: false,
overlay: {