3dviewer-sdk 1.0.2 → 1.0.3

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.
@@ -0,0 +1,42 @@
1
+ import { ViewerMessageType } from "../contracts/messages";
2
+ function createRequestId() {
3
+ return `tree_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
4
+ }
5
+ export class ModelTreeModule {
6
+ constructor(viewer) {
7
+ this.viewer = viewer;
8
+ }
9
+ open() {
10
+ this.viewer.postToViewer(ViewerMessageType.PANEL_OPEN, {
11
+ panel: "model-tree",
12
+ format: "3d",
13
+ });
14
+ }
15
+ selectNode(nodeId) {
16
+ this.viewer.postToViewer(ViewerMessageType.TREE_SELECT_NODE, {
17
+ nodeId: String(nodeId),
18
+ });
19
+ }
20
+ getNodeIds(options) {
21
+ var _a;
22
+ const requestId = createRequestId();
23
+ const timeoutMs = Math.max(1000, (_a = options === null || options === void 0 ? void 0 : options.timeoutMs) !== null && _a !== void 0 ? _a : 10000);
24
+ return new Promise((resolve, reject) => {
25
+ const timer = setTimeout(() => {
26
+ off();
27
+ reject(new Error("Timeout while getting node ids from viewer"));
28
+ }, timeoutMs);
29
+ const off = this.viewer._on("modelTree:node-ids", (payload) => {
30
+ if (payload.requestId !== requestId)
31
+ return;
32
+ clearTimeout(timer);
33
+ off();
34
+ resolve(payload.nodeIds);
35
+ });
36
+ this.viewer.postToViewer(ViewerMessageType.TREE_GET_NODE_IDS, {
37
+ requestId,
38
+ onlyRealNodes: (options === null || options === void 0 ? void 0 : options.onlyRealNodes) !== false,
39
+ });
40
+ });
41
+ }
42
+ }
@@ -0,0 +1,11 @@
1
+ import { Viewer3D } from "../viewer";
2
+ export declare class NodeModule {
3
+ private viewer;
4
+ on: {
5
+ select: (cb: (payload: {
6
+ nodeId: string;
7
+ timestamp: number;
8
+ }) => void) => () => void;
9
+ };
10
+ constructor(viewer: Viewer3D);
11
+ }
@@ -0,0 +1,6 @@
1
+ export class NodeModule {
2
+ constructor(viewer) {
3
+ this.viewer = viewer;
4
+ this.on = { select: (cb) => this.viewer._on("node:select", cb) };
5
+ }
6
+ }
@@ -0,0 +1,83 @@
1
+ import { Viewer3D } from "../viewer";
2
+ import { type SheetListItem } from "../contracts/messages";
3
+ export declare type ToolbarFormat = "3d" | "pdf";
4
+ export declare type GetSheetsOptions = {
5
+ timeoutMs?: number;
6
+ };
7
+ export declare class ToolbarModule {
8
+ private viewer;
9
+ on: {
10
+ planMode: (cb: (payload: {
11
+ mode: "plan" | "document";
12
+ timestamp: number;
13
+ }) => void) => () => void;
14
+ documentMode: (cb: (payload: {
15
+ mode: "plan" | "document";
16
+ timestamp: number;
17
+ }) => void) => () => void;
18
+ firstPage: (cb: (payload: {
19
+ timestamp: number;
20
+ }) => void) => () => void;
21
+ previousPage: (cb: (payload: {
22
+ timestamp: number;
23
+ }) => void) => () => void;
24
+ nextPage: (cb: (payload: {
25
+ timestamp: number;
26
+ }) => void) => () => void;
27
+ lastPage: (cb: (payload: {
28
+ timestamp: number;
29
+ }) => void) => () => void;
30
+ currentPage: (cb: (payload: {
31
+ pageIndex: number;
32
+ pageNumber: number;
33
+ timestamp: number;
34
+ }) => void) => () => void;
35
+ };
36
+ constructor(viewer: Viewer3D);
37
+ setDisabled3D(operators: string[]): void;
38
+ setDisabledPdf(operators: string[]): void;
39
+ clearDisabled3D(): void;
40
+ clearDisabledPdf(): void;
41
+ disableAll3D(): void;
42
+ disableAllPdf(): void;
43
+ enableAll3D(): void;
44
+ enableAllPdf(): void;
45
+ openClippingPlanes(): void;
46
+ closeClippingPlanes(): void;
47
+ openSetting(): void;
48
+ closeSetting(): void;
49
+ openSetting3D(): void;
50
+ closeSetting3D(): void;
51
+ openSettingPdf(): void;
52
+ closeSettingPdf(): void;
53
+ openStatesObjects(): void;
54
+ closeStatesObjects(): void;
55
+ openLinkedObjects(): void;
56
+ closeLinkedObjects(): void;
57
+ openModelTree(): void;
58
+ closeModelTree(): void;
59
+ openObjectProperties(): void;
60
+ closeObjectProperties(): void;
61
+ openSheets(): void;
62
+ closeSheets(): void;
63
+ getSheets(options?: GetSheetsOptions): Promise<SheetListItem[]>;
64
+ applySheet(sheetId: string | number): void;
65
+ cuttingCloseSections(): void;
66
+ cuttingMultipleSides(): void;
67
+ cuttingToggleSelection(): void;
68
+ cuttingTogglePlanes(): void;
69
+ cuttingPlaneX(): void;
70
+ cuttingPlaneY(): void;
71
+ cuttingPlaneZ(): void;
72
+ cuttingPlaneBox(): void;
73
+ cuttingRotateBox(): void;
74
+ cuttingReversePlaneX(): void;
75
+ cuttingReversePlaneY(): void;
76
+ cuttingReversePlaneZ(): void;
77
+ private postConfig;
78
+ private postPanelOpen;
79
+ private postPanelClose;
80
+ private postCuttingAction;
81
+ private postSheetsGetList;
82
+ private postSheetsApply;
83
+ }
@@ -0,0 +1,219 @@
1
+ import { ViewerMessageType, } from "../contracts/messages";
2
+ function createRequestId() {
3
+ return `sheets_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
4
+ }
5
+ const ALL_3D_TOOLBAR_OPERATORS = [
6
+ "home",
7
+ "select",
8
+ "areaSelect",
9
+ "pan",
10
+ "zoomIn",
11
+ "zoomOut",
12
+ "zoomWindow",
13
+ "zoomFit",
14
+ "orbit",
15
+ "rotateZ",
16
+ "walkThrough",
17
+ "drawMode-shaded",
18
+ "drawMode-wireframe",
19
+ "drawMode-shaded-wire",
20
+ "drawMode-hidden-line",
21
+ "drawMode-xray",
22
+ "drawMode-ghosting",
23
+ "cutting-plane",
24
+ "clipping-commands",
25
+ "explode",
26
+ "setting",
27
+ "propertyPanel",
28
+ "model-tree",
29
+ "linkedObjects",
30
+ "statesObjects",
31
+ "synchronized",
32
+ ];
33
+ const ALL_PDF_TOOLBAR_OPERATORS = [
34
+ "home",
35
+ "select",
36
+ "pan",
37
+ "zoomIn",
38
+ "zoomOut",
39
+ "zoomWindow",
40
+ "zoomFit",
41
+ "rotateZ",
42
+ "save",
43
+ "setting",
44
+ "plan-mode",
45
+ "document-mode",
46
+ "first-page",
47
+ "previous-page",
48
+ "next-page",
49
+ "last-page",
50
+ "current-page",
51
+ ];
52
+ export class ToolbarModule {
53
+ constructor(viewer) {
54
+ this.viewer = viewer;
55
+ this.on = {
56
+ planMode: (cb) => this.viewer._on("toolbar:pdf-plan-mode", cb),
57
+ documentMode: (cb) => this.viewer._on("toolbar:pdf-document-mode", cb),
58
+ firstPage: (cb) => this.viewer._on("toolbar:pdf-first-page", cb),
59
+ previousPage: (cb) => this.viewer._on("toolbar:pdf-previous-page", cb),
60
+ nextPage: (cb) => this.viewer._on("toolbar:pdf-next-page", cb),
61
+ lastPage: (cb) => this.viewer._on("toolbar:pdf-last-page", cb),
62
+ currentPage: (cb) => this.viewer._on("toolbar:pdf-current-page", cb),
63
+ };
64
+ }
65
+ setDisabled3D(operators) {
66
+ this.postConfig({ format: "3d", mode: "set", operators });
67
+ }
68
+ setDisabledPdf(operators) {
69
+ this.postConfig({ format: "pdf", mode: "set", operators });
70
+ }
71
+ clearDisabled3D() {
72
+ this.postConfig({ format: "3d", mode: "clear" });
73
+ }
74
+ clearDisabledPdf() {
75
+ this.postConfig({ format: "pdf", mode: "clear" });
76
+ }
77
+ disableAll3D() {
78
+ this.setDisabled3D(ALL_3D_TOOLBAR_OPERATORS);
79
+ }
80
+ disableAllPdf() {
81
+ this.setDisabledPdf(ALL_PDF_TOOLBAR_OPERATORS);
82
+ }
83
+ enableAll3D() {
84
+ this.clearDisabled3D();
85
+ }
86
+ enableAllPdf() {
87
+ this.clearDisabledPdf();
88
+ }
89
+ openClippingPlanes() {
90
+ this.postPanelOpen({ panel: "clipping-commands", format: "3d" });
91
+ }
92
+ closeClippingPlanes() {
93
+ this.postPanelClose({ panel: "clipping-commands", format: "3d" });
94
+ }
95
+ openSetting() {
96
+ this.postPanelOpen({ panel: "setting" });
97
+ }
98
+ closeSetting() {
99
+ this.postPanelClose({ panel: "setting" });
100
+ }
101
+ openSetting3D() {
102
+ this.postPanelOpen({ panel: "setting", format: "3d" });
103
+ }
104
+ closeSetting3D() {
105
+ this.postPanelClose({ panel: "setting", format: "3d" });
106
+ }
107
+ openSettingPdf() {
108
+ this.postPanelOpen({ panel: "setting", format: "pdf" });
109
+ }
110
+ closeSettingPdf() {
111
+ this.postPanelClose({ panel: "setting", format: "pdf" });
112
+ }
113
+ openStatesObjects() {
114
+ this.postPanelOpen({ panel: "statesObjects", format: "3d" });
115
+ }
116
+ closeStatesObjects() {
117
+ this.postPanelClose({ panel: "statesObjects", format: "3d" });
118
+ }
119
+ openLinkedObjects() {
120
+ this.postPanelOpen({ panel: "linkedObjects", format: "3d" });
121
+ }
122
+ closeLinkedObjects() {
123
+ this.postPanelClose({ panel: "linkedObjects", format: "3d" });
124
+ }
125
+ openModelTree() {
126
+ this.postPanelOpen({ panel: "model-tree", format: "3d" });
127
+ }
128
+ closeModelTree() {
129
+ this.postPanelClose({ panel: "model-tree", format: "3d" });
130
+ }
131
+ openObjectProperties() {
132
+ this.postPanelOpen({ panel: "object-properties", format: "3d" });
133
+ }
134
+ closeObjectProperties() {
135
+ this.postPanelClose({ panel: "object-properties", format: "3d" });
136
+ }
137
+ openSheets() {
138
+ this.postPanelOpen({ panel: "sheets", format: "3d" });
139
+ }
140
+ closeSheets() {
141
+ this.postPanelClose({ panel: "sheets", format: "3d" });
142
+ }
143
+ getSheets(options) {
144
+ var _a;
145
+ const requestId = createRequestId();
146
+ const timeoutMs = Math.max(1000, (_a = options === null || options === void 0 ? void 0 : options.timeoutMs) !== null && _a !== void 0 ? _a : 10000);
147
+ return new Promise((resolve, reject) => {
148
+ const timer = setTimeout(() => {
149
+ off();
150
+ reject(new Error("Timeout while getting sheets list from viewer"));
151
+ }, timeoutMs);
152
+ const off = this.viewer._on("sheets:list", (payload) => {
153
+ if (payload.requestId !== requestId)
154
+ return;
155
+ clearTimeout(timer);
156
+ off();
157
+ resolve(payload.sheets);
158
+ });
159
+ this.postSheetsGetList({ requestId });
160
+ });
161
+ }
162
+ applySheet(sheetId) {
163
+ this.postSheetsApply({ sheetId });
164
+ }
165
+ cuttingCloseSections() {
166
+ this.postCuttingAction({ action: "close" });
167
+ }
168
+ cuttingMultipleSides() {
169
+ this.postCuttingAction({ action: "multi" });
170
+ }
171
+ cuttingToggleSelection() {
172
+ this.postCuttingAction({ action: "toggle-section" });
173
+ }
174
+ cuttingTogglePlanes() {
175
+ this.postCuttingAction({ action: "toggle-plane" });
176
+ }
177
+ cuttingPlaneX() {
178
+ this.postCuttingAction({ action: "plane-x" });
179
+ }
180
+ cuttingPlaneY() {
181
+ this.postCuttingAction({ action: "plane-y" });
182
+ }
183
+ cuttingPlaneZ() {
184
+ this.postCuttingAction({ action: "plane-z" });
185
+ }
186
+ cuttingPlaneBox() {
187
+ this.postCuttingAction({ action: "plane-box" });
188
+ }
189
+ cuttingRotateBox() {
190
+ this.postCuttingAction({ action: "rotate-box" });
191
+ }
192
+ cuttingReversePlaneX() {
193
+ this.postCuttingAction({ action: "reverse-plane-x" });
194
+ }
195
+ cuttingReversePlaneY() {
196
+ this.postCuttingAction({ action: "reverse-plane-y" });
197
+ }
198
+ cuttingReversePlaneZ() {
199
+ this.postCuttingAction({ action: "reverse-plane-z" });
200
+ }
201
+ postConfig(payload) {
202
+ this.viewer.postToViewer(ViewerMessageType.TOOLBAR_CONFIG, payload);
203
+ }
204
+ postPanelOpen(payload) {
205
+ this.viewer.postToViewer(ViewerMessageType.PANEL_OPEN, payload);
206
+ }
207
+ postPanelClose(payload) {
208
+ this.viewer.postToViewer(ViewerMessageType.PANEL_CLOSE, payload);
209
+ }
210
+ postCuttingAction(payload) {
211
+ this.viewer.postToViewer(ViewerMessageType.CUTTING_PLANE_ACTION, payload);
212
+ }
213
+ postSheetsGetList(payload) {
214
+ this.viewer.postToViewer(ViewerMessageType.SHEETS_GET_LIST, payload);
215
+ }
216
+ postSheetsApply(payload) {
217
+ this.viewer.postToViewer(ViewerMessageType.SHEETS_APPLY, payload);
218
+ }
219
+ }
@@ -0,0 +1,51 @@
1
+ import type { PreparedViewerData, SdkEventKey, SdkEventPayload } from "./contracts/events";
2
+ import { CameraModule } from "./modules/camera.module";
3
+ import { InteractionModule } from "./modules/interaction.module";
4
+ import { NodeModule } from "./modules/node.module";
5
+ import { FilesModule } from "./modules/files.module";
6
+ import { ToolbarModule } from "./modules/toolbar.module";
7
+ import { ModelTreeModule } from "./modules/model-tree.module";
8
+ import { MarkupModule } from "./modules/markup.module";
9
+ import { LanguageModule } from "./modules/language.module";
10
+ import { ViewerMessageType } from "./contracts/messages";
11
+ export declare type Viewer3DOptions = {
12
+ container: HTMLElement | string;
13
+ url?: string;
14
+ baseUrl?: string;
15
+ viewerPath?: string;
16
+ uploadPath?: string;
17
+ file?: File;
18
+ width?: string;
19
+ height?: string;
20
+ sandbox?: string;
21
+ allowedOrigin?: string;
22
+ };
23
+ export declare class Viewer3D {
24
+ private options;
25
+ private containerEl;
26
+ private iframeEl;
27
+ private initialized;
28
+ private emitter;
29
+ camera: CameraModule;
30
+ interaction: InteractionModule;
31
+ node: NodeModule;
32
+ files: FilesModule;
33
+ toolbar: ToolbarModule;
34
+ modelTree: ModelTreeModule;
35
+ markup: MarkupModule;
36
+ language: LanguageModule;
37
+ constructor(options: Viewer3DOptions);
38
+ getOptions(): Viewer3DOptions;
39
+ patchOptions(next: Partial<Viewer3DOptions>): void;
40
+ getUrl(): string | null;
41
+ init(): void;
42
+ render(file?: File): Promise<PreparedViewerData | void>;
43
+ open(url: string): void;
44
+ destroy(): void;
45
+ private ensureInit;
46
+ _on<K extends SdkEventKey>(event: K, cb: (payload: SdkEventPayload<K>) => void): () => void;
47
+ _off<K extends SdkEventKey>(event: K, cb: (payload: SdkEventPayload<K>) => void): void;
48
+ _emit<K extends SdkEventKey>(event: K, payload: SdkEventPayload<K>): void;
49
+ postToViewer<TPayload = unknown>(type: ViewerMessageType, payload?: TPayload): void;
50
+ private handleMessage;
51
+ }
package/dist/viewer.js ADDED
@@ -0,0 +1,277 @@
1
+ // sdk/src/viewer.ts
2
+ import { Emitter } from "./core/emitter";
3
+ import { CameraModule } from "./modules/camera.module";
4
+ import { InteractionModule } from "./modules/interaction.module";
5
+ import { NodeModule } from "./modules/node.module";
6
+ import { FilesModule } from "./modules/files.module";
7
+ import { ToolbarModule } from "./modules/toolbar.module";
8
+ import { ModelTreeModule } from "./modules/model-tree.module";
9
+ import { MarkupModule } from "./modules/markup.module";
10
+ import { LanguageModule } from "./modules/language.module";
11
+ import { ViewerMessageType, ViewerMessageSource, } from "./contracts/messages";
12
+ export class Viewer3D {
13
+ constructor(options) {
14
+ this.options = options;
15
+ this.containerEl = null;
16
+ this.iframeEl = null;
17
+ this.initialized = false;
18
+ this.emitter = new Emitter();
19
+ this.handleMessage = (event) => {
20
+ var _a, _b, _c, _d;
21
+ const data = event.data;
22
+ if (!data || typeof data !== "object")
23
+ return;
24
+ switch (data.type) {
25
+ case ViewerMessageType.HOME_CLICK:
26
+ this._emit("camera:home", { timestamp: Date.now() });
27
+ break;
28
+ case ViewerMessageType.NODE_SELECT:
29
+ this._emit("node:select", { nodeId: String((_b = (_a = data.payload) === null || _a === void 0 ? void 0 : _a.nodeId) !== null && _b !== void 0 ? _b : ""), timestamp: Date.now() });
30
+ break;
31
+ case ViewerMessageType.PAN_CHANGE:
32
+ this._emit("interaction:pan-change", { enabled: Boolean((_c = data.payload) === null || _c === void 0 ? void 0 : _c.enabled) });
33
+ break;
34
+ case ViewerMessageType.PDF_PLAN_MODE: {
35
+ const payload = data.payload;
36
+ this._emit("toolbar:pdf-plan-mode", {
37
+ mode: "plan",
38
+ timestamp: Number(payload === null || payload === void 0 ? void 0 : payload.timestamp) || Date.now(),
39
+ });
40
+ break;
41
+ }
42
+ case ViewerMessageType.PDF_DOCUMENT_MODE: {
43
+ const payload = data.payload;
44
+ this._emit("toolbar:pdf-document-mode", {
45
+ mode: "document",
46
+ timestamp: Number(payload === null || payload === void 0 ? void 0 : payload.timestamp) || Date.now(),
47
+ });
48
+ break;
49
+ }
50
+ case ViewerMessageType.PDF_FIRST_PAGE: {
51
+ const payload = data.payload;
52
+ this._emit("toolbar:pdf-first-page", {
53
+ timestamp: Number(payload === null || payload === void 0 ? void 0 : payload.timestamp) || Date.now(),
54
+ });
55
+ break;
56
+ }
57
+ case ViewerMessageType.PDF_PREVIOUS_PAGE: {
58
+ const payload = data.payload;
59
+ this._emit("toolbar:pdf-previous-page", {
60
+ timestamp: Number(payload === null || payload === void 0 ? void 0 : payload.timestamp) || Date.now(),
61
+ });
62
+ break;
63
+ }
64
+ case ViewerMessageType.PDF_NEXT_PAGE: {
65
+ const payload = data.payload;
66
+ this._emit("toolbar:pdf-next-page", {
67
+ timestamp: Number(payload === null || payload === void 0 ? void 0 : payload.timestamp) || Date.now(),
68
+ });
69
+ break;
70
+ }
71
+ case ViewerMessageType.PDF_LAST_PAGE: {
72
+ const payload = data.payload;
73
+ this._emit("toolbar:pdf-last-page", {
74
+ timestamp: Number(payload === null || payload === void 0 ? void 0 : payload.timestamp) || Date.now(),
75
+ });
76
+ break;
77
+ }
78
+ case ViewerMessageType.PDF_CURRENT_PAGE: {
79
+ const payload = data.payload;
80
+ if (!payload)
81
+ break;
82
+ this._emit("toolbar:pdf-current-page", {
83
+ pageIndex: Number(payload.pageIndex) || 0,
84
+ pageNumber: Number(payload.pageNumber) || 1,
85
+ timestamp: Number(payload.timestamp) || Date.now(),
86
+ });
87
+ break;
88
+ }
89
+ case ViewerMessageType.TREE_NODE_IDS: {
90
+ const payload = data.payload;
91
+ if (!payload || !payload.requestId || !Array.isArray(payload.nodeIds))
92
+ break;
93
+ this._emit("modelTree:node-ids", {
94
+ requestId: String(payload.requestId),
95
+ nodeIds: payload.nodeIds.map(String),
96
+ timestamp: Number(payload.timestamp) || Date.now(),
97
+ });
98
+ break;
99
+ }
100
+ case ViewerMessageType.SHEETS_LIST: {
101
+ const payload = data.payload;
102
+ if (!payload || !payload.requestId || !Array.isArray(payload.sheets))
103
+ break;
104
+ this._emit("sheets:list", {
105
+ requestId: String(payload.requestId),
106
+ sheets: payload.sheets.map((sheet) => {
107
+ var _a;
108
+ return ({
109
+ id: sheet.id,
110
+ name: String((_a = sheet.name) !== null && _a !== void 0 ? _a : ""),
111
+ is3D: Boolean(sheet.is3D),
112
+ viewId: sheet.viewId ? String(sheet.viewId) : undefined,
113
+ });
114
+ }),
115
+ activeSheetId: (_d = payload.activeSheetId) !== null && _d !== void 0 ? _d : null,
116
+ timestamp: Number(payload.timestamp) || Date.now(),
117
+ });
118
+ break;
119
+ }
120
+ case ViewerMessageType.MARKUP_LIST: {
121
+ const payload = data.payload;
122
+ if (!payload || !payload.requestId || !Array.isArray(payload.markups))
123
+ break;
124
+ this._emit("markup:list", {
125
+ requestId: String(payload.requestId),
126
+ markups: payload.markups.map((markup) => {
127
+ var _a, _b;
128
+ return ({
129
+ id: String(markup.id),
130
+ viewId: String(markup.viewId),
131
+ viewName: markup.viewName ? String(markup.viewName) : undefined,
132
+ title: String((_a = markup.title) !== null && _a !== void 0 ? _a : ""),
133
+ type: String((_b = markup.type) !== null && _b !== void 0 ? _b : ""),
134
+ shapeName: markup.shapeName ? String(markup.shapeName) : undefined,
135
+ createdDate: markup.createdDate ? String(markup.createdDate) : undefined,
136
+ modifiedDate: markup.modifiedDate ? String(markup.modifiedDate) : undefined,
137
+ createdBy: markup.createdBy ? String(markup.createdBy) : undefined,
138
+ lastModifiedBy: markup.lastModifiedBy ? String(markup.lastModifiedBy) : undefined,
139
+ });
140
+ }),
141
+ timestamp: Number(payload.timestamp) || Date.now(),
142
+ });
143
+ break;
144
+ }
145
+ case ViewerMessageType.MARKUP_SAVE_RESULT: {
146
+ const payload = data.payload;
147
+ if (!payload || !payload.requestId)
148
+ break;
149
+ this._emit("markup:save", {
150
+ requestId: String(payload.requestId),
151
+ success: Boolean(payload.success),
152
+ error: payload.error ? String(payload.error) : undefined,
153
+ timestamp: Number(payload.timestamp) || Date.now(),
154
+ });
155
+ break;
156
+ }
157
+ case ViewerMessageType.MARKUP_CANCEL_RESULT: {
158
+ const payload = data.payload;
159
+ if (!payload || !payload.requestId)
160
+ break;
161
+ this._emit("markup:cancel", {
162
+ requestId: String(payload.requestId),
163
+ success: Boolean(payload.success),
164
+ error: payload.error ? String(payload.error) : undefined,
165
+ timestamp: Number(payload.timestamp) || Date.now(),
166
+ });
167
+ break;
168
+ }
169
+ default:
170
+ break;
171
+ }
172
+ };
173
+ this.camera = new CameraModule(this);
174
+ this.interaction = new InteractionModule(this);
175
+ this.node = new NodeModule(this);
176
+ this.files = new FilesModule(this);
177
+ this.toolbar = new ToolbarModule(this);
178
+ this.modelTree = new ModelTreeModule(this);
179
+ this.markup = new MarkupModule(this);
180
+ this.language = new LanguageModule(this);
181
+ }
182
+ // ===== options helpers =====
183
+ getOptions() {
184
+ return this.options;
185
+ }
186
+ patchOptions(next) {
187
+ this.options = { ...this.options, ...next };
188
+ }
189
+ getUrl() {
190
+ var _a;
191
+ return (_a = this.options.url) !== null && _a !== void 0 ? _a : null;
192
+ }
193
+ // ===== lifecycle =====
194
+ init() {
195
+ if (this.initialized)
196
+ return;
197
+ this.containerEl =
198
+ typeof this.options.container === "string"
199
+ ? document.querySelector(this.options.container)
200
+ : this.options.container;
201
+ if (!this.containerEl)
202
+ throw new Error("Container element not found");
203
+ window.addEventListener("message", this.handleMessage);
204
+ this.initialized = true;
205
+ }
206
+ async render(file) {
207
+ this.ensureInit();
208
+ if (this.iframeEl)
209
+ return;
210
+ // If URL is missing, render falls back to files pipeline.
211
+ if (!this.options.url)
212
+ return this.files.render(file);
213
+ const iframe = document.createElement("iframe");
214
+ iframe.src = this.options.url;
215
+ iframe.style.border = "none";
216
+ iframe.style.width = this.options.width || "100%";
217
+ iframe.style.height = this.options.height || "100%";
218
+ iframe.width = this.options.width || "100%";
219
+ iframe.height = this.options.height || "100%";
220
+ if (this.options.sandbox)
221
+ iframe.setAttribute("sandbox", this.options.sandbox);
222
+ this.containerEl.appendChild(iframe);
223
+ this.iframeEl = iframe;
224
+ }
225
+ open(url) {
226
+ this.ensureInit();
227
+ this.options.url = url;
228
+ if (!this.iframeEl) {
229
+ this.render();
230
+ return;
231
+ }
232
+ this.iframeEl.src = url;
233
+ }
234
+ destroy() {
235
+ // remove listener using the same function reference
236
+ window.removeEventListener("message", this.handleMessage);
237
+ // remove iframe
238
+ if (this.iframeEl && this.containerEl) {
239
+ try {
240
+ this.containerEl.removeChild(this.iframeEl);
241
+ }
242
+ catch {
243
+ // ignore
244
+ }
245
+ }
246
+ this.iframeEl = null;
247
+ this.containerEl = null;
248
+ this.initialized = false;
249
+ }
250
+ ensureInit() {
251
+ if (!this.initialized)
252
+ throw new Error("Call viewer.init() before using viewer");
253
+ }
254
+ // ===== typed internal events used by modules =====
255
+ _on(event, cb) {
256
+ return this.emitter.on(event, cb);
257
+ }
258
+ _off(event, cb) {
259
+ this.emitter.off(event, cb);
260
+ }
261
+ _emit(event, payload) {
262
+ this.emitter.emit(event, payload);
263
+ }
264
+ // ===== postMessage bridge =====
265
+ postToViewer(type, payload) {
266
+ var _a;
267
+ if (!((_a = this.iframeEl) === null || _a === void 0 ? void 0 : _a.contentWindow))
268
+ return;
269
+ const message = {
270
+ source: ViewerMessageSource.SDK,
271
+ type,
272
+ payload,
273
+ };
274
+ const targetOrigin = this.options.allowedOrigin || "*";
275
+ this.iframeEl.contentWindow.postMessage(message, targetOrigin);
276
+ }
277
+ }