sdc_client 0.158.9 → 0.160.0

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.
Files changed (57) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/LICENSE +21 -0
  3. package/dist/index.js +23 -13
  4. package/dist/ugly.index.js +2 -1
  5. package/dist/ugly.index.js.LICENSE.txt +1 -8
  6. package/gulp/gulp.jsx +11 -36
  7. package/package.json +23 -12
  8. package/src/index.js +31 -7
  9. package/src/simpleDomControl/AbstractSDC.js +15 -11
  10. package/src/simpleDomControl/sdc_controller.js +3 -3
  11. package/src/simpleDomControl/sdc_dom_events.js +5 -10
  12. package/src/simpleDomControl/sdc_events.js +3 -3
  13. package/src/simpleDomControl/sdc_main.js +34 -13
  14. package/src/simpleDomControl/sdc_model.js +225 -92
  15. package/src/simpleDomControl/sdc_params.js +0 -3
  16. package/src/simpleDomControl/sdc_server_call.js +21 -4
  17. package/src/simpleDomControl/sdc_test_utils.js +0 -1
  18. package/src/simpleDomControl/sdc_utils.js +59 -7
  19. package/src/simpleDomControl/sdc_view.js +16 -1
  20. package/.github/workflows/node.js.yml +0 -38
  21. package/.idea/inspectionProfiles/Project_Default.xml +0 -17
  22. package/.idea/jsLibraryMappings.xml +0 -6
  23. package/.idea/misc.xml +0 -6
  24. package/.idea/modules.xml +0 -8
  25. package/.idea/vcs.xml +0 -6
  26. package/.idea/workspace.xml +0 -403
  27. package/.readthedocs.yaml +0 -13
  28. package/.tool-versions +0 -1
  29. package/SimpleDomControlClient.iml +0 -12
  30. package/docs/api-reference.rst +0 -225
  31. package/docs/conf.py +0 -11
  32. package/docs/controllers.rst +0 -228
  33. package/docs/events-and-dom.rst +0 -120
  34. package/docs/getting-started.rst +0 -143
  35. package/docs/index.rst +0 -22
  36. package/docs/models.rst +0 -351
  37. package/docs/overview.rst +0 -66
  38. package/docs/requirements.txt +0 -1
  39. package/eslint.config.js +0 -43
  40. package/gulpfile.cjs +0 -30
  41. package/jest.config.mjs +0 -197
  42. package/test/controller.test.js +0 -93
  43. package/test/dist.test.js +0 -127
  44. package/test/model.test.js +0 -330
  45. package/test/models/Author.js +0 -145
  46. package/test/models/Book.js +0 -112
  47. package/test/models/BookContent.js +0 -114
  48. package/test/models/SdcUser.js +0 -75
  49. package/test/models/src.js +0 -8
  50. package/test/sdc_model_dates.ai.test.js +0 -57
  51. package/test/sdc_server_call.ai.test.js +0 -267
  52. package/test/utils.js +0 -88
  53. package/test/view.test.js +0 -159
  54. package/test-setup.js +0 -4
  55. package/webpack.config/webpack.default.config.cjs +0 -14
  56. package/webpack.config/webpack.development.config.cjs +0 -14
  57. package/webpack.config/webpack.production.config.cjs +0 -11
@@ -6,7 +6,22 @@ let IS_CONNECTING = false;
6
6
  let SDC_SOCKET = null;
7
7
  let OPEN_REQUESTS = {};
8
8
 
