neo.mjs 10.0.0-alpha.3 → 10.0.0-alpha.4

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.
Files changed (83) hide show
  1. package/.github/CODING_GUIDELINES.md +1 -1
  2. package/README.md +52 -11
  3. package/ServiceWorker.mjs +2 -2
  4. package/apps/portal/index.html +1 -1
  5. package/apps/portal/view/home/FooterContainer.mjs +1 -1
  6. package/apps/portal/view/learn/ContentComponent.mjs +2 -1
  7. package/apps/portal/view/learn/MainContainerStateProvider.mjs +3 -6
  8. package/apps/realworld/view/HomeComponent.mjs +1 -1
  9. package/apps/realworld/view/user/ProfileComponent.mjs +1 -1
  10. package/apps/sharedcovid/view/MainContainerController.mjs +1 -1
  11. package/apps/shareddialog/view/MainContainerController.mjs +2 -2
  12. package/learn/README.md +83 -0
  13. package/learn/guides/ApplicationBootstrap.md +354 -0
  14. package/learn/guides/DeclarativeComponentTreesVsImperativeVdom.md +500 -0
  15. package/learn/guides/WorkingWithVDom.md +748 -0
  16. package/learn/tree.json +53 -0
  17. package/package.json +2 -2
  18. package/src/DefaultConfig.mjs +27 -14
  19. package/src/Main.mjs +1 -1
  20. package/src/Neo.mjs +16 -0
  21. package/src/button/Base.mjs +2 -2
  22. package/src/calendar/view/MainContainerStateProvider.mjs +1 -1
  23. package/src/grid/header/Button.mjs +1 -1
  24. package/src/layout/Cube.mjs +2 -2
  25. package/src/main/DeltaUpdates.mjs +11 -10
  26. package/src/main/addon/Navigator.mjs +1 -1
  27. package/src/main/addon/WindowPosition.mjs +1 -1
  28. package/src/main/render/StringBasedRenderer.mjs +1 -1
  29. package/src/tab/header/Toolbar.mjs +1 -1
  30. package/src/table/header/Button.mjs +1 -1
  31. package/src/toolbar/Base.mjs +1 -1
  32. package/src/util/VDom.mjs +1 -1
  33. package/src/util/VNode.mjs +1 -1
  34. package/src/vdom/Helper.mjs +96 -49
  35. package/src/vdom/VNode.mjs +38 -2
  36. package/src/worker/App.mjs +2 -1
  37. package/src/worker/Base.mjs +87 -5
  38. package/src/worker/Manager.mjs +86 -28
  39. package/resources/data/deck/learnneo/tree.json +0 -50
  40. package/resources/data/deck/whyneo.md +0 -80
  41. /package/{resources/data/deck/learnneo/pages → learn}/Glossary.md +0 -0
  42. /package/{resources/data/deck/learnneo/pages → learn}/UsingTheseTopics.md +0 -0
  43. /package/{resources/data/deck/learnneo/pages → learn}/benefits/ConfigSystem.md +0 -0
  44. /package/{resources/data/deck/learnneo/pages → learn}/benefits/Effort.md +0 -0
  45. /package/{resources/data/deck/learnneo/pages → learn}/benefits/Features.md +0 -0
  46. /package/{resources/data/deck/learnneo/pages → learn}/benefits/FormsEngine.md +0 -0
  47. /package/{resources/data/deck/learnneo/pages → learn}/benefits/FourEnvironments.md +0 -0
  48. /package/{resources/data/deck/learnneo/pages → learn}/benefits/Introduction.md +0 -0
  49. /package/{resources/data/deck/learnneo/pages → learn}/benefits/MultiWindow.md +0 -0
  50. /package/{resources/data/deck/learnneo/pages → learn}/benefits/OffTheMainThread.md +0 -0
  51. /package/{resources/data/deck/learnneo/pages → learn}/benefits/Quick.md +0 -0
  52. /package/{resources/data/deck/learnneo/pages → learn}/benefits/Speed.md +0 -0
  53. /package/{resources/data/deck/learnneo/pages → learn}/gettingstarted/ComponentModels.md +0 -0
  54. /package/{resources/data/deck/learnneo/pages → learn}/gettingstarted/Config.md +0 -0
  55. /package/{resources/data/deck/learnneo/pages → learn}/gettingstarted/DescribingTheUI.md +0 -0
  56. /package/{resources/data/deck/learnneo/pages → learn}/gettingstarted/Events.md +0 -0
  57. /package/{resources/data/deck/learnneo/pages → learn}/gettingstarted/Extending.md +0 -0
  58. /package/{resources/data/deck/learnneo/pages → learn}/gettingstarted/References.md +0 -0
  59. /package/{resources/data/deck/learnneo/pages → learn}/gettingstarted/Setup.md +0 -0
  60. /package/{resources/data/deck/learnneo/pages → learn}/gettingstarted/Workspaces.md +0 -0
  61. /package/{resources/data/deck/learnneo/pages → learn}/guides/ComponentsAndContainers.md +0 -0
  62. /package/{resources/data/deck/learnneo/pages → learn}/guides/CustomComponents.md +0 -0
  63. /package/{resources/data/deck/learnneo/pages → learn}/guides/Forms.md +0 -0
  64. /package/{resources/data/deck/learnneo/pages → learn}/guides/InstanceLifecycle.md +0 -0
  65. /package/{resources/data/deck/learnneo/pages → learn}/guides/Layouts.md +0 -0
  66. /package/{resources/data/deck/learnneo/pages → learn}/guides/MainThreadAddonExample.md +0 -0
  67. /package/{resources/data/deck/learnneo/pages → learn}/guides/MainThreadAddonIntro.md +0 -0
  68. /package/{resources/data/deck/learnneo/pages → learn}/guides/Mixins.md +0 -0
  69. /package/{resources/data/deck/learnneo/pages → learn}/guides/MultiWindow.md +0 -0
  70. /package/{resources/data/deck/learnneo/pages → learn}/guides/PortalApp.md +0 -0
  71. /package/{resources/data/deck/learnneo/pages → learn}/guides/StateProviders.md +0 -0
  72. /package/{resources/data/deck/learnneo/pages → learn}/guides/Tables.md +0 -0
  73. /package/{resources/data/deck/learnneo/pages → learn}/guides/events/CustomEvents.md +0 -0
  74. /package/{resources/data/deck/learnneo/pages → learn}/guides/events/DomEvents.md +0 -0
  75. /package/{resources/data/deck/learnneo/pages → learn}/javascript/ClassFeatures.md +0 -0
  76. /package/{resources/data/deck/learnneo/pages → learn}/javascript/Classes.md +0 -0
  77. /package/{resources/data/deck/learnneo/pages → learn}/javascript/NewNode.md +0 -0
  78. /package/{resources/data/deck/learnneo/pages → learn}/javascript/Overrides.md +0 -0
  79. /package/{resources/data/deck/learnneo/pages → learn}/javascript/Super.md +0 -0
  80. /package/{resources/data/deck/learnneo/pages → learn}/tutorials/Earthquakes.md +0 -0
  81. /package/{resources/data/deck/learnneo/pages → learn}/tutorials/RSP.md +0 -0
  82. /package/{resources/data/deck/learnneo/pages → learn}/tutorials/TodoList.md +0 -0
  83. /package/resources/data/{deck/learnneo/data/theBeatles.json → theBeatles.json} +0 -0
