neo.mjs 6.41.0 → 6.42.0

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.
@@ -20,9 +20,9 @@ class ServiceWorker extends ServiceBase {
20
20
  */
21
21
  singleton: true,
22
22
  /**
23
- * @member {String} version='6.41.0'
23
+ * @member {String} version='6.42.0'
24
24
  */
25
- version: '6.41.0'
25
+ version: '6.42.0'
26
26
  }
27
27
 
28
28
  /**
@@ -55,7 +55,7 @@ class ContentView extends Component {
55
55
  scope : me
56
56
  });
57
57
 
58
- Neo.main.addon.HighlightJS.loadLibrary({
58
+ Neo.main.addon.HighlightJS.loadFiles({
59
59
  appName : me.appName,
60
60
  highlightJsPath: '../../docs/resources/highlight/highlight.pack.js',
61
61
  themePath : '../../docs/resources/highlightjs-custom-github-theme.css'
@@ -24,7 +24,7 @@ class Component extends BaseComponent {
24
24
  {cls: ['info-block'], cn: [
25
25
  {tag: 'h2', html: 'Weekly Workshops'},
26
26
  {tag: 'p', html: [
27
- 'We are doing weekly workshops on Thursdays 17:30 CEST (11:30am EST) for 60m free of charge.</br>',
27
+ 'We are doing weekly workshops on Thursdays 18:30 CEST (12:30am EST) for 60m free of charge.</br>',
28
28
  'Ping us inside our ',
29
29
  '<a href="https://join.slack.com/t/neomjs/shared_invite/zt-6c50ueeu-3E1~M4T9xkNnb~M_prEEOA">Slack Channel</a>, ',
30
30
  'in case you would like to join us.'
@@ -42,7 +42,7 @@ class MainContainerController extends Component {
42
42
  opts.themePath = '../../docs/resources/highlightjs-custom-github-theme.css'
43
43
  }
44
44
 
45
- Neo.main.addon.HighlightJS.loadLibrary(opts)
45
+ Neo.main.addon.HighlightJS.loadFiles(opts)
46
46
  }
47
47
 
48
48
  /**
@@ -20,9 +20,9 @@ class ServiceWorker extends ServiceBase {
20
20
  */
21
21
  singleton: true,
22
22
  /**
23
- * @member {String} version='6.41.0'
23
+ * @member {String} version='6.42.0'
24
24
  */
25
- version: '6.41.0'
25
+ version: '6.42.0'
26
26
  }
27
27
 
28
28
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neo.mjs",
3
- "version": "6.41.0",
3
+ "version": "6.42.0",
4
4
  "description": "The webworkers driven UI framework",
5
5
  "type": "module",
