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/11/14 11:10:27 UTC
(myfaces) branch 4.0.x updated: https://issues.apache.org/jira/browse/MYFACES-4640:
This is an automated email from the ASF dual-hosted git repository.
werpu pushed a commit to branch 4.0.x
in repository https://gitbox.apache.org/repos/asf/myfaces.git
The following commit(s) were added to refs/heads/4.0.x by this push:
new f22b38f12 https://issues.apache.org/jira/browse/MYFACES-4640:
new f9b7d124c Merge pull request #643 from werpu/4.0.x
f22b38f12 is described below
commit f22b38f124b9958cf4e854f783328fbea336560c
Author: Werner Punz <we...@gmail.com>
AuthorDate: Tue Nov 14 11:49:52 2023 +0100
https://issues.apache.org/jira/browse/MYFACES-4640:
Fixes fro 4640 and new tests!
---
api/src/client/package-lock.json | 30 +--
api/src/client/package.json | 2 +-
.../typescript/faces/impl/xhrCore/XhrRequest.ts | 4 +-
.../faces/test/xhrCore/RequestParamsTest.spec.ts | 300 ++++++++++++++++++++-
api/src/client/typescript/mona_dish/AssocArray.ts | 114 ++++++--
5 files changed, 414 insertions(+), 36 deletions(-)
diff --git a/api/src/client/package-lock.json b/api/src/client/package-lock.json
index 6b61c2fa6..df1d17680 100644
--- a/api/src/client/package-lock.json
+++ b/api/src/client/package-lock.json
@@ -22,7 +22,7 @@
"html-webpack-plugin": "^5.5.1",
"jsdom": "^21.1.1",
"jsdom-global": "^3.0.2",
- "jsf.js_next_gen": "4.0.2-beta.5",
+ "jsf.js_next_gen": "4.0.2-beta.8",
"mocha": "^10.2.0",
"npm-check-updates": "^16.10.8",
"nyc": "^15.1.0",
@@ -5063,12 +5063,12 @@
}
},
"node_modules/jsf.js_next_gen": {
- "version": "4.0.2-beta.5",
- "resolved": "https://registry.npmjs.org/jsf.js_next_gen/-/jsf.js_next_gen-4.0.2-beta.5.tgz",
- "integrity": "sha512-O6whE+an6vm4sFyMXOVMp7HXk+W/ID0dtn5Vo7yI7NU6lL+17QMqggW7DLTcJhLAxwavr3AVoSfnaSuYUnu+kw==",
+ "version": "4.0.2-beta.8",
+ "resolved": "https://registry.npmjs.org/jsf.js_next_gen/-/jsf.js_next_gen-4.0.2-beta.8.tgz",
+ "integrity": "sha512-Jvcq52qJlV0M2+l94SRMW2HZ5ICax6fzppVR6HViqIx5u3PwA9YAS8OPsW5IahmPlMIGY600oFXBCrNiHChudw==",
"dev": true,
"dependencies": {
- "mona-dish": "0.28.10"
+ "mona-dish": "0.28.11"
}
},
"node_modules/json-buffer": {
@@ -5961,9 +5961,9 @@
}
},
"node_modules/mona-dish": {
- "version": "0.28.10",
- "resolved": "https://registry.npmjs.org/mona-dish/-/mona-dish-0.28.10.tgz",
- "integrity": "sha512-m7SZ8HsFZCCuadDWLaea6jekGeDZ8f2NdhsBkjVgChQoud4gIHCf7zf0lYrz5HDzLNL0zmI9wdzAoRavzidwuA==",
+ "version": "0.28.11",
+ "resolved": "https://registry.npmjs.org/mona-dish/-/mona-dish-0.28.11.tgz",
+ "integrity": "sha512-1sfIvsdgt+SdTgnvGS6VHYEtFZKaNUNZo13Oq+XaJjsxEk0gYPGO2XnHpIgog8ZSlNX/KcgF+w7fX3P3MixYsw==",
"dev": true
},
"node_modules/ms": {
@@ -13964,12 +13964,12 @@
"dev": true
},
"jsf.js_next_gen": {
- "version": "4.0.2-beta.5",
- "resolved": "https://registry.npmjs.org/jsf.js_next_gen/-/jsf.js_next_gen-4.0.2-beta.5.tgz",
- "integrity": "sha512-O6whE+an6vm4sFyMXOVMp7HXk+W/ID0dtn5Vo7yI7NU6lL+17QMqggW7DLTcJhLAxwavr3AVoSfnaSuYUnu+kw==",
+ "version": "4.0.2-beta.8",
+ "resolved": "https://registry.npmjs.org/jsf.js_next_gen/-/jsf.js_next_gen-4.0.2-beta.8.tgz",
+ "integrity": "sha512-Jvcq52qJlV0M2+l94SRMW2HZ5ICax6fzppVR6HViqIx5u3PwA9YAS8OPsW5IahmPlMIGY600oFXBCrNiHChudw==",
"dev": true,
"requires": {
- "mona-dish": "0.28.10"
+ "mona-dish": "0.28.11"
}
},
"json-buffer": {
@@ -14668,9 +14668,9 @@
}
},
"mona-dish": {
- "version": "0.28.10",
- "resolved": "https://registry.npmjs.org/mona-dish/-/mona-dish-0.28.10.tgz",
- "integrity": "sha512-m7SZ8HsFZCCuadDWLaea6jekGeDZ8f2NdhsBkjVgChQoud4gIHCf7zf0lYrz5HDzLNL0zmI9wdzAoRavzidwuA==",
+ "version": "0.28.11",
+ "resolved": "https://registry.npmjs.org/mona-dish/-/mona-dish-0.28.11.tgz",
+ "integrity": "sha512-1sfIvsdgt+SdTgnvGS6VHYEtFZKaNUNZo13Oq+XaJjsxEk0gYPGO2XnHpIgog8ZSlNX/KcgF+w7fX3P3MixYsw==",
"dev": true
},
"ms": {
diff --git a/api/src/client/package.json b/api/src/client/package.json
index 93ca0b198..2b10d3569 100644
--- a/api/src/client/package.json
+++ b/api/src/client/package.json
@@ -24,7 +24,7 @@
"html-webpack-plugin": "^5.5.1",
"jsdom": "^21.1.1",
"jsdom-global": "^3.0.2",
- "jsf.js_next_gen": "4.0.2-beta.5",
+ "jsf.js_next_gen": "4.0.2-beta.8",
"mocha": "^10.2.0",
"npm-check-updates": "^16.10.8",
"nyc": "^15.1.0",
diff --git a/api/src/client/typescript/faces/impl/xhrCore/XhrRequest.ts b/api/src/client/typescript/faces/impl/xhrCore/XhrRequest.ts
index ac5c3097a..abfb4aea5 100644
--- a/api/src/client/typescript/faces/impl/xhrCore/XhrRequest.ts
+++ b/api/src/client/typescript/faces/impl/xhrCore/XhrRequest.ts
@@ -412,11 +412,11 @@ export class XhrRequest extends AsyncRunnable<XMLHttpRequest> {
//Checkbox and radio only value pass if checked is set, otherwise they should not show
//up at all, and if checked is set, they either can have a value or simply being boolean
- if((type == "checkbox" || type == "radio") && issuingItem.attr("checked").isAbsent()) {
+ if((type == "checkbox" || type == "radio") && !issuingItem.checked) {
return;
} else if((type == "checkbox" || type == "radio")) {
arr.assign(issuingItemId).value = itemValue.orElse(true).value;
- } else {
+ } else if (itemValue.isPresent()) {
arr.assign(issuingItemId).value = itemValue.value;
}
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 9f8e53889..e6ce77ed0 100644
--- a/api/src/client/typescript/faces/test/xhrCore/RequestParamsTest.spec.ts
+++ b/api/src/client/typescript/faces/test/xhrCore/RequestParamsTest.spec.ts
@@ -265,4 +265,302 @@ describe("test for proper request param patterns identical to the old implementa
done();
});
-});
\ No newline at end of file
+ /**
+ * This test is based on Tobago 6.0.0 (Jakarte EE 10).
+ */
+ it("tobago tree select", function (done) {
+ window.document.body.innerHTML = `
+<tobago-page locale='de' class='container-fluid' id='page' focus-on-error='true' wait-overlay-delay-full='1000' wait-overlay-delay-ajax='1000'>
+ <form action='/content/090-tree/01-select/Tree_Select.xhtml' id='page::form' method='post' accept-charset='UTF-8' data-tobago-context-path=''>
+ <input type='hidden' name='jakarta.faces.source' id='jakarta.faces.source' disabled='disabled'>
+ <tobago-focus id='page::lastFocusId'>
+ <input type='hidden' name='page::lastFocusId' id='page::lastFocusId::field'>
+ </tobago-focus>
+ <input type='hidden' name='org.apache.myfaces.tobago.webapp.Secret' id='org.apache.myfaces.tobago.webapp.Secret' value='secretValue'>
+ <div class='tobago-page-menuStore'>
+ </div>
+ <div class='tobago-page-toastStore'>
+ </div>
+ <span id='page::faces-state-container'><input type='hidden' name='jakarta.faces.ViewState' id='j_id__v_0:jakarta.faces.ViewState:1' value='viewStateValue' autocomplete='off'><input type='hidden' name='jakarta.faces.RenderKitId' value='tobago'><input type='hidden' id='j_id__v_0:jakarta.faces.ClientWindow:1' name='jakarta.faces.ClientWindow' value='clientWindowValue'></span>
+ <tobago-tree id='page:categoriesTree' data-tobago-selectable='multi' selectable='multi'>
+<tobago-tree-node id='page:categoriesTree:0:j_id_3' class='tobago-folder tobago-expanded' expandable='expandable' index='0' data-tobago-level='0'>
+<span id='page:categoriesTree:0:j_id_4' class='tobago-toggle'><i class='bi-dash-square' data-tobago-open='bi-dash-square' data-tobago-closed='bi-plus-square'></i></span>
+<tobago-tree-select class='form-check-inline form-check'>
+<input class='form-check-input' type='checkbox' name='page:categoriesTree:0:select' value='page:categoriesTree:0:select' id='page:categoriesTree:0:select'>
+<label class='form-check-label' for='page:categoriesTree:0:select'>Category</label>
+<tobago-behavior event='change' client-id='page:categoriesTree:0:select' execute='page:categoriesTree:0:select page:categoriesTree' render='page:selectedNodesOutput page:categoriesTree'></tobago-behavior>
+</tobago-tree-select>
+</tobago-tree-node>
+<tobago-tree-node id='page:categoriesTree:1:j_id_3' index='1' data-tobago-tree-parent='page:categoriesTree:0:j_id_3' parent='page:categoriesTree:0:j_id_3' data-tobago-level='1'>
+<span id='page:categoriesTree:1:j_id_4' class='tobago-toggle invisible'><i class='bi-square invisible'></i></span>
+<tobago-tree-select class='form-check-inline form-check'>
+<input class='form-check-input' type='checkbox' name='page:categoriesTree:1:select' value='page:categoriesTree:1:select' id='page:categoriesTree:1:select'>
+<label class='form-check-label' for='page:categoriesTree:1:select'>Sports</label>
+<tobago-behavior event='change' client-id='page:categoriesTree:1:select' execute='page:categoriesTree:1:select page:categoriesTree' render='page:selectedNodesOutput page:categoriesTree'></tobago-behavior>
+</tobago-tree-select>
+</tobago-tree-node>
+<tobago-tree-node id='page:categoriesTree:2:j_id_3' index='2' data-tobago-tree-parent='page:categoriesTree:0:j_id_3' parent='page:categoriesTree:0:j_id_3' data-tobago-level='1'>
+<span id='page:categoriesTree:2:j_id_4' class='tobago-toggle invisible'><i class='bi-square invisible'></i></span>
+<tobago-tree-select class='form-check-inline form-check'>
+<input class='form-check-input' type='checkbox' name='page:categoriesTree:2:select' value='page:categoriesTree:2:select' id='page:categoriesTree:2:select'>
+<label class='form-check-label' for='page:categoriesTree:2:select'>Movies</label>
+<tobago-behavior event='change' client-id='page:categoriesTree:2:select' execute='page:categoriesTree:2:select page:categoriesTree' render='page:selectedNodesOutput page:categoriesTree'></tobago-behavior>
+</tobago-tree-select>
+</tobago-tree-node>
+<tobago-tree-node id='page:categoriesTree:3:j_id_3' class='tobago-selected tobago-folder tobago-expanded' selected='selected' expandable='expandable' index='3' data-tobago-tree-parent='page:categoriesTree:0:j_id_3' parent='page:categoriesTree:0:j_id_3' data-tobago-level='1'>
+<span id='page:categoriesTree:3:j_id_4' class='tobago-toggle'><i class='bi-dash-square' data-tobago-open='bi-dash-square' data-tobago-closed='bi-plus-square'></i></span>
+<tobago-tree-select class='form-check-inline form-check'>
+<input class='form-check-input' type='checkbox' name='page:categoriesTree:3:select' value='page:categoriesTree:3:select' id='page:categoriesTree:3:select' checked='checked'>
+<label class='form-check-label' for='page:categoriesTree:3:select'>Music</label>
+<tobago-behavior event='change' client-id='page:categoriesTree:3:select' execute='page:categoriesTree:3:select page:categoriesTree' render='page:selectedNodesOutput page:categoriesTree'></tobago-behavior>
+</tobago-tree-select>
+</tobago-tree-node>
+<tobago-tree-node id='page:categoriesTree:4:j_id_3' index='4' data-tobago-tree-parent='page:categoriesTree:3:j_id_3' parent='page:categoriesTree:3:j_id_3' data-tobago-level='2'>
+<span id='page:categoriesTree:4:j_id_4' class='tobago-toggle invisible'><i class='bi-square invisible'></i></span>
+<tobago-tree-select class='form-check-inline form-check'>
+<input class='form-check-input' type='checkbox' name='page:categoriesTree:4:select' value='page:categoriesTree:4:select' id='page:categoriesTree:4:select'>
+<label class='form-check-label' for='page:categoriesTree:4:select'>Classic</label>
+<tobago-behavior event='change' client-id='page:categoriesTree:4:select' execute='page:categoriesTree:4:select page:categoriesTree' render='page:selectedNodesOutput page:categoriesTree'></tobago-behavior>
+</tobago-tree-select>
+</tobago-tree-node>
+<tobago-tree-node id='page:categoriesTree:5:j_id_3' index='5' data-tobago-tree-parent='page:categoriesTree:3:j_id_3' parent='page:categoriesTree:3:j_id_3' data-tobago-level='2'>
+<span id='page:categoriesTree:5:j_id_4' class='tobago-toggle invisible'><i class='bi-square invisible'></i></span>
+<tobago-tree-select class='form-check-inline form-check'>
+<input class='form-check-input' type='checkbox' name='page:categoriesTree:5:select' value='page:categoriesTree:5:select' id='page:categoriesTree:5:select'>
+<label class='form-check-label' for='page:categoriesTree:5:select'>Pop</label>
+<tobago-behavior event='change' client-id='page:categoriesTree:5:select' execute='page:categoriesTree:5:select page:categoriesTree' render='page:selectedNodesOutput page:categoriesTree'></tobago-behavior>
+</tobago-tree-select>
+</tobago-tree-node>
+<tobago-tree-node id='page:categoriesTree:6:j_id_3' class='tobago-folder' expandable='expandable' index='6' data-tobago-tree-parent='page:categoriesTree:3:j_id_3' parent='page:categoriesTree:3:j_id_3' data-tobago-level='2'>
+<span id='page:categoriesTree:6:j_id_4' class='tobago-toggle'><i class='bi-plus-square' data-tobago-open='bi-dash-square' data-tobago-closed='bi-plus-square'></i></span>
+<tobago-tree-select class='form-check-inline form-check'>
+<input class='form-check-input' type='checkbox' name='page:categoriesTree:6:select' value='page:categoriesTree:6:select' id='page:categoriesTree:6:select'>
+<label class='form-check-label' for='page:categoriesTree:6:select'>World</label>
+<tobago-behavior event='change' client-id='page:categoriesTree:6:select' execute='page:categoriesTree:6:select page:categoriesTree' render='page:selectedNodesOutput page:categoriesTree'></tobago-behavior>
+</tobago-tree-select>
+</tobago-tree-node>
+<tobago-tree-node id='page:categoriesTree:7:j_id_3' index='7' data-tobago-tree-parent='page:categoriesTree:0:j_id_3' parent='page:categoriesTree:0:j_id_3' data-tobago-level='1'>
+<span id='page:categoriesTree:7:j_id_4' class='tobago-toggle invisible'><i class='bi-square invisible'></i></span>
+<tobago-tree-select class='form-check-inline form-check'>
+<input class='form-check-input' type='checkbox' name='page:categoriesTree:7:select' value='page:categoriesTree:7:select' id='page:categoriesTree:7:select'>
+<label class='form-check-label' for='page:categoriesTree:7:select'>Games</label>
+<tobago-behavior event='change' client-id='page:categoriesTree:7:select' execute='page:categoriesTree:7:select page:categoriesTree' render='page:selectedNodesOutput page:categoriesTree'></tobago-behavior>
+</tobago-tree-select>
+</tobago-tree-node>
+<tobago-tree-node id='page:categoriesTree:8:j_id_3' class='tobago-folder tobago-expanded' expandable='expandable' index='8' data-tobago-tree-parent='page:categoriesTree:0:j_id_3' parent='page:categoriesTree:0:j_id_3' data-tobago-level='1'>
+<span id='page:categoriesTree:8:j_id_4' class='tobago-toggle'><i class='bi-dash-square' data-tobago-open='bi-dash-square' data-tobago-closed='bi-plus-square'></i></span>
+<tobago-tree-select class='form-check-inline form-check'>
+<input class='form-check-input' type='checkbox' name='page:categoriesTree:8:select' value='page:categoriesTree:8:select' id='page:categoriesTree:8:select'>
+<label class='form-check-label' for='page:categoriesTree:8:select'>Science</label>
+<tobago-behavior event='change' client-id='page:categoriesTree:8:select' execute='page:categoriesTree:8:select page:categoriesTree' render='page:selectedNodesOutput page:categoriesTree'></tobago-behavior>
+</tobago-tree-select>
+</tobago-tree-node>
+<tobago-tree-node id='page:categoriesTree:9:j_id_3' class='tobago-folder' expandable='expandable' index='9' data-tobago-tree-parent='page:categoriesTree:8:j_id_3' parent='page:categoriesTree:8:j_id_3' data-tobago-level='2'>
+<span id='page:categoriesTree:9:j_id_4' class='tobago-toggle'><i class='bi-plus-square' data-tobago-open='bi-dash-square' data-tobago-closed='bi-plus-square'></i></span>
+<tobago-tree-select class='form-check-inline form-check'>
+<input class='form-check-input' type='checkbox' name='page:categoriesTree:9:select' value='page:categoriesTree:9:select' id='page:categoriesTree:9:select'>
+<label class='form-check-label' for='page:categoriesTree:9:select'>Mathematics</label>
+<tobago-behavior event='change' client-id='page:categoriesTree:9:select' execute='page:categoriesTree:9:select page:categoriesTree' render='page:selectedNodesOutput page:categoriesTree'></tobago-behavior>
+</tobago-tree-select>
+</tobago-tree-node>
+<tobago-tree-node id='page:categoriesTree:10:j_id_3' index='10' data-tobago-tree-parent='page:categoriesTree:8:j_id_3' parent='page:categoriesTree:8:j_id_3' data-tobago-level='2'>
+<span id='page:categoriesTree:10:j_id_4' class='tobago-toggle invisible'><i class='bi-square invisible'></i></span>
+<tobago-tree-select class='form-check-inline form-check'>
+<input class='form-check-input' type='checkbox' name='page:categoriesTree:10:select' value='page:categoriesTree:10:select' id='page:categoriesTree:10:select'>
+<label class='form-check-label' for='page:categoriesTree:10:select'>Geography</label>
+<tobago-behavior event='change' client-id='page:categoriesTree:10:select' execute='page:categoriesTree:10:select page:categoriesTree' render='page:selectedNodesOutput page:categoriesTree'></tobago-behavior>
+</tobago-tree-select>
+</tobago-tree-node>
+<tobago-tree-node id='page:categoriesTree:11:j_id_3' class='tobago-folder' expandable='expandable' index='11' data-tobago-tree-parent='page:categoriesTree:8:j_id_3' parent='page:categoriesTree:8:j_id_3' data-tobago-level='2'>
+<span id='page:categoriesTree:11:j_id_4' class='tobago-toggle'><i class='bi-plus-square' data-tobago-open='bi-dash-square' data-tobago-closed='bi-plus-square'></i></span>
+<tobago-tree-select class='form-check-inline form-check'>
+<input class='form-check-input' type='checkbox' name='page:categoriesTree:11:select' value='page:categoriesTree:11:select' id='page:categoriesTree:11:select'>
+<label class='form-check-label' for='page:categoriesTree:11:select'>Astronomy</label>
+<tobago-behavior event='change' client-id='page:categoriesTree:11:select' execute='page:categoriesTree:11:select page:categoriesTree' render='page:selectedNodesOutput page:categoriesTree'></tobago-behavior>
+</tobago-tree-select>
+</tobago-tree-node>
+<input type='hidden' name='page:categoriesTree::selected' id='page:categoriesTree::selected' class='tobago-selected' value='[3]'>
+<input type='hidden' name='page:categoriesTree::expanded' id='page:categoriesTree::expanded' class='tobago-expanded' value='[0,3,8]'>
+<tobago-scroll>
+<input id='page:categoriesTree::scrollPosition' name='page:categoriesTree::scrollPosition' type='hidden' value='[0,0]' data-tobago-scroll-position='true'>
+</tobago-scroll>
+</tobago-tree>
+ <tobago-out id='page:selectedNodesOutput' class='tobago-label-container tobago-auto-spacing'><label for='page:selectedNodesOutput' class='col-form-label'>Selected Nodes</label><span class='form-control-plaintext'>Music</span></tobago-out>
+ </form>
+ <noscript>
+ <div class='tobago-page-noscript'>Diese Seite benötigt JavaScript, allerdings ist JavaScript in Ihrem Browser derzeit deaktiviert. Um JavaScript zu aktivieren, lesen Sie ggf. die Anleitung Ihres Browsers.
+ </div>
+ </noscript>
+ </tobago-page>
+`;
+
+ //we now run the tests here
+ try {
+ document.querySelector<HTMLInputElement>("input[name='page:categoriesTree:3:select']").checked = false;
+
+ let event = {
+ isTrusted: true,
+ type: 'change',
+ target: document.getElementById("page:categoriesTree:3:select"),
+ currentTarget: document.getElementById("page:categoriesTree:3:select")
+ };
+ global.debug2 = true;
+ faces.ajax.request(
+ document.getElementById("page:categoriesTree:3:select"),
+ event as any,
+ {
+ "jakarta.faces.behavior.event": "change",
+ execute: 'page:categoriesTree:3:select page:categoriesTree',
+ render: 'page:selectedNodesOutput page:categoriesTree'
+ });
+ } catch (err) {
+ console.error(err);
+ expect(false).to.eq(true);
+ }
+ const requestBody = this.requests[0].requestBody;
+ let arsArr = requestBody.split("&");
+ let resultsMap = {};
+ for (let val of arsArr) {
+ let keyVal = val.split("=");
+
+ if (resultsMap[keyVal[0]]) {
+ console.log("duplicated key '" + keyVal[0] + "'");
+ expect(resultsMap[keyVal[0]]).not.to.exist;
+ }
+ resultsMap[keyVal[0]] = keyVal[1];
+ }
+
+ expect(resultsMap[encodeURIComponent("page::lastFocusId")]).to.exist;
+ expect(resultsMap["org.apache.myfaces.tobago.webapp.Secret"]).to.eq("secretValue");
+ expect(resultsMap["jakarta.faces.ViewState"]).to.eq("viewStateValue");
+ expect(resultsMap["jakarta.faces.RenderKitId"]).to.eq("tobago");
+ expect(resultsMap["jakarta.faces.ClientWindow"]).to.eq("clientWindowValue");
+ expect(resultsMap[encodeURIComponent("page:categoriesTree::selected")]).to.eq(encodeURIComponent("[3]"));
+ expect(resultsMap[encodeURIComponent("page:categoriesTree::expanded")]).to.eq(encodeURIComponent("[0,3,8]"));
+ expect(resultsMap[encodeURIComponent("page:categoriesTree::scrollPosition")]).to.eq(encodeURIComponent("[0,0]"));
+ expect(resultsMap["jakarta.faces.behavior.event"]).to.eq("change");
+ expect(resultsMap["jakarta.faces.partial.event"]).to.eq("change");
+ expect(resultsMap["jakarta.faces.source"]).to.eq(encodeURIComponent("page:categoriesTree:3:select"));
+ expect(resultsMap["jakarta.faces.partial.ajax"]).to.eq("true");
+ expect(resultsMap[encodeURIComponent("page::form")]).to.eq(encodeURIComponent("page::form"));
+ expect(resultsMap["jakarta.faces.partial.execute"]).to.eq(encodeURIComponent("page:categoriesTree:3:select page:categoriesTree"));
+ expect(resultsMap["jakarta.faces.partial.render"]).to.eq(encodeURIComponent("page:selectedNodesOutput page:categoriesTree"));
+ expect(resultsMap[encodeURIComponent("page:categoriesTree:3:select")]).not.to.exist;
+
+ done();
+ });
+
+ /**
+ * This test is based on Tobago 6.0.0 (Jakarte EE 10).
+ */
+ it("tobago selectManyShuttle", function (done) {
+ window.document.body.innerHTML = `
+<tobago-page locale="de" class="container-fluid" id="page" focus-on-error="true" wait-overlay-delay-full="1000" wait-overlay-delay-ajax="1000">
+ <form action="/content/030-select/70-selectManyShuttle/Shuttle.xhtml" id="page::form" method="post" accept-charset="UTF-8" data-tobago-context-path="">
+ <input type="hidden" name="jakarta.faces.source" id="jakarta.faces.source" disabled="disabled">
+ <tobago-focus id="page::lastFocusId">
+ <input type="hidden" name="page::lastFocusId" id="page::lastFocusId::field">
+ </tobago-focus>
+ <input type="hidden" name="org.apache.myfaces.tobago.webapp.Secret" id="org.apache.myfaces.tobago.webapp.Secret" value="secretValue">
+ <div class="tobago-page-menuStore">
+ </div>
+ <div class="tobago-page-toastStore">
+ </div>
+ <span id="page::faces-state-container"><input type="hidden" name="jakarta.faces.ViewState" id="j_id__v_0:jakarta.faces.ViewState:1" value="viewStateValue" autocomplete="off"><input type="hidden" name="jakarta.faces.RenderKitId" value="tobago"><input type="hidden" id="j_id__v_0:jakarta.faces.ClientWindow:1" name="jakarta.faces.ClientWindow" value="clientWindowValue"></span>
+ <tobago-select-many-shuttle id="page:ajaxExample" class="tobago-auto-spacing">
+ <div class="tobago-body">
+ <div class="tobago-unselected-container">
+ <select id="page:ajaxExample::unselected" class="tobago-unselected form-select" multiple="multiple" size="4">
+ <option value="Proxima Centauri">Proxima Centauri
+ </option>
+ <option value="Alpha Centauri">Alpha Centauri
+ </option>
+ <option value="Wolf 359">Wolf 359
+ </option></select>
+ </div>
+ <div class="tobago-controls">
+ <div class="btn-group-vertical">
+ <button type="button" class="btn btn-secondary" id="page:ajaxExample::addAll"><i class="bi-chevron-double-right"></i></button>
+ <button type="button" class="btn btn-secondary" id="page:ajaxExample::add"><i class="bi-chevron-right"></i></button>
+ <button type="button" class="btn btn-secondary" id="page:ajaxExample::remove"><i class="bi-chevron-left"></i></button>
+ <button type="button" class="btn btn-secondary" id="page:ajaxExample::removeAll"><i class="bi-chevron-double-left"></i></button>
+ </div>
+ </div>
+ <div class="tobago-selected-container">
+ <select id="page:ajaxExample::selected" class="tobago-selected form-select" multiple="multiple" size="4">
+ <option value="Sirius">Sirius
+ </option></select>
+ </div>
+ <select class="d-none" id="page:ajaxExample::hidden" name="page:ajaxExample" multiple="multiple">
+ <option value="Proxima Centauri">Proxima Centauri
+ </option>
+ <option value="Alpha Centauri">Alpha Centauri
+ </option>
+ <option value="Wolf 359">Wolf 359
+ </option>
+ <option value="Sirius" selected="selected">Sirius
+ </option></select>
+ </div>
+ <tobago-behavior event="change" client-id="page:ajaxExample" execute="page:ajaxExample" render="page:outputStars"></tobago-behavior>
+ </tobago-select-many-shuttle>
+ <tobago-out id="page:outputStars" class="tobago-label-container tobago-auto-spacing"><label for="page:outputStars" class="col-form-label">Selected Stars</label><span class="form-control-plaintext">[Sirius]</span></tobago-out>
+ </form>
+ </tobago-page>
+`;
+
+ //we now run the tests here
+ try {
+ let siriusOption = document.querySelector<HTMLOptionElement>(".tobago-selected option");
+ document.querySelector<HTMLSelectElement>(".tobago-unselected").add(siriusOption);
+ document.getElementById("page:ajaxExample::hidden")
+ .querySelector<HTMLOptionElement>("option[value='Sirius']").selected = false;
+
+ let event = {
+ isTrusted: true,
+ type: 'change',
+ target: document.getElementById("page:ajaxExample"),
+ currentTarget: document.getElementById("page:ajaxExample")
+ };
+ global.debug2 = true;
+ faces.ajax.request(
+ document.getElementById("page:ajaxExample"),
+ event as any,
+ {
+ "jakarta.faces.behavior.event": "change",
+ execute: 'page:ajaxExample',
+ render: 'page:outputStars'
+ });
+ } catch (err) {
+ console.error(err);
+ expect(false).to.eq(true);
+ }
+ const requestBody = this.requests[0].requestBody;
+ let arsArr = requestBody.split("&");
+ let resultsMap = {};
+ for (let val of arsArr) {
+ let keyVal = val.split("=");
+
+ if (resultsMap[keyVal[0]]) {
+ console.log("duplicated key '" + keyVal[0] + "'");
+ expect(resultsMap[keyVal[0]]).not.to.exist;
+ }
+ resultsMap[keyVal[0]] = keyVal[1];
+ }
+
+ expect(resultsMap[encodeURIComponent("page::lastFocusId")]).to.exist;
+ expect(resultsMap["org.apache.myfaces.tobago.webapp.Secret"]).to.eq("secretValue");
+ expect(resultsMap["jakarta.faces.ViewState"]).to.eq("viewStateValue");
+ expect(resultsMap["jakarta.faces.RenderKitId"]).to.eq("tobago");
+ expect(resultsMap["jakarta.faces.ClientWindow"]).to.eq("clientWindowValue");
+ expect(resultsMap["jakarta.faces.behavior.event"]).to.eq("change");
+ expect(resultsMap["jakarta.faces.partial.event"]).to.eq("change");
+ expect(resultsMap["jakarta.faces.source"]).to.eq(encodeURIComponent("page:ajaxExample"));
+ expect(resultsMap["jakarta.faces.partial.ajax"]).to.eq("true");
+ expect(resultsMap[encodeURIComponent("page::form")]).to.eq(encodeURIComponent("page::form"));
+ expect(resultsMap["jakarta.faces.partial.execute"]).to.eq(encodeURIComponent("page:ajaxExample"));
+ expect(resultsMap["jakarta.faces.partial.render"]).to.eq(encodeURIComponent("page:outputStars"));
+ expect(resultsMap[encodeURIComponent("page:ajaxExample")]).not.to.exist;
+
+ done();
+ });
+});
diff --git a/api/src/client/typescript/mona_dish/AssocArray.ts b/api/src/client/typescript/mona_dish/AssocArray.ts
index 852903fab..f9c03fd2a 100644
--- a/api/src/client/typescript/mona_dish/AssocArray.ts
+++ b/api/src/client/typescript/mona_dish/AssocArray.ts
@@ -251,12 +251,69 @@ export function simpleShallowMerge(...assocArrays) {
return shallowMerge(true, false, ...assocArrays);
}
+function _appendWithOverwrite(withAppend: boolean, target: { [p: string]: any }, key, arr, toAssign) {
+ if (!withAppend) {
+ target[key] = arr[key];
+ } else {
+ //overwrite means in this case, no double entries!
+ //we do not a deep compare for now a single value compare suffices
+ if ('undefined' == typeof target?.[key]) {
+ target[key] = toAssign
+ } else if (!Array.isArray(target[key])) {
+
+ let oldVal = target[key];
+ let newVals = [];
+ //TODO maybe deep deep compare here, but on the other hand it is
+ //shallow
+ toAssign.forEach(item => {
+ if (oldVal != item) {
+ newVals.push(item);
+ }
+ });
+ target[key] = new Es2019Array(...[]);
+ target[key].push(oldVal);
+ target[key].push(...newVals);
+ } else {
+ let oldVal = target[key];
+ let newVals = [];
+ //TODO deep compare here
+ toAssign.forEach(item => {
+ if (oldVal.indexOf(item) == -1) {
+ newVals.push(item);
+ }
+ });
+
+ target[key].push(...newVals);
+ }
+ }
+}
+
+function _appendWithoutOverwrite(withAppend: boolean, target: { [p: string]: any }, key, arr, toAssign) {
+ if (!withAppend) {
+ return;
+ } else {
+ //overwrite means in this case, no double entries!
+ //we do not a deep compare for now a single value compare suffices
+ 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);
+ }
+ }
+}
+
/**
* Shallow merge as in config, but on raw associative arrays
*
- * @param overwrite
- * @param withAppend
- * @param assocArrays
+ * @param overwrite overwrite existing keys, if they exist with their subtrees
+ * @param withAppend if a key exist append the values or drop them
+ * Combination overwrite withappend filters doubles out of merged arrays
+ * @param assocArrays array of assoc arres reduced right to left
*/
export function shallowMerge(overwrite = true, withAppend = false, ...assocArrays) {
let target: {[key: string]: any} = {};
@@ -269,23 +326,46 @@ export function shallowMerge(overwrite = true, withAppend = false, ...assocArray
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);
- }
- }
+ _appendWithOverwrite(withAppend, target, key, arr, toAssign);
+ } else if(!overwrite && target?.[key]) {
+ _appendWithoutOverwrite(withAppend, target, key, arr, toAssign);
}
+
+
+
})
});
return target;
}
+//TODO test this, slightly altered from https://medium.com/@pancemarko/deep-equality-in-javascript-determining-if-two-objects-are-equal-bf98cf47e934
+//he overlooked some optimizations and a shortcut at typeof!
+export function deepEqual(obj1, obj2) {
+ if(obj1 == obj2) {
+ return false;
+ }
+ if(typeof obj1 != typeof obj2) {
+ return false;
+ }
+ if(Array.isArray(obj1) && Array.isArray(obj2)) {
+ if(obj1.length != obj2.length) {
+ return;
+ }
+ //arrays must be equal, order as well, there is no way around it
+ //this is the major limitation we have
+ return obj1.every((item, cnt) => deepEqual(item, obj2[cnt]));
+ }
+ //string number and other primitives are filtered out here
+ if("object" == typeof obj1 && "object" == typeof obj2) {
+ let keys1 = Object.keys(obj1);
+ let keys2 = Object.keys(obj2);
+ if(keys1.length != keys2.length) {
+ return false;
+ }
+ return keys1.every(key => keys2.indexOf(key) != -1) &&
+ keys1.every(key => deepEqual(obj1[key], obj2[key]));
+ }
+ return false;
+ //done here no match found
+}
+