@@ -76,7 +76,7 @@ class VNode {
76
76
  } else {
77
77
  Object.assign(me, {
78
78
  attributes: config.attributes || {},
79
- className : config.className || [],
79
+ className : normalizeClassName(config.className),
80
80
  nodeName : config.nodeName || 'div',
81
81
  style : config.style
82
82
  });
@@ -88,7 +88,7 @@ class VNode {
88
88
 
89
89
  // We only apply textContent, in case it has content
90
90
  else if (Object.hasOwn(config, 'textContent')) {
91
- me.textContent = Neo.config.useStringBasedMounting ? StringUtil.escapeHtml(textContent) : textContent
91
+ me.textContent = Neo.config.useDomApiRenderer ? textContent : StringUtil.escapeHtml(textContent)
92
92
  }
93
93
  }
94
94
 
@@ -99,6 +99,42 @@ class VNode {
99
99
  }
100
100
  }
101
101
 
102
+ /**
103
+ * vdom cls definitions might contain spaces, especially when it comes to iconCls.
104
+ * @example: myVdom = {cls: ['my-button', 'fa fa-user']}
105
+ *
106
+ * On DOM level, classList.add() will throw, in case it gets an input containing a space.
107
+ *
108
+ * This is a module-scoped utility function, not a method of the VNode class.
109
+ * VNodes are transferred via structured cloning (e.g., in postMessage()), which strips methods.
110
+ * Keeping this logic separate from the VNode class itself ensures conceptual purity and a cleaner data model,
111
+ * as methods defined on the VNode instance would be lost during transfer anyway.
112
+ *
113
+ * @param {String|String[]} classNameInput
114
+ * @returns {String[]}
115
+ * @private
116
+ */
117
+ function normalizeClassName(classNameInput) {
118
+ let normalizedClasses = [];
119
+
120
+ if (Neo.isString(classNameInput)) {
121
+ normalizedClasses = classNameInput.split(' ').filter(Boolean)
122
+ } else if (Array.isArray(classNameInput)) {
123
+ classNameInput.forEach(cls => {
124
+ if (Neo.isString(cls)) {
125
+ if (cls.includes(' ')) {
126
+ normalizedClasses.push(...cls.split(' ').filter(Boolean))
127
+ } else if (cls !== '') {
128
+ normalizedClasses.push(cls)
129
+ }
130
+ }
131
+ })
132
+ }
133
+
134
+ // Remove duplicates if necessary
135
+ return [...new Set(normalizedClasses)]
136
+ }
137
+
102
138
  const ns = Neo.ns('Neo.vdom', true);
