jsf.js_next_gen 4.0.0-RC.34 → 4.0.0-RC.36
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.nyc_output/09ca9ebc-2305-4357-8db9-48ddfc7dfde2.json +1 -0
- package/.nyc_output/7c496a14-166e-4aa5-85b9-47b22b055ad8.json +1 -0
- package/.nyc_output/processinfo/09ca9ebc-2305-4357-8db9-48ddfc7dfde2.json +1 -0
- package/.nyc_output/processinfo/7c496a14-166e-4aa5-85b9-47b22b055ad8.json +1 -0
- package/.nyc_output/processinfo/index.json +1 -1
- package/dist/docs/assets/search.js +1 -1
- package/dist/docs/functions/faces.ajax.addOnError.html +6 -4
- package/dist/docs/functions/faces.ajax.addOnEvent.html +2 -0
- package/dist/docs/functions/faces.ajax.request.html +7 -7
- package/dist/docs/functions/faces.ajax.response.html +0 -1
- package/dist/docs/functions/faces.push.close.html +3 -1
- package/dist/docs/functions/faces.push.open.html +3 -1
- package/dist/docs/functions/faces.util.chain.html +3 -3
- package/dist/docs/functions/myfaces.ab.html +8 -3
- package/dist/docs/functions/myfaces.onOnDomReady.html +72 -0
- package/dist/docs/modules/myfaces.html +3 -1
- package/dist/docs/variables/myfaces.oam.html +2 -1
- package/dist/window/faces-development.js +336 -750
- package/dist/window/faces-development.js.br +0 -0
- package/dist/window/faces-development.js.gz +0 -0
- package/dist/window/faces-development.js.map +1 -1
- package/dist/window/faces.js +1 -1
- package/dist/window/faces.js.br +0 -0
- package/dist/window/faces.js.gz +0 -0
- package/dist/window/faces.js.map +1 -1
- package/dist/window/jsf-development.js +336 -750
- package/dist/window/jsf-development.js.br +0 -0
- package/dist/window/jsf-development.js.gz +0 -0
- package/dist/window/jsf-development.js.map +1 -1
- package/dist/window/jsf.js +1 -1
- package/dist/window/jsf.js.br +0 -0
- package/dist/window/jsf.js.gz +0 -0
- package/dist/window/jsf.js.map +1 -1
- package/package.json +8 -7
- package/src/main/typescript/@types/definitions/index.d.ts +5 -5
- package/src/main/typescript/api/_api.ts +55 -20
- package/src/main/typescript/impl/AjaxImpl.ts +9 -6
- package/src/main/typescript/impl/util/FileUtils.ts +25 -14
- package/src/main/typescript/impl/xhrCore/XhrFormData.ts +15 -15
- package/src/main/typescript/test/frameworkBase/_ext/monadish/DomQueryTest.spec.ts +3 -3
- package/src/main/typescript/test/frameworkBase/_ext/shared/StandardInits.ts +35 -0
- package/src/main/typescript/test/impl/ImplTest_23.spec.ts +0 -4
- package/src/main/typescript/test/xhrCore/TobagoFileUploadTest.spec.ts +109 -0
- package/target/api/_api.js +53 -18
- package/target/api/_api.js.map +1 -1
- package/target/impl/AjaxImpl.js +7 -3
- package/target/impl/AjaxImpl.js.map +1 -1
- package/target/impl/util/FileUtils.js +24 -15
- package/target/impl/util/FileUtils.js.map +1 -1
- package/target/impl/util/XhrQueueController.js +82 -0
- package/target/impl/util/XhrQueueController.js.map +1 -0
- package/target/impl/xhrCore/XhrFormData.js +10 -8
- package/target/impl/xhrCore/XhrFormData.js.map +1 -1
- package/target/impl/xhrCore/XhrQueHandler.js +11 -0
- package/target/impl/xhrCore/XhrQueHandler.js.map +1 -0
- package/target/impl/xhrCore/XhrQueueController.js +62 -0
- package/target/impl/xhrCore/XhrQueueController.js.map +1 -0
- package/target/test/frameworkBase/_ext/monadish/DomQueryTest.spec.js.map +1 -1
- package/target/test/frameworkBase/_ext/shared/StandardInits.js +36 -0
- package/target/test/frameworkBase/_ext/shared/StandardInits.js.map +1 -1
- package/target/test/impl/ImplTest_23.spec.js.map +1 -1
- package/target/test/xhrCore/TobagoFileUploadTest.spec.js +128 -0
- package/target/test/xhrCore/TobagoFileUploadTest.spec.js.map +1 -0
- package/.nyc_output/2303b649-59d9-485c-9228-73b0ec8787a7.json +0 -1
- package/.nyc_output/756f2f03-c85b-4cdb-9b92-5799eb45ef1e.json +0 -1
- package/.nyc_output/processinfo/2303b649-59d9-485c-9228-73b0ec8787a7.json +0 -1
- package/.nyc_output/processinfo/756f2f03-c85b-4cdb-9b92-5799eb45ef1e.json +0 -1
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jsf.js_next_gen",
|
|
3
|
-
"version": "4.0.0-RC.
|
|
3
|
+
"version": "4.0.0-RC.36",
|
|
4
4
|
"description": "A next generation typescript reimplementation of jsf.js",
|
|
5
5
|
"main": "dist/window/faces.js",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"doc": "npx typedoc --tsconfig src/main/typescript/tsconfig-typedoc.json",
|
|
8
|
-
"test": "env TS_NODE_PROJECT=./src/main/typescript/tsconfig.json mocha",
|
|
9
|
-
"coverage": "env TS_NODE_PROJECT=./src/
|
|
8
|
+
"test": "cross-env TS_NODE_PROJECT=./src/main/typescript/tsconfig.json mocha",
|
|
9
|
+
"coverage": "cross-env TS_NODE_PROJECT=./src/main/typescript/tsconfig.json nyc --reporter=html --reporter=text mocha",
|
|
10
10
|
"build-production": "webpack --mode production --env FILE_NAME=jsf && ts-node ./remap.ts --production",
|
|
11
11
|
"build-development": "webpack --mode development --env FILE_NAME=jsf-development && ts-node ./remap.ts --development",
|
|
12
12
|
"build": "npm run doc && npm run build-development && npm run build-production",
|
|
@@ -23,17 +23,18 @@
|
|
|
23
23
|
"@istanbuljs/nyc-config-typescript": "^1.0.2",
|
|
24
24
|
"@types/chai": "^4.3.4",
|
|
25
25
|
"@types/mocha": "^10.0.1",
|
|
26
|
-
"@types/node": "^18.
|
|
26
|
+
"@types/node": "^18.14.2",
|
|
27
27
|
"@types/sinon": "^10.0.13",
|
|
28
28
|
"babel-plugin-syntax-dynamic-import": "^6.18.0",
|
|
29
29
|
"chai": "^4.3.7",
|
|
30
30
|
"compression-webpack-plugin": "^10.0.0",
|
|
31
|
+
"cross-env": "^7.0.3",
|
|
31
32
|
"html-webpack-plugin": "^5.5.0",
|
|
32
33
|
"http-server": "^14.1.1",
|
|
33
34
|
"jsdom": "^21.1.0",
|
|
34
35
|
"jsdom-global": "^3.0.2",
|
|
35
36
|
"mocha": "^10.2.0",
|
|
36
|
-
"npm-check-updates": "^16.
|
|
37
|
+
"npm-check-updates": "^16.7.10",
|
|
37
38
|
"nyc": "^15.1.0",
|
|
38
39
|
"replace-in-file": "^6.3.5",
|
|
39
40
|
"rxjs": "^7.8.0",
|
|
@@ -41,13 +42,13 @@
|
|
|
41
42
|
"terser-webpack-plugin": "^5.3.6",
|
|
42
43
|
"ts-loader": "^9.4.2",
|
|
43
44
|
"ts-node": "^10.9.1",
|
|
44
|
-
"typedoc": "^0.23.
|
|
45
|
+
"typedoc": "^0.23.26",
|
|
45
46
|
"typescript": "^4.9.5",
|
|
46
47
|
"webpack": "^5.75.0",
|
|
47
48
|
"webpack-cli": "^5.0.1",
|
|
48
49
|
"webpack-dev-server": "^4.11.1"
|
|
49
50
|
},
|
|
50
51
|
"dependencies": {
|
|
51
|
-
"mona-dish": "^0.
|
|
52
|
+
"mona-dish": "^0.26.0"
|
|
52
53
|
}
|
|
53
54
|
}
|
|
@@ -50,10 +50,10 @@ declare global {
|
|
|
50
50
|
* <li> errorData.status : the error status message</li>
|
|
51
51
|
* <li> errorData.serverErrorName : the server error name in case of a server error</li>
|
|
52
52
|
* <li> errorData.serverErrorMessage : the server error message in case of a server error</li>
|
|
53
|
-
* <li> errorData.source : the issuing source element which triggered the
|
|
54
|
-
* <li> eventData.responseCode: the response code (aka http
|
|
55
|
-
* <li> eventData.responseText: the
|
|
56
|
-
* <li> eventData.responseXML: the
|
|
53
|
+
* <li> errorData.source : the issuing source element which triggered the request </li>
|
|
54
|
+
* <li> eventData.responseCode: the response code (aka http request response code, 401 etc...) </li>
|
|
55
|
+
* <li> eventData.responseText: the response text </li>
|
|
56
|
+
* <li> eventData.responseXML: the response xml </li>
|
|
57
57
|
* </ul>
|
|
58
58
|
*/
|
|
59
59
|
interface IErrorData {
|
|
@@ -78,7 +78,7 @@ declare global {
|
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
interface Ajax {
|
|
81
|
-
request(element: Element, event?: Event, options?:
|
|
81
|
+
request(element: Element, event?: Event, options?: Options): void;
|
|
82
82
|
response(request: XMLHttpRequest, context?: Context): void;
|
|
83
83
|
}
|
|
84
84
|
|
|
@@ -99,7 +99,7 @@ export module faces {
|
|
|
99
99
|
return Implementation.getClientWindow(rootNode);
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
-
//private helper functions
|
|
102
|
+
// private helper functions
|
|
103
103
|
function getSeparatorChar(): string {
|
|
104
104
|
const sep = '#{facesContext.namingContainerSeparatorChar}';
|
|
105
105
|
//We now enable standalone mode, the separator char was not mapped we make a fallback to 2.3 behavior
|
|
@@ -117,19 +117,19 @@ export module faces {
|
|
|
117
117
|
/**
|
|
118
118
|
* this function has to send the ajax requests
|
|
119
119
|
*
|
|
120
|
-
* following
|
|
120
|
+
* following request conditions must be met:
|
|
121
121
|
* <ul>
|
|
122
|
-
* <li> the
|
|
123
|
-
* <li> the
|
|
124
|
-
* <li> the
|
|
125
|
-
* <li> all requests must be queued with a client side
|
|
122
|
+
* <li> the request must be sent asynchronously! </li>
|
|
123
|
+
* <li> the request must be a POST!!! request </li>
|
|
124
|
+
* <li> the request url must be the form action attribute </li>
|
|
125
|
+
* <li> all requests must be queued with a client side request queue to ensure the request ordering!</li>
|
|
126
126
|
* </ul>
|
|
127
127
|
*
|
|
128
128
|
* @param {String|Node} element: any dom element no matter being it html or jsf, from which the event is emitted
|
|
129
129
|
* @param {EVENT} event: any javascript event supported by that object
|
|
130
130
|
* @param {Map} options : map of options being pushed into the ajax cycle
|
|
131
131
|
*/
|
|
132
|
-
export function request(element: Element, event?: Event, options?:
|
|
132
|
+
export function request(element: Element, event?: Event, options?: Options): void {
|
|
133
133
|
Implementation.request(element, event, options)
|
|
134
134
|
}
|
|
135
135
|
|
|
@@ -138,7 +138,6 @@ export module faces {
|
|
|
138
138
|
* @param request the request object having triggered this response
|
|
139
139
|
* @param context the request context
|
|
140
140
|
*
|
|
141
|
-
* TODO add info on what can be in the context
|
|
142
141
|
*/
|
|
143
142
|
export function response(request: XMLHttpRequest, context?: Context): void {
|
|
144
143
|
Implementation.response(request, context);
|
|
@@ -153,13 +152,13 @@ export module faces {
|
|
|
153
152
|
* <li> errorData.status : the error status message</li>
|
|
154
153
|
* <li> errorData.serverErrorName : the server error name in case of a server error</li>
|
|
155
154
|
* <li> errorData.serverErrorMessage : the server error message in case of a server error</li>
|
|
156
|
-
* <li> errorData.source : the issuing source element which triggered the
|
|
157
|
-
* <li> eventData.responseCode: the response code (aka http
|
|
158
|
-
* <li> eventData.responseText: the
|
|
159
|
-
* <li> eventData.responseXML: the
|
|
155
|
+
* <li> errorData.source : the issuing source element which triggered the request </li>
|
|
156
|
+
* <li> eventData.responseCode: the response code (aka http request response code, 401 etc...) </li>
|
|
157
|
+
* <li> eventData.responseText: the request response text </li>
|
|
158
|
+
* <li> eventData.responseXML: the request response xml </li>
|
|
160
159
|
* </ul>
|
|
161
160
|
*
|
|
162
|
-
* @param
|
|
161
|
+
* @param errorFunc error handler must be of the format <i>function errorListener(<errorData>)</i>
|
|
163
162
|
*/
|
|
164
163
|
export function addOnError(errorFunc: (data: ErrorData) => void): void {
|
|
165
164
|
Implementation.addOnError(<any>errorFunc);
|
|
@@ -169,7 +168,7 @@ export module faces {
|
|
|
169
168
|
* Adds a global event listener to the ajax event queue. The event listener must be a function
|
|
170
169
|
* of following format: <i>function eventListener(<eventData>)</i>
|
|
171
170
|
*
|
|
172
|
-
* @param
|
|
171
|
+
* @param eventFunc event must be of the format <i>function eventListener(<eventData>)</i>
|
|
173
172
|
*/
|
|
174
173
|
export function addOnEvent(eventFunc: (data: EventData) => void): void {
|
|
175
174
|
Implementation.addOnEvent(<any>eventFunc);
|
|
@@ -184,12 +183,12 @@ export module faces {
|
|
|
184
183
|
* if any of the code returns false, the execution
|
|
185
184
|
* is terminated prematurely skipping the rest of the code!
|
|
186
185
|
*
|
|
187
|
-
* @param {
|
|
186
|
+
* @param {HTMLElement | String} source, the callee object
|
|
188
187
|
* @param {Event} event, the event object of the callee event triggering this function
|
|
189
188
|
* @param funcs ... arbitrary array of functions or strings
|
|
190
189
|
* @returns true if the chain has succeeded false otherwise
|
|
191
190
|
*/
|
|
192
|
-
export function chain(source, event, ...funcs: Array<Function | string>): boolean {
|
|
191
|
+
export function chain(source: HTMLElement | string, event: Event | null, ...funcs: Array<Function | string>): boolean {
|
|
193
192
|
return Implementation.chain(source, event, ...(funcs as EvalFuncs));
|
|
194
193
|
}
|
|
195
194
|
}
|
|
@@ -220,7 +219,7 @@ export module faces {
|
|
|
220
219
|
|
|
221
220
|
/**
|
|
222
221
|
* Open the web socket on the given channel.
|
|
223
|
-
* @param
|
|
222
|
+
* @param socketClientId The name of the web socket channel.
|
|
224
223
|
* @throws Error is thrown, if the channel is unknown.
|
|
225
224
|
*/
|
|
226
225
|
export function open(socketClientId: string): void {
|
|
@@ -229,7 +228,7 @@ export module faces {
|
|
|
229
228
|
|
|
230
229
|
/**
|
|
231
230
|
* Close the web socket on the given channel.
|
|
232
|
-
* @param
|
|
231
|
+
* @param socketClientId The id of the web socket client.
|
|
233
232
|
* @throws Error is thrown, if the channel is unknown.
|
|
234
233
|
*/
|
|
235
234
|
export function close(socketClientId: string): void {
|
|
@@ -249,8 +248,8 @@ export module myfaces {
|
|
|
249
248
|
* @param event the event
|
|
250
249
|
* @param eventName event name for java.jakarta.faces.behavior.evemnt
|
|
251
250
|
* @param execute execute list as passed down in faces.ajax.request
|
|
252
|
-
* @param render
|
|
253
|
-
* @param options
|
|
251
|
+
* @param render the render list as string
|
|
252
|
+
* @param options the options which need to be mered in
|
|
254
253
|
*/
|
|
255
254
|
export function ab(source: Element, event: Event, eventName: string, execute: string, render: string, options: Options = {}): void {
|
|
256
255
|
if (eventName) {
|
|
@@ -267,6 +266,42 @@ export module myfaces {
|
|
|
267
266
|
(window?.faces ?? window.jsf).ajax.request(source, event, options);
|
|
268
267
|
}
|
|
269
268
|
|
|
269
|
+
|
|
270
|
+
const onReadyChain: Array<() => void> = [];
|
|
271
|
+
let readyStateListener = null;
|
|
272
|
+
// noinspection JSUnusedGlobalSymbols
|
|
273
|
+
/**
|
|
274
|
+
* Helper function in the myfaces namespace to handle document ready properly for the load case
|
|
275
|
+
* the ajax case, does not need proper treatment, since it is deferred anyway.
|
|
276
|
+
* Used by command script as helper function!
|
|
277
|
+
*
|
|
278
|
+
* @param executionFunc the function to be executed upon ready
|
|
279
|
+
*/
|
|
280
|
+
export function onOnDomReady(executionFunc: () => void) {
|
|
281
|
+
if(document.readyState !== "complete") {
|
|
282
|
+
onReadyChain.push(executionFunc);
|
|
283
|
+
if(!readyStateListener) {
|
|
284
|
+
readyStateListener = () => {
|
|
285
|
+
window.removeEventListener("DOMContentLoaded", readyStateListener);
|
|
286
|
+
readyStateListener = null;
|
|
287
|
+
try {
|
|
288
|
+
onReadyChain.forEach(func => func());
|
|
289
|
+
} finally {
|
|
290
|
+
//done we clear now the ready chain
|
|
291
|
+
onReadyChain.length = 0;
|
|
292
|
+
}
|
|
293
|
+
};
|
|
294
|
+
window.addEventListener("DOMContentLoaded", readyStateListener);
|
|
295
|
+
}
|
|
296
|
+
} else {
|
|
297
|
+
if(readyStateListener) {
|
|
298
|
+
readyStateListener();
|
|
299
|
+
}
|
|
300
|
+
executionFunc();
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
}
|
|
304
|
+
|
|
270
305
|
/**
|
|
271
306
|
* legacy oam functions
|
|
272
307
|
*/
|
|
@@ -20,7 +20,6 @@ import {XhrRequest} from "./xhrCore/XhrRequest";
|
|
|
20
20
|
import {AsynchronousQueue} from "./util/AsyncQueue";
|
|
21
21
|
import {AssocArrayCollector, Config, DQ, Lang, LazyStream, Optional, Stream} from "mona-dish";
|
|
22
22
|
import {Assertions} from "./util/Assertions";
|
|
23
|
-
import {XhrFormData} from "./xhrCore/XhrFormData";
|
|
24
23
|
import {ExtConfig, ExtDomQuery} from "./util/ExtDomQuery";
|
|
25
24
|
import {ErrorData} from "./xhrCore/ErrorData";
|
|
26
25
|
import {EventData} from "./xhrCore/EventData";
|
|
@@ -113,11 +112,15 @@ export module Implementation {
|
|
|
113
112
|
a) Monad like structures for querying because this keeps the code denser and adds abstractions
|
|
114
113
|
that always was the strong point of jquery and it still is better in this regard than what ecmascript provides
|
|
115
114
|
|
|
116
|
-
b) Streams and
|
|
115
|
+
b) Streams and lazy streams like java has, a pull stream construct, ecmascript does not have anything like it.
|
|
116
|
+
(it has array filters and maps, but ES2015 does not support flatMap)
|
|
117
117
|
Another option would have been rxjs but that would have introduced a code dependency and probably more code. We might
|
|
118
|
-
move to RXJS if the need arises however. But for now I would rather stick with my small self grown library which works
|
|
118
|
+
move to RXJS if the need arises, however. But for now I would rather stick with my small self grown library which works
|
|
119
119
|
quite well and where I can patch quickly (I have used it in several industrial projects, so it works well
|
|
120
120
|
and is heavily fortified by unit tests (140 testcases as time of writing this))
|
|
121
|
+
The long term plan is to eliminate the Stream usage as soon as we can move up to ES2019 (adding the missing
|
|
122
|
+
functions as shims, is a no go, because we are a library, and absolutey do not Shim anything which can leak
|
|
123
|
+
into the global namespace!)
|
|
121
124
|
|
|
122
125
|
c) A neutral json like configuration which allows assignments of arbitrary values with reduce code which then can be
|
|
123
126
|
transformed into different data representations
|
|
@@ -189,7 +192,7 @@ export module Implementation {
|
|
|
189
192
|
/**
|
|
190
193
|
* @return the project stage also emitted by the server:
|
|
191
194
|
* it cannot be cached and must be delivered over the server
|
|
192
|
-
* The value for it comes from the
|
|
195
|
+
* The value for it comes from the request parameter of the faces.js script called "stage".
|
|
193
196
|
*/
|
|
194
197
|
export function getProjectStage(): string | null {
|
|
195
198
|
return resolveGlobalConfig()?.projectStage ??
|
|
@@ -215,7 +218,7 @@ export module Implementation {
|
|
|
215
218
|
* @param event
|
|
216
219
|
* @param funcs
|
|
217
220
|
*/
|
|
218
|
-
export function chain(source:
|
|
221
|
+
export function chain(source: HTMLElement | string, event: Event | null, ...funcs: EvalFuncs): boolean {
|
|
219
222
|
// we can use our lazy stream each functionality to run our chain here..
|
|
220
223
|
// by passing a boolean as return value into the onElem call
|
|
221
224
|
// we can stop early at the first false, just like the spec requests
|
|
@@ -500,7 +503,7 @@ export module Implementation {
|
|
|
500
503
|
* or non-existent. If they exist all of them must be the same
|
|
501
504
|
*/
|
|
502
505
|
|
|
503
|
-
let formWindowId: Optional<string> = searchRoot.stream.map
|
|
506
|
+
let formWindowId: Optional<string> = searchRoot.stream.map(getValue).reduce(differenceCheck, INIT);
|
|
504
507
|
|
|
505
508
|
|
|
506
509
|
//if the resulting window id is set on altered then we have an unresolvable problem
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {ArrayCollector, Config, DomQuery, DQ, LazyStream, Stream} from "mona-dish";
|
|
2
|
-
import {
|
|
2
|
+
import {ExtDomQuery} from "./ExtDomQuery";
|
|
3
3
|
import {$faces, EMPTY_STR} from "../core/Const";
|
|
4
4
|
|
|
5
5
|
/*
|
|
@@ -59,35 +59,46 @@ export function decodeEncodedValues(encoded: string): Stream<string[]> {
|
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
|
|
62
|
+
/**
|
|
63
|
+
* gets all the input files and their corresponding file objects
|
|
64
|
+
* @param dataSource
|
|
65
|
+
*/
|
|
62
66
|
export function resolveFiles(dataSource: DQ): Stream<[string, File]> {
|
|
67
|
+
|
|
68
|
+
const expandFilesArr = ([key, files]) => Stream.of(...files).map(file => [key, file]);
|
|
69
|
+
const remapFileInput = fileInput => [fileInput.name.value || fileInput.id.value, fileInput.filesFromElem(0)];
|
|
63
70
|
return dataSource
|
|
64
71
|
.querySelectorAllDeep("input[type='file']")
|
|
65
72
|
.stream
|
|
66
|
-
.map(
|
|
67
|
-
.flatMap(
|
|
68
|
-
return Stream.of(...files).map(file => [key, file])
|
|
69
|
-
});
|
|
73
|
+
.map(remapFileInput)
|
|
74
|
+
.flatMap(expandFilesArr);
|
|
70
75
|
}
|
|
71
76
|
|
|
72
77
|
|
|
73
|
-
export function
|
|
74
|
-
return keyVal.length < 3 ? [keyVal?.[0] ?? [], keyVal?.[1] ?? []] : keyVal;
|
|
78
|
+
export function fixEmmptyParameters(keyVal: any[]): [string, any] {
|
|
79
|
+
return (keyVal.length < 3 ? [keyVal?.[0] ?? [], keyVal?.[1] ?? []] : keyVal) as [string, any];
|
|
75
80
|
}
|
|
76
81
|
|
|
77
82
|
/**
|
|
78
|
-
*
|
|
79
|
-
* as stream
|
|
83
|
+
* returns the decoded viewState from parentItem
|
|
80
84
|
* @param parentItem
|
|
81
85
|
*/
|
|
82
|
-
|
|
83
|
-
//encoded String
|
|
86
|
+
function resolveViewState(parentItem: DomQuery): Stream<string[] | [string, File]> {
|
|
84
87
|
const viewStateStr = $faces().getViewState(parentItem.getAsElem(0).value);
|
|
85
88
|
|
|
86
89
|
// we now need to decode it and then merge it into the target buf
|
|
87
90
|
// which hosts already our overrides (aka do not override what is already there(
|
|
88
91
|
// after that we need to deal with form elements on a separate level
|
|
89
|
-
|
|
92
|
+
return decodeEncodedValues(viewStateStr);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* gets all the inputs under the form parentItem
|
|
97
|
+
* as stream
|
|
98
|
+
* @param parentItem
|
|
99
|
+
*/
|
|
100
|
+
export function getFormInputsAsStream(parentItem: DomQuery): Stream<string[] | [string, File]> {
|
|
101
|
+
const standardInputs = resolveViewState(parentItem);
|
|
90
102
|
const fileInputs = resolveFiles(parentItem);
|
|
91
|
-
|
|
92
|
-
return allInputs;
|
|
103
|
+
return standardInputs.concat(fileInputs as any)
|
|
93
104
|
}
|
|
@@ -13,14 +13,12 @@
|
|
|
13
13
|
* See the License for the specific language governing permissions and
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
*/
|
|
16
|
-
import {
|
|
17
|
-
import {$
|
|
18
|
-
|
|
16
|
+
import {Config, DQ, FormDataCollector, Stream} from "mona-dish";
|
|
17
|
+
import {$nsp, EMPTY_STR, IDENT_NONE, P_VIEWSTATE} from "../core/Const";
|
|
18
|
+
|
|
19
19
|
import {
|
|
20
|
-
decodeEncodedValues,
|
|
21
20
|
encodeFormData,
|
|
22
|
-
|
|
23
|
-
fixKeyWithoutVal, getFormInputsAsStream
|
|
21
|
+
fixEmmptyParameters, getFormInputsAsStream
|
|
24
22
|
} from "../util/FileUtils";
|
|
25
23
|
|
|
26
24
|
|
|
@@ -28,7 +26,6 @@ type ParamsMapper<V, K> = (key: V, item: K) => [V, K];
|
|
|
28
26
|
const defaultParamsMapper: ParamsMapper<string, any> = (key, item) => [key, item];
|
|
29
27
|
|
|
30
28
|
|
|
31
|
-
|
|
32
29
|
/**
|
|
33
30
|
* A unified form data class
|
|
34
31
|
* which builds upon our configuration.
|
|
@@ -80,20 +77,20 @@ export class XhrFormData extends Config {
|
|
|
80
77
|
toFormData(): FormData {
|
|
81
78
|
/*
|
|
82
79
|
* expands key: [item1, item2]
|
|
83
|
-
* to: [{key: item1}, {key, item2}]
|
|
80
|
+
* to: [{key: key, value: item1}, {key: key, value: item2}]
|
|
84
81
|
*/
|
|
85
82
|
let expandAssocArray = ([key, item]) =>
|
|
86
|
-
Stream.of(...(item as Array<any>)).map(
|
|
87
|
-
return {key,
|
|
83
|
+
Stream.of(...(item as Array<any>)).map(value => {
|
|
84
|
+
return {key, value};
|
|
88
85
|
});
|
|
89
86
|
|
|
90
87
|
/*
|
|
91
88
|
* remaps the incoming {key, value} tuples
|
|
92
89
|
* to naming container prefixed keys and values
|
|
93
90
|
*/
|
|
94
|
-
let remapForNamingContainer = ({key,
|
|
91
|
+
let remapForNamingContainer = ({key, value}) => {
|
|
95
92
|
key = this.remapKeyForNamingContainer(key);
|
|
96
|
-
return {key,
|
|
93
|
+
return {key, value}
|
|
97
94
|
};
|
|
98
95
|
|
|
99
96
|
/*
|
|
@@ -149,10 +146,13 @@ export class XhrFormData extends Config {
|
|
|
149
146
|
private encodeSubmittableFields(parentItem: DQ, partialIds ?: string[]) {
|
|
150
147
|
|
|
151
148
|
const formInputs = getFormInputsAsStream(parentItem);
|
|
149
|
+
const mergeIntoThis = ([key, value]) => this.append(key).value = value;
|
|
150
|
+
const namingContainerRemap = ([key, value]) => this.paramsMapper(key as string, value);
|
|
151
|
+
|
|
152
152
|
formInputs
|
|
153
|
-
.map(
|
|
154
|
-
.map(
|
|
155
|
-
.each(
|
|
153
|
+
.map(fixEmmptyParameters)
|
|
154
|
+
.map(namingContainerRemap)
|
|
155
|
+
.each(mergeIntoThis);
|
|
156
156
|
}
|
|
157
157
|
|
|
158
158
|
private remapKeyForNamingContainer(key: string): string {
|
|
@@ -376,7 +376,7 @@ describe('DOMQuery tests', function () {
|
|
|
376
376
|
expect(length2 == 8).to.be.true;
|
|
377
377
|
|
|
378
378
|
let count = DomQuery.byId("embed1").elements
|
|
379
|
-
.stream.map
|
|
379
|
+
.stream.map(item => item.disabled ? 1 : 0)
|
|
380
380
|
.reduce((val1, val2) => val1 + val2, 0);
|
|
381
381
|
expect(count.value).to.eq(1);
|
|
382
382
|
|
|
@@ -385,12 +385,12 @@ describe('DOMQuery tests', function () {
|
|
|
385
385
|
.each(item => item.disabled = false);
|
|
386
386
|
|
|
387
387
|
count = DomQuery.byId("embed1").elements
|
|
388
|
-
.stream.map
|
|
388
|
+
.stream.map(item => item.disabled ? 1 : 0)
|
|
389
389
|
.reduce((val1, val2) => val1 + val2, 0);
|
|
390
390
|
expect(count.value).to.eq(0);
|
|
391
391
|
|
|
392
392
|
count = DomQuery.byId("embed1").elements
|
|
393
|
-
.stream.map
|
|
393
|
+
.stream.map(item => item.attr("checked").isPresent() ? 1 : 0)
|
|
394
394
|
.reduce((val1, val2) => val1 + val2, 0);
|
|
395
395
|
expect(count.value).to.eq(1);
|
|
396
396
|
|
|
@@ -111,6 +111,38 @@ export module StandardInits {
|
|
|
111
111
|
</html>`;
|
|
112
112
|
|
|
113
113
|
|
|
114
|
+
/**
|
|
115
|
+
* a page based on Tobago for a file upload
|
|
116
|
+
*/
|
|
117
|
+
const HTML_TOBAGO_FILE_FORM = `<!DOCTYPE html>
|
|
118
|
+
<html lang='de'>
|
|
119
|
+
<head>
|
|
120
|
+
<meta charset='UTF-8'>
|
|
121
|
+
<meta name='viewport' content='width=device-width, initial-scale=1.0'>
|
|
122
|
+
<title>Test</title>
|
|
123
|
+
</head>
|
|
124
|
+
<body>
|
|
125
|
+
<tobago-page locale='de' class='container-fluid' id='page' focus-on-error='true' wait-overlay-delay-full='1000' wait-overlay-delay-ajax='1000'>
|
|
126
|
+
<form action='/content/100-upload/File_Upload.xhtml' id='page::form' method='post' enctype='multipart/form-data' accept-charset='UTF-8' data-tobago-context-path=''>
|
|
127
|
+
<input type='hidden' name='javax.faces.source' id='javax.faces.source' disabled='disabled'>
|
|
128
|
+
<tobago-focus id='page::lastFocusId'>
|
|
129
|
+
<input type='hidden' name='page::lastFocusId' id='page::lastFocusId::field'>
|
|
130
|
+
</tobago-focus>
|
|
131
|
+
<input type='hidden' name='org.apache.myfaces.tobago.webapp.Secret' id='org.apache.myfaces.tobago.webapp.Secret' value='secretValue'>
|
|
132
|
+
<tobago-file id='page:fileAjax' class='tobago-auto-spacing'>
|
|
133
|
+
<div class='input-group'>
|
|
134
|
+
<input type='file' tabindex='-1' id='page:fileAjax::field' class='form-control' name='page:fileAjax'>
|
|
135
|
+
<tobago-behavior event='change' client-id='page:fileAjax' field-id='page:fileAjax::field' execute='page:fileAjax'></tobago-behavior>
|
|
136
|
+
<label class='input-group-text' for='page:fileAjax::field'><span><i class='bi-folder2-open'></i></span></label>
|
|
137
|
+
</div>
|
|
138
|
+
</tobago-file>
|
|
139
|
+
<div class='tobago-page-menuStore'>
|
|
140
|
+
</div>
|
|
141
|
+
<span id='page::jsf-state-container'><input type='hidden' name='javax.faces.ViewState' id='j_id__v_0:javax.faces.ViewState:1' value='viewStateValue' autocomplete='off'><input type='hidden' name='javax.faces.RenderKitId' value='tobago'><input type='hidden' id='j_id__v_0:javax.faces.ClientWindow:1' name='javax.faces.ClientWindow' value='clientWindowValue'></span>
|
|
142
|
+
</form>
|
|
143
|
+
</tobago-page>
|
|
144
|
+
</body>
|
|
145
|
+
</html>`;
|
|
114
146
|
|
|
115
147
|
/**
|
|
116
148
|
* a page simulating basically a simple faces form
|
|
@@ -350,6 +382,9 @@ export module StandardInits {
|
|
|
350
382
|
export function defaultFileForm(withJsf = true): Promise<() => void> {
|
|
351
383
|
return init(HTML_FILE_FORM_DEFAULT, withJsf);
|
|
352
384
|
}
|
|
385
|
+
export function tobagoFileForm(withJsf = true): Promise<() => void> {
|
|
386
|
+
return init(HTML_TOBAGO_FILE_FORM, withJsf);
|
|
387
|
+
}
|
|
353
388
|
export function defaultFileForm_23(withJsf = true): Promise<() => void> {
|
|
354
389
|
return init(HTML_FILE_FORM_DEFAULT.replace(/jakarta/gi, "javax"), withJsf, false);
|
|
355
390
|
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/*! Licensed to the Apache Software Foundation (ASF) under one or more
|
|
2
|
+
* contributor license agreements. See the NOTICE file distributed with
|
|
3
|
+
* this work for additional information regarding copyright ownership.
|
|
4
|
+
* The ASF licenses this file to you under the Apache License, Version 2.0
|
|
5
|
+
* (the "License"); you may not use this file except in compliance with
|
|
6
|
+
* the License. You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
import {describe, it} from "mocha";
|
|
17
|
+
import * as sinon from "sinon";
|
|
18
|
+
import {expect} from "chai";
|
|
19
|
+
import {StandardInits} from "../frameworkBase/_ext/shared/StandardInits";
|
|
20
|
+
import {DomQuery} from "mona-dish";
|
|
21
|
+
import {Implementation} from "../../impl/AjaxImpl";
|
|
22
|
+
import defaultFileForm = StandardInits.tobagoFileForm;
|
|
23
|
+
|
|
24
|
+
declare var faces: any;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* specialized tests testing the xhr core behavior when it hits the xmlHttpRequest object
|
|
28
|
+
*/
|
|
29
|
+
describe('Tests on the xhr core when it starts to call the request', function () {
|
|
30
|
+
beforeEach(async function () {
|
|
31
|
+
|
|
32
|
+
let waitForResult = defaultFileForm();
|
|
33
|
+
return waitForResult.then((close) => {
|
|
34
|
+
|
|
35
|
+
this.xhr = sinon.useFakeXMLHttpRequest();
|
|
36
|
+
this.requests = [];
|
|
37
|
+
|
|
38
|
+
this.respond = (response: string): XMLHttpRequest => {
|
|
39
|
+
let xhrReq = this.requests.shift();
|
|
40
|
+
xhrReq.responsetype = "text/xml";
|
|
41
|
+
xhrReq.respond(200, {'Content-Type': 'text/xml'}, response);
|
|
42
|
+
return xhrReq;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
this.xhr.onCreate = (xhr) => {
|
|
46
|
+
this.requests.push(xhr);
|
|
47
|
+
};
|
|
48
|
+
(<any>global).XMLHttpRequest = this.xhr;
|
|
49
|
+
window.XMLHttpRequest = this.xhr;
|
|
50
|
+
|
|
51
|
+
this.closeIt = () => {
|
|
52
|
+
(<any>global).XMLHttpRequest = window.XMLHttpRequest = this.xhr.restore();
|
|
53
|
+
Implementation.reset();
|
|
54
|
+
close();
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
});
|
|
59
|
+
afterEach(function () {
|
|
60
|
+
this.closeIt();
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('tobago file upload', function (done) {
|
|
64
|
+
let send = sinon.spy(XMLHttpRequest.prototype, "send");
|
|
65
|
+
|
|
66
|
+
const POST = "POST";
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
let fileUploadField = DomQuery.byId("page:fileAjax::field");
|
|
70
|
+
let actionElement = DomQuery.byId("page:fileAjax");
|
|
71
|
+
|
|
72
|
+
fileUploadField.addEventListener("change", (event: Event) => {
|
|
73
|
+
faces.ajax.request(
|
|
74
|
+
actionElement,
|
|
75
|
+
event,
|
|
76
|
+
{
|
|
77
|
+
"javax.faces.behavior.event": "change",
|
|
78
|
+
execute: 'page:fileAjax',
|
|
79
|
+
render: null
|
|
80
|
+
});
|
|
81
|
+
}).dispatchEvent(new Event("change"));
|
|
82
|
+
|
|
83
|
+
expect(this.requests.length).to.eq(1);
|
|
84
|
+
let request = this.requests[0];
|
|
85
|
+
expect(request.method).to.eq(POST);
|
|
86
|
+
expect(request.async).to.be.true;
|
|
87
|
+
expect(send.called).to.be.true;
|
|
88
|
+
expect(send.callCount).to.eq(1);
|
|
89
|
+
expect(request.requestBody instanceof FormData).to.be.true;
|
|
90
|
+
|
|
91
|
+
let formData: FormData = request.requestBody;
|
|
92
|
+
expect(formData.get("page::lastFocusId")).to.eq("");
|
|
93
|
+
expect(formData.get("org.apache.myfaces.tobago.webapp.Secret")).to.eq("secretValue");
|
|
94
|
+
expect(formData.get("javax.faces.ViewState")).to.eq("viewStateValue");
|
|
95
|
+
expect(formData.get("javax.faces.RenderKitId")).to.eq("tobago");
|
|
96
|
+
expect(formData.get("javax.faces.ClientWindow")).to.eq("clientWindowValue");
|
|
97
|
+
expect(formData.get("javax.faces.behavior.event")).to.eq("change");
|
|
98
|
+
expect(formData.get("jakarta.faces.partial.event")).to.eq("change");
|
|
99
|
+
expect(formData.get("jakarta.faces.source")).to.eq("page:fileAjax");
|
|
100
|
+
expect(formData.get("jakarta.faces.partial.ajax")).to.eq("true");
|
|
101
|
+
expect(formData.get("page::form")).to.eq("page::form");
|
|
102
|
+
expect(formData.get("jakarta.faces.partial.execute")).to.eq("page:fileAjax");
|
|
103
|
+
|
|
104
|
+
} finally {
|
|
105
|
+
send.restore();
|
|
106
|
+
}
|
|
107
|
+
done();
|
|
108
|
+
});
|
|
109
|
+
});
|