You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by we...@apache.org on 2023/03/10 06:28:41 UTC
[myfaces] branch main updated: https://issues.apache.org/jira/browse/MYFACES-4584: Improvements in the Ajax Codebase (#547)
This is an automated email from the ASF dual-hosted git repository.
werpu pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/myfaces.git
The following commit(s) were added to refs/heads/main by this push:
new 00cf33a65 https://issues.apache.org/jira/browse/MYFACES-4584: Improvements in the Ajax Codebase (#547)
00cf33a65 is described below
commit 00cf33a65fdc99647126175b16ca3dd7e38f008a
Author: Werner Punz <we...@apache.org>
AuthorDate: Fri Mar 10 07:28:36 2023 +0100
https://issues.apache.org/jira/browse/MYFACES-4584: Improvements in the Ajax Codebase (#547)
* https://issues.apache.org/jira/browse/MYFACES-4584: improvements:
Elimination of Streams in favor of Arrays
Configs now have a flatter structure
Additional Tests
Simplification of the XHR Queue
Simplification of XhrRequest
onLoad.spec was not executed due to a wrong name pattern!
---
api/src/client/package-lock.json | 82 +-
api/src/client/package.json | 6 +-
api/src/client/tsconfig-myfaces.json | 1 -
api/src/client/typescript/faces/impl/AjaxImpl.ts | 108 +--
api/src/client/typescript/faces/impl/core/Const.ts | 7 +-
.../typescript/faces/impl/util/AsyncRunnable.ts | 87 +-
.../typescript/faces/impl/util/ExtDomQuery.ts | 16 +-
.../client/typescript/faces/impl/util/FileUtils.ts | 48 +-
.../faces/impl/util/HiddenInputBuilder.ts | 10 +-
api/src/client/typescript/faces/impl/util/Lang.ts | 66 +-
.../faces/impl/util/XhrQueueController.ts | 112 +++
.../typescript/faces/impl/xhrCore/EventData.ts | 6 +-
.../faces/impl/xhrCore/ResponseProcessor.ts | 26 +-
.../typescript/faces/impl/xhrCore/XhrFormData.ts | 52 +-
.../typescript/faces/impl/xhrCore/XhrRequest.ts | 123 ++-
.../client/typescript/faces/myfaces/OamSubmit.ts | 12 +-
.../_ext/monadish/DomQueryTest.spec.ts | 219 ++++-
.../frameworkBase/_ext/monadish/MonadTest.spec.ts | 8 +-
.../frameworkBase/_ext/monadish/StreamTest.spec.ts | 231 ------
.../frameworkBase/_ext/monadish/fixtures/blank.css | 15 +
.../frameworkBase/_ext/monadish/fixtures/test.js | 15 +
.../frameworkBase/_ext/monadish/fixtures/test2.js | 15 +
.../_ext/monadish/markups/tobago-with-header.ts | 921 +++++++++++++++++++++
.../_ext/monadish/markups/tobago-without-header.ts | 108 +++
.../frameworkBase/_ext/shared/StandardInits.ts | 5 +-
.../fixtures/jakarta.faces.resource/faces.js.jsf | 15 +
.../typescript/faces/test/impl/ImplTest.spec.ts | 30 +-
.../typescript/faces/test/myfaces/OnLoad.spec.ts | 52 ++
.../faces/test/queue/AsynchronousProbe.ts | 10 +-
.../faces/test/queue/AsynchronousQueueTest.spec.ts | 7 +-
.../faces/test/xhrCore/ClientWindow.spec.ts | 2 +
.../faces/test/xhrCore/EventTests.spec.ts | 50 +-
.../faces/test/xhrCore/FileUploadTest.spec.ts | 10 +-
.../test/xhrCore/NamespacesRequestTest.spec.ts | 37 +-
.../faces/test/xhrCore/OamSubmitTest.spec.ts | 11 +-
.../faces/test/xhrCore/RequestParamsTest.spec.ts | 6 +-
.../faces/test/xhrCore/RequestTest.spec.ts | 97 ++-
.../faces/test/xhrCore/RequestTest_23.spec.ts | 7 +-
.../faces/test/xhrCore/ResponseTest.spec.ts | 56 +-
.../test/xhrCore/TobagoFileUploadTest.spec.ts | 10 +-
.../typescript/faces/test/xhrCore/WebsocketTest.ts | 13 +-
.../faces/test/xhrCore/XhrFormDataTest.spec.ts | 11 +-
api/src/client/typescript/mona_dish/AssocArray.ts | 291 +++++++
api/src/client/typescript/mona_dish/Config.ts | 263 ++++++
api/src/client/typescript/mona_dish/DomQuery.ts | 246 +++---
api/src/client/typescript/mona_dish/Es2019Array.ts | 135 +++
.../client/typescript/mona_dish/ExtendedArray.ts | 66 --
api/src/client/typescript/mona_dish/Lang.ts | 19 +-
api/src/client/typescript/mona_dish/Messaging.ts | 11 +-
api/src/client/typescript/mona_dish/Monad.ts | 431 +---------
api/src/client/typescript/mona_dish/PromiseShim.ts | 1 -
.../typescript/mona_dish/SourcesCollectors.ts | 198 ++---
api/src/client/typescript/mona_dish/Stream.ts | 159 +++-
api/src/client/typescript/mona_dish/index.ts | 12 +-
api/src/client/typescript/mona_dish/index_core.ts | 25 +-
55 files changed, 3181 insertions(+), 1399 deletions(-)
diff --git a/api/src/client/package-lock.json b/api/src/client/package-lock.json
index ddc8cfa5d..3adcfc058 100644
--- a/api/src/client/package-lock.json
+++ b/api/src/client/package-lock.json
@@ -22,19 +22,19 @@
"html-webpack-plugin": "^5.5.0",
"jsdom": "^21.1.0",
"jsdom-global": "^3.0.2",
- "jsf.js_next_gen": "4.0.1-beta.2",
+ "jsf.js_next_gen": "4.0.1-beta.5",
"mocha": "^10.2.0",
"npm-check-updates": "^16.7.10",
"nyc": "^15.1.0",
"replace-in-file": "^6.3.5",
"rxjs": "^7.8.0",
"sinon": "^15.0.1",
- "terser-webpack-plugin": "^5.3.6",
+ "terser-webpack-plugin": "^5.3.7",
"ts-loader": "^9.4.2",
"ts-node": "^10.9.1",
"typedoc": "^0.23.26",
"typescript": "^4.9.5",
- "webpack": "^5.75.0",
+ "webpack": "^5.76.0",
"webpack-cli": "^5.0.1",
"webpack-dev-server": "^4.11.1"
}
@@ -4998,12 +4998,12 @@
}
},
"node_modules/jsf.js_next_gen": {
- "version": "4.0.1-beta.2",
- "resolved": "https://registry.npmjs.org/jsf.js_next_gen/-/jsf.js_next_gen-4.0.1-beta.2.tgz",
- "integrity": "sha512-CZ/H+6ZVuJudhkAZsx4U1FiqkpMp23UkN+x/Io9Y36G9E0LIMojAgTBTBUqg+EYlOy1lTVP12GzWtRXGJAthQA==",
+ "version": "4.0.1-beta.5",
+ "resolved": "https://registry.npmjs.org/jsf.js_next_gen/-/jsf.js_next_gen-4.0.1-beta.5.tgz",
+ "integrity": "sha512-+wezsuZ8H9396asy2x82jbwvIvXWL1glk1RjVGlCi2+1HSyllHAWFp/ZKcAYYkIVl7wT8O7Izv/ABYY+lTxfFQ==",
"dev": true,
"dependencies": {
- "mona-dish": "0.26.0"
+ "mona-dish": "0.28.3"
}
},
"node_modules/json-buffer": {
@@ -5886,9 +5886,9 @@
}
},
"node_modules/mona-dish": {
- "version": "0.26.0",
- "resolved": "https://registry.npmjs.org/mona-dish/-/mona-dish-0.26.0.tgz",
- "integrity": "sha512-fvbQOv9x97iw3TFjGZD8Tx4i7Joy7gJYlQuo9INMflSWmBZKdK3232wOkQZD9siE1jMFCPhPNwKGR5u9wbO/kw==",
+ "version": "0.28.3",
+ "resolved": "https://registry.npmjs.org/mona-dish/-/mona-dish-0.28.3.tgz",
+ "integrity": "sha512-VqB+3R4wisQJ0SNjVzXVHsGeqpACS8lbC9EoeOuVhD/F3pWIx3/4iZtQmPKZw6n+5m9js0MWyKZLZFIFE6j+8g==",
"dev": true
},
"node_modules/ms": {
@@ -8480,9 +8480,9 @@
}
},
"node_modules/terser": {
- "version": "5.16.3",
- "resolved": "https://registry.npmjs.org/terser/-/terser-5.16.3.tgz",
- "integrity": "sha512-v8wWLaS/xt3nE9dgKEWhNUFP6q4kngO5B8eYFUuebsu7Dw/UNAnpUod6UHo04jSSkv8TzKHjZDSd7EXdDQAl8Q==",
+ "version": "5.16.5",
+ "resolved": "https://registry.npmjs.org/terser/-/terser-5.16.5.tgz",
+ "integrity": "sha512-qcwfg4+RZa3YvlFh0qjifnzBHjKGNbtDo9yivMqMFDy9Q6FSaQWSB/j1xKhsoUFJIqDOM3TsN6D5xbrMrFcHbg==",
"dev": true,
"dependencies": {
"@jridgewell/source-map": "^0.3.2",
@@ -8498,16 +8498,16 @@
}
},
"node_modules/terser-webpack-plugin": {
- "version": "5.3.6",
- "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.6.tgz",
- "integrity": "sha512-kfLFk+PoLUQIbLmB1+PZDMRSZS99Mp+/MHqDNmMA6tOItzRt+Npe3E+fsMs5mfcM0wCtrrdU387UnV+vnSffXQ==",
+ "version": "5.3.7",
+ "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.7.tgz",
+ "integrity": "sha512-AfKwIktyP7Cu50xNjXF/6Qb5lBNzYaWpU6YfoX3uZicTx0zTy0stDDCsvjDapKsSDvOeWo5MEq4TmdBy2cNoHw==",
"dev": true,
"dependencies": {
- "@jridgewell/trace-mapping": "^0.3.14",
+ "@jridgewell/trace-mapping": "^0.3.17",
"jest-worker": "^27.4.5",
"schema-utils": "^3.1.1",
- "serialize-javascript": "^6.0.0",
- "terser": "^5.14.1"
+ "serialize-javascript": "^6.0.1",
+ "terser": "^5.16.5"
},
"engines": {
"node": ">= 10.13.0"
@@ -9217,9 +9217,9 @@
}
},
"node_modules/webpack": {
- "version": "5.75.0",
- "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.75.0.tgz",
- "integrity": "sha512-piaIaoVJlqMsPtX/+3KTTO6jfvrSYgauFVdt8cr9LTHKmcq/AMd4mhzsiP7ZF/PGRNPGA8336jldh9l2Kt2ogQ==",
+ "version": "5.76.0",
+ "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.76.0.tgz",
+ "integrity": "sha512-l5sOdYBDunyf72HW8dF23rFtWq/7Zgvt/9ftMof71E/yUb1YLOBmTgA2K4vQthB3kotMrSj609txVE0dnr2fjA==",
"dev": true,
"dependencies": {
"@types/eslint-scope": "^3.7.3",
@@ -13761,12 +13761,12 @@
"dev": true
},
"jsf.js_next_gen": {
- "version": "4.0.1-beta.2",
- "resolved": "https://registry.npmjs.org/jsf.js_next_gen/-/jsf.js_next_gen-4.0.1-beta.2.tgz",
- "integrity": "sha512-CZ/H+6ZVuJudhkAZsx4U1FiqkpMp23UkN+x/Io9Y36G9E0LIMojAgTBTBUqg+EYlOy1lTVP12GzWtRXGJAthQA==",
+ "version": "4.0.1-beta.5",
+ "resolved": "https://registry.npmjs.org/jsf.js_next_gen/-/jsf.js_next_gen-4.0.1-beta.5.tgz",
+ "integrity": "sha512-+wezsuZ8H9396asy2x82jbwvIvXWL1glk1RjVGlCi2+1HSyllHAWFp/ZKcAYYkIVl7wT8O7Izv/ABYY+lTxfFQ==",
"dev": true,
"requires": {
- "mona-dish": "0.26.0"
+ "mona-dish": "0.28.3"
}
},
"json-buffer": {
@@ -14455,9 +14455,9 @@
}
},
"mona-dish": {
- "version": "0.26.0",
- "resolved": "https://registry.npmjs.org/mona-dish/-/mona-dish-0.26.0.tgz",
- "integrity": "sha512-fvbQOv9x97iw3TFjGZD8Tx4i7Joy7gJYlQuo9INMflSWmBZKdK3232wOkQZD9siE1jMFCPhPNwKGR5u9wbO/kw==",
+ "version": "0.28.3",
+ "resolved": "https://registry.npmjs.org/mona-dish/-/mona-dish-0.28.3.tgz",
+ "integrity": "sha512-VqB+3R4wisQJ0SNjVzXVHsGeqpACS8lbC9EoeOuVhD/F3pWIx3/4iZtQmPKZw6n+5m9js0MWyKZLZFIFE6j+8g==",
"dev": true
},
"ms": {
@@ -16450,9 +16450,9 @@
}
},
"terser": {
- "version": "5.16.3",
- "resolved": "https://registry.npmjs.org/terser/-/terser-5.16.3.tgz",
- "integrity": "sha512-v8wWLaS/xt3nE9dgKEWhNUFP6q4kngO5B8eYFUuebsu7Dw/UNAnpUod6UHo04jSSkv8TzKHjZDSd7EXdDQAl8Q==",
+ "version": "5.16.5",
+ "resolved": "https://registry.npmjs.org/terser/-/terser-5.16.5.tgz",
+ "integrity": "sha512-qcwfg4+RZa3YvlFh0qjifnzBHjKGNbtDo9yivMqMFDy9Q6FSaQWSB/j1xKhsoUFJIqDOM3TsN6D5xbrMrFcHbg==",
"dev": true,
"requires": {
"@jridgewell/source-map": "^0.3.2",
@@ -16470,16 +16470,16 @@
}
},
"terser-webpack-plugin": {
- "version": "5.3.6",
- "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.6.tgz",
- "integrity": "sha512-kfLFk+PoLUQIbLmB1+PZDMRSZS99Mp+/MHqDNmMA6tOItzRt+Npe3E+fsMs5mfcM0wCtrrdU387UnV+vnSffXQ==",
+ "version": "5.3.7",
+ "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.7.tgz",
+ "integrity": "sha512-AfKwIktyP7Cu50xNjXF/6Qb5lBNzYaWpU6YfoX3uZicTx0zTy0stDDCsvjDapKsSDvOeWo5MEq4TmdBy2cNoHw==",
"dev": true,
"requires": {
- "@jridgewell/trace-mapping": "^0.3.14",
+ "@jridgewell/trace-mapping": "^0.3.17",
"jest-worker": "^27.4.5",
"schema-utils": "^3.1.1",
- "serialize-javascript": "^6.0.0",
- "terser": "^5.14.1"
+ "serialize-javascript": "^6.0.1",
+ "terser": "^5.16.5"
},
"dependencies": {
"ajv": {
@@ -16986,9 +16986,9 @@
"dev": true
},
"webpack": {
- "version": "5.75.0",
- "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.75.0.tgz",
- "integrity": "sha512-piaIaoVJlqMsPtX/+3KTTO6jfvrSYgauFVdt8cr9LTHKmcq/AMd4mhzsiP7ZF/PGRNPGA8336jldh9l2Kt2ogQ==",
+ "version": "5.76.0",
+ "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.76.0.tgz",
+ "integrity": "sha512-l5sOdYBDunyf72HW8dF23rFtWq/7Zgvt/9ftMof71E/yUb1YLOBmTgA2K4vQthB3kotMrSj609txVE0dnr2fjA==",
"dev": true,
"requires": {
"@types/eslint-scope": "^3.7.3",
diff --git a/api/src/client/package.json b/api/src/client/package.json
index 218d54624..940f23440 100644
--- a/api/src/client/package.json
+++ b/api/src/client/package.json
@@ -24,19 +24,19 @@
"html-webpack-plugin": "^5.5.0",
"jsdom": "^21.1.0",
"jsdom-global": "^3.0.2",
- "jsf.js_next_gen": "4.0.1-beta.2",
+ "jsf.js_next_gen": "4.0.1-beta.5",
"mocha": "^10.2.0",
"npm-check-updates": "^16.7.10",
"nyc": "^15.1.0",
"replace-in-file": "^6.3.5",
"rxjs": "^7.8.0",
"sinon": "^15.0.1",
- "terser-webpack-plugin": "^5.3.6",
+ "terser-webpack-plugin": "^5.3.7",
"ts-loader": "^9.4.2",
"ts-node": "^10.9.1",
"typedoc": "^0.23.26",
"typescript": "^4.9.5",
- "webpack": "^5.75.0",
+ "webpack": "^5.76.0",
"webpack-cli": "^5.0.1",
"webpack-dev-server": "^4.11.1"
}
diff --git a/api/src/client/tsconfig-myfaces.json b/api/src/client/tsconfig-myfaces.json
index 80d5f4b45..d4054aa63 100644
--- a/api/src/client/tsconfig-myfaces.json
+++ b/api/src/client/tsconfig-myfaces.json
@@ -46,7 +46,6 @@
}
},
"exclude": [
- "./typescript/mona_dish/ExtendedArray.ts",
"./typescript/mona_dish/index.ts",
"./typescript/mona_dish/Messaging.ts"
]
diff --git a/api/src/client/typescript/faces/impl/AjaxImpl.ts b/api/src/client/typescript/faces/impl/AjaxImpl.ts
index 959533e4b..5981c3361 100644
--- a/api/src/client/typescript/faces/impl/AjaxImpl.ts
+++ b/api/src/client/typescript/faces/impl/AjaxImpl.ts
@@ -17,8 +17,7 @@
import {IListener} from "./util/IListener";
import {Response} from "./xhrCore/Response";
import {XhrRequest} from "./xhrCore/XhrRequest";
-import {AsynchronousQueue} from "./util/AsyncQueue";
-import {AssocArrayCollector, Config, DQ, DQ$, Lang, LazyStream, Optional, Stream} from "mona-dish";
+import {Config, DQ, DQ$, Lang, Optional} from "mona-dish";
import {Assertions} from "./util/Assertions";
import {ExtConfig, ExtDomQuery} from "./util/ExtDomQuery";
import {ErrorData} from "./xhrCore/ErrorData";
@@ -41,7 +40,7 @@ import {
P_CLIENT_WINDOW,
P_EVT,
P_EXECUTE,
- P_PARTIAL_SOURCE,
+ P_AJAX_SOURCE,
P_RENDER,
P_RESET_VALUES,
P_WINDOW_ID,
@@ -52,9 +51,10 @@ import {
VIEW_ID,
$faces,
EMPTY_STR,
- CTX_PARAM_MF_INTERNAL,
NAMED_VIEWROOT,
NAMING_CONTAINER_ID,
+ CTX_PARAM_PPS,
+ MYFACES_OPTION_PPS,
$nsp
} from "./core/Const";
import {
@@ -64,7 +64,7 @@ import {
resolveTimeout, resolveViewId, resolveViewRootId, resoveNamingContainerMapper
} from "./xhrCore/RequestDataResolver";
import {encodeFormData} from "./util/FileUtils";
-
+import {XhrQueueController} from "./util/XhrQueueController";
/*
* allowed project stages
@@ -93,6 +93,8 @@ enum BlockFilter {
params = "params"
}
+
+
/**
* Core Implementation
* to distinct between api and impl
@@ -112,17 +114,7 @@ export module Implementation {
it provides following
a) Monad like structures for querying because this keeps the code denser and adds abstractions
- that always was the strong point of jquery and it still is better in this regard than what ecmascript provides
-
- b) Streams and lazy streams like java has, a pull stream construct, ecmascript does not have anything like it.
- (it has array filters and maps, but ES2015 does not support flatMap)
- Another option would have been rxjs but that would have introduced a code dependency and probably more code. We might
- move to RXJS if the need arises, however. But for now I would rather stick with my small self grown library which works
- quite well and where I can patch quickly (I have used it in several industrial projects, so it works well
- and is heavily fortified by unit tests (140 testcases as time of writing this))
- The long term plan is to eliminate the Stream usage as soon as we can move up to ES2019 (adding the missing
- functions as shims, is a no go, because we are a library, and absolutey do not Shim anything which can leak
- into the global namespace!)
+ that always was the strong point of jQuery, and it still is better in this regard than what ecmascript provides
c) A neutral json like configuration which allows assignments of arbitrary values with reduce code which then can be
transformed into different data representations
@@ -159,12 +151,14 @@ export module Implementation {
import getMessage = ExtLang.getMessage;
import getGlobalConfig = ExtLang.getGlobalConfig;
import assert = Assertions.assert;
+ import ofAssoc = ExtLang.ofAssoc;
+ import collectAssoc = ExtLang.collectAssoc;
let projectStage: string = null;
let separator: string = null;
let eventQueue = [];
let errorQueue = [];
- export let requestQueue: AsynchronousQueue<XhrRequest> = null;
+ export let requestQueue: XhrQueueController<XhrRequest> = null;
/*error reporting threshold*/
let threshold = "ERROR";
@@ -194,7 +188,7 @@ export module Implementation {
/**
* @return the project stage also emitted by the server:
* it cannot be cached and must be delivered over the server
- * The value for it comes from the request parameter of the faces.js script called "stage".
+ * The value for it comes from the requestInternal parameter of the faces.js script called "stage".
*/
export function getProjectStage(): string | null {
return resolveGlobalConfig()?.projectStage ??
@@ -220,17 +214,21 @@ export module Implementation {
* @param event
* @param funcs
*/
- export function chain(source: HTMLElement | string, event: Event | null, ...funcs: EvalFuncs): boolean {
- // we can use our lazy stream each functionality to run our chain here..
+ export function chain(source: any, event: Event, ...funcs: EvalFuncs): boolean {
+ // we can use our lazy stream each functionality to run our chain here.
// by passing a boolean as return value into the onElem call
// we can stop early at the first false, just like the spec requests
- return LazyStream.of(...funcs)
- .map(func => resolveAndExecute(source, event, func))
- // we use the return false == stop as an early stop, onElem stops at the first false
- .onElem((opResult: boolean) => opResult)
- //last ensures we run until the first false is returned
- .last().value;
+ let ret;
+ funcs.every(func => {
+ let returnVal = resolveAndExecute(source, event, func);
+ if(returnVal !== false) {
+ ret = returnVal;
+ }
+ return returnVal !== false;
+ });
+ return ret;
+
}
/**
@@ -280,7 +278,7 @@ export module Implementation {
requestCtx.assignIf(!!windowId, P_WINDOW_ID).value = windowId;
- // old non spec behavior will be removed after it is clear whether the removal breaks any code
+ // old non - spec behavior will be removed after it is clear whether the removal breaks any code
requestCtx.assign(CTX_PARAM_REQ_PASS_THR).value = extractLegacyParams(options.value);
// spec conform behavior, all passthrough params must be under "passthrough
@@ -312,14 +310,14 @@ export module Implementation {
requestCtx.assign(ON_ERROR).value = options.value?.onerror;
/**
- * lets drag the myfaces config params also in
+ * Fetch the myfaces config params
*/
requestCtx.assign(MYFACES).value = options.value?.myfaces;
/**
* binding contract the jakarta.faces.source must be set
*/
- requestCtx.assign(CTX_PARAM_REQ_PASS_THR, P_PARTIAL_SOURCE).value = elementId;
+ requestCtx.assign(CTX_PARAM_REQ_PASS_THR, P_AJAX_SOURCE).value = elementId;
/**
* jakarta.faces.partial.ajax must be set to true
@@ -346,6 +344,9 @@ export module Implementation {
// won't hurt but for the sake of compatibility we are going to add it
requestCtx.assign(CTX_PARAM_REQ_PASS_THR, formId).value = formId;
internalCtx.assign(CTX_PARAM_SRC_CTL_ID).value = elementId;
+ // reintroduction of PPS as per myfaces 2.3 (myfaces.pps = true, only the executes are submitted)
+ internalCtx.assign(CTX_PARAM_PPS).value = extractMyFacesParams(options.value)?.[MYFACES_OPTION_PPS] ?? false;
+
assignClientWindowId(form, requestCtx);
assignExecute(options, requestCtx, form, elementId);
@@ -426,7 +427,7 @@ export module Implementation {
}
} finally {
if (clearRequestQueue) {
- requestQueue.cleanup();
+ requestQueue.clear();
}
}
}
@@ -522,16 +523,16 @@ export module Implementation {
* or non-existent. If they exist all of them must be the same
*/
- let formWindowId: Optional<string> = inputs.stream.map(getValue).reduce(differenceCheck, INIT);
+ let formWindowId: string = inputs.asArray.map(getValue).reduce(differenceCheck, INIT);
//if the resulting window id is set on altered then we have an unresolvable problem
- assert(ALTERED != formWindowId.value, "Multiple different windowIds found in document");
+ assert(ALTERED != formWindowId, "Multiple different windowIds found in document");
/*
* return the window id or null
*/
- return formWindowId.value != INIT ? formWindowId.value : (fetchWindowIdFromURL() || fetchWindowIdFromJSFJS());
+ return formWindowId != INIT ? formWindowId : (fetchWindowIdFromURL() || fetchWindowIdFromJSFJS());
}
/**
@@ -560,7 +561,7 @@ export module Implementation {
let formElements = element.deepElements.encodeFormElement()
// encode them! (file inputs are handled differently and are not part of the viewstate)
- return encodeFormData(formElements, resoveNamingContainerMapper(dummyContext));
+ return encodeFormData(new ExtConfig(formElements), resoveNamingContainerMapper(dummyContext));
}
/**
@@ -575,8 +576,8 @@ export module Implementation {
* adds a new request to our queue for further processing
*/
addRequestToQueue: function (elem: DQ, form: DQ, reqCtx: ExtConfig, respPassThr: Config, delay = 0, timeout = 0) {
- requestQueue = requestQueue ?? new AsynchronousQueue<XhrRequest>();
- requestQueue.enqueue(new XhrRequest(elem, form, reqCtx, respPassThr, [], timeout), delay);
+ requestQueue = requestQueue ?? new XhrQueueController<XhrRequest>();
+ requestQueue.enqueue(new XhrRequest(reqCtx, respPassThr, timeout), delay);
}
};
@@ -685,7 +686,7 @@ export module Implementation {
* can deal with them either prefixed ir not
* also resolves the absolute id case (it was assumed the server does this, but
* apparently the RI does not, so we have to follow the RI behavior here)
- * @param componentIdToTransform the componentId which needs post processing
+ * @param componentIdToTransform the componentId which needs post-processing
*/
const remapNamingContainer = componentIdToTransform => {
// pattern :<anything> must be prepended by viewRoot if there is one,
@@ -699,7 +700,7 @@ export module Implementation {
const hasLeadingSep = componentIdToTransform.indexOf(SEP) === 0;
const isAbsolutSearchExpr = hasLeadingSep || (rootNamingContainerId.length
&& componentIdToTransform.indexOf(rootNamingContainerPrefix) == 0);
- let finalIdentifier = "";
+ let finalIdentifier: string;
if (isAbsolutSearchExpr) {
//we cut off the leading sep if there is one
componentIdToTransform = hasLeadingSep ? componentIdToTransform.substring(1) : componentIdToTransform;
@@ -718,14 +719,14 @@ export module Implementation {
[rootNamingContainerPrefix, componentIdToTransform].join(EMPTY_STR) :
[nearestNamingContainerPrefix, componentIdToTransform].join(EMPTY_STR);
}
- // We need to double check because we have scenarios where we have a naming container
+ // We need to double-check because we have scenarios where we have a naming container
// and no prepend (aka tobago testcase "must handle ':' in IDs properly", scenario 3,
// in this case we return the component id, and be happy
// we can roll a dom check here
return (!!document.getElementById(finalIdentifier)) ? finalIdentifier : componentIdToTransform;
};
- // in this case we do not use lazy stream because it wont bring any code reduction
+ // in this case we do not use lazy stream because it won´t bring any code reduction
// or speedup
for (let cnt = 0; cnt < iterValues.length; cnt++) {
//avoid doubles
@@ -767,24 +768,37 @@ export module Implementation {
* the values required for params-through are processed in the ajax request
*
* Note this is a bug carried over from the old implementation
- * the spec conform behavior is to use params for passthrough values
+ * the spec conform behavior is to use params for pass - through values
* this will be removed soon, after it is cleared up whether removing
* it breaks any legacy code
*
* @param {Context} mappedOpts the options to be filtered
- * @deprecated
*/
- function extractLegacyParams(mappedOpts: Options): Context {
+ function extractLegacyParams(mappedOpts: Options): {[key: string]: any} {
+ //we now can use the full code reduction given by our stream api
+ //to filter
+ return ofAssoc(mappedOpts)
+ .filter((item => !(item[0] in BlockFilter)))
+ .reduce(collectAssoc, {});
+ }
+
+ /**
+ * extracts the myfaces config parameters which provide extra functionality
+ * on top of JSF
+ * @param mappedOpts
+ * @private
+ */
+ function extractMyFacesParams(mappedOpts: Options): {[key: string]: any} {
//we now can use the full code reduction given by our stream api
//to filter
- return Stream.ofAssoc(mappedOpts)
- .filter(item => !(item[0] in BlockFilter))
- .collect(new AssocArrayCollector());
+ return ofAssoc(mappedOpts)
+ .filter((item => (item[0] == "myfaces")))
+ .reduce(collectAssoc, {})?.[MYFACES];
}
function remapArrayToAssocArr(arrayedParams: [[string, any]] | {[key: string]: any}): {[key: string]: any} {
if(Array.isArray(arrayedParams)) {
- return Stream.of(... arrayedParams).collect(new AssocArrayCollector());
+ return arrayedParams.reduce(collectAssoc, {} as any);
}
return arrayedParams;
}
diff --git a/api/src/client/typescript/faces/impl/core/Const.ts b/api/src/client/typescript/faces/impl/core/Const.ts
index 484328edf..f0e4eedea 100644
--- a/api/src/client/typescript/faces/impl/core/Const.ts
+++ b/api/src/client/typescript/faces/impl/core/Const.ts
@@ -21,8 +21,8 @@
export const XML_ATTR_NAMED_VIEWROOT = "namedViewRoot";
export const NAMED_VIEWROOT = "namedViewRoot";
-export const P_PARTIAL_SOURCE = "jakarta.faces.source";
-export const NAMING_CONTAINER_ID = "myfaces.partialId";
+export const P_AJAX_SOURCE = "jakarta.faces.source";
+export const NAMING_CONTAINER_ID = "myfaces.NamingContainerId";
export const VIEW_ID = "myfaces.viewId";
export const P_VIEWSTATE = "jakarta.faces.ViewState";
@@ -108,6 +108,7 @@ export const CTX_PARAM_MF_INTERNAL = "myfaces.internal";
export const CTX_PARAM_SRC_FRM_ID = "myfaces.source.formId";
export const CTX_PARAM_SRC_CTL_ID = "myfaces.source.controlId";
export const CTX_PARAM_REQ_PASS_THR = "myfaces.request.passThrough";
+export const CTX_PARAM_PPS = "myfaces.request.pps";
export const CONTENT_TYPE = "Content-Type";
export const HEAD_FACES_REQ = "Faces-Request";
@@ -141,6 +142,8 @@ export const PHASE_PROCESS_RESPONSE = "processResponse";
export const ERR_NO_PARTIAL_RESPONSE = "Partial response not set";
+export const MYFACES_OPTION_PPS = "pps";
+
export const ATTR_URL = "url";
export const ATTR_NAME = "name";
export const ATTR_VALUE = "value";
diff --git a/api/src/client/typescript/faces/impl/util/AsyncRunnable.ts b/api/src/client/typescript/faces/impl/util/AsyncRunnable.ts
index 49c5030ac..77097bd3e 100644
--- a/api/src/client/typescript/faces/impl/util/AsyncRunnable.ts
+++ b/api/src/client/typescript/faces/impl/util/AsyncRunnable.ts
@@ -26,7 +26,7 @@
* from the implementation side it is mostly registering callbacks
* and calling them at the appropriate time.
*/
-export interface AsyncRunnable<T> {
+export interface IAsyncRunnable<T> {
/**
* starts the runnable
*/
@@ -45,9 +45,9 @@ export interface AsyncRunnable<T> {
* and then triggers all the registered then functions
* when it is complete
*
- * @param func
+ * @param func the then functor
*/
- then(func: (data: any) => any): AsyncRunnable<T>;
+ then(func: (data: any) => any): IAsyncRunnable<T>;
/**
* callback for catch functionality
@@ -59,7 +59,7 @@ export interface AsyncRunnable<T> {
*
* @param func
*/
- catch(func: (data: any) => any): AsyncRunnable<T>;
+ catch(func: (data: any) => any): IAsyncRunnable<T>;
/**
@@ -68,5 +68,80 @@ export interface AsyncRunnable<T> {
* and once the finally time for the promise has
* come the finally functions must be performed
*/
- finally(func: () => void): AsyncRunnable<T>;
-}
\ No newline at end of file
+ finally(func: () => void): IAsyncRunnable<T>;
+}
+
+
+/**
+ * pretty much the same as cancellable Promise, but given
+ * we do not have that on browser level yet this is sort
+ * of a non - intrusive Shim!
+ */
+export abstract class AsyncRunnable<T> implements IAsyncRunnable<T>{
+ /**
+ * helper support so that we do not have to drag in Promise shims
+ */
+ private catchFunctions: Array<Function> = [];
+ private thenFunctions: Array<Function> = [];
+
+ /**
+ * cancel the run of the runnable (which then depending on the implementation
+ * either triggers indirectly resolve or reject)
+ */
+ abstract cancel(): void;
+
+ /**
+ * extended functionality start to trigger the runnable
+ */
+ abstract start(): void;
+
+ /**
+ * resolve handler function which calls the then chain
+ * and after that finally
+ * @param data
+ */
+ resolve(data: any) {
+ this.thenFunctions.reduce((inputVal: any, thenFunc: any) => {
+ return thenFunc(inputVal);
+ }, data)
+ }
+
+ /**
+ * reject handler function which triggers the catch chain
+ * @param data
+ */
+ reject(data: any) {
+ this.catchFunctions.reduce((inputVal: any, catchFunc: any) => {
+ return catchFunc(inputVal);
+ }, data);
+ }
+
+ /**
+ * register a catch functor
+ * @param func the functor for the catch monad
+ */
+ catch(func: (data: any) => any): IAsyncRunnable<T> {
+ this.catchFunctions.push(func);
+ return this;
+ }
+
+ /**
+ * registers a finally functor
+ * @param func the functor for the finally handling chanin
+ */
+ finally(func: () => void): IAsyncRunnable<T> {
+ // no ie11 support we probably are going to revert to shims for that one
+ this.catchFunctions.push(func);
+ this.thenFunctions.push(func);
+ return this;
+ }
+
+ /**
+ * @param func then functor similar to promise
+ */
+ then(func: (data: any) => any): IAsyncRunnable<T> {
+ this.thenFunctions.push(func);
+ return this;
+ }
+}
+
diff --git a/api/src/client/typescript/faces/impl/util/ExtDomQuery.ts b/api/src/client/typescript/faces/impl/util/ExtDomQuery.ts
index 465d5b3a4..e28da5a9f 100644
--- a/api/src/client/typescript/faces/impl/util/ExtDomQuery.ts
+++ b/api/src/client/typescript/faces/impl/util/ExtDomQuery.ts
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import {Config, IValueHolder, Optional, DomQuery, DQ, Stream, ArrayCollector} from "mona-dish";
+import {Config, IValueHolder, Optional, DomQuery, DQ, Es2019Array} from "mona-dish";
import {$nsp, P_WINDOW_ID} from "../core/Const";
@@ -120,12 +120,10 @@ export class ExtDomQuery extends DQ {
}
// fallback if the currentScript method fails, we just search the jsf tags for nonce, this is
// the last possibility
- let nonceScript = DQ
- .querySelectorAll("script[src], link[src]")
- .lazyStream
+ let nonceScript = Optional.fromNullable(DQ
+ .querySelectorAll("script[src], link[src]").asArray
.filter((item) => this.extractNonce(item) && item.attr(ATTR_SRC) != null)
- .filter(item => IS_FACES_SOURCE(item.attr(ATTR_SRC).value))
- .first();
+ .filter(item => IS_FACES_SOURCE(item.attr(ATTR_SRC).value))?.[0]);
if (nonceScript.isPresent()) {
return this.extractNonce(nonceScript.value);
@@ -144,13 +142,13 @@ export class ExtDomQuery extends DQ {
*/
searchJsfJsFor(regExp: RegExp): Optional<string> {
//perfect application for lazy stream
- return DQ.querySelectorAll("script[src], link[src]").lazyStream
+ return Optional.fromNullable(DQ.querySelectorAll("script[src], link[src]").asArray
.filter(item => IS_FACES_SOURCE(item.attr(ATTR_SRC).value))
.map(item => item.attr(ATTR_SRC).value.match(regExp))
.filter(item => item != null && item.length > 1)
.map((result: string[]) => {
return decodeURIComponent(result[1]);
- }).first();
+ })?.[0]);
}
globalEval(code: string, nonce ?: string): DQ {
@@ -332,6 +330,6 @@ export class ExtConfig extends Config {
if(!this.$nspEnabled) {
return accessPath;
}
- return Stream.of(...accessPath).map(key => $nsp(key)).collect(new ArrayCollector());
+ return new Es2019Array(...accessPath).map(key => $nsp(key));
}
}
\ No newline at end of file
diff --git a/api/src/client/typescript/faces/impl/util/FileUtils.ts b/api/src/client/typescript/faces/impl/util/FileUtils.ts
index 176b0d142..4415e8493 100644
--- a/api/src/client/typescript/faces/impl/util/FileUtils.ts
+++ b/api/src/client/typescript/faces/impl/util/FileUtils.ts
@@ -1,4 +1,4 @@
-import {ArrayCollector, Config, DomQuery, DQ, LazyStream, Stream} from "mona-dish";
+import {Config, DomQuery, DQ, Es2019Array} from "mona-dish";
import {ExtDomQuery} from "./ExtDomQuery";
import {$faces, EMPTY_STR} from "../core/Const";
@@ -22,27 +22,24 @@ export function encodeFormData(formData: Config,
}
const assocValues = formData.value;
- const expandValueArrAndRename = key => Stream.of(...assocValues[key]).map(val => paramsMapper(key, val));
+ const expandValueArrAndRename = key => assocValues[key].map(val => paramsMapper(key, val));
const isPropertyKey = key => assocValues.hasOwnProperty(key);
const isNotFile = ([, value]) => !(value instanceof ExtDomQuery.global().File);
const mapIntoUrlParam = keyVal => `${encodeURIComponent(keyVal[0])}=${encodeURIComponent(keyVal[1])}`;
- const entries = LazyStream.of(...Object.keys(assocValues))
+ return new Es2019Array(...Object.keys(assocValues))
.filter(isPropertyKey)
.flatMap(expandValueArrAndRename)
- //we cannot encode file elements that is handled by multipart requests anyway
.filter(isNotFile)
.map(mapIntoUrlParam)
- .collect(new ArrayCollector());
-
- return entries.join("&")
+ .join("&");
}
/**
* splits and decodes encoded values into strings containing of key=value
* @param encoded encoded string
*/
-export function decodeEncodedValues(encoded: string): Stream<string[]> {
+export function decodeEncodedValues(encoded: string): string[][] {
const filterBlanks = item => !!(item || '').replace(/\s+/g, '');
const splitKeyValuePair = line => {
let index = line.indexOf("=");
@@ -53,9 +50,7 @@ export function decodeEncodedValues(encoded: string): Stream<string[]> {
};
let requestParamEntries = decodeURIComponent(encoded).split(/&/gi);
- return Stream.of(...requestParamEntries)
- .filter(filterBlanks)
- .map(splitKeyValuePair)
+ return requestParamEntries.filter(filterBlanks).map(splitKeyValuePair);
}
@@ -63,19 +58,28 @@ export function decodeEncodedValues(encoded: string): Stream<string[]> {
* gets all the input files and their corresponding file objects
* @param dataSource
*/
-export function resolveFiles(dataSource: DQ): Stream<[string, File]> {
+export function resolveFiles(dataSource: DQ): [string, File][] {
+
+ const expandFilesArr = ([key, files]) => {
+ return [...files].map(file => [key, file]);
+ }
+ const remapFileInput = fileInput => {
+ return [fileInput.name.value || fileInput.id.value, fileInput.filesFromElem(0)];
+ }
- const expandFilesArr = ([key, files]) => Stream.of(...files).map(file => [key, file]);
- const remapFileInput = fileInput => [fileInput.name.value || fileInput.id.value, fileInput.filesFromElem(0)];
- return dataSource
+ const files = dataSource
.querySelectorAllDeep("input[type='file']")
- .stream
+ .asArray;
+
+ const ret = files
.map(remapFileInput)
.flatMap(expandFilesArr);
+
+ return ret as any;
}
-export function fixEmmptyParameters(keyVal: any[]): [string, any] {
+export function fixEmptyParameters(keyVal: any[]): [string, any] {
return (keyVal.length < 3 ? [keyVal?.[0] ?? [], keyVal?.[1] ?? []] : keyVal) as [string, any];
}
@@ -83,7 +87,7 @@ export function fixEmmptyParameters(keyVal: any[]): [string, any] {
* returns the decoded viewState from parentItem
* @param parentItem
*/
-function resolveViewState(parentItem: DomQuery): Stream<string[] | [string, File]> {
+function resolveViewState(parentItem: DomQuery): string[][] | [string, File][] {
const viewStateStr = $faces().getViewState(parentItem.getAsElem(0).value);
// we now need to decode it and then merge it into the target buf
@@ -94,11 +98,11 @@ function resolveViewState(parentItem: DomQuery): Stream<string[] | [string, File
/**
* gets all the inputs under the form parentItem
- * as stream
+ * as array
* @param parentItem
*/
-export function getFormInputsAsStream(parentItem: DomQuery): Stream<string[] | [string, File]> {
- const standardInputs = resolveViewState(parentItem);
+export function getFormInputsAsArr(parentItem: DomQuery): string[][] | [string, File][] {
+ const standardInputs: any = resolveViewState(parentItem);
const fileInputs = resolveFiles(parentItem);
- return standardInputs.concat(fileInputs as any)
+ return standardInputs.concat(fileInputs)
}
\ No newline at end of file
diff --git a/api/src/client/typescript/faces/impl/util/HiddenInputBuilder.ts b/api/src/client/typescript/faces/impl/util/HiddenInputBuilder.ts
index 82ee20794..d2a50a484 100644
--- a/api/src/client/typescript/faces/impl/util/HiddenInputBuilder.ts
+++ b/api/src/client/typescript/faces/impl/util/HiddenInputBuilder.ts
@@ -58,13 +58,17 @@ export class HiddenInputBuilder {
const SEP = $faces().separatorchar;
let existingStates = DQ$(`[name*='${$nsp(this.name)}']`);
- let cnt = existingStates.stream.map(state => {
+ let cnt = existingStates.asArray.map(state => {
let ident: string = state.id.orElse("-1").value;
ident = ident.substring(ident.lastIndexOf(SEP)+1);
return parseInt(ident);
})
- .filter(item => !isNaN(item))
- .reduce((item1, item2) => Math.max(item1, item2), -1).value;
+ .filter(item => {
+ return !isNaN(item);
+ })
+ .reduce((item1, item2) => {
+ return Math.max(item1, item2);
+ }, -1);
//the maximum new ident is the current max + 1
cnt++;
diff --git a/api/src/client/typescript/faces/impl/util/Lang.ts b/api/src/client/typescript/faces/impl/util/Lang.ts
index 8b1b931d6..b5038127d 100644
--- a/api/src/client/typescript/faces/impl/util/Lang.ts
+++ b/api/src/client/typescript/faces/impl/util/Lang.ts
@@ -15,10 +15,11 @@
*
*/
-import {Lang as LangBase, Config, Optional, DomQuery, DQ, Stream} from "mona-dish";
+import {Lang as LangBase, Config, Optional, DomQuery, DQ} from "mona-dish";
import {Messages} from "../i18n/Messages";
import {EMPTY_STR, HTML_TAG_FORM} from "../core/Const";
import {getEventTarget} from "../xhrCore/RequestDataResolver";
+import {Es2019Array} from "mona-dish";
export module ExtLang {
@@ -85,10 +86,10 @@ export module ExtLang {
installedLocale = installedLocale ?? new Messages();
let msg = installedLocale[key] ?? defaultMessage ?? key;
-
- Stream.of(...templateParams).each((param, cnt) => {
+ templateParams.forEach((param, cnt) => {
msg = msg.replace(new RegExp(["\\{", cnt, "\\}"].join(EMPTY_STR), "g"), param);
- });
+ })
+
return msg;
}
@@ -202,6 +203,61 @@ export module ExtLang {
defaultValue;
}
+ /**
+ * expands an associative array into an array of key value tuples
+ * @param value
+ */
+ export function ofAssoc(value: {[key: string]: any}) {
+ return new Es2019Array(...Object.keys(value))
+ .map(key => [key, value[key]]);
+ }
+
+ export function collectAssoc(target: any, item: any) {
+ target[item[0]] = item[1];
+ return target;
+ }
+
+ /**
+ * The active timeout for the "debounce".
+ * Since we only use it in the XhrController
+ * we can use a local module variable here
+ */
+ let activeTimeouts = {};
+
+
+
+
+ /**
+ * a simple debounce function
+ * which waits until a timeout is reached and
+ * if something comes in in between debounces
+ *
+ * @param runnable a runnable which should go under debounce control
+ * @param timeout a timeout for the debounce window
+ */
+ export function debounce(key, runnable, timeout) {
+ function clearActiveTimeout() {
+ clearTimeout(activeTimeouts[key]);
+ delete activeTimeouts[key];
+ }
+
+ if (!!(activeTimeouts?.[key])) {
+ clearActiveTimeout();
+ }
+ if (timeout > 0) {
+ activeTimeouts[key] = setTimeout(() => {
+ try {
+ runnable();
+ } finally {
+ clearActiveTimeout();
+ }
+ }, timeout);
+ } else {
+ runnable();
+ }
+ }
+
+
/**
* assert that the form exists and throw an exception in the case it does not
*
@@ -212,4 +268,6 @@ export module ExtLang {
throw makeException(new Error(), null, null, "Impl", "getForm", getMessage("ERR_FORM"));
}
}
+
+
}
\ No newline at end of file
diff --git a/api/src/client/typescript/faces/impl/util/XhrQueueController.ts b/api/src/client/typescript/faces/impl/util/XhrQueueController.ts
new file mode 100644
index 000000000..76af89da0
--- /dev/null
+++ b/api/src/client/typescript/faces/impl/util/XhrQueueController.ts
@@ -0,0 +1,112 @@
+import {IAsyncRunnable} from "./AsyncRunnable";
+import {ExtLang} from "./Lang";
+import debounce = ExtLang.debounce;
+
+/**
+ * A simple XHR queue controller
+ * following the async op -> next pattern
+ * Faces enforces for the XHR handling
+ */
+export class XhrQueueController<T extends IAsyncRunnable<any>> {
+ queue = [];
+ taskRunning = false;
+
+ constructor() {
+ }
+
+ /**
+ * executes or enqueues an element
+ * @param runnable the runnable (request) to be enqueued
+ * @param timeOut timeout if > 0 which defers the execution
+ * until the debounce window for the timeout is closed.
+ */
+ enqueue(runnable: T, timeOut: number = 0) {
+ debounce("xhrQueue", () => {
+ const requestHandler = this.enrichRunnable(runnable);
+ if (!this.taskRunning) {
+ this.signalTaskRunning();
+ requestHandler.start();
+ } else {
+ this.queue.push(requestHandler);
+ }
+ }, timeOut);
+ }
+
+ /**
+ * trigger the next element in the queue
+ * to be started!
+ */
+ next() {
+ this.updateTaskRunning();
+ const next = this.queue.shift();
+ next?.start();
+ }
+
+ /**
+ * clears and resets the queue
+ */
+ clear() {
+ this.queue.length = 0;
+ this.updateTaskRunning();
+ }
+
+ /**
+ * true if queue is empty
+ */
+ get isEmpty(): boolean {
+ return !this.queue.length;
+ }
+
+ /**
+ * Enriches the incoming async asyncRunnable
+ * with the error and next handling
+ * (aka: asyncRunnable is done -> next
+ * error -> clear queue
+ * @param asyncRunnable the async runnable which needs enrichment
+ * @private
+ */
+ private enrichRunnable(asyncRunnable: T) {
+ /**
+ * we can use the Promise pattern asyncrunnable uses
+ * to trigger queue control callbacks of next element
+ * and clear the queue (theoretically this
+ * would work with any promise)
+ */
+ try {
+ return asyncRunnable
+ .then(() => this.next())
+ .catch((e) => this.handleError(e));
+ } catch (e) {
+ this.handleError(e);
+ }
+ }
+
+
+ /**
+ * alerts the queue that a task is running
+ *
+ * @private
+ */
+ private signalTaskRunning() {
+ this.taskRunning = true;
+ }
+
+ /**
+ * updates the task running status according to the current queue
+ * @private
+ */
+ private updateTaskRunning() {
+ this.taskRunning = !this.isEmpty;
+ }
+
+ /**
+ * standard error handling
+ * we clear the queue and then bomb out
+ * @param e
+ * @private
+ */
+ private handleError(e) {
+ this.clear();
+ throw e;
+ }
+}
\ No newline at end of file
diff --git a/api/src/client/typescript/faces/impl/xhrCore/EventData.ts b/api/src/client/typescript/faces/impl/xhrCore/EventData.ts
index fab410a31..a04f0ebb6 100644
--- a/api/src/client/typescript/faces/impl/xhrCore/EventData.ts
+++ b/api/src/client/typescript/faces/impl/xhrCore/EventData.ts
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import {Config, DQ} from "mona-dish";
-import {BEGIN, CTX_PARAM_REQ_PASS_THR, EVENT, P_PARTIAL_SOURCE, SOURCE} from "../core/Const";
+import {BEGIN, CTX_PARAM_REQ_PASS_THR, EVENT, P_AJAX_SOURCE, SOURCE} from "../core/Const";
export class EventData implements IEventData{
type: string;
@@ -32,8 +32,8 @@ export class EventData implements IEventData{
eventData.status = name;
let sourceId: string = context.getIf(SOURCE)
- .orElseLazy(() => context.getIf(P_PARTIAL_SOURCE).value)
- .orElseLazy(() => context.getIf(CTX_PARAM_REQ_PASS_THR, P_PARTIAL_SOURCE).value)
+ .orElseLazy(() => context.getIf(P_AJAX_SOURCE).value)
+ .orElseLazy(() => context.getIf(CTX_PARAM_REQ_PASS_THR, P_AJAX_SOURCE).value)
.value;
if (sourceId) {
eventData.source = DQ.byId(sourceId, true).first().value.value;
diff --git a/api/src/client/typescript/faces/impl/xhrCore/ResponseProcessor.ts b/api/src/client/typescript/faces/impl/xhrCore/ResponseProcessor.ts
index 3be0f837a..a27def1ed 100644
--- a/api/src/client/typescript/faces/impl/xhrCore/ResponseProcessor.ts
+++ b/api/src/client/typescript/faces/impl/xhrCore/ResponseProcessor.ts
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-import {Config, DomQuery, DomQueryCollector, DQ, DQ$, Lang, LazyStream, Stream, XMLQuery} from "mona-dish";
+import {Config, DomQuery, DQ, DQ$, Lang, XMLQuery} from "mona-dish";
import {Implementation} from "../AjaxImpl";
import {Assertions} from "../util/Assertions";
import {IResponseProcessor} from "./IResponseProcessor";
@@ -46,7 +46,7 @@ import {
ON_EVENT,
P_CLIENT_WINDOW,
P_EXECUTE,
- P_PARTIAL_SOURCE,
+ P_AJAX_SOURCE,
P_RENDER,
P_RENDER_OVERRIDE,
P_VIEWSTATE,
@@ -65,6 +65,8 @@ import {
import {ExtConfig, ExtDomQuery} from "../util/ExtDomQuery";
import {HiddenInputBuilder} from "../util/HiddenInputBuilder";
import trim = Lang.trim;
+import {ExtLang} from "../util/Lang";
+import ofAssoc = ExtLang.ofAssoc;
/**
@@ -113,8 +115,8 @@ export class ResponseProcessor implements IResponseProcessor {
const nodesToAdd = (shadowHead.tagName.value === "HEAD") ? shadowHead.childNodes : shadowHead;
// this is stored for "post" processing
// after the rest of the "physical build up", head before body
- const scriptElements = nodesToAdd.stream
- .filter(item => scriptTags.indexOf(item.tagName.orElse("").value) != -1).collect(new DomQueryCollector());
+ const scriptElements = new DomQuery(...nodesToAdd.asArray
+ .filter(item => scriptTags.indexOf(item.tagName.orElse("").value) != -1));
this.addToHeadDeferred(scriptElements);
}
@@ -174,7 +176,7 @@ export class ResponseProcessor implements IResponseProcessor {
*/
const mergedErrorData = new ExtConfig({});
- mergedErrorData.assign(SOURCE).value = this.externalContext.getIf(P_PARTIAL_SOURCE).get(0).value;
+ mergedErrorData.assign(SOURCE).value = this.externalContext.getIf(P_AJAX_SOURCE).get(0).value;
mergedErrorData.assign(ERROR_NAME).value = node.querySelectorAll(ERROR_NAME).textContent(EMPTY_STR);
mergedErrorData.assign(ERROR_MESSAGE).value = node.querySelectorAll(ERROR_MESSAGE).cDATAAsString;
@@ -346,15 +348,15 @@ export class ResponseProcessor implements IResponseProcessor {
* as last lifecycle step, before going into the next request.
*/
fixViewStates() {
- Stream.ofAssoc<StateHolder>(this.internalContext.getIf(APPLIED_VST).orElse({}).value)
- .each(([, value]) => {
+ ofAssoc(this.internalContext.getIf(APPLIED_VST).orElse({}).value)
+ .forEach(([, value]) => {
const namingContainerId = this.internalContext.getIf(NAMING_CONTAINER_ID);
const namedViewRoot = !!this.internalContext.getIf(NAMED_VIEWROOT).value
const affectedForms = this.getContainerForms(namingContainerId)
.filter(affectedForm => this.isInExecuteOrRender(affectedForm));
this.appendViewStateToForms(affectedForms, namedViewRoot, value.value, namingContainerId.orElse("").value);
- });
+ })
}
@@ -364,8 +366,8 @@ export class ResponseProcessor implements IResponseProcessor {
* is done.
*/
fixClientWindow() {
- Stream.ofAssoc<StateHolder>(this.internalContext.getIf(APPLIED_CLIENT_WINDOW).orElse({}).value)
- .each(([, value]) => {
+ ofAssoc(this.internalContext.getIf(APPLIED_CLIENT_WINDOW).orElse({}).value)
+ .forEach(([, value]) => {
const namingContainerId = this.internalContext.getIf(NAMING_CONTAINER_ID);
const namedViewRoot = !!this.internalContext.getIf(NAMED_VIEWROOT).value;
const affectedForms = this.getContainerForms(namingContainerId)
@@ -522,7 +524,7 @@ export class ResponseProcessor implements IResponseProcessor {
.orElseLazy(() => this.externalContext.getIf($nsp(P_RENDER)).value)
.orElse(IDENT_NONE).value.split(/\s+/gi);
const executeAndRenders = executes.concat(...renders);
- return LazyStream.of(...executeAndRenders).filter(nameOrId => {
+ return [...executeAndRenders].filter(nameOrId => {
if ([IDENT_ALL, IDENT_NONE].indexOf(nameOrId) != -1) {
return true;
}
@@ -532,7 +534,7 @@ export class ResponseProcessor implements IResponseProcessor {
return affectedForm.matchesSelector(NAME_OR_ID) ||
affectedForm.querySelectorAll(NAME_OR_ID).isPresent() ||
affectedForm.firstParent(NAME_OR_ID).isPresent();
- }).first().isPresent();
+ }).length > 0;
}
/**
diff --git a/api/src/client/typescript/faces/impl/xhrCore/XhrFormData.ts b/api/src/client/typescript/faces/impl/xhrCore/XhrFormData.ts
index 83937708a..dff98415e 100644
--- a/api/src/client/typescript/faces/impl/xhrCore/XhrFormData.ts
+++ b/api/src/client/typescript/faces/impl/xhrCore/XhrFormData.ts
@@ -13,19 +13,21 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import {Config, DQ, FormDataCollector, Stream} from "mona-dish";
+import {Config, DQ} from "mona-dish";
import {$nsp, EMPTY_STR, IDENT_NONE, P_VIEWSTATE} from "../core/Const";
import {
encodeFormData,
- fixEmmptyParameters, getFormInputsAsStream
+ fixEmptyParameters, getFormInputsAsArr
} from "../util/FileUtils";
+import {ExtLang} from "../util/Lang";
+import ofAssoc = ExtLang.ofAssoc;
+import {Es2019Array} from "mona-dish";
type ParamsMapper<V, K> = (key: V, item: K) => [V, K];
const defaultParamsMapper: ParamsMapper<string, any> = (key, item) => [key, item];
-
/**
* A unified form data class
* which builds upon our configuration.
@@ -76,13 +78,17 @@ export class XhrFormData extends Config {
*/
toFormData(): FormData {
/*
- * expands key: [item1, item2]
- * to: [{key: key, value: item1}, {key: key, value: item2}]
- */
- let expandAssocArray = ([key, item]) =>
- Stream.of(...(item as Array<any>)).map(value => {
- return {key, value};
- });
+ * expands key: [item1, item2]
+ * to: [{key: key, value: item1}, {key: key, value: item2}]
+ */
+ let expandValueArrays = ([key, item]) => {
+ if (Array.isArray(item)) {
+ return new Es2019Array(...item).map(value => {
+ return {key, value}
+ })
+ }
+ return [{key, value: item}]
+ }
/*
* remaps the incoming {key, value} tuples
@@ -96,10 +102,13 @@ export class XhrFormData extends Config {
/*
* collects everything into a FormData object
*/
- return Stream.ofAssoc(this.value)
- .flatMap(expandAssocArray)
+ return ofAssoc(this.value)
+ .flatMap(expandValueArrays)
.map(remapForNamingContainer)
- .collect(new FormDataCollector() as any);
+ .reduce((formData: FormData, {key, value}: any) => {
+ formData.append(key, value);
+ return formData;
+ }, new FormData()) as FormData;
}
/**
@@ -113,6 +122,7 @@ export class XhrFormData extends Config {
/**
* generic post init code, for now, this performs some post assign data post-processing
+ * @param rootElement the root element which knows the request type (usually a form)
* @param executes the executable dom nodes which need to be processed into the form data, which we can send
* in our ajax request
*/
@@ -139,20 +149,24 @@ export class XhrFormData extends Config {
/**
* determines fields to submit
- * @param {Object} targetBuf - the target form buffer receiving the data
* @param {Node} parentItem - form element item is nested in
* @param {Array} partialIds - ids fo PPS
*/
- private encodeSubmittableFields(parentItem: DQ, partialIds ?: string[]) {
+ private encodeSubmittableFields(parentItem: DQ, partialIds: string[] = []) {
- const formInputs = getFormInputsAsStream(parentItem);
const mergeIntoThis = ([key, value]) => this.append(key).value = value;
const namingContainerRemap = ([key, value]) => this.paramsMapper(key as string, value);
- formInputs
- .map(fixEmmptyParameters)
+ const remappedPartialIds = partialIds.map(partialId => this.remapKeyForNamingContainer(partialId));
+ const partialIdsFilter = ([key, value]) => (!remappedPartialIds.length || key.indexOf("@") == 0) ? true :
+ remappedPartialIds.indexOf(key) != -1;
+
+ let inputs = getFormInputsAsArr(parentItem);
+ inputs
+ .map(fixEmptyParameters)
.map(namingContainerRemap)
- .each(mergeIntoThis);
+ .filter(partialIdsFilter)
+ .forEach(mergeIntoThis);
}
private remapKeyForNamingContainer(key: string): string {
diff --git a/api/src/client/typescript/faces/impl/xhrCore/XhrRequest.ts b/api/src/client/typescript/faces/impl/xhrCore/XhrRequest.ts
index 642fa61e4..b23417ac1 100644
--- a/api/src/client/typescript/faces/impl/xhrCore/XhrRequest.ts
+++ b/api/src/client/typescript/faces/impl/xhrCore/XhrRequest.ts
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-import {AsyncRunnable} from "../util/AsyncRunnable";
-import {Config, DQ, DQ$, Stream} from "mona-dish";
+import {AsyncRunnable, IAsyncRunnable} from "../util/AsyncRunnable";
+import {Config, DQ} from "mona-dish";
import {Implementation} from "../AjaxImpl";
import {XhrFormData} from "./XhrFormData";
@@ -41,7 +41,7 @@ import {
STATE_EVT_TIMEOUT,
STD_ACCEPT,
URL_ENCODED,
- VAL_AJAX, IDENT_NONE
+ VAL_AJAX, IDENT_NONE, CTX_PARAM_SRC_FRM_ID, CTX_PARAM_SRC_CTL_ID, CTX_PARAM_PPS
} from "../core/Const";
import {
resolveFinalUrl,
@@ -54,6 +54,10 @@ import {ExtConfig} from "../util/ExtDomQuery";
/**
* Faces XHR Request Wrapper
* as AsyncRunnable for our Asynchronous queue
+ * This means from the outside the
+ * xhr request is similar to a Promise in a way
+ * that you can add then and catch and finally callbacks.
+ *
*
* The idea is that we basically just enqueue
* a single ajax request into our queue
@@ -62,60 +66,43 @@ import {ExtConfig} from "../util/ExtDomQuery";
*
*/
-export class XhrRequest implements AsyncRunnable<XMLHttpRequest> {
+export class XhrRequest extends AsyncRunnable<XMLHttpRequest> {
private responseContext: Config;
private stopProgress = false;
- /**
- * helper support so that we do not have to drag in Promise shims
- */
- private catchFunctions: Array<Function> = [];
- private thenFunctions: Array<Function> = [];
+ private xhrObject = new XMLHttpRequest();
/**
* Required Parameters
*
- * @param source the issuing element
- * @param sourceForm the form which is related to the issuing element
* @param requestContext the request context with all pass through values
- *
- * Optional Parameters
- *
* @param internalContext internal context with internal info which is passed through, not used by the user
- * @param partialIdsArray an optional restricting partial ids array for encoding
+ * Optional Parameters
* @param timeout optional xhr timeout
* @param ajaxType optional request type, default "POST"
* @param contentType optional content type, default "application/x-www-form-urlencoded"
- * @param xhrObject optional xhr object which must fulfill the XMLHTTPRequest api, default XMLHttpRequest
*/
constructor(
- private source: DQ,
- private sourceForm: DQ,
private requestContext: ExtConfig,
private internalContext: Config,
- private partialIdsArray = [],
private timeout = NO_TIMEOUT,
private ajaxType = REQ_TYPE_POST,
- private contentType = URL_ENCODED,
- private xhrObject = new XMLHttpRequest()
+ private contentType = URL_ENCODED
) {
-
+ super();
// we omit promises here because we have to deal with cancel functionality,
// and promises to not provide that (yet) instead we have our async queue
// which uses an api internally, which is very close to promises
- this.registerXhrCallbacks((data: any) => {
- this.resolve(data)
- }, (data: any) => {
- this.reject(data)
- });
+ this.registerXhrCallbacks((data: any) => this.resolve(data), (data: any) => this.reject(data));
}
- start(): AsyncRunnable<XMLHttpRequest> {
+ start(): IAsyncRunnable<XMLHttpRequest> {
let ignoreErr = failSaveExecute;
let xhrObject = this.xhrObject;
+ let sourceForm = DQ.byId(this.internalContext.getIf(CTX_PARAM_SRC_FRM_ID).value)
let executesArr = () => {
return this.requestContext.getIf(CTX_PARAM_REQ_PASS_THR, P_EXECUTE).get(IDENT_NONE).value.split(/\s+/gi);
@@ -130,15 +117,20 @@ export class XhrRequest implements AsyncRunnable<XMLHttpRequest> {
// whatever the formData object delivers
// the partialIdsArray arr is almost deprecated legacy code where we allowed to send a separate list of partial
// ids for reduced load and server processing, this will be removed soon, we can handle the same via execute
- // anyway TODO reimplement the partial ids array, we still do not have it in jsf the way we need it
- let formData: XhrFormData = new XhrFormData(this.sourceForm, resoveNamingContainerMapper(this.internalContext), executesArr(), this.partialIdsArray);
+ const executes = executesArr();
+ const partialIdsArray = this.internalContext.getIf(CTX_PARAM_PPS).value === true ? executes : [];
+ const formData: XhrFormData = new XhrFormData(
+ sourceForm,
+ resoveNamingContainerMapper(this.internalContext),
+ executes, partialIdsArray
+ );
this.contentType = formData.isMultipartRequest ? "undefined" : this.contentType;
// next step the pass through parameters are merged in for post params
this.requestContext.$nspEnabled = false;
- let requestContext = this.requestContext;
- let requestPassThroughParams = requestContext.getIf(CTX_PARAM_REQ_PASS_THR) as ExtConfig;
+ const requestContext = this.requestContext;
+ const requestPassThroughParams = requestContext.getIf(CTX_PARAM_REQ_PASS_THR) as ExtConfig;
// we are turning off here the jsf, faces remapping because we are now dealing with
// pass-through parameters
@@ -149,6 +141,9 @@ export class XhrRequest implements AsyncRunnable<XMLHttpRequest> {
try {
formData.shallowMerge(requestPassThroughParams, true, true);
} finally {
+ // unfortunately as long as we support
+ // both namespaces we have to keep manual control
+ // on the key renaming before doing ops like deep copy
this.requestContext.$nspEnabled = true;
requestPassThroughParams.$nspEnabled = true;
}
@@ -156,15 +151,15 @@ export class XhrRequest implements AsyncRunnable<XMLHttpRequest> {
this.responseContext = requestPassThroughParams.deepCopy;
// we have to shift the internal passthroughs around to build up our response context
- let responseContext = this.responseContext;
+ const responseContext = this.responseContext;
responseContext.assign(CTX_PARAM_MF_INTERNAL).value = this.internalContext.value;
- // per spec the onevent and onerror handlers must be passed through to the response
+ // per spec the onEvent and onError handlers must be passed through to the response
responseContext.assign(ON_EVENT).value = requestContext.getIf(ON_EVENT).value;
responseContext.assign(ON_ERROR).value = requestContext.getIf(ON_ERROR).value;
- xhrObject.open(this.ajaxType, resolveFinalUrl(this.sourceForm, formData, this.ajaxType), true);
+ xhrObject.open(this.ajaxType, resolveFinalUrl(sourceForm, formData, this.ajaxType), true);
// adding timeout
this.timeout ? xhrObject.timeout = this.timeout : null;
@@ -194,40 +189,15 @@ export class XhrRequest implements AsyncRunnable<XMLHttpRequest> {
cancel() {
try {
+ // this causes onError to be called where the error
+ // handling takes over
this.xhrObject.abort();
} catch (e) {
this.handleError(e);
}
}
- resolve(data: any) {
- Stream.of(...this.thenFunctions).reduce((inputVal: any, thenFunc: any) => {
- return thenFunc(inputVal);
- }, data);
- }
- reject(data: any) {
- Stream.of(...this.catchFunctions).reduce((inputVal: any, catchFunc: any) => {
- return catchFunc(inputVal);
- }, data);
- }
-
- catch(func: (data: any) => any): AsyncRunnable<XMLHttpRequest> {
- this.catchFunctions.push(func);
- return this;
- }
-
- finally(func: () => void): AsyncRunnable<XMLHttpRequest> {
- // no ie11 support we probably are going to revert to shims for that one
- this.catchFunctions.push(func);
- this.thenFunctions.push(func);
- return this;
- }
-
- then(func: (data: any) => any): AsyncRunnable<XMLHttpRequest> {
- this.thenFunctions.push(func);
- return this;
- }
/**
* attaches the internal event and processing
@@ -237,7 +207,7 @@ export class XhrRequest implements AsyncRunnable<XMLHttpRequest> {
* @param reject
*/
private registerXhrCallbacks(resolve: Consumer<any>, reject: Consumer<any>) {
- let xhrObject = this.xhrObject;
+ const xhrObject = this.xhrObject;
xhrObject.onabort = () => {
this.onAbort(reject);
@@ -253,13 +223,17 @@ export class XhrRequest implements AsyncRunnable<XMLHttpRequest> {
};
xhrObject.onerror = (errorData: any) => {
- // some browsers trigger an error when cancelling a request internally
+ // some browsers trigger an error when cancelling a request internally, or when
+ // cancel is called from outside
// in this case we simply ignore the request and clear up the queue, because
// it is not safe anymore to proceed with the current queue
// This bypasses a Safari issue where it keeps requests hanging after page unload
// and then triggers a cancel error on then instead of just stopping
// and clearing the code
if(this.isCancelledResponse(this.xhrObject)) {
+ /*
+ * this triggers the catch chain and after that finally
+ */
reject();
this.stopProgress = true;
return;
@@ -307,14 +281,12 @@ export class XhrRequest implements AsyncRunnable<XMLHttpRequest> {
private handleMalFormedXML(resolve: Function) {
this.stopProgress = true;
- let errorData = {
+ const errorData = {
type: ERROR,
status: MALFORMEDXML,
responseCode: 200,
responseText: this.xhrObject?.responseText,
- // we remap the element just in case it gets replaced
- // it will be unremapped
- source: this.source.id.value
+ source: this.internalContext.getIf(CTX_PARAM_SRC_CTL_ID).value
};
try {
this.handleError(errorData, true);
@@ -331,16 +303,22 @@ export class XhrRequest implements AsyncRunnable<XMLHttpRequest> {
if (this.stopProgress) {
return;
}
+ /**
+ * now call the then chain
+ */
resolve(data);
}
private onError(errorData: any, reject: Consumer<any>) {
this.handleError(errorData);
+ /*
+ * this triggers the catch chain and after that finally
+ */
reject();
}
private sendRequest(formData: XhrFormData) {
- let isPost = this.ajaxType != REQ_TYPE_GET;
+ const isPost = this.ajaxType != REQ_TYPE_GET;
if (formData.isMultipartRequest) {
// in case of a multipart request we send in a formData object as body
this.xhrObject.send((isPost) ? formData.toFormData() : null);
@@ -354,7 +332,7 @@ export class XhrRequest implements AsyncRunnable<XMLHttpRequest> {
* other helpers
*/
private sendEvent(evtType: string) {
- let eventData = EventData.createFromRequest(this.xhrObject, this.requestContext, evtType);
+ const eventData = EventData.createFromRequest(this.xhrObject, this.requestContext, evtType);
try {
// User code error, we might cover
// this in onError, but also we cannot swallow it.
@@ -366,14 +344,15 @@ export class XhrRequest implements AsyncRunnable<XMLHttpRequest> {
} catch (e) {
e.source = e?.source ?? this.requestContext.getIf(SOURCE).value;
this.handleError(e);
+
throw e;
}
}
private handleError(exception, responseFormatError: boolean = false) {
- let errorData = (responseFormatError) ? ErrorData.fromHttpConnection(exception.source, exception.type, exception.status, exception.responseText, exception.responseCode, exception.status) : ErrorData.fromClient(exception);
+ const errorData = (responseFormatError) ? ErrorData.fromHttpConnection(exception.source, exception.type, exception.status, exception.responseText, exception.responseCode, exception.status) : ErrorData.fromClient(exception);
+ const eventHandler = resolveHandlerFunc(this.requestContext, this.responseContext, ON_ERROR);
- let eventHandler = resolveHandlerFunc(this.requestContext, this.responseContext, ON_ERROR);
Implementation.sendError(errorData, eventHandler);
}
}
\ No newline at end of file
diff --git a/api/src/client/typescript/faces/myfaces/OamSubmit.ts b/api/src/client/typescript/faces/myfaces/OamSubmit.ts
index bed3b7d57..c094cc358 100644
--- a/api/src/client/typescript/faces/myfaces/OamSubmit.ts
+++ b/api/src/client/typescript/faces/myfaces/OamSubmit.ts
@@ -14,7 +14,8 @@
* limitations under the License.
*/
-import {DQ, Stream} from "mona-dish";
+import {DQ} from "mona-dish";
+import {ExtLang} from "../impl/util/Lang";
/**
* legacy code to enable various aspects
@@ -28,6 +29,7 @@ import {DQ, Stream} from "mona-dish";
* we might move the code over in the future, but for now a straight 1:1 port suffices
*/
export module oam {
+ import ofAssoc = ExtLang.ofAssoc;
/**
* sets a hidden input field
* @param formName the formName
@@ -86,8 +88,8 @@ export module oam {
if (window?.myfaces?.core?.config?.autoScroll && (window as any)?.getScrolling) {
myfaces.oam.setHiddenInput(formName, 'autoScroll', (window as any)?.getScrolling());
}
- let paramsStream: Stream<[string, any]> = Array.isArray(params) ? Stream.of(...params) : Stream.ofAssoc(params);
- paramsStream.each(([key, data]) => myfaces.oam.setHiddenInput(formName, key, data));
+ let paramsStream: Array<[string, any]> = Array.isArray(params) ? [...params] : ofAssoc(params);
+ paramsStream.forEach(([key, data]) => myfaces.oam.setHiddenInput(formName, key, data));
//we call the namespaced function, to allow decoration, via a direct call we would
myfaces.oam.setHiddenInput(formName, `${formName}:_idcl`, linkId ?? '');
@@ -102,8 +104,6 @@ export module oam {
(form.getAsElem(0).value as HTMLFormElement).setAttribute("target", target);
}
-
-
const result = formElement?.onsubmit?.(null);
try {
@@ -120,7 +120,7 @@ export module oam {
}
// noinspection JSUnusedLocalSymbols
- paramsStream.each(([key, data]) => {
+ paramsStream.forEach(([key, data]) => {
myfaces.oam.clearHiddenInput(formName, key);
});
myfaces.oam.clearHiddenInput(formName, `${formName}:_idcl`);
diff --git a/api/src/client/typescript/faces/test/frameworkBase/_ext/monadish/DomQueryTest.spec.ts b/api/src/client/typescript/faces/test/frameworkBase/_ext/monadish/DomQueryTest.spec.ts
index f21317d33..4b7b0874b 100644
--- a/api/src/client/typescript/faces/test/frameworkBase/_ext/monadish/DomQueryTest.spec.ts
+++ b/api/src/client/typescript/faces/test/frameworkBase/_ext/monadish/DomQueryTest.spec.ts
@@ -16,20 +16,21 @@
import {expect} from 'chai';
import {describe, it} from 'mocha';
-import {ArrayCollector, DomQuery, DomQueryCollector, Lang, LazyStream} from "mona-dish";
-import trim = Lang.trim;
import {from} from "rxjs";
-
+import {ArrayCollector, Config, DomQuery, DomQueryCollector, Lang, LazyStream, Stream} from "mona-dish/dist/js/umd";
+import {tobagoSheetWithHeader} from "./markups/tobago-with-header";
+import {tobagoSheetWithoutHeader} from "./markups/tobago-without-header";
const jsdom = require("jsdom");
const {JSDOM} = jsdom;
+import trim = Lang.trim;
+
(global as any).window = {}
let dom = null;
describe('DOMQuery tests', function () {
beforeEach(function () {
- // language=HTML
dom = new JSDOM(`
<!DOCTYPE html>
<html lang="en">
@@ -86,14 +87,14 @@ describe('DOMQuery tests', function () {
let probe1 = new DomQuery(window.document.body);
let probe2 = DomQuery.querySelectorAll("div");
- let o1 = from(probe1.stream);
- let o2 = from(probe2.stream);
+ let o1 = from(Stream.ofDataSource(probe1));
+ let o2 = from(Stream.ofDataSource(probe2));
let cnt1 = 0;
let isDQuery = false;
let cnt2 = 0;
- o1.subscribe(() => {
+ o1.subscribe((item: any) => {
cnt1++;
});
@@ -112,14 +113,14 @@ describe('DOMQuery tests', function () {
let probe1 = new DomQuery(window.document.body);
let probe2 = DomQuery.querySelectorAll("div");
- let o1 = from(probe1.stream);
- let o2 = from(probe2.stream);
+ let o1 = from(Stream.ofDataSource(probe1));
+ let o2 = from(Stream.ofDataSource(probe2));
let cnt1 = 0;
let isDQuery = false;
let cnt2 = 0;
- o1.subscribe(() => {
+ o1.subscribe((item: any) => {
cnt1++;
});
@@ -183,7 +184,7 @@ describe('DOMQuery tests', function () {
it('domquery ops test2 eachNode', function () {
let probe2 = DomQuery.querySelectorAll("div");
let noIter = 0;
- probe2.each((item, cnt) => {
+ probe2 = probe2.each((item, cnt) => {
expect(item instanceof DomQuery).to.be.true;
expect(noIter == cnt).to.be.true;
noIter++;
@@ -298,28 +299,61 @@ describe('DOMQuery tests', function () {
expect(DomQuery.querySelectorAll("#insertedAfter2").isPresent()).to.be.true;
});
+ it('do not create new <html> tag on <header', function () {
+
+ const fromMarkupWithHeader = DomQuery.fromMarkup(tobagoSheetWithHeader);
+ const fromMarkupWithoutHeader = DomQuery.fromMarkup(tobagoSheetWithoutHeader);
+
+ expect(fromMarkupWithHeader.tagName.value === "HTML").to.be.false;
+ expect(fromMarkupWithoutHeader.tagName.value === "HTML").to.be.false;
+ });
+
+
+ it('do not falsely assume standard tag', function () {
+
+ const fromMarkup1 = DomQuery.fromMarkup(`
+ <head-mine>booga</head-mine>
+ `);
+
+ const fromMarkup2 = DomQuery.fromMarkup(`
+ <body_mine>booga</body_mine>
+
+ `);
+ expect(fromMarkup1.tagName.value === "HTML").to.be.false;
+ expect(fromMarkup1.tagName.value === "HTML").to.be.false;
+ expect(fromMarkup1.tagName.value === "HEAD").to.be.false;
+ expect(fromMarkup2.tagName.value === "BODY").to.be.false;
+ });
+
+
it('it must stream', function () {
let probe1 = new DomQuery(document).querySelectorAll("div");
- let coll: Array<any> = probe1.stream.collect(new ArrayCollector());
+ let coll: Array<any> = Stream.ofDomQuery(probe1).collect(new ArrayCollector());
expect(coll.length == 4).to.be.true;
- coll = probe1.lazyStream.collect(new ArrayCollector());
+ coll = LazyStream.ofDomQuery(probe1).collect(new ArrayCollector());
expect(coll.length == 4).to.be.true;
});
- it('it must stream to a domquery', function () {
+ it('it must stream - DQ API (dynamically added)', function () {
let probe1 = new DomQuery(document).querySelectorAll("div");
- let coll: DomQuery = probe1.stream.collect(new DomQueryCollector());
+ let coll: Array<any> = probe1.asArray;
expect(coll.length == 4).to.be.true;
+ });
- coll = probe1.lazyStream.collect(new DomQueryCollector());
+ it('it must stream to a domquery', function () {
+ let probe1 = new DomQuery(document).querySelectorAll("div");
+ let coll: DomQuery = Stream.ofDataSource(probe1).collect(new DomQueryCollector());
+ expect(coll.length == 4).to.be.true;
+ probe1.reset();
+ coll = LazyStream.ofStreamDataSource(probe1).collect(new DomQueryCollector());
expect(coll.length == 4).to.be.true;
});
it('it must have parents', function () {
let probe1 = new DomQuery(document).querySelectorAll("div");
- let coll: Array<any> = probe1.firstParent("body").stream.collect(new ArrayCollector());
+ let coll: Array<any> = Stream.ofDataSource(probe1.parentsWhileMatch("body")).collect(new ArrayCollector());
expect(coll.length == 1).to.be.true;
});
@@ -342,22 +376,90 @@ describe('DOMQuery tests', function () {
expect(innerHtml.indexOf("id_x_1_1") > innerHtml.indexOf("id_x_0_1")).to.be.true;
})
+
+ it("must have a working replace", function () {
+ let probe1 = new DomQuery(document).byId("id_1");
+ probe1.replace(DomQuery.fromMarkup(` <div id="id_x_0"></div><div id="id_x_1"></div>`));
+
+
+ expect(DomQuery.querySelectorAll("div").length).to.eq(5);
+
+ let innerHtml = DomQuery.querySelectorAll("body").innerHTML;
+
+ expect(innerHtml.indexOf("id_x_0") > 0).to.be.true;
+ expect(innerHtml.indexOf("id_x_0") < innerHtml.indexOf("id_2")).to.be.true;
+ expect(innerHtml.indexOf("id_x_0") < innerHtml.indexOf("id_3")).to.be.true;
+
+ expect(innerHtml.indexOf("id_x_0") < innerHtml.indexOf("id_x_1")).to.be.true;
+
+ expect(innerHtml.indexOf("id_x_1") > 0).to.be.true;
+ expect(innerHtml.indexOf("id_x_1") < innerHtml.indexOf("id_2")).to.be.true;
+ expect(innerHtml.indexOf("id_x_1") < innerHtml.indexOf("id_3")).to.be.true;
+
+ expect(innerHtml.indexOf("id_1") == -1).to.be.true;
+ })
+
+ it("must have a working replace - 2", function () {
+ let probe1 = new DomQuery(document).byId("id_2");
+ probe1.replace(DomQuery.fromMarkup(` <div id="id_x_0"></div><div id="id_x_1"></div>`));
+
+
+ expect(DomQuery.querySelectorAll("div").length).to.eq(5);
+
+ let innerHtml = DomQuery.querySelectorAll("body").innerHTML;
+ expect(innerHtml.indexOf("id_x_0") > innerHtml.indexOf("id_1")).to.be.true;
+
+ expect(innerHtml.indexOf("id_x_0") > 0).to.be.true;
+ expect(innerHtml.indexOf("id_x_0") > innerHtml.indexOf("id_0")).to.be.true;
+ expect(innerHtml.indexOf("id_x_0") < innerHtml.indexOf("id_3")).to.be.true;
+
+ expect(innerHtml.indexOf("id_x_1") > 0).to.be.true;
+ expect(innerHtml.indexOf("id_x_1") > innerHtml.indexOf("id_0")).to.be.true;
+ expect(innerHtml.indexOf("id_x_1") < innerHtml.indexOf("id_3")).to.be.true;
+
+ expect(innerHtml.indexOf("id_2") == -1).to.be.true;
+ })
+
+ it("must have a working replace - 3", function () {
+ let probe1 = new DomQuery(document).byId("id_4");
+ probe1.replace(DomQuery.fromMarkup(` <div id="id_x_0"></div><div id="id_x_1"></div>`));
+
+
+ expect(DomQuery.querySelectorAll("div").length).to.eq(5);
+
+ let innerHtml = DomQuery.querySelectorAll("body").innerHTML;
+
+ expect(innerHtml.indexOf("id_x_0") > 0).to.be.true;
+ expect(innerHtml.indexOf("id_x_0") > innerHtml.indexOf("id_1")).to.be.true;
+ expect(innerHtml.indexOf("id_x_0") > innerHtml.indexOf("id_2")).to.be.true;
+ expect(innerHtml.indexOf("id_x_0") > innerHtml.indexOf("id_3")).to.be.true;
+
+ expect(innerHtml.indexOf("id_x_0") < innerHtml.indexOf("id_x_1")).to.be.true;
+
+ expect(innerHtml.indexOf("id_x_1") > 0).to.be.true;
+ expect(innerHtml.indexOf("id_x_1") > innerHtml.indexOf("id_1")).to.be.true;
+ expect(innerHtml.indexOf("id_x_1") > innerHtml.indexOf("id_2")).to.be.true;
+ expect(innerHtml.indexOf("id_x_1") > innerHtml.indexOf("id_3")).to.be.true;
+
+ expect(innerHtml.indexOf("id_4") == -1).to.be.true;
+ })
+
it("must have a working input handling", function () {
DomQuery.querySelectorAll("body").innerHTML = `<form id="blarg">
<div id="embed1">
- <input type="text" id="id_1" name="id_1" value="id_1_val">
- <input type="text" id="id_2" name="id_2" value="id_2_val" disabled="disabled">
- <textarea id="id_3" name="id_3">textareaVal</textarea>
+ <input type="text" id="id_1" name="id_1" value="id_1_val"></input>
+ <input type="text" id="id_2" name="id_2" value="id_2_val" disabled="disabled"> </input>
+ <textarea type="text" id="id_3" name="id_3">textareaVal</textarea>
<fieldset>
- <input type="radio" id="mc" name="cc_1" value="Mastercard" checked="checked">
+ <input type="radio" id="mc" name="cc_1" value="Mastercard" checked="checked"></input>
<label for="mc"> Mastercard</label>
- <input type="radio" id="vi" name="cc_1" value="Visa">
+ <input type="radio" id="vi" name="cc_1" value="Visa"></input>
<label for="vi"> Visa</label>
- <input type="radio" id="ae" name="cc_1" value="AmericanExpress">
+ <input type="radio" id="ae" name="cc_1" value="AmericanExpress"></input>
<label for="ae"> American Express</label>
</fieldset>
- <select id="val_5" name="val_5" size="5">
+ <select id="val_5" name="val_5" name="top5" size="5">
<option>barg</option>
<option>jjj</option>
<option selected>akaka</option>
@@ -375,22 +477,22 @@ describe('DOMQuery tests', function () {
let length2 = DomQuery.byId("embed1").elements.length;
expect(length2 == 8).to.be.true;
- let count = DomQuery.byId("embed1").elements
- .stream.map(item => item.disabled ? 1 : 0)
+ let count = Stream.ofDataSource(DomQuery.byId("embed1").elements)
+ .map<number>(item => item.disabled ? 1 : 0)
.reduce((val1, val2) => val1 + val2, 0);
expect(count.value).to.eq(1);
- DomQuery.byId("embed1").elements
- .stream.filter(item => item.disabled)
+ Stream.ofDataSource(DomQuery.byId("embed1").elements)
+ .filter(item => item.disabled)
.each(item => item.disabled = false);
- count = DomQuery.byId("embed1").elements
- .stream.map(item => item.disabled ? 1 : 0)
+ count = Stream.ofDataSource(DomQuery.byId("embed1").elements)
+ .map<number>(item => item.disabled ? 1 : 0)
.reduce((val1, val2) => val1 + val2, 0);
expect(count.value).to.eq(0);
- count = DomQuery.byId("embed1").elements
- .stream.map(item => item.attr("checked").isPresent() ? 1 : 0)
+ count = Stream.ofDataSource(DomQuery.byId("embed1").elements)
+ .map<number>(item => item.attr("checked").isPresent() ? 1 : 0)
.reduce((val1, val2) => val1 + val2, 0);
expect(count.value).to.eq(1);
@@ -403,7 +505,7 @@ describe('DOMQuery tests', function () {
DomQuery.byId("id_3").inputValue.value = "hello world";
expect(DomQuery.byId("id_3").inputValue.value).to.eq("hello world");
- let cfg = DomQuery.querySelectorAll("form").elements.encodeFormElement();
+ let cfg = new Config(DomQuery.querySelectorAll("form").elements.encodeFormElement());
expect(cfg.getIf("id_1").value[0]).to.eq("booga");
expect(cfg.getIf("id_2").value[0]).to.eq("id_2_val");
expect(cfg.getIf("id_3").value[0]).to.eq("hello world");
@@ -451,17 +553,22 @@ describe('DOMQuery tests', function () {
//-->
</script>
- <style>
+ <style type="text/css">
#first {
border: 1px solid black;
}
</style>
+
+ <link rel="stylesheet" href="./fixtures/blank.css"></link>
`;
let content = DomQuery.byTagName("body").runScripts().runCss();
expect(content.byId("first").innerHTML).to.eq("hello world");
expect(content.byId("second").innerHTML).to.eq("hello world");
expect(content.byId("third").innerHTML).to.eq("hello world");
expect(content.byId("fourth").innerHTML).to.eq("hello world");
+
+ expect(DomQuery.byTagName("head")
+ .querySelectorAll("link[rel='stylesheet'][href='./fixtures/blank.css']").length).to.eq(1);
done();
});
@@ -482,7 +589,7 @@ describe('DOMQuery tests', function () {
it("it must handle events properly", function () {
let clicked = 0;
- let listener = () => {
+ let listener = (evt: any) => {
clicked++;
};
let eventReceiver = DomQuery.byId("id_1");
@@ -498,7 +605,7 @@ describe('DOMQuery tests', function () {
});
- it("it must handle innerText properly", function () {
+ it("it must handle innerText properly", function (done) {
//jsdom bug
Object.defineProperty(Object.prototype, 'innerText', {
@@ -510,6 +617,7 @@ describe('DOMQuery tests', function () {
let probe = DomQuery.byId("id_1");
probe.innerHTML = "<div>hello</div><div>world</div>";
expect(probe.innerText()).to.eq("helloworld");
+ done();
});
it("it must handle textContent properly", function () {
let probe = DomQuery.byId("id_1");
@@ -519,7 +627,7 @@ describe('DOMQuery tests', function () {
it("it must handle iterations properly", function () {
let probe = DomQuery.byTagName("div");
- let resArr = probe.lazyStream.collect(new ArrayCollector());
+ let resArr = LazyStream.ofStreamDataSource(probe).collect(new ArrayCollector());
expect(resArr.length).to.eq(4);
probe.reset();
@@ -558,6 +666,21 @@ describe('DOMQuery tests', function () {
})
+ it("parent must break shadow barriers", function () {
+ let probe = DomQuery.fromMarkup("<div id='shadowItem'>hello</div>'");
+ try {
+ //probably not testable atm, mocha does not have shadow dom support
+ //we might be able to shim it in one way or the other
+ let element = DomQuery.byId("id_1").attachShadow();
+ element.append(probe);
+ expect(probe.firstParent("#id_1").length > 0).to.eq(true);
+ } catch (e) {
+ //not supported we still need to get an error here
+ expect(e.message.indexOf("not supported") != -1).to.be.true;
+ }
+ })
+
+
it('it must have a working wait for dom with mut observer and must detect condition after change', async function () {
let probe = DomQuery.byId('id_1');
probe.innerHTML = 'true';
@@ -629,11 +752,14 @@ describe('DOMQuery tests', function () {
it('must by recycleable', function () {
let probe = DomQuery.querySelectorAll("div");
+ let probe2 = DomQuery.querySelectorAll("body");
let res1 = probe.filter(item => item.matchesSelector("div"));
expect(res1.length).to.eq(4);
let res2 = probe.filter(item => item.matchesSelector("div"));
expect(res2.length).to.eq(4);
+
+
})
it('delete must work', function () {
@@ -651,9 +777,22 @@ describe('DOMQuery tests', function () {
let probe2 = DomQuery.querySelectorAll("div");
let probeCnt = 0;
let probe2Cnt = 0;
- from(probe).subscribe(() => probeCnt++);
- from(probe2.stream).subscribe(() => probe2Cnt++);
+ from(probe).subscribe(el => probeCnt++);
+ from(Stream.ofDataSource(probe2)).subscribe(el => probe2Cnt++);
expect(probeCnt).to.be.above(0);
expect(probeCnt).to.eq(probe2Cnt);
- })
+ });
+
+ it('must handle closest properly', function() {
+ let probe = DomQuery.byId("id_1");
+ probe.innerHTML = "<div id='inner_elem'>hello world<div id='inner_elem2'></div></div>";
+
+ let probe2 = DomQuery.byId("inner_elem");
+ expect(probe2.closest("div#id_1").id.value).to.eq("id_1");
+ expect(probe2.parent().closest("div").id.value).to.eq("id_1");
+ probe2 = DomQuery.byId("inner_elem2");
+ expect(probe2.closest("div").id.value).to.eq("inner_elem2");
+ expect(probe2.closest("div#id_1").id.value).to.eq("id_1");
+ expect(probe2.parent().parent().closest("div").id.value).to.eq("id_1");
+ });
});
diff --git a/api/src/client/typescript/faces/test/frameworkBase/_ext/monadish/MonadTest.spec.ts b/api/src/client/typescript/faces/test/frameworkBase/_ext/monadish/MonadTest.spec.ts
index df5dbe334..42026158e 100644
--- a/api/src/client/typescript/faces/test/frameworkBase/_ext/monadish/MonadTest.spec.ts
+++ b/api/src/client/typescript/faces/test/frameworkBase/_ext/monadish/MonadTest.spec.ts
@@ -125,7 +125,7 @@ describe('Config tests', () => {
});
it('array config2', () => {
- let config = setup();
+ let config = new Config([]);
config.assign("[5]", "world[3]", "from").value = "me";
expect(config.getIf("[5]", "world[3]", "from").value).to.be.eq("me");
expect(config.value[5].world[3].from).to.be.eq("me");
@@ -133,7 +133,7 @@ describe('Config tests', () => {
});
it('array config3', () => {
- let config = setup();
+ let config = new Config([]);
config.assign("[5]", "[3]", "from").value = "me";
expect(config.getIf("[5]", "[3]", "from").value).to.be.eq("me");
expect(config.value[5][3].from).to.be.eq("me");
@@ -141,7 +141,7 @@ describe('Config tests', () => {
});
it('array config4', () => {
- let config = setup();
+ let config = new Config([]);
config.assign("[5]", "[3]", "[2]").value = "me";
expect(config.getIf("[5]", "[3]", "[2]").value).to.be.eq("me");
expect(config.value[5][3][2]).to.be.eq("me");
@@ -149,7 +149,7 @@ describe('Config tests', () => {
});
it('array config5', () => {
- let config = setup();
+ let config = new Config([]);
config.assign("[5]", "world[3]", "from[2]").value = "me";
expect(config.getIf("[5]", "world[3]", "from[2]").value).to.be.eq("me");
expect(config.value[5].world[3].from[2]).to.be.eq("me");
diff --git a/api/src/client/typescript/faces/test/frameworkBase/_ext/monadish/StreamTest.spec.ts b/api/src/client/typescript/faces/test/frameworkBase/_ext/monadish/StreamTest.spec.ts
deleted file mode 100644
index fa2724738..000000000
--- a/api/src/client/typescript/faces/test/frameworkBase/_ext/monadish/StreamTest.spec.ts
+++ /dev/null
@@ -1,231 +0,0 @@
-/*! Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to you under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import {describe} from "mocha";
-import {expect} from "chai";
-import {ArrayCollector, LazyStream, Stream} from "mona-dish";
-
-describe('early stream tests', () => {
-
- beforeEach(function () {
- this.probe = [1, 2, 3, 4, 5];
- });
-
- it("must iterate normal", function () {
- let stream = Stream.of<number>(...this.probe);
- let sum = 0;
- stream.each((data) => {
- sum = sum + data;
- });
- expect(sum).to.eq(15);
-
- let stream2 = LazyStream.of<number>(...this.probe);
- sum = 0;
- stream2.each((data: number) => {
- sum = sum + data;
- });
- expect(sum).to.eq(15);
- });
-
- it("must iterate filtered", function () {
- let stream = Stream.of<number>(...this.probe);
- let sum = 0;
- stream.filter((data) => data != 5).each((data) => {
- sum = sum + data;
- });
- expect(sum).to.eq(10);
-
- let stream2 = LazyStream.of<number>(...this.probe);
- sum = 0;
- stream2.filter((data) => data != 5).each((data: number) => {
- sum = sum + data;
- });
- expect(sum).to.eq(10);
- });
-
- it("must onElem", function () {
- let stream = Stream.of<number>(...this.probe);
- let sum = 0;
- let sum2: number = stream.filter((data) => data != 5).onElem((data) => {
- sum = sum + data;
- }).reduce<number>((el1, el2) => el1 + el2).value;
- expect(sum).to.eq(10);
- expect(sum2).to.eq(10);
-
- let stream2 = LazyStream.of<number>(...this.probe);
- sum = 0;
- sum2 = stream2.filter((data) => data != 5).onElem((data: number) => {
- sum = sum + data;
- }).reduce((el1: number, el2: number) => el1 + el2).value;
- expect(sum).to.eq(10);
- expect(sum2).to.eq(10);
- })
-
- it("must have a correct first last", function () {
- let first = Stream.of<number>(...this.probe).filter((data) => data != 5).onElem(() => {
- }).first().value;
- let last = Stream.of<number>(...this.probe).filter((data) => data != 5).onElem(() => {
- }).last().value;
- expect(first).to.eq(1);
- expect(last).to.eq(4);
-
- });
-
- it("must have a correct first last lazy", function () {
- let first = LazyStream.of<number>(...this.probe).filter((data) => data != 5).onElem((data) => {
- data;
- }).first().value;
- let last = Stream.of<number>(...this.probe).filter((data) => data != 5).onElem((data) => {
- data;
- }).last().value;
- expect(first).to.eq(1);
- expect(last).to.eq(4);
-
- });
-
- it("must have a correct limits", function () {
- let cnt = 0;
- let last = Stream.of<number>(...this.probe).filter((data) => data != 5).limits(2).onElem(() => {
- cnt++;
- }).last().value;
-
- expect(last).to.eq(2);
- expect(cnt).to.eq(2);
-
- });
-
- it("must initialize correctly from assoc array", function () {
- let probe = {
- key1: "val1",
- key2: 2,
- key3: "val3"
- }
-
- let arr1: Array<string | number> = [];
- let arr2: Array<string | number> = [];
-
- Stream.ofAssoc(probe).each(item => {
- expect(item.length).to.eq(2);
- arr1.push(item[0]);
- arr2.push(item[1]);
- });
-
- expect(arr1.join(",")).to.eq("key1,key2,key3");
- expect(arr2.join(",")).to.eq("val1,2,val3");
-
- });
-
- it("must have a correct lazy limits", function () {
- let last = LazyStream.of<number>(...this.probe).filter((data) => data != 5).limits(2).onElem((data) => {
- data;
- }).last().value;
-
- expect(last).to.eq(2);
-
- })
-
- it("must correctly lazily flatmap", function () {
-
- let resultingArr = LazyStream.of<number>(...this.probe).flatMap((data) => LazyStream.of(...[data, 2])).value;
-
- expect(resultingArr.length == 10).to.be.true;
- expect(resultingArr.join(",")).to.eq("1,2,2,2,3,2,4,2,5,2");
- });
-
- it("must correctly lazily flatmap with arrays", function () {
-
- let resultingArr = LazyStream.of<number>(...this.probe).flatMap((data) => [data, 2]).value;
-
- expect(resultingArr.length == 10).to.be.true;
- expect(resultingArr.join(",")).to.eq("1,2,2,2,3,2,4,2,5,2");
- });
-
- it("must correctly early flatmap", function () {
-
- let resultingArr = Stream.of<number>(...this.probe).flatMap((data) => Stream.of(...[data, 2])).value;
-
- expect(resultingArr.length == 10).to.be.true;
- expect(resultingArr.join(",")).to.eq("1,2,2,2,3,2,4,2,5,2");
- });
-
- it("must correctly early flatmap with arrays", function () {
-
- let resultingArr = Stream.of<number>(...this.probe).flatMap((data) => [data, 2]).value;
-
- expect(resultingArr.length == 10).to.be.true;
- expect(resultingArr.join(",")).to.eq("1,2,2,2,3,2,4,2,5,2");
- });
-
- it("must correctly flatmap intermixed", function () {
-
- let resultingArr = LazyStream.of<number>(...this.probe).flatMap((data) => Stream.of(...[data, 2])).value;
-
- expect(resultingArr.length == 10).to.be.true;
- expect(resultingArr.join(",")).to.eq("1,2,2,2,3,2,4,2,5,2");
- });
-
- it("must correctly flatmap intermixed2", function () {
-
- let resultingArr = Stream.of<number>(...this.probe).flatMap((data) => LazyStream.of(...[data, 2])).value;
-
- expect(resultingArr.length == 10).to.be.true;
- expect(resultingArr.join(",")).to.eq("1,2,2,2,3,2,4,2,5,2");
- });
-
- it("must correctly pass anyMatch allMatch noneMatch", function () {
- let anyMatch = LazyStream.of<number>(...this.probe).anyMatch((item) => item == 3);
- let allMatch = LazyStream.of<number>(...this.probe).allMatch((item) => item < 6);
- let noneMatch = LazyStream.of<number>(...this.probe).noneMatch((item) => item > 5);
-
- expect(anyMatch).to.be.true;
- expect(allMatch).to.be.true;
- expect(noneMatch).to.be.true;
- })
-
- it("must correctly pass anyMatch allMatch noneMatch early", function () {
- let anyMatch = Stream.of<number>(...this.probe).anyMatch((item) => item == 3);
- let allMatch = Stream.of<number>(...this.probe).allMatch((item) => item < 6);
- let noneMatch = Stream.of<number>(...this.probe).noneMatch((item) => item > 5);
-
- expect(anyMatch).to.be.true;
- expect(allMatch).to.be.true;
- expect(noneMatch).to.be.true;
- })
-
- it("must sort correctly", function () {
-
- let probe: Array<number> = [1, 5, 3, 2, 4];
-
- let res = Stream.of<number>(...probe)
- .sort((el1: number, el2: number) => el1 - el2)
- .collect(new ArrayCollector());
-
- expect(res.join(",")).to.eq("1,2,3,4,5");
-
- })
-
- it("must sort correctly lazy", function () {
-
- let probe: Array<number> = [1, 5, 3, 2, 4];
-
- let res = LazyStream.of<number>(...probe)
- .sort((el1: number, el2: number) => el1 - el2)
- .collect(new ArrayCollector());
-
- expect(res.join(",")).to.eq("1,2,3,4,5");
-
- })
-});
\ No newline at end of file
diff --git a/api/src/client/typescript/faces/test/frameworkBase/_ext/monadish/fixtures/blank.css b/api/src/client/typescript/faces/test/frameworkBase/_ext/monadish/fixtures/blank.css
new file mode 100644
index 000000000..1eb4b5b0c
--- /dev/null
+++ b/api/src/client/typescript/faces/test/frameworkBase/_ext/monadish/fixtures/blank.css
@@ -0,0 +1,15 @@
+/*! Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
\ No newline at end of file
diff --git a/api/src/client/typescript/faces/test/frameworkBase/_ext/monadish/fixtures/test.js b/api/src/client/typescript/faces/test/frameworkBase/_ext/monadish/fixtures/test.js
index 24b4a21f0..b78d5194b 100644
--- a/api/src/client/typescript/faces/test/frameworkBase/_ext/monadish/fixtures/test.js
+++ b/api/src/client/typescript/faces/test/frameworkBase/_ext/monadish/fixtures/test.js
@@ -1 +1,16 @@
+/*! Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
document.getElementById('id_1').innerHTML = "hello world";
diff --git a/api/src/client/typescript/faces/test/frameworkBase/_ext/monadish/fixtures/test2.js b/api/src/client/typescript/faces/test/frameworkBase/_ext/monadish/fixtures/test2.js
index 24b4a21f0..b78d5194b 100644
--- a/api/src/client/typescript/faces/test/frameworkBase/_ext/monadish/fixtures/test2.js
+++ b/api/src/client/typescript/faces/test/frameworkBase/_ext/monadish/fixtures/test2.js
@@ -1 +1,16 @@
+/*! Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
document.getElementById('id_1').innerHTML = "hello world";
diff --git a/api/src/client/typescript/faces/test/frameworkBase/_ext/monadish/markups/tobago-with-header.ts b/api/src/client/typescript/faces/test/frameworkBase/_ext/monadish/markups/tobago-with-header.ts
new file mode 100644
index 000000000..d5961acbe
--- /dev/null
+++ b/api/src/client/typescript/faces/test/frameworkBase/_ext/monadish/markups/tobago-with-header.ts
@@ -0,0 +1,921 @@
+export const tobagoSheetWithHeader = `<tobago-sheet id="page:mainForm:s1" data-tobago-selection-mode="multi" data-tobago-first="0" rows="0" row-count="88" data-tobago-layout="{"columns":[1.0]}">
+ <style nonce="" id="page:mainForm:s1:j_id_2n">#page\\:mainForm\\:s1{max-height:486px;}
+ </style>
+ <input id="page:mainForm:s1::widths" name="page:mainForm:s1::widths" type="hidden" value="[]">
+ <input id="page:mainForm:s1::rendered" name="page:mainForm:s1::rendered" type="hidden" value="[true]">
+ <input id="page:mainForm:s1::scrollPosition" name="page:mainForm:s1::scrollPosition" type="hidden" value="[0,0]">
+ <input id="page:mainForm:s1::selected" name="page:mainForm:s1::selected" type="hidden" value="[]">
+ <header>
+ <table cellspacing="0" cellpadding="0" summary="" class="table table-sm tobago-tableLayout-fixed">
+ <colgroup>
+ <col width="385">
+ <col>
+ <col width="15">
+ </colgroup>
+ <thead>
+ <tr>
+ <th>
+ <span><tobago-out id="page:mainForm:s1:_col0"><span class="form-control-plaintext">Name</span></tobago-out></span>
+ <span class="tobago-resize" data-tobago-column-index="0"> </span>
+ </th>
+ <th>
+ <span></span>
+ </th>
+ <th>
+ <span></span>
+ </th>
+ </tr>
+ </thead>
+ </table>
+ </header>
+ <div class="tobago-body">
+ <table cellspacing="0" cellpadding="0" summary="" class="table table-sm tobago-tableLayout-fixed">
+ <colgroup>
+ <col width="385">
+ <col>
+ </colgroup>
+ <tbody>
+ <tr row-index="0" id="page:mainForm:s1:0:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:0:t_name"><span class="form-control-plaintext">Sun</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:0:sample0" execute="page:mainForm:s1:0:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="1" id="page:mainForm:s1:1:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:1:t_name"><span class="form-control-plaintext">Mercury</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:1:sample0" execute="page:mainForm:s1:1:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="2" id="page:mainForm:s1:2:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:2:t_name"><span class="form-control-plaintext">Venus</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:2:sample0" execute="page:mainForm:s1:2:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="3" id="page:mainForm:s1:3:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:3:t_name"><span class="form-control-plaintext">Earth</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:3:sample0" execute="page:mainForm:s1:3:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="4" id="page:mainForm:s1:4:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:4:t_name"><span class="form-control-plaintext">Mars</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:4:sample0" execute="page:mainForm:s1:4:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="5" id="page:mainForm:s1:5:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:5:t_name"><span class="form-control-plaintext">Jupiter</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:5:sample0" execute="page:mainForm:s1:5:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="6" id="page:mainForm:s1:6:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:6:t_name"><span class="form-control-plaintext">Saturn</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:6:sample0" execute="page:mainForm:s1:6:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="7" id="page:mainForm:s1:7:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:7:t_name"><span class="form-control-plaintext">Uranus</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:7:sample0" execute="page:mainForm:s1:7:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="8" id="page:mainForm:s1:8:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:8:t_name"><span class="form-control-plaintext">Neptune</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:8:sample0" execute="page:mainForm:s1:8:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="9" id="page:mainForm:s1:9:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:9:t_name"><span class="form-control-plaintext">Pluto</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:9:sample0" execute="page:mainForm:s1:9:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="10" id="page:mainForm:s1:10:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:10:t_name"><span class="form-control-plaintext">Moon</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:10:sample0" execute="page:mainForm:s1:10:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="11" id="page:mainForm:s1:11:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:11:t_name"><span class="form-control-plaintext">Phobos</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:11:sample0" execute="page:mainForm:s1:11:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="12" id="page:mainForm:s1:12:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:12:t_name"><span class="form-control-plaintext">Deimos</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:12:sample0" execute="page:mainForm:s1:12:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="13" id="page:mainForm:s1:13:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:13:t_name"><span class="form-control-plaintext">Metis</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:13:sample0" execute="page:mainForm:s1:13:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="14" id="page:mainForm:s1:14:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:14:t_name"><span class="form-control-plaintext">Adrastea</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:14:sample0" execute="page:mainForm:s1:14:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="15" id="page:mainForm:s1:15:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:15:t_name"><span class="form-control-plaintext">Amalthea</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:15:sample0" execute="page:mainForm:s1:15:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="16" id="page:mainForm:s1:16:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:16:t_name"><span class="form-control-plaintext">Thebe</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:16:sample0" execute="page:mainForm:s1:16:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="17" id="page:mainForm:s1:17:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:17:t_name"><span class="form-control-plaintext">Io</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:17:sample0" execute="page:mainForm:s1:17:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="18" id="page:mainForm:s1:18:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:18:t_name"><span class="form-control-plaintext">Europa</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:18:sample0" execute="page:mainForm:s1:18:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="19" id="page:mainForm:s1:19:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:19:t_name"><span class="form-control-plaintext">Ganymede</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:19:sample0" execute="page:mainForm:s1:19:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="20" id="page:mainForm:s1:20:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:20:t_name"><span class="form-control-plaintext">Callisto</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:20:sample0" execute="page:mainForm:s1:20:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="21" id="page:mainForm:s1:21:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:21:t_name"><span class="form-control-plaintext">Themisto</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:21:sample0" execute="page:mainForm:s1:21:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="22" id="page:mainForm:s1:22:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:22:t_name"><span class="form-control-plaintext">Leda</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:22:sample0" execute="page:mainForm:s1:22:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="23" id="page:mainForm:s1:23:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:23:t_name"><span class="form-control-plaintext">Himalia</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:23:sample0" execute="page:mainForm:s1:23:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="24" id="page:mainForm:s1:24:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:24:t_name"><span class="form-control-plaintext">Lysithea</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:24:sample0" execute="page:mainForm:s1:24:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="25" id="page:mainForm:s1:25:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:25:t_name"><span class="form-control-plaintext">Elara</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:25:sample0" execute="page:mainForm:s1:25:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="26" id="page:mainForm:s1:26:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:26:t_name"><span class="form-control-plaintext">Ananke</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:26:sample0" execute="page:mainForm:s1:26:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="27" id="page:mainForm:s1:27:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:27:t_name"><span class="form-control-plaintext">Carme</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:27:sample0" execute="page:mainForm:s1:27:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="28" id="page:mainForm:s1:28:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:28:t_name"><span class="form-control-plaintext">Pasiphae</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:28:sample0" execute="page:mainForm:s1:28:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="29" id="page:mainForm:s1:29:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:29:t_name"><span class="form-control-plaintext">Sinope</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:29:sample0" execute="page:mainForm:s1:29:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="30" id="page:mainForm:s1:30:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:30:t_name"><span class="form-control-plaintext">Iocaste</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:30:sample0" execute="page:mainForm:s1:30:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="31" id="page:mainForm:s1:31:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:31:t_name"><span class="form-control-plaintext">Harpalyke</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:31:sample0" execute="page:mainForm:s1:31:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="32" id="page:mainForm:s1:32:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:32:t_name"><span class="form-control-plaintext">Praxidike</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:32:sample0" execute="page:mainForm:s1:32:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="33" id="page:mainForm:s1:33:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:33:t_name"><span class="form-control-plaintext">Taygete</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:33:sample0" execute="page:mainForm:s1:33:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="34" id="page:mainForm:s1:34:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:34:t_name"><span class="form-control-plaintext">Chaldene</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:34:sample0" execute="page:mainForm:s1:34:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="35" id="page:mainForm:s1:35:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:35:t_name"><span class="form-control-plaintext">Kalyke</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:35:sample0" execute="page:mainForm:s1:35:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="36" id="page:mainForm:s1:36:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:36:t_name"><span class="form-control-plaintext">Callirrhoe</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:36:sample0" execute="page:mainForm:s1:36:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="37" id="page:mainForm:s1:37:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:37:t_name"><span class="form-control-plaintext">Megaclite</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:37:sample0" execute="page:mainForm:s1:37:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="38" id="page:mainForm:s1:38:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:38:t_name"><span class="form-control-plaintext">Isonoe</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:38:sample0" execute="page:mainForm:s1:38:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="39" id="page:mainForm:s1:39:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:39:t_name"><span class="form-control-plaintext">Erinome</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:39:sample0" execute="page:mainForm:s1:39:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="40" id="page:mainForm:s1:40:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:40:t_name"><span class="form-control-plaintext">Pan</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:40:sample0" execute="page:mainForm:s1:40:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="41" id="page:mainForm:s1:41:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:41:t_name"><span class="form-control-plaintext">Atlas</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:41:sample0" execute="page:mainForm:s1:41:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="42" id="page:mainForm:s1:42:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:42:t_name"><span class="form-control-plaintext">Prometheus</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:42:sample0" execute="page:mainForm:s1:42:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="43" id="page:mainForm:s1:43:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:43:t_name"><span class="form-control-plaintext">Pandora</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:43:sample0" execute="page:mainForm:s1:43:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="44" id="page:mainForm:s1:44:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:44:t_name"><span class="form-control-plaintext">Epimetheus</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:44:sample0" execute="page:mainForm:s1:44:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="45" id="page:mainForm:s1:45:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:45:t_name"><span class="form-control-plaintext">Janus</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:45:sample0" execute="page:mainForm:s1:45:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="46" id="page:mainForm:s1:46:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:46:t_name"><span class="form-control-plaintext">Mimas</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:46:sample0" execute="page:mainForm:s1:46:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="47" id="page:mainForm:s1:47:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:47:t_name"><span class="form-control-plaintext">Enceladus</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:47:sample0" execute="page:mainForm:s1:47:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="48" id="page:mainForm:s1:48:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:48:t_name"><span class="form-control-plaintext">Tethys</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:48:sample0" execute="page:mainForm:s1:48:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="49" id="page:mainForm:s1:49:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:49:t_name"><span class="form-control-plaintext">Telesto</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:49:sample0" execute="page:mainForm:s1:49:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="50" id="page:mainForm:s1:50:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:50:t_name"><span class="form-control-plaintext">Calypso</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:50:sample0" execute="page:mainForm:s1:50:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="51" id="page:mainForm:s1:51:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:51:t_name"><span class="form-control-plaintext">Dione</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:51:sample0" execute="page:mainForm:s1:51:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="52" id="page:mainForm:s1:52:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:52:t_name"><span class="form-control-plaintext">Helene</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:52:sample0" execute="page:mainForm:s1:52:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="53" id="page:mainForm:s1:53:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:53:t_name"><span class="form-control-plaintext">Rhea</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:53:sample0" execute="page:mainForm:s1:53:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="54" id="page:mainForm:s1:54:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:54:t_name"><span class="form-control-plaintext">Titan</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:54:sample0" execute="page:mainForm:s1:54:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="55" id="page:mainForm:s1:55:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:55:t_name"><span class="form-control-plaintext">Hyperion</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:55:sample0" execute="page:mainForm:s1:55:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="56" id="page:mainForm:s1:56:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:56:t_name"><span class="form-control-plaintext">Iapetus</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:56:sample0" execute="page:mainForm:s1:56:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="57" id="page:mainForm:s1:57:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:57:t_name"><span class="form-control-plaintext">Phoebe</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:57:sample0" execute="page:mainForm:s1:57:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="58" id="page:mainForm:s1:58:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:58:t_name"><span class="form-control-plaintext">Cordelia</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:58:sample0" execute="page:mainForm:s1:58:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="59" id="page:mainForm:s1:59:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:59:t_name"><span class="form-control-plaintext">Ophelia</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:59:sample0" execute="page:mainForm:s1:59:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="60" id="page:mainForm:s1:60:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:60:t_name"><span class="form-control-plaintext">Bianca</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:60:sample0" execute="page:mainForm:s1:60:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="61" id="page:mainForm:s1:61:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:61:t_name"><span class="form-control-plaintext">Cressida</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:61:sample0" execute="page:mainForm:s1:61:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="62" id="page:mainForm:s1:62:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:62:t_name"><span class="form-control-plaintext">Desdemona</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:62:sample0" execute="page:mainForm:s1:62:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="63" id="page:mainForm:s1:63:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:63:t_name"><span class="form-control-plaintext">Juliet</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:63:sample0" execute="page:mainForm:s1:63:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="64" id="page:mainForm:s1:64:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:64:t_name"><span class="form-control-plaintext">Portia</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:64:sample0" execute="page:mainForm:s1:64:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="65" id="page:mainForm:s1:65:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:65:t_name"><span class="form-control-plaintext">Rosalind</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:65:sample0" execute="page:mainForm:s1:65:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="66" id="page:mainForm:s1:66:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:66:t_name"><span class="form-control-plaintext">Belinda</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:66:sample0" execute="page:mainForm:s1:66:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="67" id="page:mainForm:s1:67:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:67:t_name"><span class="form-control-plaintext">1986U10</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:67:sample0" execute="page:mainForm:s1:67:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="68" id="page:mainForm:s1:68:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:68:t_name"><span class="form-control-plaintext">Puck</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:68:sample0" execute="page:mainForm:s1:68:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="69" id="page:mainForm:s1:69:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:69:t_name"><span class="form-control-plaintext">Miranda</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:69:sample0" execute="page:mainForm:s1:69:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="70" id="page:mainForm:s1:70:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:70:t_name"><span class="form-control-plaintext">Ariel</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:70:sample0" execute="page:mainForm:s1:70:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="71" id="page:mainForm:s1:71:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:71:t_name"><span class="form-control-plaintext">Umbriel</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:71:sample0" execute="page:mainForm:s1:71:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="72" id="page:mainForm:s1:72:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:72:t_name"><span class="form-control-plaintext">Titania</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:72:sample0" execute="page:mainForm:s1:72:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="73" id="page:mainForm:s1:73:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:73:t_name"><span class="form-control-plaintext">Oberon</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:73:sample0" execute="page:mainForm:s1:73:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="74" id="page:mainForm:s1:74:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:74:t_name"><span class="form-control-plaintext">Caliban</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:74:sample0" execute="page:mainForm:s1:74:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="75" id="page:mainForm:s1:75:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:75:t_name"><span class="form-control-plaintext">Stephano</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:75:sample0" execute="page:mainForm:s1:75:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="76" id="page:mainForm:s1:76:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:76:t_name"><span class="form-control-plaintext">Sycorax</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:76:sample0" execute="page:mainForm:s1:76:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="77" id="page:mainForm:s1:77:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:77:t_name"><span class="form-control-plaintext">Prospero</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:77:sample0" execute="page:mainForm:s1:77:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="78" id="page:mainForm:s1:78:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:78:t_name"><span class="form-control-plaintext">Setebos</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:78:sample0" execute="page:mainForm:s1:78:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="79" id="page:mainForm:s1:79:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:79:t_name"><span class="form-control-plaintext">Naiad</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:79:sample0" execute="page:mainForm:s1:79:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="80" id="page:mainForm:s1:80:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:80:t_name"><span class="form-control-plaintext">Thalassa</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:80:sample0" execute="page:mainForm:s1:80:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="81" id="page:mainForm:s1:81:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:81:t_name"><span class="form-control-plaintext">Despina</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:81:sample0" execute="page:mainForm:s1:81:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="82" id="page:mainForm:s1:82:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:82:t_name"><span class="form-control-plaintext">Galatea</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:82:sample0" execute="page:mainForm:s1:82:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="83" id="page:mainForm:s1:83:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:83:t_name"><span class="form-control-plaintext">Larissa</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:83:sample0" execute="page:mainForm:s1:83:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="84" id="page:mainForm:s1:84:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:84:t_name"><span class="form-control-plaintext">Proteus</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:84:sample0" execute="page:mainForm:s1:84:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="85" id="page:mainForm:s1:85:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:85:t_name"><span class="form-control-plaintext">Triton</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:85:sample0" execute="page:mainForm:s1:85:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="86" id="page:mainForm:s1:86:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:86:t_name"><span class="form-control-plaintext">Nereid</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:86:sample0" execute="page:mainForm:s1:86:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="87" id="page:mainForm:s1:87:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:87:t_name"><span class="form-control-plaintext">Charon</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:87:sample0" execute="page:mainForm:s1:87:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </tobago-sheet>`
\ No newline at end of file
diff --git a/api/src/client/typescript/faces/test/frameworkBase/_ext/monadish/markups/tobago-without-header.ts b/api/src/client/typescript/faces/test/frameworkBase/_ext/monadish/markups/tobago-without-header.ts
new file mode 100644
index 000000000..61950c2bd
--- /dev/null
+++ b/api/src/client/typescript/faces/test/frameworkBase/_ext/monadish/markups/tobago-without-header.ts
@@ -0,0 +1,108 @@
+export const tobagoSheetWithoutHeader = `<tobago-sheet id="page:mainForm:s1" data-tobago-selection-mode="multi" data-tobago-first="0" rows="0" row-count="88" data-tobago-layout="{"columns":[1.0]}">
+ <style nonce="" id="page:mainForm:s1:j_id_2n">#page\\:mainForm\\:s1{max-height:486px;}
+ </style>
+ <input id="page:mainForm:s1::widths" name="page:mainForm:s1::widths" type="hidden" value="[]">
+ <input id="page:mainForm:s1::rendered" name="page:mainForm:s1::rendered" type="hidden" value="[true]">
+ <input id="page:mainForm:s1::scrollPosition" name="page:mainForm:s1::scrollPosition" type="hidden" value="[0,0]">
+ <input id="page:mainForm:s1::selected" name="page:mainForm:s1::selected" type="hidden" value="[]">
+ <div class="tobago-body">
+ <table cellspacing="0" cellpadding="0" summary="" class="table table-sm tobago-tableLayout-fixed">
+ <colgroup>
+ <col>
+ <col>
+ </colgroup>
+ <tbody>
+ <tr row-index="0" id="page:mainForm:s1:0:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:0:t_name"><span class="form-control-plaintext">Sun</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:0:sample0" execute="page:mainForm:s1:0:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="1" id="page:mainForm:s1:1:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:1:t_name"><span class="form-control-plaintext">Mercury</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:1:sample0" execute="page:mainForm:s1:1:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="2" id="page:mainForm:s1:2:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:2:t_name"><span class="form-control-plaintext">Venus</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:2:sample0" execute="page:mainForm:s1:2:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="3" id="page:mainForm:s1:3:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:3:t_name"><span class="form-control-plaintext">Earth</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:3:sample0" execute="page:mainForm:s1:3:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="4" id="page:mainForm:s1:4:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:4:t_name"><span class="form-control-plaintext">Mars</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:4:sample0" execute="page:mainForm:s1:4:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="5" id="page:mainForm:s1:5:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:5:t_name"><span class="form-control-plaintext">Jupiter</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:5:sample0" execute="page:mainForm:s1:5:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="6" id="page:mainForm:s1:6:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:6:t_name"><span class="form-control-plaintext">Saturn</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:6:sample0" execute="page:mainForm:s1:6:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="7" id="page:mainForm:s1:7:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:7:t_name"><span class="form-control-plaintext">Uranus</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:7:sample0" execute="page:mainForm:s1:7:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ <tr row-index="8" id="page:mainForm:s1:8:sample0">
+ <td>
+ <tobago-out id="page:mainForm:s1:8:t_name"><span class="form-control-plaintext">Neptune</span></tobago-out>
+ </td>
+ <td>
+ <div>
+ </div>
+ <tobago-behavior event="click" client-id="page:mainForm:s1:8:sample0" execute="page:mainForm:s1:8:sample0"></tobago-behavior>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+</tobago-sheet>`;
diff --git a/api/src/client/typescript/faces/test/frameworkBase/_ext/shared/StandardInits.ts b/api/src/client/typescript/faces/test/frameworkBase/_ext/shared/StandardInits.ts
index dd668771b..d726e06f8 100644
--- a/api/src/client/typescript/faces/test/frameworkBase/_ext/shared/StandardInits.ts
+++ b/api/src/client/typescript/faces/test/frameworkBase/_ext/shared/StandardInits.ts
@@ -83,6 +83,7 @@ export module StandardInits {
<body>
<form id="blarg">
<input type="text" id="input_1" name="input_1" value="input_1_val"></input>
+ <input type="text" id="input_2_text" name="input_2_text" value="input_2_text_val"></input>
<input type="hidden" id="jakarta.faces.ViewState" name="jakarta.faces.ViewState" value="blubbblubblubb"></input>
<input type="button" id="input_2" name="input_2" value="input_1_val"></input>
</form>
@@ -222,7 +223,7 @@ export module StandardInits {
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript"
- src="/wfmportal/${IS_40 ? 'jakarta' : 'javax'}.faces.resource/${IS_40 ? 'faces': 'jsf'}.js.jsf?ln=jakarta.faces&separator=${separatorChar}"></script>
+ src="./fixtures/${IS_40 ? 'jakarta' : 'javax'}.faces.resource/${IS_40 ? 'faces': 'jsf'}.js.jsf?ln=jakarta.faces&separator=${separatorChar}"></script>
</head>
<body>
<form id="blarg">
@@ -513,7 +514,7 @@ export module StandardInits {
//let dom2 = new JSDOM(template)
//return initMyFacesFromDom(dom2);
let clean = null;
- //we use jsdom global to fullfill our requirements
+ //we use jsdom global to fulfill our requirements
//we need to import dynamically and use awaits
if (withJsf) {
diff --git a/api/src/client/typescript/faces/test/frameworkBase/_ext/shared/fixtures/jakarta.faces.resource/faces.js.jsf b/api/src/client/typescript/faces/test/frameworkBase/_ext/shared/fixtures/jakarta.faces.resource/faces.js.jsf
new file mode 100644
index 000000000..86fc3d32a
--- /dev/null
+++ b/api/src/client/typescript/faces/test/frameworkBase/_ext/shared/fixtures/jakarta.faces.resource/faces.js.jsf
@@ -0,0 +1,15 @@
+/*! Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements. See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to you under the Apache License, Version 2.0
+* (the "License"); you may not use this file except in compliance with
+* the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
\ No newline at end of file
diff --git a/api/src/client/typescript/faces/test/impl/ImplTest.spec.ts b/api/src/client/typescript/faces/test/impl/ImplTest.spec.ts
index 3dc77449d..46ec0e544 100644
--- a/api/src/client/typescript/faces/test/impl/ImplTest.spec.ts
+++ b/api/src/client/typescript/faces/test/impl/ImplTest.spec.ts
@@ -15,13 +15,14 @@
*/
import {Config, DomQuery} from "mona-dish";
-import {describe, it} from 'mocha';
+import {afterEach, describe, it} from 'mocha';
import {expect} from 'chai';
import * as sinon from 'sinon';
import {StandardInits} from "../frameworkBase/_ext/shared/StandardInits";
import {CTX_PARAM_REQ_PASS_THR, P_EXECUTE, P_RENDER} from "../../impl/core/Const";
import defaultMyFaces = StandardInits.defaultMyFaces;
+import {_Es2019Array} from "mona-dish";
sinon.reset();
@@ -37,11 +38,26 @@ declare var Implementation: any;
*/
describe('faces.ajax.request test suite', () => {
-
+ let oldFlatMap = null;
beforeEach(async () => {
- return await defaultMyFaces();
+ //we test ES2019 with it and whether we have missed
+ //a map somewhere
+ return await defaultMyFaces().then( () => {
+ if(Array.prototype.map) {
+ oldFlatMap =Array.prototype["flatMap"];
+ window["Es2019Array"] = _Es2019Array;
+ delete Array.prototype["flatMap"];
+ }
+ });
});
+ afterEach(() => {
+ if(oldFlatMap) {
+ Array.prototype["flatMap"] = oldFlatMap;
+ oldFlatMap = null;
+ }
+ })
+
it("faces.ajax.request can be called", () => {
//we stub the addRequestToQueue, to enable the request check only
//without any xhr and response, both will be tested separately for
@@ -50,12 +66,15 @@ describe('faces.ajax.request test suite', () => {
//now the faces.ajax.request should trigger but should not go into
//the asynchronous event loop.
//lets check it out
+ let flatMap = Array.prototype.flatMap;
try {
DomQuery.byId("input_2").addEventListener("click", (event: Event) => {
- faces.ajax.request(null, event, {render: '@all', execute: '@form'})
+
+ faces.ajax.request(null, event, {render: '@all', execute: '@form'});
}).click();
+
expect(addRequestToQueue.called).to.be.true;
expect(addRequestToQueue.callCount).to.eq(1);
const context = (<Config>addRequestToQueue.args[0][2]);
@@ -103,7 +122,7 @@ describe('faces.ajax.request test suite', () => {
called["func5"] = true;
return false;
};
-
+ delete Array.prototype["flatMap"];
faces.util.chain(this, called, func1, func2, func3, func4, func5);
expect(called["func1"]).to.be.true;
@@ -118,7 +137,6 @@ describe('faces.ajax.request test suite', () => {
expect(called["func2"]).to.be.true;
expect(!!called["func4"]).to.be.true;
expect(!!called["func5"]).to.be.false;
-
});
diff --git a/api/src/client/typescript/faces/test/myfaces/OnLoad.spec.ts b/api/src/client/typescript/faces/test/myfaces/OnLoad.spec.ts
new file mode 100644
index 000000000..f82e469b7
--- /dev/null
+++ b/api/src/client/typescript/faces/test/myfaces/OnLoad.spec.ts
@@ -0,0 +1,52 @@
+/*! Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import {describe, it} from "mocha";
+
+import {expect} from "chai";
+import {StandardInits} from "../frameworkBase/_ext/shared/StandardInits";
+import defaultMyFaces = StandardInits.defaultMyFaces;
+
+
+
+declare var myfaces: any;
+
+/**
+ * Adds test s for the newly introduced onload handling
+ */
+describe('Tests on the xhr core when it starts to call the request', function () {
+
+ beforeEach(async () => {
+ return
+ });
+
+ it("must be present", async function() {
+ await defaultMyFaces();
+ expect(myfaces?.onDomReady).to.exist;
+ return true;
+ });
+
+ it("must be called on onDocumentReady", async function(done) {
+ let called: boolean = false;
+ const onDomCalled = () => {
+ expect(true).to.true;
+ done();
+ }
+ myfaces?.onDomReady(onDomCalled);
+ await defaultMyFaces();
+ });
+
+});
diff --git a/api/src/client/typescript/faces/test/queue/AsynchronousProbe.ts b/api/src/client/typescript/faces/test/queue/AsynchronousProbe.ts
index 2ea93717e..25b77a554 100644
--- a/api/src/client/typescript/faces/test/queue/AsynchronousProbe.ts
+++ b/api/src/client/typescript/faces/test/queue/AsynchronousProbe.ts
@@ -18,10 +18,10 @@
* we need a probe to test our asynchronous behavior
* the asynchronous behavior needs to be in an asynchronous runnable
*/
-import {AsyncRunnable} from "../../impl/util/AsyncRunnable";
+import {IAsyncRunnable} from "../../impl/util/AsyncRunnable";
import {IValueHolder} from "mona-dish";
-export class ProbeClass implements AsyncRunnable<Promise<any>>, IValueHolder<Promise<any>> {
+export class ProbeClass implements IAsyncRunnable<Promise<any>>, IValueHolder<Promise<any>> {
value: Promise<any>;
fCatch: (data: any) => any;
@@ -44,7 +44,7 @@ export class ProbeClass implements AsyncRunnable<Promise<any>>, IValueHolder<Pro
});
}
- catch(func: (data: any) => any): AsyncRunnable<boolean> {
+ catch(func: (data: any) => any): IAsyncRunnable<boolean> {
let catchFunc = (data: any) => {
this.catchPerformed = true;
return func(data);
@@ -57,7 +57,7 @@ export class ProbeClass implements AsyncRunnable<Promise<any>>, IValueHolder<Pro
return this;
}
- finally(func: () => void): AsyncRunnable<boolean> {
+ finally(func: () => void): IAsyncRunnable<boolean> {
let finallyFunc = () => {
this.finallyPerformed = true;
func();
@@ -88,7 +88,7 @@ export class ProbeClass implements AsyncRunnable<Promise<any>>, IValueHolder<Pro
//TODO do something with it
}
- then(func: (data: any) => any): AsyncRunnable<boolean> {
+ then(func: (data: any) => any): IAsyncRunnable<boolean> {
let thenFunc = (data: any) => {
this.thenPerformed = true;
diff --git a/api/src/client/typescript/faces/test/queue/AsynchronousQueueTest.spec.ts b/api/src/client/typescript/faces/test/queue/AsynchronousQueueTest.spec.ts
index 611c5fe04..7773adeaa 100644
--- a/api/src/client/typescript/faces/test/queue/AsynchronousQueueTest.spec.ts
+++ b/api/src/client/typescript/faces/test/queue/AsynchronousQueueTest.spec.ts
@@ -18,10 +18,11 @@ import {describe, it} from 'mocha';
import {expect} from 'chai';
import * as sinon from 'sinon';
import {ProbeClass} from "./AsynchronousProbe";
-import {AsynchronousQueue} from "../../impl/util/AsyncQueue";
+
import {StandardInits} from "../frameworkBase/_ext/shared/StandardInits";
import {Implementation} from "../../impl/AjaxImpl";
import defaultMyFaces = StandardInits.defaultMyFaces;
+import {XhrQueueController} from "../../impl/util/XhrQueueController";
describe('Asynchronous Queue tests', () => {
@@ -60,7 +61,7 @@ describe('Asynchronous Queue tests', () => {
const probe1 = new ProbeClass(setTimeout);
- const queue = new AsynchronousQueue();
+ const queue = new XhrQueueController();
probe1.then(() => {
expect(probe1.thenPerformed, "called").to.be.true;
done();
@@ -69,7 +70,7 @@ describe('Asynchronous Queue tests', () => {
});
it('multiple entries', (done) => {
- const queue = new AsynchronousQueue();
+ const queue = new XhrQueueController();
const probe1 = new ProbeClass(setTimeout);
const probe2 = new ProbeClass(setTimeout);
const probe3 = new ProbeClass(setTimeout);
diff --git a/api/src/client/typescript/faces/test/xhrCore/ClientWindow.spec.ts b/api/src/client/typescript/faces/test/xhrCore/ClientWindow.spec.ts
index 5f25bb1e6..277b695b0 100644
--- a/api/src/client/typescript/faces/test/xhrCore/ClientWindow.spec.ts
+++ b/api/src/client/typescript/faces/test/xhrCore/ClientWindow.spec.ts
@@ -4,6 +4,8 @@ import {Implementation} from "../../impl/AjaxImpl";
import {StandardInits} from "../frameworkBase/_ext/shared/StandardInits";
import {expect} from "chai";
+const jsdom = require("jsdom");
+const {JSDOM} = jsdom;
describe('adds a getClientWindowTests', function () {
diff --git a/api/src/client/typescript/faces/test/xhrCore/EventTests.spec.ts b/api/src/client/typescript/faces/test/xhrCore/EventTests.spec.ts
index bb9e8b09b..cf8c2536f 100644
--- a/api/src/client/typescript/faces/test/xhrCore/EventTests.spec.ts
+++ b/api/src/client/typescript/faces/test/xhrCore/EventTests.spec.ts
@@ -87,28 +87,34 @@ describe('tests the addOnEvent and addOnError handling', function () {
});
it("must have onError called in case of error", function () {
- let onErrorCalled1 = 0;
- let onErrorCalled2 = 0;
-
- let errorTitle = '';
- let errorMessage = '';
- faces.ajax.addOnError((data: any) => {
- errorTitle = data.errorName;
- errorMessage = data.errorMessage;
- onErrorCalled1++
- });
- faces.ajax.addOnError(() => {
- onErrorCalled2++;
- });
-
- //cmd_error_component
- DQ.byId("cmd_error_component").click();
- this.respond(XmlResponses.ERROR_2);
-
- expect(onErrorCalled1).to.eq(1);
- expect(onErrorCalled2).to.eq(1);
- expect(errorTitle).to.eq('Erro21');
- expect(errorMessage).to.eq('serverError: Error2 Text');
+ const oldErr = console.error;
+ console.error = () => {}
+ try {
+ let onErrorCalled1 = 0;
+ let onErrorCalled2 = 0;
+
+ let errorTitle = '';
+ let errorMessage = '';
+ faces.ajax.addOnError((data: any) => {
+ errorTitle = data.errorName;
+ errorMessage = data.errorMessage;
+ onErrorCalled1++
+ });
+ faces.ajax.addOnError(() => {
+ onErrorCalled2++;
+ });
+
+ //cmd_error_component
+ DQ.byId("cmd_error_component").click();
+ this.respond(XmlResponses.ERROR_2);
+
+ expect(onErrorCalled1).to.eq(1);
+ expect(onErrorCalled2).to.eq(1);
+ expect(errorTitle).to.eq('Erro21');
+ expect(errorMessage).to.eq('serverError: Error2 Text');
+ } finally {
+ console.error = oldErr;
+ }
});
it("must have an id set if there is an emitting element", function () {
diff --git a/api/src/client/typescript/faces/test/xhrCore/FileUploadTest.spec.ts b/api/src/client/typescript/faces/test/xhrCore/FileUploadTest.spec.ts
index 0ae8096e0..d67c137a0 100644
--- a/api/src/client/typescript/faces/test/xhrCore/FileUploadTest.spec.ts
+++ b/api/src/client/typescript/faces/test/xhrCore/FileUploadTest.spec.ts
@@ -17,7 +17,7 @@ import {describe, it} from "mocha";
import * as sinon from "sinon";
import {expect} from "chai";
import {StandardInits} from "../frameworkBase/_ext/shared/StandardInits";
-import {DomQuery, DQ} from "mona-dish";
+import {_Es2019Array, DomQuery, DQ} from "mona-dish";
import defaultFileForm = StandardInits.defaultFileForm;
import {Implementation} from "../../impl/AjaxImpl";
@@ -27,6 +27,7 @@ declare var faces: any;
* specialized tests testing the xhr core behavior when it hits the xmlHttpRequest object
*/
describe('Tests on the xhr core when it starts to call the request', function () {
+ let oldFlatMap = null;
beforeEach(async function () {
let waitForResult = defaultFileForm();
@@ -47,6 +48,9 @@ describe('Tests on the xhr core when it starts to call the request', function ()
};
(<any>global).XMLHttpRequest = this.xhr;
window.XMLHttpRequest = this.xhr;
+ oldFlatMap =Array.prototype["flatMap"];
+ window["Es2019Array"] = _Es2019Array;
+ delete Array.prototype["flatMap"];
this.closeIt = () => {
(<any>global).XMLHttpRequest = window.XMLHttpRequest = this.xhr.restore();
@@ -59,6 +63,10 @@ describe('Tests on the xhr core when it starts to call the request', function ()
});
afterEach(function () {
this.closeIt();
+ if(oldFlatMap) {
+ Array.prototype["flatMap"] = oldFlatMap;
+ oldFlatMap = null;
+ }
});
it('must have sent a form multipart request', function (done) {
diff --git a/api/src/client/typescript/faces/test/xhrCore/NamespacesRequestTest.spec.ts b/api/src/client/typescript/faces/test/xhrCore/NamespacesRequestTest.spec.ts
index 94ad76469..6f7aa3283 100644
--- a/api/src/client/typescript/faces/test/xhrCore/NamespacesRequestTest.spec.ts
+++ b/api/src/client/typescript/faces/test/xhrCore/NamespacesRequestTest.spec.ts
@@ -18,22 +18,19 @@ import {describe, it} from "mocha";
import * as sinon from "sinon";
import {expect} from "chai";
import {StandardInits} from "../frameworkBase/_ext/shared/StandardInits";
-import {DomQuery, DQ$, Stream} from "mona-dish";
+import {_Es2019Array, DQ$} from "mona-dish";
import {
- $nsp,
- COMPLETE,
P_AJAX,
P_EXECUTE,
- P_PARTIAL_SOURCE,
+ P_AJAX_SOURCE,
P_RENDER,
P_VIEWSTATE,
- P_WINDOW_ID,
- SUCCESS
+ P_WINDOW_ID
} from "../../impl/core/Const";
-import defaultMyFaces = StandardInits.defaultMyFaces;
-import STD_XML = StandardInits.STD_XML;
import defaultMyFacesNamespaces = StandardInits.defaultMyFacesNamespaces;
import {escape} from "querystring";
+import {ExtLang} from "../../impl/util/Lang";
+import ofAssoc = ExtLang.ofAssoc;
declare var faces: any;
declare var Implementation: any;
@@ -50,6 +47,7 @@ let issueStdReq = function (element) {
};
describe('Namespacing tests', function () {
+ let oldFlatMap = null;
beforeEach(async function () {
let waitForResult = defaultMyFacesNamespaces();
@@ -65,6 +63,9 @@ describe('Namespacing tests', function () {
window.XMLHttpRequest = this.xhr;
this.jsfAjaxResponse = sinon.spy((<any>global).faces.ajax, "response");
+ oldFlatMap =Array.prototype["flatMap"];
+ window["Es2019Array"] = _Es2019Array;
+ delete Array.prototype["flatMap"];
this.closeIt = () => {
(<any>global).XMLHttpRequest = window.XMLHttpRequest = this.xhr.restore();
@@ -77,6 +78,10 @@ describe('Namespacing tests', function () {
afterEach(function () {
this.closeIt();
+ if(oldFlatMap) {
+ Array.prototype["flatMap"] = oldFlatMap;
+ oldFlatMap = null;
+ }
});
it('must send the element identifiers properly encoded', function () {
@@ -107,7 +112,7 @@ describe('Namespacing tests', function () {
expect(!!resultsMap["execute"]).to.be.false;
expect(P_WINDOW_ID in resultsMap).to.be.false;
expect(P_VIEWSTATE in resultsMap).to.be.true;
- expect(resultsMap[P_PARTIAL_SOURCE]).to.eq(escape("jd_0:input_2"));
+ expect(resultsMap[P_AJAX_SOURCE]).to.eq(escape("jd_0:input_2"));
expect(resultsMap[P_AJAX]).to.eq("true");
expect(resultsMap[P_RENDER]).to.eq(escape("jd_0:blarg jd_0:input_2"));
expect(resultsMap[P_EXECUTE]).to.eq(escape("jd_0:input_1 jd_0:input_2"));
@@ -143,7 +148,7 @@ describe('Namespacing tests', function () {
expect(!!resultsMap["execute"]).to.be.false;
expect(P_WINDOW_ID in resultsMap).to.be.false;
expect(P_VIEWSTATE in resultsMap).to.be.true;
- expect(resultsMap[P_PARTIAL_SOURCE]).to.eq(escape("jd_0:input_2"));
+ expect(resultsMap[P_AJAX_SOURCE]).to.eq(escape("jd_0:input_2"));
expect(resultsMap[P_AJAX]).to.eq("true");
expect(resultsMap[P_RENDER]).to.eq(escape("jd_0:blarg jd_0:input_2"));
expect(resultsMap[P_EXECUTE]).to.eq(escape("jd_0:input_1 jd_0:input_2"));
@@ -182,16 +187,16 @@ describe('Namespacing tests', function () {
expect(!!resultsMap["render"]).to.be.false;
expect(!!resultsMap["execute"]).to.be.false;
- let hasWindowdId = Stream.ofAssoc(resultsMap).filter(data => data[0].indexOf(P_WINDOW_ID) != -1).first().isPresent();
- let hasViewState = Stream.ofAssoc(resultsMap).filter(data => data[0].indexOf(P_VIEWSTATE) != -1).first().isPresent();
+ let hasWindowdId = ofAssoc(resultsMap).filter(data => data[0].indexOf(P_WINDOW_ID) != -1)?.[0];
+ let hasViewState = ofAssoc(resultsMap).filter(data => data[0].indexOf(P_VIEWSTATE) != -1)?.[0];
- expect(hasWindowdId).to.be.false;
- expect(hasViewState).to.be.true;
+ expect(!!hasWindowdId).to.be.false;
+ expect(!!hasViewState).to.be.true;
- let viewState = Stream.ofAssoc(resultsMap).filter(data => data[0].indexOf(P_VIEWSTATE) != -1).map(item => item[1]).first().value;
+ let viewState = ofAssoc(resultsMap).filter(data => data[0].indexOf(P_VIEWSTATE) != -1).map(item => item[1])?.[0];
expect(viewState).to.eq("booga");
- expect(resultsMap[NAMING_CONTAINER_PREF + P_PARTIAL_SOURCE]).to.eq("jd_0:input_2");
+ expect(resultsMap[NAMING_CONTAINER_PREF + P_AJAX_SOURCE]).to.eq("jd_0:input_2");
expect(resultsMap[NAMING_CONTAINER_PREF + P_AJAX]).to.eq("true");
expect(resultsMap[NAMING_CONTAINER_PREF + P_RENDER]).to.eq("jd_0:blarg jd_0:input_2");
expect(resultsMap[NAMING_CONTAINER_PREF + P_EXECUTE]).to.eq("jd_0:input_1 jd_0:input_2");
diff --git a/api/src/client/typescript/faces/test/xhrCore/OamSubmitTest.spec.ts b/api/src/client/typescript/faces/test/xhrCore/OamSubmitTest.spec.ts
index e00a5f1c4..7024076f5 100644
--- a/api/src/client/typescript/faces/test/xhrCore/OamSubmitTest.spec.ts
+++ b/api/src/client/typescript/faces/test/xhrCore/OamSubmitTest.spec.ts
@@ -19,11 +19,11 @@ import * as sinon from "sinon";
import {Implementation} from "../../impl/AjaxImpl";
import {StandardInits} from "../frameworkBase/_ext/shared/StandardInits";
import defaultMyFaces = StandardInits.defaultMyFaces;
-import {DQ, DQ$} from "mona-dish";
+import {_Es2019Array, DQ, DQ$} from "mona-dish";
import {expect} from "chai";
describe('Tests for the MyFaces specifig oam submit', function () {
-
+ let oldFlatMap = null;
beforeEach(async function () {
let waitForResult = defaultMyFaces();
@@ -39,6 +39,9 @@ describe('Tests for the MyFaces specifig oam submit', function () {
window.XMLHttpRequest = this.xhr;
this.jsfAjaxResponse = sinon.spy((<any>global).faces.ajax, "response");
+ oldFlatMap =Array.prototype["flatMap"];
+ window["Es2019Array"] = _Es2019Array;
+ delete Array.prototype["flatMap"];
this.closeIt = () => {
(<any>global).XMLHttpRequest = window.XMLHttpRequest = this.xhr.restore();
@@ -51,6 +54,10 @@ describe('Tests for the MyFaces specifig oam submit', function () {
afterEach(function () {
this.closeIt();
+ if(oldFlatMap) {
+ Array.prototype["flatMap"] = oldFlatMap;
+ oldFlatMap = null;
+ }
});
it(("must handle oam submit correctly, tuples"), (done) => {
diff --git a/api/src/client/typescript/faces/test/xhrCore/RequestParamsTest.spec.ts b/api/src/client/typescript/faces/test/xhrCore/RequestParamsTest.spec.ts
index 9ef6d0de9..a58e35503 100644
--- a/api/src/client/typescript/faces/test/xhrCore/RequestParamsTest.spec.ts
+++ b/api/src/client/typescript/faces/test/xhrCore/RequestParamsTest.spec.ts
@@ -18,7 +18,7 @@ import {Implementation} from "../../impl/AjaxImpl";
import {StandardInits} from "../frameworkBase/_ext/shared/StandardInits";
import protocolPage = StandardInits.protocolPage;
-import {Config, DQ, Stream} from "mona-dish";
+import {Config, DQ} from "mona-dish";
import {expect} from "chai";
import HTML_PREFIX_EMBEDDED_BODY = StandardInits.HTML_PREFIX_EMBEDDED_BODY;
import {it} from "mocha";
@@ -31,7 +31,7 @@ import {ExtConfig} from "../../impl/util/ExtDomQuery";
* @param keyValueEntries a list of key value entries divided by =
* @param paramsMapper a key value remapper
*/
-function mergeKeyValueEntries(target: Config, keyValueEntries: Stream<string[]>, paramsMapper = (key, value) => [key, value]) {
+function mergeKeyValueEntries(target: Config, keyValueEntries: string[][], paramsMapper = (key, value) => [key, value]) {
function fixKeyWithoutVal(keyVal: string[]) {
return keyVal.length < 3 ? [keyVal?.[0] ?? [], keyVal?.[1] ?? []] : keyVal;
@@ -42,7 +42,7 @@ function mergeKeyValueEntries(target: Config, keyValueEntries: Stream<string[]>,
//special case of having keys without values
.map(keyVal => fixKeyWithoutVal(keyVal))
.map(keyVal => paramsMapper(keyVal[0] as string, keyVal[1]))
- .each(keyVal => {
+ .forEach(keyVal => {
let value = keyVal?.splice(1)?.join("") ?? "";
if(toMerge.getIfPresent(keyVal[0]).isPresent()) {
toMerge.append(keyVal[0] as string).value = value;
diff --git a/api/src/client/typescript/faces/test/xhrCore/RequestTest.spec.ts b/api/src/client/typescript/faces/test/xhrCore/RequestTest.spec.ts
index 9d14a0ff5..1420eb883 100644
--- a/api/src/client/typescript/faces/test/xhrCore/RequestTest.spec.ts
+++ b/api/src/client/typescript/faces/test/xhrCore/RequestTest.spec.ts
@@ -18,12 +18,12 @@ import {describe, it} from "mocha";
import * as sinon from "sinon";
import {expect} from "chai";
import {StandardInits} from "../frameworkBase/_ext/shared/StandardInits";
-import {DomQuery} from "mona-dish";
+import {_Es2019Array, DomQuery} from "mona-dish";
import {
COMPLETE, EMPTY_STR,
P_AJAX,
P_EXECUTE,
- P_PARTIAL_SOURCE,
+ P_AJAX_SOURCE,
P_RENDER,
P_VIEWSTATE,
P_WINDOW_ID,
@@ -45,11 +45,34 @@ let issueStdReq = function (element) {
}
});
};
+
+
+let issueStdPPSReq = function (element) {
+ faces.ajax.request(element, null, {
+ execute: "input_1",
+ render: "@form",
+ /*
+ * myfaces is the extension placeholder, atm only pps is used
+ * which was the most useful extension in our legacy codebase
+ */
+ myfaces: {
+ pps: true
+ },
+ /*
+ * params is the spec conform way to pass additional request paramerters
+ */
+ params: {
+ pass1: "pass1",
+ pass2: "pass2"
+ }
+ });
+};
+
/**
* specialized tests testing the xhr core behavior when it hits the xmlHttpRequest object
*/
describe('Tests on the xhr core when it starts to call the request', function () {
-
+ let oldFlatMap = null;
beforeEach(async function () {
let waitForResult = defaultMyFaces();
@@ -65,6 +88,9 @@ describe('Tests on the xhr core when it starts to call the request', function ()
window.XMLHttpRequest = this.xhr;
this.jsfAjaxResponse = sinon.spy((<any>global).faces.ajax, "response");
+ oldFlatMap =Array.prototype["flatMap"];
+ window["Es2019Array"] = _Es2019Array;
+ delete Array.prototype["flatMap"];
this.closeIt = () => {
(<any>global).XMLHttpRequest = window.XMLHttpRequest = this.xhr.restore();
@@ -77,6 +103,10 @@ describe('Tests on the xhr core when it starts to call the request', function ()
afterEach(function () {
this.closeIt();
+ if(oldFlatMap) {
+ Array.prototype["flatMap"] = oldFlatMap;
+ oldFlatMap = null;
+ }
});
it('must have the standard parameters all in', function (done) {
@@ -119,13 +149,16 @@ describe('Tests on the xhr core when it starts to call the request', function ()
resultsMap[keyVal[0]] = keyVal[1];
}
+ // normal request, all issuing form must be encoded!
+ expect(!!(resultsMap?.["input_2_text"] ?? false)).to.eq(true);
+ expect(!!(resultsMap?.["input_1"] ?? false)).to.eq(true);
expect(resultsMap["pass1"]).to.eq("pass1");
expect(resultsMap["pass2"]).to.eq("pass2");
expect(!!resultsMap["render"]).to.be.false;
expect(!!resultsMap["execute"]).to.be.false;
expect(P_WINDOW_ID in resultsMap).to.be.false;
expect(P_VIEWSTATE in resultsMap).to.be.true;
- expect(resultsMap[P_PARTIAL_SOURCE]).to.eq("input_2");
+ expect(resultsMap[P_AJAX_SOURCE]).to.eq("input_2");
expect(resultsMap[P_AJAX]).to.eq("true");
expect(resultsMap[P_RENDER]).to.eq("blarg");
expect(resultsMap[P_EXECUTE]).to.eq("input_1%20input_2");
@@ -292,7 +325,7 @@ describe('Tests after core when it hits response', function () {
expect(!!lastArg.onError).to.be.false;
expect(lastArg.pass1 == "pass1").to.be.true;
expect(lastArg.pass2 == "pass2").to.be.true;
- expect(!!lastArg[P_PARTIAL_SOURCE]).to.be.true;
+ expect(!!lastArg[P_AJAX_SOURCE]).to.be.true;
expect(!!lastArg[P_AJAX]).to.be.true;
expect(!!lastArg[P_EXECUTE]).to.be.true;
expect(!!lastArg[P_RENDER]).to.be.true;
@@ -354,7 +387,7 @@ describe('Tests after core when it hits response', function () {
expect(!!lastArg.onError).to.be.false;
expect(lastArg.pass1 == "pass1").to.be.true;
expect(lastArg.pass2 == "pass2").to.be.true;
- expect(!!lastArg[P_PARTIAL_SOURCE]).to.be.true;
+ expect(!!lastArg[P_AJAX_SOURCE]).to.be.true;
expect(!!lastArg[P_AJAX]).to.be.true;
expect(!!lastArg[P_EXECUTE]).to.be.true;
expect(!!lastArg[P_RENDER]).to.be.true;
@@ -417,7 +450,7 @@ describe('Tests after core when it hits response', function () {
expect(!!lastArg.onError).to.be.false;
expect(lastArg.pass1 == "pass1").to.be.true;
expect(lastArg.pass2 == "pass2").to.be.true;
- expect(!!lastArg[P_PARTIAL_SOURCE]).to.be.true;
+ expect(!!lastArg[P_AJAX_SOURCE]).to.be.true;
expect(!!lastArg[P_AJAX]).to.be.true;
expect(!!lastArg[P_EXECUTE]).to.be.true;
expect(!!lastArg[P_RENDER]).to.be.true;
@@ -451,9 +484,10 @@ describe('Tests after core when it hits response', function () {
let send = sinon.spy(XMLHttpRequest.prototype, "send");
let xhrReq = null;
-
+ const oldErr = console.error;
try {
let errorCnt = 0;
+
let element = DomQuery.byId("input_2").getAsElem(0).value;
faces.ajax.request(element, null, {
execute: "input_1",
@@ -474,6 +508,8 @@ describe('Tests after core when it hits response', function () {
},
onevent: (evt: any) => {
if (evt.status == COMPLETE) {
+ console.error = () => {
+ };
throw Error("This error is wanted, ignore the log");
}
}
@@ -484,9 +520,13 @@ describe('Tests after core when it hits response', function () {
xhrReq.respond(200, {'Content-Type': 'text/xml'}, STD_XML);
} catch (e) {
+ if (e.message.indexOf("This error is wanted") != -1) {
+ return;
+ }
console.error(e);
} finally {
+ console.error = oldErr;
send.restore();
}
@@ -557,6 +597,47 @@ describe('Tests after core when it hits response', function () {
done("Expecting a client error to be thrown")
});
+ it("must have a proper working myfaces.pps = true, partial page submit", function (done) {
+ /**
+ * derived from the passthrough test
+ */
+ let send = sinon.spy(XMLHttpRequest.prototype, "send");
+ try {
+ //we only issue input 1
+ let element = DomQuery.byId("input_1").getAsElem(0).value;
+ issueStdPPSReq(element);
+
+ expect(send.called).to.be.true;
+ let argsVal: any = send.args[0][0];
+ let arsArr = argsVal.split("&");
+ let resultsMap = {};
+ for (let val of arsArr) {
+ let keyVal = val.split("=");
+ resultsMap[keyVal[0]] = keyVal[1];
+ }
+
+ expect(resultsMap?.["input_2_text"] ?? false).to.eq(false);
+ expect(!!(resultsMap?.["input_1"] ?? false)).to.eq(true);
+ expect(resultsMap["pass1"]).to.eq("pass1");
+ expect(resultsMap["pass2"]).to.eq("pass2");
+ expect(!!resultsMap["render"]).to.be.false;
+ expect(!!resultsMap["execute"]).to.be.false;
+ expect(P_WINDOW_ID in resultsMap).to.be.false;
+ expect(P_VIEWSTATE in resultsMap).to.be.true;
+ expect(resultsMap[P_AJAX_SOURCE]).to.eq("input_1");
+ expect(resultsMap[P_AJAX]).to.eq("true");
+ expect(resultsMap[P_RENDER]).to.eq("blarg");
+ expect(resultsMap[P_EXECUTE]).to.eq("input_1");
+
+
+ // TODO the request map only has the params and input1_ and input_2 passed no matter now many other values
+ // we might have to add some input elements into the form which are filetered out
+ } finally {
+ send.restore();
+ }
+ done();
+ });
+
});
diff --git a/api/src/client/typescript/faces/test/xhrCore/RequestTest_23.spec.ts b/api/src/client/typescript/faces/test/xhrCore/RequestTest_23.spec.ts
index a8fb83354..95da34b5e 100644
--- a/api/src/client/typescript/faces/test/xhrCore/RequestTest_23.spec.ts
+++ b/api/src/client/typescript/faces/test/xhrCore/RequestTest_23.spec.ts
@@ -324,7 +324,7 @@ describe('Tests after core when it hits response', function () {
let send = sinon.spy(XMLHttpRequest.prototype, "send");
let xhrReq = null;
-
+ let oldErr = console.error;
try {
let element = DomQuery.byId("input_2").getAsElem(0).value;
jsf.ajax.request(element, null, {
@@ -344,6 +344,7 @@ describe('Tests after core when it hits response', function () {
},
onevent: (evt: any) => {
if (evt.status == COMPLETE) {
+ console.error = () => {};
throw Error("This error is wanted, ignore the log");
}
}
@@ -354,9 +355,13 @@ describe('Tests after core when it hits response', function () {
xhrReq.respond(200, {'Content-Type': 'text/xml'}, STD_XML);
} catch (e) {
+ if(e.message.indexOf("This error is wanted") != -1) {
+ return;
+ }
console.error(e);
} finally {
+ console.error = oldErr;
send.restore();
}
diff --git a/api/src/client/typescript/faces/test/xhrCore/ResponseTest.spec.ts b/api/src/client/typescript/faces/test/xhrCore/ResponseTest.spec.ts
index 0103f1fd3..8a9c733df 100644
--- a/api/src/client/typescript/faces/test/xhrCore/ResponseTest.spec.ts
+++ b/api/src/client/typescript/faces/test/xhrCore/ResponseTest.spec.ts
@@ -21,7 +21,7 @@ import * as sinon from "sinon";
import {XmlResponses} from "../frameworkBase/_ext/shared/XmlResponses";
import {expect} from "chai";
-import {DomQuery, DQ, DQ$} from "mona-dish";
+import {_Es2019Array, DomQuery, DQ, DQ$} from "mona-dish";
import protocolPage = StandardInits.protocolPage;
@@ -35,7 +35,7 @@ declare var Implementation: any;
* we do not need to go through the entire ajax cycle for that.
*/
describe('Tests of the various aspects of the response protocol functionality', function () {
-
+ let oldFlatMap = null;
beforeEach(async function () {
let waitForResult = protocolPage();
return waitForResult.then((close) => {
@@ -55,6 +55,9 @@ describe('Tests of the various aspects of the response protocol functionality',
};
(<any>global).XMLHttpRequest = this.xhr;
window.XMLHttpRequest = this.xhr;
+ oldFlatMap =Array.prototype["flatMap"];
+ window["Es2019Array"] = _Es2019Array;
+ delete Array.prototype["flatMap"];
this.closeIt = () => {
(<any>global).XMLHttpRequest = window.XMLHttpRequest = this.xhr.restore();
@@ -66,6 +69,10 @@ describe('Tests of the various aspects of the response protocol functionality',
afterEach(function () {
this.closeIt();
+ if(oldFlatMap) {
+ Array.prototype["flatMap"] = oldFlatMap;
+ oldFlatMap = null;
+ }
});
it("must have a simple field updated as well as the viewstate", function (done) {
@@ -517,7 +524,6 @@ describe('Tests of the various aspects of the response protocol functionality',
</partial-response>`
window.document.body.innerHTML = INNER_HTML_MULIT_VIEW;
- global["debug4"] = true;
faces.ajax.request(window.document.getElementById("viewroot_1:submit_1"), null, {
"javax.faces.behavior.event": "change",
execute: "submit_1",
@@ -675,7 +681,7 @@ describe('Tests of the various aspects of the response protocol functionality',
});
//TODO xhr stubbing, to check if the viewId is prepended in render!
-
+ global["state_1"] = true;
this.respond(`<?xml version="1.0" encoding="UTF-8"?>
<partial-response id="viewroot_1">
<changes>
@@ -727,35 +733,37 @@ describe('Tests of the various aspects of the response protocol functionality',
it('must handle a ViewExpired Error correctly, and only once in a listener', function (done) {
document.body.innerHTML = TCK_790_NAV_MARKUP;
-
- let errorCalled = 0;
- faces.ajax.addOnError((error)=> {
- expect(error.errorName).to.eq("jakarta.faces.application.ViewExpiredException");
- expect(error.errorMessage).to.eq("serverError: View \"/testhmtl.xhtml\" could not be restored.");
- expect(error.source.id).to.eq("form1x:button");
- errorCalled++;
- });
-
- faces.ajax.request(window.document.getElementById("form1x:button"), null, {
- "javax.faces.behavior.event": "click",
- execute: "@form",
- render: ":form1x:button"
- });
-
- this.respond(`<?xml version="1.0" encoding="UTF-8"?>
+ const oldErr = console.error;
+ console.error = () => {};
+ try {
+ let errorCalled = 0;
+ faces.ajax.addOnError((error) => {
+ expect(error.errorName).to.eq("jakarta.faces.application.ViewExpiredException");
+ expect(error.errorMessage).to.eq("serverError: View \"/testhmtl.xhtml\" could not be restored.");
+ expect(error.source.id).to.eq("form1x:button");
+ errorCalled++;
+ });
+
+ faces.ajax.request(window.document.getElementById("form1x:button"), null, {
+ "javax.faces.behavior.event": "click",
+ execute: "@form",
+ render: ":form1x:button"
+ });
+
+ this.respond(`<?xml version="1.0" encoding="UTF-8"?>
<partial-response><error>
<error-name>jakarta.faces.application.ViewExpiredException</error-name>
<error-message><![CDATA[View "/testhmtl.xhtml" could not be restored.]]></error-message>
</error>
</partial-response>`)
- expect(errorCalled).to.eq(1);
-
+ expect(errorCalled).to.eq(1);
+ } finally {
+ console.error = oldErr;
+ }
done();
});
-
-
});
\ No newline at end of file
diff --git a/api/src/client/typescript/faces/test/xhrCore/TobagoFileUploadTest.spec.ts b/api/src/client/typescript/faces/test/xhrCore/TobagoFileUploadTest.spec.ts
index 0bc297ce7..722819702 100644
--- a/api/src/client/typescript/faces/test/xhrCore/TobagoFileUploadTest.spec.ts
+++ b/api/src/client/typescript/faces/test/xhrCore/TobagoFileUploadTest.spec.ts
@@ -17,7 +17,7 @@ import {describe, it} from "mocha";
import * as sinon from "sinon";
import {expect} from "chai";
import {StandardInits} from "../frameworkBase/_ext/shared/StandardInits";
-import {DomQuery} from "mona-dish";
+import {_Es2019Array, DomQuery} from "mona-dish";
import {Implementation} from "../../impl/AjaxImpl";
import defaultFileForm = StandardInits.tobagoFileForm;
@@ -27,6 +27,7 @@ declare var faces: any;
* specialized tests testing the xhr core behavior when it hits the xmlHttpRequest object
*/
describe('Tests on the xhr core when it starts to call the request', function () {
+ let oldFlatMap = null;
beforeEach(async function () {
let waitForResult = defaultFileForm();
@@ -47,11 +48,18 @@ describe('Tests on the xhr core when it starts to call the request', function ()
};
(<any>global).XMLHttpRequest = this.xhr;
window.XMLHttpRequest = this.xhr;
+ oldFlatMap =Array.prototype["flatMap"];
+ window["Es2019Array"] = _Es2019Array;
+ delete Array.prototype["flatMap"];
this.closeIt = () => {
(<any>global).XMLHttpRequest = window.XMLHttpRequest = this.xhr.restore();
Implementation.reset();
close();
+ if(oldFlatMap) {
+ Array.prototype["flatMap"] = oldFlatMap;
+ oldFlatMap = null;
+ }
}
});
diff --git a/api/src/client/typescript/faces/test/xhrCore/WebsocketTest.ts b/api/src/client/typescript/faces/test/xhrCore/WebsocketTest.ts
index 2bc70c8ff..ef420ff4e 100644
--- a/api/src/client/typescript/faces/test/xhrCore/WebsocketTest.ts
+++ b/api/src/client/typescript/faces/test/xhrCore/WebsocketTest.ts
@@ -21,16 +21,16 @@ import {Implementation} from "../../impl/AjaxImpl";
import {expect} from "chai";
import defaultMyFaces = StandardInits.defaultMyFaces;
-import {Lang} from "mona-dish";
+import {_Es2019Array, Lang} from "mona-dish";
import {FakeWebsocket} from "./FakeWebsocket";
import assertType = Lang.assertType;
declare var faces: any;
describe('Tests the jsf websocket client side api on high level (generic test without any myfaces dependencies', function () {
-
+ let oldFlatMap = null;
beforeEach(async function () {
-
+ let oldFlatMap = null;
let waitForResult = defaultMyFaces();
return waitForResult.then((close) => {
@@ -51,6 +51,9 @@ describe('Tests the jsf websocket client side api on high level (generic test wi
this.pushImpl = (<any>global).PushImpl;
this.initSpy = sinon.spy(this.pushImpl, "init");
+ oldFlatMap =Array.prototype["flatMap"];
+ window["Es2019Array"] = _Es2019Array;
+ delete Array.prototype["flatMap"];
this.closeIt = () => {
(<any>global).XMLHttpRequest = window.XMLHttpRequest = this.xhr.restore();
@@ -67,6 +70,10 @@ describe('Tests the jsf websocket client side api on high level (generic test wi
afterEach(function () {
this.closeIt();
+ if(oldFlatMap) {
+ Array.prototype["flatMap"] = oldFlatMap;
+ oldFlatMap = null;
+ }
});
it("must register a channel", function (done: Function) {
diff --git a/api/src/client/typescript/faces/test/xhrCore/XhrFormDataTest.spec.ts b/api/src/client/typescript/faces/test/xhrCore/XhrFormDataTest.spec.ts
index 491a1c487..7ae120ed7 100644
--- a/api/src/client/typescript/faces/test/xhrCore/XhrFormDataTest.spec.ts
+++ b/api/src/client/typescript/faces/test/xhrCore/XhrFormDataTest.spec.ts
@@ -15,7 +15,7 @@
*/
import {describe, it} from 'mocha';
-import {DQ} from "mona-dish";
+import {_Es2019Array, DQ} from "mona-dish";
import * as sinon from 'sinon';
import {XhrFormData} from "../../impl/xhrCore/XhrFormData";
import {expect} from "chai";
@@ -27,7 +27,7 @@ const jsdom = require("jsdom");
const {JSDOM} = jsdom;
describe('XhrFormData tests', function () {
-
+ let oldFlatMap = null;
beforeEach(async function () {
let waitForResult = defaultMyFaces();
@@ -43,12 +43,19 @@ describe('XhrFormData tests', function () {
window.XMLHttpRequest = this.xhr;
this.jsfAjaxResponse = sinon.spy((<any>global).faces.ajax, "response");
+ oldFlatMap =Array.prototype["flatMap"];
+ window["Es2019Array"] = _Es2019Array;
+ delete Array.prototype["flatMap"];
this.closeIt = () => {
(<any>global).XMLHttpRequest = window.XMLHttpRequest = this.xhr.restore();
this.jsfAjaxResponse.restore();
Implementation.reset();
close();
+ if(oldFlatMap) {
+ Array.prototype["flatMap"] = oldFlatMap;
+ oldFlatMap = null;
+ }
}
});
});
diff --git a/api/src/client/typescript/mona_dish/AssocArray.ts b/api/src/client/typescript/mona_dish/AssocArray.ts
new file mode 100644
index 000000000..5552724f1
--- /dev/null
+++ b/api/src/client/typescript/mona_dish/AssocArray.ts
@@ -0,0 +1,291 @@
+/*!
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * various helpers to deal with associative
+ * arrays. If someone feels uncomfortable using
+ * The config system, this is similar!
+ */
+import {IValueHolder} from "./Monad";
+import {Es2019Array} from "./Es2019Array";
+
+/**
+ * A nop as assign functionality (aka ignore assign)
+ */
+class IgnoreAssign implements IValueHolder<any> {
+ constructor(private parent: any) {}
+
+ set value(value: any | Array<any>) {
+ }
+ get value(): any | Array<any> {
+ return this.parent;
+ }
+};
+
+/**
+ * uses the known pattern from config
+ * assign(target, key1, key2, key3).value = value;
+ * @param target
+ * @param keys
+ */
+export function assign<T>(target: {[key: string]: any}, ...accessPath: string[]): IValueHolder<T> {
+ if (accessPath.length < 1) {
+ return new IgnoreAssign(target);
+ }
+ const lastPathItem = buildPath(target, ...accessPath);
+ let assigner: IValueHolder<T> = new (class {
+ set value(value: T | Array<T>) {
+ lastPathItem.target[lastPathItem.key] = value;
+ }
+
+ get value(): T | Array<T> {
+ return lastPathItem.target[lastPathItem.key];
+ }
+ })();
+ return assigner;
+}
+
+
+export function append<T>(target: {[key: string]: any}, ...accessPath: string[]): IValueHolder<T> {
+ if (accessPath.length < 1) {
+ return new IgnoreAssign(target);
+ }
+ const lastPathItem = buildPath(target, ...accessPath);
+ let appender: IValueHolder<T> = new (class {
+ set value(value: T | Array<T>) {
+ if(!Array.isArray(value)) {
+ value = [value];
+ }
+ if(!lastPathItem.target[lastPathItem.key]) {
+ lastPathItem.target[lastPathItem.key] = value
+ } else {
+ if(!Array.isArray(lastPathItem.target[lastPathItem.key])) {
+ lastPathItem.target[lastPathItem.key] = [lastPathItem.target[lastPathItem.key]];
+ }
+ lastPathItem.target[lastPathItem.key].push(...value);
+ }
+ }
+ })();
+ return appender;
+}
+
+/**
+ * uses the known pattern from config
+ * assign(target, key1, key2, key3).value = value;
+ * @param target
+ * @param keys
+ */
+export function assignIf<T>(condition: boolean, target: {[key: string]: any}, ...accessPath: string[]): IValueHolder<T> {
+ if ((!condition) || accessPath.length < 1) {
+ return new IgnoreAssign(target);
+ }
+ return assign(target, ...accessPath);
+}
+
+
+/**
+ * uses the known pattern from config
+ * assign(target, key1, key2, key3).value = value;
+ * @param target
+ * @param keys
+ */
+export function appendIf<T>(condition: boolean, target: {[key: string]: any}, ...accessPath: string[]): IValueHolder<T> {
+ if ((!condition) || accessPath.length < 1) {
+ return new IgnoreAssign(target);
+ }
+ return append(target, ...accessPath);
+}
+
+export function resolve<T>(target, ...accessPath: string[]): T | null {
+ let ret = null;
+ accessPath = flattenAccessPath(accessPath);
+ let currPtr = target;
+ for(let cnt = 0; cnt < accessPath.length; cnt++) {
+ let accessKeyIndex: number | string = accessPath[cnt];
+ accessKeyIndex = arrayIndex(accessKeyIndex) != -1 ? arrayIndex(accessKeyIndex) : accessKeyIndex;
+ currPtr = currPtr?.[accessKeyIndex];
+ if('undefined' == typeof currPtr) {
+ return null;
+ }
+ ret = currPtr;
+ }
+ return currPtr;
+}
+
+
+function keyVal(key: string): string {
+ let start = key.indexOf("[");
+
+ if (start >= 0) {
+ return key.substring(0, start);
+ } else {
+ return key;
+ }
+}
+
+function arrayIndex(key: string): number {
+ let start = key.indexOf("[");
+ let end = key.indexOf("]");
+ if (start >= 0 && end > 0 && start < end) {
+ return parseInt(key.substring(start + 1, end));
+ } else {
+ return -1;
+ }
+}
+
+function isArrayPos(currKey: string, arrPos: number): boolean {
+ return currKey === "" && arrPos >= 0;
+}
+
+function isNoArray(arrPos: number): boolean {
+ return arrPos == -1;
+}
+
+function alloc(arr: Array<any>, length: number, defaultVal = {}) {
+ let toAdd = [];
+ toAdd.length = length;
+ toAdd[length - 1] = defaultVal;
+ arr.push(...toAdd);
+}
+
+
+function flattenAccessPath(accessPath: string[]) {
+ return new Es2019Array(...accessPath).flatMap(path => path.split("["))
+ .map(path => path.indexOf("]") != -1 ? "[" + path : path)
+ .filter(path => path != "");
+}
+
+/**
+ * builds up a path, only done if no data is present!
+ * @param target
+ * @param accessPath
+ * @returns the last assignable entry
+ */
+export function buildPath(target, ...accessPath: string[]) {
+ accessPath = flattenAccessPath(accessPath);
+ //we now have a pattern of having the array accessors always in separate items
+ let parentPtr = target;
+ let parKeyArrPos = null;
+ let currKey = null;
+ let arrPos = -1;
+
+ for (let cnt = 0; cnt < accessPath.length; cnt++) {
+ currKey = keyVal(accessPath[cnt]);
+ arrPos = arrayIndex(accessPath[cnt]);
+ //it now is either key or arrPos
+ if (arrPos != -1) {
+ //case root(array)[5] -> root must be array and allocate 5 elements
+ //case root.item[5] root.item must be array and of 5 elements
+ if(!Array.isArray(parentPtr)) {
+ throw Error("Associative array referenced as index array in path reference");
+ }
+
+ //we need to look ahead for proper allocation
+ //not end reached
+ let nextArrPos = -1;
+ if(cnt < accessPath.length - 1) {
+ nextArrPos = arrayIndex(accessPath[cnt + 1])
+ }
+ let dataPresent = 'undefined' != typeof parentPtr?.[arrPos];
+ //no data present check here is needed, because alloc only reserves if not present
+ alloc(parentPtr, arrPos + 1, nextArrPos != -1 ?[]: {});
+ parKeyArrPos = arrPos;
+ //we now go to the reserved element
+ if(cnt == accessPath.length - 1) {
+ parentPtr[arrPos] = (dataPresent) ? parentPtr[arrPos] : null;
+ } else {
+ parentPtr = parentPtr[arrPos];
+ }
+ } else {
+ if(Array.isArray(parentPtr)) {
+ throw Error("Index array referenced as associative array in path reference");
+ }
+ //again look ahead whether the next value is an array or assoc array
+ let nextArrPos = -1;
+ if(cnt < accessPath.length - 1) {
+ nextArrPos = arrayIndex(accessPath[cnt + 1])
+ }
+ parKeyArrPos = currKey;
+ let dataPresent = 'undefined' != typeof parentPtr?.[currKey];
+ if(cnt == accessPath.length - 1) {
+ if(!dataPresent) {
+ parentPtr[currKey] = null;
+ }
+ } else {
+ if(!dataPresent) {
+ parentPtr[currKey] = nextArrPos == -1 ? {} : [];
+ }
+ parentPtr = parentPtr[currKey];
+ }
+ }
+ }
+
+ return {target: parentPtr, key: parKeyArrPos};
+
+}
+
+export function deepCopy(fromAssoc: {[key: string]: any}): {[key: string]: any} {
+ return JSON.parse(JSON.stringify(fromAssoc));
+}
+
+/**
+ * simple left to right merge
+ *
+ * @param assocArrays
+ */
+export function simpleShallowMerge(...assocArrays) {
+ return shallowMerge(true, false, ...assocArrays);
+}
+
+/**
+ * Shallow merge as in config
+ *
+ * @param overwrite
+ * @param withAppend
+ * @param assocArrays
+ */
+export function shallowMerge(overwrite = true, withAppend = false, ...assocArrays) {
+ let target: {[key: string]: any} = {};
+ new Es2019Array(...assocArrays).map(arr => {
+ return {arr, keys: Object.keys(arr)};
+ }).forEach(({arr, keys}) => {
+ keys.forEach(key => {
+ let toAssign = arr[key];
+ if(!Array.isArray(toAssign) && withAppend) {
+ toAssign = new Es2019Array(...[toAssign]);
+ }
+ if(overwrite || !target?.[key]) {
+ if(!withAppend) {
+ target[key] = arr[key];
+ } else {
+ if('undefined' == typeof target?.[key]) {
+ target[key] = toAssign
+ } else if(!Array.isArray(target[key])) {
+ let oldVal = target[key];
+ target[key] = new Es2019Array(...[]);
+ target[key].push(oldVal);
+ target[key].push(...toAssign);
+ } else {
+ target[key].push(...toAssign);
+ }
+ }
+ }
+ })
+ });
+ return target;
+}
+
diff --git a/api/src/client/typescript/mona_dish/Config.ts b/api/src/client/typescript/mona_dish/Config.ts
new file mode 100644
index 000000000..0cb62ec8f
--- /dev/null
+++ b/api/src/client/typescript/mona_dish/Config.ts
@@ -0,0 +1,263 @@
+import {Es2019Array} from "./Es2019Array";
+import {IValueHolder, Optional, ValueEmbedder} from "./Monad";
+import {Lang} from "./Lang";
+import objAssign = Lang.objAssign;
+import {append, appendIf, assign, assignIf, resolve, shallowMerge} from "./AssocArray";
+
+/**
+ * specialized value embedder
+ * for our Configuration
+ */
+class ConfigEntry<T> extends ValueEmbedder<T> {
+
+ /*default value for absent*/
+ static absent = ConfigEntry.fromNullable(null);
+
+ /**
+ * arrayed value positions
+ */
+ arrPos: number;
+
+ constructor(rootElem: any, key: any, arrPos?: number) {
+ super(rootElem, key);
+
+ this.arrPos = arrPos ?? -1;
+ }
+
+ get value() {
+ if (this.key == "" && this.arrPos >= 0) {
+ return this._value[this.arrPos];
+ } else if (this.key && this.arrPos >= 0) {
+ return this._value[this.key][this.arrPos];
+ }
+ return this._value[this.key];
+ }
+
+ set value(val: T) {
+ if (this.key == "" && this.arrPos >= 0) {
+ this._value[this.arrPos] = val;
+ return;
+ } else if (this.key && this.arrPos >= 0) {
+ this._value[this.key][this.arrPos] = val;
+ return;
+ }
+ this._value[this.key] = val;
+ }
+}
+
+export const CONFIG_VALUE = "__END_POINT__";
+export const CONFIG_ANY = "__ANY_POINT__";
+export type ConfigDef = { [key: string]: any };
+
+/**
+ * Config, basically an optional wrapper for a json structure
+ * (not Side - effect free, since we can alter the internal config state
+ * without generating a new config), not sure if we should make it side - effect free
+ * since this would swallow a lot of performance and ram
+ */
+export class Config extends Optional<any> {
+ constructor(root: any, private configDef ?: ConfigDef) {
+ super(root);
+ }
+
+ /**
+ * shallow copy getter, copies only the first level, references the deeper nodes
+ * in a shared manner
+ */
+ get shallowCopy(): Config {
+ return this.shallowCopy$();
+ }
+
+ protected shallowCopy$(): Config {
+ let ret = new Config({});
+ ret.shallowMerge(this.value);
+ return ret;
+ }
+
+ /**
+ * deep copy, copies all config nodes
+ */
+ get deepCopy(): Config {
+ return this.deepCopy$();
+ }
+
+ protected deepCopy$(): Config {
+ return new Config(objAssign({}, this.value));
+ }
+
+ /**
+ * creates a config from an initial value or null
+ * @param value
+ */
+ static fromNullable<T>(value?: T | null): Config {
+ return new Config(value);
+ }
+
+ /**
+ * simple merge for the root configs
+ */
+ shallowMerge(other: Config, overwrite = true, withAppend = false) {
+ //shallow merge must be mutable so we have to remap
+ let newThis = shallowMerge(overwrite, withAppend, this.value, other.value);
+ if (Array.isArray(this._value)) {
+ this._value.length = 0;
+ this._value.push(...(newThis as any));
+ } else {
+ Object.getOwnPropertyNames(this._value).forEach(key => delete this._value[key]);
+ Object.getOwnPropertyNames(newThis).forEach(key => this._value[key] = newThis[key]);
+ }
+ }
+
+ /**
+ * assigns a single value as array, or appends it
+ * to an existing value mapping a single value to array
+ *
+ *
+ * usage myConfig.append("foobaz").value = "newValue"
+ * myConfig.append("foobaz").value = "newValue2"
+ *
+ * resulting in myConfig.foobaz == ["newValue, newValue2"]
+ *
+ * @param {string[]} accessPath
+ */
+ append(...accessPath: string[]): IValueHolder<any> {
+ return append(this._value, ...accessPath);
+ }
+
+ /**
+ * appends to an existing entry (or extends into an array and appends)
+ * if the condition is met
+ * @param {boolean} condition
+ * @param {string[]} accessPath
+ */
+ appendIf(condition: boolean, ...accessPath: string[]): IValueHolder<any> {
+ return appendIf(condition, this._value, ...accessPath);
+ }
+
+ /**
+ * assigns a new value on the given access path
+ * @param accessPath
+ */
+ assign(...accessPath): IValueHolder<any> {
+ return assign(this.value, ...accessPath);
+ }
+
+
+ /**
+ * assign a value if the condition is set to true, otherwise skip it
+ *
+ * @param condition the condition, the access accessPath into the config
+ * @param accessPath
+ */
+ assignIf(condition: boolean, ...accessPath: Array<any>): IValueHolder<any> {
+ return assignIf(condition, this._value, ...accessPath);
+ }
+
+ /**
+ * get if the access path is present (get is reserved as getter with a default, on the current path)
+ * TODO will be renamed to something more meaningful and deprecated, the name is ambiguous
+ * @param accessPath the access path
+ */
+ getIf(...accessPath: Array<string>): Config {
+ this.assertAccessPath(...accessPath);
+ return this.getClass().fromNullable(resolve(this.value, ...accessPath));
+ }
+
+
+ /**
+ * gets the current node and if none is present returns a config with a default value
+ * @param defaultVal
+ */
+ get(defaultVal: any): Config {
+ return this.getClass().fromNullable(super.get(defaultVal).value);
+ }
+
+ //empties the current config entry
+ delete(key: string): Config {
+ if (key in this.value) {
+ delete this.value[key];
+ }
+ return this;
+ }
+
+ /**
+ * converts the entire config into a json object
+ */
+ toJson(): any {
+ return JSON.stringify(this.value);
+ }
+
+
+ protected getClass(): any {
+ return Config;
+ }
+
+ private setVal(val: any) {
+ this._value = val;
+ }
+
+
+ /**
+ * asserts the access path for a semi typed access
+ * @param accessPath
+ * @private
+ */
+ private assertAccessPath(...accessPath: Array<string>) {
+ accessPath = this.preprocessKeys(...accessPath);
+ if (!this.configDef) {
+ //untyped
+ return;
+ }
+
+ const ERR_ACCESS_PATH = "Access Path to config invalid";
+ let currAccessPos: any = Optional.fromNullable(Object.keys(this.configDef).map(key => {
+ let ret = {};
+ ret[key] = this.configDef[key];
+ return ret;
+ }));
+
+ for (let cnt = 0; cnt < accessPath.length; cnt++) {
+ let currKey = this.keyVal(accessPath[cnt]);
+ let arrPos: any = this.arrayIndex(accessPath[cnt]);
+
+ //key index
+ if (this.isArray(arrPos)) {
+ if (currKey != "") {
+ currAccessPos = Array.isArray(currAccessPos.value) ?
+ Optional.fromNullable(new Es2019Array(...currAccessPos.value)
+ .find(item => {
+ return !!(item?.[currKey] ?? false)
+ })?.[currKey]?.[arrPos]) :
+ Optional.fromNullable(currAccessPos.value?.[currKey]?.[arrPos] ?? null);
+
+ } else {
+ currAccessPos = (Array.isArray(currAccessPos.value)) ?
+ Optional.fromNullable(currAccessPos.value?.[arrPos]) : Optional.absent;
+ }
+ //we noe store either the current array or the filtered look ahead to go further
+ } else {
+ //we now have an array and go further with a singular key
+ currAccessPos = (Array.isArray(currAccessPos.value)) ? Optional.fromNullable(new Es2019Array(...currAccessPos.value)
+ .find(item => {
+ return !!(item?.[currKey] ?? false);
+ })?.[currKey]) :
+ Optional.fromNullable(currAccessPos.value?.[currKey] ?? null);
+ }
+ if (!currAccessPos.isPresent()) {
+ throw Error(ERR_ACCESS_PATH)
+ }
+ if (currAccessPos.value == CONFIG_ANY) {
+ return;
+ }
+ }
+ }
+
+ private isNoArray(arrPos: number) {
+ return arrPos == -1;
+ }
+
+ private isArray(arrPos: number) {
+ return !this.isNoArray(arrPos);
+ }
+
+}
\ No newline at end of file
diff --git a/api/src/client/typescript/mona_dish/DomQuery.ts b/api/src/client/typescript/mona_dish/DomQuery.ts
index 192c37201..e3a94f29d 100644
--- a/api/src/client/typescript/mona_dish/DomQuery.ts
+++ b/api/src/client/typescript/mona_dish/DomQuery.ts
@@ -15,23 +15,19 @@
* limitations under the License.
*/
-import {Config, Optional, ValueEmbedder} from "./Monad";
+import {Optional, ValueEmbedder} from "./Monad";
import {XMLQuery} from "./XmlQuery";
-import {IStream, LazyStream, Stream} from "./Stream";
-import {
- ArrayCollector,
- AssocArrayCollector,
- ICollector,
- IStreamDataSource,
- ITERATION_STATUS
-} from "./SourcesCollectors";
+
+import {ICollector, IStreamDataSource, ITERATION_STATUS} from "./SourcesCollectors";
import {Lang} from "./Lang";
+import {_global$} from "./Global";
+import {Es2019Array} from "./Es2019Array";
import trim = Lang.trim;
import isString = Lang.isString;
import eqi = Lang.equalsIgnoreCase;
-import {_global$} from "./Global";
import objToArray = Lang.objToArray;
+import {append, assign, simpleShallowMerge} from "./AssocArray";
declare var ownerDocument: any;
@@ -307,11 +303,11 @@ interface IDomQuery {
/**
* an early stream representation for this DomQuery
*/
- readonly stream: Stream<DomQuery>;
+ readonly stream: any;
/**
* lazy stream representation for this DomQuery
*/
- readonly lazyStream: LazyStream<DomQuery>;
+ readonly lazyStream: any;
/**
* transform this node collection to an array
*/
@@ -332,7 +328,7 @@ interface IDomQuery {
innerHtml: string;
/**
- * convenience for dq.id.value to make the code a little bit tighter
+ * convenience for dq.id.value to make the code a little tighter
*/
nodeId: string;
@@ -660,7 +656,7 @@ interface IDomQuery {
/**
* all parents until the selector match stops
- * @param tagName
+ * @param selector
*/
parentsWhileMatch(selector: string): DomQuery;
@@ -749,7 +745,7 @@ interface IDomQuery {
* @param toMerge optional config which can be merged in
* @return a copy pf
*/
- encodeFormElement(toMerge): Config;
+ encodeFormElement(toMerge): {[key: string]: any};
/**
* fetches the sub-nodes from ... to..
@@ -854,6 +850,14 @@ export class DomQuery implements IDomQuery, IStreamDataSource<DomQuery>, Iterabl
return _global$;
}
+ get stream(): any {
+ throw Error("Not implemented, include Stream.ts for this to work")
+ }
+
+ get lazyStream(): any {
+ throw Error("Not implemented, include Stream.ts for this to work")
+ }
+
/**
* returns the id of the first element
*/
@@ -942,7 +946,7 @@ export class DomQuery implements IDomQuery, IStreamDataSource<DomQuery>, Iterabl
}
get checked(): boolean {
- return Stream.of(...this.values).allMatch(el => !!(<any>el).checked);
+ return new Es2019Array(...this.values).every(el => !!(<any>el).checked);
}
set checked(newChecked: boolean) {
@@ -1010,65 +1014,47 @@ export class DomQuery implements IDomQuery, IStreamDataSource<DomQuery>, Iterabl
return new DomQuery(...childNodeArr);
}
- /**
- * binding into stream
- */
- get stream(): Stream<DomQuery> {
- return new Stream<DomQuery>(...this.asArray);
- }
- /**
- * fetches a lazy stream representation
- * lazy should be applied if you have some filters etc.
- * in between, this can reduce the number of post filter operations
- * and ram usage
- * significantly because the operations are done lazily and stop
- * once they hit a dead end.
- */
- get lazyStream(): LazyStream<DomQuery> {
- return LazyStream.of(...this.asArray);
- }
-
- get asArray(): Array<DomQuery> {
+ get asArray(): DomQuery[] {
// filter not supported by IE11
- return [].concat(LazyStream.of(...this.rootNode).filter(item => {
+ let items = new Es2019Array(...this.rootNode).filter(item => {
return item != null
- })
- .map(item => {
- return DomQuery.byId(item)
- }).collect(new ArrayCollector()));
+ }).map(item => {
+ return DomQuery.byId(item)
+ });
+ return items as DomQuery[];
}
get offsetWidth(): number {
- return LazyStream.of(...this.rootNode)
+ return new Es2019Array(...this.rootNode)
.filter(item => item != null)
.map(elem => (elem as HTMLElement).offsetWidth)
- .reduce((accumulate, incoming) => accumulate + incoming, 0).value;
+ .reduce((accumulate, incoming) => accumulate + incoming, 0);
}
get offsetHeight(): number {
- return LazyStream.of(...this.rootNode)
+ return new Es2019Array(...this.rootNode)
.filter(item => item != null)
.map(elem => (elem as HTMLElement).offsetHeight)
- .reduce((accumulate, incoming) => accumulate + incoming, 0).value;
+ .reduce((accumulate, incoming) => accumulate + incoming, 0);
}
get offsetLeft(): number {
- return LazyStream.of(...this.rootNode)
+ return new Es2019Array(...this.rootNode)
.filter(item => item != null)
.map(elem => (elem as HTMLElement).offsetLeft)
- .reduce((accumulate, incoming) => accumulate + incoming, 0).value;
+ .reduce((accumulate, incoming) => accumulate + incoming, 0);
}
get offsetTop(): number {
- return LazyStream.of(...this.rootNode)
+ return new Es2019Array(this.rootNode)
.filter(item => item != null)
- .map(elem => (elem as HTMLElement).offsetTop)
- .reduce((accumulate, incoming) => accumulate + incoming, 0).value;
+ .map(elem => (elem as any).offsetTop)
+ .reduce((accumulate, incoming) => accumulate + incoming, 0);
}
- get asNodeArray(): Array<DomQuery> {
- return [].concat(Stream.of(...this.rootNode).filter(item => item != null).collect(new ArrayCollector()));
+ get asNodeArray(): Array<Element> {
+ return new Es2019Array(...this.rootNode.filter(item => item != null));
}
@@ -1138,10 +1124,10 @@ export class DomQuery implements IDomQuery, IStreamDataSource<DomQuery>, Iterabl
const doc = document.implementation.createHTMLDocument("");
markup = trim(markup);
let lowerMarkup = markup.toLowerCase();
- if (lowerMarkup.search(/\<\!doctype[^\w\-]+/gi) != -1 ||
- lowerMarkup.search(/\<html[^\w\-]+/gi) != -1 ||
- lowerMarkup.search(/\<head[^\w\-]+/gi) != -1 ||
- lowerMarkup.search(/\<body[^\w\-]+/gi) != -1) {
+ if (lowerMarkup.search(/<!doctype[^\w\-]+/gi) != -1 ||
+ lowerMarkup.search(/<html[^\w\-]+/gi) != -1 ||
+ lowerMarkup.search(/<head[^\w\-]+/gi) != -1 ||
+ lowerMarkup.search(/<body[^\w\-]+/gi) != -1) {
doc.documentElement.innerHTML = markup;
return new DomQuery(doc.documentElement);
} else {
@@ -1283,11 +1269,10 @@ export class DomQuery implements IDomQuery, IStreamDataSource<DomQuery>, Iterabl
byId(id: string, includeRoot?: boolean): DomQuery {
let res: Array<DomQuery> = [];
if (includeRoot) {
- res = res.concat(
- LazyStream.of(...(this?.rootNode || []))
- .filter(item => id == item.id)
+ res = res.concat(...
+ new Es2019Array(...(this?.rootNode || []))
+ .filter(((item) => id == item.id) as any)
.map(item => new DomQuery(item))
- .collect(new ArrayCollector())
);
}
@@ -1303,10 +1288,9 @@ export class DomQuery implements IDomQuery, IStreamDataSource<DomQuery>, Iterabl
let res: Array<DomQuery> = [];
if (includeRoot) {
res = res.concat(
- LazyStream.of(...(this?.rootNode || []))
+ new Es2019Array(...(this?.rootNode || []))
.filter(item => id == item.id)
.map(item => new DomQuery(item))
- .collect(new ArrayCollector())
);
}
@@ -1327,10 +1311,9 @@ export class DomQuery implements IDomQuery, IStreamDataSource<DomQuery>, Iterabl
byTagName(tagName: string, includeRoot ?: boolean, deep ?: boolean): DomQuery {
let res: Array<Element | DomQuery> = [];
if (includeRoot) {
- res = <any>LazyStream.of(...(this?.rootNode ?? []))
+ res = new Es2019Array(...(this?.rootNode ?? []))
.filter(element => element?.tagName == tagName)
- .reduce<Array<Element | DomQuery>>((reduction: any, item: Element) => reduction.concat([item]), res)
- .orElse(res).value;
+ .reduce((reduction: any, item: Element) => reduction.concat([item]), res);
}
(deep) ? res.push(this.querySelectorAllDeep(tagName)) : res.push(this.querySelectorAll(tagName));
@@ -1481,11 +1464,8 @@ export class DomQuery implements IDomQuery, IStreamDataSource<DomQuery>, Iterabl
* @param selector
*/
matchesSelector(selector: string): boolean {
- const ret = this.lazyStream
- .map(item => this._mozMatchesSelector(item.getAsElem(0).value, selector))
- .filter(match => match)
- .first();
- return ret.isPresent();
+ return this.asArray
+ .some(item => this._mozMatchesSelector(item.getAsElem(0).value, selector));
}
/**
@@ -1533,8 +1513,8 @@ export class DomQuery implements IDomQuery, IStreamDataSource<DomQuery>, Iterabl
}
each(func: (item: DomQuery, cnt?: number) => any): DomQuery {
- Stream.of(...this.rootNode)
- .each((item, cnt) => {
+ new Es2019Array(...this.rootNode)
+ .forEach((item, cnt) => {
// we could use a filter, but for the best performance we don´t
if (item == null) {
return;
@@ -1775,7 +1755,7 @@ export class DomQuery implements IDomQuery, IStreamDataSource<DomQuery>, Iterabl
let ret = [];
while(parent.isPresent()) {
if(parent.matchesSelector(selector)) {
- ret.push(parent);
+ ret.push(parent);
}
parent = parent.parent();
}
@@ -1916,7 +1896,7 @@ export class DomQuery implements IDomQuery, IStreamDataSource<DomQuery>, Iterabl
// scripts before we run the 'include' command
// this.globalEval(finalScripts.join("\n"));
let joinedScripts = [];
- Stream.of(...scriptsToProcess).each(item => {
+ new Es2019Array(...scriptsToProcess).forEach(item => {
if (!item.nonce) {
joinedScripts.push(item.evalText)
} else {
@@ -2006,10 +1986,10 @@ export class DomQuery implements IDomQuery, IStreamDataSource<DomQuery>, Iterabl
try {
let scriptElements = new DomQuery(this.filterSelector("script"), this.querySelectorAll("script"));
// script execution order by relative pos in their dom tree
- scriptElements.stream
- .flatMap(item => Stream.of(...item.values))
+ scriptElements.asArray
+ .flatMap(item => [...item.values])
.sort((node1, node2) => node1.compareDocumentPosition(node2) - 3) // preceding 2, following == 4)
- .each(item => execScript(item));
+ .forEach(item => execScript(item));
evalCollectedScripts(finalScripts);
} catch (e) {
@@ -2036,38 +2016,39 @@ export class DomQuery implements IDomQuery, IStreamDataSource<DomQuery>, Iterabl
runCss(): DomQuery {
const execCss = (toReplace: HTMLElement) => {
- const _toReplace = DomQuery.byId(toReplace);
- const tagName = _toReplace.tagName.orElse("").value;
- const head = DomQuery.byTagName("head");
+ const _toReplace = DomQuery.byId(toReplace);
+ const tagName = _toReplace.tagName.orElse("").value;
+ const head = DomQuery.byTagName("head");
- if (tagName && eqi(tagName, "link") && eqi(toReplace.getAttribute("rel"), "stylesheet")) {
- const rel = toReplace.getAttribute("rel");
- //if possible we are now replacing the existing elements where we reference this stylesheet
- const matches = head.querySelectorAll(`link[rel='stylesheet'][href='${rel}']`);
+ if (tagName && eqi(tagName, "link") && eqi(toReplace.getAttribute("rel"), "stylesheet")) {
+ const rel = toReplace.getAttribute("rel");
+ //if possible we are now replacing the existing elements where we reference this stylesheet
+ const matches = head.querySelectorAll(`link[rel='stylesheet'][href='${rel}']`);
- if(matches.length) {
- matches.replace(_toReplace);
- } else {
- head.append(_toReplace);
- }
- } else if (tagName && eqi(tagName, "style")) {
- let innerText = _toReplace.innerHTML.replace(/\s+/gi, "");
- let styles = head.querySelectorAll("style");
- styles = styles.stream.filter(style => {
- return style.innerHTML.replace(/\s+/gi, "") == innerText;
- }).collect(new DomQueryCollector())
- if(!styles.length) { //already present
- head.append(_toReplace);
- }
+ if(matches.length) {
+ matches.replace(_toReplace);
+ } else {
+ head.append(_toReplace);
}
- };
+ } else if (tagName && eqi(tagName, "style")) {
+ let innerText = _toReplace.innerHTML.replace(/\s+/gi, "");
+ let styles = head.querySelectorAll("style");
+ let filteredStyles = styles.asArray.filter(style => {
+ return style.innerHTML.replace(/\s+/gi, "") == innerText;
+ });
+ styles = new DomQuery(...filteredStyles);
+ if(!styles.length) { //already present
+ head.append(_toReplace);
+ }
+ }
+ };
const scriptElements: DomQuery = new DomQuery(this.filterSelector("link, style"), this.querySelectorAll("link, style"));
- scriptElements.stream
- .flatMap(item => Stream.of(...item.values))
+ scriptElements.asArray
+ .flatMap(item => [...item.values])
.sort((node1, node2) => node1.compareDocumentPosition(node2) - 3)
- .each(item => execCss(item));
+ .forEach(item => execCss(item as HTMLElement));
return this;
}
@@ -2095,10 +2076,12 @@ export class DomQuery implements IDomQuery, IStreamDataSource<DomQuery>, Iterabl
*/
fireEvent(eventName: string, options: {[key: string]: any} = {}) {
// merge with last one having the highest priority
- let finalOptions = Stream.ofAssoc({
+
+ let finalOptions: any = {
bubbles: true, cancelable: true
- }).concat(Stream.ofAssoc(options)).collect(new AssocArrayCollector());
-
+ }
+ finalOptions = simpleShallowMerge(finalOptions, options);
+
this.eachElem((node: Element) => {
let doc;
if (node.ownerDocument) {
@@ -2149,16 +2132,14 @@ export class DomQuery implements IDomQuery, IStreamDataSource<DomQuery>, Iterabl
// IE-old school style, you can drop this if you don't need to support IE8 and lower
let event = doc.createEventObject();
event.synthetic = true; // allow detection of synthetic events
- Stream.ofAssoc(finalOptions).each(([key, value]): void => {
- event[key] = value;
- });
+ Object.keys(finalOptions).forEach(key => event[key] = finalOptions[key]);
(<any>node).fireEvent("on" + eventName, event);
}
})
}
textContent(joinString: string = ""): string {
- return this.stream
+ return this.asArray
.map((value: DomQuery) => {
let item = value.getAsElem(0).orElseLazy(() => {
return <any>{
@@ -2167,11 +2148,11 @@ export class DomQuery implements IDomQuery, IStreamDataSource<DomQuery>, Iterabl
}).value;
return (<any>item).textContent || "";
})
- .reduce((text1, text2) => [text1,joinString,text2].join(""), "").value;
+ .reduce((text1, text2) => [text1,joinString,text2].join(""), "");
}
innerText(joinString: string = ""): string {
- return this.stream
+ return this.asArray
.map((value: DomQuery) => {
let item = value.getAsElem(0).orElseLazy(() => {
return <any>{
@@ -2180,8 +2161,9 @@ export class DomQuery implements IDomQuery, IStreamDataSource<DomQuery>, Iterabl
}).value;
return (<any>item).innerText || "";
})
- .reduce((text1, text2) => [text1, text2].join(joinString), "").value;
-
+ .reduce((text1, text2) => {
+ return [text1, text2].join(joinString)
+ }, "");
}
/**
@@ -2193,7 +2175,7 @@ export class DomQuery implements IDomQuery, IStreamDataSource<DomQuery>, Iterabl
* @param toMerge optional config which can be merged in
* @return a copy pf
*/
- encodeFormElement(toMerge = new Config({})): Config {
+ encodeFormElement(toMerge = {}): {[key: string]: any} {
// browser behavior no element name no encoding (normal submit fails in that case)
// https:// issues.apache.org/jira/browse/MYFACES-2847
@@ -2202,7 +2184,7 @@ export class DomQuery implements IDomQuery, IStreamDataSource<DomQuery>, Iterabl
}
// let´s keep it side-effects free
- let target = toMerge.shallowCopy;
+ let target = simpleShallowMerge(toMerge);
this.each((element: DomQuery) => {
if (element.name.isAbsent()) {// no name, no encoding
@@ -2240,7 +2222,7 @@ export class DomQuery implements IDomQuery, IStreamDataSource<DomQuery>, Iterabl
// let subBuf = [];
if (selectElem.options[u].selected) {
let elementOption = selectElem.options[u];
- target.append(name).value = (elementOption.getAttribute("value") != null) ?
+ append(target, name).value = (elementOption.getAttribute("value") != null) ?
elementOption.value : elementOption.text;
}
}
@@ -2268,13 +2250,13 @@ export class DomQuery implements IDomQuery, IStreamDataSource<DomQuery>, Iterabl
let filesArr: any = uploadedFiles ?? [];
if (filesArr?.length) { //files can be empty but set
// xhr level2, single multiple must be passes as they are
- target.assign(name).value = Array.from(filesArr);
+ assign(target, name).value = Array.from(filesArr);
} else {
if(!!uploadedFiles) { //we skip empty file elements i
return;
}
//checkboxes etc.. need to be appended
- target.append(name).value = element.inputValue.value;
+ append(target, name).value = element.inputValue.value;
}
}
@@ -2287,14 +2269,25 @@ export class DomQuery implements IDomQuery, IStreamDataSource<DomQuery>, Iterabl
get cDATAAsString(): string {
let TYPE_CDATA_BLOCK = 4;
- let res: any = this.lazyStream.flatMap(item => {
+ let res = this.asArray
+ .flatMap( item => {
+ return item.childNodes.asArray;
+ })
+ .filter(item => {
+ return item?.value?.value?.nodeType == TYPE_CDATA_BLOCK;
+ })
+ .reduce((reduced: Array<any>, item: DomQuery) => {
+ reduced.push((<any>item?.value?.value)?.data ?? "");
+ return reduced;
+ }, []);
+ /*let res: any = this.lazyStream.flatMap(item => {
return item.childNodes.stream
}).filter(item => {
return item?.value?.value?.nodeType == TYPE_CDATA_BLOCK;
}).reduce((reduced: Array<any>, item: DomQuery) => {
reduced.push((<any>item?.value?.value)?.data ?? "");
return reduced;
- }, []).value;
+ }, []).value;*/
// response may contain several blocks
return res.join("");
@@ -2311,7 +2304,7 @@ export class DomQuery implements IDomQuery, IStreamDataSource<DomQuery>, Iterabl
// because we can stream from an array stream directly into the dom query
_limits = -1;
- limits(end: number): IStream<DomQuery> {
+ limits(end: number): DomQuery {
this._limits = end;
return <any>this;
}
@@ -2456,7 +2449,7 @@ export class DomQuery implements IDomQuery, IStreamDataSource<DomQuery>, Iterabl
* Implementation of an iterator
* to allow loops over dom query collections
*/
- [Symbol.iterator](): Iterator<DomQuery, any, undefined> {
+ [Symbol.iterator](): Iterator<DomQuery> {
return {
next: () => {
let done = !this.hasNext();
@@ -2474,18 +2467,19 @@ export class DomQuery implements IDomQuery, IStreamDataSource<DomQuery>, Iterabl
* @param toAttach the elements to attach
* @param filterDoubles filter out possible double elements (aka same markup)
*/
- concat(toAttach: DomQuery, filterDoubles = true): any {
- const ret = this.lazyStream.concat(toAttach.lazyStream).collect(new DomQueryCollector());
+ concat(toAttach: DomQuery, filterDoubles = true): DomQuery {
+ let domQueries = this.asArray;
+ const ret = new DomQuery(...domQueries.concat(toAttach.asArray));
// we now filter the doubles out
if (!filterDoubles) {
return ret;
}
let idx = {}; // ie11 does not support sets, we have to fake it
- return ret.lazyStream.filter(node => {
+ return new DomQuery(...ret.asArray.filter(node => {
const notFound = !(idx?.[node.value.value.outerHTML as any]);
idx[node.value.value.outerHTML as any] = true;
return notFound;
- }).collect(new DomQueryCollector());
+ }));
}
append(elem: DomQuery): DomQuery {
@@ -2523,7 +2517,7 @@ export class DomQuery implements IDomQuery, IStreamDataSource<DomQuery>, Iterabl
continue;
}
let res = this.rootNode[cnt].querySelectorAll(selector);
- nodes = nodes.concat(objToArray(res));
+ nodes = nodes.concat(...objToArray(res));
}
return new DomQuery(...nodes);
diff --git a/api/src/client/typescript/mona_dish/Es2019Array.ts b/api/src/client/typescript/mona_dish/Es2019Array.ts
new file mode 100644
index 000000000..b0be4e420
--- /dev/null
+++ b/api/src/client/typescript/mona_dish/Es2019Array.ts
@@ -0,0 +1,135 @@
+/**
+ * Extended array
+ */
+
+/**
+ * Extended array which adds various es 2019 shim functions to the normal array
+ * We must remap all array producing functions in order to keep
+ * the delegation active, once we are in!
+ */
+class Es2019Array_<T> extends Array<T>{
+
+ _another: T[];
+
+ constructor(...another: T[]) {
+ super(...another);
+ if((another as any)._another) {
+ this._another = (another as any)._another;
+ } else {
+ this._another = another;
+ }
+
+ //for testing it definitely runs into this branch because we are on es5 level
+ //if (!(<any>Array.prototype).flatMap) {
+ this.flatMap = (flatMapFun) => this._flatMap(flatMapFun) as any;
+ //}
+ //if (!(<any>Array.prototype).flat) {
+ this.flat = (flatLevel: number = 1) => this._flat(flatLevel);
+ //}
+ }
+
+ map<U>(callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any): U[] {
+ const ret = Array.prototype.map.call(this._another, callbackfn, thisArg);
+ return new (_Es2019Array as any) (... ret);
+ }
+
+ concat(...items): T[] {
+ const ret = Array.prototype.concat.call(this._another, ...items);
+ return new (_Es2019Array as any)(... ret);
+ }
+
+ reverse(): T[] {
+ const ret = Array.prototype.reverse.call(this._another);
+ return new (_Es2019Array as any)(... ret);
+ }
+
+ slice(start?: number, end?: number): T[] {
+ const ret = Array.prototype.slice.call(this._another, start, end);
+ return new (_Es2019Array as any)(...ret);
+ }
+
+ splice(start: number, deleteCount?: number): T[] {
+ const ret = Array.prototype.splice.call(this._another, start, deleteCount);
+ return new (_Es2019Array as any)(...ret);
+ }
+
+ filter<S extends T>(predicate: (value: T, index: number, array: T[]) => any, thisArg?: any): S[] {
+ const ret = Array.prototype.filter.call(this._another, predicate, thisArg);
+ return new (_Es2019Array as any)(...ret);
+ }
+
+
+ reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue?: T): T {
+ const ret = Array.prototype.reduce.call(this._another, callbackfn, initialValue);
+ return ret;
+ }
+
+ /*reduceRight(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue: T): T {
+ const ret = Array.prototype.reduceRight.call(callbackfn, initialValue);
+ return ret;
+ }*/
+
+ private _flat(flatDepth = 1) {
+ return this._flatResolve(this._another, flatDepth);
+ }
+
+ private _flatResolve(arr, flatDepth = 1) {
+ //recursion break
+ if (flatDepth == 0) {
+ return arr;
+ }
+ let res = [];
+
+ let reFlat = item => {
+ item = Array.isArray(item) ? item : [item];
+ let mapped = this._flatResolve(item, flatDepth - 1);
+ res = res.concat(mapped);
+ };
+ arr.forEach(reFlat)
+
+ return new Es2019Array(...res);
+ }
+
+ private _flatMap(mapperFunction: Function): any {
+ let res = this.map(item => mapperFunction(item));
+ return this._flatResolve(res);
+ }
+}
+
+//let _Es2019Array = function<T>(...data: T[]) {};
+
+//let oldProto = Es2019Array.prototype;
+
+export function _Es2019Array<T>(...data: T[]): Es2019Array_<T> {
+ let ret = new Es2019Array_<T>(...data);
+ let proxied = new Proxy<Es2019Array_<T>>(ret, {
+ get(target: Es2019Array_<unknown>, p: string | symbol, receiver: any): any {
+ if("symbol" == typeof p) {
+
+ return target._another[p];
+ }
+ if(!isNaN(parseInt(p as string))) {
+ return target._another[p];
+ } else {
+ return target[p];
+ }
+ },
+
+ set(target, property, value): boolean {
+ target[property] = value;
+ target._another[property] = value;
+ return true;
+ }
+
+ });
+ return proxied;
+};
+
+/**
+ * this is the switch between normal array and our shim
+ * the shim is only provided in case the native browser
+ * does not yet have flatMap support on arrays
+ */
+export var Es2019Array: any = (Array.prototype.flatMap) ? function<T>(...data: T[]): T[] {
+ return data;
+} : _Es2019Array;
\ No newline at end of file
diff --git a/api/src/client/typescript/mona_dish/ExtendedArray.ts b/api/src/client/typescript/mona_dish/ExtendedArray.ts
deleted file mode 100644
index 82ea34cd5..000000000
--- a/api/src/client/typescript/mona_dish/ExtendedArray.ts
+++ /dev/null
@@ -1,66 +0,0 @@
-/*!
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-/**
- * Array with a set of shim functions for older browsers
- * we do not extend prototype (rule #1)
- *
- * This is a helper which for now adds the missing flatMap, without prototype pollution
- *
- * that way we can avoid streams wherever we just want to go pure JS
- * This class is self isolated, so it suffices to just dump it into a project one way or the other
- * without anything else
- */
-export class ExtendedArray<T> extends Array<T> {
-
- constructor(...items: T[]) {
- super(...items);
- //es5 base class see //fix for es5 deficit from https://github.com/Microsoft/TypeScript/issues/13720
-
- //for testing it definitely runs into this branch because we are on es5 level
- if(!(<any>Array.prototype).flatMap) {
- let flatmapFun = (<any>ExtendedArray).prototype.flatMap;
- //unfortunately in es5 the flaptmap function is lost due to inheritance of a primitive
- //es class, we have to remap it back in
- this.flatMap = flatmapFun;
- }
- }
-
- flatMap(mapperFunction: Function, noFallback: boolean = false): ExtendedArray<T> {
-
- let res = [];
-
- let remap = item => {
- let opRes = mapperFunction(item);
- if(Array.isArray(opRes)) {
- if(opRes.length == 1) {
- res.push(opRes[1])
- return;
- }
- if(opRes.length > 1) {
- opRes.forEach(newItem => remap(newItem))
- }
- } else {
- res.push(item);
- }
- };
- this.forEach( item => remap(item) )
-
- return new ExtendedArray(...res);
- }
-}
\ No newline at end of file
diff --git a/api/src/client/typescript/mona_dish/Lang.ts b/api/src/client/typescript/mona_dish/Lang.ts
index 40293c563..a4d846da2 100644
--- a/api/src/client/typescript/mona_dish/Lang.ts
+++ b/api/src/client/typescript/mona_dish/Lang.ts
@@ -16,6 +16,7 @@
*/
import {Optional} from "./Monad";
+import {Es2019Array} from "./Es2019Array";
/**
* Lang helpers crossported from the apache myfaces project
@@ -25,7 +26,7 @@ export module Lang {
//should be in lang, but for now here to avoid recursive imports, not sure if typescript still has a problem with those
/**
- * helper function to savely resolve anything
+ * helper function to safely resolve anything
* this is not an elvis operator, it resolves
* a value without exception in a tree and if
* it is not resolvable then an optional of
@@ -50,6 +51,12 @@ export module Lang {
}
}
+ /**
+ * lazy resolve... aka the function is called on resolve and a default value also
+ * is a producing function (called only if the original producer does not produce any result)
+ * @param resolverProducer the producer for the resolve
+ * @param defaultValue the default value producer function
+ */
export function saveResolveLazy<T>(resolverProducer: () => T, defaultValue: () => T = null): Optional<T> {
try {
let result = resolverProducer();
@@ -105,11 +112,11 @@ export module Lang {
//special condition array delivered no offset no pack
if ((<any>obj) instanceof Array && !offset && !pack) return obj;
- return pack.concat(Array.prototype.slice.call(obj, offset));
+ return new Es2019Array(...pack.concat(Array.prototype.slice.call(obj, offset)));
}
/**
- * equalsIgnoreCase, case insensitive comparison of two strings
+ * equalsIgnoreCase, case-insensitive comparison of two strings
*
* @param source
* @param destination
@@ -134,7 +141,7 @@ export module Lang {
}
/**
- * Backported from dojo
+ * Back ported from Dojo
* a failsafe string determination method
* (since in javascript String != "" typeof alone fails!)
* @param it {|Object|} the object to be checked for being a string
@@ -146,6 +153,10 @@ export module Lang {
return !!arguments.length && it != null && (typeof it == "string" || it instanceof String); // Boolean
}
+ /**
+ * Back-ported, a failsafe determination code for checking whether an object is a function
+ * @param it the object to check for being a function
+ */
export function isFunc(it: any): boolean {
return it instanceof Function || typeof it === "function";
}
diff --git a/api/src/client/typescript/mona_dish/Messaging.ts b/api/src/client/typescript/mona_dish/Messaging.ts
index 96aca50c7..4371e9401 100644
--- a/api/src/client/typescript/mona_dish/Messaging.ts
+++ b/api/src/client/typescript/mona_dish/Messaging.ts
@@ -109,7 +109,10 @@ class MessageWrapper implements CustomEventInit<Message> {
}
}
-
+/**
+ * abstract broker class
+ * (The broker is the central distribution unit of messages)
+ */
abstract class BaseBroker {
static readonly EVENT_TYPE = "brokerEvent";
@@ -346,8 +349,12 @@ let broadCastChannelBrokerGenerator = (name) => {
const DEFAULT_CHANNEL_GROUP = "brokr";
/**
- * a broker which hooks into the Broadcast Channel broker
+ * a broker which hooks into the Broadcast Channel
* either via shim or substitute lib
+ *
+ * The broadcast channels are a standardized messaging library
+ * The broker mechanism sets a layer on top to make it more manageable!
+ *
*/
export class BroadcastChannelBroker extends BaseBroker {
private openChannels: [{ key: string }, BroadcastChannel] = <any>{};
diff --git a/api/src/client/typescript/mona_dish/Monad.ts b/api/src/client/typescript/mona_dish/Monad.ts
index 6a574bf39..8b385e9a1 100644
--- a/api/src/client/typescript/mona_dish/Monad.ts
+++ b/api/src/client/typescript/mona_dish/Monad.ts
@@ -16,16 +16,14 @@
*/
/**
- * A module which keeps basic monadish like definitions in place without any sidedependencies to other modules.
+ * A module which keeps basic monad like definitions in place
* Useful if you need the functions in another library to keep its dependencies down
*/
/*IMonad definitions*/
import {Lang} from "./Lang";
-import {ArrayCollector, AssocArrayCollector} from "./SourcesCollectors";
-import {Stream} from "./Stream";
-import objAssign = Lang.objAssign;
+import {Es2019Array} from "./Es2019Array";
/**
@@ -38,7 +36,7 @@ export interface IFunctor<T> {
}
/**
- * IMonad definition, basically a functor with a flaptmap implementation (flatmap reduces all nested monads after a
+ * IMonad definition, basically a functor with a flatMap implementation, flatMap reduces all nested monads after a
* function call f into a monad with the nesting level of 1
*
* flatmap flats nested Monads into a IMonad of the deepest nested implementation
@@ -57,10 +55,11 @@ export interface IIdentity<T> extends IFunctor<T> {
readonly value: T;
}
+
/**
* custom value holder definition, since we are not pure functional
* but iterative we have structures which allow the assignment of a value
- * also not all structures are sideffect free
+ * also not all structures are side - effect free
*/
export interface IValueHolder<T> {
value: T | Array<T>;
@@ -68,7 +67,7 @@ export interface IValueHolder<T> {
/**
* Implementation of a monad
- * (Sideffect free), no write allowed directly on the monads
+ * (Side - effect free), no write allowed directly on the monads
* value state
*/
export class Monad<T> implements IMonad<T, Monad<any>>, IValueHolder<T> {
@@ -84,7 +83,7 @@ export class Monad<T> implements IMonad<T, Monad<any>>, IValueHolder<T> {
map<R>(fn?: (data: T) => R): Monad<R> {
if (!fn) {
- fn = (inval: any) => <R>inval;
+ fn = (inVal: any) => <R>inVal;
}
let result: R = fn(this.value);
return new Monad(result);
@@ -103,7 +102,7 @@ export class Monad<T> implements IMonad<T, Monad<any>>, IValueHolder<T> {
/**
* optional implementation, an optional is basically an implementation of a Monad with additional syntactic
* sugar on top
- * (Sideeffect free, since value assignment is not allowed)
+ * (Side - effect free, since value assignment is not allowed)
* */
export class Optional<T> extends Monad<T> {
@@ -173,8 +172,8 @@ export class Optional<T> extends Monad<T> {
}
/*
- * we need to implement it to fullfill the contract, although it is used only internally
- * all values are flattened when accessed anyway, so there is no need to call this methiod
+ * we need to implement it to fulfill the contract, although it is used only internally
+ * all values are flattened when accessed anyway, so there is no need to call this method
*/
flatMap<R>(fn?: (data: T) => R): Optional<any> {
let val = super.flatMap(fn);
@@ -187,7 +186,7 @@ export class Optional<T> extends Monad<T> {
/*
* elvis operation, take care, if you use this you lose typesafety and refactoring
- * capabilites, unfortunately typesceript does not allow to have its own elvis operator
+ * capabilities, unfortunately typescript does not allow to have its own elvis operator
* this is some syntactic sugar however which is quite useful*/
getIf<R>(...key: string[]): Optional<R> {
@@ -223,9 +222,7 @@ export class Optional<T> extends Monad<T> {
currentPos = this.getClass().fromNullable(currentPos.value[arrPos]);
}
}
- let retVal = currentPos;
-
- return retVal;
+ return currentPos;
}
/**
@@ -266,7 +263,7 @@ export class Optional<T> extends Monad<T> {
* by having a getClass operation we can avoid direct calls into the constructor or
* static methods and do not have to implement several methods which rely on the type
* of "this"
- * @returns {Monadish.Optional}
+ * @returns the type of Optional
*/
protected getClass(): any {
return Optional;
@@ -331,9 +328,9 @@ export class Optional<T> extends Monad<T> {
protected preprocessKeys(...keys): string[] {
- return Stream.of(...keys)
+ return new Es2019Array(...keys)
.flatMap(item => {
- return Stream.of(...item.split(/\]\s*\[/gi))
+ return new Es2019Array(...item.split(/]\s*\[/gi))
.map(item => {
item = item.replace(/^\s+|\s+$/g, "");
if(item.indexOf("[") == -1 && item.indexOf("]") != -1) {
@@ -344,17 +341,16 @@ export class Optional<T> extends Monad<T> {
}
return item;
})
- })
-
- .collect(new ArrayCollector());
+ });
}
}
-// --------------------- From here onwards we break out the sideffects free limits ------------
+
+// --------------------- From here onwards we break out the side effect free limits ------------
/**
* ValueEmbedder is the writeable version
- * of optional, it basically is a wrappber
+ * of optional, it basically is a wrapper
* around a construct which has a state
* and can be written to.
*
@@ -405,7 +401,7 @@ export class ValueEmbedder<T> extends Optional<T> implements IValueHolder<T> {
* by having a getClass operation we can avoid direct calls into the constructor or
* static methods and do not have to implement several methods which rely on the type
* of "this"
- * @returns {Monadish.Optional}
+ * @returns ValueEmbedder
*/
protected getClass(): any {
return ValueEmbedder;
@@ -417,392 +413,5 @@ export class ValueEmbedder<T> extends Optional<T> implements IValueHolder<T> {
}
-/**
- * specialized value embedder
- * for our Configuration
- */
-class ConfigEntry<T> extends ValueEmbedder<T> {
-
- /*default value for absent*/
- static absent = ConfigEntry.fromNullable(null);
-
- /**
- * arrayed value positions
- */
- arrPos: number;
-
- constructor(rootElem: any, key: any, arrPos?: number) {
- super(rootElem, key);
-
- this.arrPos = arrPos ?? -1;
- }
-
- get value() {
- if (this.key == "" && this.arrPos >= 0) {
- return this._value[this.arrPos];
- } else if (this.key && this.arrPos >= 0) {
- return this._value[this.key][this.arrPos];
- }
- return this._value[this.key];
- }
-
- set value(val: T) {
- if (this.key == "" && this.arrPos >= 0) {
- this._value[this.arrPos] = val;
- return;
- } else if (this.key && this.arrPos >= 0) {
- this._value[this.key][this.arrPos] = val;
- return;
- }
- this._value[this.key] = val;
- }
-}
-
-
-export const CONFIG_VALUE = "__END_POINT__";
-export const CONFIG_ANY = "__ANY_POINT__";
-const ALL_VALUES = "*";
-
-
-
-export type ConfigDef = {[key: string]: any};
-
-
-/**
- * Config, basically an optional wrapper for a json structure
- * (not sideeffect free, since we can alter the internal config state
- * without generating a new config), not sure if we should make it sideffect free
- * since this would swallow a lot of performane and ram
- */
-export class Config extends Optional<any> {
- constructor(root: any, private configDef ?: ConfigDef) {
- super(root);
- }
-
- /**
- * shallow copy getter, copies only the first level, references the deeper nodes
- * in a shared manner
- */
- get shallowCopy(): Config {
- return this.shallowCopy$();
- }
-
- protected shallowCopy$(): Config {
- return new Config(Stream.ofAssoc(this.value).collect(new AssocArrayCollector()));
- }
-
- /**
- * deep copy, copies all config nodes
- */
- get deepCopy(): Config {
- return this.deepCopy$();
- }
-
- protected deepCopy$(): Config {
- return new Config(objAssign({}, this.value));
- }
-
- /**
- * creates a config from an initial value or null
- * @param value
- */
- static fromNullable<T>(value?: T | null): Config {
- return new Config(value);
- }
-
- /**
- * simple merge for the root configs
- */
- shallowMerge(other: Config, overwrite = true, withAppend = false) {
- for (let key in other.value) {
- if('undefined' == typeof key || null == key) {
- continue;
- }
- if (overwrite || !(key in this.value)) {
- if (!withAppend) {
- this.assign(key).value = other.getIf(key).value;
- } else {
- if (Array.isArray(other.getIf(key).value)) {
- Stream.of(...other.getIf(key).value).each(item => this.append(key).value = item);
- } else {
- this.append(key).value = other.getIf(key).value;
- }
- }
- }
- }
- }
-
- /**
- * assigns a single value as array, or appends it
- * to an existing value mapping a single value to array
- *
- *
- * usage myConfig.append("foobaz").value = "newValue"
- * myConfig.append("foobaz").value = "newValue2"
- *
- * resulting in myConfig.foobaz == ["newValue, newValue2"]
- *
- * @param {string[]} accessPath
- */
- append(...accessPath: string[]): IValueHolder<any> {
- let noKeys = accessPath.length < 1;
- if (noKeys) {
- return;
- }
- this.assertAccessPath(...accessPath);
-
- let lastKey = accessPath[accessPath.length - 1];
- let currKey, finalKey = this.keyVal(lastKey);
-
- let pathExists = this.getIf(...accessPath).isPresent();
- this.buildPath(...accessPath);
-
- let finalKeyArrPos = this.arrayIndex(lastKey);
- if (finalKeyArrPos > -1) {
- throw Error("Append only possible on non array properties, use assign on indexed data");
- }
- let value = <any>this.getIf(...accessPath).value;
- if (!Array.isArray(value)) {
- value = this.assign(...accessPath).value = [value];
- }
- if (pathExists) {
- value.push({});
- }
- finalKeyArrPos = value.length - 1;
-
- let retVal = new ConfigEntry(accessPath.length == 1 ? this.value : this.getIf.apply(this, accessPath.slice(0, accessPath.length - 1)).value,
- lastKey, finalKeyArrPos
- );
-
- return retVal;
- }
-
- /**
- * appends to an existing entry (or extends into an array and appends)
- * if the condition is met
- * @param {boolean} condition
- * @param {string[]} accessPath
- */
- appendIf(condition: boolean, ...accessPath: string[]): IValueHolder<any> {
- if (!condition) {
- return {value: null};
- }
- return this.append(...accessPath);
- }
-
- /**
- * assings an new value on the given access path
- * @param accessPath
- */
- assign(...accessPath): IValueHolder<any> {
- if (accessPath.length < 1) {
- return;
- }
- this.assertAccessPath(...accessPath);
-
- this.buildPath(...accessPath);
-
- let currKey = this.keyVal(accessPath[accessPath.length - 1]);
- let arrPos = this.arrayIndex(accessPath[accessPath.length - 1]);
- let retVal = new ConfigEntry(accessPath.length == 1 ? this.value : this.getIf.apply(this, accessPath.slice(0, accessPath.length - 1)).value,
- currKey, arrPos
- );
-
- return retVal;
- }
-
-
- /**
- * assign a value if the condition is set to true, otherwise skip it
- *
- * @param condition the condition, the access accessPath into the config
- * @param accessPath
- */
- assignIf(condition: boolean, ...accessPath: Array<any>): IValueHolder<any> {
- return condition ? this.assign(...accessPath) : {value: null};
- }
-
- /**
- * get if the access path is present (get is reserved as getter with a default, on the current path)
- * TODO will be renamed to something more meaningful and deprecated, the name is ambigous
- * @param accessPath the access path
- */
- getIf(...accessPath: Array<string>): Config {
- this.assertAccessPath(...accessPath);
- return this.getClass().fromNullable(super.getIf.apply(this, accessPath).value);
- }
-
-
-
- /**
- * gets the current node and if none is present returns a config with a default value
- * @param defaultVal
- */
- get(defaultVal: any): Config {
- return this.getClass().fromNullable(super.get(defaultVal).value);
- }
-
- //empties the current config entry
- delete(key: string): Config {
- if (key in this.value) {
- delete this.value[key];
- }
- return this;
- }
-
- /**
- * converts the entire config into a json object
- */
- toJson(): any {
- return JSON.stringify(this.value);
- }
-
- /**
- * returns the first config level as streeam
- */
- get stream(): Stream<[string, any]> {
- return Stream.of(... Object.keys(this.value)).map(key => [key, this.value[key]]);
- }
-
- protected getClass(): any {
- return Config;
- }
-
- private setVal(val: any) {
- this._value = val;
- }
-
-
- /**
- * asserts the access path for a semy typed access
- * @param accessPath
- * @private
- */
- private assertAccessPath(...accessPath: Array<string>) {
- accessPath = this.preprocessKeys(...accessPath);
- if(!this.configDef) {
- //untyped
- return;
- }
-
- let currAccessPos = null;
-
- const ERR_ACCESS_PATH = "Access Path to config invalid";
- const ABSENT = "__ABSENT__";
- currAccessPos = this.configDef;
-
-
-
- for (let cnt = 0; cnt < accessPath.length; cnt++) {
- let currKey = this.keyVal(accessPath[cnt]);
- let arrPos = this.arrayIndex(accessPath[cnt]);
-
- //key index
- if(this.isArray(arrPos)) {
- if(currKey != "") {
- currAccessPos = (Array.isArray(currAccessPos)) ?
- Stream.of(...currAccessPos)
- .filter(item => !!(item?.[currKey] ?? false))
- .map(item => item?.[currKey]).first() :
- Optional.fromNullable(currAccessPos?.[currKey] ?? null);
- } else {
- currAccessPos = (Array.isArray(currAccessPos)) ?
- Stream.of(...currAccessPos)
- .filter(item => Array.isArray(item))
- .flatMap(item => Stream.of(...item)).first() : Optional.absent;
- }
- //we noe store either the current array or the filtered look ahead to go further
- } else {
- //we now have an array and go further with a singular key
- currAccessPos = (Array.isArray(currAccessPos)) ? Stream.of(...currAccessPos)
- .filter(item => !! (item?.[currKey] ?? false))
- .map(item => item?.[currKey])
- .first():
- Optional.fromNullable(currAccessPos?.[currKey] ?? null);
- }
- if(!currAccessPos.isPresent()) {
- throw Error(ERR_ACCESS_PATH)
- }
- currAccessPos = currAccessPos.value;
-
- //no further testing needed, from this point onwards we are on our own
- if(currAccessPos == CONFIG_ANY) {
- return;
- }
- }
-
- }
-
-
- /**
- * builds the config path
- *
- * @param accessPath a sequential array of accessPath containing either a key name or an array reference name[<index>]
- */
- private buildPath(...accessPath: string[]): Config {
- accessPath = this.preprocessKeys(...accessPath);
- let val = this;
- let parentVal = this.getClass().fromNullable(null);
- let parentPos = -1;
- let alloc = function (arr: Array<any>, length: number) {
- let length1 = arr.length;
- let length2 = length1 + length;
- for (let cnt = length1; cnt < length2; cnt++) {
- arr.push({});
- }
- };
-
- for (let cnt = 0; cnt < accessPath.length; cnt++) {
- let currKey = this.keyVal(accessPath[cnt]);
- let arrPos = this.arrayIndex(accessPath[cnt]);
-
- if (this.isArrayPos(currKey, arrPos)) {
-
- val.setVal((val.value instanceof Array) ? val.value : []);
- alloc(val.value, arrPos + 1);
- if (parentPos >= 0) {
- parentVal.value[parentPos] = val.value;
- }
- parentVal = val;
- parentPos = arrPos;
- val = this.getClass().fromNullable(val.value[arrPos]);
- continue;
- }
-
- let tempVal = <Config>val.getIf(currKey);
- if (this.isNoArray(arrPos)) {
- if (tempVal.isAbsent()) {
- tempVal = <Config>this.getClass().fromNullable(val.value[currKey] = {});
- } else {
- val = <any>tempVal;
- }
- } else {
- let arr = (tempVal.value instanceof Array) ? tempVal.value : [];
- alloc(arr, arrPos + 1);
- val.value[currKey] = arr;
- tempVal = this.getClass().fromNullable(arr[arrPos]);
- }
- parentVal = val;
- parentPos = arrPos;
- val = <any>tempVal;
- }
-
- return this;
- }
-
- private isNoArray(arrPos: number) {
- return arrPos == -1;
- }
-
- private isArray(arrPos: number) {
- return !this.isNoArray(arrPos);
- }
-
- private isArrayPos(currKey: string, arrPos: number) {
- return currKey === "" && arrPos >= 0;
- }
-
-}
-
diff --git a/api/src/client/typescript/mona_dish/PromiseShim.ts b/api/src/client/typescript/mona_dish/PromiseShim.ts
index 5af5f0640..0d420d9cc 100644
--- a/api/src/client/typescript/mona_dish/PromiseShim.ts
+++ b/api/src/client/typescript/mona_dish/PromiseShim.ts
@@ -21,7 +21,6 @@
*/
import {Promise as _Promise} from "./Promise";
-import type = Mocha.utils.type;
import {_global$} from "./Global";
diff --git a/api/src/client/typescript/mona_dish/SourcesCollectors.ts b/api/src/client/typescript/mona_dish/SourcesCollectors.ts
index 01734753c..687bfcd27 100644
--- a/api/src/client/typescript/mona_dish/SourcesCollectors.ts
+++ b/api/src/client/typescript/mona_dish/SourcesCollectors.ts
@@ -15,19 +15,18 @@
* limitations under the License.
*/
-import {Stream, StreamMapper} from "./Stream";
import {DomQuery} from "./DomQuery";
-
-import {Config} from "./Monad";
+import {Es2019Array} from "./Es2019Array";
+import {Config} from "./Config";
/**
* special status of the datasource location pointer
- * if an access, outside of the possible data boundaries is happening
+ * if an access, outside - of the possible data boundaries is happening
* (example for instance current without a first next call, or next
* which goes over the last possible dataset), an iteration status return
* value is returned marking this boundary instead of a classical element
*
- * Note this is only internally used but must be implemented to fullfill
+ * Note this is only internally used but must be implemented to fulfill
* internal contracts, the end user will never see those values if he uses
* streams!
*/
@@ -36,6 +35,14 @@ export enum ITERATION_STATUS {
BEF_STRM = '___BEF_STRM__',
}
+export function calculateSkips(next_strm: IStreamDataSource<any>) {
+ let pos = 1;
+ while (next_strm.lookAhead(pos) != ITERATION_STATUS.EO_STRM) {
+ pos++;
+ }
+ return --pos;
+}
+
/**
* Every data source wich feeds data into the lazy stream
* or stream generally must implement this interface
@@ -55,12 +62,14 @@ export interface IStreamDataSource<T> {
next(): T | ITERATION_STATUS;
/**
- * returns the next element in the stream
- * difference to next is, that the internal data position
- * is not changed, so next still will deliver the next item from the current
- * data position. Look ahead is mostly needed internally
- * by possible endless data constructs which have no fixed data boundary, or index
- * positions. (aka infinite sets, or flatmapped constructs)
+ * looks ahead cnt without changing the internal data "pointers" of the data source
+ * (this is mostly needed by possibly infinite constructs like lazy streams,
+ * because they do not know by definition their
+ * boundaries)
+ *
+ * @param cnt the elements to look ahead
+ * @return either the element or ITERATION_STATUS.EO_STRM if we hit the end of the stream before
+ * finding the "cnt" element
*/
lookAhead(cnt ?: number): T | ITERATION_STATUS;
@@ -95,14 +104,11 @@ export interface ICollector<T, S> {
finalValue: S;
}
-function calculateSkips(next_strm: IStreamDataSource<any>) {
- let pos = 1;
- while (next_strm.lookAhead(pos) != ITERATION_STATUS.EO_STRM) {
- pos++;
- }
- return --pos;
-}
+/**
+ * A data source which combines multiple streams sequentially into one
+ * (this is used internally by flatmap, but also can be used externally)
+ */
export class MultiStreamDatasource<T> implements IStreamDataSource<T> {
private activeStrm;
@@ -229,7 +235,7 @@ export class SequenceDataSource implements IStreamDataSource<number> {
/**
- * implementation of iteratable on top of array
+ * implementation of a datasource on top of a standard array
*/
export class ArrayStreamDataSource<T> implements IStreamDataSource<T> {
value: Array<T>;
@@ -329,6 +335,15 @@ export class FilteredStreamDatasource<T> implements IStreamDataSource<T> {
return found;
}
+ /**
+ * looks ahead cnt without changing the internal data "pointers" of the data source
+ * (this is mostly needed by LazyStreams, because they do not know by definition their
+ * boundaries)
+ *
+ * @param cnt the elements to look ahead
+ * @return either the element or ITERATION_STATUS.EO_STRM if we hit the end of the stream before
+ * finding the "cnt" element
+ */
lookAhead(cnt = 1): ITERATION_STATUS | T {
let lookupVal: T | ITERATION_STATUS;
@@ -390,129 +405,13 @@ export class MappedStreamDataSource<T, S> implements IStreamDataSource<S> {
}
}
-/**
- * Same for flatmap to deal with element -> stream mappings
- */
-export class FlatMapStreamDataSource<T, S> implements IStreamDataSource<S> {
-
- mapFunc: StreamMapper<T>;
-
- inputDataSource: IStreamDataSource<T>;
-
- /**
- * the currently active stream
- * coming from an incoming element
- * once the end of this one is reached
- * it is swapped out by another one
- * from the next element
- */
- activeDataSource: IStreamDataSource<S>;
- walkedDataSources = [];
- _currPos = 0;
-
- constructor(func: StreamMapper<T>, parent: IStreamDataSource<T>) {
- this.mapFunc = func;
- this.inputDataSource = parent;
- }
-
- hasNext(): boolean {
- return this.resolveActiveHasNext() || this.resolveNextHasNext();
- }
-
- private resolveActiveHasNext() {
- let next = false;
- if (this.activeDataSource) {
- next = this.activeDataSource.hasNext();
- }
- return next;
- }
-
- lookAhead(cnt = 1): ITERATION_STATUS | S {
-
- let lookAhead = this?.activeDataSource?.lookAhead(cnt);
- if (this?.activeDataSource && lookAhead != ITERATION_STATUS.EO_STRM) {
- //this should cover 95% of all cases
- return lookAhead;
- }
-
- if (this.activeDataSource) {
- cnt -= calculateSkips(this.activeDataSource)
- }
-
- //the idea is basically to look into the streams sub-sequentially for a match
- //after each stream we have to take into consideration that the skipCnt is
- //reduced by the number of datasets we already have looked into in the previous stream/datasource
- //unfortunately for now we have to loop into them, so we introduce a small o2 here
- for (let dsLoop = 1; true; dsLoop++) {
- let datasourceData = this.inputDataSource.lookAhead(dsLoop);
- //we have looped out
- //no embedded data anymore? we are done, data
- //can either be a scalar an array or another datasource
- if (datasourceData === ITERATION_STATUS.EO_STRM) {
- return ITERATION_STATUS.EO_STRM;
- }
- let mappedData = this.mapFunc(datasourceData as T);
-
- //it either comes in as datasource or as array
- //both cases must be unified into a datasource
- let currentDataSource = this.toDatasource(mappedData);
- //we now run again a lookahead
- let ret = currentDataSource.lookAhead(cnt);
- //if the value is found then we are set
- if (ret != ITERATION_STATUS.EO_STRM) {
- return ret;
- }
- //reduce the next lookahead by the number of elements
- //we are now skipping in the current data source
- cnt -= calculateSkips(currentDataSource);
- }
- }
-
- private toDatasource(mapped: Array<S> | IStreamDataSource<S>) {
- let ds = Array.isArray(mapped) ? new ArrayStreamDataSource(...mapped) : mapped;
- this.walkedDataSources.push(ds)
- return ds;
- }
-
- private resolveNextHasNext() {
- let next = false;
- while (!next && this.inputDataSource.hasNext()) {
- let mapped = this.mapFunc(this.inputDataSource.next() as T);
- this.activeDataSource = this.toDatasource(mapped);
- next = this.activeDataSource.hasNext();
- }
- return next;
- }
-
- next(): S | ITERATION_STATUS {
- if (this.hasNext()) {
- this._currPos++;
- return this.activeDataSource.next();
- }
- }
-
- reset(): void {
- this.inputDataSource.reset();
- this.walkedDataSources.forEach(ds => ds.reset());
- this.walkedDataSources = [];
- this._currPos = 0;
- this.activeDataSource = null;
- }
-
- current(): S | ITERATION_STATUS {
- if (!this.activeDataSource) {
- this.hasNext();
- }
- return this.activeDataSource.current();
- }
-}
/**
* For the time being we only need one collector
* a collector which collects a stream back into arrays
*/
-export class ArrayCollector<S> implements ICollector<S, Array<S>> {
- private data: Array<S> = [];
+export class ShimArrayCollector<S> implements ICollector<S, Array<S>> {
+ private data: Array<S> = new Es2019Array(...[]);
collect(element: S) {
this.data.push(element);
@@ -632,9 +531,24 @@ export class QueryFormStringCollector implements ICollector<DomQuery, string> {
}
get finalValue(): string {
- return Stream.of(...this.formData)
- .map<string>(keyVal => keyVal.join("="))
- .reduce((item1, item2) => [item1, item2].join("&"))
- .orElse("").value;
+ return new Es2019Array(...this.formData)
+ .map(keyVal => keyVal.join("="))
+ .reduce((item1, item2) => [item1, item2].join("&"));
}
-}
\ No newline at end of file
+}
+
+/**
+ * For the time being we only need one collector
+ * a collector which collects a stream back into arrays
+ */
+export class ArrayCollector<S> implements ICollector<S, Array<S>> {
+ private data: Array<S> = [];
+
+ collect(element: S) {
+ this.data.push(element);
+ }
+
+ get finalValue(): Array<S> {
+ return this.data;
+ }
+}
diff --git a/api/src/client/typescript/mona_dish/Stream.ts b/api/src/client/typescript/mona_dish/Stream.ts
index 7fd24d126..548e4d60a 100644
--- a/api/src/client/typescript/mona_dish/Stream.ts
+++ b/api/src/client/typescript/mona_dish/Stream.ts
@@ -21,14 +21,15 @@
import {IMonad, IValueHolder, Optional} from "./Monad";
import {
ArrayCollector,
- ArrayStreamDataSource,
+ ArrayStreamDataSource, calculateSkips,
FilteredStreamDatasource,
- FlatMapStreamDataSource,
ICollector,
IStreamDataSource,
ITERATION_STATUS,
MappedStreamDataSource, MultiStreamDatasource
} from "./SourcesCollectors";
+import {DomQuery} from "./DomQuery";
+import {Config} from "./Config";
//import {from, Observable} from "rxjs";
@@ -43,6 +44,124 @@ export type Matchable<T> = (data: T) => boolean;
export type Mappable<T, R> = (data: T) => R;
export type Comparator<T> = (el1: T, el2: T) => number;
+
+/**
+ * Same for flatmap to deal with element -> stream mappings
+ */
+export class FlatMapStreamDataSource<T, S> implements IStreamDataSource<S> {
+
+ mapFunc: StreamMapper<T>;
+
+ inputDataSource: IStreamDataSource<T>;
+
+ /**
+ * the currently active stream
+ * coming from an incoming element
+ * once the end of this one is reached
+ * it is swapped out by another one
+ * from the next element
+ */
+ activeDataSource: IStreamDataSource<S>;
+ walkedDataSources = [];
+ _currPos = 0;
+
+ constructor(func: StreamMapper<T>, parent: IStreamDataSource<T>) {
+ this.mapFunc = func;
+ this.inputDataSource = parent;
+ }
+
+ hasNext(): boolean {
+ return this.resolveActiveHasNext() || this.resolveNextHasNext();
+ }
+
+ private resolveActiveHasNext() {
+ let next = false;
+ if (this.activeDataSource) {
+ next = this.activeDataSource.hasNext();
+ }
+ return next;
+ }
+
+ lookAhead(cnt = 1): ITERATION_STATUS | S {
+
+ let lookAhead = this?.activeDataSource?.lookAhead(cnt);
+ if (this?.activeDataSource && lookAhead != ITERATION_STATUS.EO_STRM) {
+ //this should cover 95% of all cases
+ return lookAhead;
+ }
+
+ if (this.activeDataSource) {
+ cnt -= calculateSkips(this.activeDataSource)
+ }
+
+ //the idea is basically to look into the streams sub-sequentially for a match
+ //after each stream we have to take into consideration that the skipCnt is
+ //reduced by the number of datasets we already have looked into in the previous stream/datasource
+ //unfortunately for now we have to loop into them, so we introduce a small o2 here
+ for (let dsLoop = 1; true; dsLoop++) {
+ let datasourceData = this.inputDataSource.lookAhead(dsLoop);
+ //we have looped out
+ //no embedded data anymore? we are done, data
+ //can either be a scalar an array or another datasource
+ if (datasourceData === ITERATION_STATUS.EO_STRM) {
+ return ITERATION_STATUS.EO_STRM;
+ }
+ let mappedData = this.mapFunc(datasourceData as T);
+
+ //it either comes in as datasource or as array
+ //both cases must be unified into a datasource
+ let currentDataSource = this.toDatasource(mappedData);
+ //we now run again a lookahead
+ let ret = currentDataSource.lookAhead(cnt);
+ //if the value is found then we are set
+ if (ret != ITERATION_STATUS.EO_STRM) {
+ return ret;
+ }
+ //reduce the next lookahead by the number of elements
+ //we are now skipping in the current data source
+ cnt -= calculateSkips(currentDataSource);
+ }
+ }
+
+ private toDatasource(mapped: Array<S> | IStreamDataSource<S>) {
+ let ds = Array.isArray(mapped) ? new ArrayStreamDataSource(...mapped) : mapped;
+ this.walkedDataSources.push(ds)
+ return ds;
+ }
+
+ private resolveNextHasNext() {
+ let next = false;
+ while (!next && this.inputDataSource.hasNext()) {
+ let mapped = this.mapFunc(this.inputDataSource.next() as T);
+ this.activeDataSource = this.toDatasource(mapped);
+ next = this.activeDataSource.hasNext();
+ }
+ return next;
+ }
+
+ next(): S | ITERATION_STATUS {
+ if (this.hasNext()) {
+ this._currPos++;
+ return this.activeDataSource.next();
+ }
+ }
+
+ reset(): void {
+ this.inputDataSource.reset();
+ this.walkedDataSources.forEach(ds => ds.reset());
+ this.walkedDataSources = [];
+ this._currPos = 0;
+ this.activeDataSource = null;
+ }
+
+ current(): S | ITERATION_STATUS {
+ if (!this.activeDataSource) {
+ this.hasNext();
+ }
+ return this.activeDataSource.current();
+ }
+}
+
/**
* Generic interface defining a stream
*/
@@ -216,6 +335,14 @@ export class Stream<T> implements IMonad<T, Stream<any>>, IValueHolder<Array<T>>
return new Stream(...value);
}
+ static ofDomQuery(value: DomQuery): Stream<DomQuery> {
+ return Stream.of(...value.asArray);
+ }
+
+ static ofConfig(value: Config): Stream<[string, any]> {
+ return Stream.of(... Object.keys(value.value)).map(key => [key, value.value[key]])
+ }
+
current(): T | ITERATION_STATUS {
if(this.pos == -1) {
return ITERATION_STATUS.BEF_STRM;
@@ -459,6 +586,14 @@ export class LazyStream<T> implements IStreamDataSource<T>, IStream<T>, IMonad<T
return new LazyStream(value);
}
+ static ofDomQuery(value: DomQuery): LazyStream<DomQuery> {
+ return LazyStream.of(...value.asArray);
+ }
+
+ static ofConfig(value: Config): LazyStream<[string, any]> {
+ return LazyStream.of(... Object.keys(value.value)).map(key => [key, value.value[key]])
+ }
+
constructor(parent: IStreamDataSource<T>) {
this.dataSource = parent;
@@ -669,3 +804,23 @@ export class LazyStream<T> implements IStreamDataSource<T>, IStream<T>, IMonad<T
}
+/**
+ * 1.0 backwards compatibility functions
+ *
+ * this restores the stream and lazy stream
+ * property on DomQuery on prototype level
+ *
+ */
+
+Object.defineProperty(DomQuery.prototype, "stream", {
+ get: function stream(){
+ return Stream.ofDomQuery(this);
+ }
+})
+
+
+Object.defineProperty(DomQuery.prototype, "lazyStream", {
+ get: function lazyStream(){
+ return LazyStream.ofDomQuery(this);
+ }
+})
\ No newline at end of file
diff --git a/api/src/client/typescript/mona_dish/index.ts b/api/src/client/typescript/mona_dish/index.ts
index a8961a973..fc71978e6 100644
--- a/api/src/client/typescript/mona_dish/index.ts
+++ b/api/src/client/typescript/mona_dish/index.ts
@@ -18,15 +18,14 @@
*/
export {DomQuery, ElementAttribute, DomQueryCollector, DQ, DQ$} from "./DomQuery";
export {Lang} from "./Lang";
-export {Config, CONFIG_VALUE, CONFIG_ANY, ConfigDef, Monad, IValueHolder, IFunctor, IMonad, IIdentity, Optional, ValueEmbedder} from "./Monad";
+export {Monad, IValueHolder, IFunctor, IMonad, IIdentity, Optional, ValueEmbedder} from "./Monad";
export {CancellablePromise, IPromise, PromiseStatus} from "./Promise";
export {XMLQuery, XQ} from "./XmlQuery";
-export {Stream, LazyStream, IteratableConsumer, IStream} from "./Stream";
+export {Stream, LazyStream, IteratableConsumer, FlatMapStreamDataSource, IStream} from "./Stream";
export {
ArrayStreamDataSource,
MappedStreamDataSource,
FilteredStreamDatasource,
- FlatMapStreamDataSource,
MultiStreamDatasource,
SequenceDataSource,
QueryFormStringCollector,
@@ -44,5 +43,10 @@ export {TagBuilder} from "./TagBuilder";
export {Message, Broker, BroadcastChannelBroker, Crypto, NoCrypto, Hash} from "./Messaging";
export {JSONCrypto, ExpiringCrypto} from "./CryptoExtensions";
-
+export {assign, assignIf, simpleShallowMerge, shallowMerge} from "./AssocArray"
+export {Config} from "./Config";
+export {ConfigDef} from "./Config";
+export {CONFIG_ANY} from "./Config";
+export {CONFIG_VALUE} from "./Config";
+export {Es2019Array, _Es2019Array} from "./Es2019Array";
diff --git a/api/src/client/typescript/mona_dish/index_core.ts b/api/src/client/typescript/mona_dish/index_core.ts
index f1a911664..aaf734e64 100644
--- a/api/src/client/typescript/mona_dish/index_core.ts
+++ b/api/src/client/typescript/mona_dish/index_core.ts
@@ -18,24 +18,15 @@
*/
export {DomQuery, ElementAttribute, DomQueryCollector, DQ, DQ$} from "./DomQuery";
export {Lang} from "./Lang";
-export {Config, CONFIG_VALUE, CONFIG_ANY, ConfigDef, Monad, IValueHolder, IFunctor, IMonad, IIdentity, Optional, ValueEmbedder} from "./Monad";
+export {Monad, IValueHolder, IFunctor, IMonad, IIdentity, Optional, ValueEmbedder} from "./Monad";
export {XMLQuery, XQ} from "./XmlQuery";
-export {Stream, LazyStream, IteratableConsumer, IStream} from "./Stream";
-export {
- ArrayStreamDataSource,
- MappedStreamDataSource,
- FilteredStreamDatasource,
- FlatMapStreamDataSource,
- SequenceDataSource,
- QueryFormStringCollector,
- IStreamDataSource,
- ICollector,
- ArrayCollector,
- AssocArrayCollector,
- FormDataCollector,
- QueryFormDataCollector
-} from "./SourcesCollectors";
-
+export {assign, assignIf, append, simpleShallowMerge, shallowMerge} from "./AssocArray"
+export {Config} from "./Config";
+export {ConfigDef} from "./Config";
+export {CONFIG_ANY} from "./Config";
+export {CONFIG_VALUE} from "./Config";
+export * as Assoc from "./AssocArray";
+export {Es2019Array, _Es2019Array} from "./Es2019Array";