9
+ /**
10
+ * Server methods receive the arguments as keyword arguments, so they must be an
11
+ * object. Other values are passed as ``arg0``.
12
+ */
13
+ function normalizeArgs(args) {
14
+ if (args === undefined || args === null) {
15
+ return {};
16
+ }
17
+ if (typeof args !== "object" || Array.isArray(args)) {
18
+ return { arg0: args };
19
+ }
20
+ return args;
21
+ }
22
+
9
23
  export function callServer(app, controller, parsedContentUrl, funcName, args) {
24
+ args = normalizeArgs(args);
10
25
  if (window.SERVER_CALL_VIA_WEB_SOCKET) {
11
26
  return socketCallServer(app, controller, funcName, args);
12
27
  } else {
@@ -29,9 +44,6 @@ export function close() {
29
44
  }
30
45
 
31
46
  function postCallServer(parsedContentUrl, funcName, args) {
32
- if (typeof args !== "object" && Array.isArray(args) && args === null) {
33
- args = { arg0: args };
34
- }
35
47
  args = {
36
48
  data: JSON.stringify(args),
37
49
  _sdc_func_name: funcName,
@@ -145,12 +157,17 @@ function _handle_response(data) {
145
157
  }
146
158
 
147
159
  if (data.type && data.type === "sdc_recall") {
160
+ // Like over HTTP: msg/header in the return value of the server method show a message.
161
+ const { data: returnData } = data;
162
+ if (returnData && typeof returnData === "object" && (returnData.msg || returnData.header)) {
163
+ trigger("pushMsg", returnData.header || "", returnData.msg || "");
164
+ }
148
165
  if (data.id && OPEN_REQUESTS[data.id]) {
149
166
  OPEN_REQUESTS[data.id][0](data.data);
150
167
  delete OPEN_REQUESTS[data.id];
151
168
  }
152
169
  } else if (data.type && data.type === "sdc_event") {
153
- let event = data.event;
170
+ let { event } = data;
154
171
  if (event) {
155
172
  trigger(event, data.payload);
156
173
  }
@@ -25,7 +25,6 @@ function setDefaults() {
25
25
  }
26
26
 
27
27
  function getCookie(name) {
28
- let cookieValue = null;
29
28
  if (document.cookie && document.cookie !== "") {
30
29
  const cookies = document.cookie.split(";");
31
30
  for (let i = 0; i < cookies.length; i++) {
@@ -1,4 +1,4 @@
1
- import {SdcModel, SdcQuerySet} from "../index.js";
1
+ import { SdcModel, SdcQuerySet } from "../index.js";
2
2
 
3
3
  /**
4
4
  * Reference to the HTML body.
@@ -193,7 +193,7 @@ function progressHandlingFunction(e) {
193
193
 
194
194
  $progressContainer
195
195
  .find(".progress-bar")
196
- .css({width: percentVal})
196
+ .css({ width: percentVal })
197
197
  .text(percentVal);
198
198
  }
199
199
  }
@@ -331,7 +331,7 @@ function parseHiddenInputs(value) {
331
331
  }
332
332
 
333
333
  export function getValueFromField(formItem) {
334
- let {type, name} = formItem;
334
+ let { type, name } = formItem;
335
335
  if (name && name !== "") {
336
336
  if (type === "hidden") {
337
337
  return parseHiddenInputs($(formItem).val());
@@ -350,12 +350,64 @@ export function getValueFromField(formItem) {
350
350
  return null;
351
351
  }
352
352
 
353
+ const pad2 = (n) => String(n).padStart(2, "0");
354
+
355
+ /**
356
+ * Convert a Date, a date string or a timestamp into a valid Date.
357
+ * A date-only string ("YYYY-MM-DD") is read as local midnight, not UTC,
358
+ * so the day does not shift in time zones west of UTC.
359
+ *
360
+ * @param {*} value
361
+ * @returns {?Date} null if the value is empty or not a valid date
362
+ */
363
+ export function toDate(value) {
364
+ if (value === null || value === undefined || value === "") {
365
+ return null;
366
+ }
367
+ let date;
368
+ if (value instanceof Date) {
369
+ date = value;
370
+ } else if (typeof value === "string" && /^\d{4}-\d{2}-\d{2}$/.test(value)) {
371
+ const [y, m, d] = value.split("-").map(Number);
372
+ date = new Date(y, m - 1, d);
373
+ } else {
374
+ date = new Date(value);
375
+ }
376
+ return isNaN(date.getTime()) ? null : date;
377
+ }
378
+
379
+ /**
380
+ * @param {*} value
381
+ * @returns {string} "YYYY-MM-DD" in local time, or "" if not a valid date
382
+ */
383
+ export function formatDate(value) {
384
+ const date = toDate(value);
385
+ if (!date) {
386
+ return "";
387
+ }
388
+ return `${date.getFullYear()}-${pad2(date.getMonth() + 1)}-${pad2(date.getDate())}`;
389
+ }
390
+
391
+ /**
392
+ * @param {*} value
393
+ * @returns {string} "YYYY-MM-DDTHH:MM" in local time, or "" if not a valid date
394
+ */
395
+ export function formatDateTimeLocal(value) {
396
+ const date = toDate(value);
397
+ if (!date) {
398
+ return "";
399
+ }
400
+ return `${formatDate(date)}T${pad2(date.getHours())}:${pad2(date.getMinutes())}`;
401
+ }
353
402
 
354
403
  export function setValueInField(formItem, value) {
355
- let {type, name} = formItem;
404
+ let { type, name } = formItem;
356
405
  if (name && name !== "") {
357
-
358
- if (type === "checkbox") {
406
+ if (type === "datetime-local") {
407
+ formItem.value = formatDateTimeLocal(value);
408
+ } else if (type === "date") {
409
+ formItem.value = formatDate(value);
410
+ } else if (type === "checkbox") {
359
411
  formItem.checked = !!value;
360
412
  } else if (type === "file") {
361
413
  if (value instanceof File) {
@@ -368,7 +420,7 @@ export function setValueInField(formItem, value) {
368
420
  } else if (value instanceof SdcModel) {
369
421
  $(formItem).val(value.id);
370
422
  } else if (value instanceof SdcQuerySet) {
371
- $(formItem).val(`[${value.getIds().join(',')}]`);
423
+ $(formItem).val(`[${value.getIds().join(",")}]`);
372
424
  } else {
373
425
  if (value === null) {
374
426
  value = "";
@@ -72,7 +72,7 @@ function findSdcTgs($container, tagNameList, parentController) {
72
72
  */
73
73
  function replacePlaceholderController(controller, url, urlValues) {
74
74
  for (let key_idx in controller._urlParams) {
75
- if (controller._urlParams.hasOwnProperty(key_idx)) {
75
+ if (Object.hasOwn(controller._urlParams, key_idx)) {
76
76
  let key = controller._urlParams[key_idx];
77
77
  let re = RegExp("%\\(" + key + "\\)\\w", "gm");
78
78
  const val = urlValues[key];
@@ -516,7 +516,22 @@ export function reconcile($virtualNode, $realNode) {
516
516
  toRemove.forEach(($element) => $element.safeRemove());
517
517
  }
518
518
 
519
+ /**
520
+ * Replace the event listeners added by sdcDom (JSX on* props) on a kept node
521
+ * with the listeners of its new virtual counterpart.
522
+ */
523
+ function syncListeners(real, virtual) {
524
+ if (!real || !virtual || (!real._sdcListeners && !virtual._sdcListeners)) {
525
+ return;
526
+ }
527
+ (real._sdcListeners ?? []).forEach(([type, fn]) => real.removeEventListener(type, fn));
528
+ const listeners = virtual._sdcListeners ?? [];
529
+ listeners.forEach(([type, fn]) => real.addEventListener(type, fn));
530
+ real._sdcListeners = listeners;
531
+ }
532
+
519
533
  function syncAttributes($real, $virtual) {
534
+ syncListeners($real[0], $virtual[0]);
520
535
  const realAttrs = $real[0].attributes ?? [];
521
536
  const virtualAttrs = $virtual[0].attributes ?? [];
522
537
  // Remove missing attrs
@@ -1,38 +0,0 @@
1
- # This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node
2
- # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs
3
-
4
- name: Node.js CI
5
-
6
- on:
7
- push:
8
- branches: [ "main" ]
9
- pull_request:
10
- branches: [ "main" ]
11
-
12
- jobs:
13
- build:
14
-
15
- runs-on: ubuntu-latest
16
-
17
- strategy:
18
- matrix:
19
- node-version: [20.x, 22.x]
20
- # See supported Node.js release schedule at https://nodejs.org/en/about/releases/
21
-
22
- steps:
23
- - uses: actions/checkout@v4
24
- - name: Use Node.js ${{ matrix.node-version }}
25
- uses: actions/setup-node@v4
26
- with:
27
- node-version: ${{ matrix.node-version }}
28
- cache: 'npm'
29
-
30
- - name: Install dependencies (npm)
31
- run: |
32
- npm i -g yarn
33
- yarn install
34
-
35
- - name: Run Tests
36
- run: |
37
- yarn test
38
-
@@ -1,17 +0,0 @@
1
- <component name="InspectionProjectProfileManager">
2
- <profile version="1.0">
3
- <option name="myName" value="Project Default" />
4
- <inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
5
- <inspection_tool class="HtmlUnknownAttribute" enabled="true" level="WARNING" enabled_by_default="true">
6
- <option name="myValues">
7
- <value>
8
- <list size="1">
9
- <item index="0" class="java.lang.String" itemvalue="keydown" />
10
- </list>
11
- </value>
12
- </option>
13
- <option name="myCustomValuesEnabled" value="true" />
14
- </inspection_tool>
15
- <inspection_tool class="JSHint" enabled="true" level="ERROR" enabled_by_default="true" />
16
- </profile>
17
- </component>
@@ -1,6 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <project version="4">
3
- <component name="JavaScriptLibraryMappings">
4
- <includedPredefinedLibrary name="Node.js Core" />
5
- </component>
6
- </project>
package/.idea/misc.xml DELETED
@@ -1,6 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <project version="4">
3
- <component name="Black">
4
- <option name="sdkName" value="Poetry (SimpleDomControlClient)" />
5
- </component>
6
- </project>
package/.idea/modules.xml DELETED
@@ -1,8 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <project version="4">
3
- <component name="ProjectModuleManager">
4
- <modules>
5
- <module fileurl="file://$PROJECT_DIR$/SimpleDomControlClient.iml" filepath="$PROJECT_DIR$/SimpleDomControlClient.iml" />
6
- </modules>
7
- </component>
8
- </project>
package/.idea/vcs.xml DELETED
@@ -1,6 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <project version="4">
3
- <component name="VcsDirectoryMappings">
4
- <mapping directory="$PROJECT_DIR$" vcs="Git" />
5
- </component>
6
- </project>