/
preview.ts
129 lines (110 loc) · 3.43 KB
/
preview.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
import {EditorPreviewSettings, WorkspaceData} from './api';
import {Base} from '@blinkk/selective-edit/dist/mixins';
import {ListenersMixin} from '../mixin/listeners';
import {interpolate} from '../utility/stringLiteral';
import {shortenWorkspaceName} from './workspace';
/**
* Uses string literals to convert a preview server url into a full url.
*
* @param settings Editor preview settings.
* @param workspace Workspace to generate the url for.
* @returns Interpolated url for the base preview server.
*/
export function interpolatePreviewUrl(
settings: EditorPreviewSettings,
workspace: WorkspaceData,
params?: Record<string, string>,
path = '/'
) {
params = params ?? {
workspace: shortenWorkspaceName(workspace.name),
workspaceFull: workspace.name,
};
const baseUrl = interpolate(params, settings.baseUrl);
path = path.replace(/^\/*/, '');
return `${baseUrl}${baseUrl.endsWith('/') ? '' : '/'}${path}`;
}
/**
* Uses string literals to convert a preview server url into a full url.
*
* Specific to for loading the settings config file.
*
* @param settings Editor preview settings.
* @param workspace Workspace to generate the url for.
* @returns Interpolated url for the base preview server.
*/
export function interpolatePreviewConfigUrl(
settings: EditorPreviewSettings,
workspace: WorkspaceData
) {
const params = {
workspace: shortenWorkspaceName(workspace.name),
workspaceFull: workspace.name,
baseUrl: '',
};
params.baseUrl = interpolatePreviewUrl(settings, workspace, params);
params.baseUrl = `${params.baseUrl}${
params.baseUrl.endsWith('/') ? '' : '/'
}`;
if (settings.configUrl) {
let configUrl = settings.configUrl;
// If the config URL is a absolute path, prepend with base url.
if (configUrl.startsWith('/')) {
configUrl = `${params.baseUrl}${configUrl.slice(1)}`;
}
return interpolate(params, configUrl);
}
// Default to the `preview.json` file.
return `${params.baseUrl}preview.json`;
}
export interface PreviewEvent {
event: string;
details?: any;
}
export interface PreviewConnectEvent extends PreviewEvent {
event: 'connect';
}
export type PreviewCommunication = PreviewConnectEvent | PreviewEvent;
/**
* Enable communication between the preview iframe and the editor.
*/
export class PreviewCommunicator extends ListenersMixin(Base) {
iframe?: HTMLIFrameElement;
queue: PreviewCommunication[] = [];
constructor() {
super();
window.addEventListener('message', event => {
if (!event.data.event) {
return;
}
// Check for a connect event, link the iframe to the communicator.
if (event.data.event === 'connect') {
const iframe = document.querySelector(
'.le__part__preview__frame iframe'
);
if (iframe) {
this.connect(iframe as HTMLIFrameElement);
}
}
this.triggerListener(event.data.event, event.data.details || {});
});
}
connect(iframe: HTMLIFrameElement) {
this.iframe = iframe;
// Send connect message back to iframe as a confirmation.
this.send({event: 'connect'});
// Send queued events.
for (const event of this.queue) {
this.send(event);
}
this.queue = [];
}
send(event: PreviewCommunication) {
if (!this.iframe) {
this.queue.push(event);
return;
}
// Currently ignores when no iframe. Change to queue messages?
this.iframe?.contentWindow?.postMessage(event, '*');
}
}