@livinglogic/livingapi 0.12.0 → 0.13.2

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/src/livingapi.js CHANGED
@@ -2,8 +2,8 @@
2
2
  * LivingAPI JavaScript Library
3
3
  * https://my.living-apps.de/docs/LivingAPI.html
4
4
  *
5
- * Copyright 2017-2019 by LivingLogic AG, Bayreuth/Germany
6
- * Copyright 2017-2019 by Walter Dörwald
5
+ * Copyright 2017-2022 by LivingLogic AG, Bayreuth/Germany
6
+ * Copyright 2017-2022 by Walter Dörwald
7
7
  *
8
8
  * All Rights Reserved
9
9
  *
@@ -33,11 +33,18 @@ export { version };
33
33
 
34
34
  import * as ul4 from '@livinglogic/ul4';
35
35
 
36
+
36
37
  export class Base extends ul4.Proto
37
38
  {
38
- constructor()
39
+ constructor(id)
39
40
  {
40
41
  super();
42
+ this.id = id;
43
+ }
44
+
45
+ get ul4onid()
46
+ {
47
+ return this.id;
41
48
  }
42
49
 
43
50
  ul4ondump(encoder)
@@ -76,7 +83,7 @@ export class Base extends ul4.Proto
76
83
  this[name] = null;
77
84
  }
78
85
 
79
- __getattr__(name)
86
+ [ul4.symbols.getattr](name)
80
87
  {
81
88
  if (this._ul4attrs.has(name))
82
89
  {
@@ -95,12 +102,18 @@ export class Base extends ul4.Proto
95
102
  throw new ul4.AttributeError(this, name);
96
103
  }
97
104
 
98
- __repr__()
105
+ [ul4.symbols.repr]()
99
106
  {
100
107
  return "<" + this.constructor.name + ">";
101
108
  }
109
+
110
+ toString()
111
+ {
112
+ return this[ul4.symbols.repr]();
113
+ }
102
114
  };
103
115
 
116
+
104
117
  export class Handler
105
118
  {
106
119
  save(record)
@@ -112,11 +125,14 @@ export class Handler
112
125
  }
113
126
  };
114
127
 
128
+
115
129
  export class Globals extends Base
116
130
  {
117
- constructor()
131
+ static classdoc = "Global information";
132
+
133
+ constructor(id)
118
134
  {
119
- super();
135
+ super(id);
120
136
  this.version = null;
121
137
  this.hostname = null;
122
138
  this.platform = null;
@@ -157,67 +173,137 @@ export class Globals extends Base
157
173
  return s;
158
174
  }
159
175
 
160
- __repr__()
176
+ scaledURL(url, width, height, type="fill", enlarge=true, gravity="sm", quality=null, rotate=null, blur=null, sharpen=null, format=null, cache=true)
177
+ {
178
+ let filename = null;
179
+
180
+ if (url instanceof File)
181
+ {
182
+ url = "https://" + this.hostname + url.url;
183
+ filename = encodeURIComponent(url.filename);
184
+ if (filename != url.filename)
185
+ filename = null;
186
+ }
187
+
188
+ let result = "";
189
+
190
+ if (cache)
191
+ result += "/imgproxycache/insecure";
192
+ else
193
+ result += "/imgproxy/insecure";
194
+
195
+ if (type !== "fit")
196
+ result += "/rt:" + type;
197
+
198
+ if (width !== null && width !== undefined)
199
+ result += "/w:" + width;
200
+
201
+ if (height !== null && height !== undefined)
202
+ result += "/h:" + height;
203
+
204
+ if (enlarge)
205
+ result += "/el:1";
206
+
207
+ if (gravity !== null && gravity !== undefined)
208
+ result += "/g:" + gravity;
209
+
210
+ if (quality !== null && quality !== undefined)
211
+ result += "/q:" + quality;
212
+
213
+ if (rotate !== null && rotate % 360 != 0)
214
+ result += "/rot:" + rotatet;
215
+
216
+ if (blur !== null && blur !== undefined)
217
+ result += "/bl:" + blur;
218
+
219
+ if (sharpen !== null && sharpen !== undefined)
220
+ result += "/sh:" + sharpen;
221
+
222
+ if (format !== null && format !== undefined)
223
+ result += "/f:" + format;
224
+
225
+ if (filename !== null && filename !== undefined)
226
+ result += "/fn:" + filename;
227
+
228
+ result += "/plain/" + encodeURIComponent(url);
229
+
230
+ return result;
231
+ }
232
+
233
+ [ul4.symbols.repr]()
161
234
  {
162
235
  return "<Globals version=" + ul4._repr(this.version) + ">";
163
236
  }
164
237
  };
165
238
 
166
- Globals.prototype._ul4onattrs = ["version", "platform", "user", "maxdbactions", "maxtemplateruntime", "flashmessages", "lang", "datasources", "hostname", "app", "record"];
167
- Globals.prototype._ul4attrs = ul4._makeset("version", "hostname", "platform", "user", "lang", "app", "record", "maxdbactions", "maxtemplateruntime", "flashmessages");
239
+ Globals.prototype._ul4onattrs = ["version", "platform", "user", "maxdbactions", "maxtemplateruntime", "flashmessages", "lang", "datasources", "hostname", "app", "record", "google_api_key", "mode", "view_template_id", "email_template_id", "view_id"];
240
+ Globals.prototype._ul4attrs = new Set(["version", "hostname", "platform", "user", "lang", "app", "record", "maxdbactions", "maxtemplateruntime", "flashmessages", "mode"]);
241
+
168
242
 
169
243
  export class FlashMessage extends Base
170
244
  {
171
- __repr__()
245
+ static classdoc = "A flash message in a web page";
246
+
247
+ [ul4.symbols.repr]()
172
248
  {
173
249
  return "<FlashMessage type=" + ul4._repr(this.type) + " title=" + ul4._repr(this.title) + ">";
174
250
  }
175
251
  };
176
252
 
177
253
  FlashMessage.prototype._ul4onattrs = ["timestamp", "type", "title", "message"];
178
- FlashMessage.prototype._ul4attrs = ul4._makeset("timestamp", "type", "title", "message");
254
+ FlashMessage.prototype._ul4attrs = new Set(["timestamp", "type", "title", "message"]);
255
+
179
256
 
180
257
  export class App extends Base
181
258
  {
182
- __repr__()
259
+ static classdoc = "A LivingApps application";
260
+
261
+ [ul4.symbols.repr]()
183
262
  {
184
263
  return "<App id=" + ul4._repr(this.id) + " name=" + ul4._repr(this.name) + ">";
185
264
  }
186
265
 
187
266
  insert(values={})
188
267
  {
189
- let record = this.__call__(values);
268
+ let record = this[ul4.symbols.call](values);
190
269
  this.globals.handler.save(this);
191
270
  return record;
192
271
  }
193
272
 
194
- __call__(values={})
273
+ get layout_controls()
274
+ {
275
+ if (this.active_view === null)
276
+ return new Map();
277
+ return this.active_view.layout_controls;
278
+ }
279
+
280
+ [ul4.symbols.call](values={})
195
281
  {
196
- let record = new Record(this);
282
+ let record = new Record(null, this);
197
283
  if (ul4._ismap(values))
198
284
  {
199
- for (let [key, value] of values.entries())
285
+ for (let identifier of values.keys())
200
286
  {
201
- if (!record.fields.has(key))
202
- throw new ul4.ArgumentError("update() get an unexpected keyword argument " + ul4._repr(key));
203
- record.fields.get(key).value = value;
287
+ if (!record.fields.has(identifier))
288
+ throw new ul4.ArgumentError(ul4._repr(this) + "() get an unexpected keyword argument " + ul4._repr(identifier));
204
289
  }
290
+ record._make_fields(true, values, null, null);
205
291
  }
206
292
  else if (ul4._isobject(values))
207
293
  {
208
- for (let key in values)
294
+ for (let identifier in values)
209
295
  {
210
- if (!record.fields.has(key))
211
- throw new ul4.ArgumentError("update() get an unexpected keyword argument " + ul4._repr(key));
212
- record.fields.get(key).value = values[key];
296
+ if (!record.fields.has(identifier))
297
+ throw new ul4.ArgumentError(ul4._repr(this) + "() get an unexpected keyword argument " + ul4._repr(identifier));
213
298
  }
299
+ record._make_fields(true, new Map(ul4._list(ul4.dicttype.items(values))), null, null);
214
300
  }
215
301
  else
216
302
  throw new ul4.TypeError("values must be an object or a Map");
217
303
  return record;
218
304
  }
219
305
 
220
- __getattr__(name)
306
+ [ul4.symbols.getattr](name)
221
307
  {
222
308
  if (name.startsWith("c_"))
223
309
  {
@@ -226,43 +312,83 @@ export class App extends Base
226
312
  return this.controls.get(name.substr(2));
227
313
  }
228
314
  else
229
- return super.__getattr__(name);
315
+ return super[ul4.symbols.getattr](name);
316
+ }
317
+
318
+ get active_view()
319
+ {
320
+ return this._active_view;
321
+ }
322
+
323
+ set active_view(view)
324
+ {
325
+ if (view === null)
326
+ ;
327
+ else if (typeof(view) === "string")
328
+ {
329
+ if (this.views !== null && this.views.has(view))
330
+ view = this.views.get(view);
331
+ else
332
+ throw new ul4.ValueError("View " + ul4._repr(view) + " not found!");
333
+ }
334
+ else if (view instanceof View)
335
+ {
336
+ if (view.app === this)
337
+ ;
338
+ else
339
+ throw new ul4.ValueError("View " + ul4._repr(view) + " belongs to the wrong app!");
340
+ }
341
+ else
342
+ {
343
+ throw new ul4.TypeError(ul4._repr(view) + " is not a view!");
344
+ }
345
+
346
+ this._active_view = view;
230
347
  }
231
348
  };
232
349
 
233
- App.prototype._ul4onattrs = ["id", "globals", "name", "description", "language", "startlink", "iconlarge", "iconsmall", "createdby", "controls", "records", "recordcount", "installation", "categories", "params", "views", "datamanagement_identifier", "basetable", "primarykey", "insertprocedure", "updateprocedure", "deleteprocedure", "templates", "createdat", "updatedat", "updatedby"];
234
- App.prototype._ul4attrs = ul4._makeset("id", "globals", "name", "description", "language", "startlink", "iconlarge", "iconsmall", "createdat", "createdby", "updatedat", "updatedby", "controls", "records", "recordcount", "installation", "categories", "params", "views", "datamanagement_identifier", "insert");
235
- ul4.expose(App.prototype.__call__, ["**values"], {"needsobject": true});
350
+
351
+ App.prototype._ul4onattrs = ["globals", "name", "description", "lang", "startlink", "iconlarge", "iconsmall", "createdby", "controls", "records", "recordcount", "installation", "categories", "params", "views", "datamanagement_identifier", "basetable", "primarykey", "insertprocedure", "updateprocedure", "deleteprocedure", "templates", "createdat", "updatedat", "updatedby", "superid", "favorite", "_active_view"];
352
+ App.prototype._ul4attrs = new Set(["id", "globals", "name", "description", "lang", "startlink", "iconlarge", "iconsmall", "createdat", "createdby", "updatedat", "updatedby", "controls", "records", "recordcount", "installation", "categories", "params", "views", "datamanagement_identifier", "insert", "favorite", "_active_view"]);
353
+ ul4.expose(App.prototype[ul4.symbols.call], ["**values"], {"needsobject": true});
236
354
  ul4.expose(App.prototype.insert, ["**values"], {"needsobject": true});