6
6
  "repository": {
@@ -248,8 +248,8 @@ In a nutshell:
248
248
  {
249
249
  module: TabContainer,
250
250
  items : [
251
- {module: () => import('./MyChildForm1')},
252
- {module: () => import('./MyChildForm2')}
251
+ {module: () => import('./MyChildForm1.mjs')},
252
+ {module: () => import('./MyChildForm2.mjs')}
253
253
  ]
254
254
  }
255
255
  </pre>
@@ -24,7 +24,7 @@ On its own, a Browser will just use ***one*** core per tab / window.
24
24
  Meaning: your Angular or React apps look like this:
25
25
 
26
26
  <p style="overflow-x: auto;">
27
- <img alt="Current State of Apps" src="https://raw.githubusercontent.com/neomjs/pages/main/resources_pub/images/apps-today.png">
27
+ <img style="height: 279px; width: 481px;" alt="Current State of Apps" src="https://raw.githubusercontent.com/neomjs/pages/main/resources_pub/images/apps-today.png">
28
28
  </p>
29
29
 
30
30
  The more JavaScript tasks are running inside your app, the slower it will get.
@@ -82,7 +82,7 @@ To resolve this performance bottleneck, we want to get main threads as idle as p
82
82
  rendering / dynamically manipulating the DOM:
83
83
 
84
84
  <p style="overflow-x: auto;">
85
- <img alt="App Worker Concept" src="https://raw.githubusercontent.com/neomjs/pages/main/resources_pub/images/app-worker.png">
85
+ <img style="height: 481px; width: 442px;" alt="App Worker Concept" src="https://raw.githubusercontent.com/neomjs/pages/main/resources_pub/images/app-worker.png">
86
86
  </p>
87
87
 
88
88
  The worst case that could happen now is that your app worker will slow down and this core runs at 100%. However,
@@ -2,12 +2,10 @@
2
2
  box-shadow: 0 5px 10px rgba(0,0,0,.3);
3
3
  z-index : 8;
4
4
 
5
- .logo {
6
- margin-left: 1em;
7
- }
8
-
9
5
  .neo-button {
10
- margin-left: 10px;
6
+ &:not(:first-child) {
7
+ margin-left: 10px;
8
+ }
11
9
 
12
10
  &.no-text {
13
11
  margin-left: 0;
@@ -1,28 +1,61 @@
1
1
  .portal-content-box {
2
2
  border-radius : 3px;
3
3
  box-shadow : 0 3px 6px rgba(0, 0, 0, 0.3);
4
- cursor : pointer;
5
4
  color : #000;
5
+ cursor : pointer;
6
6
  min-width : 300px;
7
7
  padding : 0 1em 1em 1em;
8
+ position : relative;
8
9
  text-decoration: none;
9
10
 
10
11
  &:hover {
11
12
  background-color: var(--sem-color-surface-primary-background);
13
+
14
+ &:after {
15
+ background-color: var(--purple-200);
16
+ border-radius : 2px;
17
+ bottom : 10px;
18
+ content : attr(href) ' \2192';
19
+ font-size : 0.7em;
20
+ left : -1px;
21
+ opacity : 0.7;
22
+ padding : 3px 20px;
23
+ position : absolute;
24
+ right : -1px;
25
+ text-align : center;
26
+ }
27
+
28
+ .portal-content-box-headline {
29
+ border-bottom-color: #b7c0da;
30
+ }
12
31
  }
13
32
 
14
33
  .portal-content-box-content {
34
+ margin-top : 0.75em;
15
35
  padding-left: 1.3em;
16
- margin-top: 0.75em;
17
36
  }
18
37
 
19
38
  .portal-content-box-headline {
20
- border-bottom: 1px solid #ececec;
39
+ border-bottom : 1px solid #ececec;
40
+ letter-spacing: 1.5px !important;
41
+ margin : 1em auto 0 auto;
42
+ max-width : 85%;
21
43
  padding-bottom: 0.75em;
22
- max-width: 85%;
23
- margin: 1em auto 0 auto;
24
- text-align: center;
25
- letter-spacing: 1.5px!important;
26
- word-spacing: 1.5px;
44
+ text-align : center;
45
+ word-spacing : 1.5px;
46
+ }
47
+ }
48
+
49
+ .neo-no-mouse {
50
+ .portal-content-box {
51
+ &:after {
52
+ bottom : .5em;
53
+ color : var(--purple-200);
54
+ content : '\f08e';
55
+ font-family: var(--fa-style-family-classic);
56
+ font-weight: 600;
57
+ position : absolute;
58
+ right : .5em;
59
+ }
27
60
  }
28
61
  }
@@ -5,6 +5,10 @@
5
5
 
6
6
  .neo-button {
7
7
  margin: 0;
8
+
9
+ @media (max-width: 600px) {
10
+ padding: 0 5px;
11
+ }
8
12
  }
9
13
 
10
14
  &.neo-dock-left {
@@ -260,12 +260,12 @@ const DefaultConfig = {
260
260
  useVdomWorker: true,
261
261
  /**
262
262
  * buildScripts/injectPackageVersion.mjs will update this value
263
- * @default '6.41.0'
263
+ * @default '6.42.0'
264
264
  * @memberOf! module:Neo
265
265
  * @name config.version
266
266
  * @type String
267
267
  */
268
- version: '6.41.0'
268
+ version: '6.42.0'
269
269
  };
270
270
 
271
271
  Object.assign(DefaultConfig, {
@@ -1,5 +1,7 @@
1
1
  import BaseComponent from '../component/Base.mjs';
2
- import VDomUtil from '../util/VDom.mjs';
2
+ import VDomUtil from '../util/VDom.mjs';
3
+
4
+ let isOperaMini = null;
3
5
 
4
6
  /**
5
7
  * @class Neo.component.Video
@@ -39,7 +41,7 @@ class Video extends BaseComponent {
39
41
  autoplay: false,
40
42
  /**
41
43
  * In case the browser does not support the video source the component should show an error.
42
- * @member {Boolean} errorMsg='The browser does not support the video'
44
+ * @member {String} errorMsg='The browser does not support the video'
43
45
  */
44
46
  errorMsg: 'Your browser does not support the video tag.',
45
47
  /**
@@ -109,32 +111,35 @@ class Video extends BaseComponent {
109
111
  }
110
112
 
111
113
  /**
112
- * afterSetUrl
113
114
  * Add a source element into the video element containing the url
114
- *
115
115
  * @param {String} value
116
116
  * @param {String|null} oldValue
117
117
  */
118
- afterSetUrl(value, oldValue) {
118
+ async afterSetUrl(value, oldValue) {
119
119
  if (!value) return;
120
120
 
121
- let {vdom} = this,
122
- media = VDomUtil.getFlags(vdom, 'media')[0],
123
- ua = navigator.userAgent || navigator.vendor || window.opera,
124
- isOperaMini = /Opera Mini/i.test(ua);
121
+ let me = this,
122
+ {vdom} = me,
123
+ media = VDomUtil.getFlags(vdom, 'media')[0],
124
+ userAgent;
125
+
126
+ if (isOperaMini === null) {
127
+ userAgent = await Neo.Main.getByPath({path: 'navigator.userAgent'});
128
+ isOperaMini = userAgent.includes('Opera Mini');
129
+ }
125
130
 
126
131
  media.cn = [{
127
- tag: 'source',
128
- src: value,
129
- type: this.type
132
+ tag : 'source',
133
+ src : value,
134
+ type: me.type
130
135
  }];
131
136
 
132
137
  // Opera Mini might not support the video-source => check the user agent string
133
138
  if (isOperaMini) {
134
139
  media.cn.push({
135
- tag: 'span',
136
- html: this.errorMsg,
137
- });
140
+ tag : 'span',
141
+ html: me.errorMsg
142
+ })
138
143
  }
139
144
 
140
145
  this.update()
@@ -144,7 +149,7 @@ class Video extends BaseComponent {
144
149
  * autoplay - run the event listeners
145
150
  * Only called in constructor and sets playing => calls update already
146
151
  *
147
- * Rationale: update() sends the vdom & vnode on a workers roundtrip to get the deltas.
152
+ * Rationale: update() sends the vdom & vnode on a workers roundtrip to get the deltas.
148
153
  * While this is happening, the component locks itself for future updates until the new vnode got back (async).
149
154
  * After the delay the framework would trigger a 2nd roundtrip to get the deltas for the visible node.
150
155
  *
@@ -566,18 +566,21 @@ class DomAccess extends Base {
566
566
 
567
567
  /**
568
568
  * Include a script into the document.head
569
+ * You can add more attributes if needed. See: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script
569
570
  * @param {String} src
570
- * @param {Boolean} [async=true]
571
+ * @param {Object} opts=defer:true}
572
+ * @param {Boolean} [opts.async]
573
+ * @param {Boolean} [opts.defer]
571
574
  * @returns {Promise<unknown>}
572
575
  */
573
- loadScript(src, async=true) {
576
+ loadScript(src, opts={defer:true}) {
574
577
  let script;
575
578
 
576
579
  return new Promise((resolve, reject) => {
577
580
  script = document.createElement('script');
578
581
 
579
582
  Object.assign(script, {
580
- async,
583
+ ...opts,
581
584
  onerror: reject,
582
585
  onload : resolve,
583
586
  src
@@ -20,12 +20,6 @@ class AmCharts extends Base {
20
20
  * @protected
21
21
  */
22
22
  charts: {},
23
- /**
24
- * Stores all chart config objects which arrived before the chart lib scripts got loaded
25
- * @member {Object[]} chartsToCreate=[]
26
- * @protected
27
- */
28
- chartsToCreate: [],
29
23
  /**
30
24
  * Stores all chart data inside an object. key => chart id
31
25
  * No array since in case a chart gets loaded multiple times, we only want to apply the last data on mount.
@@ -45,7 +39,7 @@ class AmCharts extends Base {
45
39
  fallbackPath: 'https://neomjs.github.io/pages/resources_pub/amCharts/',
46
40
  /**
47
41
  * Remote method access for other workers
48
- * @member {Object} remote={app: [//...]}
42
+ * @member {Object} remote
49
43
  * @protected
50
44
  */
51
45
  remote: {
@@ -57,39 +51,21 @@ class AmCharts extends Base {
57
51
  'setProperty',
58
52
  'updateData'
59
53
  ]
60
- },
61
- /**
62
- * @member {Boolean} scriptsLoaded_=true
63
- * @protected
64
- */
65
- scriptsLoaded_: false
66
- }
67
-
68
- /**
69
- * @param {Object} config
70
- */
71
- construct(config) {
72
- super.construct(config);
73
-
74
- this.insertAmChartsScripts()
54
+ }
75
55
  }
76
56
 
77
57
  /**
78
- * Triggered after the scriptsLoaded config got changed
58
+ * Triggered after the isReady config got changed
79
59
  * @param {Boolean} value
80
60
  * @param {Boolean} oldValue
81
61
  * @protected
82
62
  */
83
- afterSetScriptsLoaded(value, oldValue) {
63
+ afterSetIsReady(value, oldValue) {
64
+ super.afterSetIsReady(value, oldValue);
65
+
84
66
  if (value) {
85
67
  let me = this;
86
68
 
87
- me.chartsToCreate.forEach(config => {
88
- me.create(config)
89
- });
90
-
91
- me.chartsToCreate = [];
92
-
93
69
  me.timeout(1000).then(() => {
94
70
  Object.entries(me.dataMap).forEach(([key, dataValue]) => {
95
71
  me.updateData(dataValue)
@@ -107,15 +83,21 @@ class AmCharts extends Base {
107
83
  * @param {Array} [data.params]
108
84
  */
109
85
  callMethod(data) {
110
- if (this.hasChart(data.id)) {
111
- let chart = this.charts[data.id],
112
- pathArray = data.path.split('.'),
113
- methodName = pathArray.pop(),
114
- scope = pathArray.length < 1 ? chart: Neo.ns(pathArray.join('.'), false, chart);
86
+ let me = this;
115
87
 
116
- scope[methodName].call(scope, ...data.params || [])
88
+ if (!me.isReady) {
89
+ return me.cacheMethodCall({fn: 'callMethod', data})
117
90
  } else {
118
- // todo
91
+ if (me.hasChart(data.id)) {
92
+ let chart = me.charts[data.id],
93
+ pathArray = data.path.split('.'),
94
+ methodName = pathArray.pop(),
95
+ scope = pathArray.length < 1 ? chart: Neo.ns(pathArray.join('.'), false, chart);
96
+
97
+ scope[methodName].call(scope, ...data.params || [])
98
+ } else {
99
+ // todo
100
+ }
119
101
  }
120
102
  }
121
103
 
@@ -149,11 +131,10 @@ class AmCharts extends Base {
149
131
  create(data) {
150
132
  let me = this;
151
133
 
152
- if (!me.scriptsLoaded) {
153
- me.chartsToCreate.push(data)
134
+ if (!me.isReady) {
135
+ return me.cacheMethodCall({fn: 'create', data})
154
136
  } else {
155
137
  // todo: check if globalThis[data.package] exists, if not load it and call create afterwards
156
-
157
138
  am4core.useTheme(am4themes_dark);
158
139
 
159
140
  me.charts[data.id] = am4core.createFromConfig(data.config, data.id, globalThis[data.package][data.type || 'XYChart']);
@@ -181,8 +162,14 @@ class AmCharts extends Base {
181
162
  * @param {String} data.id
182
163
  */
183
164
  destroy(data) {
184
- this.charts[data.id]?.dispose?.();
185
- delete this.charts[data.id]
165
+ let me = this;
166
+
167
+ if (!me.isReady) {
168
+ return me.cacheMethodCall({fn: 'destroy', data})
169
+ } else {
170
+ me.charts[data.id]?.dispose?.();
171
+ delete me.charts[data.id]
172
+ }
186
173
  }
187
174
 
188
175
  /**
@@ -197,11 +184,14 @@ class AmCharts extends Base {
197
184
  * Async approach
198
185
  * core.js has to arrive first or the other scripts will cause JS errors since they rely on it
199
186
  * => fetching the other files after core.js is loaded
187
+ * @param {Boolean} useFallback=false
200
188
  */
201
- insertAmChartsScripts(useFallback=false) {
189
+ loadFiles(useFallback=false) {
202
190
  let me = this,
203
191
  basePath = useFallback ? me.fallbackPath : me.downloadPath;
204
192
 
193
+ me.isLoading = true;
194
+
205
195
  DomAccess.loadScript(basePath + 'core.js').then(() => {
206
196
  Promise.all([
207
197
  DomAccess.loadScript(basePath + 'charts.js'),
@@ -209,11 +199,12 @@ class AmCharts extends Base {
209
199
  DomAccess.loadScript(basePath + 'themes/dark.js'),
210
200
  DomAccess.loadScript(basePath + 'geodata/worldLow.js')
211
201
  ]).then(() => {
212
- me.scriptsLoaded = true
213
- });
202
+ me.isLoading = false;
203
+ me.isReady = true
204
+ })
214
205
  }).catch(e => {
215
206
  console.log('Download from amcharts.com failed, switching to fallback', e);
216
- me.insertAmChartsScripts(true)
207
+ me.loadFiles(true)
217
208
  })
218
209
  }
219
210
 
@@ -223,13 +214,19 @@ class AmCharts extends Base {
223
214
  * @param {Object} data.properties
224
215
  */
225
216
  setProperties(data) {
226
- Object.entries(data.properties).forEach(([key, value]) => {
227
- this.setProperty({
228
- id : data.id,
229
- path : key,
230
- value
217
+ let me = this;
218
+
219
+ if (!me.isReady) {
220
+ return me.cacheMethodCall({fn: 'setProperties', data})
221
+ } else {
222
+ Object.entries(data.properties).forEach(([key, value]) => {
223
+ me.setProperty({
224
+ id : data.id,
225
+ path : key,
226
+ value
227
+ })
231
228
  })
232
- })
229
+ }
233
230
  }
234
231
 
235
232
  /**
@@ -240,15 +237,21 @@ class AmCharts extends Base {
240
237
  * @param {*} data.value
241
238
  */
242
239
  setProperty(data) {
243
- if (this.hasChart(data.id)) {
244
- let chart = this.charts[data.id],
245
- pathArray = data.path.split('.'),
246
- propertyName = pathArray.pop(),
247
- scope = Neo.ns(pathArray.join('.'), false, chart);
240
+ let me = this;
248
241
 
249
- scope[propertyName] = data.isColor ? am4core.color(data.value) : data.value
242
+ if (!me.isReady) {
243
+ return me.cacheMethodCall({fn: 'setProperty', data})
250
244
  } else {
251
- // todo
245
+ if (this.hasChart(data.id)) {
246
+ let chart = this.charts[data.id],
247
+ pathArray = data.path.split('.'),
248
+ propertyName = pathArray.pop(),
249
+ scope = Neo.ns(pathArray.join('.'), false, chart);
250
+
251
+ scope[propertyName] = data.isColor ? am4core.color(data.value) : data.value
252
+ } else {
253
+ // todo
254
+ }
252
255
  }
253
256
  }
254
257
 
@@ -261,10 +264,12 @@ class AmCharts extends Base {
261
264
  updateData(data) {
262
265
  let me = this;
263
266
 
264
- if (!me.scriptsLoaded || !me.hasChart(data.id)) {
267
+ if (!me.isReady) {
268
+ return me.cacheMethodCall({fn: 'updateData', data})
269
+ } else if (!me.hasChart(data.id)) {
265
270
  me.dataMap[data.id] = data
266
271
  } else {
267
- const chart = me.charts[data.id];
272
+ let chart = me.charts[data.id];
268
273
 
269
274
  if (data.dataPath === '') {
270
275
  chart.data = data.data
@@ -17,7 +17,94 @@ class Base extends CoreBase {
17
17
  * @member {Boolean} isMainThreadAddon=true
18
18
  * @protected
19
19
  */
20
- isMainThreadAddon: true
20
+ isMainThreadAddon: true,
21
+ /**
22
+ * Will get set to true once all addon related files got loaded (if there is a need to load)
23
+ * @member {Boolean} isReady_=false
24
+ * @protected
25
+ */
26
+ isReady_: false,
27
+ /**
28
+ * Amount in ms to delay the loading of library files, unless remote method access happens
29
+ * Change the value to false in case you don't want an automated preloading
30
+ * @member {Boolean|Number} preloadFilesDelay=5000
31
+ * @protected
32
+ */
33
+ preloadFilesDelay: 5000,
34
+ }
35
+
36
+ /**
37
+ * @member {Object[]} cache=[]
38
+ */
39
+ cache = []
40
+ /**
41
+ * Will get set to true once we start loading Monaco related files
42
+ * @member {Boolean} isLoading=false
43
+ */
44
+ isLoading = false
45
+ /**
46
+ * Internal flag to store the setTimeout() id for loading external files
47
+ * @member {Number|null} loadingTimeoutId=null
48
+ */
49
+ loadingTimeoutId = null
50
+
51
+ /**
52
+ * @param {Object} config
53
+ */
54
+ construct(config) {
55
+ super.construct(config);
56
+
57
+ let me = this;
58
+
59
+ if (me.loadFiles) {
60
+ if (me.preloadFilesDelay === 0) {
61
+ me.loadFiles()
62
+ } else if (Neo.isNumber(me.preloadFilesDelay)) {
63
+ me.loadingTimeoutId = setTimeout(() => {
64
+ me.loadFiles()
65
+ }, me.preloadFilesDelay)
66
+ }
67
+ }
68
+ }
69
+
70
+ /**
71
+ * Triggered after the isReady config got changed
72
+ * @param {Boolean} value
73
+ * @param {Boolean} oldValue
74
+ * @protected
75
+ */
76
+ afterSetIsReady(value, oldValue) {
77
+ if (value) {
78
+ let me = this,
79
+ returnValue;
80
+
81
+ me.cache.forEach(item => {
82
+ returnValue = me[item.fn](item.data);
83
+ item.resolve(returnValue)
84
+ });
85
+
86
+ me.cache = []
87
+ }
88
+ }
89
+
90
+ /**
91
+ * Internally caches call when isReady===false
92
+ * Loads the library files in case this is not already happening
93
+ * @param item
94
+ * @returns {Promise<unknown>}
95
+ */
96
+ cacheMethodCall(item) {
97
+ let me = this;
98
+
99
+ if (!me.isLoading) {
100
+ me.loadingTimeoutId && clearTimeout(me.loadingTimeoutId);
101
+ me.loadingTimeoutId = null;
102
+ me.loadFiles()
103
+ }
104
+
105
+ return new Promise((resolve, reject) => {
106
+ me.cache.push({...item, resolve})
107
+ })
21
108
  }
22
109
  }
23
110
 
@@ -23,11 +23,6 @@ class HighlightJS extends Base {
23
23
  * @protected
24
24
  */
25
25
  highlightJsLineNumbersPath: Neo.config.basePath + 'node_modules/highlightjs-line-numbers.js/dist/highlightjs-line-numbers.min.js',
26
- /**
27
- * @member {Boolean} libraryLoaded_=true
28
- * @protected
29
- */
30
- libraryLoaded_: false,
31
26
  /**
32
27
  * Remote method access for other workers
33
28
  * @member {Object} remote
@@ -36,7 +31,7 @@ class HighlightJS extends Base {
36
31
  remote: {
37
32
  app: [
38
33
  'highlightAuto',
39
- 'loadLibrary',
34
+ 'loadFiles',
40
35
  'scrollIntoView',
41
36
  'syntaxHighlight',
42
37
  'switchTheme',
@@ -51,43 +46,6 @@ class HighlightJS extends Base {
51
46
  themePath: './resources/highlightjs-custom-github-theme.css'
52
47
  }
53
48
 
54
- /**
55
- * @member {Object[]} cache=[]
56
- * @protected
57
- */
58
- cache = []
59
-
60
- /**
61
- * Triggered after the libraryLoaded config got changed
62
- * @param {Boolean} value
63
- * @param {Boolean} oldValue
64
- * @protected
65
- */
66
- afterSetLibraryLoaded(value, oldValue) {
67
- if (value) {
68
- let me = this,
69
- returnValue;
70
-
71
- me.cache.forEach(item => {
72
- returnValue = me[item.fn](item.data);
73
- item.resolve(returnValue)
74
- });
75
-
76
- me.cache = []
77
- }
78
- }
79
-
80
- /**
81
- * Internally caches call when the hljs namespace does not exist yet
82
- * @param item
83
- * @returns {Promise<unknown>}
84
- */
85
- cacheMethodCall(item) {
86
- return new Promise((resolve, reject) => {
87
- this.cache.push({...item, resolve})
88
- })
89
- }
90
-
91
49
  /**
92
50
  * See: https://highlightjs.readthedocs.io/en/latest/api.html#highlightauto
93
51
  * @param {Object} data
@@ -95,31 +53,42 @@ class HighlightJS extends Base {
95
53
  * @returns {Object} of the form {language, relevance, value, secondBest}
96
54
  */
97
55
  highlightAuto(data) {
98
- if (window.hljs) {
99
- return hljs.highlightAuto(data.html)
100
- } else {
56
+ if (!this.isReady) {
101
57
  return this.cacheMethodCall({fn: 'highlightAuto', data})
102
58
  }
59
+
60
+ return hljs.highlightAuto(data.html)
103
61
  }
104
62
 
105
63
  /**
106
64
  * @param {Object} data
107
65
  * @returns {Boolean}
108
66
  */
109
- async loadLibrary(data) {
110
- delete data.appName;
111
-
67
+ async loadFiles(data) {
112
68
  let me = this;
113
69
 
114
- me.set(data);
70
+ if (me.isLoading || me.isReady) {
71
+ return true
72
+ }
73
+
74
+ me.isLoading = true;
75
+
76
+ if (data) {
77
+ delete data.appName;
78
+ delete data.windowId;
79
+
80
+ me.set(data);
81
+ }
115
82
 
116
- await DomAccess.loadScript(me.highlightJsPath).then(() => {
117
- DomAccess.addScript({src: me.highlightJsLineNumbersPath})
118
- });
83
+ await DomAccess.loadScript(me.highlightJsPath);
119
84
 
120
- Neo.main.addon.Stylesheet.createStyleSheet(null, 'hljs-theme', me.themePath);
85
+ await Promise.all([
86
+ DomAccess.loadScript(me.highlightJsLineNumbersPath),
87
+ Neo.main.addon.Stylesheet.createStyleSheet(null, 'hljs-theme', me.themePath)
88
+ ]);
121
89
 
122
- this.libraryLoaded = true;
90
+ me.isLoading = false;
91
+ me.isReady = true;
123
92
 
124
93
  return true
125
94
  }
@@ -131,6 +100,10 @@ class HighlightJS extends Base {
131
100
  * @protected
132
101
  */
133
102
  scrollIntoView(data) {
103
+ if (!this.isReady) {
104
+ return this.cacheMethodCall({fn: 'scrollIntoView', data})
105
+ }
106
+
134
107
  let parentEl = DomAccess.getElement(data.vnodeId),
135
108
  el = parentEl.querySelector('[data-list-header="' + data.text + '"]');
136
109
 
@@ -162,28 +135,28 @@ class HighlightJS extends Base {
162
135
  * @param {String} data.vnodeId
163
136
  */
164
137
  syntaxHighlight(data) {
165
- if (window.hljs) {
166
- let node = document.getElementById(data.vnodeId);
167
-
168
- if (node) {
169
- hljs.highlightBlock(node);
170
- hljs.lineNumbersBlock(node)
171
- }
172
- } else {
138
+ if (!this.isReady) {
173
139
  return this.cacheMethodCall({fn: 'syntaxHighlight', data})
174
140
  }
141
+
142
+ let node = document.getElementById(data.vnodeId);
143
+
144
+ if (node) {
145
+ hljs.highlightBlock(node);
146
+ hljs.lineNumbersBlock(node)
147
+ }
175
148
  }
176
149
 
177
150
  /**
178
151
  * @param {Object} data
179
152
  */
180
153
  syntaxHighlightInit(data) {
181
- if (window.hljs) {
182
- let blocks = document.querySelectorAll('pre code:not(.hljs)');
183
- Array.prototype.forEach.call(blocks, hljs.highlightBlock)
184
- } else {
154
+ if (!this.isReady) {
185
155
  return this.cacheMethodCall({fn: 'syntaxHighlightInit', data})
186
156
  }
157
+
158
+ let blocks = document.querySelectorAll('pre code:not(.hljs)');
159
+ Array.prototype.forEach.call(blocks, hljs.highlightBlock)
187
160
  }
188
161
 
189
162
  /**
@@ -193,7 +166,7 @@ class HighlightJS extends Base {
193
166
  * @param {Number} data.removeLine
194
167
  */
195
168
  syntaxHighlightLine(data) {
196
- if (!window.hljs) {
169
+ if (!this.isReady) {
197
170
  return this.cacheMethodCall({fn: 'syntaxHighlightLine', data})
198
171
  }
199
172
 
@@ -39,25 +39,12 @@ class MonacoEditor extends Base {
39
39
  }
40
40
  }
41
41
 
42
- /**
43
- * Will get set to true once all Monaco related files got loaded
44
- * @member {Boolean} isReady=false
45
- */
46
- isReady = false
47
42
  /**
48
43
  * Stores component DOM ids as keys and editor instances as values
49
44
  * @member {Object} map={}
50
45
  */
51
46
  map = {}
52
47
 
53
- /**
54
- * @param {Object} config
55
- */
56
- construct(config) {
57
- super.construct(config);
58
- this.loadFiles()
59
- }
60
-
61
48
  /**
62
49
  * For a complete list of options see:
63
50
  * https://microsoft.github.io/monaco-editor/typedoc/interfaces/editor.IEditorOptions.html
@@ -68,12 +55,16 @@ class MonacoEditor extends Base {
68
55
  {id} = data,
69
56
  editor;
70
57
 
71
- delete data.appName;
72
- delete data.id;
58
+ if (!me.isReady) {
59
+ return me.cacheMethodCall({fn: 'createInstance', data})
60
+ } else {
61
+ delete data.appName;
62
+ delete data.id;
73
63
 
74
- editor = me.map[id] = monaco.editor.create(DomAccess.getElement(id), data);
64
+ editor = me.map[id] = monaco.editor.create(DomAccess.getElement(id), data);
75
65
 
76
- editor.getModel().onDidChangeContent(me.onContentChange.bind(me, id))
66
+ editor.getModel().onDidChangeContent(me.onContentChange.bind(me, id))
67
+ }
77
68
  }
78
69
 
79
70
  /**
@@ -81,8 +72,14 @@ class MonacoEditor extends Base {
81
72
  * @param {String} data.id
82
73
  */
83
74
  destroyInstance(data) {
84
- // todo: destroy the editor instance if possible
85
- delete this.map[data.id]
75
+ let me = this;
76
+
77
+ if (!me.isReady) {
78
+ return me.cacheMethodCall({fn: 'destroyInstance', data})
79
+ } else {
80
+ // todo: destroy the editor instance if possible
81
+ delete this.map[data.id]
82
+ }
86
83
  }
87
84
 
88
85
  /**
@@ -91,7 +88,13 @@ class MonacoEditor extends Base {
91
88
  * @returns {Object}
92
89
  */
93
90
  getValue(data) {
94
- return this.map[data.id].getModel().getValue()
91
+ let me = this;
92
+
93
+ if (!me.isReady) {
94
+ return me.cacheMethodCall({fn: 'getValue', data})
95
+ } else {
96
+ return me.map[data.id].getModel().getValue()
97
+ }
95
98
  }
96
99
 
97
100
  /**
@@ -100,23 +103,29 @@ class MonacoEditor extends Base {
100
103
  * @param {String} data.id
101
104
  */
102
105
  layoutEditor(data) {
103
- this.map[data.id].layout()
106
+ this.isReady && this.map[data.id].layout()
104
107
  }
105
108
 
106
109
  /**
107
110
  *
108
111
  */
109
112
  async loadFiles() {
110
- let path = this.libraryBasePath;
113
+ let me = this,
114
+ path = me.libraryBasePath;
115
+
116
+ me.isLoading = true;
111
117
 
112
118
  window.require = {paths: {vs: path}};
113
119
 
114
- await DomAccess.loadStylesheet(path + '/editor/editor.main.css', {name: 'vs/editor/editor.main'});
115
- await DomAccess.loadScript(path + '/loader.js');
116
- await DomAccess.loadScript(path + '/editor/editor.main.nls.js');
117
- await DomAccess.loadScript(path + '/editor/editor.main.js');
120
+ await Promise.all([
121
+ DomAccess.loadStylesheet(path + '/editor/editor.main.css', {name: 'vs/editor/editor.main'}),
122
+ DomAccess.loadScript(path + '/loader.js'),
123
+ DomAccess.loadScript(path + '/editor/editor.main.nls.js'),
124
+ DomAccess.loadScript(path + '/editor/editor.main.js')
125
+ ])
118
126
 
119
- this.isReady = true
127
+ me.isLoading = false;
128
+ me.isReady = true
120
129
  }
121
130
 
122
131
  /**
@@ -143,7 +152,13 @@ class MonacoEditor extends Base {
143
152
  * @param {String} data.value
144
153
  */
145
154
  setLanguage(data) {
146
- this.map[data.id].getModel().setLanguage(data.value)
155
+ let me = this;
156
+
157
+ if (!me.isReady) {
158
+ return me.cacheMethodCall({fn: 'setLanguage', data})
159
+ } else {
160
+ me.map[data.id].getModel().setLanguage(data.value)
161
+ }
147
162
  }
148
163
 
149
164
  /**
@@ -152,7 +167,13 @@ class MonacoEditor extends Base {
152
167
  * @param {String} data.value
153
168
  */
154
169
  setTheme(data) {
155
- this.map[data.id]._themeService.setTheme(data.value)
170
+ let me = this;
171
+
172
+ if (!me.isReady) {
173
+ return me.cacheMethodCall({fn: 'setTheme', data})
174
+ } else {
175
+ me.map[data.id]._themeService.setTheme(data.value)
176
+ }
156
177
  }
157
178
 
158
179
  /**
@@ -161,7 +182,13 @@ class MonacoEditor extends Base {
161
182
  * @param {String} data.value
162
183
  */
163
184
  setValue(data) {
164
- this.map[data.id].getModel().setValue(data.value)
185
+ let me = this;
186
+
187
+ if (!me.isReady) {
188
+ return me.cacheMethodCall({fn: 'setValue', data})
189
+ } else {
190
+ me.map[data.id].getModel().setValue(data.value)
191
+ }
165
192
  }
166
193
 
167
194
  /**
@@ -170,7 +197,13 @@ class MonacoEditor extends Base {
170
197
  * @param {Object} data.options
171
198
  */
172
199
  updateOptions(data) {
173
- this.map[data.id].updateOptions(data.options)
200
+ let me = this;
201
+
202
+ if (!me.isReady) {
203
+ return me.cacheMethodCall({fn: 'updateOptions', data})
204
+ } else {
205
+ me.map[data.id].updateOptions(data.options)
206
+ }
174
207
  }
175
208
  }
176
209
 
@@ -231,6 +231,9 @@ class Manager extends Base {
231
231
  NeoConfig.hasMouseEvents = matchMedia('(pointer:fine)').matches;
232
232
  NeoConfig.hasTouchEvents = ('ontouchstart' in window) || (navigator.maxTouchPoints > 0);
233
233
 
234
+ // Useful for styling
235
+ !NeoConfig.hasMouseEvents && document.body.classList.add('neo-no-mouse');
236
+
234
237
  if (window.Worker) {
235
238
  me.webWorkersEnabled = true
236
239
  } else {