jsgui3-server 0.0.125 → 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 +1020 -4
- package/package.json +4 -4
- package/server.js +3 -250
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
|
|
package/package.json
CHANGED
|
@@ -3,15 +3,15 @@
|
|
|
3
3
|
"main": "module.js",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"dependencies": {
|
|
6
|
-
"@babel/core": "^7.
|
|
6
|
+
"@babel/core": "^7.27.7",
|
|
7
7
|
"@babel/generator": "^7.27.5",
|
|
8
8
|
"@babel/parser": "7.27.7",
|
|
9
9
|
"cookies": "^0.9.1",
|
|
10
10
|
"esbuild": "^0.25.5",
|
|
11
11
|
"fnl": "^0.0.36",
|
|
12
12
|
"fnlfs": "^0.0.33",
|
|
13
|
-
"jsgui3-client": "^0.0.
|
|
14
|
-
"jsgui3-html": "^0.0.
|
|
13
|
+
"jsgui3-client": "^0.0.115",
|
|
14
|
+
"jsgui3-html": "^0.0.162",
|
|
15
15
|
"jsgui3-webpage": "^0.0.8",
|
|
16
16
|
"jsgui3-website": "^0.0.8",
|
|
17
17
|
"lang-tools": "^0.0.36",
|
|
@@ -38,5 +38,5 @@
|
|
|
38
38
|
"type": "git",
|
|
39
39
|
"url": "https://github.com/metabench/jsgui3-server.git"
|
|
40
40
|
},
|
|
41
|
-
"version": "0.0.
|
|
41
|
+
"version": "0.0.126"
|
|
42
42
|
}
|
package/server.js
CHANGED
|
@@ -23,16 +23,6 @@ const HTTP_Website_Publisher = require('./publishers/http-website-publisher');
|
|
|
23
23
|
const Webpage = require('./website/webpage');
|
|
24
24
|
const HTTP_Webpage_Publisher = require('./publishers/http-webpage-publisher');
|
|
25
25
|
|
|
26
|
-
// A class that contains resources and publishers?
|
|
27
|
-
// Is the server itself a publisher?
|
|
28
|
-
// Is this a Server_Process, whereby a Server could hold multiple processes?
|
|
29
|
-
|
|
30
|
-
// Think this is / should be a Server. Not sure though.
|
|
31
|
-
// Maybe Single_Process_Server ????
|
|
32
|
-
// Could make for cleaner debugs if we have multiples of them and maybe a Multi_Process_Coordinator_Server ???
|
|
33
|
-
// Multi_Single_Process_Server_Coordinater_Server ???
|
|
34
|
-
|
|
35
|
-
|
|
36
26
|
const Static_Route_HTTP_Responder = require('./http/responders/static/Static_Route_HTTP_Responder');
|
|
37
27
|
|
|
38
28
|
|
|
@@ -91,81 +81,12 @@ class JSGUI_Single_Process_Server extends Evented_Class {
|
|
|
91
81
|
const opts_webpage = {
|
|
92
82
|
'name': this.name || 'Website'
|
|
93
83
|
};
|
|
94
|
-
|
|
95
|
-
// Seems like a decent mechanism already in here at the moment, but may want to make the API more powerful and flexible.
|
|
96
|
-
|
|
97
|
-
// A single Ctrl that represents a page (could a Ctrl represent a site too? Could be possible with a bit of work.)
|
|
98
|
-
|
|
99
|
-
|
|
100
84
|
|
|
101
85
|
if (Ctrl) {
|
|
102
|
-
// could be a web page, not a web site.
|
|
103
|
-
// But a site can contain one page. Easy enough default?
|
|
104
|
-
// Though more directly serving a page seems simpler. More logical too, if we are not serving a site with it.
|
|
105
|
-
//opts_website.content = Ctrl;
|
|
106
|
-
//opts_webpage.content = Ctrl;
|
|
107
|
-
|
|
108
|
-
// set up a web page with the ctrl, and a web page publisher.
|
|
109
|
-
|
|
110
|
-
// But does that Webpage need (automatically or not) to include the necessary (built) JS client file?
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
// May need to process / get info on the referenced JS first.
|
|
114
|
-
// Better to make something like a JS_File_Info_Provider than to do it here.
|
|
115
|
-
// Want a really DRY framework and to make it so slower parts can be upgraded as files and swapped as files.
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
// Tell the webpage it needs to have a built version of the referenced client JS?
|
|
121
|
-
|
|
122
|
-
// The Webpage is an app???
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
// And give it the src_path_client_js ...?
|
|
126
|
-
// Best to be specific with the API, but make it fairly short and simple on the top level.
|
|
127
|
-
|
|
128
|
-
// But the webpage needs to have stuff in its head that references the JS.
|
|
129
|
-
// But maybe the publisher could insert that.
|
|
130
|
-
|
|
131
|
-
// Basically need to put everything in the relevant lower level abstraction and use it there.
|
|
132
|
-
// Need to make it very clear what is being done, but concise too.
|
|
133
|
-
// Really need to get it simply serving JS clients, extracting CSS from JS too.
|
|
134
|
-
// Want to make it easy to make some really interesting demos with very simple idiomatic code
|
|
135
|
-
// that is clear how it works both on client and server.
|
|
136
|
-
|
|
137
|
-
// Could get more into 'undo button' type transformations, that's something React and Redux can do well,
|
|
138
|
-
// would be worth going into UI_Action classes or similar.
|
|
139
|
-
// UI_Action_Result too - but def want to keep it really simple on the top level, simple enough on the 2nd level,
|
|
140
|
-
// and then it can be moderately to very complex on 3rd level down (though there could be options like choosing which
|
|
141
|
-
// js build system to use).
|
|
142
|
-
|
|
143
|
-
// Def looks like a fair bit more work to be done to get all the abstractions made and working to run simple apps,
|
|
144
|
-
// with really simple and efficient defaults.
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
86
|
|
|
153
87
|
|
|
154
88
|
const wp_app = new Webpage({content: Ctrl});
|
|
155
89
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
// And maybe by default the webpage publisher should make sure that the relevant JS and CSS is built / packaged and ready to serve.
|
|
160
|
-
// May need to give it one or two file paths.
|
|
161
|
-
// But if we give it those file path(s) do we need to provide that Ctrl in the first place?
|
|
162
|
-
// For the moment a single (or 2) extra properties should be fine.
|
|
163
|
-
// a client_src_js_path property should be fine.
|
|
164
|
-
// src_path_client_js
|
|
165
|
-
// or src_client_js as a string.
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
90
|
const opts_wp_publisher = {
|
|
170
91
|
'webpage': wp_app
|
|
171
92
|
|
|
@@ -185,66 +106,16 @@ class JSGUI_Single_Process_Server extends Evented_Class {
|
|
|
185
106
|
|
|
186
107
|
|
|
187
108
|
console.log('waiting for wp_publisher ready');
|
|
188
|
-
|
|
189
|
-
// Server can (maybe just in theory) serve multiple websites at once.
|
|
190
|
-
// Worth making that more of a feature.
|
|
191
|
-
// For the moment, seems like new website resource inside server makes sense.
|
|
192
|
-
// Then does the website resource contain a lot of its own resources, in a pool???
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
// The publisher should (probably) set things up with the server itself....
|
|
196
|
-
// Give the publisher access to the server.
|
|
197
|
-
// Or access to a more limited number of functions that the publisher can call.
|
|
198
|
-
|
|
199
|
-
// So the Publisher itself finds the Server Router and sets up routes on it, but uses very specific classes to help do that.
|
|
200
|
-
|
|
201
|
-
// Maybe Http_Publisher on a lower level could put together headers and do compression.
|
|
202
|
-
// This part will be a little more like Express (and other) middleware.
|
|
203
|
-
|
|
204
|
-
// http_publisher.publish_static_content_to_routes
|
|
205
|
-
// .publish_static_bundle_to_routes
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
109
|
wp_publisher.on('ready', (wp_ready_res) => {
|
|
216
|
-
console.log('wp publisher is ready');
|
|
217
|
-
|
|
218
|
-
// The ready res on complete(res) ???
|
|
219
|
-
// But the ready event does not (easily) carry this object.
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
//console.log('wp_ready_res', wp_ready_res);
|
|
223
|
-
|
|
224
|
-
// then go through the array....
|
|
225
|
-
|
|
110
|
+
//console.log('wp publisher is ready');
|
|
226
111
|
if (wp_ready_res._arr) {
|
|
227
112
|
|
|
228
113
|
|
|
229
114
|
for (const bundle_item of wp_ready_res._arr) {
|
|
230
115
|
//console.log('Object.keys(bundle_item)', Object.keys(bundle_item));
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
116
|
const {type, extension, text, route, response_buffers, response_headers} = bundle_item;
|
|
235
|
-
//console.log('');
|
|
236
|
-
//console.log('bundle_item route:', route);
|
|
237
|
-
//console.log('bundle_item type:', type);
|
|
238
|
-
|
|
239
117
|
const bundle_item_http_responder = new Static_Route_HTTP_Responder(bundle_item);
|
|
240
118
|
|
|
241
|
-
//console.log('bundle_item_http_responder.handle_http', bundle_item_http_responder.handle_http);
|
|
242
|
-
|
|
243
|
-
// So set_route needs to set it up with the proper context for the handle_http call.
|
|
244
|
-
// At least it looks fairly close to being solved, though maybe Router and Routing tree
|
|
245
|
-
// should have comprehensive fixes and improvements.
|
|
246
|
-
|
|
247
|
-
|
|
248
119
|
|
|
249
120
|
server_router.set_route(route, bundle_item_http_responder, bundle_item_http_responder.handle_http);
|
|
250
121
|
|
|
@@ -259,111 +130,12 @@ class JSGUI_Single_Process_Server extends Evented_Class {
|
|
|
259
130
|
throw 'NYI';
|
|
260
131
|
}
|
|
261
132
|
|
|
262
|
-
// But do we get a bundle from it when ready?
|
|
263
|
-
// Maybe it should provide that bundle in the 'ready' event.
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
133
|
const ws_resource = new Website_Resource({
|
|
269
134
|
'name': 'Website_Resource - Single Webpage',
|
|
270
135
|
'webpage': wp_app
|
|
271
136
|
});
|
|
272
137
|
resource_pool.add(ws_resource);
|
|
273
138
|
|
|
274
|
-
//
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
// Possibly set multiple routes here, with multiple response buffers depending on the encoding-type
|
|
278
|
-
// accepted by the client.
|
|
279
|
-
|
|
280
|
-
// Seems best not to rely on the Webpage_Publisher to handle the HTTP.
|
|
281
|
-
// Better for the Publisher to create the Bundle that is ready to serve, than provide that
|
|
282
|
-
// to the Server here, or maybe to something else.
|
|
283
|
-
|
|
284
|
-
// Seems best to get that ready to serve static bundle from the publisher,
|
|
285
|
-
// and if it helps use some kind of system to set up some more details with the server router...?
|
|
286
|
-
|
|
287
|
-
// Initial_Response_Handler perhaps?
|
|
288
|
-
|
|
289
|
-
// Webpage_HTTP_Response_Handler?
|
|
290
|
-
|
|
291
|
-
// Static_Webpage_HTTP_Response_Handler???
|
|
292
|
-
|
|
293
|
-
// Static_Webpage_Bundle_HTTP_Response_Handler ???
|
|
294
|
-
|
|
295
|
-
// Static_Webpage_Bundle_HTTP_Route_Response_Handler ????
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
// Static_Webpage_Bundle_HTTP_Route_Responder ???
|
|
299
|
-
|
|
300
|
-
// new Static_Webpage_Bundle_HTTP_Route_Responder(bundle_item)
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
// Static_Webpage_Bundle_HTTP_Responder ??? (would do routing / route checking itself perhaps?)
|
|
306
|
-
// Seems like for the moment we should continue to use the server router.
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
// Need to decide which encoded (compressed) buffer to return depending
|
|
312
|
-
// on what Content-Type(s) are supported on the client.
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
// Very explicit class names make the responsibilities very clear.
|
|
318
|
-
|
|
319
|
-
// Static_Route_HTTP_Responder seems best to provide the handle_http function.
|
|
320
|
-
// Even after routing a decision needs to be made regarding which buffer to send to the client
|
|
321
|
-
// Should be the last thing needed to get this simple square box demo server working properly.
|
|
322
|
-
// Hopefully the client-side activation still works fine.
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
// HTTP_Responder class and subclasses???
|
|
327
|
-
// Could be a helpful type of middleware.
|
|
328
|
-
// Want to have it handle creating or using the correct compressed buffer.
|
|
329
|
-
|
|
330
|
-
// go through the array of bundle items....
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
// Need the bundle object / array here.
|
|
334
|
-
// Would be nice to have the bundle itself hold info on what's inside.
|
|
335
|
-
// Incl what packaging stage it is at, how ready to serve.
|
|
336
|
-
|
|
337
|
-
// Though possible one object could handle the whole static bundle setup with the router...?
|
|
338
|
-
|
|
339
|
-
// The code in a loop should be simple enough here though.
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
// Set the routes of the various items in the bundle (and use the handlers provided by class
|
|
349
|
-
// instance objects that specifically provide HTTP handlers)
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
// Should be the very last part of serving the HTTP for this particular server type.
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
//server_router.set_route('/', wp_publisher, wp_publisher.handle_http);
|
|
365
|
-
|
|
366
|
-
|
|
367
139
|
this.raise('ready');
|
|
368
140
|
});
|
|
369
141
|
|
|
@@ -501,29 +273,8 @@ class JSGUI_Single_Process_Server extends Evented_Class {
|
|
|
501
273
|
}
|
|
502
274
|
}
|
|
503
275
|
|
|
504
|
-
// Not sure that controls should be considered server only????
|
|
505
|
-
// Possibly makes sense, though the 'client' part may need the active HTML document.
|
|
506
|
-
|
|
507
|
-
// Possibly the server could / should produce / provide such a document.
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
// Return the 'jsgui' object???
|
|
514
|
-
|
|
515
|
-
//jsgui.controls.Active_HTML_Document = require('./controls/Active_HTML_Document');
|
|
516
|
-
|
|
517
|
-
|
|
518
276
|
JSGUI_Single_Process_Server.jsgui = jsgui;
|
|
519
277
|
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
// Also want Active_HTML_Document
|
|
523
|
-
// or Active_HTML - needs to be simple to use, putting in the active stuff automatically.
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
278
|
JSGUI_Single_Process_Server.Resource = Resource;
|
|
528
279
|
JSGUI_Single_Process_Server.Page_Context = Server_Page_Context;
|
|
529
280
|
JSGUI_Single_Process_Server.Server_Page_Context = Server_Page_Context;
|
|
@@ -607,3 +358,5 @@ const summary = {
|
|
|
607
358
|
]
|
|
608
359
|
}
|
|
609
360
|
}
|
|
361
|
+
|
|
362
|
+
JSGUI_Single_Process_Server.summary = summary;
|