@livinglogic/livingapi 0.12.0 → 0.13.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.
package/src/livingapi.js CHANGED
@@ -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
377
  return "<DataSource.Data 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 Field(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,893 @@ 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._value = value;
635
+ this._dirty = false;
636
+ this._lookupdate = null;
637
+ this.errors = [];
638
+ }
639
+
640
+ get value()
641
+ {
642
+ return this._value;
643
+ }
644
+
645
+ set value(value)
646
+ {
647
+ let oldvalue = this._value;
648
+
649
+ let change = {value: value, errors: []};
650
+ this._validate(change);
651
+
652
+ if (ul4._ne(oldvalue, change.value))
653
+ {
654
+ this.record.values.set(this.control.identifier, change.value);
655
+ this._value = change.value;
656
+ this._dirty = true;
657
+ }
658
+ this.errors = change.errors;
659
+ }
660
+
661
+ _validate(change)
662
+ {
663
+ // Do nothing, i.e. accept the value unchanged and without errors
664
+ }
665
+
666
+ get label()
667
+ {
668
+ if (this._label !== null)
669
+ return this._label;
670
+ return this.control.label;
671
+ }
672
+
673
+ set label(value)
674
+ {
675
+ this._label = value;
676
+ }
677
+
678
+ get globals()
679
+ {
680
+ return this.control.globals;
681
+ }
682
+
683
+ is_empty()
684
+ {
685
+ return this._value === null || (ul4._islist(this._value) && this._value.length === 0);
686
+ }
687
+
688
+ is_dirty()
689
+ {
690
+ return this._dirty;
691
+ }
692
+
693
+ has_errors()
694
+ {
695
+ return this.errors.length !== 0;
696
+ }
697
+
698
+ search(searchvalue)
699
+ {
700
+ return this.control.search(this.value, searchvalue);
701
+ }
702
+
703
+ msg_field_required(value)
704
+ {
705
+ switch (this.globals.lang)
706
+ {
707
+ case "de":
708
+ return `"${this.label}" wird benötigt!`;
709
+ default:
710
+ return `"${this.label}" is required!`;
711
+ }
712
+ }
713
+
714
+ msg_field_wrong_type(value)
715
+ {
716
+ return `"${this.label}" doesn't support the type ${ul4._type(value).name}!`;
717
+ }
718
+
719
+ msg_bool_truerequired(value)
720
+ {
721
+ return `"${this.label}" only accepts "Yes"!`;
722
+ }
723
+
724
+ msg_string_tooshort(value)
725
+ {
726
+ return `"${this.label}" is too short. Tou must use at least ${this.control.minlength} characters!`;
727
+ }
728
+
729
+ msg_string_toolong(value)
730
+ {
731
+ return `"${this.label}" is too long. You may use at most ${this.control.maxlength} characters!`;
732
+ }
733
+
734
+ msg_email_format(value)
735
+ {
736
+ return `"${this.label}" must be a valid email address!`;
737
+ }
738
+
739
+ msg_url_format(value)
740
+ {
741
+ return `"${this.label}" must be a valid URL!`;
742
+ }
743
+
744
+ msg_tel_format(value)
745
+ {
746
+ return `"${this.label}" must be a valid phone number!`;
747
+ }
748
+
749
+ msg_date_format(value)
750
+ {
751
+ return `"${this.label}" doesn't support this date format!`;
752
+ }
753
+
754
+ msg_lookup_wrongitem(value)
755
+ {
756
+ return `The option ${ul4._repr(value)} for "${this.label}" doesn't belong to this lookup!`;
757
+ }
758
+
759
+ msg_lookup_unknownkey(value)
760
+ {
761
+ return `The option ${ul4._repr(value)} for "${this.label}" is unknown!`;
762
+ }
763
+
764
+ msg_applookup_wrongrecord(value)
765
+ {
766
+ return `The record ${ul4._repr(value)} for "${this.label}" doesn't belong to the correct app!`;
767
+ }
768
+
769
+ msg_applookup_unknownkey(value)
770
+ {
771
+ return `The record ${ul4._repr(value)} for "${this.label}" can't be found!`;
772
+ }
773
+
774
+ [ul4.symbols.repr]()
775
+ {
776
+ let s = "<Field identifier=";
777
+ s += ul4._repr(this.control.identifier)
778
+ if (this._dirty)
779
+ s += " is_dirty()=True";
780
+ if (this.errors.length > 0)
781
+ s += " has_errors()=True";
782
+ s += ">"
783
+ return s;
784
+ }
785
+ };
786
+
787
+ Field.prototype._ul4onattrs = ["control", "record", "label", "value", "errors", "enabled", "writable", "visible"];
788
+ Field.prototype._ul4attrs = new Set(["control", "record", "label", "value", "errors", "enabled", "writable", "visible"]);
789
+
790
+
791
+ export class BoolField extends Field
792
+ {
793
+ static classdoc = "Holds the value of a bool field of a record (and related information)";
794
+
795
+ _validate(change)
796
+ {
797
+ if (change.value === null)
798
+ {
799
+ if (this.control.required)
800
+ change.errors.push(this.msg_field_required(change.value));
801
+ }
802
+ else if (typeof(change.value) === "boolean")
803
+ {
804
+ if (this.control.required && !change.value)
805
+ change.errors.push(this.msg_bool_truerequired(change.value));
806
+ }
807
+ else
808
+ {
809
+ change.errors.push(this.msg_field_wrong_type(change.value));
810
+ change.value = null;
811
+ }
812
+ }
813
+ };
814
+
815
+
816
+ export class IntField extends Field
817
+ {
818
+ static classdoc = "Holds the value of an int field of a record (and related information)";
819
+
820
+ _validate(change)
821
+ {
822
+ if (change.value === null)
823
+ {
824
+ if (this.control.required)
825
+ change.errors.push(this.msg_field_required(change.value));
826
+ }
827
+ else if (typeof(change.value) !== "number" || Math.round(change.value) !== change.value)
828
+ {
829
+ change.errors.push(this.msg_field_wrong_type(change.value));
830
+ }
831
+ }
832
+ };
833
+
834
+
835
+ export class NumberField extends Field
836
+ {
837
+ static classdoc = "Holds the value of a number field of a record (and related information)";
838
+
839
+ _validate(change)
840
+ {
841
+ if (change.value === null || change.value === "")
842
+ {
843
+ if (this.control.required)
844
+ change.errors.push(this.msg_field_required(change.value));
845
+ change.value = null;
846
+ }
847
+ else if (typeof(change.value) !== "number")
848
+ {
849
+ change.errors.push(this.msg_field_wrong_type(change.value));
850
+ }
851
+ }
852
+ };
853
+
854
+
855
+ export class StringField extends Field
856
+ {
857
+ static classdoc = "Holds the value of a string field of a record (and related information)";
858
+
859
+ _validate(change)
860
+ {
861
+ if (change.value === null || change.value === "")
862
+ {
863
+ if (this.control.required)
864
+ change.errors.push(this.msg_field_required(change.value));
865
+ change.value = null;
866
+ }
867
+ else if (typeof(change.value) === "string")
868
+ {
869
+ let minlength = this.control.minlength;
870
+ let maxlength = this.control.maxlength;
871
+ if (minlength !== null && change.value.length < minlength)
872
+ change.errors.push(this.msg_string_tooshort(change.value))
873
+ if (maxlength !== null && change.value.length > maxlength)
874
+ change.errors.push(this.msg_string_toolong(change.value))
875
+ }
876
+ else
877
+ {
878
+ change.errors.push(this.msg_field_wrong_type(change.value));
879
+ change.value = null;
880
+ }
881
+ }
882
+ };
883
+
884
+
885
+ export class EmailField extends StringField
886
+ {
887
+ static classdoc = "Holds the value of a string/email field of a record (and related information)";
888
+
889
+ _validate(change)
890
+ {
891
+ if (typeof(change.value) === "string" && change.value.length > 0)
892
+ {
893
+ 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;
894
+ if (!re.test(change.value))
895
+ change.errors.push(this.msg_email_format(change.value));
896
+ }
897
+ super._validate(change);
898
+ }
899
+ };
900
+
901
+
902
+ export class URLField extends StringField
903
+ {
904
+ static classdoc = "Holds the value of a string/url field of a record (and related information)";
905
+
906
+ _validate(change)
907
+ {
908
+ if (typeof(change.value) === "string" && change.value.length > 0)
909
+ {
910
+ let url = null;
911
+ try
912
+ {
913
+ url = new URL(change.value);
914
+ }
915
+ catch (exc)
916
+ {
917
+ change.errors.push(this.msg_url_format(change.value));
918
+ }
919
+ if (url !== null && url.protocol !== "http:" && url.protocol !== "https:")
920
+ {
921
+ change.errors.push(this.msg_url_format(change.value));
922
+ }
923
+ }
924
+ super._validate(change);
925
+ }
926
+ };
927
+
928
+
929
+ export class TelField extends StringField
930
+ {
931
+ static classdoc = "Holds the value of a string/tel field of a record (and related information)";
932
+
933
+ _validate(change)
934
+ {
935
+ if (typeof(change.value) === "string" && change.value.length > 0)
936
+ {
937
+ const re = /^\\+?[0-9 /()-]+$/;
938
+ if (!re.test(change.value))
939
+ change.errors.push(this.msg_tel_format(change.value));
940
+ }
941
+ super._validate(change);
942
+ }
943
+ };
944
+
945
+
946
+ export class TextAreaField extends StringField
947
+ {
948
+ static classdoc = "Holds the value of a string/textarea field of a record (and related information)";
949
+ };
950
+
951
+
952
+ export class HTMLField extends StringField
953
+ {
954
+ static classdoc = "Holds the value of a string/html field of a record (and related information)";
955
+ };
956
+
957
+
958
+ export class GeoField extends Field
959
+ {
960
+ static classdoc = "Holds the value of a geo field of a record (and related information)";
961
+
962
+ _validate(change)
963
+ {
964
+ if (change.value === null || change.value === "")
965
+ {
966
+ if (this.control.required)
967
+ change.errors.push(this.msg_field_required(change.value));
968
+ change.value = null;
969
+ }
970
+ else if (!(change.value instanceof Geo))
971
+ {
972
+ change.errors.push(this.msg_field_wrong_type(change.value));
973
+ change.value = null;
974
+ }
975
+ }
976
+ };
977
+
978
+
979
+ export class FileField extends Field
980
+ {
981
+ static classdoc = "Holds the value of a file field of a record (and related information)";
982
+
983
+ _validate(change)
984
+ {
985
+ if (change.value === null || change.value === "")
986
+ {
987
+ if (this.control.required)
988
+ change.errors.push(this.msg_field_required(change.value));
989
+ change.value = null;
990
+ }
991
+ else if (!(change.value instanceof File))
992
+ {
993
+ change.errors.push(this.msg_field_wrong_type(change.value));
994
+ change.value = null;
995
+ }
996
+ }
997
+ };
998
+
999
+
1000
+ export class FileSignatureField extends FileField
1001
+ {
1002
+ static classdoc = "Holds the value of a file/signature field of a record (and related information)";
1003
+
1004
+ _validate(change)
1005
+ {
1006
+ // FIXME: Implement handling of data URLs.
1007
+ super._validate(change);
1008
+ }
1009
+ };
1010
+
1011
+
1012
+ export class DateFieldBase extends Field
1013
+ {
1014
+ static classdoc = "Holds the value of a date field of a record (and related information)";
1015
+
1016
+ _validate(change)
1017
+ {
1018
+ if (change.value === null || change.value === "")
1019
+ {
1020
+ if (this.control.required)
1021
+ change.errors.push(this.msg_field_required(change.value));
1022
+ change.value = null;
1023
+ }
1024
+ else if (change.value instanceof Date)
1025
+ {
1026
+ change.value = this._convert(new Date(change.value.getTime()));
1027
+ }
1028
+ else if (typeof(change.value) === "string")
1029
+ {
1030
+ let value = null;
1031
+ if (change.value.length === 10)
1032
+ value = new Date(change.value + "T00:00:00");
1033
+ else if (change.value.length === 16)
1034
+ value = new Date(change.value + ":00");
1035
+ else if (change.value.length === 19)
1036
+ value = new Date(change.value);
1037
+
1038
+ if (value === null || Number.isNaN(value.getYear()))
1039
+ {
1040
+ change.errors.push(this.msg_date_format(change.value));
1041
+ // Keep original invalid string
1042
+ }
1043
+ else
1044
+ {
1045
+ change.value = this._convert(value);
1046
+ }
1047
+ }
1048
+ else
1049
+ {
1050
+ change.errors.push(this.msg_field_wrong_type(change.value));
1051
+ change.value = null;
1052
+ }
1053
+ }
1054
+ };
1055
+
1056
+
1057
+ export class DateField extends DateFieldBase
1058
+ {
1059
+ static classdoc = "Holds the value of a date/date field of a record (and related information)";
1060
+
1061
+ _convert(date)
1062
+ {
1063
+ return new ul4.Date_(date.getFullYear(), date.getMonth()+1, date.getDate());
1064
+ }
1065
+ };
1066
+
1067
+
1068
+ export class DatetimeMinuteField extends DateFieldBase
1069
+ {
1070
+ static classdoc = "Holds the value of a date/datetimeminute field of a record (and related information)";
1071
+
1072
+ _convert(date)
1073
+ {
1074
+ // Get rid of seconds and milliseconds
1075
+ date.setSeconds(0);
1076
+ date.setMilliseconds(0);
1077
+ return date;
1078
+ }
1079
+ };
1080
+
1081
+
1082
+ export class DatetimeSecondField extends DateFieldBase
1083
+ {
1084
+ static classdoc = "Holds the value of a date/datetimesecond field of a record (and related information)";
1085
+
1086
+ _convert(date)
1087
+ {
1088
+ // Get rid of milliseconds
1089
+ date.setMilliseconds(0);
1090
+ return date;
1091
+ }
1092
+ };
1093
+
1094
+
1095
+ export class LookupFieldBase extends Field
1096
+ {
1097
+ static classdoc = "Base type of LookupField and MultipleLookupField";
1098
+
1099
+ get lookupdata()
1100
+ {
1101
+ if (this._lookupdate !== null)
1102
+ return this._lookupdate;
1103
+ else
1104
+ return this.control.lookupdata;
1105
+ }
1106
+
1107
+ set lookupdata(value)
1108
+ {
1109
+ this._lookupdate = value;
1110
+ }
1111
+
1112
+ _find_lookupitem(change)
1113
+ {
1114
+ let v = change.value;
1115
+ if (typeof(v) === "string")
1116
+ {
1117
+ // If `lookupdata`} is `null`, this is probably an interface,
1118
+ // so we can't validate anything (note that interfaces will however go
1119
+ // away in the future)
1120
+ if (this.lookupdata === null)
1121
+ return;
1122
+ let lookupitem = this.lookupdata.get(v);
1123
+ if (lookupitem === undefined)
1124
+ {
1125
+ // If the lookup control is auto expandable,
1126
+ // the user may have given us a label
1127
+ if (this.control.autoexpandable)
1128
+ {
1129
+ // Search for the LookupItem with that label
1130
+ for (let search_lookupitem of this.lookupdata.values())
1131
+ {
1132
+ if (v === search_lookupitem.label)
1133
+ {
1134
+ change.value = search_lookupitem;
1135
+ return;
1136
+ }
1137
+ }
1138
+ // If we couldn't find the label, keep the label as it is.
1139
+ }
1140
+ else
1141
+ {
1142
+ change.errors.push(this.msg_lookup_unknownkey(v))
1143
+ change.value = null;
1144
+ }
1145
+ }
1146
+ else
1147
+ {
1148
+ change.value = lookupitem;
1149
+ }
1150
+ }
1151
+ else if (v instanceof LookupItem)
1152
+ {
1153
+ let value = this.control.lookupdata.get(v.key);
1154
+ if (value !== v)
1155
+ {
1156
+ change.errors.push(this.msg_lookup_wrongitem(v));
1157
+ change.value = null;
1158
+ }
1159
+ }
1160
+ else
1161
+ {
1162
+ change.errors.push(this.msg_field_wrong_type(v));
1163
+ change.value = null;
1164
+ }
1165
+ }
1166
+ };
1167
+
1168
+
1169
+ export class LookupField extends LookupFieldBase
1170
+ {
1171
+ static classdoc = "Holds the value of a lookup field of a record (and related information)";
1172
+
1173
+ _validate(change)
1174
+ {
1175
+ let v = change.value;
1176
+ if (v === null || v === "" || this.control.nonekey === v)
1177
+ {
1178
+ if (this.control.required)
1179
+ change.errors.push(this.msg_field_required(v));
1180
+ change.value = null;
1181
+ }
1182
+ else
1183
+ {
1184
+ this._find_lookupitem(change);
1185
+ }
1186
+ }
1187
+ };
1188
+
1189
+
1190
+ export class MultipleLookupField extends LookupFieldBase
1191
+ {
1192
+ static classdoc = "Holds the value of a multiple lookup field of a record (and related information)";
1193
+
1194
+ _validate(change)
1195
+ {
1196
+ let v = change.value;
1197
+ let islist = (Object.prototype.toString.call(v) === "[object Array]");
1198
+
1199
+ if (v === null || v === "" || this.control.nonekey === v || (islist && v.length === 0))
1200
+ {
1201
+ if (this.control.required)
1202
+ change.errors.push(this.msg_field_required(v));
1203
+ change.value = [];
1204
+ }
1205
+ else if (islist)
1206
+ {
1207
+ let newvalue = [];
1208
+ for (let item of v)
1209
+ {
1210
+ let singlechange = {value: item, errors: []};
1211
+ this._find_lookupitem(singlechange);
1212
+ if (singlechange.errors.length > 0)
1213
+ change.errors = [...change.errors, ...singlechange.errors];
1214
+ else
1215
+ newvalue.push(singlechange.value);
1216
+ }
1217
+ change.value = newvalue;
1218
+ }
1219
+ else
1220
+ {
1221
+ this._find_lookupitem(change);
1222
+ if (change.errors.length > 0)
1223
+ change.value = [];
1224
+ else
1225
+ change.value = [change.value];
1226
+ }
1227
+ }
1228
+ };
1229
+
1230
+
1231
+ export class AppLookupFieldBase extends Field
1232
+ {
1233
+ static classdoc = "Base type of AppLookupField and MultipleAppLookupField";
1234
+
1235
+ get lookupdata()
1236
+ {
1237
+ if (this._lookupdate !== null)
1238
+ return this._lookupdate;
1239
+ else
1240
+ {
1241
+ if (this.control.lookup_app.records !== null)
1242
+ return this.control.lookup_app.records;
1243
+ }
1244
+ return new Map();
1245
+ }
1246
+
1247
+ set lookupdata(value)
1248
+ {
1249
+ this._lookupdate = value;
1250
+ }
1251
+
1252
+ _find_record(change)
1253
+ {
1254
+ let v = change.value;
1255
+ if (typeof(v) === "string")
1256
+ {
1257
+ let record = this.lookupdata.get(v);
1258
+ if (record === undefined)
1259
+ {
1260
+ change.errors.push(this.msg_applookup_unknownkey(v))
1261
+ change.value = null;
1262
+ }
1263
+ else
1264
+ {
1265
+ change.value = record;
1266
+ }
1267
+ }
1268
+ else if (v instanceof Record)
1269
+ {
1270
+ if (v.app !== this.control.lookup_app)
1271
+ {
1272
+ change.errors.push(this.msg_applookup_wrongrecord(v));
1273
+ change.value = null;
1274
+ }
1275
+ }
1276
+ else
1277
+ {
1278
+ change.errors.push(this.msg_field_wrong_type(v));
1279
+ change.value = null;
1280
+ }
1281
+ }
1282
+ };
1283
+
1284
+
1285
+ export class AppLookupField extends AppLookupFieldBase
1286
+ {
1287
+ static classdoc = "Holds the value of a applookup field of a record (and related information)";
1288
+
1289
+ _validate(change)
1290
+ {
1291
+ this._find_record(change);
1292
+ }
1293
+ };
1294
+
1295
+
1296
+ export class MultipleAppLookupField extends AppLookupFieldBase
1297
+ {
1298
+ static classdoc = "Holds the value of a multiple applookup field of a record (and related information)";
1299
+
1300
+ _validate(change)
1301
+ {
1302
+ let v = change.value;
1303
+ let islist = (Object.prototype.toString.call(v) === "[object Array]");
1304
+
1305
+ if (v === null || v === "" || this.control.nonekey === v || (islist && v.length === 0))
1306
+ {
1307
+ if (this.control.required)
1308
+ change.errors.push(this.msg_field_required(v));
1309
+ change.value = [];
1310
+ }
1311
+ else if (islist)
1312
+ {
1313
+ let newvalue = [];
1314
+ for (let item of v)
1315
+ {
1316
+ let singlechange = {value: item, errors: []};
1317
+ this._find_record(singlechange);
1318
+ if (singlechange.errors.length > 0)
1319
+ change.errors = [...change.errors, ...singlechange.errors];
1320
+ else
1321
+ newvalue.push(singlechange.value);
1322
+ }
1323
+ change.value = newvalue;
1324
+ }
1325
+ else
1326
+ {
1327
+ this._find_record(change);
1328
+ if (change.errors.length > 0)
1329
+ change.value = [];
1330
+ else
1331
+ change.value = [change.value];
1332
+ }
1333
+ }
1334
+ };
1335
+
1336
+
1337
+ export class Control extends Base
1338
+ {
1339
+ [ul4.symbols.repr]()
1340
+ {
1341
+ return "<" + this.constructor.name + " id=" + ul4._repr(this.id) + " identifier=" + ul4._repr(this.identifier) + ">";
1342
+ }
1343
+
1344
+ get globals()
1345
+ {
1346
+ return this.app.globals;
1347
+ }
1348
+
1349
+ get fulltype()
1350
+ {
1351
+ return this.subtype ? this.type + "/" + this.subtype : this.type;
1352
+ }
1353
+
1354
+ get label()
1355
+ {
1356
+ let view_control = this._view_control();
1357
+ if (view_control === null)
1358
+ return this._label;
1359
+ let label = view_control.label;
1360
+ if (label === null)
1361
+ return this._label;
1362
+ return label;
1363
+ }
1364
+
1365
+ set label(value)
1366
+ {
1367
+ this._label = value;
1368
+ }
1369
+
1370
+ get default()
1371
+ {
1372
+ return null;
1373
+ }
1374
+
1375
+ get top()
1376
+ {
1377
+ let view_control = this._view_control();
1378
+ return view_control !== null ? view_control.top : null;
1379
+ }
1380
+
1381
+ get left()
1382
+ {
1383
+ let view_control = this._view_control();
1384
+ return view_control !== null ? view_control.left : null;
1385
+ }
1386
+
1387
+ get width()
1388
+ {
1389
+ let view_control = this._view_control();
1390
+ return view_control !== null ? view_control.width : null;
1391
+ }
1392
+
1393
+ get height()
1394
+ {
1395
+ let view_control = this._view_control();
1396
+ return view_control !== null ? view_control.height : null;
1397
+ }
1398
+
1399
+ get liveupdate()
1400
+ {
1401
+ let view_control = this._view_control();
1402
+ return view_control !== null ? view_control.liveupdate : false;
1403
+ }
1404
+
1405
+ get tabindex()
1406
+ {
1407
+ let view_control = this._view_control();
1408
+ return view_control !== null ? view_control.tabindex : null;
1409
+ }
1410
+
1411
+ get required()
403
1412
  {
404
- if (name === "values")
405
- return this._sparsevalues;
406
- else
407
- return this[name];
1413
+ let view_control = this._view_control();
1414
+ return view_control !== null ? view_control.required : false;
408
1415
  }
409
1416
 
410
- _loadUL4ONAttr(name, value)
1417
+ get mode()
411
1418
  {
412
- if (name === "values")
413
- {
414
- this._sparsevalues = value;
415
- this._values = null;
416
- this._fields = null;
417
- }
418
- else
419
- this[name] = value;
1419
+ let view_control = this._view_control();
1420
+ return view_control !== null ? view_control.mode : "edit";
420
1421
  }
421
1422
 
422
- __getattr__(name)
1423
+ get labelpos()
423
1424
  {
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];
1425
+ let view_control = this._view_control();
1426
+ return view_control !== null ? view_control.labelpos : "left";
432
1427
  }
