jsgui3-server 0.0.124 → 0.0.126
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/README.md
CHANGED
|
@@ -1,10 +1,1026 @@
|
|
|
1
1
|
# Jsgui3 Server
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
JSGUI3 Server is a comprehensive Node.js-based application server designed to serve modern JavaScript GUI applications to web clients. It provides a complete runtime environment for delivering dynamic, component-based user interfaces with integrated data binding, event handling, and real-time synchronization.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Architecture Overview
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
The server operates as a bridge between server-side JavaScript applications and browser clients, offering:
|
|
8
|
+
|
|
9
|
+
- **Component Serving:** Bundles and serves JSGUI3 controls (Windows, Date_Pickers, Checkboxes, etc.) to browsers
|
|
10
|
+
- **Data Model Management:** Handles shared data objects and real-time synchronization between multiple UI controls
|
|
11
|
+
- **CSS Bundling:** Dynamically compiles and serves CSS from component definitions
|
|
12
|
+
- **Event Coordination:** Manages client-server event communication and state synchronization
|
|
13
|
+
|
|
14
|
+
## Core Components
|
|
15
|
+
|
|
16
|
+
### Active_HTML_Document
|
|
17
|
+
Base class for all server-rendered applications. Provides:
|
|
18
|
+
- Automatic activation lifecycle management
|
|
19
|
+
- CSS injection and bundling
|
|
20
|
+
- Context-aware control composition
|
|
21
|
+
- Event handling infrastructure
|
|
22
|
+
|
|
23
|
+
### Control System
|
|
24
|
+
JSGUI3 uses a hierarchical control system where:
|
|
25
|
+
- **Controls** are UI components (buttons, text fields, windows, etc.)
|
|
26
|
+
- **Containers** hold other controls (windows, panels, grids)
|
|
27
|
+
- **Data Models** provide shared state using `Data_Object` instances
|
|
28
|
+
- **Context** manages the runtime environment and event coordination
|
|
29
|
+
|
|
30
|
+
### Data Binding Architecture
|
|
31
|
+
The framework implements a sophisticated data binding system:
|
|
32
|
+
- `Data_Object` instances serve as observable data models
|
|
33
|
+
- `field()` function from 'obext' creates reactive properties
|
|
34
|
+
- Controls automatically update when their bound data changes
|
|
35
|
+
- Multiple controls can share the same data model for real-time synchronization
|
|
36
|
+
|
|
37
|
+
## Example Applications
|
|
38
|
+
|
|
39
|
+
The `/examples/controls/` directory contains numerous demonstrations:
|
|
40
|
+
|
|
41
|
+
- **Window + Checkbox:** Basic control composition and event handling
|
|
42
|
+
- **Window + Tabbed Panel:** Container controls with multiple child components
|
|
43
|
+
- **Window + Month View:** Calendar/date selection interfaces
|
|
44
|
+
- **Shared Data Model Date Pickers:** Advanced data binding with synchronized controls
|
|
45
|
+
|
|
46
|
+
Each example follows the pattern:
|
|
47
|
+
1. `client.js` - Defines the UI components and their composition
|
|
48
|
+
2. `server.js` - Configures and starts the application server
|
|
49
|
+
3. `README.md` - Documentation specific to that example
|
|
50
|
+
|
|
51
|
+
2022 Note: The server architecture is designed to be extensible with compilers and data transformers, allowing it to serve as a development and testing platform for various content processing pipelines.
|
|
52
|
+
|
|
53
|
+
## Server Resources and Resource Pools
|
|
54
|
+
|
|
55
|
+
Jsgui3 Server manages various runtime resources through configurable pools to ensure high performance and scalability:
|
|
56
|
+
|
|
57
|
+
- **Connection Pool:** Reuses client sockets and HTTP connections to minimize overhead.
|
|
58
|
+
- **Thread/Worker Pool:** Allocates and recycles background workers for tasks like compilation and data transformation.
|
|
59
|
+
- **Cache Pool:** Stores compiled templates, transformed data, and static assets in memory for quick retrieval.
|
|
60
|
+
- **Data Model Pool:** Pre-allocates shared data models (e.g., `Data_Object`) to reduce allocation costs and speed up UI control instantiation.
|
|
61
|
+
|
|
62
|
+
Each pool can be tuned via server configuration (e.g., `server.config.js`), allowing you to adjust sizes and timeouts to match your application's workload.
|
|
63
|
+
|
|
64
|
+
## Technical Architecture Deep Dive
|
|
65
|
+
|
|
66
|
+
### Server Initialization and Lifecycle
|
|
67
|
+
|
|
68
|
+
The JSGUI3 server follows a structured initialization pattern:
|
|
69
|
+
|
|
70
|
+
1. **Server Construction:**
|
|
71
|
+
- Takes a `Ctrl` parameter (the main UI control class)
|
|
72
|
+
- Accepts `src_path_client_js` pointing to the client-side entry point
|
|
73
|
+
- Bundles client code and prepares serving infrastructure
|
|
74
|
+
|
|
75
|
+
2. **Event-Driven Startup:**
|
|
76
|
+
- Emits 'ready' event when bundling and preparation is complete
|
|
77
|
+
- `server.start(port, callback)` begins HTTP listening
|
|
78
|
+
- Multiple console.log statements track startup progress
|
|
79
|
+
|
|
80
|
+
3. **Request Handling:**
|
|
81
|
+
- Serves bundled JavaScript to browsers
|
|
82
|
+
- Injects CSS from control definitions
|
|
83
|
+
- Manages WebSocket connections for real-time updates
|
|
84
|
+
|
|
85
|
+
### Control Hierarchy and Composition
|
|
86
|
+
|
|
87
|
+
Controls in JSGUI3 follow a strict composition pattern:
|
|
88
|
+
|
|
89
|
+
```javascript
|
|
90
|
+
// Basic control creation pattern
|
|
91
|
+
const control = new controls.ControlType({
|
|
92
|
+
context: context, // Required runtime context
|
|
93
|
+
// ... other properties
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// Container pattern
|
|
97
|
+
container.inner.add(childControl); // Adding to containers
|
|
98
|
+
this.body.add(topLevelControl); // Adding to document body
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
**Key Control Types:**
|
|
102
|
+
- **Window:** Top-level container with title bar, positioning, and dragging capabilities
|
|
103
|
+
- **Tabbed_Panel:** Container organizing content into tabs, supports both string labels and [label, control] pairs
|
|
104
|
+
- **Date_Picker:** Input control for date selection with data model binding
|
|
105
|
+
- **Checkbox:** Boolean input with label support
|
|
106
|
+
- **Month_View:** Calendar display widget
|
|
107
|
+
|
|
108
|
+
### Data Model System Details
|
|
109
|
+
|
|
110
|
+
The data binding system is built on observable patterns:
|
|
111
|
+
|
|
112
|
+
1. **Data_Object Creation:**
|
|
113
|
+
```javascript
|
|
114
|
+
const model = new Data_Object({ context });
|
|
115
|
+
field(model, 'fieldName'); // Creates reactive property
|
|
116
|
+
context.register_control(model); // Registers for lifecycle management
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
2. **Control Binding:**
|
|
120
|
+
```javascript
|
|
121
|
+
const control = new SomeControl({
|
|
122
|
+
context,
|
|
123
|
+
data: { model: sharedDataModel }
|
|
124
|
+
});
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
3. **Change Handler Management:**
|
|
128
|
+
- Controls automatically register change handlers on their data models
|
|
129
|
+
- `assign_data_model_value_change_handler()` method reestablishes bindings
|
|
130
|
+
- Multiple controls can share the same model for synchronized updates
|
|
131
|
+
|
|
132
|
+
### File Structure and Conventions
|
|
133
|
+
|
|
134
|
+
```
|
|
135
|
+
jsgui3-server/
|
|
136
|
+
├── examples/
|
|
137
|
+
│ └── controls/
|
|
138
|
+
│ ├── 4) window, tabbed panel/
|
|
139
|
+
│ │ ├── client.js # UI definition
|
|
140
|
+
│ │ └── server.js # Server setup
|
|
141
|
+
│ ├── 7) window, month_view/
|
|
142
|
+
│ ├── 8) window, checkbox/
|
|
143
|
+
│ └── 9b) window, shared data.model mirrored date pickers/
|
|
144
|
+
├── controls/
|
|
145
|
+
│ └── Active_HTML_Document.js # Base class for all UIs
|
|
146
|
+
└── server/ # Core server implementation
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
**Naming Conventions:**
|
|
150
|
+
- Example directories use numbered prefixes for ordering
|
|
151
|
+
- `client.js` always contains UI control definitions
|
|
152
|
+
- `server.js` follows standard server startup pattern
|
|
153
|
+
- Control classes use PascalCase (e.g., `Demo_UI`, `Date_Picker`)
|
|
154
|
+
|
|
155
|
+
### Context and Runtime Environment
|
|
156
|
+
|
|
157
|
+
The `context` object is central to JSGUI3 operation:
|
|
158
|
+
|
|
159
|
+
- **Control Registration:** Tracks all controls for lifecycle management
|
|
160
|
+
- **Event Coordination:** Routes events between controls and server
|
|
161
|
+
- **Resource Management:** Handles cleanup and memory management
|
|
162
|
+
- **State Synchronization:** Maintains consistency across client-server boundary
|
|
163
|
+
|
|
164
|
+
### CSS Integration Patterns
|
|
165
|
+
|
|
166
|
+
CSS is defined as template literals within control classes:
|
|
167
|
+
|
|
168
|
+
```javascript
|
|
169
|
+
Demo_UI.css = `
|
|
170
|
+
* { margin: 0; padding: 0; }
|
|
171
|
+
body {
|
|
172
|
+
overflow-x: hidden;
|
|
173
|
+
overflow-y: hidden;
|
|
174
|
+
background-color: #E0E0E0;
|
|
175
|
+
}
|
|
176
|
+
.demo-ui {
|
|
177
|
+
/* commented styles provide optional layouts */
|
|
178
|
+
}`;
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
- Global resets are common across examples
|
|
182
|
+
- Body styling typically prevents scrollbars for desktop app feel
|
|
183
|
+
- Comments within CSS preserve alternative styling options
|
|
184
|
+
- Multi-line formatting is preserved for readability
|
|
185
|
+
|
|
186
|
+
### Error Handling and Consistency Checks
|
|
187
|
+
|
|
188
|
+
The framework includes several consistency verification patterns:
|
|
189
|
+
|
|
190
|
+
1. **Model Consistency:** Activation methods verify shared data models remain synchronized
|
|
191
|
+
2. **Type Checking:** `typeof this.body.add_class === 'function'` guards against missing methods
|
|
192
|
+
3. **Conditional Composition:** `if (!spec.el) { compose(); }` prevents double-initialization
|
|
193
|
+
4. **Error Propagation:** Server startup includes error handling: `if (err) throw err;`
|
|
194
|
+
|
|
195
|
+
### Development and Debugging Features
|
|
196
|
+
|
|
197
|
+
- **Console Logging:** Extensive logging during server startup and events
|
|
198
|
+
- **Port Configuration:** Standardized on port 52000 for examples
|
|
199
|
+
- **Hot Reloading:** Server can be restarted to pick up client.js changes
|
|
200
|
+
- **Activation Lifecycle:** `activate()` method provides hook for post-initialization logic
|
|
201
|
+
|
|
202
|
+
## Dependencies and Integration
|
|
203
|
+
|
|
204
|
+
### Core Dependencies
|
|
205
|
+
|
|
206
|
+
- **jsgui3-client:** Client-side framework providing controls and data binding
|
|
207
|
+
- **obext:** Object extension library providing the `field()` function for reactive properties
|
|
208
|
+
- **Node.js:** Server runtime environment
|
|
209
|
+
|
|
210
|
+
### Integration Patterns
|
|
211
|
+
|
|
212
|
+
- **Require Resolution:** Uses `require.resolve('./client.js')` for reliable path resolution
|
|
213
|
+
- **Module Exports:** Each client.js exports the jsgui object with added controls
|
|
214
|
+
- **Control Registration:** `controls.Demo_UI = Demo_UI;` makes controls available globally
|
|
215
|
+
|
|
216
|
+
## Programming Patterns and Code Structure for AI Understanding
|
|
217
|
+
|
|
218
|
+
### Constructor Patterns
|
|
219
|
+
|
|
220
|
+
All JSGUI3 controls follow a consistent constructor pattern:
|
|
221
|
+
|
|
222
|
+
```javascript
|
|
223
|
+
class Demo_UI extends Active_HTML_Document {
|
|
224
|
+
constructor(spec = {}) {
|
|
225
|
+
spec.__type_name = spec.__type_name || 'demo_ui'; // Type identification
|
|
226
|
+
super(spec); // Call parent constructor
|
|
227
|
+
const { context } = this; // Extract context reference
|
|
228
|
+
|
|
229
|
+
// Optional CSS class application
|
|
230
|
+
if (typeof this.body.add_class === 'function') {
|
|
231
|
+
this.body.add_class('demo-ui');
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Define composition function
|
|
235
|
+
const compose = () => {
|
|
236
|
+
// UI creation logic here
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
// Conditional composition (only if no external DOM element provided)
|
|
240
|
+
if (!spec.el) { compose(); }
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
**Key Pattern Elements:**
|
|
246
|
+
- `spec` parameter is configuration object, always defaulted to `{}`
|
|
247
|
+
- `__type_name` provides runtime type identification
|
|
248
|
+
- `context` extraction is standard pattern for accessing framework services
|
|
249
|
+
- Defensive programming with `typeof` checks prevents runtime errors
|
|
250
|
+
- Conditional composition prevents double-initialization when control is attached to existing DOM
|
|
251
|
+
|
|
252
|
+
### Activation Lifecycle Pattern
|
|
253
|
+
|
|
254
|
+
The activation pattern provides post-construction initialization:
|
|
255
|
+
|
|
256
|
+
```javascript
|
|
257
|
+
activate() {
|
|
258
|
+
if (!this.__active) { // Prevent double-activation
|
|
259
|
+
super.activate(); // Call parent activation
|
|
260
|
+
const { context } = this; // Re-extract context reference
|
|
261
|
+
|
|
262
|
+
// Control-specific activation logic
|
|
263
|
+
context.on('window-resize', e_resize => {
|
|
264
|
+
// Event handler logic
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
**Activation Principles:**
|
|
271
|
+
- `__active` flag prevents multiple activation
|
|
272
|
+
- Parent `activate()` must be called first
|
|
273
|
+
- Event listeners are typically registered during activation
|
|
274
|
+
- Context is re-extracted for consistency
|
|
275
|
+
|
|
276
|
+
### Data Model Binding Patterns
|
|
277
|
+
|
|
278
|
+
JSGUI3 implements reactive data binding through several patterns:
|
|
279
|
+
|
|
280
|
+
#### 1. Shared Model Creation
|
|
281
|
+
```javascript
|
|
282
|
+
// Create observable data model
|
|
283
|
+
this.data = { model: new Data_Object({ context }) };
|
|
284
|
+
field(this.data.model, 'value'); // Add reactive property
|
|
285
|
+
context.register_control(this.data.model); // Register for lifecycle management
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
#### 2. Control Data Binding
|
|
289
|
+
```javascript
|
|
290
|
+
const control = new Date_Picker({
|
|
291
|
+
context,
|
|
292
|
+
data: { model: this.data.model } // Bind to shared model
|
|
293
|
+
});
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
#### 3. Model Consistency Verification
|
|
297
|
+
```javascript
|
|
298
|
+
// In activate() method - verify model synchronization
|
|
299
|
+
if (date_picker_1.data.model !== date_picker_2.data.model) {
|
|
300
|
+
const dm = new Data_Object({ context });
|
|
301
|
+
field(dm, 'value');
|
|
302
|
+
date_picker_1.data.model = dm;
|
|
303
|
+
date_picker_2.data.model = dm;
|
|
304
|
+
date_picker_1.assign_data_model_value_change_handler();
|
|
305
|
+
date_picker_2.assign_data_model_value_change_handler();
|
|
306
|
+
}
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
### Server Startup Pattern
|
|
310
|
+
|
|
311
|
+
Every example follows identical server startup:
|
|
312
|
+
|
|
313
|
+
```javascript
|
|
314
|
+
const jsgui = require('./client'); // Import client-side definition
|
|
315
|
+
const { Demo_UI } = jsgui.controls; // Extract main control class
|
|
316
|
+
const Server = require('../../../server'); // Import server framework
|
|
317
|
+
|
|
318
|
+
if (require.main === module) { // Only run if directly executed
|
|
319
|
+
const server = new Server({
|
|
320
|
+
Ctrl: Demo_UI, // Main UI control class
|
|
321
|
+
src_path_client_js: require.resolve('./client.js') // Client bundle path
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
console.log('waiting for server ready event');
|
|
325
|
+
server.on('ready', () => { // Wait for bundling completion
|
|
326
|
+
console.log('server ready');
|
|
327
|
+
server.start(52000, (err) => { // Start HTTP server
|
|
328
|
+
if (err) throw err; // Propagate startup errors
|
|
329
|
+
console.log('server started');
|
|
330
|
+
});
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
## Memory Management and Lifecycle
|
|
336
|
+
|
|
337
|
+
### Control Reference Management
|
|
338
|
+
|
|
339
|
+
JSGUI3 maintains careful control references:
|
|
340
|
+
|
|
341
|
+
```javascript
|
|
342
|
+
// Store control references for later access
|
|
343
|
+
this._ctrl_fields = { date_picker_1, date_picker_2 };
|
|
344
|
+
|
|
345
|
+
// Access during activation
|
|
346
|
+
const { _ctrl_fields } = this;
|
|
347
|
+
const { date_picker_1, date_picker_2 } = _ctrl_fields;
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
### Context Registration
|
|
351
|
+
|
|
352
|
+
The context system manages control lifecycles:
|
|
353
|
+
|
|
354
|
+
- `context.register_control(model)` - Registers data models for cleanup
|
|
355
|
+
- Controls automatically register with context during construction
|
|
356
|
+
- Context handles cleanup when UI is destroyed
|
|
357
|
+
- Event listeners are automatically removed during cleanup
|
|
358
|
+
|
|
359
|
+
### CSS Memory Management
|
|
360
|
+
|
|
361
|
+
CSS is defined as static properties to prevent memory leaks:
|
|
362
|
+
|
|
363
|
+
```javascript
|
|
364
|
+
Demo_UI.css = `...`; // Static property, not instance property
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
This pattern ensures CSS definitions are shared across all instances rather than duplicated.
|
|
368
|
+
|
|
369
|
+
## Event System Architecture
|
|
370
|
+
|
|
371
|
+
### Event Registration Patterns
|
|
372
|
+
|
|
373
|
+
```javascript
|
|
374
|
+
// Standard event listener registration
|
|
375
|
+
context.on('window-resize', e_resize => {
|
|
376
|
+
// Handler logic - note arrow function preserves 'this' context
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
// Event handler with empty body (placeholder for future functionality)
|
|
380
|
+
context.on('window-resize', e_resize => { });
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
### Event Naming Conventions
|
|
384
|
+
|
|
385
|
+
- `window-resize` - Browser window size changes
|
|
386
|
+
- `data-model-value-change` - Data model property updates
|
|
387
|
+
- Control-specific events follow `control-event` pattern
|
|
388
|
+
|
|
389
|
+
## Control Composition Hierarchy
|
|
390
|
+
|
|
391
|
+
### Container Control Pattern
|
|
392
|
+
|
|
393
|
+
```javascript
|
|
394
|
+
// Create container
|
|
395
|
+
const window = new controls.Window({
|
|
396
|
+
context: context, // Required context
|
|
397
|
+
title: 'Window Title', // Window title bar text
|
|
398
|
+
pos: [10, 10] // Initial position [x, y]
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
// Add child controls to container's inner area
|
|
402
|
+
window.inner.add(childControl);
|
|
403
|
+
|
|
404
|
+
// Add top-level container to document body
|
|
405
|
+
this.body.add(window);
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
### Tabbed Panel Composition
|
|
409
|
+
|
|
410
|
+
```javascript
|
|
411
|
+
const tabbed_panel = new controls.Tabbed_Panel({
|
|
412
|
+
context,
|
|
413
|
+
tabs: [
|
|
414
|
+
// Simple string tabs
|
|
415
|
+
'tab 1', 'tab 2'
|
|
416
|
+
|
|
417
|
+
// OR complex tab definitions with controls
|
|
418
|
+
['tab 1', new Control({context, size: [250, 250], background: {color: '#553311'}})],
|
|
419
|
+
['tab 2', new Control({context, size: [250, 250], background: {color: '#1177AA'}})]
|
|
420
|
+
]
|
|
421
|
+
});
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
## Debugging and Development Aids
|
|
425
|
+
|
|
426
|
+
### Console Logging Strategy
|
|
427
|
+
|
|
428
|
+
JSGUI3 uses extensive console logging for development:
|
|
429
|
+
|
|
430
|
+
```javascript
|
|
431
|
+
console.log('waiting for server ready event'); // Server initialization
|
|
432
|
+
console.log('server ready'); // Bundling complete
|
|
433
|
+
console.log('server started'); // HTTP server listening
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
**Logging Conventions:**
|
|
437
|
+
- Present tense for ongoing states ("waiting", "starting")
|
|
438
|
+
- Past tense for completed actions ("ready", "started")
|
|
439
|
+
- No timestamps (rely on console timestamps)
|
|
440
|
+
- Lowercase, descriptive messages
|
|
441
|
+
|
|
442
|
+
### Error Handling Patterns
|
|
443
|
+
|
|
444
|
+
```javascript
|
|
445
|
+
// Standard error propagation
|
|
446
|
+
server.start(52000, (err) => {
|
|
447
|
+
if (err) throw err; // Re-throw errors for visibility
|
|
448
|
+
console.log('server started'); // Only log success if no error
|
|
449
|
+
});
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
### Type Safety Patterns
|
|
453
|
+
|
|
454
|
+
```javascript
|
|
455
|
+
// Defensive method checking
|
|
456
|
+
if (typeof this.body.add_class === 'function') {
|
|
457
|
+
this.body.add_class('demo-ui');
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// Property existence checking before use
|
|
461
|
+
if (!spec.el) { compose(); }
|
|
462
|
+
|
|
463
|
+
// Model existence verification
|
|
464
|
+
if (date_picker_1.data.model !== date_picker_2.data.model) {
|
|
465
|
+
// Recovery logic
|
|
466
|
+
}
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
## Framework Integration Points
|
|
470
|
+
|
|
471
|
+
### jsgui3-client Integration
|
|
472
|
+
|
|
473
|
+
```javascript
|
|
474
|
+
const jsgui = require('jsgui3-client');
|
|
475
|
+
const { controls, Control, mixins, Data_Object } = jsgui; // Destructure imports
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
**Key Exports:**
|
|
479
|
+
- `controls` - Collection of UI control constructors
|
|
480
|
+
- `Control` - Base control class
|
|
481
|
+
- `mixins` - Behavioral mixins (e.g., `dragable`)
|
|
482
|
+
- `Data_Object` - Observable data model constructor
|
|
483
|
+
|
|
484
|
+
### obext Integration
|
|
485
|
+
|
|
486
|
+
```javascript
|
|
487
|
+
const { field } = require('obext'); // Object extension utilities
|
|
488
|
+
field(dataModel, 'propertyName'); // Creates reactive property
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
The `field()` function creates observable properties that trigger change events when modified.
|
|
492
|
+
|
|
493
|
+
### Module Export Pattern
|
|
494
|
+
|
|
495
|
+
```javascript
|
|
496
|
+
// At end of client.js files
|
|
497
|
+
controls.Demo_UI = Demo_UI; // Register control globally
|
|
498
|
+
module.exports = jsgui; // Export entire framework
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
This pattern extends the jsgui framework with custom controls while maintaining the complete API.
|
|
502
|
+
|
|
503
|
+
## Performance Considerations
|
|
504
|
+
|
|
505
|
+
### Lazy Composition
|
|
506
|
+
|
|
507
|
+
```javascript
|
|
508
|
+
if (!spec.el) { compose(); } // Only compose if no external DOM
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
This pattern allows controls to be created without immediately building their UI, enabling faster initialization and memory savings.
|
|
512
|
+
|
|
513
|
+
### Shared Data Models
|
|
514
|
+
|
|
515
|
+
```javascript
|
|
516
|
+
// Single model shared by multiple controls
|
|
517
|
+
const sharedModel = new Data_Object({ context });
|
|
518
|
+
// Multiple controls reference same model - no data duplication
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
### CSS Bundling
|
|
522
|
+
|
|
523
|
+
CSS is collected from all control classes and bundled once during server startup, rather than injected per-instance.
|
|
524
|
+
|
|
525
|
+
## AI Development Guidelines
|
|
526
|
+
|
|
527
|
+
When working with JSGUI3:
|
|
528
|
+
|
|
529
|
+
1. **Always follow the constructor pattern** - spec parameter, __type_name, super() call, context extraction
|
|
530
|
+
2. **Use defensive programming** - typeof checks, property existence verification
|
|
531
|
+
3. **Maintain activation lifecycle** - check __active flag, call super.activate()
|
|
532
|
+
4. **Follow naming conventions** - PascalCase for classes, snake_case for properties
|
|
533
|
+
5. **Preserve CSS formatting** - multi-line CSS with comments
|
|
534
|
+
6. **Use consistent error handling** - throw errors, don't swallow them
|
|
535
|
+
7. **Register controls properly** - context.register_control() for data models
|
|
536
|
+
8. **Store control references** - use _ctrl_fields pattern for later access
|
|
537
|
+
|
|
538
|
+
## Detailed Component Architecture
|
|
539
|
+
|
|
540
|
+
### Active_HTML_Document Inheritance Chain
|
|
541
|
+
|
|
542
|
+
The base class hierarchy is crucial for understanding the framework:
|
|
543
|
+
|
|
544
|
+
```javascript
|
|
545
|
+
class Demo_UI extends Active_HTML_Document {
|
|
546
|
+
// All UI classes extend this base class
|
|
547
|
+
}
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
**Active_HTML_Document Responsibilities:**
|
|
551
|
+
- Provides `this.body` property for DOM manipulation
|
|
552
|
+
- Implements activation lifecycle (`activate()` method)
|
|
553
|
+
- Handles CSS injection and bundling
|
|
554
|
+
- Manages context assignment and control registration
|
|
555
|
+
- Provides event coordination infrastructure
|
|
556
|
+
|
|
557
|
+
### Control Specification System
|
|
558
|
+
|
|
559
|
+
The `spec` parameter is a configuration object with standardized properties:
|
|
560
|
+
|
|
561
|
+
```javascript
|
|
562
|
+
const control = new SomeControl({
|
|
563
|
+
context: context, // REQUIRED - Runtime environment
|
|
564
|
+
size: [width, height], // Optional dimensions
|
|
565
|
+
pos: [x, y], // Optional position
|
|
566
|
+
title: 'String', // Optional title (for Windows)
|
|
567
|
+
background: { // Optional styling
|
|
568
|
+
color: '#RRGGBB'
|
|
569
|
+
},
|
|
570
|
+
data: { // Optional data binding
|
|
571
|
+
model: dataObject
|
|
572
|
+
},
|
|
573
|
+
label: { // Optional label configuration
|
|
574
|
+
text: 'Label Text'
|
|
575
|
+
}
|
|
576
|
+
});
|
|
577
|
+
```
|
|
578
|
+
|
|
579
|
+
**Common Spec Properties:**
|
|
580
|
+
- `context` - Always required, provides runtime services
|
|
581
|
+
- `size` - Array [width, height] for dimensioned controls
|
|
582
|
+
- `pos` - Array [x, y] for positioned controls (Windows)
|
|
583
|
+
- `data` - Object containing `model` property for data binding
|
|
584
|
+
- `background` - Styling object with color, image, etc.
|
|
585
|
+
- `label` - Label configuration for input controls
|
|
586
|
+
|
|
587
|
+
### Window Control Deep Dive
|
|
588
|
+
|
|
589
|
+
Windows are the primary container control:
|
|
590
|
+
|
|
591
|
+
```javascript
|
|
592
|
+
const window = new controls.Window({
|
|
593
|
+
context: context,
|
|
594
|
+
title: 'Window Title', // Appears in title bar
|
|
595
|
+
pos: [10, 10] // Initial position [x, y]
|
|
596
|
+
});
|
|
597
|
+
|
|
598
|
+
// Key Window properties:
|
|
599
|
+
window.inner // Container for child controls
|
|
600
|
+
window.title // Title bar text
|
|
601
|
+
window.pos // Current position
|
|
602
|
+
```
|
|
603
|
+
|
|
604
|
+
**Window Features:**
|
|
605
|
+
- Draggable by title bar (via `dragable` mixin)
|
|
606
|
+
- Resizable borders
|
|
607
|
+
- Close button functionality
|
|
608
|
+
- Z-index management for overlapping windows
|
|
609
|
+
- Child control containment via `inner` property
|
|
610
|
+
|
|
611
|
+
### Data_Object Reactive System
|
|
612
|
+
|
|
613
|
+
The observable data system is fundamental to JSGUI3:
|
|
614
|
+
|
|
615
|
+
```javascript
|
|
616
|
+
// 1. Create Data_Object
|
|
617
|
+
const model = new Data_Object({ context });
|
|
618
|
+
|
|
619
|
+
// 2. Add reactive fields
|
|
620
|
+
field(model, 'value'); // Creates getter/setter with change events
|
|
621
|
+
field(model, 'name'); // Multiple fields supported
|
|
622
|
+
field(model, 'selected');
|
|
623
|
+
|
|
624
|
+
// 3. Register with context
|
|
625
|
+
context.register_control(model);
|
|
626
|
+
|
|
627
|
+
// 4. Bind to controls
|
|
628
|
+
const picker = new Date_Picker({
|
|
629
|
+
context,
|
|
630
|
+
data: { model: model } // Control automatically subscribes to changes
|
|
631
|
+
});
|
|
632
|
+
|
|
633
|
+
// 5. Model changes trigger UI updates
|
|
634
|
+
model.value = new Date(); // All bound controls update automatically
|
|
635
|
+
```
|
|
636
|
+
|
|
637
|
+
**Reactive Properties:**
|
|
638
|
+
- Getter/setter pairs created by `field()` function
|
|
639
|
+
- Change events emitted when properties modified
|
|
640
|
+
- Controls automatically subscribe to their bound models
|
|
641
|
+
- Multiple controls can share single model for synchronization
|
|
642
|
+
|
|
643
|
+
### Control Registration and Lifecycle
|
|
644
|
+
|
|
645
|
+
```javascript
|
|
646
|
+
// Registration patterns
|
|
647
|
+
context.register_control(dataModel); // Register data models
|
|
648
|
+
// Controls auto-register during construction
|
|
649
|
+
|
|
650
|
+
// Lifecycle events
|
|
651
|
+
constructor() -> activate() -> use -> cleanup()
|
|
652
|
+
|
|
653
|
+
// Activation requirements
|
|
654
|
+
if (!this.__active) {
|
|
655
|
+
super.activate(); // MUST call parent first
|
|
656
|
+
// ... control-specific activation
|
|
657
|
+
}
|
|
658
|
+
```
|
|
659
|
+
|
|
660
|
+
### Event System Details
|
|
661
|
+
|
|
662
|
+
```javascript
|
|
663
|
+
// Event registration
|
|
664
|
+
context.on('event-name', handler);
|
|
665
|
+
|
|
666
|
+
// Common event types:
|
|
667
|
+
'window-resize' // Browser window size changes
|
|
668
|
+
'data-model-value-change' // Data model property updates
|
|
669
|
+
'control-activated' // Control becomes active
|
|
670
|
+
'control-deactivated' // Control becomes inactive
|
|
671
|
+
|
|
672
|
+
// Event handler patterns
|
|
673
|
+
context.on('window-resize', e_resize => {
|
|
674
|
+
// e_resize contains resize event data
|
|
675
|
+
// 'this' context preserved with arrow functions
|
|
676
|
+
});
|
|
677
|
+
```
|
|
678
|
+
|
|
679
|
+
## Advanced Data Binding Scenarios
|
|
680
|
+
|
|
681
|
+
### Model Synchronization Recovery
|
|
682
|
+
|
|
683
|
+
When shared models become desynchronized, JSGUI3 provides recovery patterns:
|
|
684
|
+
|
|
685
|
+
```javascript
|
|
686
|
+
activate() {
|
|
687
|
+
if (!this.__active) {
|
|
688
|
+
super.activate();
|
|
689
|
+
const { _ctrl_fields } = this;
|
|
690
|
+
const { picker1, picker2 } = _ctrl_fields;
|
|
691
|
+
|
|
692
|
+
// Detect model desynchronization
|
|
693
|
+
if (picker1.data.model !== picker2.data.model) {
|
|
694
|
+
// Create new shared model
|
|
695
|
+
const dm = new Data_Object({ context });
|
|
696
|
+
field(dm, 'value');
|
|
697
|
+
|
|
698
|
+
// Reassign to both controls
|
|
699
|
+
picker1.data.model = dm;
|
|
700
|
+
picker2.data.model = dm;
|
|
701
|
+
|
|
702
|
+
// Re-establish change handlers
|
|
703
|
+
picker1.assign_data_model_value_change_handler();
|
|
704
|
+
picker2.assign_data_model_value_change_handler();
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
```
|
|
709
|
+
|
|
710
|
+
### Multi-Control Data Flows
|
|
711
|
+
|
|
712
|
+
```javascript
|
|
713
|
+
// One-to-many data binding
|
|
714
|
+
const sharedModel = new Data_Object({ context });
|
|
715
|
+
field(sharedModel, 'value');
|
|
716
|
+
|
|
717
|
+
const controls = [
|
|
718
|
+
new Date_Picker({ context, data: { model: sharedModel } }),
|
|
719
|
+
new Text_Input({ context, data: { model: sharedModel } }),
|
|
720
|
+
new Display_Label({ context, data: { model: sharedModel } })
|
|
721
|
+
];
|
|
722
|
+
|
|
723
|
+
// Any control's changes propagate to all others
|
|
724
|
+
```
|
|
725
|
+
|
|
726
|
+
### Data Model Field Types
|
|
727
|
+
|
|
728
|
+
```javascript
|
|
729
|
+
// Different field types and their behaviors
|
|
730
|
+
field(model, 'stringField'); // String values
|
|
731
|
+
field(model, 'numberField'); // Numeric values
|
|
732
|
+
field(model, 'dateField'); // Date objects
|
|
733
|
+
field(model, 'booleanField'); // Boolean values
|
|
734
|
+
field(model, 'objectField'); // Complex objects
|
|
735
|
+
field(model, 'arrayField'); // Array collections
|
|
736
|
+
|
|
737
|
+
// Fields support any JavaScript type
|
|
738
|
+
model.stringField = "text";
|
|
739
|
+
model.numberField = 42;
|
|
740
|
+
model.dateField = new Date();
|
|
741
|
+
model.booleanField = true;
|
|
742
|
+
model.objectField = { nested: "data" };
|
|
743
|
+
model.arrayField = [1, 2, 3];
|
|
744
|
+
```
|
|
745
|
+
|
|
746
|
+
## Server-Side Architecture Details
|
|
747
|
+
|
|
748
|
+
### Bundling and Serving Process
|
|
749
|
+
|
|
750
|
+
```javascript
|
|
751
|
+
// Server construction phase
|
|
752
|
+
const server = new Server({
|
|
753
|
+
Ctrl: Demo_UI, // Main UI control class
|
|
754
|
+
src_path_client_js: require.resolve('./client.js') // Client bundle path
|
|
755
|
+
});
|
|
756
|
+
|
|
757
|
+
// Bundling process (happens during 'ready' event):
|
|
758
|
+
// 1. Parse client.js and dependencies
|
|
759
|
+
// 2. Collect CSS from all control classes
|
|
760
|
+
// 3. Bundle JavaScript for browser delivery
|
|
761
|
+
// 4. Prepare HTTP routes for serving
|
|
762
|
+
// 5. Emit 'ready' event when complete
|
|
763
|
+
```
|
|
764
|
+
|
|
765
|
+
### Request/Response Cycle
|
|
766
|
+
|
|
767
|
+
```javascript
|
|
768
|
+
// HTTP request handling:
|
|
769
|
+
// 1. Browser requests application
|
|
770
|
+
// 2. Server serves bundled JavaScript
|
|
771
|
+
// 3. Client executes and creates UI
|
|
772
|
+
// 4. WebSocket connection established for real-time updates
|
|
773
|
+
// 5. UI events flow back to server via WebSocket
|
|
774
|
+
// 6. Server processes events and updates models
|
|
775
|
+
// 7. Model changes propagate back to client
|
|
776
|
+
```
|
|
777
|
+
|
|
778
|
+
### Multi-Client Synchronization
|
|
779
|
+
|
|
780
|
+
```javascript
|
|
781
|
+
// Server maintains shared state across multiple clients
|
|
782
|
+
// Changes from one client propagate to all connected clients
|
|
783
|
+
// Data models are server-authoritative
|
|
784
|
+
// Client UI updates reflect server state changes
|
|
785
|
+
```
|
|
786
|
+
|
|
787
|
+
## CSS Architecture and Styling
|
|
788
|
+
|
|
789
|
+
### CSS Definition Patterns
|
|
790
|
+
|
|
791
|
+
```javascript
|
|
792
|
+
// Static CSS property on control class
|
|
793
|
+
ControlClass.css = `
|
|
794
|
+
/* Global resets - common across all examples */
|
|
795
|
+
* {
|
|
796
|
+
margin: 0;
|
|
797
|
+
padding: 0;
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
/* Body styling - prevents scrollbars, sets background */
|
|
801
|
+
body {
|
|
802
|
+
overflow-x: hidden;
|
|
803
|
+
overflow-y: hidden;
|
|
804
|
+
background-color: #E0E0E0;
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
/* Control-specific styling */
|
|
808
|
+
.control-class-name {
|
|
809
|
+
/* Active styles */
|
|
810
|
+
|
|
811
|
+
/* Commented alternative styles for future use */
|
|
812
|
+
/* display: flex; */
|
|
813
|
+
/* justify-content: center; */
|
|
814
|
+
}
|
|
815
|
+
`;
|
|
816
|
+
```
|
|
817
|
+
|
|
818
|
+
### CSS Bundling Process
|
|
819
|
+
|
|
820
|
+
```javascript
|
|
821
|
+
// CSS collection during server startup:
|
|
822
|
+
// 1. Scan all control classes for .css properties
|
|
823
|
+
// 2. Concatenate CSS in dependency order
|
|
824
|
+
// 3. Minify and optimize
|
|
825
|
+
// 4. Serve as single stylesheet to clients
|
|
826
|
+
// 5. Cache for subsequent requests
|
|
827
|
+
```
|
|
828
|
+
|
|
829
|
+
### Responsive Design Patterns
|
|
830
|
+
|
|
831
|
+
```javascript
|
|
832
|
+
// Window resize handling
|
|
833
|
+
context.on('window-resize', e_resize => {
|
|
834
|
+
// Update control dimensions
|
|
835
|
+
// Reflow layouts
|
|
836
|
+
// Adjust positioning
|
|
837
|
+
});
|
|
838
|
+
|
|
839
|
+
// CSS supports responsive breakpoints
|
|
840
|
+
.control-class {
|
|
841
|
+
/* Desktop styles */
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
@media (max-width: 768px) {
|
|
845
|
+
.control-class {
|
|
846
|
+
/* Mobile styles */
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
```
|
|
850
|
+
|
|
851
|
+
## Control-Specific Implementation Details
|
|
852
|
+
|
|
853
|
+
### Checkbox Control
|
|
854
|
+
|
|
855
|
+
```javascript
|
|
856
|
+
const checkbox = new Checkbox({
|
|
857
|
+
context,
|
|
858
|
+
label: { text: 'Checkbox Label' }, // Label configuration
|
|
859
|
+
// Optional data binding
|
|
860
|
+
data: { model: booleanModel } // Binds to boolean field
|
|
861
|
+
});
|
|
862
|
+
|
|
863
|
+
// Checkbox events
|
|
864
|
+
checkbox.on('change', (checked) => {
|
|
865
|
+
// Handle checkbox state changes
|
|
866
|
+
});
|
|
867
|
+
```
|
|
868
|
+
|
|
869
|
+
### Tabbed_Panel Control
|
|
870
|
+
|
|
871
|
+
```javascript
|
|
872
|
+
const tabbed_panel = new controls.Tabbed_Panel({
|
|
873
|
+
context,
|
|
874
|
+
tabs: [
|
|
875
|
+
// String-only tabs (content provided separately)
|
|
876
|
+
'Tab 1', 'Tab 2', 'Tab 3',
|
|
877
|
+
|
|
878
|
+
// OR control-embedded tabs
|
|
879
|
+
['Tab 1', controlInstance1],
|
|
880
|
+
['Tab 2', controlInstance2],
|
|
881
|
+
['Tab 3', controlInstance3]
|
|
882
|
+
]
|
|
883
|
+
});
|
|
884
|
+
|
|
885
|
+
// Tab switching events
|
|
886
|
+
tabbed_panel.on('tab-changed', (tabIndex) => {
|
|
887
|
+
// Handle tab selection
|
|
888
|
+
});
|
|
889
|
+
```
|
|
890
|
+
|
|
891
|
+
### Date_Picker Control
|
|
892
|
+
|
|
893
|
+
```javascript
|
|
894
|
+
const date_picker = new Date_Picker({
|
|
895
|
+
context,
|
|
896
|
+
data: { model: dateModel }, // Binds to date field
|
|
897
|
+
// Optional configuration
|
|
898
|
+
format: 'YYYY-MM-DD', // Date display format
|
|
899
|
+
minDate: new Date('2020-01-01'), // Minimum selectable date
|
|
900
|
+
maxDate: new Date('2030-12-31') // Maximum selectable date
|
|
901
|
+
});
|
|
902
|
+
|
|
903
|
+
// Date selection events
|
|
904
|
+
date_picker.on('date-selected', (date) => {
|
|
905
|
+
// Handle date selection
|
|
906
|
+
});
|
|
907
|
+
```
|
|
908
|
+
|
|
909
|
+
### Month_View Control
|
|
910
|
+
|
|
911
|
+
```javascript
|
|
912
|
+
const month_view = new Month_View({
|
|
913
|
+
context,
|
|
914
|
+
// Optional configuration
|
|
915
|
+
showToday: true, // Highlight today's date
|
|
916
|
+
showWeekNumbers: false, // Show week numbers
|
|
917
|
+
firstDayOfWeek: 0 // 0=Sunday, 1=Monday, etc.
|
|
918
|
+
});
|
|
919
|
+
|
|
920
|
+
// Month navigation events
|
|
921
|
+
month_view.on('month-changed', (year, month) => {
|
|
922
|
+
// Handle month navigation
|
|
923
|
+
});
|
|
924
|
+
```
|
|
925
|
+
|
|
926
|
+
## Error Handling and Recovery Patterns
|
|
927
|
+
|
|
928
|
+
### Common Error Scenarios
|
|
929
|
+
|
|
930
|
+
```javascript
|
|
931
|
+
// 1. Missing context
|
|
932
|
+
if (!context) {
|
|
933
|
+
throw new Error('Context required for control creation');
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
// 2. Invalid data model
|
|
937
|
+
if (spec.data && !spec.data.model) {
|
|
938
|
+
throw new Error('Data specification requires model property');
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
// 3. Missing required methods
|
|
942
|
+
if (typeof this.body.add_class !== 'function') {
|
|
943
|
+
console.warn('add_class method not available on body');
|
|
944
|
+
// Graceful degradation
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
// 4. Activation state errors
|
|
948
|
+
if (this.__active) {
|
|
949
|
+
console.warn('Control already activated, skipping');
|
|
950
|
+
return;
|
|
951
|
+
}
|
|
952
|
+
```
|
|
953
|
+
|
|
954
|
+
### Recovery Strategies
|
|
955
|
+
|
|
956
|
+
```javascript
|
|
957
|
+
// Data model recovery
|
|
958
|
+
try {
|
|
959
|
+
model.value = newValue;
|
|
960
|
+
} catch (error) {
|
|
961
|
+
console.error('Model update failed:', error);
|
|
962
|
+
// Revert to previous value or default
|
|
963
|
+
model.value = this._previousValue || this._defaultValue;
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
// Control reference recovery
|
|
967
|
+
if (!this._ctrl_fields || !this._ctrl_fields.someControl) {
|
|
968
|
+
console.warn('Control references lost, rebuilding');
|
|
969
|
+
this._rebuildControls();
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
// Event handler recovery
|
|
973
|
+
try {
|
|
974
|
+
context.on('event-name', handler);
|
|
975
|
+
} catch (error) {
|
|
976
|
+
console.error('Event registration failed:', error);
|
|
977
|
+
// Use fallback polling or alternative event mechanism
|
|
978
|
+
}
|
|
979
|
+
```
|
|
980
|
+
|
|
981
|
+
## Testing and Development Utilities
|
|
982
|
+
|
|
983
|
+
### Development Mode Features
|
|
984
|
+
|
|
985
|
+
```javascript
|
|
986
|
+
// Enhanced logging in development
|
|
987
|
+
if (process.env.NODE_ENV === 'development') {
|
|
988
|
+
console.log('Control created:', this.__type_name);
|
|
989
|
+
console.log('Context:', context);
|
|
990
|
+
console.log('Specification:', spec);
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
// Debug helpers
|
|
994
|
+
this._debug = {
|
|
995
|
+
controlType: this.__type_name,
|
|
996
|
+
createdAt: new Date(),
|
|
997
|
+
context: context,
|
|
998
|
+
specification: spec
|
|
999
|
+
};
|
|
1000
|
+
```
|
|
1001
|
+
|
|
1002
|
+
### Runtime Inspection
|
|
1003
|
+
|
|
1004
|
+
```javascript
|
|
1005
|
+
// Control hierarchy inspection
|
|
1006
|
+
function inspectControlTree(control, depth = 0) {
|
|
1007
|
+
const indent = ' '.repeat(depth);
|
|
1008
|
+
console.log(`${indent}${control.__type_name}`);
|
|
1009
|
+
|
|
1010
|
+
if (control.children) {
|
|
1011
|
+
control.children.forEach(child => {
|
|
1012
|
+
inspectControlTree(child, depth + 1);
|
|
1013
|
+
});
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
// Data model inspection
|
|
1018
|
+
function inspectDataModel(model) {
|
|
1019
|
+
console.log('Model fields:', Object.keys(model));
|
|
1020
|
+
Object.keys(model).forEach(key => {
|
|
1021
|
+
console.log(` ${key}:`, model[key]);
|
|
1022
|
+
});
|
|
1023
|
+
}
|
|
1024
|
+
```
|
|
9
1025
|
|
|
10
1026
|
|