103
139
  ns.VNode = VNode;
104
140
 
@@ -35,7 +35,8 @@ class App extends Base {
35
35
  'destroyNeoInstance',
36
36
  'fireEvent',
37
37
  'getConfigs',
38
- 'setConfigs'
38
+ 'setConfigs',
39
+ 'setGlobalConfig' // points to worker.Base: setGlobalConfig()
39
40
  ]
40
41
  },
41
42
  /**
@@ -71,8 +71,9 @@ class Worker extends Base {
71
71
  gt.onmessage = me.onMessage.bind(me)
72
72
  }
73
73
 
74
- Neo.currentWorker = me;
75
- Neo.workerId = me.workerId
74
+ Neo.currentWorker = me;
75
+ Neo.setGlobalConfig = me.setGlobalConfig.bind(me);
76
+ Neo.workerId = me.workerId
76
77
  }
77
78
 
78
79
  /**
@@ -220,10 +221,17 @@ class Worker extends Base {
220
221
  }
221
222
 
222
223
  /**
223
- * @param {Object} msg
224
+ * Handles the initial registration of the `Neo.config` for this worker's realm.
225
+ * Triggered when receiving a worker message with `{action: 'registerNeoConfig'}` from the Main Thread's `Neo.worker.Manager`.
226
+ * This method is primarily responsible for setting the initial global `Neo.config` object in this worker's scope
227
+ * upon its creation. It also handles associating `windowId` with `MessagePort`s for Shared Workers.
228
+ *
229
+ * @param {Object} msg The incoming message object.
230
+ * @param {Object} msg.data The initial global Neo.config data object.
231
+ * @param {Number} msg.data.windowId The unique ID of the window/tab (relevant for SharedWorkers).
224
232
  */
225
233
  onRegisterNeoConfig(msg) {
226
- Neo.config = Neo.config || {};
234
+ Neo.ns('Neo.config', true);
227
235
 
228
236
  let me = this,
229
237
  {windowId} = msg.data,
@@ -236,7 +244,29 @@ class Worker extends Base {
236
244
  }
237
245
  }
238
246
 