237
355
 
356
+
238
357
  export class View extends Base
239
358
  {
240
- __repr__()
359
+ static classdoc = "An input form for a LivingApps application";
360
+
361
+ [ul4.symbols.repr]()
241
362
  {
242
363
  return "<View id=" + ul4._repr(this.id) + " name=" + ul4._repr(this.name) + ">";
243
364
  }
244
365
  };
245
366
 
246
- View.prototype._ul4onattrs = ["id", "name", "app", "order", "width", "height", "start", "end"];
247
- View.prototype._ul4attrs = ul4._makeset("id", "name", "app", "order", "width", "height", "start", "end");
367
+ View.prototype._ul4onattrs = ["name", "combined_type", "app", "order", "width", "height", "start", "end", "controls", "layout_controls", "lang"];
368
+ View.prototype._ul4attrs = new Set(["id", "name", "combined_type", "app", "order", "width", "height", "start", "end", "controls", "layout_controls", "lang"]);
369
+
248
370
 
249
371
  export class DataSourceData extends Base
250
372
  {
251
- __repr__()
373
+ static classdoc = "The data resulting from a data source configuration";
374
+
375
+ [ul4.symbols.repr]()
252
376
  {
253
- return "<DataSource.Data id=" + ul4._repr(this.id) + " identifier=" + ul4._repr(this.identifier) + ">";
377
+ return "<DataSourceData id=" + ul4._repr(this.id) + " identifier=" + ul4._repr(this.identifier) + ">";
254
378
  }
255
379
  };
256
380
 
257
- DataSourceData.prototype._ul4onattrs = ["id", "identifier", "app", "apps"];
258
- DataSourceData.prototype._ul4attrs = ul4._makeset("id", "identifier", "app", "apps");
381
+ DataSourceData.prototype._ul4onattrs = ["identifier", "app", "apps"];
382
+ DataSourceData.prototype._ul4attrs = new Set(["id", "identifier", "app", "apps"]);
383
+
259
384
 
260
385
  export class Record extends Base
