@iebh/tera-fy 1.0.7 → 1.0.9
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/lib/terafy.client.js +142 -75
- package/lib/terafy.server.js +98 -8
- package/package.json +1 -1
- package/plugins/vue.js +16 -14
package/lib/terafy.client.js
CHANGED
|
@@ -15,13 +15,17 @@ export default class TeraFy {
|
|
|
15
15
|
*
|
|
16
16
|
* @type {Object}
|
|
17
17
|
* @property {Boolean} devMode Operate in devMode - i.e. force outer refresh when encountering an existing TeraFy instance
|
|
18
|
+
* @property {'detect'|'parent'|'child'} How to communicate with TERA. 'parent' assumes that the parent of the current document is TERA, 'child' spawns an iFrame and uses TERA there, 'detect' tries parent and fallsback to 'child'
|
|
19
|
+
* @property {Number} modeTimeout How long entities have in 'detect' mode to identify themselves
|
|
18
20
|
* @property {String} siteUrl The TERA URL to connect to
|
|
19
21
|
* @property {String} restrictOrigin URL to restrict communications to
|
|
20
22
|
*/
|
|
21
23
|
settings = {
|
|
22
24
|
devMode: true,
|
|
25
|
+
mode: 'detect',
|
|
26
|
+
modeTimeout: 300,
|
|
23
27
|
siteUrl: 'https://tera-tools.com/embed',
|
|
24
|
-
restrictOrigin: '*', //
|
|
28
|
+
restrictOrigin: '*', // FIXME: Need to restrict this to TERA site
|
|
25
29
|
};
|
|
26
30
|
|
|
27
31
|
|
|
@@ -60,6 +64,9 @@ export default class TeraFy {
|
|
|
60
64
|
'getProjectState', 'applyProjectStatePatch',
|
|
61
65
|
// bindProjectState() - See below
|
|
62
66
|
|
|
67
|
+
// Project files
|
|
68
|
+
'getProjectFiles',
|
|
69
|
+
|
|
63
70
|
// Project Libraries
|
|
64
71
|
'getProjectLibrary', 'setProjectLibrary',
|
|
65
72
|
];
|
|
@@ -112,7 +119,16 @@ export default class TeraFy {
|
|
|
112
119
|
id: message.id || nanoid(),
|
|
113
120
|
...cloneDeep(message), // Need to clone to resolve promise nasties
|
|
114
121
|
};
|
|
115
|
-
|
|
122
|
+
|
|
123
|
+
if (this.settings.mode == 'parent') {
|
|
124
|
+
window.parent.postMessage(payload, this.settings.restrictOrigin);
|
|
125
|
+
} else if (this.settings.mode == 'child') {
|
|
126
|
+
this.dom.iframe.contentWindow.postMessage(payload, this.settings.restrictOrigin);
|
|
127
|
+
} else if (this.settings.mode == 'detect') {
|
|
128
|
+
throw new Error('Call init() or detectMode() before trying to send data to determine the mode');
|
|
129
|
+
} else {
|
|
130
|
+
throw new Error(`Unknown TERA communication mode "${this.settings.mode}"`);
|
|
131
|
+
}
|
|
116
132
|
} catch (e) {
|
|
117
133
|
this.debug('ERROR', 'Message compose client->server:', e);
|
|
118
134
|
this.debug('ERROR', 'Attempted to dispatch payload client->server', payload);
|
|
@@ -144,6 +160,8 @@ export default class TeraFy {
|
|
|
144
160
|
* @param {MessageEvent} Raw message event to process
|
|
145
161
|
*/
|
|
146
162
|
acceptMessage(rawMessage) {
|
|
163
|
+
if (rawMessage.origin == window.location.origin) return; // Message came from us
|
|
164
|
+
|
|
147
165
|
let message = rawMessage.data;
|
|
148
166
|
if (!message.TERA || !message.id) return; // Ignore non-TERA signed messages
|
|
149
167
|
this.debug('Recieved', message);
|
|
@@ -200,23 +218,56 @@ export default class TeraFy {
|
|
|
200
218
|
|
|
201
219
|
/**
|
|
202
220
|
* Initalize the TERA client singleton
|
|
221
|
+
* This function can only be called once and will return the existing init() worker Promise if its called againt
|
|
203
222
|
*
|
|
204
223
|
* @returns {Promise<TeraFy>} An eventual promise which will resovle with this terafy instance
|
|
205
224
|
*/
|
|
206
225
|
init() {
|
|
226
|
+
if (this.init.promise) return this.init.promise; // Aleady been called - return init promise
|
|
227
|
+
|
|
207
228
|
window.addEventListener('message', this.acceptMessage.bind(this));
|
|
208
229
|
|
|
209
|
-
return Promise.
|
|
210
|
-
|
|
211
|
-
this.
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
230
|
+
return this.init.promise = Promise.resolve()
|
|
231
|
+
.then(()=> this.detectMode())
|
|
232
|
+
.then(mode => this.settings.mode = mode)
|
|
233
|
+
.then(()=> Promise.all([
|
|
234
|
+
// Init core functions async
|
|
235
|
+
this.injectComms(),
|
|
236
|
+
this.injectStylesheet(),
|
|
237
|
+
this.injectMethods(),
|
|
238
|
+
|
|
239
|
+
// Init all plugins
|
|
240
|
+
...this.plugins
|
|
241
|
+
.map(plugin => plugin.init()),
|
|
242
|
+
]))
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Populate `settings.mode`
|
|
249
|
+
* Try to communicate with a parent frame, if none assume we need to fallback to child mode
|
|
250
|
+
*
|
|
251
|
+
* @returns {Promise<String>} A promise which will resolve with the detected mode to use
|
|
252
|
+
*/
|
|
253
|
+
detectMode() {
|
|
254
|
+
if (this.settings.mode != 'detect') { // Dev has specified a forced mode to use
|
|
255
|
+
return this.settings.mode;
|
|
256
|
+
} else if (window.self === window.parent) { // This frame is already at the top
|
|
257
|
+
return 'child';
|
|
258
|
+
} else { // No idea - try messaging
|
|
259
|
+
return Promise.resolve()
|
|
260
|
+
.then(()=> this.settings.mode = 'parent') // Switch to parent mode...
|
|
261
|
+
.then(()=> new Promise((resolve, reject) => { // And try messaging with a timeout
|
|
262
|
+
let timeoutHandle = setTimeout(()=> reject(), this.settings.modeTimeout);
|
|
263
|
+
|
|
264
|
+
this.rpc('handshake')
|
|
265
|
+
.then(()=> clearTimeout(timeoutHandle))
|
|
266
|
+
.then(()=> resolve())
|
|
267
|
+
}))
|
|
268
|
+
.then(()=> 'parent')
|
|
269
|
+
.catch(()=> 'child')
|
|
270
|
+
}
|
|
220
271
|
}
|
|
221
272
|
|
|
222
273
|
|
|
@@ -226,23 +277,32 @@ export default class TeraFy {
|
|
|
226
277
|
* @returns {Promise} A promise which will resolve when the loading has completed and we have found a parent TERA instance or initiallized a child
|
|
227
278
|
*/
|
|
228
279
|
injectComms() { return new Promise(resolve => {
|
|
229
|
-
this.
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
280
|
+
switch (this.settings.mode) {
|
|
281
|
+
case 'child':
|
|
282
|
+
this.dom.el = document.createElement('div')
|
|
283
|
+
this.dom.el.id = 'tera-fy';
|
|
284
|
+
this.dom.el.classList.toggle('dev-mode', this.settings.devMode);
|
|
285
|
+
document.body.append(this.dom.el);
|
|
286
|
+
|
|
287
|
+
this.dom.iframe = document.createElement('iframe')
|
|
288
|
+
|
|
289
|
+
// Queue up event chain when document loads
|
|
290
|
+
this.dom.iframe.setAttribute('sandbox', 'allow-downloads allow-scripts allow-same-origin');
|
|
291
|
+
this.dom.iframe.addEventListener('load', ()=> {
|
|
292
|
+
this.debug('Embed frame ready');
|
|
293
|
+
resolve();
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
// Start document load sequence + append to DOM
|
|
297
|
+
this.dom.iframe.src = this.settings.siteUrl;
|
|
298
|
+
this.dom.el.append(this.dom.iframe);
|
|
299
|
+
break;
|
|
300
|
+
case 'parent':
|
|
301
|
+
resolve();
|
|
302
|
+
break;
|
|
303
|
+
default:
|
|
304
|
+
throw new Error(`Unsupported mode "${this.settings.mode}" when calling injectComms()`);
|
|
305
|
+
}
|
|
246
306
|
})}
|
|
247
307
|
|
|
248
308
|
|
|
@@ -250,50 +310,58 @@ export default class TeraFy {
|
|
|
250
310
|
* Inject a local stylesheet to handle TERA server functionality
|
|
251
311
|
*/
|
|
252
312
|
injectStylesheet() {
|
|
253
|
-
this.
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
313
|
+
switch (this.settings.mode) {
|
|
314
|
+
case 'child':
|
|
315
|
+
this.dom.stylesheet = document.createElement('style');
|
|
316
|
+
this.dom.stylesheet.innerHTML = [
|
|
317
|
+
':root {',
|
|
318
|
+
'--TERA-accent: #4d659c;',
|
|
319
|
+
'}',
|
|
320
|
+
|
|
321
|
+
'#tera-fy {',
|
|
322
|
+
'display: none;',
|
|
323
|
+
'position: fixed;',
|
|
324
|
+
'right: 50px;',
|
|
325
|
+
'bottom: 50px;',
|
|
326
|
+
'width: 300px;',
|
|
327
|
+
'height: 150px;',
|
|
328
|
+
'background: transparent;',
|
|
329
|
+
|
|
330
|
+
'&.dev-mode {',
|
|
331
|
+
'display: flex;',
|
|
332
|
+
'border: 5px solid var(--TERA-accent);',
|
|
333
|
+
'background: #FFF;',
|
|
334
|
+
'}',
|
|
335
|
+
|
|
336
|
+
'& > iframe {',
|
|
337
|
+
'width: 100%;',
|
|
338
|
+
'height: 100%;',
|
|
339
|
+
'}',
|
|
340
|
+
'}',
|
|
341
|
+
|
|
342
|
+
// Fullscreen functionality {{{
|
|
343
|
+
'body.tera-fy-focus {',
|
|
344
|
+
'overflow: hidden;',
|
|
345
|
+
|
|
346
|
+
'& #tera-fy {',
|
|
347
|
+
'display: flex !important;',
|
|
348
|
+
'position: fixed !important;',
|
|
349
|
+
'top: 0px !important;',
|
|
350
|
+
'width: 100vw !important;',
|
|
351
|
+
'height: 100vh !important;',
|
|
352
|
+
'left: 0px !important;',
|
|
353
|
+
'z-index: 10000 !important;',
|
|
354
|
+
'}',
|
|
355
|
+
'}',
|
|
356
|
+
// }}}
|
|
357
|
+
].join('\n');
|
|
358
|
+
document.head.appendChild(this.dom.stylesheet);
|
|
359
|
+
break;
|
|
360
|
+
case 'parent':
|
|
361
|
+
break;
|
|
362
|
+
default:
|
|
363
|
+
throw new Error(`Unsupported mode "${this.settings.mode}" when injectStylesheet()`);
|
|
364
|
+
}
|
|
297
365
|
}
|
|
298
366
|
|
|
299
367
|
|
|
@@ -386,7 +454,6 @@ export default class TeraFy {
|
|
|
386
454
|
Object.getOwnPropertyNames(Object.getPrototypeOf(source))
|
|
387
455
|
.filter(prop => !['constructor', 'prototype', 'name'].includes(prop))
|
|
388
456
|
.forEach((prop) => {
|
|
389
|
-
console.log('Merge method', prop);
|
|
390
457
|
Object.defineProperty(
|
|
391
458
|
target,
|
|
392
459
|
prop,
|
package/lib/terafy.server.js
CHANGED
|
@@ -126,8 +126,9 @@ export default class TeraFyServer {
|
|
|
126
126
|
* Send raw message content to the client
|
|
127
127
|
*
|
|
128
128
|
* @param {Object} message Message object to send
|
|
129
|
+
* @param {Window} Window context to dispatch the message via if its not the same as the regular window
|
|
129
130
|
*/
|
|
130
|
-
sendRaw(message) {
|
|
131
|
+
sendRaw(message, sendVia) {
|
|
131
132
|
let payload;
|
|
132
133
|
try {
|
|
133
134
|
payload = {
|
|
@@ -135,7 +136,7 @@ export default class TeraFyServer {
|
|
|
135
136
|
...cloneDeep(message), // Need to clone to resolve promise nasties
|
|
136
137
|
};
|
|
137
138
|
this.debug('INFO', 'Parent reply', message, '<=>', payload);
|
|
138
|
-
globalThis.parent.postMessage(payload, this.settings.restrictOrigin);
|
|
139
|
+
(sendVia || globalThis.parent).postMessage(payload, this.settings.restrictOrigin);
|
|
139
140
|
} catch (e) {
|
|
140
141
|
this.debug('ERROR', 'Attempted to dispatch payload server->client', payload);
|
|
141
142
|
this.debug('ERROR', 'Message compose server->client:', e);
|
|
@@ -150,6 +151,8 @@ export default class TeraFyServer {
|
|
|
150
151
|
* @param {MessageEvent} Raw message event to process
|
|
151
152
|
*/
|
|
152
153
|
acceptMessage(rawMessage) {
|
|
154
|
+
if (rawMessage.origin == window.location.origin) return; // Message came from us
|
|
155
|
+
|
|
153
156
|
let message = rawMessage.data;
|
|
154
157
|
if (!message.TERA) return; // Ignore non-TERA signed messages
|
|
155
158
|
this.debug('Recieved', message);
|
|
@@ -174,7 +177,7 @@ export default class TeraFyServer {
|
|
|
174
177
|
id: message.id,
|
|
175
178
|
action: 'response',
|
|
176
179
|
response,
|
|
177
|
-
}))
|
|
180
|
+
}, rawMessage.source))
|
|
178
181
|
.catch(e => {
|
|
179
182
|
console.warn(`TERA-FY server threw on RPC:${message.method}:`, e);
|
|
180
183
|
this.sendRaw({
|
|
@@ -370,6 +373,7 @@ export default class TeraFyServer {
|
|
|
370
373
|
* @param {Object} [options] Additional options to mutate behaviour
|
|
371
374
|
* @param {String} [options.title="Select a project to work with"] The title of the dialog to display
|
|
372
375
|
* @param {Boolean} [options.allowCancel=true] Advertise cancelling the operation, the dialog can still be cancelled by closing it
|
|
376
|
+
* @param {Boolean} [options.setActive=false] Also set the project as active when selected
|
|
373
377
|
*
|
|
374
378
|
* @returns {Promise<Project>} The active project
|
|
375
379
|
*/
|
|
@@ -377,6 +381,7 @@ export default class TeraFyServer {
|
|
|
377
381
|
let settings = {
|
|
378
382
|
title: 'Select a project to work with',
|
|
379
383
|
allowCancel: true,
|
|
384
|
+
setActive: false,
|
|
380
385
|
...options,
|
|
381
386
|
};
|
|
382
387
|
|
|
@@ -388,6 +393,11 @@ export default class TeraFyServer {
|
|
|
388
393
|
buttons: settings.allowCancel && ['cancel'],
|
|
389
394
|
})
|
|
390
395
|
))
|
|
396
|
+
.then(project => settings.setActive
|
|
397
|
+
? this.setActiveProject(project)
|
|
398
|
+
.then(()=> project)
|
|
399
|
+
: project
|
|
400
|
+
)
|
|
391
401
|
}
|
|
392
402
|
|
|
393
403
|
|
|
@@ -424,6 +434,63 @@ export default class TeraFyServer {
|
|
|
424
434
|
this.debug('Applying sever state patch', {patch});
|
|
425
435
|
}
|
|
426
436
|
|
|
437
|
+
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* Subscribers to server project state changes
|
|
441
|
+
* @type {Array<Object>}}
|
|
442
|
+
* @property {String} id A unique ID for this subscriber state
|
|
443
|
+
* @property {String} source The human readable source for each subscriber
|
|
444
|
+
* @property {Function} sendPatch Function used to send a patch to a subscriber
|
|
445
|
+
* @property {Function} unsubscribe Function to release the client subscription
|
|
446
|
+
*/
|
|
447
|
+
projectStateSubscribers = [];
|
|
448
|
+
// }}}
|
|
449
|
+
|
|
450
|
+
// Project files - getProjectFiles() {{{
|
|
451
|
+
|
|
452
|
+
/**
|
|
453
|
+
* Data structure for a project file
|
|
454
|
+
* @class ProjectFile
|
|
455
|
+
*
|
|
456
|
+
* @property {String} id A UUID string representing the unique ID of the file
|
|
457
|
+
* @property {String} name Relative name path (can contain prefix directories) for the human readable file name
|
|
458
|
+
* @property {Object} parsedName An object representing meta file parts of a file name
|
|
459
|
+
* @property {String} parsedName.basename The filename + extention (i.e. everything without directory name)
|
|
460
|
+
* @property {String} parsedName.filename The file portion of the name (basename without the extension)
|
|
461
|
+
* @property {String} parsedName.ext The extension portion of the name (always lower case)
|
|
462
|
+
* @property {String} parsedName.dirName The directory path portion of the name
|
|
463
|
+
* @property {Date} created A date representing when the file was created
|
|
464
|
+
* @property {Date} modified A date representing when the file was created
|
|
465
|
+
* @property {Date} accessed A date representing when the file was last accessed
|
|
466
|
+
* @property {Number} size Size, in bytes, of the file
|
|
467
|
+
* @property {String} mime The associated mime type for the file
|
|
468
|
+
*/
|
|
469
|
+
|
|
470
|
+
|
|
471
|
+
/**
|
|
472
|
+
* Fetch the files associated with a given project
|
|
473
|
+
*
|
|
474
|
+
* @param {Object} options Options which mutate behaviour
|
|
475
|
+
* @param {Boolean} [options.autoRequire=true] Run `requireProject()` automatically before continuing
|
|
476
|
+
* @param {Boolean} [options.meta=true] Pull meta information for each file entity
|
|
477
|
+
*
|
|
478
|
+
* @returns {Promise<ProjectFile>} A collection of project files for the given project
|
|
479
|
+
*/
|
|
480
|
+
getProjectFiles(options) {
|
|
481
|
+
let settings = {
|
|
482
|
+
autoRequire: true,
|
|
483
|
+
meta: true,
|
|
484
|
+
...options,
|
|
485
|
+
};
|
|
486
|
+
|
|
487
|
+
return Promise.resolve()
|
|
488
|
+
.then(()=> app.service('$projects').promise())
|
|
489
|
+
.then(()=> settings.autoRequire && this.requireProject())
|
|
490
|
+
.then(project => app.service('$supabase').fileList(`/projects/${project.id}`, {
|
|
491
|
+
meta: settings.meta,
|
|
492
|
+
}))
|
|
493
|
+
}
|
|
427
494
|
// }}}
|
|
428
495
|
|
|
429
496
|
// Project Libraries - getProjectLibrary(), setProjectLibrary() {{{
|
|
@@ -431,24 +498,47 @@ export default class TeraFyServer {
|
|
|
431
498
|
/**
|
|
432
499
|
* Fetch the active projects citation library
|
|
433
500
|
*
|
|
501
|
+
* @param {String} [path] Optional file path to use, if omitted the contents of `options` are used to guess at a suitable file
|
|
434
502
|
* @param {Object} [options] Additional options to mutate behaviour
|
|
435
503
|
* @param {Boolean} [options.autoRequire=true] Run `requireProject()` automatically before continuing
|
|
436
504
|
* @param {Boolean} [options.multiple=false] Allow selection of multiple libraries
|
|
505
|
+
* @param {Function} [options.filter] Optional async file filter, called each time as `(File:ProjectFile)`
|
|
506
|
+
* @param {Function} [options.find] Optional async final stage file filter to reduce all candidates down to one subject file
|
|
437
507
|
* @param {String|Array<String>} [options.hint] Hints to identify the library to select in array order of preference. Generally corresponds to the previous stage - e.g. 'deduped', 'review1', 'review2', 'dedisputed'
|
|
438
508
|
*
|
|
439
|
-
* @returns {Promise<Array<
|
|
509
|
+
* @returns {Promise<Array<ProjectFile>>} Collection of references for the selected library matching the given hint + filter, this could be a zero length array
|
|
440
510
|
*/
|
|
441
|
-
getProjectLibrary(options) {
|
|
511
|
+
getProjectLibrary(path, options) {
|
|
442
512
|
let settings = {
|
|
513
|
+
path,
|
|
443
514
|
autoRequire: true,
|
|
444
515
|
multiple: false,
|
|
445
516
|
hint: null,
|
|
446
|
-
|
|
517
|
+
filter: file => true,
|
|
518
|
+
find: files => files.at(0),
|
|
519
|
+
...(typeof path == 'string' ? {path, ...options} : path),
|
|
447
520
|
};
|
|
448
521
|
|
|
449
522
|
return Promise.resolve()
|
|
450
|
-
.then(()=>
|
|
451
|
-
|
|
523
|
+
.then(()=> {
|
|
524
|
+
if (settings.path) { // Already have a file name picked
|
|
525
|
+
if (!settings.path.startsWith('/')) throw new Error('All file names must start with a forward slash');
|
|
526
|
+
return settings.path;
|
|
527
|
+
} else { // Try to guess the file from the options structure
|
|
528
|
+
return this.getProjectFiles({
|
|
529
|
+
autoRequire: settings.autoRequire,
|
|
530
|
+
})
|
|
531
|
+
.then(files => files.filter(file =>
|
|
532
|
+
settings.filter(file)
|
|
533
|
+
))
|
|
534
|
+
.then(files => settings.find(files))
|
|
535
|
+
.then(file => file.path)
|
|
536
|
+
}
|
|
537
|
+
})
|
|
538
|
+
.then(filePath => app.service('$supabase').fileGet(filePath, {
|
|
539
|
+
json: true,
|
|
540
|
+
toast: false,
|
|
541
|
+
}))
|
|
452
542
|
}
|
|
453
543
|
|
|
454
544
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@iebh/tera-fy",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.9",
|
|
4
4
|
"description": "TERA website worker",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"dev": "esbuild --platform=browser --format=esm --bundle lib/terafy.client.js --outfile=dist/terafy.js --minify --sourcemap --serve --servedir=.",
|
package/plugins/vue.js
CHANGED
|
@@ -111,6 +111,7 @@ export default class TeraFyPluginVue extends TeraFyPluginBase {
|
|
|
111
111
|
* Install into Vue as a generic Vue@3 plugin
|
|
112
112
|
*
|
|
113
113
|
* @param {Object} [options] Additional options to mutate behaviour
|
|
114
|
+
* @param {Boolean} [options.autoInit=true] Call Init() during the `statePromiseable` cycle if its not already been called
|
|
114
115
|
* @param {String} [options.globalName='$tera'] Globa property to allocate this service as
|
|
115
116
|
* @param {Objecct} [options.bindOptions] Options passed to `bindProjectState()`
|
|
116
117
|
*
|
|
@@ -118,9 +119,9 @@ export default class TeraFyPluginVue extends TeraFyPluginBase {
|
|
|
118
119
|
*/
|
|
119
120
|
install(app, options) {
|
|
120
121
|
let settings = {
|
|
122
|
+
autoInit: true,
|
|
121
123
|
globalName: '$tera',
|
|
122
124
|
stateOptions: {
|
|
123
|
-
autoRequire: true,
|
|
124
125
|
write: true,
|
|
125
126
|
},
|
|
126
127
|
...options,
|
|
@@ -131,19 +132,20 @@ export default class TeraFyPluginVue extends TeraFyPluginBase {
|
|
|
131
132
|
$tera.state = null;
|
|
132
133
|
|
|
133
134
|
// $tera.statePromisable becomes the promise we are waiting on to resolve
|
|
134
|
-
$tera.statePromisable = Promise.
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
135
|
+
$tera.statePromisable = Promise.resolve()
|
|
136
|
+
.then(()=> settings.autoInit && $tera.init())
|
|
137
|
+
.then(()=> Promise.all([
|
|
138
|
+
// Bind available project and wait on it
|
|
139
|
+
$tera.bindProjectState(settings.stateOptions)
|
|
140
|
+
.then(state => $tera.state = state)
|
|
141
|
+
.then(()=> $tera.debug('INFO', 'Loaded project state', $tera.state)),
|
|
142
|
+
|
|
143
|
+
// Fetch available projects
|
|
144
|
+
// TODO: It would be nice if this was responsive to remote changes
|
|
145
|
+
$tera.getProjects()
|
|
146
|
+
.then(projects => $tera.projects = reactive(projects))
|
|
147
|
+
.then(()=> $tera.debug('INFO', 'Loaded projects', $tera.projects)),
|
|
148
|
+
]))
|
|
147
149
|
|
|
148
150
|
|
|
149
151
|
// Make this module available globally
|