433
1428
 
434
- __setattr__(name, value)
1429
+ get labelwidth()
435
1430
  {
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);
1431
+ let view_control = this._view_control();
1432
+ return view_control !== null ? view_control.labelwidth : null;
442
1433
  }
443
- };
444
1434
 
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});
1435
+ get autoalign()
1436
+ {
1437
+ let view_control = this._view_control();
1438
+ return view_control !== null ? view_control.autoalign : true;
1439
+ }
452
1440
 
453
- export class Control extends Base
454
- {
455
- __repr__()
1441
+ get in_active_view()
1442
+ {
1443
+ let view_control = this._view_control();
1444
+ return view_control !== null;
1445
+ }
1446
+
1447
+ _view_control()
456
1448
  {
457
- return "<" + this.__type__ + " id=" + ul4._repr(this.id) + " identifier=" + ul4._repr(this.identifier) + ">";
1449
+ if (this.app._active_view === null)
1450
+ return null;
1451
+ let view_control = this.app._active_view.controls.get(this.identifier);
1452
+ return view_control ?? null;
458
1453
  }
459
1454
 
460
1455
  _logsearch(value, search)
@@ -474,11 +1469,14 @@ export class Control extends Base
474
1469
 
475
1470
  Control.prototype.type = null;
476
1471
  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");
1472
+ Control.prototype._ul4onattrs = ["identifier", "fieldname", "app", "_label", "priority", "order", "ininsertprocedure", "inupdateprocedure"];
1473
+ 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"]);
1474
+
479
1475
 
480
1476
  export class BoolControl extends Control
481
1477
  {
1478
+ static classdoc = "A LivingApps boolean field (type 'bool')";
1479
+
482
1480
  // ``search`` must by ``null``, ``false`` or ``true``
483
1481
  search(value, search)
484
1482
  {
@@ -491,9 +1489,13 @@ export class BoolControl extends Control
491
1489
  };
492
1490
 
493
1491
  BoolControl.prototype.type = "bool";
1492
+ BoolControl.prototype.fieldtype = BoolField;
1493
+
494
1494
 
495
1495
  export class IntControl extends Control
496
1496
  {
1497
+ static classdoc = "A LivingApps integer field (type 'int')";
1498
+
497
1499
  // ``search.value`` must by ``null`` or an integer
498
1500
  search(value, search)
499
1501
  {
@@ -506,9 +1508,29 @@ export class IntControl extends Control
506
1508
  };
507
1509
 
508
1510
  IntControl.prototype.type = "int";
1511
+ IntControl.prototype.fieldtype = IntField;
1512
+
1513
+
1514
+ class PlaceholderControl extends Control
1515
+ {
1516
+ static classdoc = "A LivingApps multiplelookup field (type 'multiplelookup/select')";
1517
+
1518
+ get placeholder()
1519
+ {
1520
+ let view_control = this._view_control();
1521
+ if (view_control === null)
1522
+ return null;
1523
+ return view_control.placeholder;
1524
+ }
1525
+ };
1526
+
1527
+ PlaceholderControl.prototype._ul4attrs = new Set([...Control.prototype._ul4attrs, "placeholder"]);
509
1528
 
510
- export class NumberControl extends Control
1529
+
1530
+ export class NumberControl extends PlaceholderControl
511
1531
  {
1532
+ static classdoc = "A LivingApps number field (type 'number')";
1533
+
512
1534
  // ``search.value`` must by ``null`` or an integer
513
1535
  search(value, search)
514
1536
  {
@@ -527,9 +1549,103 @@ export class NumberControl extends Control
527
1549
  };
528
1550
 
529
1551
  NumberControl.prototype.type = "number";
1552
+ NumberControl.prototype.fieldtype = NumberField;
530
1553
 
531
- export class StringControl extends Control
1554
+
1555
+ export class StringControl extends PlaceholderControl
532
1556
  {
1557
+ _user_placeholder(user, placeholder)
1558
+ {
1559
+ if (placeholder === null)
1560
+ return null;
1561
+ switch (placeholder)
1562
+ {
1563
+ case "{gender}":
1564
+ return user !== null ? user.gender : null;
1565
+ case "{title}":
1566
+ return user !== null ? user.title : null;
1567
+ case "{firstname}":
1568
+ return user !== null ? user.firstname : null;
1569
+ case "{surname}":
1570
+ return user !== null ? user.surname : null;
1571
+ case "{account}":
1572
+ return user !== null ? user.email : null;
1573
+ case "{streetname}":
1574
+ return user !== null ? user.streetname : null;
1575
+ case "{streetnumber}":
1576
+ return user !== null ? user.streetnumber : null;
1577
+ case "{street}":
1578
+ if (user === null)
1579
+ return null;
1580
+ else if (user.street === null)
1581
+ return user.streetnumber;
1582
+ else if (user.streetnumber === null)
1583
+ return user.street;
1584
+ else
1585
+ return user.street + " " + user.streetnumber;
1586
+ case "{zip}":
1587
+ return user !== null ? user.zip : null;
1588
+ case "{phone}":
1589
+ return user !== null ? user.phone : null;
1590
+ case "{fax}":
1591
+ return user !== null ? user.fax : null;
1592
+ case "{company}":
1593
+ return user !== null ? user.company : null;
1594
+ case "{city}":
1595
+ return user !== null ? user.city : null;
1596
+ case "{summary}":
1597
+ return user !== null ? user.summary : null;
1598
+ case "{interests}":
1599
+ return user !== null ? user.interests : null;
1600
+ case "{personal_website}":
1601
+ return user !== null ? user.personal_website : null;
1602
+ case "{company_website}":
1603
+ return user !== null ? user.company_website : null;
1604
+ case "{position}":
1605
+ return user !== null ? user.position : null;
1606
+ case "{department}":
1607
+ return user !== null ? user.department : null;
1608
+ case "{today}":
1609
+ return ul4.today();
1610
+ default:
1611
+ return placeholder;
1612
+ }
1613
+ }
1614
+
1615
+ get default()
1616
+ {
1617
+ let view_control = this._view_control();
1618
+
1619
+ if (view_control === null)
1620
+ return null;
1621
+
1622
+ return this._user_placeholder(this.app.globals.user, view_control.default);
1623
+ }
1624
+
1625
+ get minlength()
1626
+ {
1627
+ let view_control = this._view_control();
1628
+
1629
+ if (view_control === null)
1630
+ return 0;
1631
+ let minlength = view_control.minlength;
1632
+ if (minlength === null || minlength < 0)
1633
+ return 0;
1634
+ return minlength;
1635
+ }
1636
+
1637
+ get maxlength()
1638
+ {
1639
+ let view_control = this._view_control();
1640
+
1641
+ if (view_control === null)
1642
+ return 4000;
1643
+ let maxlength = view_control.maxlength;
1644
+ if (maxlength === null || maxlength < 0 || maxlength > 4000)
1645
+ return 4000;
1646
+ return maxlength;
1647
+ }
1648
+
533
1649
  search(value, search)
534
1650
  {
535
1651
  this._logsearch(value, search);
@@ -547,52 +1663,92 @@ export class StringControl extends Control
547
1663
 
548
1664
  StringControl.prototype.type = "string";
549
1665
 
1666
+
550
1667
  export class TextControl extends StringControl
551
1668
  {
1669
+ static classdoc = "A LivingApps text field (type 'string/text')";
552
1670
  };
553
1671
 
554
1672
  TextControl.prototype.subtype = "text";
1673
+ TextControl.prototype.fieldtype = StringField;
1674
+
555
1675
 
556
1676
  export class EmailControl extends StringControl
557
1677
  {
1678
+ static classdoc = "A LivingApps email field (type 'string/email')";
558
1679
  };
559
1680
 
560
1681
  EmailControl.prototype.subtype = "email";
1682
+ EmailControl.prototype.fieldtype = EmailField;
1683
+
561
1684
 
562
1685
  export class URLControl extends StringControl
563
1686
  {
1687
+ static classdoc = "A LivingApps URL field (type 'string/url')";
564
1688
  };
565
1689
 
566
1690
  URLControl.prototype.subtype = "url";
1691
+ URLControl.prototype.fieldtype = URLField;
1692
+
567
1693
 
568
1694
  export class TelControl extends StringControl
569
1695
  {
1696
+ static classdoc = "A LivingApps phone number field (type 'string/tel')";
570
1697
  };
571
1698
 
572
1699
  TelControl.prototype.subtype = "tel";
1700
+ TelControl.prototype.fieldtype = TelField;
573
1701
 
574
- export class PasswordControl extends StringControl
575
- {
576
- };
577
-
578
- PasswordControl.prototype.subtype = "password";
579
1702
 
580
1703
  export class TextAreaControl extends StringControl
581
1704
  {
1705
+ static classdoc = "A LivingApps textarea field (type 'string/textarea')";
1706
+
1707
+ get maxlength()
1708
+ {
1709
+ let view_control = this._view_control();
1710
+
1711
+ if (view_control === null)
1712
+ return null;
1713
+ // We only get the encrypted text in encryption mode, so we can't check its length
1714
+ if (this.encrypted !== null)
1715
+ return null;
1716
+ let maxlength = view_control.maxlength;
1717
+ if (maxlength < 0)
1718
+ return null;
1719
+ return maxlength;
1720
+ }
582
1721
  };
583
1722
 
584
1723
  TextAreaControl.prototype.subtype = "textarea";
585
- TextAreaControl.prototype._ul4onattrs = StringControl.prototype._ul4onattrs.concat(["encrypted"]);
586
- TextAreaControl.prototype._ul4attrs = ul4._makeset(...StringControl.prototype._ul4attrs, "encrypted");
1724
+ TextAreaControl.prototype.fieldtype = TextAreaField;
1725
+ TextAreaControl.prototype._ul4onattrs = [...StringControl.prototype._ul4onattrs, "encrypted"];
1726
+ TextAreaControl.prototype._ul4attrs = new Set([...StringControl.prototype._ul4attrs, "encrypted"]);
1727
+
587
1728
 
588
1729
  export class HTMLControl extends StringControl
589
1730
  {
1731
+ static classdoc = "A LivingApps HTML field (type 'string/html')";
590
1732
  };
591
1733
 
592
1734
  HTMLControl.prototype.subtype = "html";
1735
+ HTMLControl.prototype.fieldtype = HTMLField;
1736
+
593
1737
 
594
- export class DateControl extends Control
1738
+ export class DateControl extends PlaceholderControl
595
1739
  {
1740
+ static classdoc = "A LivingApps date field (type 'date/date')";
1741
+
1742
+ get default()
1743
+ {
1744
+ let view_control = this._view_control();
1745
+
1746
+ if (view_control === null)
1747
+ return null;
1748
+
1749
+ return view_control.default === "{today}" ? ul4.today() : null;
1750
+ }
1751
+
596
1752
  formatstring(language)
597
1753
  {
598
1754
  language = language || this.app.language;
@@ -630,9 +1786,31 @@ export class DateControl extends Control
630
1786
 
631
1787
  DateControl.prototype.type = "date";
632
1788
  DateControl.prototype.subtype = "date";
1789
+ DateControl.prototype.fieldtype = DateField;
1790
+
633
1791
 
634
1792
  export class DatetimeMinuteControl extends DateControl
635
1793
  {
1794
+ static classdoc = "A LivingApps date field (type 'date/datetimeminute')";
1795
+
1796
+ get default()
1797
+ {
1798
+ let view_control = this._view_control();
1799
+
1800
+ if (view_control === null)
1801
+ return null;
1802
+
1803
+ if (view_control.default === "{today}")
1804
+ {
1805
+ let now = new Date();
1806
+ now.setSeconds(0);
1807
+ now.setMilliseconds(0);
1808
+ return now;
1809
+ }
1810
+ else
1811
+ return null;
1812
+ }
1813
+
636
1814
  formatstring(language)
637
1815
  {
638
1816
  language = language || this.app.language;
@@ -645,9 +1823,30 @@ export class DatetimeMinuteControl extends DateControl
645
1823
  };
646
1824
 
647
1825
  DatetimeMinuteControl.prototype.subtype = "datetimeminute";
1826
+ DatetimeMinuteControl.prototype.fieldtype = DatetimeMinuteField;
1827
+
648
1828
 
649
1829
  export class DatetimeSecondControl extends DateControl
650
1830
  {
1831
+ static classdoc = "A LivingApps date field (type 'date/datetimesecond')";
1832
+
1833
+ get default()
1834
+ {
1835
+ let view_control = this._view_control();
1836
+
1837
+ if (view_control === null)
1838
+ return null;
1839
+
1840
+ if (view_control.default === "{today}")
1841
+ {
1842
+ let now = new Date();
1843
+ now.setMilliseconds(0);
1844
+ return now;
1845
+ }
1846
+ else
1847
+ return null;
1848
+ }
1849
+
651
1850
  formatstring(language)
652
1851
  {
653
1852
  language = language || this.app.language;
@@ -660,9 +1859,45 @@ export class DatetimeSecondControl extends DateControl
660
1859
  };
661
1860
 
662
1861
  DatetimeSecondControl.prototype.subtype = "datetimesecond";
1862
+ DatetimeSecondControl.prototype.fieldtype = DatetimeSecondField;
1863
+
663
1864
 
664
1865
  export class LookupControl extends Control
665
1866
  {
1867
+ get default()
1868
+ {
1869
+ let view_control = this._view_control();
1870
+
1871
+ if (view_control === null)
1872
+ return null;
1873
+
1874
+ return this.lookupdata.get(view_control) ?? null;
1875
+ }
1876
+
1877
+ get autoexpandable()
1878
+ {
1879
+ let view_control = this._view_control();
1880
+ if (view_control === null)
1881
+ return false;
1882
+ return view_control.autoexpandable;
1883
+ }
1884
+
1885
+ get nonekey()
1886
+ {
1887
+ let view_control = this._view_control();
1888
+ if (view_control === null)
1889
+ return null;
1890
+ return view_control.lookupnonekey;
1891
+ }
1892
+
1893
+ get nonelabel()
1894
+ {
1895
+ let view_control = this._view_control();
1896
+ if (view_control === null)
1897
+ return null;
1898
+ return view_control.lookupnonelabel;
1899
+ }
1900
+
666
1901
  // ``search.value`` must be ``null`` or a ``LookupItem`` key
667
1902
  search(value, search)
668
1903
  {
@@ -679,27 +1914,38 @@ export class LookupControl extends Control
679
1914
  };
680
1915
 
681
1916
  LookupControl.prototype.type = "lookup";
682
- LookupControl.prototype._ul4onattrs = Control.prototype._ul4onattrs.concat(["lookupdata"]);
683
- LookupControl.prototype._ul4attrs = ul4._makeset(...Control.prototype._ul4attrs, "lookupdata");
1917
+ LookupControl.prototype.fieldtype = LookupField;
1918
+ LookupControl.prototype._ul4onattrs = [...Control.prototype._ul4onattrs, "lookupdata"];
1919
+ LookupControl.prototype._ul4attrs = new Set([...Control.prototype._ul4attrs, "lookupdata"]);
1920
+
684
1921
 
685
1922
  export class LookupSelectControl extends LookupControl
686
1923
  {
1924
+ static classdoc = "A LivingApps lookup field (type 'lookup/select')";
1925
+
687
1926
  };
688
1927
 
689
1928
  LookupSelectControl.prototype.subtype = "select";
690
1929
 
1930
+
691
1931
  export class LookupRadioControl extends LookupControl
692
1932
  {
1933
+ static classdoc = "A LivingApps lookup field (type 'lookup/radio')";
1934
+
693
1935
  };
694
1936
 
695
1937
  LookupRadioControl.prototype.subtype = "radio";
696
1938
 
1939
+
697
1940
  export class LookupChoiceControl extends LookupControl
698
1941
  {
1942
+ static classdoc = "A LivingApps lookup field (type 'lookup/choice')";
1943
+
699
1944
  };
700
1945
 
701
1946
  LookupChoiceControl.prototype.subtype = "choice";
702
1947
 
1948
+
703
1949
  export class AppLookupControl extends Control
704
1950
  {
705
1951
  // ``search.value`` must be an object containing the search criteria for the referenced record
@@ -713,29 +1959,52 @@ export class AppLookupControl extends Control
713
1959
  };
714
1960
 
715
1961
  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");
1962
+ AppLookupControl.prototype.fieldtype = AppLookupField;
1963
+ AppLookupControl.prototype._ul4onattrs = [...Control.prototype._ul4onattrs, "lookup_app", "lookup_controls", "local_master_control", "local_detail_controls", "remote_master_control"];
1964
+ AppLookupControl.prototype._ul4attrs = new Set([...Control.prototype._ul4attrs, "lookup_app", "lookup_controls", "local_master_control", "local_detail_controls", "remote_master_control"]);
1965
+
718
1966
 
719
1967
  export class AppLookupSelectControl extends AppLookupControl
720
1968
  {
1969
+ static classdoc = "A LivingApps applookup field (type 'applookup/select')";
1970
+
721
1971
  };
722
1972
 
723
1973
  AppLookupSelectControl.prototype.subtype = "select";
724
1974
 
1975
+
725
1976
  export class AppLookupRadioControl extends AppLookupControl
726
1977
  {
1978
+ static classdoc = "A LivingApps applookup field (type 'applookup/radio')";
1979
+
727
1980
  };
728
1981
 
729
1982
  AppLookupRadioControl.prototype.subtype = "radio";
730
1983
 
1984
+
731
1985
  export class AppLookupChoiceControl extends AppLookupControl
732
1986
  {
1987
+ static classdoc = "A LivingApps applookup field (type 'applookup/choice')";
1988
+
733
1989
  };
734
1990
 
735
1991
  AppLookupChoiceControl.prototype.subtype = "choice";
736
1992
 
1993
+
737
1994
  export class MultipleLookupControl extends LookupControl
738
1995
  {
1996
+ get default()
1997
+ {
1998
+ let view_control = this._view_control();
1999
+
2000
+ if (view_control === null)
2001
+ return null;
2002
+
2003
+ let defaultValue = this.lookupdata.get(view_control)
2004
+
2005
+ return (defaultValue !== undefined) ? [defaultValue] : [];
2006
+ }
2007
+
739
2008
  // search.value must be ``null`` or a ``LookupItem`` key
740
2009
  search(value, search)
741
2010
  {
@@ -754,25 +2023,36 @@ export class MultipleLookupControl extends LookupControl
754
2023
  };
755
2024
 
756
2025
  MultipleLookupControl.prototype.subtype = "multiplelookup";
2026
+ MultipleLookupControl.prototype.fieldtype = MultipleLookupField;
2027
+
757
2028
 
758
2029
  export class MultipleLookupSelectControl extends MultipleLookupControl
759
2030
  {
2031
+ static classdoc = "A LivingApps multiplelookup field (type 'multiplelookup/select')";
2032
+
760
2033
  };
761
2034
 
762
2035
  MultipleLookupSelectControl.prototype.subtype = "select";
763
2036
 
2037
+
764
2038
  export class MultipleLookupCheckboxControl extends MultipleLookupControl
765
2039
  {
2040
+ static classdoc = "A LivingApps multiplelookup field (type 'multiplelookup/checkbox')";
2041
+
766
2042
  };
767
2043
 
768
2044
  MultipleLookupCheckboxControl.prototype.subtype = "checkbox";
769
2045
 
2046
+
770
2047
  export class MultipleLookupChoiceControl extends MultipleLookupControl
771
2048
  {
2049
+ static classdoc = "A LivingApps multiplelookup field (type 'multiplelookup/choice')";
2050
+
772
2051
  };
773
2052
 
774
2053
  MultipleLookupChoiceControl.prototype.subtype = "choice";
775
2054
 
2055
+
776
2056
  export class MultipleAppLookupControl extends AppLookupControl
777
2057
  {
778
2058
  // ``search.value`` must be an object containing the search criteria for the referenced record
@@ -798,42 +2078,61 @@ export class MultipleAppLookupControl extends AppLookupControl
798
2078
  };
799
2079
 
800
2080
  MultipleAppLookupControl.prototype.type = "multipleapplookup";
2081
+ MultipleAppLookupControl.prototype.fieldtype = MultipleAppLookupField;
2082
+
801
2083
 
802
2084
  export class MultipleAppLookupSelectControl extends MultipleAppLookupControl
803
2085
  {
2086
+ static classdoc = "A LivingApps multiple applookup field (type 'multipleapplookup/select')";
2087
+
804
2088
  };
805
2089
 
806
2090
  MultipleAppLookupSelectControl.prototype.subtype = "select";
807
2091
 
2092
+
808
2093
  export class MultipleAppLookupCheckboxControl extends MultipleAppLookupControl
809
2094
  {
2095
+ static classdoc = "A LivingApps multiple applookup field (type 'multipleapplookup/checkbox')";
2096
+
810
2097
  };
811
2098
 
812
2099
  MultipleAppLookupCheckboxControl.prototype.subtype = "checkbox";
813
2100
 
2101
+
814
2102
  export class MultipleAppLookupChoiceControl extends MultipleAppLookupControl
815
2103
  {
2104
+ static classdoc = "A LivingApps multiple applookup field (type 'multipleapplookup/choice')";
816
2105
  };
817
2106
 
818
2107
  MultipleAppLookupChoiceControl.prototype.subtype = "choice";
819
2108
 
2109
+
820
2110
  export class GeoControl extends Control
821
2111
  {
2112
+ static classdoc = "A LivingApps geo field (type 'geo')";
822
2113
  };
823
2114
 
824
2115
  GeoControl.prototype.type = "geo";
2116
+ GeoControl.prototype.fieldtype = GeoField;
2117
+
825
2118
 
826
2119
  export class FileControl extends Control
827
2120
  {
2121
+ static classdoc = "A LivingApps upload field (type 'file')";
828
2122
  };
829
2123
 
830
2124
  FileControl.prototype.type = "file";
2125
+ FileControl.prototype.fieldtype = FileField;
2126
+
831
2127
 
832
2128
  export class FileSignatureControl extends FileControl
833
2129
  {
2130
+ static classdoc = "A LivingApps signature image field (type 'file/signature')";
834
2131
  };
835
2132
 
836
2133
  FileSignatureControl.prototype.subtype = "signature";
2134
+ FileSignatureControl.prototype.fieldtype = FileSignatureField;
2135
+
837
2136
 
838
2137
  export class ButtonControl extends Control
839
2138
  {
@@ -841,165 +2140,237 @@ export class ButtonControl extends Control
841
2140
 
842
2141
  ButtonControl.prototype.type = "button";
843
2142
 
844
- export class Field extends Base
2143
+
2144
+ export class LookupItem extends Base
845
2145
  {
846
- constructor(control, record, value)
2146
+ static classdoc = "An option in a lookup control/field";
2147
+
2148
+ get label()
847
2149
  {
848
- super();
849
- this.control = control;
850
- this.record = record;
851
- this._value = value;
852
- this._dirty = false;
853
- this.errors = [];
2150
+ if (this.control === null)
2151
+ return this._label;
2152
+ let view_lookupitem = this._view_lookupitem();
2153
+ if (view_lookupitem === null)
2154
+ return this._label;
2155
+ let label = view_lookupitem.label;
2156
+ if (label === null)
2157
+ return this._label;
2158
+ return label;
854
2159
  }
855
2160
 
856
- get value()
2161
+ get visible()
857
2162
  {
858
- return this._value;
2163
+ if (this.control === null)
2164
+ return true;
2165
+ let view_lookupitem = this._view_lookupitem();
2166
+ if (view_lookupitem === null)
2167
+ return true;
2168
+ return view_lookupitem.visible;
859
2169
  }
860
2170
 
861
- set value(value)
2171
+ _view_lookupitem()
862
2172
  {
863
- let oldvalue = this._value;
864
-
865
- if (ul4._ne(oldvalue, value))
2173
+ let identifier = this.control.identifier;
2174
+ let active_view = this.control.app.active_view;
2175
+ if (active_view !== null && active_view.controls !== null)
866
2176
  {
867
- this.record.values.set(this.control.identifier, value);
868
- this._value = value;
869
- this._dirty = true;
2177
+ let view_control = active_view.controls.get(identifier);
2178
+ if (view_control !== undefined && view_control.lookupdata !== undefined && view_control.lookupdata !== null)
2179
+ {
2180
+ let view_lookupitem = view_control.lookupdata.get(this.key);
2181
+ if (view_lookupitem !== undefined)
2182
+ return view_lookupitem;
2183
+ }
870
2184
  }
2185
+ return null;
871
2186
  }
872
2187
 
873
- is_empty()
2188
+ [ul4.symbols.repr]()
874
2189
  {
875
- return this._value === null || (ul4._islist(this._value) && this._value.length === 0);
2190
+ let repr = "<LookupItem key=" + ul4._repr(this.key) + " label=" + ul4._repr(this.label);
2191
+ if (!this.visible)
2192
+ repr += "visible=False"
2193
+ return repr + ">";
876
2194
  }
2195
+ };
877
2196
 
878
- is_dirty()
879
- {
880
- return this._dirty;
881
- }
2197
+ LookupItem.prototype._ul4onattrs = ["control", "key", "_label"];
2198
+ LookupItem.prototype._ul4attrs = new Set(["id", "control", "key", "label"]);
882
2199
 
883
- has_errors()
884
- {
885
- return this.errors.length !== 0;
886
- }
887
2200
 
888
- search(searchvalue)
2201
+ export class ViewLookupItem extends Base
2202
+ {
2203
+ static classdoc = "View specific information about a lookup item";
2204
+
2205
+ [ul4.symbols.repr]()
889
2206
  {
890
- return this.control.search(this.value, searchvalue);
2207
+ let repr = "<ViewLookupItem key=" + ul4._repr(this.key) + " label=" + ul4._repr(this.label);
2208
+ if (!this.visible)
2209
+ repr += " visible=False"
2210
+ return repr + ">";
891
2211
  }
2212
+ };
2213
+
2214
+ ViewLookupItem.prototype._ul4onattrs = ["key", "label", "visible"];
2215
+ ViewLookupItem.prototype._ul4attrs = new Set(["key", "label", "visible"]);
892
2216
 
893
- __repr__()
2217
+
2218
+ export class LayoutControl extends Base
2219
+ {
2220
+ static classdoc = "A decoration in an input form";
2221
+
2222
+ [ul4.symbols.repr]()
894
2223
  {
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;
2224
+ return "<" + this.constructor.name + " id=" + ul4._repr(this.id) + " identifier=" + ul4._repr(this.identifier) + ">";
903
2225
  }
904
2226
  };
905
2227
 
906
- export class LookupItem extends Base
2228
+ LayoutControl.prototype._ul4onattrs = ["label", "identifier", "view", "top", "left", "width", "height"];
2229
+ LayoutControl.prototype._ul4attrs = new Set(["id", "label", "identifier", "view", "top", "left", "width", "height"]);
2230
+
2231
+
2232
+ export class HTMLLayoutControl extends LayoutControl
2233
+ {
2234
+ static classdoc = "An HTML decoration in an input form";
2235
+ };
2236
+
2237
+ HTMLLayoutControl.prototype._ul4onattrs = [...LayoutControl.prototype._ul4onattrs, "value"];
2238
+ HTMLLayoutControl.prototype._ul4attrs = new Set([LayoutControl.prototype._ul4attrs, "value"]);
2239
+
2240
+
2241
+ export class ImageLayoutControl extends LayoutControl
2242
+ {
2243
+ static classdoc = "An image decoration in an input form";
2244
+ };
2245
+
2246
+ ImageLayoutControl.prototype._ul4onattrs = [...LayoutControl.prototype._ul4onattrs, "original", "scaled"];
2247
+ ImageLayoutControl.prototype._ul4attrs = new Set([LayoutControl.prototype._ul4attrs, "original", "scaled"]);
2248
+
2249
+
2250
+ export class ViewControl extends Base
907
2251
  {
908
- __repr__()
2252
+ static classdoc = "Contains view specific information aboutn a control";
2253
+
2254
+ get mode()
909
2255
  {
910
- return "<LookupItem key=" + ul4._repr(this.key) + " label=" + ul4._repr(this.label) + ">";
2256
+ return this._mode ? "display" : "edit";
911
2257
  }
912
2258
  };
913
2259
 
914
- LookupItem.prototype._ul4onattrs = ["key", "label"];
915
- LookupItem.prototype._ul4attrs = ul4._makeset("key", "label");
2260
+ 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"];
2261
+ 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"]);
2262
+
916
2263
 
917
2264
  export class User extends Base
918
2265
  {
919
- __repr__()
2266
+ static classdoc = "A LivingApps user/account";
2267
+
2268
+ [ul4.symbols.repr]()
920
2269
  {
921
2270
  return "<User id=" + ul4._repr(this.id) + " firstname=" + ul4._repr(this.firstname) + " surname=" + ul4._repr(this.surname) + " email=" + ul4._repr(this.email) + ">";
922
2271
  }
923
2272
  };
924
2273
 
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");
2274
+ 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"];
2275
+ 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"]);
2276
+
927
2277
 
928
2278
  export class File extends Base
929
2279
  {
930
- __repr__()
2280
+ static classdoc = "An uploaded file";
2281
+
2282
+ [ul4.symbols.repr]()
931
2283
  {
932
2284
  return "<File id=" + ul4._repr(this.id) + " url=" + ul4._repr(this.url) + " filename=" + ul4._repr(this.filename) + ">";
933
2285
  }
934
2286
  };
935
2287
 
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");
2288
+ File.prototype._ul4onattrs = ["url", "filename", "mimetype", "width", "height", "internalid", "createdat", "size"];
2289
+ File.prototype._ul4attrs = new Set(["id", "url", "filename", "mimetype", "width", "height", "size", "createdat"]);
2290
+
938
2291
 
939
2292
  export class Geo extends Base
940
2293
  {
2294
+ static classdoc = "Geographical coordinates and location information";
2295
+
941
2296
  constructor(lat, long, info)
942
2297
  {
943
- super();
2298
+ super(null);
944
2299
  this.lat = lat;
945
2300
  this.long = long;
946
2301
  this.info = info;
947
2302
  }
948
2303
 
949
- __repr__()
2304
+ [ul4.symbols.repr]()
950
2305
  {
951
2306
  return "<Geo lat=" + ul4._repr(this.lat) + " long=" + ul4._repr(this.long) + " info=" + ul4._repr(this.info) + ">";
952
2307
  }
953
2308
  };
954
2309
 
955
2310
  Geo.prototype._ul4onattrs = ["lat", "long", "info"];
956
- Geo.prototype._ul4attrs = ul4._makeset("lat", "long", "info");
2311
+ Geo.prototype._ul4attrs = new Set(["lat", "long", "info"]);
2312
+
957
2313
 
958
2314
  export class Attachment extends Base
959
2315
  {
960
- __repr__()
2316
+ [ul4.symbols.repr]()
961
2317
  {
962
- return "<" + this.__type__ + " id=" + ul4._repr(this.id) + " label=" + ul4._repr(this.label) + ">";
2318
+ return "<" + this.constructor.name + " id=" + ul4._repr(this.id) + " label=" + ul4._repr(this.label) + ">";
963
2319
  }
964
2320
  };
965
2321
 
966
- Attachment.prototype._ul4onattrs = ["id", "record", "label", "active"];
967
- Attachment.prototype._ul4attrs = ul4._makeset("id", "record", "label", "active");
2322
+ Attachment.prototype._ul4onattrs = ["record", "label", "active"];
2323
+ Attachment.prototype._ul4attrs = new Set(["id", "record", "label", "active"]);
2324
+
968
2325
 
969
2326
  export class NoteAttachment extends Attachment
970
2327
  {
2328
+ static classdoc = "A note attachment of a record";
2329
+
971
2330
  };
972
2331
 
973
2332
  NoteAttachment.prototype.type = "noteattachment";
974
- NoteAttachment.prototype._ul4onattrs = Attachment.prototype._ul4onattrs.concat(["value"]);
975
- NoteAttachment.prototype._ul4attrs = ul4._makeset(...Attachment.prototype._ul4onattrs, "value");
2333
+ NoteAttachment.prototype._ul4onattrs = [...Attachment.prototype._ul4onattrs, "value"];
2334
+ NoteAttachment.prototype._ul4attrs = new Set([...Attachment.prototype._ul4onattrs, "value"]);
2335
+
976
2336
 
977
2337
  export class URLAttachment extends Attachment
978
2338
  {
2339
+ static classdoc = "A URL attachment of a record";
2340
+
979
2341
  };
980
2342
 
981
2343
  URLAttachment.prototype.type = "urlattachment";
982
- URLAttachment.prototype._ul4onattrs = Attachment.prototype._ul4onattrs.concat(["value"]);
983
- URLAttachment.prototype._ul4attrs = ul4._makeset(...Attachment.prototype._ul4onattrs, "value");
2344
+ URLAttachment.prototype._ul4onattrs = [...Attachment.prototype._ul4onattrs, "value"];
2345
+ URLAttachment.prototype._ul4attrs = new Set([...Attachment.prototype._ul4onattrs, "value"]);
2346
+
984
2347
 
985
2348
  export class FileAttachment extends Attachment
986
2349
  {
2350
+ static classdoc = "A file attachment of a record";
2351
+
987
2352
  };
988
2353
 
989
2354
  FileAttachment.prototype.type = "fileattachment";
990
- FileAttachment.prototype._ul4onattrs = Attachment.prototype._ul4onattrs.concat(["value"]);
991
- FileAttachment.prototype._ul4attrs = ul4._makeset(...Attachment.prototype._ul4onattrs, "value");
2355
+ FileAttachment.prototype._ul4onattrs = [...Attachment.prototype._ul4onattrs, "value"];
2356
+ FileAttachment.prototype._ul4attrs = new Set([...Attachment.prototype._ul4onattrs, "value"]);
2357
+
992
2358
 
993
2359
  export class ImageAttachment extends Attachment
994
2360
  {
2361
+ static classdoc = "An image attachment of a record";
2362
+
995
2363
  };
996
2364
 
997
2365
  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");
2366
+ ImageAttachment.prototype._ul4onattrs = [...Attachment.prototype._ul4onattrs, "original", "thumb", "small", "medium","large"];
2367
+ ImageAttachment.prototype._ul4attrs = new Set([...Attachment.prototype._ul4onattrs, "original", "thumb", "small", "medium", "large"]);
2368
+
1000
2369
 
1001
2370
  export class JSONAttachment extends Attachment
1002
2371
  {
2372
+ static classdoc = "A JSON attachment of a record";
2373
+
1003
2374
  _dumpUL4ONAttr(name)
1004
2375
  {
1005
2376
  if (name === "value")
@@ -1018,23 +2389,29 @@ export class JSONAttachment extends Attachment
1018
2389
  };
1019
2390
 
1020
2391
  JSONAttachment.prototype.type = "jsonattachment";
1021
- JSONAttachment.prototype._ul4onattrs = Attachment.prototype._ul4onattrs.concat(["value"]);
1022
- JSONAttachment.prototype._ul4attrs = ul4._makeset(...Attachment.prototype._ul4onattrs, "value");
2392
+ JSONAttachment.prototype._ul4onattrs = [...Attachment.prototype._ul4onattrs, "value"];
2393
+ JSONAttachment.prototype._ul4attrs = new Set([...Attachment.prototype._ul4onattrs, "value"]);
2394
+
1023
2395
 
1024
2396
  export class Installation extends Base
1025
2397
  {
1026
- __repr__()
2398
+ static classdoc = "The installation that created an app";
2399
+
2400
+ [ul4.symbols.repr]()
1027
2401
  {
1028
2402
  return "<Installation id=" + ul4._repr(this.id) + " name=" + ul4._repr(this.name) + ">";
1029
2403
  }
1030
2404
  };
1031
2405
 
1032
- Installation.prototype._ul4onattrs = ["id", "name"];
1033
- Installation.prototype._ul4attrs = ul4._makeset("id", "name");
2406
+ Installation.prototype._ul4onattrs = ["name"];
2407
+ Installation.prototype._ul4attrs = new Set(["id", "name"]);
2408
+
1034
2409
 
1035
2410
  export class Category extends Base
1036
2411
  {
1037
- __repr__()
2412
+ static classdoc = "A navigation category";
2413
+
2414
+ [ul4.symbols.repr]()
1038
2415
  {
1039
2416
  let v = [];
1040
2417
  let category = this;
@@ -1047,30 +2424,37 @@ export class Category extends Base
1047
2424
  }
1048
2425
  };
1049
2426
 
1050
- Category.prototype._ul4onattrs = ["id", "identifier", "name", "order", "parent", "children", "apps"];
1051
- Category.prototype._ul4attrs = ul4._makeset("id", "identifier", "name", "order", "parent", "children", "apps");
2427
+ Category.prototype._ul4onattrs = ["identifier", "name", "order", "parent", "children", "apps"];
2428
+ Category.prototype._ul4attrs = new Set(["id", "identifier", "name", "order", "parent", "children", "apps"]);
2429
+
1052
2430
 
1053
2431
  export class KeyView extends Base
1054
2432
  {
1055
- __repr__()
2433
+ static classdoc = "Object granting access to a view template";
2434
+
2435
+ [ul4.symbols.repr]()
1056
2436
  {
1057
2437
  return "<KeyView id=" + ul4._repr(this.id) + " identifier=" + ul4._repr(this.identifier) + ">";
1058
2438
  }
1059
2439
  };
1060
2440
 
1061
- KeyView.prototype._ul4onattrs = ["id", "identifier", "name", "key", "user"];
1062
- KeyView.prototype._ul4attrs = ul4._makeset("id", "identifier", "name", "key", "user");
2441
+ KeyView.prototype._ul4onattrs = ["identifier", "name", "key", "user"];
2442
+ KeyView.prototype._ul4attrs = new Set(["id", "identifier", "name", "key", "user"]);
2443
+
1063
2444
 
1064
2445
  export class AppParameter extends Base
1065
2446
  {
1066
- __repr__()
2447
+ static classdoc = "A parameter of a LivingApps application";
2448
+
2449
+ [ul4.symbols.repr]()
1067
2450
  {
1068
2451
  return "<AppParameter id=" + ul4._repr(this.id) + " identifier=" + ul4._repr(this.identifier) + ">";
1069
2452
  }
1070
2453
  };
1071
2454
 
1072
- AppParameter.prototype._ul4onattrs = ["id", "app", "identifier", "description", "value"];
1073
- AppParameter.prototype._ul4attrs = ul4._makeset("id", "app", "identifier", "description", "value");
2455
+ AppParameter.prototype._ul4onattrs = ["app", "identifier", "description", "value"];
2456
+ AppParameter.prototype._ul4attrs = new Set(["id", "app", "identifier", "description", "value"]);
2457
+
1074
2458
 
1075
2459
  let classes = [
1076
2460
  Globals,
@@ -1086,7 +2470,6 @@ let classes = [
1086
2470
  EmailControl,
1087
2471
  URLControl,
1088
2472
  TelControl,
1089
- PasswordControl,
1090
2473
  TextAreaControl,
1091
2474
  DateControl,
1092
2475
  DatetimeMinuteControl,
@@ -1109,9 +2492,37 @@ let classes = [
1109
2492
  MultipleAppLookupChoiceControl,
1110
2493
  GeoControl,
1111
2494
  FileControl,
2495
+ FileSignatureControl,
1112
2496
  ButtonControl,
1113
2497
  Field,
2498
+ BoolField,
2499
+ StringField,
2500
+ EmailField,
2501
+ URLField,
2502
+ TelField,
2503
+ TextAreaField,
2504
+ HTMLField,
2505
+ IntField,
2506
+ NumberField,
2507
+ GeoField,
2508
+ FileField,
2509
+ FileSignatureField,
2510
+ DateFieldBase,
2511
+ DateField,
2512
+ DatetimeMinuteField,
2513
+ DatetimeSecondField,
2514
+ LookupFieldBase,
2515
+ LookupField,
2516
+ MultipleLookupField,
2517
+ AppLookupFieldBase,
2518
+ AppLookupField,
2519
+ MultipleAppLookupField,
1114
2520
  LookupItem,
2521
+ ViewLookupItem,
2522
+ LayoutControl,
2523
+ HTMLLayoutControl,
2524
+ ImageLayoutControl,
2525
+ ViewControl,
1115
2526
  User,
1116
2527
  File,
1117
2528
  Geo,