261
386
  {
262
- constructor(app)
387
+ static classdoc = "A record of a LivingApp application";
388
+
389
+ constructor(id, app)
263
390
  {
264
- super();
265
- this.id = null;
391
+ super(id);
266
392
  this.app = app;
267
393
  this.createdat = null;
268
394
  this.createdby = null;
@@ -275,15 +401,47 @@ export class Record extends Base
275
401
  this.children = new Map();
276
402
  this.attachments = null;
277
403
  this.errors = [];
404
+ this._sparsefielderrors = null;
405
+ this._sparsefieldlookupdata = null;
278
406
  this._is_deleted = false;
407
+ this._is_new = true;
408
+ }
409
+
410
+ _make_fields(defaults, values, errors, lookupdata)
411
+ {
412
+ let fields = new Map();
413
+
414
+ for (let control of this.app.controls.values())
415
+ {
416
+ let identifier = control.identifier;
417
+ let value = null;
418
+ if (values !== null)
419
+ {
420
+ if (defaults && !values.has(identifier))
421
+ value = control.default;
422
+ else if (values.has(identifier))
423
+ value = values.get(identifier);
424
+ }
425
+ let field = new control.fieldtype(control, this, value);
426
+ fields.set(identifier, field);
427
+ if (errors !== null && errors.has(identifier))
428
+ field.errors = errors.get(identifier);
429
+ if (lookupdata !== null && lookupdata.has(identifier))
430
+ field.lookupdata = lookupdata.get(identifier);
431
+ }
432
+
433
+ this._fields = fields;
434
+ this._sparsevalues = null;
435
+ this._sparsefielderrors = null;
436
+ this._sparselookupdata = null;
279
437
  }
280
438
 
281
- __repr__()
439
+ [ul4.symbols.repr]()
282
440
  {
283
441
  let v = ["<Record id=", ul4._repr(this.id)];
284
442
  for (let field of this.fields.values())
285
443
  {
286
- if (field.control.priority)
444
+ if (field.control.priority && !field.is_empty())
287
445
  {
288
446
  v.push(" v_");
289
447
  v.push(field.control.identifier);
@@ -291,21 +449,32 @@ export class Record extends Base
291
449
  v.push(ul4._repr(field.value)); // FIXME: This might lead to infinite recursions
292
450
  }
293
451
  }
294
- v.push(">")
452
+ v.push(" state=");
453
+ v.push(ul4._repr(this.state));
454
+ v.push(">");
295
455
  return v.join("");
296
456
  }
297
457
 
458
+ get state()
459
+ {
460
+ if (this._is_deleted)
461
+ return "deleted";
462
+ else if (this._is_new)
463
+ return "new";
464
+ else if (this.is_dirty())
465
+ return "dirty";
466
+ else
467
+ return "saved";
468
+ }
469
+
298
470
  get values()
299
471
  {
300
472
  if (this._values === null)
301
473
  {
302
- this._values = ul4._havemap ? new Map() : {};
303
- for (let [identifier, control] of this.app.controls.entries())
474
+ this._values = new Map();
475
+ for (let field of this.fields.values())
304
476
  {
305
- let fieldvalue = this._sparsevalues.get(identifier);
306
- if (typeof(fieldvalue) === "undefined")
307
- fieldvalue = null;
308
- this._values.set(identifier, fieldvalue);
477
+ this._values.set(field.control.identifier, field.value);
309
478
  }
310
479
  }
311
480
  return this._values;
@@ -314,14 +483,7 @@ export class Record extends Base
314
483
  get fields()
315
484
  {
316
485
  if (this._fields === null)
317
- {
318
- this._fields = ul4._havemap ? new Map() : {};
319
- for (let [identifier, value] of this.values.entries())
320
- {
321
- let field = new Field(this.app.controls.get(identifier), this, value);
322
- this._fields.set(identifier, field);
323
- }
324
- }
486
+ this._make_fields(false, this._sparsevalues, this._sparsefielderrors, this._sparsefieldlookupdata)
325
487
  return this._fields;
326
488
  }
327
489
 
@@ -351,7 +513,9 @@ export class Record extends Base
351
513
 
352
514
  delete()
353
515
  {
354
- return this.app.globals.handler.delete(this);
516
+ let result = this.app.globals.handler.delete(this);
517
+ this._is_deleted = true;
518
+ return result;
355
519
  }
356
520
 
357
521
  save()
@@ -399,62 +563,898 @@ export class Record extends Base
399
563
  return true;
400
564
  }
401
565
 
402
- _dumpUL4ONAttr(name)
566
+ ul4onload(decoder)
567
+ {
568
+ super.ul4onload(decoder);
569
+ this._is_new = false;
570
+ }
571
+
572
+ _dumpUL4ONAttr(name)
573
+ {
574
+ if (name === "values")
575
+ return this._sparsevalues;
576
+ else
577
+ return this[name];
578
+ }
579
+
580
+ _loadUL4ONAttr(name, value)
581
+ {
582
+ if (name === "values")
583
+ {
584
+ this._sparsevalues = value;
585
+ this._values = null;
586
+ this._fields = null;
587
+ }
588
+ else
589
+ this[name] = value;
590
+ }
591
+
592
+ [ul4.symbols.getattr](name)
593
+ {
594
+ if (name.startsWith("c_"))
595
+ return this.children.get(name.substr(2))
596
+ else if (name.startsWith("f_"))
597
+ return this.fields.get(name.substr(2))
598
+ else if (name.startsWith("v_"))
599
+ return this.values.get(name.substr(2))
600
+ else
601
+ return this[name];
602
+ }
603
+
604
+ [ul4.symbols.setattr](name, value)
605
+ {
606
+ if (name.startsWith("c_"))
607
+ this.children[name.substr(2)] = value;
608
+ else if (name.startsWith("v_"))
609
+ this.fields.get(name.substr(2)).value = value;
610
+ else
611
+ throw new ul4.AttributeError(this, name);
612
+ }
613
+ };
614
+
615
+ Record.prototype._ul4onattrs = ["app", "createdat", "createdby", "updatedat", "updatedby", "updatecount", "_sparsevalues", "attachments", "children", "errors", "_sparsefielderrors", "_sparsefieldlookupdata"];
616
+ Record.prototype._ul4attrs = new Set(["id", "app", "createdat", "createdby", "updatedat", "updatedby", "updatecount", "values", "attachments", "children", "errors"]);
617
+ ul4.expose(Record.prototype.is_dirty, []);
618
+ ul4.expose(Record.prototype.has_errors, []);
619
+ ul4.expose(Record.prototype.delete, []);
620
+ ul4.expose(Record.prototype.save, []);
621
+ ul4.expose(Record.prototype.update, ["**values"], {"needsobject": true});
622
+
623
+
624
+ export class Field extends Base
625
+ {
626
+ static classdoc = "Holds the value of a field of a record (and related information)";
627
+
628
+ constructor(control, record, value)
629
+ {
630
+ super(null);
631
+ this.control = control;
632
+ this.record = record;
633
+ this._label = null;
634
+ this._lookupdate = null;
635
+ this.errors = [];
636
+ this._value = null;
637
+ this.value = value;
638
+ this._dirty = false;
639
+ }
640
+
641
+ get value()
642
+ {
643
+ return this._value;
644
+ }
645
+
646
+ set value(value)
647
+ {
648
+ let oldvalue = this._value;
649
+
650
+ let change = {value: value, errors: []};
651
+ this._validate(change);
652
+
653
+ if (ul4._ne(oldvalue, change.value))
654
+ {
655
+ this.record.values.set(this.control.identifier, change.value);
656
+ this._value = change.value;
657
+ this._dirty = true;
658
+ }
659
+ this.errors = change.errors;
660
+ }
661
+
662
+ _validate(change)
663
+ {
664
+ // Do nothing, i.e. accept the value unchanged and without errors
665
+ }
666
+
667
+ get label()
668
+ {
669
+ if (this._label !== null)
670
+ return this._label;
671
+ return this.control.label;
672
+ }
673
+
674
+ set label(value)
675
+ {
676
+ this._label = value;
677
+ }
678
+
679
+ get globals()
680
+ {
681
+ return this.control.globals;
682
+ }
683
+
684
+ is_empty()
685
+ {
686
+ return this._value === null || (ul4._islist(this._value) && this._value.length === 0);
687
+ }
688
+
689
+ is_dirty()
690
+ {
691
+ return this._dirty;
692
+ }
693
+
694
+ has_errors()
695
+ {
696
+ return this.errors.length !== 0;
697
+ }
698
+
699
+ search(searchvalue)
700
+ {
701
+ return this.control.search(this.value, searchvalue);
702
+ }
703
+
704
+ msg_field_required(value)
705
+ {
706
+ switch (this.globals.lang)
707
+ {
708
+ case "de":
709
+ return `"${this.label}" wird benötigt!`;
710
+ default:
711
+ return `"${this.label}" is required!`;
712
+ }
713
+ }
714
+
715
+ msg_field_wrong_type(value)
716
+ {
717
+ return `"${this.label}" doesn't support the type ${ul4._type(value).name}!`;
718
+ }
719
+
720
+ msg_bool_truerequired(value)
721
+ {
722
+ return `"${this.label}" only accepts "Yes"!`;
723
+ }
724
+
725
+ msg_string_tooshort(value)
726
+ {
727
+ return `"${this.label}" is too short. Tou must use at least ${this.control.minlength} characters!`;
728
+ }
729
+
730
+ msg_string_toolong(value)
731
+ {
732
+ return `"${this.label}" is too long. You may use at most ${this.control.maxlength} characters!`;
733
+ }
734
+
735
+ msg_email_format(value)
736
+ {
737
+ return `"${this.label}" must be a valid email address!`;
738
+ }
739
+
740
+ msg_url_format(value)
741
+ {
742
+ return `"${this.label}" must be a valid URL!`;
743
+ }
744
+
745
+ msg_tel_format(value)
746
+ {
747
+ return `"${this.label}" must be a valid phone number!`;
748
+ }
749
+
750
+ msg_date_format(value)
751
+ {
752
+ return `"${this.label}" doesn't support this date format!`;
753
+ }
754
+
755
+ msg_lookup_wrongitem(value)
756
+ {
757
+ return `The option ${ul4._repr(value)} for "${this.label}" doesn't belong to this lookup!`;
758
+ }
759
+
760
+ msg_lookup_unknownkey(value)
761
+ {
762
+ return `The option ${ul4._repr(value)} for "${this.label}" is unknown!`;
763
+ }
764
+
765
+ msg_applookup_wrongrecord(value)
766
+ {
767
+ return `The record ${ul4._repr(value)} for "${this.label}" doesn't belong to the correct app!`;
768
+ }
769
+
770
+ msg_applookup_unknownkey(value)
771
+ {
772
+ return `The record ${ul4._repr(value)} for "${this.label}" can't be found!`;
773
+ }
774
+
775
+ [ul4.symbols.repr]()
776
+ {
777
+ let s = "<Field identifier=";
778
+ s += ul4._repr(this.control.identifier)
779
+ if (this._dirty)
780
+ s += " is_dirty()=True";
781
+ if (this.errors.length > 0)
782
+ s += " has_errors()=True";
783
+ s += ">"
784
+ return s;
785
+ }
786
+ };
787
+
788
+ Field.prototype._ul4onattrs = ["control", "record", "label", "value", "errors", "enabled", "writable", "visible"];
789
+ Field.prototype._ul4attrs = new Set(["control", "record", "label", "value", "errors", "enabled", "writable", "visible"]);
790
+
791
+
792
+ export class BoolField extends Field
793
+ {
794
+ static classdoc = "Holds the value of a bool field of a record (and related information)";
795
+
796
+ _validate(change)
797
+ {
798
+ if (change.value === null)
799
+ {
800
+ if (this.control.required)
801
+ change.errors.push(this.msg_field_required(change.value));
802
+ }
803
+ else if (typeof(change.value) === "boolean")
804
+ {
805
+ if (this.control.required && !change.value)
806
+ change.errors.push(this.msg_bool_truerequired(change.value));
807
+ }
808
+ else
809
+ {
810
+ change.errors.push(this.msg_field_wrong_type(change.value));
811
+ change.value = null;
812
+ }
813
+ }
814
+ };
815
+
816
+
817
+ export class IntField extends Field
818
+ {
819
+ static classdoc = "Holds the value of an int field of a record (and related information)";
820
+
821
+ _validate(change)
822
+ {
823
+ if (change.value === null)
824
+ {
825
+ if (this.control.required)
826
+ change.errors.push(this.msg_field_required(change.value));
827
+ }
828
+ else if (typeof(change.value) !== "number" || Math.round(change.value) !== change.value)
829
+ {
830
+ change.errors.push(this.msg_field_wrong_type(change.value));
831
+ }
832
+ }
833
+ };
834
+
835
+
836
+ export class NumberField extends Field
837
+ {
838
+ static classdoc = "Holds the value of a number field of a record (and related information)";
839
+
840
+ _validate(change)
841
+ {
842
+ if (change.value === null || change.value === "")
843
+ {
844
+ if (this.control.required)
845
+ change.errors.push(this.msg_field_required(change.value));
846
+ change.value = null;
847
+ }
848
+ else if (typeof(change.value) !== "number")
849
+ {
850
+ change.errors.push(this.msg_field_wrong_type(change.value));
851
+ }
852
+ }
853
+ };
854
+
855
+
856
+ export class StringField extends Field
857
+ {
858
+ static classdoc = "Holds the value of a string field of a record (and related information)";
859
+
860
+ _validate(change)
861
+ {
862
+ if (change.value === null || change.value === "")
863
+ {
864
+ if (this.control.required)
865
+ change.errors.push(this.msg_field_required(change.value));
866
+ change.value = null;
867
+ }
868
+ else if (typeof(change.value) === "string")
869
+ {
870
+ let minlength = this.control.minlength;
871
+ let maxlength = this.control.maxlength;
872
+ if (minlength !== null && change.value.length < minlength)
873
+ change.errors.push(this.msg_string_tooshort(change.value))
874
+ if (maxlength !== null && change.value.length > maxlength)
875
+ change.errors.push(this.msg_string_toolong(change.value))
876
+ }
877
+ else
878
+ {
879
+ change.errors.push(this.msg_field_wrong_type(change.value));
880
+ change.value = null;
881
+ }
882
+ }
883
+ };
884
+
885
+
886
+ export class EmailField extends StringField
887
+ {
888
+ static classdoc = "Holds the value of a string/email field of a record (and related information)";
889
+
890
+ _validate(change)
891
+ {
892
+ if (typeof(change.value) === "string" && change.value.length > 0)
893
+ {
894
+ const re = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/i;
895
+ if (!re.test(change.value))
896
+ change.errors.push(this.msg_email_format(change.value));
897
+ }
898
+ super._validate(change);
899
+ }
900
+ };
901
+
902
+
903
+ export class URLField extends StringField
904
+ {
905
+ static classdoc = "Holds the value of a string/url field of a record (and related information)";
906
+
907
+ _validate(change)
908
+ {
909
+ if (typeof(change.value) === "string" && change.value.length > 0)
910
+ {
911
+ let url = null;
912
+ try
913
+ {
914
+ url = new URL(change.value);
915
+ }
916
+ catch (exc)
917
+ {
918
+ change.errors.push(this.msg_url_format(change.value));
919
+ }
920
+ if (url !== null && url.protocol !== "http:" && url.protocol !== "https:")
921
+ {
922
+ change.errors.push(this.msg_url_format(change.value));
923
+ }
924
+ }
925
+ super._validate(change);
926
+ }
927
+ };
928
+
929
+
930
+ export class TelField extends StringField
931
+ {
932
+ static classdoc = "Holds the value of a string/tel field of a record (and related information)";
933
+
934
+ _validate(change)
935
+ {
936
+ if (typeof(change.value) === "string" && change.value.length > 0)
937
+ {
938
+ const re = /^\\+?[0-9 /()-]+$/;
939
+ if (!re.test(change.value))
940
+ change.errors.push(this.msg_tel_format(change.value));
941
+ }
942
+ super._validate(change);
943
+ }
944
+ };
945
+
946
+
947
+ export class TextAreaField extends StringField
948
+ {
949
+ static classdoc = "Holds the value of a string/textarea field of a record (and related information)";
950
+ };
951
+
952
+
953
+ export class HTMLField extends StringField
954
+ {
955
+ static classdoc = "Holds the value of a string/html field of a record (and related information)";
956
+ };
957
+
958
+
959
+ export class GeoField extends Field
960
+ {
961
+ static classdoc = "Holds the value of a geo field of a record (and related information)";
962
+
963
+ _validate(change)
964
+ {
965
+ if (change.value === null || change.value === "")
966
+ {
967
+ if (this.control.required)
968
+ change.errors.push(this.msg_field_required(change.value));
969
+ change.value = null;
970
+ }
971
+ else if (!(change.value instanceof Geo))
972
+ {
973
+ change.errors.push(this.msg_field_wrong_type(change.value));
974
+ change.value = null;
975
+ }
976
+ }
977
+ };
978
+
979
+
980
+ export class FileField extends Field
981
+ {
982
+ static classdoc = "Holds the value of a file field of a record (and related information)";
983
+
984
+ _validate(change)
985
+ {
986
+ if (change.value === null || change.value === "")
987
+ {
988
+ if (this.control.required)
989
+ change.errors.push(this.msg_field_required(change.value));
990
+ change.value = null;
991
+ }
992
+ else if (!(change.value instanceof File))
993
+ {
994
+ change.errors.push(this.msg_field_wrong_type(change.value));
995
+ change.value = null;
996
+ }
997
+ }
998
+ };
999
+
1000
+
1001
+ export class FileSignatureField extends FileField
1002
+ {
1003
+ static classdoc = "Holds the value of a file/signature field of a record (and related information)";
1004
+
1005
+ _validate(change)
1006
+ {
1007
+ // FIXME: Implement handling of data URLs.
1008
+ super._validate(change);
1009
+ }
1010
+ };
1011
+
1012
+
1013
+ export class DateFieldBase extends Field
1014
+ {
1015
+ static classdoc = "Holds the value of a date field of a record (and related information)";
1016
+
1017
+ _validate(change)
1018
+ {
1019
+ if (change.value === null || change.value === "")
1020
+ {
1021
+ if (this.control.required)
1022
+ change.errors.push(this.msg_field_required(change.value));
1023
+ change.value = null;
1024
+ }
1025
+ else if (change.value instanceof Date)
1026
+ {
1027
+ change.value = this._convert(change.value);
1028
+ }
1029
+ else if (change.value instanceof ul4.Date_)
1030
+ {
1031
+ change.value = this._convert(change.value._date);
1032
+ }
1033
+ else if (typeof(change.value) === "string")
1034
+ {
1035
+ let value = null;
1036
+ if (change.value.length === 10)
1037
+ value = new Date(change.value + "T00:00:00");
1038
+ else if (change.value.length === 16)
1039
+ value = new Date(change.value + ":00");
1040
+ else if (change.value.length === 19)
1041
+ value = new Date(change.value);
1042
+
1043
+ if (value === null || Number.isNaN(value.getYear()))
1044
+ {
1045
+ change.errors.push(this.msg_date_format(change.value));
1046
+ // Keep original invalid string
1047
+ }
1048
+ else
1049
+ {
1050
+ change.value = this._convert(value);
1051
+ }
1052
+ }
1053
+ else
1054
+ {
1055
+ change.errors.push(this.msg_field_wrong_type(change.value));
1056
+ change.value = null;
1057
+ }
1058
+ }
1059
+ };
1060
+
1061
+
1062
+ export class DateField extends DateFieldBase
1063
+ {
1064
+ static classdoc = "Holds the value of a date/date field of a record (and related information)";
1065
+
1066
+ _convert(date)
1067
+ {
1068
+ return new ul4.Date_(date.getFullYear(), date.getMonth()+1, date.getDate());
1069
+ }
1070
+ };
1071
+
1072
+
1073
+ export class DatetimeMinuteField extends DateFieldBase
1074
+ {
1075
+ static classdoc = "Holds the value of a date/datetimeminute field of a record (and related information)";
1076
+
1077
+ _convert(date)
1078
+ {
1079
+ // Get rid of seconds and milliseconds
1080
+ date.setSeconds(0);
1081
+ date.setMilliseconds(0);
1082
+ return date;
1083
+ }
1084
+ };
1085
+
1086
+
1087
+ export class DatetimeSecondField extends DateFieldBase
1088
+ {
1089
+ static classdoc = "Holds the value of a date/datetimesecond field of a record (and related information)";
1090
+
1091
+ _convert(date)
1092
+ {
1093
+ // Get rid of milliseconds
1094
+ date.setMilliseconds(0);
1095
+ return date;
1096
+ }
1097
+ };
1098
+
1099
+
1100
+ export class LookupFieldBase extends Field
1101
+ {
1102
+ static classdoc = "Base type of LookupField and MultipleLookupField";
1103
+
1104
+ get lookupdata()
1105
+ {
1106
+ if (this._lookupdate !== null)
1107
+ return this._lookupdate;
1108
+ else
1109
+ return this.control.lookupdata;
1110
+ }
1111
+
1112
+ set lookupdata(value)
1113
+ {
1114
+ this._lookupdate = value;
1115
+ }
1116
+
1117
+ _find_lookupitem(change)
1118
+ {
1119
+ let v = change.value;
1120
+ if (typeof(v) === "string")
1121
+ {
1122
+ // If `lookupdata`} is `null`, this is probably an interface,
1123
+ // so we can't validate anything (note that interfaces will however go
1124
+ // away in the future)
1125
+ if (this.lookupdata === null)
1126
+ return;
1127
+ let lookupitem = this.lookupdata.get(v);
1128
+ if (lookupitem === undefined)
1129
+ {
1130
+ // If the lookup control is auto expandable,
1131
+ // the user may have given us a label
1132
+ if (this.control.autoexpandable)
1133
+ {
1134
+ // Search for the LookupItem with that label
1135
+ for (let search_lookupitem of this.lookupdata.values())
1136
+ {
1137
+ if (v === search_lookupitem.label)
1138
+ {
1139
+ change.value = search_lookupitem;
1140
+ return;
1141
+ }
1142
+ }
1143
+ // If we couldn't find the label, keep the label as it is.
1144
+ }
1145
+ else
1146
+ {
1147
+ change.errors.push(this.msg_lookup_unknownkey(v))
1148
+ change.value = null;
1149
+ }
1150
+ }
1151
+ else
1152
+ {
1153
+ change.value = lookupitem;
1154
+ }
1155
+ }
1156
+ else if (v instanceof LookupItem)
1157
+ {
1158
+ let value = this.control.lookupdata.get(v.key);
1159
+ if (value !== v)
1160
+ {
1161
+ change.errors.push(this.msg_lookup_wrongitem(v));
1162
+ change.value = null;
1163
+ }
1164
+ }
1165
+ else
1166
+ {
1167
+ change.errors.push(this.msg_field_wrong_type(v));
1168
+ change.value = null;
1169
+ }
1170
+ }
1171
+ };
1172
+
1173
+
1174
+ export class LookupField extends LookupFieldBase
1175
+ {
1176
+ static classdoc = "Holds the value of a lookup field of a record (and related information)";
1177
+
1178
+ _validate(change)
1179
+ {
1180
+ let v = change.value;
1181
+ if (v === null || v === "" || this.control.nonekey === v)
1182
+ {
1183
+ if (this.control.required)
1184
+ change.errors.push(this.msg_field_required(v));
1185
+ change.value = null;
1186
+ }
1187
+ else
1188
+ {
1189
+ this._find_lookupitem(change);
1190
+ }
1191
+ }
1192
+ };
1193
+
1194
+
1195
+ export class MultipleLookupField extends LookupFieldBase
1196
+ {
1197
+ static classdoc = "Holds the value of a multiple lookup field of a record (and related information)";
1198
+
1199
+ _validate(change)
1200
+ {
1201
+ let v = change.value;
1202
+ let islist = (Object.prototype.toString.call(v) === "[object Array]");
1203
+
1204
+ if (v === null || v === "" || this.control.nonekey === v || (islist && v.length === 0))
1205
+ {
1206
+ if (this.control.required)
1207
+ change.errors.push(this.msg_field_required(v));
1208
+ change.value = [];
1209
+ }
1210
+ else if (islist)
1211
+ {
1212
+ let newvalue = [];
1213
+ for (let item of v)
1214
+ {
1215
+ let singlechange = {value: item, errors: []};
1216
+ this._find_lookupitem(singlechange);
1217
+ if (singlechange.errors.length > 0)
1218
+ change.errors = [...change.errors, ...singlechange.errors];
1219
+ else
1220
+ newvalue.push(singlechange.value);
1221
+ }
1222
+ change.value = newvalue;
1223
+ }
1224
+ else
1225
+ {
1226
+ this._find_lookupitem(change);
1227
+ if (change.errors.length > 0)
1228
+ change.value = [];
1229
+ else
1230
+ change.value = [change.value];
1231
+ }
1232
+ }
1233
+ };
1234
+
1235
+
1236
+ export class AppLookupFieldBase extends Field
1237
+ {
1238
+ static classdoc = "Base type of AppLookupField and MultipleAppLookupField";
1239
+
1240
+ get lookupdata()
1241
+ {
1242
+ if (this._lookupdata !== null)
1243
+ return this._lookupdata;
1244
+ else
1245
+ {
1246
+ if (this.control.lookup_app.records !== null)
1247
+ return this.control.lookup_app.records;
1248
+ }
1249
+ return new Map();
1250
+ }
1251
+
1252
+ set lookupdata(value)
1253
+ {
1254
+ this._lookupdata = value;
1255
+ }
1256
+
1257
+ _find_record(change)
1258
+ {
1259
+ let v = change.value;
1260
+ if (typeof(v) === "string")
1261
+ {
1262
+ let record = this.lookupdata.get(v);
1263
+ if (record === undefined)
1264
+ {
1265
+ change.errors.push(this.msg_applookup_unknownkey(v))
1266
+ change.value = null;
1267
+ }
1268
+ else
1269
+ {
1270
+ change.value = record;
1271
+ }
1272
+ }
1273
+ else if (v instanceof Record)
1274
+ {
1275
+ if (v.app !== this.control.lookup_app)
1276
+ {
1277
+ change.errors.push(this.msg_applookup_wrongrecord(v));
1278
+ change.value = null;
1279
+ }
1280
+ }
1281
+ else
1282
+ {
1283
+ change.errors.push(this.msg_field_wrong_type(v));
1284
+ change.value = null;
1285
+ }
1286
+ }
1287
+ };
1288
+
1289
+
1290
+ export class AppLookupField extends AppLookupFieldBase
1291
+ {
1292
+ static classdoc = "Holds the value of a applookup field of a record (and related information)";
1293
+
1294
+ _validate(change)
1295
+ {
1296
+ this._find_record(change);
1297
+ }
1298
+ };
1299
+
1300
+
1301
+ export class MultipleAppLookupField extends AppLookupFieldBase
1302
+ {
1303
+ static classdoc = "Holds the value of a multiple applookup field of a record (and related information)";
1304
+
1305
+ _validate(change)
1306
+ {
1307
+ let v = change.value;
1308
+ let islist = (Object.prototype.toString.call(v) === "[object Array]");
1309
+
1310
+ if (v === null || v === "" || this.control.nonekey === v || (islist && v.length === 0))
1311
+ {
1312
+ if (this.control.required)
1313
+ change.errors.push(this.msg_field_required(v));
1314
+ change.value = [];
1315
+ }
1316
+ else if (islist)
1317
+ {
1318
+ let newvalue = [];
1319
+ for (let item of v)
1320
+ {
1321
+ let singlechange = {value: item, errors: []};
1322
+ this._find_record(singlechange);
1323
+ if (singlechange.errors.length > 0)
1324
+ change.errors = [...change.errors, ...singlechange.errors];
1325
+ else
1326
+ newvalue.push(singlechange.value);
1327
+ }
1328
+ change.value = newvalue;
1329
+ }
1330
+ else
1331
+ {
1332
+ this._find_record(change);
1333
+ if (change.errors.length > 0)
1334
+ change.value = [];
1335
+ else
1336
+ change.value = [change.value];
1337
+ }
1338
+ }
1339
+ };
1340
+
1341
+
1342
+ export class Control extends Base
1343
+ {
1344
+ [ul4.symbols.repr]()
1345
+ {
1346
+ return "<" + this.constructor.name + " id=" + ul4._repr(this.id) + " identifier=" + ul4._repr(this.identifier) + ">";
1347
+ }
1348
+
1349
+ get globals()
1350
+ {
1351
+ return this.app.globals;
1352
+ }
1353
+
1354
+ get fulltype()
1355
+ {
1356
+ return this.subtype ? this.type + "/" + this.subtype : this.type;
1357
+ }
1358
+
1359
+ get label()
1360
+ {
1361
+ let view_control = this._view_control();
1362
+ if (view_control === null)
1363
+ return this._label;
1364
+ let label = view_control.label;
1365
+ if (label === null)
1366
+ return this._label;
1367
+ return label;
1368
+ }
1369
+
1370
+ set label(value)
1371
+ {
1372
+ this._label = value;
1373
+ }
1374
+
1375
+ get default()
1376
+ {
1377
+ return null;
1378
+ }
1379
+
1380
+ get top()
1381
+ {
1382
+ let view_control = this._view_control();
1383
+ return view_control !== null ? view_control.top : null;
1384
+ }
1385
+
1386
+ get left()
1387
+ {
1388
+ let view_control = this._view_control();
1389
+ return view_control !== null ? view_control.left : null;
1390
+ }
1391
+
1392
+ get width()
1393
+ {
1394
+ let view_control = this._view_control();
1395
+ return view_control !== null ? view_control.width : null;
1396
+ }
1397
+
1398
+ get height()
1399
+ {
1400
+ let view_control = this._view_control();
1401
+ return view_control !== null ? view_control.height : null;
1402
+ }
1403
+
1404
+ get liveupdate()
1405
+ {
1406
+ let view_control = this._view_control();
1407
+ return view_control !== null ? view_control.liveupdate : false;
1408
+ }
1409
+
1410
+ get tabindex()
1411
+ {
1412
+ let view_control = this._view_control();
1413
+ return view_control !== null ? view_control.tabindex : null;
1414
+ }
1415
+
1416
+ get required()
403
1417
  {
404
- if (name === "values")
405
- return this._sparsevalues;
406
- else
407
- return this[name];
1418
+ let view_control = this._view_control();
1419
+ return view_control !== null ? view_control.required : false;
408
1420
  }
409
1421
 
410
- _loadUL4ONAttr(name, value)
1422
+ get mode()
411
1423
  {
412
- if (name === "values")
413
- {
414
- this._sparsevalues = value;
415
- this._values = null;
416
- this._fields = null;
417
- }
418
- else
419
- this[name] = value;
1424
+ let view_control = this._view_control();
1425
+ return view_control !== null ? view_control.mode : "edit";
420
1426
  }
421
1427
 
422
- __getattr__(name)
1428
+ get labelpos()
423
1429
  {
424
- if (name.startsWith("c_"))
425
- return this.children.get(name.substr(2))
426
- else if (name.startsWith("f_"))
427
- return this.fields.get(name.substr(2))
428
- else if (name.startsWith("v_"))
429
- return this.values.get(name.substr(2))
430
- else
431
- return this[name];
1430
+ let view_control = this._view_control();
1431
+ return view_control !== null ? view_control.labelpos : "left";
432
1432
  }
433
1433
 
434
- __setattr__(name, value)
1434
+ get labelwidth()
435
1435
  {
436
- if (name.startsWith("c_"))
437
- this.children[name.substr(2)] = value;
438
- else if (name.startsWith("v_"))
439
- this.fields.get(name.substr(2)).value = value;
440
- else
441
- throw new ul4.AttributeError(this, name);
1436
+ let view_control = this._view_control();
1437
+ return view_control !== null ? view_control.labelwidth : null;
442
1438
  }
443
- };
444
1439
 