239
- Object.assign(Neo.config, msg.data)
247
+ Neo.merge(Neo.config, msg.data)
248
+ }
249
+
250
+ /**
251
+ * Handles runtime updates to the global `Neo.config` for this worker's realm.
252
+ * This method is triggered when receiving a worker message with `{action: 'setNeoConfig'}`
253
+ * from the Main Thread's `Neo.worker.Manager`. This message signifies a global config change
254
+ * that originated either from this worker's Main Thread or was broadcast from another
255
+ * connected browser window via a Shared Worker.
256
+ *
257
+ * It merges the incoming configuration changes into this worker's local `Neo.config`
258
+ * and fires a local `neoConfigChange` event, allowing other instances within this worker
259
+ * to react to the updated configuration.
260
+ *
261
+ * @param {Object} msg The destructured arguments from the message payload.
262
+ * @param {Object} msg.config The partial or full `Neo.config` object to merge.
263
+ */
264
+ onSetNeoConfig({config}) {
265
+ let me = this;
266
+
267
+ Neo.merge(Neo.config, config);
268
+
269
+ me.fire('neoConfigChange', config)
240
270
  }
241
271
 
242
272
  /**
@@ -307,6 +337,58 @@ class Worker extends Base {
307
337
 
308
338
  return message
309
339
  }
340
+
341
+ /**
342
+ * Initiates a global Neo.config change from a worker's context.
343
+ * This method is exposed globally as `Neo.setGlobalConfig` within each worker realm.
344
+ *
345
+ * It orchestrates the propagation of the config change to the Main Thread
346
+ * and, if a Shared Worker is active, across all connected browser windows,
347
+ * ensuring a single, consistent Neo.config state everywhere.
348
+ *
349
+ * You can pass a partial config object to update specific keys.
350
+ * For nested objects, Neo.mjs performs a deep merge.
351
+ *
352
+ * @param {Object} config The partial or full Neo.config object with changes to apply.
353
+ */
354
+ setGlobalConfig(config) {
355
+ const
356
+ me = this,
357
+ {Manager} = Neo.worker; // Remote access proxy object
358
+
359
+ // Apply the config change locally to this worker's Neo.config and
360
+ // trigger its local change events immediately. This ensures immediate
361
+ // feedback and an updated state for the worker that initiated the change.
362
+ me.onSetNeoConfig({config});
363
+
364
+ if (me.isSharedWorker) {
365
+ // This block executes when the calling worker instance is a Shared Worker.
366
+ // This happens if `Neo.config.useSharedWorkers` is true, meaning App, VDom,
367
+ // Data, Canvas, and Task workers are all SharedWorker instances.
368
+ // This Shared Worker (the one where setGlobalConfig was called) acts as the
369
+ // central point to inform all connected Main Threads (browser windows).
370
+ me.ports.forEach((port, index) => {
371
+ // Send the config change to each connected Main Thread.
372
+ // The `broadcast` flag is crucial here for the *receiving* Main Thread:
373
+ // - `broadcast: true` (for the first port/Main Thread in the list): This Main Thread
374
+ // will apply the config locally and is then responsible for propagating it to *all*
375
+ // its own associated Shared Workers connected to that Main Thread),
376
+ // **excluding the worker that originated this change**.
377
+ // - `broadcast: false` (for all other ports/Main Threads): These Main Threads
378
+ // will simply apply the config locally and stop. They are passive recipients
379
+ // of the broadcast, synchronizing their state without initiating further actions back.
380
+ // The `excludeOrigin` parameter ensures the originating worker doesn't receive a redundant broadcast.
381
+ Manager.setNeoConfig({broadcast: index < 1, config, excludeOrigin: me.workerId, windowId: port.windowId})
382
+ })
383
+ } else {
384
+ // This Dedicated Worker (the one where setGlobalConfig was called) informs
385
+ // its single, connected Main Thread. The Main Thread will then:
386
+ // 1. Apply the config locally.
387
+ // 2. Broadcast this change to *all* other Dedicated Workers connected to
388
+ // *that same Main Thread*, **excluding the sender worker itself**.
389
+ Manager.setNeoConfig({broadcast: true, config, excludeOrigin: me.workerId})
390
+ }
391
+ }
310
392
  }
311
393
 
