sdc_client 0.58.6 → 0.158.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 (39) hide show
  1. package/.github/workflows/node.js.yml +2 -2
  2. package/.idea/inspectionProfiles/Project_Default.xml +17 -0
  3. package/.idea/workspace.xml +122 -23
  4. package/.readthedocs.yaml +13 -0
  5. package/dist/index.js +70 -51
  6. package/dist/ugly.index.js +1 -1
  7. package/docs/api-reference.rst +225 -0
  8. package/docs/conf.py +11 -0
  9. package/docs/controllers.rst +228 -0
  10. package/docs/events-and-dom.rst +120 -0
  11. package/docs/getting-started.rst +143 -0
  12. package/docs/index.rst +22 -0
  13. package/docs/models.rst +351 -0
  14. package/docs/overview.rst +66 -0
  15. package/docs/requirements.txt +1 -0
  16. package/eslint.config.js +60 -0
  17. package/gulp/gulp.jsx +15 -13
  18. package/package.json +13 -3
  19. package/src/index.js +44 -11
  20. package/src/simpleDomControl/AbstractSDC.js +287 -286
  21. package/src/simpleDomControl/sdc_controller.js +157 -132
  22. package/src/simpleDomControl/sdc_dom_events.js +99 -88
  23. package/src/simpleDomControl/sdc_events.js +39 -40
  24. package/src/simpleDomControl/sdc_main.js +88 -67
  25. package/src/simpleDomControl/sdc_model.js +1510 -0
  26. package/src/simpleDomControl/sdc_params.js +44 -29
  27. package/src/simpleDomControl/sdc_server_call.js +153 -154
  28. package/src/simpleDomControl/sdc_socket.js +8 -829
  29. package/src/simpleDomControl/sdc_test_utils.js +77 -80
  30. package/src/simpleDomControl/sdc_utils.js +295 -176
  31. package/src/simpleDomControl/sdc_view.js +245 -177
  32. package/test/model.test.js +332 -0
  33. package/test/models/Author.js +145 -0
  34. package/test/models/Book.js +112 -0
  35. package/test/models/BookContent.js +114 -0
  36. package/test/models/SdcUser.js +75 -0
  37. package/test/models/src.js +8 -0
  38. package/test/sdc_model_dates.ai.test.js +57 -0
  39. package/test/sdc_server_call.ai.test.js +267 -0
@@ -1,3 +1,5 @@
1
+ import {SdcModel, SdcQuerySet} from "../index.js";
2
+
1
3
  /**
2
4
  * Reference to the HTML body.
3
5
  * @type {*|jQuery|HTMLElement}
@@ -5,7 +7,7 @@
5
7
  */
6
8
  let _$body;
7
9
  const arg_names_reg = /(?<=^|,\s?)[^=\s,]+/g;
8
- const commend_reg = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
10
+ const commend_reg = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/gm;
9
11
 
10
12
  /**
11
13
  * getBody returns the $body jQuery object.
@@ -13,27 +15,28 @@ const commend_reg = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
13
15
  * @returns {*|jQuery|HTMLElement} - body reference.
14
16
  */
15
17
  export function getBody() {
16
- if (!_$body) {
17
- _$body = $('body');
18
- }
18
+ if (!_$body) {
19
+ _$body = $("body");
20
+ }
19
21
 
20
- return _$body;
22
+ return _$body;
21
23
  }
22
24
 
23
-
24
25
  /**
25
26
  *
26
27
  * @param {function} func
27
28
  * @returns {RegExpMatchArray|*[]}
28
29
  */
29
30
  export function getParamsNameOfFunction(func) {
30
- var fnstr = func.toString().replace(commend_reg, '');
31
- var result = fnstr.slice(fnstr.indexOf('(') + 1, fnstr.indexOf(')')).match(arg_names_reg);
32
- if (!result) {
33
- return [];
34
- }
35
-
36
- return result;
31
+ var fnstr = func.toString().replace(commend_reg, "");
32
+ var result = fnstr
33
+ .slice(fnstr.indexOf("(") + 1, fnstr.indexOf(")"))
34
+ .match(arg_names_reg);
35
+ if (!result) {
36
+ return [];
37
+ }
38
+
39
+ return result;
37
40
  }
38
41
 
39
42
  /**
@@ -41,46 +44,58 @@ export function getParamsNameOfFunction(func) {
41
44
  * @return {Promise} window.utils
42
45
  */