445
- Record.prototype._ul4onattrs = ["id", "app", "createdat", "createdby", "updatedat", "updatedby", "updatecount", "values", "attachments", "children"];
446
- Record.prototype._ul4attrs = ul4._makeset("id", "app", "createdat", "createdby", "updatedat", "updatedby", "updatecount", "values", "attachments", "children");
447
- ul4.expose(Record.prototype.is_dirty, []);
448
- ul4.expose(Record.prototype.has_errors, []);
449
- ul4.expose(Record.prototype.delete, []);
450
- ul4.expose(Record.prototype.save, []);
451
- ul4.expose(Record.prototype.update, ["**values"], {"needsobject": true});
1440
+ get autoalign()
1441
+ {
1442
+ let view_control = this._view_control();
1443
+ return view_control !== null ? view_control.autoalign : true;
1444
+ }
452
1445
 
453
- export class Control extends Base
454
- {
455
- __repr__()
1446
+ get in_active_view()
456
1447
  {
457
- return "<" + this.__type__ + " id=" + ul4._repr(this.id) + " identifier=" + ul4._repr(this.identifier) + ">";
1448
+ let view_control = this._view_control();
1449
+ return view_control !== null;
1450
+ }
1451
+
1452
+ _view_control()
1453
+ {
1454
+ if (this.app._active_view === null)
1455
+ return null;
1456
+ let view_control = this.app._active_view.controls.get(this.identifier);
1457
+ return view_control ?? null;
458
1458
  }
459
1459
 
460
1460
  _logsearch(value, search)
@@ -474,11 +1474,14 @@ export class Control extends Base
474
1474
 
475
1475
  Control.prototype.type = null;
476
1476
  Control.prototype.subtype = null;
477
- Control.prototype._ul4onattrs = ["id", "identifier", "field", "app", "label", "priority", "order", "default", "ininsertprocedure", "inupdateprocedure"];
478
- Control.prototype._ul4attrs = ul4._makeset("id", "identifier", "field", "app", "label", "priority", "order", "default", "ininsertprocedure", "inupdateprocedure");
1477
+ Control.prototype._ul4onattrs = ["identifier", "fieldname", "app", "_label", "priority", "order", "ininsertprocedure", "inupdateprocedure"];
1478
+ Control.prototype._ul4attrs = new Set(["id", "identifier", "fieldname", "app", "priority", "order", "ininsertprocedure", "inupdateprocedure", "fulltype", "label", "top", "left", "width", "height", "liveupdate", "tabindex", "required", "mode", "labelpos", "autoalign", "in_active_view"]);
1479
+
479
1480
 
480
1481
  export class BoolControl extends Control
481
1482
  {
1483
+ static classdoc = "A LivingApps boolean field (type 'bool')";
1484
+
482
1485
  // ``search`` must by ``null``, ``false`` or ``true``
483
1486
  search(value, search)
484
1487
  {
@@ -491,9 +1494,13 @@ export class BoolControl extends Control
491
1494
  };
492
1495
 
493
1496
  BoolControl.prototype.type = "bool";
1497
+ BoolControl.prototype.fieldtype = BoolField;
1498
+
494
1499
 
495
1500
  export class IntControl extends Control
496
1501
  {
1502
+ static classdoc = "A LivingApps integer field (type 'int')";
1503
+
497
1504
  // ``search.value`` must by ``null`` or an integer
498
1505
  search(value, search)
499
1506
  {
@@ -506,9 +1513,29 @@ export class IntControl extends Control
506
1513
  };
507
1514
 
508
1515
  IntControl.prototype.type = "int";
1516
+ IntControl.prototype.fieldtype = IntField;
1517
+
509
1518
 
510
- export class NumberControl extends Control
1519
+ class PlaceholderControl extends Control
511
1520
  {
1521
+ static classdoc = "A LivingApps multiplelookup field (type 'multiplelookup/select')";
1522
+
1523
+ get placeholder()
1524
+ {
1525
+ let view_control = this._view_control();
1526
+ if (view_control === null)
1527
+ return null;
1528
+ return view_control.placeholder;
1529
+ }
1530
+ };
1531
+
1532
+ PlaceholderControl.prototype._ul4attrs = new Set([...Control.prototype._ul4attrs, "placeholder"]);
1533
+
1534
+
1535
+ export class NumberControl extends PlaceholderControl
1536
+ {
1537
+ static classdoc = "A LivingApps number field (type 'number')";
1538
+
512
1539
  // ``search.value`` must by ``null`` or an integer
513
1540
  search(value, search)
514
1541
  {
@@ -527,9 +1554,103 @@ export class NumberControl extends Control
527
1554
  };
528
1555
 
529
1556
  NumberControl.prototype.type = "number";
1557
+ NumberControl.prototype.fieldtype = NumberField;
1558
+
530
1559
 
531
- export class StringControl extends Control
1560
+ export class StringControl extends PlaceholderControl
532
1561
  {
1562
+ _user_placeholder(user, placeholder)
1563
+ {
1564
+ if (placeholder === null)
1565
+ return null;
1566
+ switch (placeholder)
1567
+ {
1568
+ case "{gender}":
1569
+ return user !== null ? user.gender : null;
1570
+ case "{title}":
1571
+ return user !== null ? user.title : null;
1572
+ case "{firstname}":
1573
+ return user !== null ? user.firstname : null;
1574
+ case "{surname}":
1575
+ return user !== null ? user.surname : null;
1576
+ case "{account}":
1577
+ return user !== null ? user.email : null;
1578
+ case "{streetname}":
1579
+ return user !== null ? user.streetname : null;
1580
+ case "{streetnumber}":
1581
+ return user !== null ? user.streetnumber : null;
1582
+ case "{street}":
1583
+ if (user === null)
1584
+ return null;
1585
+ else if (user.street === null)
1586
+ return user.streetnumber;
1587
+ else if (user.streetnumber === null)
1588
+ return user.street;
1589
+ else
1590
+ return user.street + " " + user.streetnumber;
1591
+ case "{zip}":
1592
+ return user !== null ? user.zip : null;
1593
+ case "{phone}":
1594
+ return user !== null ? user.phone : null;
1595
+ case "{fax}":
1596
+ return user !== null ? user.fax : null;
1597
+ case "{company}":
1598
+ return user !== null ? user.company : null;
1599
+ case "{city}":
1600
+ return user !== null ? user.city : null;
1601
+ case "{summary}":
1602
+ return user !== null ? user.summary : null;
1603
+ case "{interests}":
1604
+ return user !== null ? user.interests : null;
1605
+ case "{personal_website}":
1606
+ return user !== null ? user.personal_website : null;
1607
+ case "{company_website}":
1608
+ return user !== null ? user.company_website : null;
1609
+ case "{position}":
1610
+ return user !== null ? user.position : null;
1611
+ case "{department}":
1612
+ return user !== null ? user.department : null;
1613
+ case "{today}":
1614
+ return ul4.today();
1615
+ default:
1616
+ return placeholder;
1617
+ }
1618
+ }
1619
+
1620
+ get default()
1621
+ {
1622
+ let view_control = this._view_control();
1623
+
1624
+ if (view_control === null)
1625
+ return null;
1626
+
1627
+ return this._user_placeholder(this.app.globals.user, view_control.default);
1628
+ }
1629
+
1630
+ get minlength()
1631
+ {
1632
+ let view_control = this._view_control();
1633
+
1634
+ if (view_control === null)
1635
+ return 0;
1636
+ let minlength = view_control.minlength;
1637
+ if (minlength === null || minlength < 0)
1638
+ return 0;
1639
+ return minlength;
1640
+ }
1641
+
1642
+ get maxlength()
1643
+ {
1644
+ let view_control = this._view_control();
1645
+
1646
+ if (view_control === null)
1647
+ return 4000;
1648
+ let maxlength = view_control.maxlength;
1649
+ if (maxlength === null || maxlength < 0 || maxlength > 4000)
1650
+ return 4000;
1651
+ return maxlength;
1652
+ }
1653
+
533
1654
  search(value, search)
534
1655
  {
535
1656
  this._logsearch(value, search);
@@ -547,52 +1668,92 @@ export class StringControl extends Control
547
1668
 
548
1669
  StringControl.prototype.type = "string";
549
1670
 
1671
+
550
1672
  export class TextControl extends StringControl
551
1673
  {
1674
+ static classdoc = "A LivingApps text field (type 'string/text')";
552
1675
  };
553
1676
 
554
1677
  TextControl.prototype.subtype = "text";
1678
+ TextControl.prototype.fieldtype = StringField;
1679
+
555
1680
 
556
1681
  export class EmailControl extends StringControl
557
1682
  {
1683
+ static classdoc = "A LivingApps email field (type 'string/email')";
558
1684
  };
559
1685
 
560
1686
  EmailControl.prototype.subtype = "email";
1687
+ EmailControl.prototype.fieldtype = EmailField;
1688
+
561
1689
 
562
1690
  export class URLControl extends StringControl
563
1691
  {
1692
+ static classdoc = "A LivingApps URL field (type 'string/url')";
564
1693
  };
565
1694
 
566
1695
  URLControl.prototype.subtype = "url";
1696
+ URLControl.prototype.fieldtype = URLField;
1697
+
567
1698
 
568
1699
  export class TelControl extends StringControl
569
1700
  {
1701
+ static classdoc = "A LivingApps phone number field (type 'string/tel')";
570
1702
  };
571
1703
 
572
1704
  TelControl.prototype.subtype = "tel";
1705
+ TelControl.prototype.fieldtype = TelField;
573
1706
 
574
- export class PasswordControl extends StringControl
575
- {
576
- };
577
-
578
- PasswordControl.prototype.subtype = "password";
579
1707
 
580
1708
  export class TextAreaControl extends StringControl
581
1709
  {
1710
+ static classdoc = "A LivingApps textarea field (type 'string/textarea')";
1711
+
1712
+ get maxlength()
1713
+ {
1714
+ let view_control = this._view_control();
1715
+
1716
+ if (view_control === null)
1717
+ return null;
1718
+ // We only get the encrypted text in encryption mode, so we can't check its length
1719
+ if (this.encrypted !== null)
1720
+ return null;
1721
+ let maxlength = view_control.maxlength;
1722
+ if (maxlength < 0)
1723
+ return null;
1724
+ return maxlength;
1725
+ }
582
1726
  };
583
1727
 
584
1728
  TextAreaControl.prototype.subtype = "textarea";
585
- TextAreaControl.prototype._ul4onattrs = StringControl.prototype._ul4onattrs.concat(["encrypted"]);
586
- TextAreaControl.prototype._ul4attrs = ul4._makeset(...StringControl.prototype._ul4attrs, "encrypted");
1729
+ TextAreaControl.prototype.fieldtype = TextAreaField;
1730
+ TextAreaControl.prototype._ul4onattrs = [...StringControl.prototype._ul4onattrs, "encrypted"];
1731
+ TextAreaControl.prototype._ul4attrs = new Set([...StringControl.prototype._ul4attrs, "encrypted"]);
1732
+
587
1733
 
588
1734
  export class HTMLControl extends StringControl
589
1735
  {
1736
+ static classdoc = "A LivingApps HTML field (type 'string/html')";
590
1737
  };
591
1738
 
592
1739
  HTMLControl.prototype.subtype = "html";
1740
+ HTMLControl.prototype.fieldtype = HTMLField;
593
1741
 
594
- export class DateControl extends Control
1742
+
1743
+ export class DateControl extends PlaceholderControl
595
1744
  {
1745
+ static classdoc = "A LivingApps date field (type 'date/date')";
1746
+
1747
+ get default()
1748
+ {
1749
+ let view_control = this._view_control();
1750
+
1751
+ if (view_control === null)
1752
+ return null;
1753
+
1754
+ return view_control.default === "{today}" ? ul4.today() : null;
1755
+ }
1756
+
596
1757
  formatstring(language)
597
1758
  {
598
1759
  language = language || this.app.language;
@@ -630,9 +1791,31 @@ export class DateControl extends Control
630
1791
 
631
1792
  DateControl.prototype.type = "date";
632
1793
  DateControl.prototype.subtype = "date";
1794
+ DateControl.prototype.fieldtype = DateField;
1795
+
633
1796
 
634
1797
  export class DatetimeMinuteControl extends DateControl
635
1798
  {
1799
+ static classdoc = "A LivingApps date field (type 'date/datetimeminute')";
1800
+
1801
+ get default()
1802
+ {
1803
+ let view_control = this._view_control();
1804
+
1805
+ if (view_control === null)
1806
+ return null;
1807
+
1808
+ if (view_control.default === "{today}")
1809
+ {
1810
+ let now = new Date();
1811
+ now.setSeconds(0);
1812
+ now.setMilliseconds(0);
1813
+ return now;
1814
+ }
1815
+ else
1816
+ return null;
1817
+ }
1818
+
636
1819
  formatstring(language)
637
1820
  {
638
1821
  language = language || this.app.language;
@@ -645,9 +1828,30 @@ export class DatetimeMinuteControl extends DateControl
645
1828
  };
646
1829
 
647
1830
  DatetimeMinuteControl.prototype.subtype = "datetimeminute";
1831
+ DatetimeMinuteControl.prototype.fieldtype = DatetimeMinuteField;
1832
+
648
1833
 
649
1834
  export class DatetimeSecondControl extends DateControl
650
1835
  {
1836
+ static classdoc = "A LivingApps date field (type 'date/datetimesecond')";
1837
+
1838
+ get default()
1839
+ {
1840
+ let view_control = this._view_control();
1841
+
1842
+ if (view_control === null)
1843
+ return null;
1844
+
1845
+ if (view_control.default === "{today}")
1846
+ {
1847
+ let now = new Date();
1848
+ now.setMilliseconds(0);
1849
+ return now;
1850
+ }
1851
+ else
1852
+ return null;
1853
+ }
1854
+
651
1855
  formatstring(language)
652
1856
  {
653
1857
  language = language || this.app.language;
@@ -660,9 +1864,45 @@ export class DatetimeSecondControl extends DateControl
660
1864
  };
661
1865
 
662
1866
  DatetimeSecondControl.prototype.subtype = "datetimesecond";
1867
+ DatetimeSecondControl.prototype.fieldtype = DatetimeSecondField;
1868
+
663
1869
 
664
1870
  export class LookupControl extends Control
665
1871
  {
1872
+ get default()
1873
+ {
1874
+ let view_control = this._view_control();
1875
+
1876
+ if (view_control === null)
1877
+ return null;
1878
+
1879
+ return this.lookupdata.get(view_control) ?? null;
1880
+ }
1881
+
1882
+ get autoexpandable()
1883
+ {
1884
+ let view_control = this._view_control();
1885
+ if (view_control === null)
1886
+ return false;
1887
+ return view_control.autoexpandable;
1888
+ }
1889
+
1890
+ get nonekey()
1891
+ {
1892
+ let view_control = this._view_control();
1893
+ if (view_control === null)
1894
+ return null;
1895
+ return view_control.lookupnonekey;
1896
+ }
1897
+
1898
+ get nonelabel()
1899
+ {
1900
+ let view_control = this._view_control();
1901
+ if (view_control === null)
1902
+ return null;
1903
+ return view_control.lookupnonelabel;
1904
+ }
1905
+
666
1906
  // ``search.value`` must be ``null`` or a ``LookupItem`` key
667
1907
  search(value, search)
668
1908
  {
@@ -679,27 +1919,38 @@ export class LookupControl extends Control
679
1919
  };
680
1920
 
681
1921
  LookupControl.prototype.type = "lookup";
682
- LookupControl.prototype._ul4onattrs = Control.prototype._ul4onattrs.concat(["lookupdata"]);
683
- LookupControl.prototype._ul4attrs = ul4._makeset(...Control.prototype._ul4attrs, "lookupdata");
1922
+ LookupControl.prototype.fieldtype = LookupField;
1923
+ LookupControl.prototype._ul4onattrs = [...Control.prototype._ul4onattrs, "lookupdata"];
1924
+ LookupControl.prototype._ul4attrs = new Set([...Control.prototype._ul4attrs, "lookupdata"]);
1925
+
684
1926
 
685
1927
  export class LookupSelectControl extends LookupControl
686
1928
  {
1929
+ static classdoc = "A LivingApps lookup field (type 'lookup/select')";
1930
+
687
1931
  };
688
1932
 
689
1933
  LookupSelectControl.prototype.subtype = "select";
690
1934
 
1935
+
691
1936
  export class LookupRadioControl extends LookupControl
692
1937
  {
1938
+ static classdoc = "A LivingApps lookup field (type 'lookup/radio')";
1939
+
693
1940
  };
694
1941
 
695
1942
  LookupRadioControl.prototype.subtype = "radio";
696
1943
 
1944
+
697
1945
  export class LookupChoiceControl extends LookupControl
698
1946
  {
1947
+ static classdoc = "A LivingApps lookup field (type 'lookup/choice')";
1948
+
699
1949
  };
700
1950
 
701
1951
  LookupChoiceControl.prototype.subtype = "choice";
702
1952
 
1953
+
703
1954
  export class AppLookupControl extends Control
704
1955
  {
705
1956
  // ``search.value`` must be an object containing the search criteria for the referenced record
@@ -713,29 +1964,52 @@ export class AppLookupControl extends Control
713
1964
  };
714
1965
 
715
1966
  AppLookupControl.prototype.type = "applookup";
716
- AppLookupControl.prototype._ul4onattrs = Control.prototype._ul4onattrs.concat(["lookupapp", "lookupcontrols"]);
717
- AppLookupControl.prototype._ul4attrs = ul4._makeset(...Control.prototype._ul4attrs, "lookupapp", "lookupcontrols");
1967
+ AppLookupControl.prototype.fieldtype = AppLookupField;
1968
+ AppLookupControl.prototype._ul4onattrs = [...Control.prototype._ul4onattrs, "lookup_app", "lookup_controls", "local_master_control", "local_detail_controls", "remote_master_control"];
1969
+ AppLookupControl.prototype._ul4attrs = new Set([...Control.prototype._ul4attrs, "lookup_app", "lookup_controls", "local_master_control", "local_detail_controls", "remote_master_control"]);
1970
+
718
1971
 
719
1972
  export class AppLookupSelectControl extends AppLookupControl
720
1973
  {
1974
+ static classdoc = "A LivingApps applookup field (type 'applookup/select')";
1975
+
721
1976
  };
722
1977
 
723
1978
  AppLookupSelectControl.prototype.subtype = "select";
724
1979
 
1980
+
725
1981
  export class AppLookupRadioControl extends AppLookupControl
726
1982
  {
1983
+ static classdoc = "A LivingApps applookup field (type 'applookup/radio')";
1984
+
727
1985
  };
728
1986
 
729
1987
  AppLookupRadioControl.prototype.subtype = "radio";
730
1988
 
1989
+
731
1990
  export class AppLookupChoiceControl extends AppLookupControl
732
1991
  {
1992
+ static classdoc = "A LivingApps applookup field (type 'applookup/choice')";
1993
+
733
1994
  };
734
1995
 
735
1996
  AppLookupChoiceControl.prototype.subtype = "choice";
736
1997
 
1998
+
737
1999
  export class MultipleLookupControl extends LookupControl
738
2000
  {
2001
+ get default()
2002
+ {
2003
+ let view_control = this._view_control();
2004
+
2005
+ if (view_control === null)
2006
+ return null;
2007
+
2008
+ let defaultValue = this.lookupdata.get(view_control)
2009
+
2010
+ return (defaultValue !== undefined) ? [defaultValue] : [];
2011
+ }
2012
+
739
2013
  // search.value must be ``null`` or a ``LookupItem`` key
740
2014
  search(value, search)
741
2015
  {
@@ -754,25 +2028,36 @@ export class MultipleLookupControl extends LookupControl
754
2028
  };
755
2029
 
756
2030
  MultipleLookupControl.prototype.subtype = "multiplelookup";
2031
+ MultipleLookupControl.prototype.fieldtype = MultipleLookupField;
2032
+
757
2033
 
758
2034
  export class MultipleLookupSelectControl extends MultipleLookupControl
759
2035
  {
2036
+ static classdoc = "A LivingApps multiplelookup field (type 'multiplelookup/select')";
2037
+
760
2038
  };
761
2039
 
762
2040
  MultipleLookupSelectControl.prototype.subtype = "select";
763
2041
 
2042
+
764
2043
  export class MultipleLookupCheckboxControl extends MultipleLookupControl
765
2044
  {
2045
+ static classdoc = "A LivingApps multiplelookup field (type 'multiplelookup/checkbox')";
2046
+
766
2047
  };
767
2048
 
768
2049
  MultipleLookupCheckboxControl.prototype.subtype = "checkbox";
769
2050
 
2051
+
770
2052
  export class MultipleLookupChoiceControl extends MultipleLookupControl
771
2053
  {
2054
+ static classdoc = "A LivingApps multiplelookup field (type 'multiplelookup/choice')";
2055
+
772
2056
  };
773
2057
 
774
2058
  MultipleLookupChoiceControl.prototype.subtype = "choice";
775
2059
 
2060
+
776
2061
  export class MultipleAppLookupControl extends AppLookupControl
777
2062
  {
778
2063
  // ``search.value`` must be an object containing the search criteria for the referenced record
@@ -798,42 +2083,61 @@ export class MultipleAppLookupControl extends AppLookupControl
798
2083
  };
799
2084
 
800
2085
  MultipleAppLookupControl.prototype.type = "multipleapplookup";
2086
+ MultipleAppLookupControl.prototype.fieldtype = MultipleAppLookupField;
2087
+
801
2088
 
802
2089
  export class MultipleAppLookupSelectControl extends MultipleAppLookupControl
803
2090
  {
2091
+ static classdoc = "A LivingApps multiple applookup field (type 'multipleapplookup/select')";
2092
+
804
2093
  };
805
2094
 
806
2095
  MultipleAppLookupSelectControl.prototype.subtype = "select";
807
2096
 
2097
+
808
2098
  export class MultipleAppLookupCheckboxControl extends MultipleAppLookupControl
809
2099
  {
2100
+ static classdoc = "A LivingApps multiple applookup field (type 'multipleapplookup/checkbox')";
2101
+
810
2102
  };
811
2103
 
812
2104
  MultipleAppLookupCheckboxControl.prototype.subtype = "checkbox";
813
2105
 
2106
+
814
2107
  export class MultipleAppLookupChoiceControl extends MultipleAppLookupControl
815
2108
  {
2109
+ static classdoc = "A LivingApps multiple applookup field (type 'multipleapplookup/choice')";
816
2110
  };
817
2111
 
818
2112
  MultipleAppLookupChoiceControl.prototype.subtype = "choice";
819
2113
 
2114
+
820
2115
  export class GeoControl extends Control
821
2116
  {
2117
+ static classdoc = "A LivingApps geo field (type 'geo')";
822
2118
  };
823
2119
 
824
2120
  GeoControl.prototype.type = "geo";
2121
+ GeoControl.prototype.fieldtype = GeoField;
2122
+
825
2123
 
826
2124
  export class FileControl extends Control
827
2125
  {
2126
+ static classdoc = "A LivingApps upload field (type 'file')";
828
2127
  };
829
2128
 
830
2129
  FileControl.prototype.type = "file";
2130
+ FileControl.prototype.fieldtype = FileField;
2131
+
831
2132
 
832
2133
  export class FileSignatureControl extends FileControl
833
2134
  {
2135
+ static classdoc = "A LivingApps signature image field (type 'file/signature')";
834
2136
  };
835
2137
 
836
2138
  FileSignatureControl.prototype.subtype = "signature";
2139
+ FileSignatureControl.prototype.fieldtype = FileSignatureField;
2140
+
837
2141
 
838
2142
  export class ButtonControl extends Control
839
2143
  {
@@ -841,165 +2145,237 @@ export class ButtonControl extends Control
841
2145
 
842
2146
  ButtonControl.prototype.type = "button";
843
2147
 
844
- export class Field extends Base
2148
+
2149
+ export class LookupItem extends Base
845
2150
  {
846
- constructor(control, record, value)
2151
+ static classdoc = "An option in a lookup control/field";
2152
+
2153
+ get label()
847
2154
  {
848
- super();
849
- this.control = control;
850
- this.record = record;
851
- this._value = value;
852
- this._dirty = false;
853
- this.errors = [];
2155
+ if (this.control === null)
2156
+ return this._label;
2157
+ let view_lookupitem = this._view_lookupitem();
2158
+ if (view_lookupitem === null)
2159
+ return this._label;
2160
+ let label = view_lookupitem.label;
2161
+ if (label === null)
2162
+ return this._label;
2163
+ return label;
854
2164
  }
855
2165
 
856
- get value()
2166
+ get visible()
857
2167
  {
858
- return this._value;
2168
+ if (this.control === null)
2169
+ return true;
2170
+ let view_lookupitem = this._view_lookupitem();
2171
+ if (view_lookupitem === null)
2172
+ return true;
2173
+ return view_lookupitem.visible;
859
2174
  }
860
2175
 
861
- set value(value)
2176
+ _view_lookupitem()
862
2177
  {
863
- let oldvalue = this._value;
864
-
865
- if (ul4._ne(oldvalue, value))
2178
+ let identifier = this.control.identifier;
2179
+ let active_view = this.control.app.active_view;
2180
+ if (active_view !== null && active_view.controls !== null)
866
2181
  {
867
- this.record.values.set(this.control.identifier, value);
868
- this._value = value;
869
- this._dirty = true;
2182
+ let view_control = active_view.controls.get(identifier);
2183
+ if (view_control !== undefined && view_control.lookupdata !== undefined && view_control.lookupdata !== null)
2184
+ {
2185
+ let view_lookupitem = view_control.lookupdata.get(this.key);
2186
+ if (view_lookupitem !== undefined)
2187
+ return view_lookupitem;
2188
+ }
870
2189
  }
2190
+ return null;
871
2191
  }
872
2192
 
873
- is_empty()
2193
+ [ul4.symbols.repr]()
874
2194
  {
875
- return this._value === null || (ul4._islist(this._value) && this._value.length === 0);
2195
+ let repr = "<LookupItem key=" + ul4._repr(this.key) + " label=" + ul4._repr(this.label);
2196
+ if (!this.visible)
2197
+ repr += "visible=False"
2198
+ return repr + ">";
876
2199
  }
2200
+ };
877
2201
 
878
- is_dirty()
879
- {
880
- return this._dirty;
881
- }
2202
+ LookupItem.prototype._ul4onattrs = ["control", "key", "_label"];
2203
+ LookupItem.prototype._ul4attrs = new Set(["id", "control", "key", "label"]);
882
2204
 
883
- has_errors()
884
- {
885
- return this.errors.length !== 0;
886
- }
887
2205
 
888
- search(searchvalue)
2206
+ export class ViewLookupItem extends Base
2207
+ {
2208
+ static classdoc = "View specific information about a lookup item";
2209
+
2210
+ [ul4.symbols.repr]()
889
2211
  {
890
- return this.control.search(this.value, searchvalue);
2212
+ let repr = "<ViewLookupItem key=" + ul4._repr(this.key) + " label=" + ul4._repr(this.label);
2213
+ if (!this.visible)
2214
+ repr += " visible=False"
2215
+ return repr + ">";
891
2216
  }
2217
+ };
2218
+
2219
+ ViewLookupItem.prototype._ul4onattrs = ["key", "label", "visible"];
2220
+ ViewLookupItem.prototype._ul4attrs = new Set(["key", "label", "visible"]);
892
2221
 
893
- __repr__()
2222
+
2223
+ export class LayoutControl extends Base
2224
+ {
2225
+ static classdoc = "A decoration in an input form";
2226
+
2227
+ [ul4.symbols.repr]()
894
2228
  {
895
- let s = "<Field identifier=";
896
- s += ul4._repr(this.control.identifier)
897
- if (this._dirty)
898
- s += " is_dirty()=True";
899
- if (this.errors.length !== 0)
900
- s += " has_errors()=True";
901
- s += ">"
902
- return s;
2229
+ return "<" + this.constructor.name + " id=" + ul4._repr(this.id) + " identifier=" + ul4._repr(this.identifier) + ">";
903
2230
  }
904
2231
  };
905
2232
 
906
- export class LookupItem extends Base
2233
+ LayoutControl.prototype._ul4onattrs = ["label", "identifier", "view", "top", "left", "width", "height"];
2234
+ LayoutControl.prototype._ul4attrs = new Set(["id", "label", "identifier", "view", "top", "left", "width", "height"]);
2235
+
2236
+
2237
+ export class HTMLLayoutControl extends LayoutControl
2238
+ {
2239
+ static classdoc = "An HTML decoration in an input form";
2240
+ };
2241
+
2242
+ HTMLLayoutControl.prototype._ul4onattrs = [...LayoutControl.prototype._ul4onattrs, "value"];
2243
+ HTMLLayoutControl.prototype._ul4attrs = new Set([LayoutControl.prototype._ul4attrs, "value"]);
2244
+
2245
+
2246
+ export class ImageLayoutControl extends LayoutControl
2247
+ {
2248
+ static classdoc = "An image decoration in an input form";
2249
+ };
2250
+
2251
+ ImageLayoutControl.prototype._ul4onattrs = [...LayoutControl.prototype._ul4onattrs, "original", "scaled"];
2252
+ ImageLayoutControl.prototype._ul4attrs = new Set([LayoutControl.prototype._ul4attrs, "original", "scaled"]);
2253
+
2254
+
2255
+ export class ViewControl extends Base
907
2256
  {
908
- __repr__()
2257
+ static classdoc = "Contains view specific information aboutn a control";
2258
+
2259
+ get mode()
909
2260
  {
910
- return "<LookupItem key=" + ul4._repr(this.key) + " label=" + ul4._repr(this.label) + ">";
2261
+ return this._mode ? "display" : "edit";
911
2262
  }
912
2263
  };
913
2264
 
914
- LookupItem.prototype._ul4onattrs = ["key", "label"];
915
- LookupItem.prototype._ul4attrs = ul4._makeset("key", "label");
2265
+ ViewControl.prototype._ul4onattrs = ["view", "control", "top", "left", "width", "height", "liveupdate", "default", "tabindex", "minlength", "maxlength", "required", "placeholder", "_mode", "labelpos", "lookupnonekey", "lookupnonelabel", "label", "autoalign", "labelwidth", "lookupdata", "autoexpandable"];
2266
+ ViewControl.prototype._ul4attrs = new Set(["id", "view", "control", "top", "left", "width", "height", "liveupdate", "default", "tabindex", "minlength", "maxlength", "required", "placeholder", "mode", "labelpos", "lookupnonekey", "lookupnonelabel", "label", "autoalign", "labelwidth", "lookupdata", "autoexpandable"]);
2267
+
916
2268
 
917
2269
  export class User extends Base
918
2270
  {
919
- __repr__()
2271
+ static classdoc = "A LivingApps user/account";
2272
+
2273
+ [ul4.symbols.repr]()
920
2274
  {
921
2275
  return "<User id=" + ul4._repr(this.id) + " firstname=" + ul4._repr(this.firstname) + " surname=" + ul4._repr(this.surname) + " email=" + ul4._repr(this.email) + ">";
922
2276
  }
923
2277
  };
924
2278
 
925
- User.prototype._ul4onattrs = ["_id", "id", "gender", "firstname", "surname", "initials", "email", "language", "avatarsmall", "avatarlarge", "keyviews"];
926
- User.prototype._ul4attrs = ul4._makeset("_id", "id", "gender", "firstname", "surname", "initials", "email", "language", "avatarsmall", "avatarlarge", "keyviews");
2279
+ User.prototype._ul4onattrs = ["_id", "gender", "title", "firstname", "surname", "initials", "email", "streetname", "streetnumber", "zip", "city", "phone", "fax", "lang", "avatarsmall", "avatarlarge", "summary", "interests", "personalwebsite", "companywebsite", "company", "position", "department", "keyviews"];
2280
+ User.prototype._ul4attrs = new Set(["id", "_id", "gender", "title", "firstname", "surname", "initials", "email", "streetname", "streetnumber", "zip", "city", "phone", "fax", "lang", "avatarsmall", "avatarlarge", "summary", "interests", "personalwebsite", "companywebsite", "company", "position", "department", "keyviews"]);
2281
+
927
2282
 
928
2283
  export class File extends Base
929
2284
  {
930
- __repr__()
2285
+ static classdoc = "An uploaded file";
2286
+
2287
+ [ul4.symbols.repr]()
931
2288
  {
932
2289
  return "<File id=" + ul4._repr(this.id) + " url=" + ul4._repr(this.url) + " filename=" + ul4._repr(this.filename) + ">";
933
2290
  }
934
2291
  };
935
2292
 
936
- File.prototype._ul4onattrs = ["id", "url", "filename", "mimetype", "width", "height", "internalid", "createdat", "size"];
937
- File.prototype._ul4attrs = ul4._makeset("id", "url", "filename", "mimetype", "width", "height", "size", "createdat");
2293
+ File.prototype._ul4onattrs = ["url", "filename", "mimetype", "width", "height", "internalid", "createdat", "size"];
2294
+ File.prototype._ul4attrs = new Set(["id", "url", "filename", "mimetype", "width", "height", "size", "createdat"]);
2295
+
938
2296
 
939
2297
  export class Geo extends Base
940
2298
  {
2299
+ static classdoc = "Geographical coordinates and location information";
2300
+
941
2301
  constructor(lat, long, info)
942
2302
  {
943
- super();
2303
+ super(null);
944
2304
  this.lat = lat;
945
2305
  this.long = long;
946
2306
  this.info = info;
947
2307
  }
948
2308
 
949
- __repr__()
2309
+ [ul4.symbols.repr]()
950
2310
  {
951
2311
  return "<Geo lat=" + ul4._repr(this.lat) + " long=" + ul4._repr(this.long) + " info=" + ul4._repr(this.info) + ">";
952
2312
  }
953
2313
  };
954
2314
 
955
2315
  Geo.prototype._ul4onattrs = ["lat", "long", "info"];
956
- Geo.prototype._ul4attrs = ul4._makeset("lat", "long", "info");
2316
+ Geo.prototype._ul4attrs = new Set(["lat", "long", "info"]);
2317
+
957
2318
 
958
2319
  export class Attachment extends Base
959
2320
  {
960
- __repr__()
2321
+ [ul4.symbols.repr]()
961
2322
  {
962
- return "<" + this.__type__ + " id=" + ul4._repr(this.id) + " label=" + ul4._repr(this.label) + ">";
2323
+ return "<" + this.constructor.name + " id=" + ul4._repr(this.id) + " label=" + ul4._repr(this.label) + ">";
963
2324
  }
964
2325
  };
965
2326
 
966
- Attachment.prototype._ul4onattrs = ["id", "record", "label", "active"];
967
- Attachment.prototype._ul4attrs = ul4._makeset("id", "record", "label", "active");
2327
+ Attachment.prototype._ul4onattrs = ["record", "label", "active"];
2328
+ Attachment.prototype._ul4attrs = new Set(["id", "record", "label", "active"]);
2329
+
968
2330
 
969
2331
  export class NoteAttachment extends Attachment
970
2332
  {
2333
+ static classdoc = "A note attachment of a record";
2334
+
971
2335
  };
972
2336
 
973
2337
  NoteAttachment.prototype.type = "noteattachment";
974
- NoteAttachment.prototype._ul4onattrs = Attachment.prototype._ul4onattrs.concat(["value"]);
975
- NoteAttachment.prototype._ul4attrs = ul4._makeset(...Attachment.prototype._ul4onattrs, "value");
2338
+ NoteAttachment.prototype._ul4onattrs = [...Attachment.prototype._ul4onattrs, "value"];
2339
+ NoteAttachment.prototype._ul4attrs = new Set([...Attachment.prototype._ul4onattrs, "value"]);
2340
+
976
2341
 
977
2342
  export class URLAttachment extends Attachment
978
2343
  {
2344
+ static classdoc = "A URL attachment of a record";
2345
+
979
2346
  };
980
2347
 
981
2348
  URLAttachment.prototype.type = "urlattachment";
982
- URLAttachment.prototype._ul4onattrs = Attachment.prototype._ul4onattrs.concat(["value"]);
983
- URLAttachment.prototype._ul4attrs = ul4._makeset(...Attachment.prototype._ul4onattrs, "value");
2349
+ URLAttachment.prototype._ul4onattrs = [...Attachment.prototype._ul4onattrs, "value"];
2350
+ URLAttachment.prototype._ul4attrs = new Set([...Attachment.prototype._ul4onattrs, "value"]);
2351
+
984
2352
 
985
2353
  export class FileAttachment extends Attachment
986
2354
  {
2355
+ static classdoc = "A file attachment of a record";
2356
+
987
2357
  };
988
2358
 
989
2359
  FileAttachment.prototype.type = "fileattachment";
990
- FileAttachment.prototype._ul4onattrs = Attachment.prototype._ul4onattrs.concat(["value"]);
991
- FileAttachment.prototype._ul4attrs = ul4._makeset(...Attachment.prototype._ul4onattrs, "value");
2360
+ FileAttachment.prototype._ul4onattrs = [...Attachment.prototype._ul4onattrs, "value"];
2361
+ FileAttachment.prototype._ul4attrs = new Set([...Attachment.prototype._ul4onattrs, "value"]);
2362
+
992
2363
 
993
2364
  export class ImageAttachment extends Attachment
994
2365
  {
2366
+ static classdoc = "An image attachment of a record";
2367
+
995
2368
  };
996
2369
 
997
2370
  ImageAttachment.prototype.type = "imageattachment";
998
- ImageAttachment.prototype._ul4onattrs = Attachment.prototype._ul4onattrs.concat(["original", "thumb", "small", "medium", "large"]);
999
- ImageAttachment.prototype._ul4attrs = ul4._makeset(...Attachment.prototype._ul4onattrs, "original", "thumb", "small", "medium", "large");
2371
+ ImageAttachment.prototype._ul4onattrs = [...Attachment.prototype._ul4onattrs, "original", "thumb", "small", "medium","large"];
2372
+ ImageAttachment.prototype._ul4attrs = new Set([...Attachment.prototype._ul4onattrs, "original", "thumb", "small", "medium", "large"]);
2373
+
1000
2374
 
1001
2375
  export class JSONAttachment extends Attachment
1002
2376
  {
2377
+ static classdoc = "A JSON attachment of a record";
2378
+
1003
2379
  _dumpUL4ONAttr(name)
1004
2380
  {
1005
2381
  if (name === "value")
@@ -1018,23 +2394,29 @@ export class JSONAttachment extends Attachment
1018
2394
  };
1019
2395
 
1020
2396
  JSONAttachment.prototype.type = "jsonattachment";
1021
- JSONAttachment.prototype._ul4onattrs = Attachment.prototype._ul4onattrs.concat(["value"]);
1022
- JSONAttachment.prototype._ul4attrs = ul4._makeset(...Attachment.prototype._ul4onattrs, "value");
2397
+ JSONAttachment.prototype._ul4onattrs = [...Attachment.prototype._ul4onattrs, "value"];
2398
+ JSONAttachment.prototype._ul4attrs = new Set([...Attachment.prototype._ul4onattrs, "value"]);
2399
+
1023
2400
 
1024
2401
  export class Installation extends Base
1025
2402
  {
1026
- __repr__()
2403
+ static classdoc = "The installation that created an app";
2404
+
2405
+ [ul4.symbols.repr]()
1027
2406
  {
1028
2407
  return "<Installation id=" + ul4._repr(this.id) + " name=" + ul4._repr(this.name) + ">";
1029
2408
  }
1030
2409
  };
1031
2410
 
1032
- Installation.prototype._ul4onattrs = ["id", "name"];
1033
- Installation.prototype._ul4attrs = ul4._makeset("id", "name");
2411
+ Installation.prototype._ul4onattrs = ["name"];
2412
+ Installation.prototype._ul4attrs = new Set(["id", "name"]);
2413
+
1034
2414
 
1035
2415
  export class Category extends Base
1036
2416
  {
1037
- __repr__()
2417
+ static classdoc = "A navigation category";
2418
+
2419
+ [ul4.symbols.repr]()
1038
2420
  {
1039
2421
  let v = [];
1040
2422
  let category = this;
@@ -1047,30 +2429,37 @@ export class Category extends Base
1047
2429
  }
1048
2430
  };
1049
2431
 
1050
- Category.prototype._ul4onattrs = ["id", "identifier", "name", "order", "parent", "children", "apps"];
1051
- Category.prototype._ul4attrs = ul4._makeset("id", "identifier", "name", "order", "parent", "children", "apps");
2432
+ Category.prototype._ul4onattrs = ["identifier", "name", "order", "parent", "children", "apps"];
2433
+ Category.prototype._ul4attrs = new Set(["id", "identifier", "name", "order", "parent", "children", "apps"]);
2434
+
1052
2435
 
1053
2436
  export class KeyView extends Base
1054
2437
  {
1055
- __repr__()
2438
+ static classdoc = "Object granting access to a view template";
2439
+
2440
+ [ul4.symbols.repr]()
1056
2441
  {
1057
2442
  return "<KeyView id=" + ul4._repr(this.id) + " identifier=" + ul4._repr(this.identifier) + ">";
1058
2443
  }
1059
2444
  };
1060
2445
 
1061
- KeyView.prototype._ul4onattrs = ["id", "identifier", "name", "key", "user"];
1062
- KeyView.prototype._ul4attrs = ul4._makeset("id", "identifier", "name", "key", "user");
2446
+ KeyView.prototype._ul4onattrs = ["identifier", "name", "key", "user"];
2447
+ KeyView.prototype._ul4attrs = new Set(["id", "identifier", "name", "key", "user"]);
2448
+
1063
2449
 
1064
2450
  export class AppParameter extends Base
1065
2451
  {
1066
- __repr__()
2452
+ static classdoc = "A parameter of a LivingApps application";
2453
+
2454
+ [ul4.symbols.repr]()
1067
2455
  {
1068
2456
  return "<AppParameter id=" + ul4._repr(this.id) + " identifier=" + ul4._repr(this.identifier) + ">";
1069
2457
  }
1070
2458
  };
1071
2459
 
1072
- AppParameter.prototype._ul4onattrs = ["id", "app", "identifier", "description", "value"];
1073
- AppParameter.prototype._ul4attrs = ul4._makeset("id", "app", "identifier", "description", "value");
2460
+ AppParameter.prototype._ul4onattrs = ["app", "identifier", "description", "value"];
2461
+ AppParameter.prototype._ul4attrs = new Set(["id", "app", "identifier", "description", "value"]);
2462
+
1074
2463
 
1075
2464
  let classes = [
1076
2465
  Globals,
@@ -1086,7 +2475,6 @@ let classes = [
1086
2475
  EmailControl,
1087
2476
  URLControl,
1088
2477
  TelControl,
1089
- PasswordControl,
1090
2478
  TextAreaControl,
1091
2479
  DateControl,
1092
2480
  DatetimeMinuteControl,
@@ -1109,9 +2497,37 @@ let classes = [
1109
2497
  MultipleAppLookupChoiceControl,
1110
2498
  GeoControl,
1111
2499
  FileControl,
2500
+ FileSignatureControl,
1112
2501
  ButtonControl,
1113
2502
  Field,
2503
+ BoolField,
2504
+ StringField,
2505
+ EmailField,
2506
+ URLField,
2507
+ TelField,
2508
+ TextAreaField,
2509
+ HTMLField,
2510
+ IntField,
2511
+ NumberField,
2512
+ GeoField,
2513
+ FileField,
2514
+ FileSignatureField,
2515
+ DateFieldBase,
2516
+ DateField,
2517
+ DatetimeMinuteField,
2518
+ DatetimeSecondField,
2519
+ LookupFieldBase,
2520
+ LookupField,
2521
+ MultipleLookupField,
2522
+ AppLookupFieldBase,
2523
+ AppLookupField,
2524
+ MultipleAppLookupField,
1114
2525
  LookupItem,
2526
+ ViewLookupItem,
2527
+ LayoutControl,
2528
+ HTMLLayoutControl,
2529
+ ImageLayoutControl,
2530
+ ViewControl,
1115
2531
  User,
1116
2532
  File,
1117
2533
  Geo,