312
394
  export default Neo.setupClass(Worker);
@@ -27,11 +27,6 @@ class Manager extends Base {
27
27
  * @protected
28
28
  */
29
29
  className: 'Neo.worker.Manager',
30
- /**
31
- * @member {Boolean} singleton=true
32
- * @protected
33
- */
34
- singleton: true,
35
30
  /**
36
31
  * @member {Number} activeWorkers=0
37
32
  * @protected
@@ -51,12 +46,29 @@ class Manager extends Base {
51
46
  * @member {String[]|Neo.core.Base[]|null} mixins=[Observable, RemoteMethodAccess]
52
47
  */
53
48
  mixins: [Observable, RemoteMethodAccess],
49
+ /**
50
+ * Remote method access for other workers
51
+ * @member {Object} remote
52
+ * @protected
53
+ */
54
+ remote: {
55
+ app : ['setNeoConfig'],
56
+ canvas: ['setNeoConfig'],
57
+ data : ['setNeoConfig'],
58
+ task : ['setNeoConfig'],
59
+ vdom : ['setNeoConfig']
60
+ },
54
61
  /**
55
62
  * True in case the current browser supports window.SharedWorker.
56
63
  * @member {Boolean} sharedWorkersEnabled=false
57
64
  * @protected
58
65
  */
59
66
  sharedWorkersEnabled: false,
67
+ /**
68
+ * @member {Boolean} singleton=true
69
+ * @protected
70
+ */
71
+ singleton: true,
60
72
  /**
61
73
  * Internal flag to stop the worker communication in case their creation fails
62
74
  * @member {Boolean} stopCommunication=false
@@ -71,7 +83,7 @@ class Manager extends Base {
71
83
  */
72
84
  webWorkersEnabled: false,
73
85
  /**
74
- * Using the current timestamp as an unique window identifier
86
+ * Using the current timestamp as a unique window identifier
75
87
  * @member {Number} windowId=new Date().getTime()
76
88
  * @protected
77
89
  */
@@ -121,7 +133,8 @@ class Manager extends Base {
121
133
 
122
134
  !Neo.insideWorker && me.createWorkers();
123
135
 
124
- Neo.workerId = 'main';
136
+ Neo.setGlobalConfig = me.setGlobalConfig.bind(me);
137
+ Neo.workerId = 'main';
125
138
 
126
139
  me.promises = {};
127
140
 
@@ -136,18 +149,18 @@ class Manager extends Base {
136
149
 
137
150
  /**
138
151
  * Sends a message to each worker defined inside the this.workers config.
139
- * @param {Object} msg
152
+ * Only sends to workers that are currently active and available.
153
+ * @param {Object} msg The message payload to broadcast.
154
+ * @param {Object} [excludeOrigin] Optionally pass the origin realm name to exclude from the broadcast.
140
155
  */
141
- broadcast(msg) {
142
- Object.keys(this.workers).forEach(name => {
143
- if (!(
144
- name === 'canvas' && !NeoConfig.useCanvasWorker ||
145
- name === 'task' && !NeoConfig.useTaskWorker ||
146
- name === 'vdom' && !NeoConfig.useVdomWorker
147
- )) {
148
- this.sendMessage(name, msg)
156
+ broadcast(msg, excludeOrigin) {
157
+ let me = this;
158
+
159
+ Object.keys(me.workers).forEach(name => {
160
+ if (name !== excludeOrigin && me.getWorker(name)) {
161
+ me.sendMessage(name, msg)
149
162
  }
150
- });
163
+ })
151
164
  }
152
165
 
153
166
  /**
@@ -325,8 +338,10 @@ class Manager extends Base {
325
338
  data = data.data
326
339
  }
327
340
 
328
- promise[data.reject ? 'reject' : 'resolve'](data);
329
- delete me.promises[replyId]
341
+ if (data) {
342
+ promise[data.reject ? 'reject' : 'resolve'](data);
343
+ delete me.promises[replyId]
344
+ }
330
345
  }
331
346
  }
332
347
 
@@ -385,7 +400,14 @@ class Manager extends Base {
385
400
 
386
401
  return new Promise((resolve, reject) => {
387
402
  let message = me.sendMessage(dest, opts, transfer),
388
- msgId = message.id;
403
+ msgId;
404
+
405
+ if (!message) {
406
+ reject(new Error(me.stopCommunication ? 'Communication is stopped.' : `Target worker '${dest}' does not exist.`));
407
+ return
408
+ }
409
+
410
+ msgId = message.id;
389
411
 
390
412
  me.promises[msgId] = {reject, resolve}
391
413
  })
@@ -412,7 +434,7 @@ class Manager extends Base {
412
434
  * @param {Array} [transfer] An optional array of Transferable objects to transfer ownership of.
413
435
  * If the ownership of an object is transferred, it becomes unusable (neutered) in the context it was sent from
414
436
  * and becomes available only to the worker it was sent to.
415
- * @returns {Neo.worker.Message}
437
+ * @returns {Neo.worker.Message|null}
416
438
  * @protected
417
439
  */
418
440
  sendMessage(dest, opts, transfer) {
@@ -427,17 +449,53 @@ class Manager extends Base {
427
449
  worker = me.getWorker(dest)
428
450
  }
429
451
 
430
- if (!worker) {
431
- throw new Error('Called sendMessage for a worker that does not exist: ' + dest)
452
+ if (worker) {
453
+ opts.destination = dest;
454
+
455
+ message = new Message(opts);
456
+
457
+ (worker.port ? worker.port : worker).postMessage(message, transfer);
458
+ return message
432
459
  }
460
+ }
461
+
462
+ return null
463
+ }
464
+
465
+ /**
466
+ * Initiates a global Neo.config change from the Main Thread.
467
+ *
468
+ * This method acts as a proxy, routing the config change request to the App Worker.
469
+ * This design centralizes the complex multi-threaded and multi-window synchronization
470
+ * logic within the App Worker's `setGlobalConfig` method.
471
+ *
472
+ * Developers should typically use `Neo.setGlobalConfig(config)` directly,
473
+ * which will correctly resolve to this proxy when called from the Main Thread.
474
+ *
475
+ * @param {Object} config The partial or full Neo.config object with changes to apply.
476
+ */
477
+ setGlobalConfig(config) {
478
+ // Remotely calls the App Worker's setGlobalConfig method.
479
+ // This ensures all global config changes are processed through the App Worker
480
+ // which contains the centralized multi-window synchronization logic.
481
+ Neo.worker.App.setGlobalConfig(config)
482
+ }
433
483
 
434
- opts.destination = dest;
484
+ /**
485
+ * Change Neo.config globally from a worker
486
+ * @param {Object} data
487
+ * @param {Boolean} data.broadcast
488
+ * @param {Object} data.config
489
+ * @param {String} [data.excludeOrigin]
490
+ */
491
+ setNeoConfig({broadcast, config, excludeOrigin}) {
492
+ let me = this;
435
493
 
436
- message = new Message(opts);
494
+ Neo.merge(Neo.config, config);
437
495
 
438
- (worker.port ? worker.port : worker).postMessage(message, transfer);
439
- return message
440
- }
496
+ me.fire('neoConfigChange', config);
497
+
498
+ broadcast && me.broadcast({action: 'setNeoConfig', config}, excludeOrigin)
441
499
  }
442
500
  }
443
501
 
@@ -1,50 +0,0 @@
1
- {"data": [
2
- {"name": "Using These Topics", "parentId": null, "id": "UsingTheseTopics" },
3
- {"name": "Benefits", "parentId": null, "isLeaf": false, "id": "Benefits"},
4
- {"name": "Introduction ", "parentId": "Benefits", "id": "benefits.Introduction"},
5
- {"name": "Off the Main Thread", "parentId": "Benefits", "id": "benefits.OffTheMainThread"},
6
- {"name": "4 Environments", "parentId": "Benefits", "id": "benefits.FourEnvironments"},
7
- {"name": "Unified Config System", "parentId": "Benefits", "id": "benefits.ConfigSystem"},
8
- {"name": "Extreme Speed", "parentId": "Benefits", "id": "benefits.Speed"},
9
- {"name": "Multi-Window Applications", "parentId": "Benefits", "id": "benefits.MultiWindow"},
10
- {"name": "Quick Application Development", "parentId": "Benefits", "id": "benefits.Quick"},
11
- {"name": "Complexity and Effort", "parentId": "Benefits", "id": "benefits.Effort"},
12
- {"name": "Forms Engine", "parentId": "Benefits", "id": "benefits.FormsEngine"},
13
- {"name": "Features and Benefits Summary", "parentId": "Benefits", "id": "benefits.Features"},
14
- {"name": "Getting Started", "parentId": null, "isLeaf": false, "id": "GettingStarted", "collapsed": true},
15
- {"name": "Setup", "parentId": "GettingStarted", "id": "gettingstarted.Setup"},
16
- {"name": "Workspaces and Applications", "parentId": "GettingStarted", "id": "gettingstarted.Workspaces"},
17
- {"name": "Describing a View", "parentId": "GettingStarted", "id": "gettingstarted.DescribingTheUI"},
18
- {"name": "Events", "parentId": "GettingStarted", "id": "gettingstarted.Events"},
19
- {"name": "Component References", "parentId": "GettingStarted", "id": "gettingstarted.References"},
20
- {"name": "Extending Classes", "parentId": "GettingStarted", "id": "gettingstarted.Extending"},
21
- {"name": "Config", "parentId": "GettingStarted", "id": "gettingstarted.Config"},
22
- {"name": "Shared Bindable Data", "parentId": "GettingStarted", "id": "gettingstarted.ComponentModels"},
23
- {"name": "Tutorials", "parentId": null, "isLeaf": false, "id": "Tutorials", "collapsed": true},
24
- {"name": "Rock Scissors Paper", "parentId": "Tutorials", "id": "tutorials.RSP", "hidden": true},
25
- {"name": "Earthquakes", "parentId": "Tutorials", "id": "tutorials.Earthquakes"},
26
- {"name": "Todo List", "parentId": "Tutorials", "id": "tutorials.TodoList"},
27
- {"name": "Guides", "parentId": null, "isLeaf": false, "id": "InDepth", "collapsed": true},
28
- {"name": "Instance Lifecycle", "parentId": "InDepth", "id": "guides.InstanceLifecycle", "hidden": true},
29
- {"name": "User Input (Forms)", "parentId": "InDepth", "id": "guides.Forms", "hidden": true},
30
- {"name": "Component and Container Basics", "parentId": "InDepth", "id": "guides.ComponentsAndContainers"},
31
- {"name": "Layouts", "parentId": "InDepth", "isLeaf": false, "id": "guides.Layouts", "hidden": true},
32
- {"name": "Shared Bindable Data (State Providers)", "parentId": "InDepth", "id": "guides.StateProviders"},
33
- {"name": "Custom Components", "parentId": "InDepth", "id": "guides.CustomComponents", "hidden": true},
34
- {"name": "Events", "parentId": "InDepth", "isLeaf": false, "id": "GuideEvents"},
35
- {"name": "Custom Events", "parentId": "GuideEvents", "id": "guides.events.CustomEvents"},
36
- {"name": "DOM Events", "parentId": "GuideEvents", "id": "guides.events.DomEvents"},
37
- {"name": "Portal App", "parentId": "InDepth", "id": "guides.PortalApp"},
38
- {"name": "Tables (Stores)", "parentId": "InDepth", "id": "guides.Tables", "hidden": true},
39
- {"name": "Multi-Window Applications", "parentId": "InDepth", "id": "guides.MultiWindow", "hidden": true},
40
- {"name": "Main Thread Addons", "parentId": "InDepth", "isLeaf": false, "id": "MainThreadAddons", "hidden": true},
41
- {"name": "Introduction", "parentId": "MainThreadAddons", "id": "guides.MainThreadAddonIntro"},
42
- {"name": "Example", "parentId": "MainThreadAddons", "id": "guides.MainThreadAddonExample"},
43
- {"name": "Mixins", "parentId": "InDepth", "id": "guides.Mixins", "hidden": true},
44
- {"name": "JavaScript Classes", "parentId": null, "isLeaf": false, "id": "JavaScript", "hidden": true},
45
- {"name": "Classes, Properties, and Methods", "parentId": "JavaScript", "id": "javascript.Classes"},
46
- {"name": "Overriding Methods", "parentId": "JavaScript", "id": "javascript.Overrides"},
47
- {"name": "Other JavaScript Class Features", "parentId": "JavaScript", "id": "javascript.ClassFeatures"},
48
- {"name": "Super", "parentId": "JavaScript", "id": "javascript.Super"},
49
- {"name": "New Node", "parentId": "JavaScript", "id": "javascript.NewNode"}
50
- ]}
@@ -1,80 +0,0 @@
1
-
2
- #hi
3
-
4
-
5
- Neo.mjs is a framework used to create browser-based applications.
6
-
7
- Some key features and benefits of Neo.mjs are:
8
-
9
- <div type="expander" caption="Multi-Threaded">
10
- <p>
11
- When a Neo.mjs application starts, the framework spawns three web-workers.
12
- Web-workers are each run in their own thread. As a result, a typical Neo.mjs application
13
- has four threads:
14
- <ol>
15
- <li>The _main_ thread, where DOM updates are applied
16
- <li>An _application_ web-worker where normal application locic is run
17
- <li>A _data_ web-worker were HTTP and socket calls are run
18
- <li>A _view_ web-worker that manages delta updates
19
- </ol>
20
- </div>
21
-
22
- <div type="expander" caption="Extreme Speed">
23
- <p>
24
- The Neo.mjs web-worker proccesses are automatically run in parallel, on separate CPU cores.
25
- </p>
26
- <p>
27
- By contrast, other JavaScript frameworks run in a single thread. That means
28
- in a typical framework all business logic, data handling, and DOM rendering compete for
29
- CPU reasources.
30
- </p>
31
- <p>
32
- This means Neo.mjs applications run and render faster. This is
33
- particularly beneficial for processor- and data-intensive applications,
34
- and applications that need to rapidly update what's viewed. In testing, Neo.mjs applications
35
- easily apply over 20,000 DOM updates per second.
36
- </p>
37
- <p>
38
- If the default four threads aren't enough, you're free to launch additional web-worker threads
39
- to run other specialized logic.
40
- </p>
41
- </div>
42
-
43
- <div type="expander" caption="Quick Application Development">
44
- <p>
45
- Neo.js classes let you specify properties in a way that allows code to detect "before" and "after"
46
- changes. This makes it easy to handle value validation and transformation, and react to changes.
47
- </p>
48
- <p>
49
- Neo.mjs also has elegant yet powerful state management features that make it easy to create shared,
50
- bindable data. For example, if two components are bound to the same propery, a change to the
51
- property will automatically be applied to both components.
52
- </p>
53
- </div>
54
-
55
- <div type="expander" caption="Multi-Window Applications">
56
- <p>
57
- Neo.mjs applications can also launch as _shared web workers_, which allows you to have a single
58
- application run in multiple browser windows; those windows could be moved to multiple monitors.
59
- </p>
60
- <p>
61
- For example, you can have a data analysis application with a control panel on one monitor,
62
- tabular data in another, and charts on another &mdash; all sharing the same data, handling events
63
- across windows, running seamlessly as a single application.
64
- </p>
65
- </div>
66
-
67
- <div type="expander" caption="Open-Source and Standards-Based">
68
- <p>
69
- Neo.mjs is an open-source library. Features needed for the community can be added to the
70
- library via pull-requests. And since Neo.mjs uses the standard JavaScript class system,
71
- all Neo.mjs classes can be extended.
72
- </p>
73
- <p>
74
- Neo.mjs uses standard modular JavaScript, so developers don't need to learn non-standard language
75
- syntax, and there's no need for special pre-compilers or WebPack modules.
76
- That means fewer dependencies and easier configuration. Furthermore, the use of
77
- standard JavaScript makes debugging easier: any statement you write while developing your
78
- applcation can also be run in the debugging console.
79
- </p>
80
- </div>