neo.mjs 3.2.5 → 3.2.8
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/apps/ServiceWorker.mjs +36 -0
- package/buildScripts/webpack/buildThreads.mjs +7 -6
- package/buildScripts/webpack/json/build.json +4 -0
- package/buildScripts/webpack/json/myApps.template.json +1 -19
- package/examples/ServiceWorker.mjs +36 -0
- package/examples/component/helix/neo-config.json +2 -1
- package/examples/preloadingAssets/app.mjs +6 -0
- package/examples/preloadingAssets/index.html +11 -0
- package/examples/preloadingAssets/neo-config.json +7 -0
- package/examples/preloadingAssets/view/MainContainer.mjs +60 -0
- package/examples/preloadingAssets/view/MainContainerController.mjs +41 -0
- package/package.json +6 -6
- package/src/DefaultConfig.mjs +10 -1
- package/src/Main.mjs +16 -12
- package/src/calendar/README.md +1 -1
- package/src/calendar/view/EditEventContainer.mjs +1 -4
- package/src/calendar/view/MainContainer.mjs +2 -4
- package/src/layout/HBox.mjs +1 -3
- package/src/layout/VBox.mjs +1 -3
- package/src/main/addon/ServiceWorker.mjs +71 -0
- package/src/main/addon/Stylesheet.mjs +0 -1
- package/src/tab/Container.mjs +4 -4
- package/src/worker/App.mjs +2 -2
- package/src/worker/Manager.mjs +11 -2
- package/src/worker/ServiceBase.mjs +336 -0
- package/src/worker/mixin/RemoteMethodAccess.mjs +1 -1
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import Neo from '../src/Neo.mjs';
|
|
2
|
+
import * as core from '../src/core/_export.mjs';
|
|
3
|
+
import ServiceBase from '../src/worker/ServiceBase.mjs';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @class Neo.ServiceWorker
|
|
7
|
+
* @extends Neo.worker.ServiceBase
|
|
8
|
+
* @singleton
|
|
9
|
+
*/
|
|
10
|
+
class ServiceWorker extends ServiceBase {
|
|
11
|
+
static getConfig() {return {
|
|
12
|
+
/**
|
|
13
|
+
* @member {String} className='Neo.ServiceWorker'
|
|
14
|
+
* @protected
|
|
15
|
+
*/
|
|
16
|
+
className: 'Neo.ServiceWorker',
|
|
17
|
+
/**
|
|
18
|
+
* @member {Boolean} singleton=true
|
|
19
|
+
* @protected
|
|
20
|
+
*/
|
|
21
|
+
singleton: true,
|
|
22
|
+
/**
|
|
23
|
+
* @member {String} workerId='service'
|
|
24
|
+
* @protected
|
|
25
|
+
*/
|
|
26
|
+
workerId: 'service'
|
|
27
|
+
}}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
Neo.applyClassConfig(ServiceWorker);
|
|
31
|
+
|
|
32
|
+
let instance = Neo.create(ServiceWorker);
|
|
33
|
+
|
|
34
|
+
Neo.applyToGlobalNs(instance);
|
|
35
|
+
|
|
36
|
+
export default instance;
|
|
@@ -61,7 +61,7 @@ if (programOpts.info) {
|
|
|
61
61
|
type : 'list',
|
|
62
62
|
name : 'threads',
|
|
63
63
|
message: 'Please choose the threads to build:',
|
|
64
|
-
choices: ['all', 'app', 'canvas', 'data', 'main', 'vdom'],
|
|
64
|
+
choices: ['all', 'app', 'canvas', 'data', 'main', 'service', 'vdom'],
|
|
65
65
|
default: 'all'
|
|
66
66
|
});
|
|
67
67
|
}
|
|
@@ -88,11 +88,12 @@ if (programOpts.info) {
|
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
function parseThreads(tPath) {
|
|
91
|
-
(threads === 'all' || threads === 'main')
|
|
92
|
-
(threads === 'all' || threads === 'app')
|
|
93
|
-
(threads === 'all' || threads === 'canvas')
|
|
94
|
-
(threads === 'all' || threads === 'data')
|
|
95
|
-
(threads === 'all' || threads === '
|
|
91
|
+
(threads === 'all' || threads === 'main') && spawnSync(webpack, ['--config', `${tPath}.main.mjs`], cpOpts);
|
|
92
|
+
(threads === 'all' || threads === 'app') && spawnSync(webpack, ['--config', `${tPath}.appworker.mjs`, `--env insideNeo=${insideNeo}`], cpOpts);
|
|
93
|
+
(threads === 'all' || threads === 'canvas') && spawnSync(webpack, ['--config', `${tPath}.worker.mjs`, `--env insideNeo=${insideNeo} worker=canvas`], cpOpts);
|
|
94
|
+
(threads === 'all' || threads === 'data') && spawnSync(webpack, ['--config', `${tPath}.worker.mjs`, `--env insideNeo=${insideNeo} worker=data`], cpOpts);
|
|
95
|
+
(threads === 'all' || threads === 'service') && spawnSync(webpack, ['--config', `${tPath}.worker.mjs`, `--env insideNeo=${insideNeo} worker=service`], cpOpts);
|
|
96
|
+
(threads === 'all' || threads === 'vdom') && spawnSync(webpack, ['--config', `${tPath}.worker.mjs`, `--env insideNeo=${insideNeo} worker=vdom`], cpOpts);
|
|
96
97
|
}
|
|
97
98
|
|
|
98
99
|
// dist/development
|
|
@@ -13,23 +13,5 @@
|
|
|
13
13
|
"SharedDialog",
|
|
14
14
|
"SharedDialog2",
|
|
15
15
|
"Website"
|
|
16
|
-
]
|
|
17
|
-
"workers": {
|
|
18
|
-
"app": {
|
|
19
|
-
"input": "./src/worker/App.mjs",
|
|
20
|
-
"output": "appworker.js"
|
|
21
|
-
},
|
|
22
|
-
"canvas": {
|
|
23
|
-
"input": "./src/worker/Canvas.mjs",
|
|
24
|
-
"output": "canvasworker.js"
|
|
25
|
-
},
|
|
26
|
-
"data": {
|
|
27
|
-
"input": "./src/worker/Data.mjs",
|
|
28
|
-
"output": "dataworker.js"
|
|
29
|
-
},
|
|
30
|
-
"vdom": {
|
|
31
|
-
"input": "./src/worker/VDom.mjs",
|
|
32
|
-
"output": "vdomworker.js"
|
|
33
|
-
}
|
|
34
|
-
}
|
|
16
|
+
]
|
|
35
17
|
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import Neo from '../src/Neo.mjs';
|
|
2
|
+
import * as core from '../src/core/_export.mjs';
|
|
3
|
+
import ServiceBase from '../src/worker/ServiceBase.mjs';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @class Neo.ServiceWorker
|
|
7
|
+
* @extends Neo.worker.ServiceBase
|
|
8
|
+
* @singleton
|
|
9
|
+
*/
|
|
10
|
+
class ServiceWorker extends ServiceBase {
|
|
11
|
+
static getConfig() {return {
|
|
12
|
+
/**
|
|
13
|
+
* @member {String} className='Neo.ServiceWorker'
|
|
14
|
+
* @protected
|
|
15
|
+
*/
|
|
16
|
+
className: 'Neo.ServiceWorker',
|
|
17
|
+
/**
|
|
18
|
+
* @member {Boolean} singleton=true
|
|
19
|
+
* @protected
|
|
20
|
+
*/
|
|
21
|
+
singleton: true,
|
|
22
|
+
/**
|
|
23
|
+
* @member {String} workerId='service'
|
|
24
|
+
* @protected
|
|
25
|
+
*/
|
|
26
|
+
workerId: 'service'
|
|
27
|
+
}}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
Neo.applyClassConfig(ServiceWorker);
|
|
31
|
+
|
|
32
|
+
let instance = Neo.create(ServiceWorker);
|
|
33
|
+
|
|
34
|
+
Neo.applyToGlobalNs(instance);
|
|
35
|
+
|
|
36
|
+
export default instance;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<!DOCTYPE HTML>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
5
|
+
<meta charset="UTF-8">
|
|
6
|
+
<title>Preloading Assets</title>
|
|
7
|
+
</head>
|
|
8
|
+
<body>
|
|
9
|
+
<script src="../../src/MicroLoader.mjs" type="module"></script>
|
|
10
|
+
</body>
|
|
11
|
+
</html>
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import Component from '../../../src/component/Base.mjs';
|
|
2
|
+
import MainContainerController from './MainContainerController.mjs'
|
|
3
|
+
import TabContainer from '../../../src/tab/Container.mjs';
|
|
4
|
+
import Viewport from '../../../src/container/Viewport.mjs';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @class Neo.examples.preloadingAssets.view.MainContainer
|
|
8
|
+
* @extends Neo.container.Viewport
|
|
9
|
+
*/
|
|
10
|
+
class MainContainer extends Viewport {
|
|
11
|
+
static getConfig() {return {
|
|
12
|
+
className: 'Neo.examples.preloadingAssets.view.MainContainer',
|
|
13
|
+
autoMount: true,
|
|
14
|
+
controller: MainContainerController,
|
|
15
|
+
layout : {ntype: 'fit'},
|
|
16
|
+
|
|
17
|
+
items: [{
|
|
18
|
+
module: TabContainer,
|
|
19
|
+
height: 300,
|
|
20
|
+
width : 500,
|
|
21
|
+
style : {flex: 'none', margin: '20px'},
|
|
22
|
+
|
|
23
|
+
itemDefaults: {
|
|
24
|
+
module: Component,
|
|
25
|
+
style : {flex: 'none', padding: '20px'}
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
items: [{
|
|
29
|
+
tabButtonConfig: {
|
|
30
|
+
iconCls: 'fa fa-user-astronaut',
|
|
31
|
+
text : 'Bob'
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
vdom: {
|
|
35
|
+
tag: 'img',
|
|
36
|
+
src: '../../resources/examples/ai_images/000150.jpg'
|
|
37
|
+
}
|
|
38
|
+
}, {
|
|
39
|
+
tabButtonConfig: {
|
|
40
|
+
iconCls: 'fa fa-user-ninja',
|
|
41
|
+
text : 'Alice'
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
vdom: {
|
|
45
|
+
tag: 'img',
|
|
46
|
+
src: '../../resources/examples/ai_images/000074.jpg'
|
|
47
|
+
}
|
|
48
|
+
}],
|
|
49
|
+
|
|
50
|
+
listeners: {
|
|
51
|
+
activeIndexChange: 'onActiveIndexChange',
|
|
52
|
+
mounted : 'onMounted'
|
|
53
|
+
}
|
|
54
|
+
}]
|
|
55
|
+
}}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
Neo.applyClassConfig(MainContainer);
|
|
59
|
+
|
|
60
|
+
export default MainContainer;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import Component from '../../../src/controller/Component.mjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @class Neo.examples.preloadingAssets.view.MainContainerController
|
|
5
|
+
* @extends Neo.controller.Component
|
|
6
|
+
*/
|
|
7
|
+
class MainContainerController extends Component {
|
|
8
|
+
static getConfig() {return {
|
|
9
|
+
/**
|
|
10
|
+
* @member {String} className='Neo.examples.preloadingAssets.view.MainContainerController'
|
|
11
|
+
* @protected
|
|
12
|
+
*/
|
|
13
|
+
className: 'Neo.examples.preloadingAssets.view.MainContainerController'
|
|
14
|
+
}}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @param {Object} data
|
|
18
|
+
*/
|
|
19
|
+
onActiveIndexChange(data) {
|
|
20
|
+
console.log('onActiveIndexChange', data);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @param {String} id
|
|
25
|
+
*/
|
|
26
|
+
onMounted(id) {
|
|
27
|
+
console.log('onMounted', id);
|
|
28
|
+
|
|
29
|
+
setTimeout(() => {
|
|
30
|
+
Neo.ServiceWorker.preloadAssets({
|
|
31
|
+
files: ['../resources/examples/ai_images/000074.jpg']
|
|
32
|
+
}).then(data => {
|
|
33
|
+
console.log(data);
|
|
34
|
+
});
|
|
35
|
+
}, 3000)
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
Neo.applyClassConfig(MainContainerController);
|
|
40
|
+
|
|
41
|
+
export default MainContainerController;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "neo.mjs",
|
|
3
|
-
"version": "3.2.
|
|
3
|
+
"version": "3.2.8",
|
|
4
4
|
"description": "The webworkers driven UI framework",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -41,16 +41,16 @@
|
|
|
41
41
|
"chalk": "^5.0.0",
|
|
42
42
|
"clean-webpack-plugin": "^4.0.0",
|
|
43
43
|
"commander": "^9.0.0",
|
|
44
|
-
"cssnano": "^5.0
|
|
44
|
+
"cssnano": "^5.1.0",
|
|
45
45
|
"envinfo": "^7.8.1",
|
|
46
|
-
"fs-extra": "^10.0.
|
|
46
|
+
"fs-extra": "^10.0.1",
|
|
47
47
|
"highlightjs-line-numbers.js": "^2.8.0",
|
|
48
48
|
"inquirer": "^8.1.5",
|
|
49
49
|
"neo-jsdoc": "^1.0.1",
|
|
50
50
|
"neo-jsdoc-x": "^1.0.4",
|
|
51
|
-
"postcss": "^8.4.
|
|
52
|
-
"sass": "^1.49.
|
|
53
|
-
"webpack": "^5.
|
|
51
|
+
"postcss": "^8.4.7",
|
|
52
|
+
"sass": "^1.49.9",
|
|
53
|
+
"webpack": "^5.70.0",
|
|
54
54
|
"webpack-cli": "^4.9.2",
|
|
55
55
|
"webpack-dev-server": "4.7.4",
|
|
56
56
|
"webpack-hook-plugin": "^1.0.7",
|
package/src/DefaultConfig.mjs
CHANGED
|
@@ -134,7 +134,7 @@ const DefaultConfig = {
|
|
|
134
134
|
* Experimental flag if an offscreen canvas worker should get created.
|
|
135
135
|
* @default false
|
|
136
136
|
* @memberOf! module:Neo
|
|
137
|
-
* @name config.
|
|
137
|
+
* @name config.useCanvasWorker
|
|
138
138
|
* @type Boolean
|
|
139
139
|
*/
|
|
140
140
|
useCanvasWorker: false,
|
|
@@ -163,6 +163,15 @@ const DefaultConfig = {
|
|
|
163
163
|
* @type Boolean
|
|
164
164
|
*/
|
|
165
165
|
useGoogleAnalytics: false,
|
|
166
|
+
/**
|
|
167
|
+
* True will add the ServiceWorker main thread addon to support caching of assets (PWA)
|
|
168
|
+
* See: https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API
|
|
169
|
+
* @default false
|
|
170
|
+
* @memberOf! module:Neo
|
|
171
|
+
* @name config.useServiceWorker
|
|
172
|
+
* @type Boolean
|
|
173
|
+
*/
|
|
174
|
+
useServiceWorker: false,
|
|
166
175
|
/**
|
|
167
176
|
* Creates App, Data & VDom as SharedWorkers.
|
|
168
177
|
* Set this one to true in case you want to connect multiple main threads.
|
package/src/Main.mjs
CHANGED
|
@@ -188,9 +188,11 @@ class Main extends core.Base {
|
|
|
188
188
|
*
|
|
189
189
|
*/
|
|
190
190
|
async onDomContentLoaded() {
|
|
191
|
-
let me
|
|
192
|
-
config
|
|
193
|
-
|
|
191
|
+
let me = this,
|
|
192
|
+
config = Neo.config,
|
|
193
|
+
mainThreadAddons = config.mainThreadAddons,
|
|
194
|
+
imports = [],
|
|
195
|
+
modules;
|
|
194
196
|
|
|
195
197
|
DomAccess.onDomContentLoaded();
|
|
196
198
|
|
|
@@ -203,18 +205,20 @@ class Main extends core.Base {
|
|
|
203
205
|
__webpack_require__.p = config.basePath.substring(6);
|
|
204
206
|
}
|
|
205
207
|
|
|
206
|
-
config.mainThreadAddons.forEach(addon => {
|
|
207
|
-
if (addon !== 'AnalyticsByGoogle') {
|
|
208
|
-
imports.push(import(`./main/addon/${addon}.mjs`));
|
|
209
|
-
}
|
|
210
|
-
});
|
|
211
|
-
|
|
212
208
|
// intended for the online examples where we need an easy way to add GA to every generated app
|
|
213
|
-
if (config.useGoogleAnalytics
|
|
214
|
-
|
|
209
|
+
if (config.useGoogleAnalytics && !mainThreadAddons.includes('AnalyticsByGoogle')) {
|
|
210
|
+
mainThreadAddons.push('AnalyticsByGoogle');
|
|
215
211
|
}
|
|
216
212
|
|
|
217
|
-
|
|
213
|
+
if (config.useServiceWorker && !mainThreadAddons.includes('ServiceWorker')) {
|
|
214
|
+
mainThreadAddons.push('ServiceWorker');
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
mainThreadAddons.forEach(addon => {
|
|
218
|
+
imports.push(import(`./main/addon/${addon}.mjs`));
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
modules = await Promise.all(imports);
|
|
218
222
|
|
|
219
223
|
me.addon = {};
|
|
220
224
|
|
package/src/calendar/README.md
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
# Development in progress
|
|
2
|
-
Currently, scheduled for the
|
|
2
|
+
Currently, scheduled for the v4.x release
|
|
@@ -92,10 +92,7 @@ class EditEventContainer extends FormContainer {
|
|
|
92
92
|
*/
|
|
93
93
|
afterSetMounted(value, oldValue) {
|
|
94
94
|
super.afterSetMounted(value, oldValue);
|
|
95
|
-
|
|
96
|
-
if (value) {
|
|
97
|
-
this.getField('title').focus();
|
|
98
|
-
}
|
|
95
|
+
value && this.getField('title').focus();
|
|
99
96
|
}
|
|
100
97
|
|
|
101
98
|
/**
|
|
@@ -310,7 +310,7 @@ class MainContainer extends Container {
|
|
|
310
310
|
style : {marginLeft: '10px'}
|
|
311
311
|
});
|
|
312
312
|
}, 10);
|
|
313
|
-
} else if (value
|
|
313
|
+
} else if (!value && oldValue) {
|
|
314
314
|
// we only need this logic in case we dynamically change the config from true to false
|
|
315
315
|
me.items[1] .removeLast();
|
|
316
316
|
me.items[0].items[1].removeLast();
|
|
@@ -652,9 +652,7 @@ class MainContainer extends Container {
|
|
|
652
652
|
|
|
653
653
|
map[me.activeView]();
|
|
654
654
|
|
|
655
|
-
me.model.setData({
|
|
656
|
-
currentDate: currentDate
|
|
657
|
-
});
|
|
655
|
+
me.model.setData({currentDate});
|
|
658
656
|
}
|
|
659
657
|
}
|
|
660
658
|
|
package/src/layout/HBox.mjs
CHANGED
package/src/layout/VBox.mjs
CHANGED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import Base from '../../core/Base.mjs';
|
|
2
|
+
import RemoteMethodAccess from '../../worker/mixin/RemoteMethodAccess.mjs';
|
|
3
|
+
import WorkerManager from '../../worker/Manager.mjs';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Creates a ServiceWorker instance, in case Neo.config.useServiceWorker is set to true
|
|
7
|
+
* @class Neo.main.addon.ServiceWorker
|
|
8
|
+
* @extends Neo.core.Base
|
|
9
|
+
* @singleton
|
|
10
|
+
*/
|
|
11
|
+
class ServiceWorker extends Base {
|
|
12
|
+
static getConfig() {return {
|
|
13
|
+
/**
|
|
14
|
+
* @member {String} className='Neo.main.addon.ServiceWorker'
|
|
15
|
+
* @protected
|
|
16
|
+
*/
|
|
17
|
+
className: 'Neo.main.addon.ServiceWorker',
|
|
18
|
+
/**
|
|
19
|
+
* @member {String[]|Neo.core.Base[]|null} mixins=[RemoteMethodAccess]
|
|
20
|
+
*/
|
|
21
|
+
mixins: [RemoteMethodAccess],
|
|
22
|
+
/**
|
|
23
|
+
* @member {ServiceWorkerRegistration|null} registration=null
|
|
24
|
+
* @protected
|
|
25
|
+
*/
|
|
26
|
+
registration: null,
|
|
27
|
+
/**
|
|
28
|
+
* @member {Boolean} singleton=true
|
|
29
|
+
* @protected
|
|
30
|
+
*/
|
|
31
|
+
singleton: true
|
|
32
|
+
}}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @param {Object} config
|
|
36
|
+
*/
|
|
37
|
+
construct(config) {
|
|
38
|
+
if ('serviceWorker' in navigator) {
|
|
39
|
+
let me = this,
|
|
40
|
+
config = Neo.config,
|
|
41
|
+
devMode = config.environment === 'development',
|
|
42
|
+
fileName = devMode ? 'ServiceWorker.mjs' : 'serviceworker.js',
|
|
43
|
+
folder = window.location.pathname.includes('/examples/') ? 'examples/' : 'apps/',
|
|
44
|
+
opts = devMode ? {type: 'module'} : {},
|
|
45
|
+
serviceWorker = navigator.serviceWorker;
|
|
46
|
+
|
|
47
|
+
serviceWorker.register(config.basePath + folder + fileName, opts)
|
|
48
|
+
.then(registration => {
|
|
49
|
+
me.registration = registration;
|
|
50
|
+
|
|
51
|
+
serviceWorker.ready.then(() => {
|
|
52
|
+
serviceWorker.onmessage = WorkerManager.onWorkerMessage.bind(WorkerManager);
|
|
53
|
+
|
|
54
|
+
WorkerManager.sendMessage('service', {
|
|
55
|
+
action : 'registerNeoConfig',
|
|
56
|
+
channelPort: registration.active,
|
|
57
|
+
data : config
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
})
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
Neo.applyClassConfig(ServiceWorker);
|
|
66
|
+
|
|
67
|
+
let instance = Neo.create(ServiceWorker);
|
|
68
|
+
|
|
69
|
+
Neo.applyToGlobalNs(instance);
|
|
70
|
+
|
|
71
|
+
export default instance;
|
package/src/tab/Container.mjs
CHANGED
|
@@ -139,9 +139,9 @@ class Container extends BaseContainer {
|
|
|
139
139
|
me.updateTabButtons();
|
|
140
140
|
|
|
141
141
|
me.fire('activeIndexChange', {
|
|
142
|
-
item
|
|
143
|
-
oldValue
|
|
144
|
-
value
|
|
142
|
+
item: me.getActiveCard(),
|
|
143
|
+
oldValue,
|
|
144
|
+
value
|
|
145
145
|
});
|
|
146
146
|
}
|
|
147
147
|
}
|
|
@@ -245,7 +245,7 @@ class Container extends BaseContainer {
|
|
|
245
245
|
tabButtons.push(me.getTabButtonConfig(item.tabButtonConfig, index));
|
|
246
246
|
|
|
247
247
|
if (!(item instanceof Neo.component.Base)) {
|
|
248
|
-
item = {...me.itemDefaults,
|
|
248
|
+
item = {flex: 1, ...me.itemDefaults, isTab: true, ...item};
|
|
249
249
|
}
|
|
250
250
|
|
|
251
251
|
tabComponents.push(item);
|
package/src/worker/App.mjs
CHANGED
|
@@ -72,7 +72,7 @@ class App extends Base {
|
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
/**
|
|
75
|
-
* @param {
|
|
75
|
+
* @param {Object} data
|
|
76
76
|
*/
|
|
77
77
|
createThemeMap(data) {
|
|
78
78
|
Neo.ns('Neo.cssMap.fileInfo', true);
|
|
@@ -265,7 +265,7 @@ class App extends Base {
|
|
|
265
265
|
this.onRegisterApp({ appName });
|
|
266
266
|
|
|
267
267
|
this.sendMessage('main', {
|
|
268
|
-
action:'registerAppName',
|
|
268
|
+
action: 'registerAppName',
|
|
269
269
|
appName
|
|
270
270
|
});
|
|
271
271
|
}
|
package/src/worker/Manager.mjs
CHANGED
|
@@ -218,6 +218,10 @@ class Manager extends Base {
|
|
|
218
218
|
* @returns {Worker}
|
|
219
219
|
*/
|
|
220
220
|
getWorker(name) {
|
|
221
|
+
if (name === 'service') {
|
|
222
|
+
return navigator.serviceWorker?.controller;
|
|
223
|
+
}
|
|
224
|
+
|
|
221
225
|
return name instanceof Worker ? name : this.workers[name].worker;
|
|
222
226
|
}
|
|
223
227
|
|
|
@@ -386,7 +390,12 @@ class Manager extends Base {
|
|
|
386
390
|
message, worker;
|
|
387
391
|
|
|
388
392
|
if (!me.stopCommunication) {
|
|
389
|
-
|
|
393
|
+
if (opts.channelPort) {
|
|
394
|
+
worker = opts.channelPort;
|
|
395
|
+
delete opts.channelPort;
|
|
396
|
+
} else {
|
|
397
|
+
worker = me.getWorker(dest);
|
|
398
|
+
}
|
|
390
399
|
|
|
391
400
|
if (!worker) {
|
|
392
401
|
throw new Error('Called sendMessage for a worker that does not exist: ' + dest);
|
|
@@ -396,7 +405,7 @@ class Manager extends Base {
|
|
|
396
405
|
|
|
397
406
|
message = new Message(opts);
|
|
398
407
|
|
|
399
|
-
(
|
|
408
|
+
(worker.port ? worker.port : worker).postMessage(message, transfer);
|
|
400
409
|
return message;
|
|
401
410
|
}
|
|
402
411
|
}
|
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
import Base from '../core/Base.mjs';
|
|
2
|
+
import Message from './Message.mjs';
|
|
3
|
+
import RemoteMethodAccess from './mixin/RemoteMethodAccess.mjs';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @class Neo.worker.ServiceBase
|
|
7
|
+
* @extends Neo.core.Base
|
|
8
|
+
* @abstract
|
|
9
|
+
*/
|
|
10
|
+
class ServiceBase extends Base {
|
|
11
|
+
/**
|
|
12
|
+
* @member {Object[]|null} channelPorts=null
|
|
13
|
+
* @protected
|
|
14
|
+
*/
|
|
15
|
+
channelPorts = null
|
|
16
|
+
/**
|
|
17
|
+
* @member {Client|null} lastClient=null
|
|
18
|
+
* @protected
|
|
19
|
+
*/
|
|
20
|
+
lastClient = null
|
|
21
|
+
/**
|
|
22
|
+
* @member {Object[]} promises=[]
|
|
23
|
+
* @protected
|
|
24
|
+
*/
|
|
25
|
+
promises = []
|
|
26
|
+
/**
|
|
27
|
+
* @member {String[]} remotes=[]
|
|
28
|
+
* @protected
|
|
29
|
+
*/
|
|
30
|
+
remotes = []
|
|
31
|
+
|
|
32
|
+
static getConfig() {return {
|
|
33
|
+
/**
|
|
34
|
+
* @member {String} className='Neo.worker.ServiceBase'
|
|
35
|
+
* @protected
|
|
36
|
+
*/
|
|
37
|
+
className: 'Neo.worker.ServiceBase',
|
|
38
|
+
/**
|
|
39
|
+
* @member {String} cacheName='neo-runtime'
|
|
40
|
+
*/
|
|
41
|
+
cacheName: 'neo-runtime',
|
|
42
|
+
/**
|
|
43
|
+
* @member {String[]|null} cachePaths
|
|
44
|
+
*/
|
|
45
|
+
cachePaths: [
|
|
46
|
+
'raw.githubusercontent.com/',
|
|
47
|
+
'/dist/production/',
|
|
48
|
+
'/fontawesome',
|
|
49
|
+
'/resources/'
|
|
50
|
+
],
|
|
51
|
+
/**
|
|
52
|
+
* @member {String[]|Neo.core.Base[]|null} mixins=[RemoteMethodAccess]
|
|
53
|
+
*/
|
|
54
|
+
mixins: [RemoteMethodAccess],
|
|
55
|
+
/**
|
|
56
|
+
* Remote method access for other workers
|
|
57
|
+
* @member {Object} remote={app: [//...]}
|
|
58
|
+
* @protected
|
|
59
|
+
*/
|
|
60
|
+
remote: {
|
|
61
|
+
app: [
|
|
62
|
+
'clearCache',
|
|
63
|
+
'clearCaches',
|
|
64
|
+
'preloadAssets'
|
|
65
|
+
]
|
|
66
|
+
},
|
|
67
|
+
/**
|
|
68
|
+
* @member {String|null} workerId=null
|
|
69
|
+
* @protected
|
|
70
|
+
*/
|
|
71
|
+
workerId: null
|
|
72
|
+
}}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* @param {Object} config
|
|
76
|
+
*/
|
|
77
|
+
construct(config) {
|
|
78
|
+
super.construct(config);
|
|
79
|
+
|
|
80
|
+
let me = this,
|
|
81
|
+
bind = name => me[name].bind(me);
|
|
82
|
+
|
|
83
|
+
me.channelPorts = [];
|
|
84
|
+
|
|
85
|
+
Object.assign(globalThis, {
|
|
86
|
+
onactivate: bind('onActivate'),
|
|
87
|
+
onfetch : bind('onFetch'),
|
|
88
|
+
oninstall : bind('onInstall'),
|
|
89
|
+
onmessage : bind('onMessage')
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
Neo.currentWorker = me;
|
|
93
|
+
Neo.workerId = me.workerId;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* @param {String} name=this.cacheName
|
|
98
|
+
*/
|
|
99
|
+
clearCache(name=this.cacheName) {
|
|
100
|
+
caches.keys()
|
|
101
|
+
.then(cacheNames => cacheNames.filter(cacheName => cacheName === name))
|
|
102
|
+
.then(cachesToDelete => Promise.all(cachesToDelete.map(cacheToDelete => caches.delete(cacheToDelete))))
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
*
|
|
107
|
+
*/
|
|
108
|
+
clearCaches() {
|
|
109
|
+
caches.keys()
|
|
110
|
+
.then(cachesToDelete => Promise.all(cachesToDelete.map(cacheToDelete => caches.delete(cacheToDelete))))
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* @param {Client} client
|
|
115
|
+
*/
|
|
116
|
+
createMessageChannel(client) {
|
|
117
|
+
let me = this,
|
|
118
|
+
channel = new MessageChannel(),
|
|
119
|
+
port = channel.port2;
|
|
120
|
+
|
|
121
|
+
channel.port1.onmessage = me.onMessage.bind(me);
|
|
122
|
+
|
|
123
|
+
me.sendMessage('app', {action: 'registerPort', transfer: port}, [port]);
|
|
124
|
+
|
|
125
|
+
me.channelPorts.push({
|
|
126
|
+
clientId : client.id,
|
|
127
|
+
destination: 'app',
|
|
128
|
+
port : channel.port1
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
*
|
|
134
|
+
* @param {String} destination
|
|
135
|
+
* @param {String} clientId=this.lastClient.id
|
|
136
|
+
* @returns {MessagePort|null}
|
|
137
|
+
*/
|
|
138
|
+
getPort(destination, clientId=this.lastClient?.id) {
|
|
139
|
+
for (let port of this.channelPorts) {
|
|
140
|
+
if (clientId === port.clientId && destination === port.destination) {
|
|
141
|
+
return port.port;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Ignore the call in case there is no connected client in place yet
|
|
150
|
+
*/
|
|
151
|
+
initRemote() {
|
|
152
|
+
let me = this,
|
|
153
|
+
lastClientId = me.lastClient?.id;
|
|
154
|
+
|
|
155
|
+
if (lastClientId && !me.remotes.includes(lastClientId)) {
|
|
156
|
+
me.remotes.push(lastClientId);
|
|
157
|
+
super.initRemote();
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* @param {ExtendableMessageEvent} event
|
|
163
|
+
*/
|
|
164
|
+
onActivate(event) {
|
|
165
|
+
console.log('onActivate', event);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* @param {Client} source
|
|
170
|
+
*/
|
|
171
|
+
onConnect(source) {
|
|
172
|
+
console.log('onConnect', source);
|
|
173
|
+
|
|
174
|
+
this.createMessageChannel(source);
|
|
175
|
+
this.initRemote();
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* @param {ExtendableMessageEvent} event
|
|
180
|
+
*/
|
|
181
|
+
onFetch(event) {
|
|
182
|
+
let hasMatch = false,
|
|
183
|
+
request = event.request,
|
|
184
|
+
key;
|
|
185
|
+
|
|
186
|
+
for (key of this.cachePaths) {
|
|
187
|
+
if (request.url.includes(key)) {
|
|
188
|
+
hasMatch = true;
|
|
189
|
+
break;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
hasMatch && event.respondWith(
|
|
194
|
+
caches.match(request)
|
|
195
|
+
.then(cachedResponse => cachedResponse || caches.open(this.cacheName)
|
|
196
|
+
.then(cache => fetch(request)
|
|
197
|
+
.then(response => cache.put(request, response.clone())
|
|
198
|
+
.then(() => response)
|
|
199
|
+
)))
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* @param {ExtendableMessageEvent} event
|
|
205
|
+
*/
|
|
206
|
+
onInstall(event) {
|
|
207
|
+
console.log('onInstall', event);
|
|
208
|
+
globalThis.skipWaiting();
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* For a client based message we receive an ExtendableMessageEvent,
|
|
213
|
+
* for a MessageChannel based message a MessageEvent
|
|
214
|
+
* @param {ExtendableMessageEvent|MessageEvent} event
|
|
215
|
+
*/
|
|
216
|
+
onMessage(event) {
|
|
217
|
+
let me = this,
|
|
218
|
+
data = event.data,
|
|
219
|
+
action = data.action,
|
|
220
|
+
replyId = data.replyId,
|
|
221
|
+
promise;
|
|
222
|
+
|
|
223
|
+
if (event.source) { // ExtendableMessageEvent
|
|
224
|
+
me.lastClient = event.source;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (!action) {
|
|
228
|
+
throw new Error('Message action is missing: ' + data.id);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (action !== 'reply') {
|
|
232
|
+
me['on' + Neo.capitalize(action)](data, event);
|
|
233
|
+
} else if (promise = action === 'reply' && me.promises[replyId]) {
|
|
234
|
+
promise[data.reject ? 'reject' : 'resolve'](data.data);
|
|
235
|
+
delete me.promises[replyId];
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* @param {Object} msg
|
|
241
|
+
* @param {ExtendableMessageEvent} event
|
|
242
|
+
*/
|
|
243
|
+
onPing(msg, event) {
|
|
244
|
+
this.resolve(msg, {originMsg: msg});
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* @param {Object} msg
|
|
249
|
+
* @param {ExtendableMessageEvent} event
|
|
250
|
+
*/
|
|
251
|
+
onRegisterNeoConfig(msg, event) {
|
|
252
|
+
Neo.config = Neo.config || {};
|
|
253
|
+
Object.assign(Neo.config, msg.data);
|
|
254
|
+
|
|
255
|
+
this.onConnect(event.source);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* @param {Object} data
|
|
260
|
+
* @param {String} [data.cacheName=this.cacheName]
|
|
261
|
+
* @param {String[]|String} data.files
|
|
262
|
+
* @param {Boolean} [data.foreReload=false]
|
|
263
|
+
*/
|
|
264
|
+
async preloadAssets(data) {
|
|
265
|
+
let cacheName = data.cacheName || this.cacheName,
|
|
266
|
+
cache = await caches.open(cacheName),
|
|
267
|
+
files = data.files,
|
|
268
|
+
items = [],
|
|
269
|
+
asset, hasMatch, item;
|
|
270
|
+
|
|
271
|
+
if (!Array.isArray(files)) {
|
|
272
|
+
files = [files];
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
for (item of files) {
|
|
276
|
+
hasMatch = false;
|
|
277
|
+
|
|
278
|
+
if (!data.forceReload) {
|
|
279
|
+
asset = await cache.match(item);
|
|
280
|
+
hasMatch = !!asset;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
if (!hasMatch) {
|
|
284
|
+
items.push(item);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
if (items.length > 0) {
|
|
289
|
+
await cache.addAll(items);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
return {success: true};
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* @param {String} dest app, data, main or vdom (excluding the current worker)
|
|
297
|
+
* @param {Object} opts configs for Neo.worker.Message
|
|
298
|
+
* @param {Array} [transfer] An optional array of Transferable objects to transfer ownership of.
|
|
299
|
+
* If the ownership of an object is transferred, it becomes unusable (neutered) in the context it was sent from
|
|
300
|
+
* and becomes available only to the worker it was sent to.
|
|
301
|
+
* @returns {Promise<any>}
|
|
302
|
+
*/
|
|
303
|
+
promiseMessage(dest, opts, transfer) {
|
|
304
|
+
let me = this;
|
|
305
|
+
|
|
306
|
+
return new Promise(function(resolve, reject) {
|
|
307
|
+
let message = me.sendMessage(dest, opts, transfer),
|
|
308
|
+
msgId = message.id;
|
|
309
|
+
|
|
310
|
+
me.promises[msgId] = {reject, resolve};
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* @param {String} dest app, data, main or vdom (excluding the current worker)
|
|
316
|
+
* @param {Object} opts configs for Neo.worker.Message
|
|
317
|
+
* @param {Array} [transfer] An optional array of Transferable objects to transfer ownership of.
|
|
318
|
+
* If the ownership of an object is transferred, it becomes unusable (neutered) in the context it was sent from
|
|
319
|
+
* and becomes available only to the worker it was sent to.
|
|
320
|
+
* @returns {Neo.worker.Message}
|
|
321
|
+
* @protected
|
|
322
|
+
*/
|
|
323
|
+
sendMessage(dest, opts, transfer) {
|
|
324
|
+
opts.destination = dest;
|
|
325
|
+
|
|
326
|
+
let message = new Message(opts),
|
|
327
|
+
port = this.getPort(dest) || this.lastClient;
|
|
328
|
+
|
|
329
|
+
port.postMessage(message, transfer);
|
|
330
|
+
return message;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
Neo.applyClassConfig(ServiceBase);
|
|
335
|
+
|
|
336
|
+
export default ServiceBase;
|
|
@@ -56,7 +56,7 @@ class RemoteMethodAccess extends Base {
|
|
|
56
56
|
methods = remote.methods,
|
|
57
57
|
pkg = Neo.ns(className, true);
|
|
58
58
|
|
|
59
|
-
methods.forEach(
|
|
59
|
+
methods.forEach(method => {
|
|
60
60
|
if (remote.origin !== 'main' && pkg[method]) {
|
|
61
61
|
throw new Error('Duplicate remote method definition ' + className + '.' + method);
|
|
62
62
|
}
|