43
46
  export function promiseDummyFactory() {
44
- return new Promise(function (resolve) {
45
- resolve();
46
- });
47
+ return new Promise(function (resolve) {
48
+ resolve();
49
+ });
47
50
  }
48
51
 
49
52
  export function camelCaseToTagName(str) {
50
- str = str.replace(/[A-Z]/g, letter => `-${letter.toLowerCase()}`);
51
- str = str.replace(/[0-9]+/g, number => `-${number}`);
52
- return str.replace(/^[-]/g, ``);
53
+ str = str.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`);
54
+ str = str.replace(/[0-9]+/g, (number) => `-${number}`);
55
+ return str.replace(/^[-]/g, ``);
53
56
  }
54
57
 
55
58
  export function tagNameToCamelCase(str) {
56
- str = str.replace(/-./g, letter => `${letter[1].toUpperCase()}`);
57
- return str;
59
+ str = str.replace(/-./g, (letter) => `${letter[1].toUpperCase()}`);
60
+ return str;
58
61
  }
59
62
 
60
63
  export function tagNameToReadableName(str) {
61
- str = str.replace(/-./g, letter => ` ${letter[1].toUpperCase()}`).replace(/^./g, letter => `${letter.toUpperCase()}`);
62
- return str;
64
+ str = str
65
+ .replace(/-./g, (letter) => ` ${letter[1].toUpperCase()}`)
66
+ .replace(/^./g, (letter) => `${letter.toUpperCase()}`);
67
+ return str;
63
68
  }
64
69
 
65
70
  const copyProps = (targetClass, sourceClass) => {
66
- let source = sourceClass;
67
- let propNamesTarget = Object.getOwnPropertyNames(targetClass.prototype).concat(Object.getOwnPropertySymbols(targetClass.prototype))
68
- while (source.name !== '') {
69
- Object.getOwnPropertyNames(source.prototype)
70
- .concat(Object.getOwnPropertySymbols(source.prototype))
71
- .forEach((prop) => {
72
- if (prop.match(/^(?:constructor|prototype|arguments|caller|name|bind|call|apply|toString|length)$/)) {
73
- return;
74
- }
75
-
76
- if (!propNamesTarget.includes(prop)) {
77
- propNamesTarget.push(prop);
78
- Object.defineProperty(targetClass.prototype, prop, Object.getOwnPropertyDescriptor(source.prototype, prop));
79
- }
80
- });
81
- source = Object.getPrototypeOf(source);
82
- }
83
- }
71
+ let source = sourceClass;
72
+ let propNamesTarget = Object.getOwnPropertyNames(
73
+ targetClass.prototype,
74
+ ).concat(Object.getOwnPropertySymbols(targetClass.prototype));
75
+ while (source.name !== "") {
76
+ Object.getOwnPropertyNames(source.prototype)
77
+ .concat(Object.getOwnPropertySymbols(source.prototype))
78
+ .forEach((prop) => {
79
+ if (
80
+ prop.match(
81
+ /^(?:constructor|prototype|arguments|caller|name|bind|call|apply|toString|length)$/,
82
+ )
83
+ ) {
84
+ return;
85
+ }
86
+
87
+ if (!propNamesTarget.includes(prop)) {
88
+ propNamesTarget.push(prop);
89
+ Object.defineProperty(
90
+ targetClass.prototype,
91
+ prop,
92
+ Object.getOwnPropertyDescriptor(source.prototype, prop),
93
+ );
94
+ }
95
+ });
96
+ source = Object.getPrototypeOf(source);
97
+ }
98
+ };
84
99
 
85
100
  /**
86
101
  *
@@ -89,170 +104,274 @@ const copyProps = (targetClass, sourceClass) => {
89
104
  * @returns {typeof AbstractSDC}
90
105
  */
91
106
  export function agileAggregation(baseClass, ...mixins) {
92
-
93
- let base = {
94
- [baseClass.name]: class {
95
- constructor(..._args) {
96
- let _mixins = {};
97
- mixins.forEach((mixin) => {
98
- let newMixin;
99
- Object.assign(this, (newMixin = new mixin()));
100
- newMixin._tagName = mixin.prototype._tagName;
101
- newMixin._isMixin = true;
102
- _mixins[mixin.name] = newMixin;
103
- });
104
-
105
- Object.assign(this, new baseClass());
106
- this._mixins = _mixins;
107
- }
108
-
109
-
110
- static get name() {
111
- return baseClass.name;
112
- }
113
-
114
- static className() {
115
- return this.name
116
- }
117
-
118
- get mixins() {
119
- return this._mixins;
120
- }
121
- }
122
- }[baseClass.name];
123
-
124
- copyProps(base, baseClass);
125
-
126
- mixins.forEach((mixin) => {
127
- copyProps(base, mixin);
128
- });
129
-
130
- return base;
131
-
107
+ let base = {
108
+ [baseClass.name]: class {
109
+ constructor(..._args) {
110
+ let _mixins = {};
111
+ mixins.forEach((mixin) => {
112
+ let newMixin;
113
+ Object.assign(this, (newMixin = new mixin()));
114
+ newMixin._tagName = mixin.prototype._tagName;
115
+ newMixin._isMixin = true;
116
+ _mixins[mixin.name] = newMixin;
117
+ });
118
+
119
+ Object.assign(this, new baseClass());
120
+ this._mixins = _mixins;
121
+ }
122
+
123
+ static get name() {
124
+ return baseClass.name;
125
+ }
126
+
127
+ static className() {
128
+ return this.name;
129
+ }
130
+
131
+ get mixins() {
132
+ return this._mixins;
133
+ }
134
+ },
135
+ }[baseClass.name];
136
+
137
+ copyProps(base, baseClass);
138
+
139
+ mixins.forEach((mixin) => {
140
+ copyProps(base, mixin);
141
+ });
142
+
143
+ return base;
132
144
  }
133
145
 
134
146
  function csrfSafeMethod(method) {
135
- // these HTTP methods do not require CSRF protection
136
- return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
147
+ // these HTTP methods do not require CSRF protection
148
+ return /^(GET|HEAD|OPTIONS|TRACE)$/.test(method);
137
149
  }
138
150
 
139
151
  export function uploadFileFormData(formData, url, method) {
140
- return $.ajax({
141
- url: url, //Server script to process data
142
- type: method || 'POST',
143
- xhr: function () { // Custom XMLHttpRequest
144
- var myXhr = $.ajaxSettings.xhr();
145
- if (myXhr.upload) { // Check if upload property exists
146
- myXhr.upload.addEventListener('progress', progressHandlingFunction, false); // For handling the progress of the upload
147
- }
148
- return myXhr;
149
- },
150
- //Form data
151
- data: formData,
152
- //Options to tell jQuery not to process data or worry about content-type.
153
- cache: false,
154
- contentType: false,
155
- processData: false,
156
- beforeSend: function (xhr, settings) {
157
- if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
158
- xhr.setRequestHeader("X-CSRFToken", window.CSRF_TOKEN);
159
- }
160
- }
161
- });
152
+ return $.ajax({
153
+ url: url, //Server script to process data
154
+ type: method || "POST",
155
+ xhr: function () {
156
+ // Custom XMLHttpRequest
157
+ var myXhr = $.ajaxSettings.xhr();
158
+ if (myXhr.upload) {
159
+ // Check if upload property exists
160
+ myXhr.upload.addEventListener(
161
+ "progress",
162
+ progressHandlingFunction,
163
+ false,
164
+ ); // For handling the progress of the upload
165
+ }
166
+ return myXhr;
167
+ },
168
+ //Form data
169
+ data: formData,
170
+ //Options to tell jQuery not to process data or worry about content-type.
171
+ cache: false,
172
+ contentType: false,
173
+ processData: false,
174
+ beforeSend: function (xhr, settings) {
175
+ if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
176
+ xhr.setRequestHeader("X-CSRFToken", window.CSRF_TOKEN);
177
+ }
178
+ },
179
+ });
162
180
  }
163
181
 
164
182
  function progressHandlingFunction(e) {
165
- if (e.lengthComputable) {
166
- var percentVal = Math.round((e.loaded / e.total) * 100);
167
- var $progressContainer = $('.progress-container');
168
- if (percentVal === 100) {
169
- $progressContainer.hide();
170
- } else {
171
- $progressContainer.show();
172
- }
173
-
174
- percentVal += '%';
175
-
176
- $progressContainer.find('.progress-bar').css({'width': percentVal}).text(percentVal);
183
+ if (e.lengthComputable) {
184
+ var percentVal = Math.round((e.loaded / e.total) * 100);
185
+ var $progressContainer = $(".progress-container");
186
+ if (percentVal === 100) {
187
+ $progressContainer.hide();
188
+ } else {
189
+ $progressContainer.show();
177
190
  }
178
- }
179
191
 
192
+ percentVal += "%";
180
193
 
181
- export function checkIfParamNumberBoolOrString(paramElement, controller = null) {
182
- if (typeof paramElement !== 'string') {
183
- return paramElement;
184
- }
194
+ $progressContainer
195
+ .find(".progress-bar")
196
+ .css({width: percentVal})
197
+ .text(percentVal);
198
+ }
199
+ }
185
200
 
186
- if (controller && typeof controller[paramElement] !== 'undefined') {
187
- if (typeof controller[paramElement] === 'function') {
188
- return controller[paramElement].bind(controller);
189
- }
190
- return controller[paramElement];
191
- }
201
+ export function checkIfParamNumberBoolOrString(
202
+ paramElement,
203
+ controller = null,
204
+ ) {
205
+ if (typeof paramElement !== "string") {
206
+ return paramElement;
207
+ }
192
208
 
193
- let isFloatReg = /^-?\d+\.?\d+$/;
194
- let isIntReg = /^-?\d+$/;
195
- let isBoolReg = /^(true|false)$/;
196
- let isStringReg = /^(['][^']*['])|(["][^"]*["])$/;
197
-
198
- if (paramElement.match(isBoolReg)) {
199
- return paramElement === 'true';
200
- } else if (paramElement === 'undefined') {
201
- return undefined;
202
- } else if (paramElement.toLowerCase() === 'none') {
203
- return null;
204
- } else if (paramElement.match(isIntReg)) {
205
- return parseInt(paramElement);
206
- } else if (paramElement.match(isFloatReg)) {
207
- return parseFloat(paramElement);
208
- } else if (paramElement.match(isStringReg)) {
209
- return paramElement.substr(1, paramElement.length - 2);
209
+ if (controller && typeof controller[paramElement] !== "undefined") {
210
+ if (typeof controller[paramElement] === "function") {
211
+ return controller[paramElement].bind(controller);
210
212
  }
211
-
212
- return paramElement;
213
+ return controller[paramElement];
214
+ }
215
+
216
+ let isFloatReg = /^-?\d+\.?\d+$/;
217
+ let isIntReg = /^-?\d+$/;
218
+ let isBoolReg = /^(true|false)$/;
219
+ let isStringReg = /^(['][^']*['])|(["][^"]*["])$/;
220
+
221
+ if (paramElement.match(isBoolReg)) {
222
+ return paramElement === "true";
223
+ } else if (paramElement === "undefined") {
224
+ return undefined;
225
+ } else if (paramElement.toLowerCase() === "none") {
226
+ return null;
227
+ } else if (paramElement.match(isIntReg)) {
228
+ return parseInt(paramElement);
229
+ } else if (paramElement.match(isFloatReg)) {
230
+ return parseFloat(paramElement);
231
+ } else if (paramElement.match(isStringReg)) {
232
+ return paramElement.substr(1, paramElement.length - 2);
233
+ }
234
+
235
+ return paramElement;
213
236
  }
214
237
 
215
238
  export function uuidv4() {
216
- return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c =>
217
- (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)
218
- );
239
+ return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, (c) =>
240
+ (
241
+ c ^
242
+ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))
243
+ ).toString(16),
244
+ );
219
245
  }
220
246
 
221
-
222
247
  export function clearErrorsInForm($form) {
223
- $form.find('.has-error').removeClass('has-error').find('.alert-danger').remove();
224
- $form.find('.non-field-errors').remove();
248
+ $form
249
+ .find(".has-error")
250
+ .removeClass("has-error")
251
+ .find(".alert-danger")
252
+ .remove();
253
+ $form.find(".non-field-errors").remove();
225
254
  }
226
255
 
227
256
  export function setErrorsInForm($form, $resForm) {
228
- $resForm = $('<div>').append($resForm);
229
-
230
- $form.find('.has-error').removeClass('has-error').find('.alert-danger').safeRemove();
231
- $form.find('.non-field-errors').safeRemove();
232
- let $file_container = $resForm.find('input[type=file]').parent();
233
- $form.find('input[type=file]').parent().each(function (index) {
234
- $(this).replaceWith($file_container[index]);
235
- });
236
-
237
- let hasNoError = $resForm.find('.non-field-errors').insertAfter($form.find('.hidden-form-fields')).length === 0;
238
- $resForm.find('.has-error').each(function () {
239
- hasNoError = false;
240
- let $resErrorField = $(this);
241
- let className = $resErrorField.data('auto-id');
242
- let $errorField = $form.find('.form-group.' + className);
243
- $errorField.addClass('has-error');
244
- $errorField.find('.form-input-container').append($resErrorField.find('.alert-danger'));
257
+ $resForm = $("<div>").append($resForm);
258
+
259
+ $form
260
+ .find(".has-error")
261
+ .removeClass("has-error")
262
+ .find(".alert-danger")
263
+ .safeRemove();
264
+ $form.find(".non-field-errors").safeRemove();
265
+ let $file_container = $resForm.find("input[type=file]").parent();
266
+ $form
267
+ .find("input[type=file]")
268
+ .parent()
269
+ .each(function (index) {
270
+ $(this).replaceWith($file_container[index]);
245
271
  });
246
272
 
247
- return hasNoError;
273
+ let hasNoError =
274
+ $resForm
275
+ .find(".non-field-errors")
276
+ .insertAfter($form.find(".hidden-form-fields")).length === 0;
277
+ $resForm.find(".has-error").each(function () {
278
+ hasNoError = false;
279
+ let $resErrorField = $(this);
280
+ let className = $resErrorField.data("auto-id");
281
+ let $errorField = $form.find(".form-group." + className);
282
+ $errorField.addClass("has-error");
283
+ $errorField
284
+ .find(".form-input-container")
285
+ .append($resErrorField.find(".alert-danger"));
286
+ });
287
+
288
+ return hasNoError;
248
289
  }
249
290
 
250
291
  export function jqueryInsertAt($container, index, $newElement) {
251
- let lastIndex = $container.children().size();
252
- if (index < lastIndex) {
253
- $container.children().eq(index).before($newElement);
292
+ let lastIndex = $container.children().size();
293
+ if (index < lastIndex) {
294
+ $container.children().eq(index).before($newElement);
295
+ } else {
296
+ $container.append($newElement);
297
+ }
298
+ return this;
299
+ }
300
+
301
+ /**
302
+ * Parse hidden input values back into the closest JavaScript primitive.
303
+ *
304
+ * Hidden inputs are often used to preserve values that were originally booleans,
305
+ * numbers or quoted strings. This keeps form-to-model sync from turning
306
+ * everything into plain strings.
307
+ *
308
+ * @param {string} value
309
+ * @returns {*}
310
+ */
311
+ function parseHiddenInputs(value) {
312
+ let isFloatReg = /^-?\d+\.?\d+$/;
313
+ let isIntReg = /^-?\d+$/;
314
+ let isBoolReg = /^(true|false)$/;
315
+ let isStringReg = /^(['][^']*['])|(["][^"]*["])$/;
316
+
317
+ if (value.toLowerCase().match(isBoolReg)) {
318
+ return value.toLowerCase() === "true";
319
+ } else if (value === "undefined") {
320
+ return undefined;
321
+ } else if (value.toLowerCase() === "none") {
322
+ return null;
323
+ } else if (value.match(isIntReg)) {
324
+ return parseInt(value, 10);
325
+ } else if (value.match(isFloatReg)) {
326
+ return parseFloat(value);
327
+ } else if (value.match(isStringReg)) {
328
+ return value.substring(1, value.length - 1);
329
+ }
330
+ return value;
331
+ }
332
+
333
+ export function getValueFromField(formItem) {
334
+ let {type, name} = formItem;
335
+ if (name && name !== "") {
336
+ if (type === "hidden") {
337
+ return parseHiddenInputs($(formItem).val());
338
+ }
339
+
340
+ if (type === "checkbox") {
341
+ return formItem.checked;
342
+ }
343
+
344
+ if (type === "file") {
345
+ return formItem.files[0];
346
+ }
347
+
348
+ return $(formItem).val();
349
+ }
350
+ return null;
351
+ }
352
+
353
+
354
+ export function setValueInField(formItem, value) {
355
+ let {type, name} = formItem;
356
+ if (name && name !== "") {
357
+
358
+ if (type === "checkbox") {
359
+ formItem.checked = value;
360
+ } else if (type === "file") {
361
+ if (value instanceof File) {
362
+ if (typeof DataTransfer !== "undefined") {
363
+ let container = new DataTransfer();
364
+ container.items.add(value);
365
+ formItem.files = container;
366
+ }
367
+ }
368
+ } else if (value instanceof SdcModel) {
369
+ $(formItem).val(value.id);
370
+ } else if (value instanceof SdcQuerySet) {
371
+ $(formItem).val(`[${value.getIds().join(',')}]`);
254
372
  } else {
255
- $container.append($newElement);
373
+ $(formItem).val(value);
256
374
  }
257
- return this;
375
+ }
376
+ return null;
258
377
  }