@livinglogic/livingapi 0.11.0 → 0.13.1

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