@dtducas/wh-forge-viewer 1.0.5 → 1.0.6
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/README.md +25 -1
- package/dist/index.d.ts +67 -77
- package/dist/index.esm.js +1655 -618
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +1655 -618
- package/dist/index.js.map +1 -1
- package/package.json +6 -2
package/dist/index.js
CHANGED
|
@@ -186,542 +186,1625 @@ function _unsupportedIterableToArray(r, a) {
|
|
|
186
186
|
}
|
|
187
187
|
}
|
|
188
188
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
189
|
+
/**
|
|
190
|
+
* Document Browser Configuration Constants
|
|
191
|
+
*
|
|
192
|
+
* @file document-browser.constants.ts
|
|
193
|
+
* @description Constants for Document Browser panel configuration and styling
|
|
194
|
+
*/
|
|
195
|
+
const DOCUMENT_BROWSER_STYLES = {
|
|
196
|
+
top: '0',
|
|
197
|
+
left: 'unset',
|
|
198
|
+
right: '0px',
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Event Names Constants
|
|
203
|
+
*
|
|
204
|
+
* @file events.constants.ts
|
|
205
|
+
* @description Constants for Autodesk Forge events and custom application events
|
|
206
|
+
*/
|
|
207
|
+
const EVENT_NAMES = {
|
|
208
|
+
// Autodesk Forge Events
|
|
209
|
+
GEOMETRY_LOADED: 'Autodesk.Viewing.GEOMETRY_LOADED_EVENT',
|
|
210
|
+
MODEL_ADDED: 'Autodesk.Viewing.MODEL_ADDED_EVENT',
|
|
211
|
+
TOOLBAR_CREATED: 'Autodesk.Viewing.TOOLBAR_CREATED_EVENT',
|
|
212
|
+
// Custom Application Events
|
|
213
|
+
PAGE_CHANGED: 'page:changed',
|
|
214
|
+
VIEWABLES_SET: 'viewables:set',
|
|
215
|
+
DOC_BROWSER_OPENED: 'docBrowser:opened',
|
|
216
|
+
DOC_BROWSER_CLOSED: 'docBrowser:closed',
|
|
217
|
+
TOOLBAR_BUTTON_CLICKED: 'toolbar:button:clicked',
|
|
218
|
+
PAN_ACTIVATED: 'pan:activated',
|
|
219
|
+
DOWNLOAD_REQUESTED: 'download:requested',
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Toolbar Configuration Constants
|
|
224
|
+
*
|
|
225
|
+
* @file toolbar.constants.ts
|
|
226
|
+
* @description Constants for custom toolbar buttons, groups, and refresh intervals
|
|
227
|
+
*/
|
|
228
|
+
const DEFAULT_HIDDEN_TOOLBAR_GROUPS = [
|
|
229
|
+
'settingsTools',
|
|
230
|
+
'modelTools',
|
|
231
|
+
'navTools',
|
|
232
|
+
];
|
|
233
|
+
const CUSTOM_TOOLBAR_BUTTONS = {
|
|
234
|
+
PAN: {
|
|
235
|
+
id: 'custom-pan-btn',
|
|
236
|
+
icon: 'adsk-icon-pan',
|
|
237
|
+
tooltip: 'Pan',
|
|
238
|
+
},
|
|
239
|
+
DOC_BROWSER: {
|
|
240
|
+
id: 'custom-doc-browser-btn',
|
|
241
|
+
icon: 'adsk-icon-documentModels',
|
|
242
|
+
tooltip: 'Document Browser',
|
|
243
|
+
},
|
|
244
|
+
DOWNLOAD: {
|
|
245
|
+
id: 'custom-download-btn',
|
|
246
|
+
icon: 'adsk-icon-custom-download',
|
|
247
|
+
tooltip: 'Download File',
|
|
248
|
+
},
|
|
249
|
+
};
|
|
250
|
+
const PAGINATION_BUTTONS = {
|
|
251
|
+
PREV: {
|
|
252
|
+
id: 'prev-page-btn',
|
|
253
|
+
icon: 'adsk-icon-custom-prev',
|
|
254
|
+
tooltip: 'Previous Page',
|
|
255
|
+
},
|
|
256
|
+
NEXT: {
|
|
257
|
+
id: 'next-page-btn',
|
|
258
|
+
icon: 'adsk-icon-custom-next',
|
|
259
|
+
tooltip: 'Next Page',
|
|
260
|
+
},
|
|
261
|
+
LABEL: {
|
|
262
|
+
id: 'total-page-label',
|
|
263
|
+
tooltip: 'Page info',
|
|
264
|
+
},
|
|
265
|
+
};
|
|
266
|
+
const TOOLBAR_REFRESH_INTERVALS = {
|
|
267
|
+
HEALING_POLL: 8,
|
|
268
|
+
BUTTON_STATE_CHECK: 10,
|
|
269
|
+
DOM_SETTLE: 50,
|
|
270
|
+
GEOMETRY_SETTLE: 80,
|
|
271
|
+
EXTENSION_INIT: 150,
|
|
272
|
+
};
|
|
273
|
+
const TOOLBAR_CONTROL_GROUP_IDS = {
|
|
274
|
+
TOOLS: 'custom-tool-group',
|
|
275
|
+
PAGINATION: 'custom-pagination-group',
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Autodesk Forge Viewer Configuration Constants
|
|
280
|
+
*
|
|
281
|
+
* @file viewer.constants.ts
|
|
282
|
+
* @description Constants for Forge Viewer initialization, CDN URLs, and file type mappings
|
|
283
|
+
*/
|
|
284
|
+
const FORGE_VIEWER_VERSION = '7.*';
|
|
285
|
+
const FORGE_VIEWER_CDN = 'https://developer.api.autodesk.com/modelderivative/v2/viewers';
|
|
286
|
+
const FORGE_STYLE_URL = `${FORGE_VIEWER_CDN}/${FORGE_VIEWER_VERSION}/style.min.css`;
|
|
287
|
+
const FORGE_SCRIPT_URL = `${FORGE_VIEWER_CDN}/${FORGE_VIEWER_VERSION}/viewer3D.min.js`;
|
|
288
|
+
const SUPPORTED_FILE_EXTENSIONS = ['pdf', 'dwf', 'dwfx'];
|
|
289
|
+
const FILE_EXTENSION_TO_VIEWER_EXTENSION = {
|
|
290
|
+
pdf: 'Autodesk.PDF',
|
|
291
|
+
dwf: 'Autodesk.DWF',
|
|
292
|
+
dwfx: 'Autodesk.DWF',
|
|
293
|
+
};
|
|
294
|
+
const GEOMETRY_SEARCH_CRITERIA = {
|
|
295
|
+
VIEW_3D: { type: 'geometry', role: '3d', progress: 'complete' },
|
|
296
|
+
VIEW_2D: { type: 'geometry', role: '2d', progress: 'complete' },
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
/******************************************************************************
|
|
300
|
+
Copyright (c) Microsoft Corporation.
|
|
301
|
+
|
|
302
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
303
|
+
purpose with or without fee is hereby granted.
|
|
304
|
+
|
|
305
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
306
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
307
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
308
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
309
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
310
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
311
|
+
PERFORMANCE OF THIS SOFTWARE.
|
|
312
|
+
***************************************************************************** */
|
|
313
|
+
/* global Reflect, Promise, SuppressedError, Symbol, Iterator */
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
function __awaiter(thisArg, _arguments, P, generator) {
|
|
317
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
318
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
319
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
320
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
321
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
322
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
327
|
+
var e = new Error(message);
|
|
328
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
329
|
+
};
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* DOM Utility Functions
|
|
333
|
+
*
|
|
334
|
+
* @file dom.utils.ts
|
|
335
|
+
* @description Utilities for safe DOM manipulation and querying
|
|
336
|
+
*/
|
|
337
|
+
/**
|
|
338
|
+
* Find element by text content
|
|
339
|
+
*/
|
|
340
|
+
function findElementByText(selector, text) {
|
|
341
|
+
var _a;
|
|
342
|
+
const elements = Array.from(document.querySelectorAll(selector));
|
|
343
|
+
return (_a = elements.find((el) => { var _a; return (_a = el.textContent) === null || _a === void 0 ? void 0 : _a.includes(text); })) !== null && _a !== void 0 ? _a : null;
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* Find thumbnail tab element using multiple selectors
|
|
347
|
+
*/
|
|
348
|
+
function findThumbnailTab() {
|
|
349
|
+
return (document.querySelector('.docking-panel-thumbnail-view') ||
|
|
350
|
+
document.querySelector('[data-i18n="Thumbnails"]') ||
|
|
351
|
+
findElementByText('.adsk-control-group .adsk-button', 'Thumbnails'));
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* Safely click an element
|
|
355
|
+
*/
|
|
356
|
+
function clickElement(element) {
|
|
357
|
+
if (element && 'click' in element) {
|
|
358
|
+
element.click();
|
|
218
359
|
}
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
this.docBrowserShouldBeOpen = true;
|
|
227
|
-
}
|
|
228
|
-
this.updatePaginationState();
|
|
229
|
-
this.viewer.addEventListener(Autodesk.Viewing.MODEL_ADDED_EVENT, function (e) {
|
|
230
|
-
_this.updateCurrentIndexFromModel();
|
|
231
|
-
});
|
|
232
|
-
};
|
|
233
|
-
ToolbarExtension.prototype.updateCurrentIndexFromModel = function () {
|
|
234
|
-
if (!this.viewer.model || this.viewables.length === 0) return;
|
|
235
|
-
var currentNode = this.viewer.model.getDocumentNode();
|
|
236
|
-
if (currentNode) {
|
|
237
|
-
var currentGuid = currentNode.data.guid || currentNode.guid;
|
|
238
|
-
var index = this.viewables.findIndex(function (v) {
|
|
239
|
-
var vGuid = v.data && v.data.guid ? v.data.guid : v.guid;
|
|
240
|
-
return vGuid === currentGuid;
|
|
241
|
-
});
|
|
242
|
-
if (index !== -1) {
|
|
243
|
-
this.currentIndex = index;
|
|
244
|
-
}
|
|
245
|
-
this.updatePaginationState();
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Apply multiple styles to an element
|
|
363
|
+
*/
|
|
364
|
+
function applyStyles(element, styles) {
|
|
365
|
+
if (element) {
|
|
366
|
+
Object.assign(element.style, styles);
|
|
246
367
|
}
|
|
247
|
-
|
|
248
|
-
ToolbarExtension.prototype.updatePaginationState = function () {
|
|
249
|
-
if (!this.paginationGroup) return;
|
|
368
|
+
}
|
|
250
369
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
370
|
+
/**
|
|
371
|
+
* File Utility Functions
|
|
372
|
+
*
|
|
373
|
+
* @file file.utils.ts
|
|
374
|
+
* @description Utilities for file handling and path operations
|
|
375
|
+
*/
|
|
376
|
+
/**
|
|
377
|
+
* Extract filename from URL or path
|
|
378
|
+
*/
|
|
379
|
+
function extractFilenameFromPath(path) {
|
|
380
|
+
try {
|
|
381
|
+
const urlPath = new URL(path).pathname;
|
|
382
|
+
const filename = urlPath.split('/').pop();
|
|
383
|
+
return decodeURIComponent(filename || 'document.pdf');
|
|
259
384
|
}
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
// Ensure container is visible
|
|
264
|
-
if (this.paginationGroup.container) {
|
|
265
|
-
this.paginationGroup.container.style.display = '';
|
|
266
|
-
}
|
|
267
|
-
var totalBtn = this.paginationGroup.getControl('total-page-label');
|
|
268
|
-
if (totalBtn) {
|
|
269
|
-
var current = this.viewables.length > 0 ? this.currentIndex + 1 : 0;
|
|
270
|
-
var total = this.viewables.length;
|
|
271
|
-
totalBtn.setToolTip("Page ".concat(current, " of ").concat(total));
|
|
272
|
-
var domElem = totalBtn.container;
|
|
273
|
-
if (domElem) {
|
|
274
|
-
domElem.innerHTML = "<div style=\"display: flex; align-items: center; justify-content: center; height: 100%; padding: 0 10px; color: white; font-size: 14px; white-space: nowrap;\">".concat(current, " / ").concat(total, "</div>");
|
|
275
|
-
}
|
|
385
|
+
catch (_a) {
|
|
386
|
+
const parts = path.split('/');
|
|
387
|
+
return parts[parts.length - 1] || 'document.pdf';
|
|
276
388
|
}
|
|
277
|
-
|
|
278
|
-
ToolbarExtension.prototype.loadCurrentViewable = function () {
|
|
279
|
-
var _this2 = this;
|
|
280
|
-
if (this.viewables.length > 0 && this.viewables[this.currentIndex]) {
|
|
281
|
-
var viewer = this.viewer;
|
|
282
|
-
var toolbar = viewer.toolbar;
|
|
283
|
-
var self = this;
|
|
389
|
+
}
|
|
284
390
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
391
|
+
class DownloadService {
|
|
392
|
+
/**
|
|
393
|
+
* Download a file from URL
|
|
394
|
+
*
|
|
395
|
+
* @param fileUrl - URL of file to download
|
|
396
|
+
*
|
|
397
|
+
* @example
|
|
398
|
+
* const downloadService = new DownloadService();
|
|
399
|
+
* await downloadService.downloadFile('https://example.com/document.pdf');
|
|
400
|
+
*/
|
|
401
|
+
downloadFile(fileUrl) {
|
|
402
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
403
|
+
try {
|
|
404
|
+
const response = yield fetch(fileUrl);
|
|
405
|
+
if (!response.ok) {
|
|
406
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
407
|
+
}
|
|
408
|
+
const blob = yield response.blob();
|
|
409
|
+
const filename = extractFilenameFromPath(fileUrl);
|
|
410
|
+
// Create download link
|
|
411
|
+
const blobUrl = URL.createObjectURL(blob);
|
|
412
|
+
const anchor = document.createElement('a');
|
|
413
|
+
anchor.href = blobUrl;
|
|
414
|
+
anchor.download = filename;
|
|
415
|
+
anchor.style.display = 'none';
|
|
416
|
+
// Trigger download
|
|
417
|
+
document.body.appendChild(anchor);
|
|
418
|
+
anchor.click();
|
|
419
|
+
document.body.removeChild(anchor);
|
|
420
|
+
// Cleanup
|
|
421
|
+
URL.revokeObjectURL(blobUrl);
|
|
422
|
+
}
|
|
423
|
+
catch (error) {
|
|
424
|
+
throw error;
|
|
425
|
+
}
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
}
|
|
288
429
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
this.
|
|
292
|
-
|
|
430
|
+
class EventBus {
|
|
431
|
+
constructor() {
|
|
432
|
+
this.listeners = new Map();
|
|
433
|
+
// Private constructor for singleton pattern
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* Get singleton instance of EventBus
|
|
437
|
+
*
|
|
438
|
+
* @example
|
|
439
|
+
* const eventBus = EventBus.getInstance();
|
|
440
|
+
* eventBus.on('myEvent', (data) => console.log(data));
|
|
441
|
+
*/
|
|
442
|
+
static getInstance() {
|
|
443
|
+
if (!EventBus.instance) {
|
|
444
|
+
EventBus.instance = new EventBus();
|
|
445
|
+
}
|
|
446
|
+
return EventBus.instance;
|
|
447
|
+
}
|
|
448
|
+
/**
|
|
449
|
+
* Subscribe to an event
|
|
450
|
+
*
|
|
451
|
+
* @param eventName - Event name to listen for
|
|
452
|
+
* @param listener - Callback function to execute when event is emitted
|
|
453
|
+
*
|
|
454
|
+
* @example
|
|
455
|
+
* eventBus.on('page:changed', (data) => {
|
|
456
|
+
* console.log('Page changed to:', data.index);
|
|
457
|
+
* });
|
|
458
|
+
*/
|
|
459
|
+
on(eventName, listener) {
|
|
460
|
+
if (!this.listeners.has(eventName)) {
|
|
461
|
+
this.listeners.set(eventName, new Set());
|
|
462
|
+
}
|
|
463
|
+
this.listeners.get(eventName).add(listener);
|
|
464
|
+
}
|
|
465
|
+
/**
|
|
466
|
+
* Unsubscribe from an event
|
|
467
|
+
*
|
|
468
|
+
* @param eventName - Event name to stop listening for
|
|
469
|
+
* @param listener - Callback function to remove
|
|
470
|
+
*
|
|
471
|
+
* @example
|
|
472
|
+
* const handler = (data) => console.log(data);
|
|
473
|
+
* eventBus.on('myEvent', handler);
|
|
474
|
+
* eventBus.off('myEvent', handler);
|
|
475
|
+
*/
|
|
476
|
+
off(eventName, listener) {
|
|
477
|
+
const listeners = this.listeners.get(eventName);
|
|
478
|
+
if (listeners) {
|
|
479
|
+
listeners.delete(listener);
|
|
480
|
+
if (listeners.size === 0) {
|
|
481
|
+
this.listeners.delete(eventName);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* Emit an event to all subscribers
|
|
487
|
+
*
|
|
488
|
+
* @param eventName - Event name to emit
|
|
489
|
+
* @param data - Data to pass to subscribers
|
|
490
|
+
*
|
|
491
|
+
* @example
|
|
492
|
+
* eventBus.emit('page:changed', { index: 5, viewable: {...} });
|
|
493
|
+
*/
|
|
494
|
+
emit(eventName, data) {
|
|
495
|
+
const listeners = this.listeners.get(eventName);
|
|
496
|
+
if (listeners) {
|
|
497
|
+
listeners.forEach((listener) => {
|
|
498
|
+
try {
|
|
499
|
+
listener(data);
|
|
500
|
+
}
|
|
501
|
+
catch (error) {
|
|
502
|
+
// Silent error handling
|
|
503
|
+
}
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
/**
|
|
508
|
+
* Clear all listeners for an event (or all events if no eventName provided)
|
|
509
|
+
*
|
|
510
|
+
* @param eventName - Optional event name to clear. If not provided, clears all events
|
|
511
|
+
*/
|
|
512
|
+
clear(eventName) {
|
|
513
|
+
if (eventName) {
|
|
514
|
+
this.listeners.delete(eventName);
|
|
515
|
+
}
|
|
516
|
+
else {
|
|
517
|
+
this.listeners.clear();
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
/**
|
|
521
|
+
* Get number of listeners for an event
|
|
522
|
+
*
|
|
523
|
+
* @param eventName - Event name to count listeners for
|
|
524
|
+
* @returns Number of listeners registered for the event
|
|
525
|
+
*/
|
|
526
|
+
listenerCount(eventName) {
|
|
527
|
+
var _a, _b;
|
|
528
|
+
return (_b = (_a = this.listeners.get(eventName)) === null || _a === void 0 ? void 0 : _a.size) !== null && _b !== void 0 ? _b : 0;
|
|
529
|
+
}
|
|
530
|
+
}
|
|
293
531
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
viewer
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
532
|
+
class FileLoader {
|
|
533
|
+
constructor(viewer) {
|
|
534
|
+
this.viewer = viewer;
|
|
535
|
+
this.eventBus = EventBus.getInstance();
|
|
536
|
+
}
|
|
537
|
+
/**
|
|
538
|
+
* Load a file into the viewer
|
|
539
|
+
*
|
|
540
|
+
* @param filePath - Path or URL to the file
|
|
541
|
+
* @param fileExt - File extension (pdf, dwf, dwfx)
|
|
542
|
+
* @param callbacks - Optional success/error callbacks
|
|
543
|
+
*
|
|
544
|
+
* @example
|
|
545
|
+
* const fileLoader = new FileLoader(viewer);
|
|
546
|
+
* await fileLoader.loadFile('document.pdf', 'pdf', {
|
|
547
|
+
* onSuccess: (e) => console.log('Loaded successfully'),
|
|
548
|
+
* onError: (err) => console.error('Load failed', err)
|
|
549
|
+
* });
|
|
550
|
+
*/
|
|
551
|
+
loadFile(filePath, fileExt, callbacks) {
|
|
552
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
553
|
+
const viewerExtension = FILE_EXTENSION_TO_VIEWER_EXTENSION[fileExt];
|
|
554
|
+
// Load appropriate viewer extension
|
|
555
|
+
yield this.viewer.loadExtension(viewerExtension);
|
|
556
|
+
// Determine loading method based on file type
|
|
557
|
+
if (fileExt === 'pdf') {
|
|
558
|
+
yield this.loadPDFFile(filePath, callbacks);
|
|
559
|
+
}
|
|
560
|
+
else {
|
|
561
|
+
yield this.loadDWFFile(filePath, callbacks);
|
|
562
|
+
}
|
|
308
563
|
});
|
|
309
|
-
var toolGroupExists = toolbar.getControl('custom-tool-group');
|
|
310
|
-
var paginationGroupExists = toolbar.getControl('custom-pagination-group');
|
|
311
|
-
if (!toolGroupExists && _this2.subToolbar) {
|
|
312
|
-
toolbar.addControl(_this2.subToolbar);
|
|
313
|
-
}
|
|
314
|
-
if (!paginationGroupExists && _this2.paginationGroup) {
|
|
315
|
-
toolbar.addControl(_this2.paginationGroup);
|
|
316
|
-
}
|
|
317
|
-
if (_this2.subToolbar) _this2.subToolbar.setVisible(true);
|
|
318
|
-
if (_this2.paginationGroup) _this2.paginationGroup.setVisible(true);
|
|
319
|
-
toolbar.setVisible(true);
|
|
320
|
-
_this2.updatePaginationState();
|
|
321
|
-
|
|
322
|
-
// Also restore immediately (may work for cached pages)
|
|
323
|
-
_this2.restoreButtonStates(_this2.docBrowserShouldBeOpen);
|
|
324
|
-
})["catch"](function (err) {
|
|
325
|
-
return console.error('Error loading viewable:', err);
|
|
326
|
-
});
|
|
327
564
|
}
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
565
|
+
/**
|
|
566
|
+
* Load PDF file directly
|
|
567
|
+
*/
|
|
568
|
+
loadPDFFile(filePath, callbacks) {
|
|
569
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
570
|
+
const handleLoadSuccess = this.createLoadSuccessHandler(callbacks === null || callbacks === void 0 ? void 0 : callbacks.onSuccess);
|
|
571
|
+
this.viewer.loadModel(filePath, {}, handleLoadSuccess);
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
/**
|
|
575
|
+
* Load DWF/DWFX file via blob
|
|
576
|
+
* Extracted from ViewerForgePDF.jsx lines 125-136
|
|
577
|
+
*/
|
|
578
|
+
loadDWFFile(filePath, callbacks) {
|
|
579
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
580
|
+
return new Promise((resolve, reject) => {
|
|
581
|
+
const xhr = new XMLHttpRequest();
|
|
582
|
+
xhr.open('GET', filePath, true);
|
|
583
|
+
xhr.responseType = 'blob';
|
|
584
|
+
xhr.onload = () => {
|
|
585
|
+
var _a;
|
|
586
|
+
if (xhr.status === 200) {
|
|
587
|
+
const blob = xhr.response;
|
|
588
|
+
const blobUrl = window.URL.createObjectURL(blob);
|
|
589
|
+
const handleLoadSuccess = this.createLoadSuccessHandler(callbacks === null || callbacks === void 0 ? void 0 : callbacks.onSuccess);
|
|
590
|
+
this.viewer.loadModel(blobUrl + '#.dwf', {}, handleLoadSuccess);
|
|
591
|
+
resolve();
|
|
592
|
+
}
|
|
593
|
+
else {
|
|
594
|
+
const error = new Error(`Failed to load DWF file: HTTP ${xhr.status}`);
|
|
595
|
+
(_a = callbacks === null || callbacks === void 0 ? void 0 : callbacks.onError) === null || _a === void 0 ? void 0 : _a.call(callbacks, error);
|
|
596
|
+
reject(error);
|
|
597
|
+
}
|
|
598
|
+
};
|
|
599
|
+
xhr.onerror = () => {
|
|
600
|
+
var _a;
|
|
601
|
+
const error = new Error('Network error loading DWF file');
|
|
602
|
+
(_a = callbacks === null || callbacks === void 0 ? void 0 : callbacks.onError) === null || _a === void 0 ? void 0 : _a.call(callbacks, error);
|
|
603
|
+
reject(error);
|
|
604
|
+
};
|
|
605
|
+
xhr.send();
|
|
606
|
+
});
|
|
607
|
+
});
|
|
608
|
+
}
|
|
609
|
+
/**
|
|
610
|
+
* Create load success handler that extracts viewables
|
|
611
|
+
* Extracted from ViewerForgePDF.jsx lines 44-56
|
|
612
|
+
*/
|
|
613
|
+
createLoadSuccessHandler(userCallback) {
|
|
614
|
+
return (event) => {
|
|
615
|
+
try {
|
|
616
|
+
// Enable reverse zoom
|
|
617
|
+
this.viewer.setReverseZoomDirection(true);
|
|
618
|
+
// Extract viewables
|
|
619
|
+
const viewables = this.extractViewables(event);
|
|
620
|
+
// Emit event with viewables
|
|
621
|
+
this.eventBus.emit(EVENT_NAMES.VIEWABLES_SET, { viewables });
|
|
622
|
+
// Show toolbar
|
|
623
|
+
if (this.viewer.toolbar) {
|
|
624
|
+
this.viewer.toolbar.setVisible(true);
|
|
625
|
+
}
|
|
626
|
+
// Call user callback if provided
|
|
627
|
+
userCallback === null || userCallback === void 0 ? void 0 : userCallback(event);
|
|
628
|
+
}
|
|
629
|
+
catch (error) {
|
|
630
|
+
// Silent error handling
|
|
631
|
+
}
|
|
632
|
+
};
|
|
633
|
+
}
|
|
634
|
+
/**
|
|
635
|
+
* Extract viewables from document
|
|
636
|
+
* Extracted from ViewerForgePDF.jsx lines 47-56
|
|
637
|
+
*/
|
|
638
|
+
extractViewables(event) {
|
|
639
|
+
const root = event.getDocumentNode().getRootNode();
|
|
640
|
+
const view3d = root.search(GEOMETRY_SEARCH_CRITERIA.VIEW_3D, true);
|
|
641
|
+
const view2d = root.search(GEOMETRY_SEARCH_CRITERIA.VIEW_2D, true);
|
|
642
|
+
return view3d.concat(view2d);
|
|
643
|
+
}
|
|
644
|
+
}
|
|
334
645
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
docBrowserExt.ui.togglePanel();
|
|
363
|
-
setTimeout(function () {
|
|
364
|
-
self.applyDocBrowserStyling();
|
|
365
|
-
self.checkButtonState();
|
|
366
|
-
}, 150);
|
|
646
|
+
class DocumentBrowserManager {
|
|
647
|
+
constructor(viewer, config, eventBus) {
|
|
648
|
+
this.shouldRemainOpen = false;
|
|
649
|
+
this.viewer = viewer;
|
|
650
|
+
this.config = config || {
|
|
651
|
+
autoOpen: true,
|
|
652
|
+
persistState: true,
|
|
653
|
+
defaultTab: 'thumbnails',
|
|
654
|
+
};
|
|
655
|
+
// Use provided EventBus instance or fallback to singleton
|
|
656
|
+
this.eventBus = eventBus || EventBus.getInstance();
|
|
657
|
+
}
|
|
658
|
+
autoOpenIfMultiPage(isMultiPage) {
|
|
659
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
660
|
+
if (!isMultiPage || !this.config.autoOpen) {
|
|
661
|
+
return;
|
|
662
|
+
}
|
|
663
|
+
setTimeout(() => {
|
|
664
|
+
const originalDocBtn = document.getElementById('toolbar-documentModels');
|
|
665
|
+
if (originalDocBtn && !originalDocBtn.classList.contains('active')) {
|
|
666
|
+
originalDocBtn.click();
|
|
667
|
+
this.shouldRemainOpen = true;
|
|
668
|
+
setTimeout(() => {
|
|
669
|
+
this.applyCustomStyling();
|
|
670
|
+
this.switchToThumbnailTab();
|
|
671
|
+
}, TOOLBAR_REFRESH_INTERVALS.EXTENSION_INIT);
|
|
672
|
+
this.eventBus.emit(EVENT_NAMES.DOC_BROWSER_OPENED);
|
|
367
673
|
}
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
}
|
|
674
|
+
else if (originalDocBtn &&
|
|
675
|
+
originalDocBtn.classList.contains('active')) {
|
|
676
|
+
this.shouldRemainOpen = true;
|
|
677
|
+
}
|
|
678
|
+
}, TOOLBAR_REFRESH_INTERVALS.GEOMETRY_SETTLE);
|
|
679
|
+
});
|
|
680
|
+
}
|
|
681
|
+
openPanel() {
|
|
682
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
683
|
+
var _a, _b, _c;
|
|
684
|
+
let ext = this.viewer.getExtension('Autodesk.DocumentBrowser');
|
|
685
|
+
if (!ext) {
|
|
686
|
+
try {
|
|
687
|
+
yield this.viewer.loadExtension('Autodesk.DocumentBrowser');
|
|
688
|
+
ext = this.viewer.getExtension('Autodesk.DocumentBrowser');
|
|
689
|
+
}
|
|
690
|
+
catch (e) {
|
|
691
|
+
return;
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
// Check if already open
|
|
695
|
+
if ((_b = (_a = ext === null || ext === void 0 ? void 0 : ext.ui) === null || _a === void 0 ? void 0 : _a.panel) === null || _b === void 0 ? void 0 : _b.isVisible()) {
|
|
696
|
+
this.shouldRemainOpen = true;
|
|
697
|
+
this.applyCustomStyling();
|
|
698
|
+
this.switchToThumbnailTab();
|
|
699
|
+
return;
|
|
700
|
+
}
|
|
701
|
+
// Try to use original button first for proper initialization
|
|
702
|
+
const originalDocBtn = document.getElementById('toolbar-documentModels');
|
|
703
|
+
if (originalDocBtn) {
|
|
704
|
+
originalDocBtn.click();
|
|
705
|
+
this.shouldRemainOpen = true;
|
|
706
|
+
setTimeout(() => {
|
|
707
|
+
this.applyCustomStyling();
|
|
708
|
+
this.switchToThumbnailTab();
|
|
709
|
+
this.eventBus.emit(EVENT_NAMES.DOC_BROWSER_OPENED);
|
|
710
|
+
}, TOOLBAR_REFRESH_INTERVALS.EXTENSION_INIT);
|
|
711
|
+
}
|
|
712
|
+
else {
|
|
713
|
+
if ((_c = ext === null || ext === void 0 ? void 0 : ext.ui) === null || _c === void 0 ? void 0 : _c.panel) {
|
|
714
|
+
ext.ui.panel.setVisible(true);
|
|
715
|
+
this.shouldRemainOpen = true;
|
|
716
|
+
setTimeout(() => {
|
|
717
|
+
this.applyCustomStyling();
|
|
718
|
+
this.switchToThumbnailTab();
|
|
719
|
+
this.eventBus.emit(EVENT_NAMES.DOC_BROWSER_OPENED);
|
|
720
|
+
}, TOOLBAR_REFRESH_INTERVALS.EXTENSION_INIT);
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
});
|
|
724
|
+
}
|
|
725
|
+
closePanel() {
|
|
726
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
727
|
+
var _a, _b, _c;
|
|
728
|
+
const ext = this.viewer.getExtension('Autodesk.DocumentBrowser');
|
|
729
|
+
if (!((_b = (_a = ext === null || ext === void 0 ? void 0 : ext.ui) === null || _a === void 0 ? void 0 : _a.panel) === null || _b === void 0 ? void 0 : _b.isVisible())) {
|
|
730
|
+
this.shouldRemainOpen = false;
|
|
731
|
+
return;
|
|
732
|
+
}
|
|
733
|
+
const originalDocBtn = document.getElementById('toolbar-documentModels');
|
|
734
|
+
if (originalDocBtn && originalDocBtn.classList.contains('active')) {
|
|
735
|
+
originalDocBtn.click();
|
|
736
|
+
this.shouldRemainOpen = false;
|
|
737
|
+
this.eventBus.emit(EVENT_NAMES.DOC_BROWSER_CLOSED);
|
|
738
|
+
}
|
|
739
|
+
else {
|
|
740
|
+
if ((_c = ext === null || ext === void 0 ? void 0 : ext.ui) === null || _c === void 0 ? void 0 : _c.panel) {
|
|
741
|
+
ext.ui.panel.setVisible(false);
|
|
742
|
+
this.shouldRemainOpen = false;
|
|
743
|
+
this.eventBus.emit(EVENT_NAMES.DOC_BROWSER_CLOSED);
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
});
|
|
747
|
+
}
|
|
748
|
+
togglePanel() {
|
|
749
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
750
|
+
const isCurrentlyOpen = this.isOpen();
|
|
751
|
+
if (isCurrentlyOpen) {
|
|
752
|
+
yield this.closePanel();
|
|
753
|
+
}
|
|
754
|
+
else {
|
|
755
|
+
yield this.openPanel();
|
|
756
|
+
}
|
|
757
|
+
});
|
|
758
|
+
}
|
|
759
|
+
isOpen() {
|
|
760
|
+
var _a, _b, _c;
|
|
761
|
+
const ext = this.viewer.getExtension('Autodesk.DocumentBrowser');
|
|
762
|
+
return (_c = (_b = (_a = ext === null || ext === void 0 ? void 0 : ext.ui) === null || _a === void 0 ? void 0 : _a.panel) === null || _b === void 0 ? void 0 : _b.isVisible()) !== null && _c !== void 0 ? _c : false;
|
|
763
|
+
}
|
|
764
|
+
applyCustomStyling() {
|
|
765
|
+
setTimeout(() => {
|
|
766
|
+
var _a, _b;
|
|
767
|
+
const ext = this.viewer.getExtension('Autodesk.DocumentBrowser');
|
|
768
|
+
if ((_b = (_a = ext === null || ext === void 0 ? void 0 : ext.ui) === null || _a === void 0 ? void 0 : _a.panel) === null || _b === void 0 ? void 0 : _b.container) {
|
|
769
|
+
applyStyles(ext.ui.panel.container, DOCUMENT_BROWSER_STYLES);
|
|
770
|
+
}
|
|
771
|
+
}, TOOLBAR_REFRESH_INTERVALS.DOM_SETTLE);
|
|
772
|
+
}
|
|
773
|
+
switchToThumbnailTab() {
|
|
774
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
775
|
+
if (this.config.defaultTab !== 'thumbnails')
|
|
776
|
+
return;
|
|
777
|
+
setTimeout(() => {
|
|
778
|
+
const thumbnailTab = findThumbnailTab();
|
|
779
|
+
if (thumbnailTab && !thumbnailTab.classList.contains('active')) {
|
|
780
|
+
clickElement(thumbnailTab);
|
|
781
|
+
}
|
|
782
|
+
}, TOOLBAR_REFRESH_INTERVALS.DOM_SETTLE);
|
|
783
|
+
});
|
|
784
|
+
}
|
|
785
|
+
restoreState() {
|
|
786
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
787
|
+
var _a, _b, _c;
|
|
788
|
+
if (!this.config.persistState || !this.shouldRemainOpen) {
|
|
789
|
+
return;
|
|
790
|
+
}
|
|
791
|
+
const ext = this.viewer.getExtension('Autodesk.DocumentBrowser');
|
|
792
|
+
const isCurrentlyOpen = (_c = (_b = (_a = ext === null || ext === void 0 ? void 0 : ext.ui) === null || _a === void 0 ? void 0 : _a.panel) === null || _b === void 0 ? void 0 : _b.isVisible()) !== null && _c !== void 0 ? _c : false;
|
|
793
|
+
if (isCurrentlyOpen) {
|
|
794
|
+
this.applyCustomStyling();
|
|
795
|
+
this.switchToThumbnailTab();
|
|
796
|
+
// Emit event MULTIPLE times to ensure button state syncs
|
|
797
|
+
this.eventBus.emit(EVENT_NAMES.DOC_BROWSER_OPENED);
|
|
798
|
+
setTimeout(() => this.eventBus.emit(EVENT_NAMES.DOC_BROWSER_OPENED), 50);
|
|
799
|
+
setTimeout(() => this.eventBus.emit(EVENT_NAMES.DOC_BROWSER_OPENED), 100);
|
|
800
|
+
setTimeout(() => this.eventBus.emit(EVENT_NAMES.DOC_BROWSER_OPENED), 150);
|
|
801
|
+
}
|
|
802
|
+
else {
|
|
803
|
+
yield this.openPanel();
|
|
804
|
+
}
|
|
805
|
+
});
|
|
806
|
+
}
|
|
807
|
+
setShouldRemainOpen(value) {
|
|
808
|
+
this.shouldRemainOpen = value;
|
|
809
|
+
}
|
|
810
|
+
getShouldRemainOpen() {
|
|
811
|
+
return this.shouldRemainOpen;
|
|
812
|
+
}
|
|
813
|
+
}
|
|
419
814
|
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
815
|
+
/**
|
|
816
|
+
* PaginationManager
|
|
817
|
+
*
|
|
818
|
+
* @file PaginationManager.ts
|
|
819
|
+
* @description Manages page navigation for multi-page documents
|
|
820
|
+
* Extracted from ToolbarExtension.js lines 34-148
|
|
821
|
+
*/
|
|
822
|
+
class PaginationManager {
|
|
823
|
+
constructor(viewer, Autodesk, config, eventBus) {
|
|
824
|
+
this.currentIndex = 0;
|
|
825
|
+
this.viewables = [];
|
|
826
|
+
this.paginationGroup = null;
|
|
827
|
+
this.docBrowserShouldBeOpen = false;
|
|
828
|
+
this.viewer = viewer;
|
|
829
|
+
this.Autodesk = Autodesk;
|
|
830
|
+
this.config = config || {};
|
|
831
|
+
// Use provided EventBus instance or fallback to singleton
|
|
832
|
+
this.eventBus = eventBus || EventBus.getInstance();
|
|
833
|
+
this.modelAddedHandler = this.updateCurrentIndexFromModel.bind(this);
|
|
834
|
+
// Add MODEL_ADDED_EVENT listener immediately in constructor
|
|
835
|
+
// This ensures it's always listening for page changes
|
|
836
|
+
this.viewer.addEventListener(this.Autodesk.Viewing.MODEL_ADDED_EVENT, this.modelAddedHandler);
|
|
837
|
+
}
|
|
838
|
+
/**
|
|
839
|
+
* Set callback for page changes (alternative to EventBus)
|
|
840
|
+
*/
|
|
841
|
+
setPageChangeCallback(callback) {
|
|
842
|
+
this.onPageChangeCallback = callback;
|
|
843
|
+
}
|
|
844
|
+
setViewables(viewables) {
|
|
845
|
+
this.viewables = viewables;
|
|
846
|
+
this.currentIndex = 0;
|
|
847
|
+
if (viewables.length > 1) {
|
|
848
|
+
this.docBrowserShouldBeOpen = true;
|
|
849
|
+
}
|
|
850
|
+
this.updatePaginationState();
|
|
851
|
+
// Remove existing listener to avoid duplicates
|
|
852
|
+
if (this.modelAddedHandler) {
|
|
853
|
+
this.viewer.removeEventListener(this.Autodesk.Viewing.MODEL_ADDED_EVENT, this.modelAddedHandler);
|
|
854
|
+
}
|
|
855
|
+
// Add MODEL_ADDED_EVENT listener
|
|
856
|
+
this.viewer.addEventListener(this.Autodesk.Viewing.MODEL_ADDED_EVENT, this.modelAddedHandler);
|
|
857
|
+
}
|
|
858
|
+
nextPage() {
|
|
859
|
+
if (this.viewables.length > 0) {
|
|
860
|
+
this.currentIndex = (this.currentIndex + 1) % this.viewables.length;
|
|
861
|
+
this.loadCurrentViewable();
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
previousPage() {
|
|
865
|
+
if (this.viewables.length > 0) {
|
|
866
|
+
this.currentIndex =
|
|
867
|
+
(this.currentIndex - 1 + this.viewables.length) % this.viewables.length;
|
|
868
|
+
this.loadCurrentViewable();
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
goToPage(index) {
|
|
872
|
+
if (index >= 0 && index < this.viewables.length) {
|
|
873
|
+
this.currentIndex = index;
|
|
874
|
+
this.loadCurrentViewable();
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
getCurrentPage() {
|
|
878
|
+
return this.currentIndex;
|
|
879
|
+
}
|
|
880
|
+
getTotalPages() {
|
|
881
|
+
return this.viewables.length;
|
|
882
|
+
}
|
|
883
|
+
isMultiPage() {
|
|
884
|
+
return this.viewables.length > 1;
|
|
885
|
+
}
|
|
886
|
+
getViewables() {
|
|
887
|
+
return this.viewables;
|
|
888
|
+
}
|
|
889
|
+
updateCurrentIndexFromModel() {
|
|
890
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
891
|
+
if (!this.viewer.model || this.viewables.length === 0) {
|
|
892
|
+
return;
|
|
893
|
+
}
|
|
894
|
+
const currentNode = this.viewer.model.getDocumentNode();
|
|
895
|
+
if (currentNode) {
|
|
896
|
+
const currentGuid = ((_a = currentNode.data) === null || _a === void 0 ? void 0 : _a.guid) || currentNode.guid;
|
|
897
|
+
const index = this.viewables.findIndex((v) => {
|
|
898
|
+
var _a;
|
|
899
|
+
const vGuid = ((_a = v.data) === null || _a === void 0 ? void 0 : _a.guid) || v.guid;
|
|
900
|
+
return vGuid === currentGuid;
|
|
901
|
+
});
|
|
902
|
+
if (index !== -1 && index !== this.currentIndex) {
|
|
903
|
+
// Page changed via Document Browser panel
|
|
904
|
+
this.currentIndex = index;
|
|
905
|
+
// Check if Document Browser is currently open
|
|
906
|
+
const docBrowserExt = this.viewer.getExtension('Autodesk.DocumentBrowser');
|
|
907
|
+
const isCurrentlyOpen = (_d = (_c = (_b = docBrowserExt === null || docBrowserExt === void 0 ? void 0 : docBrowserExt.ui) === null || _b === void 0 ? void 0 : _b.panel) === null || _c === void 0 ? void 0 : _c.isVisible()) !== null && _d !== void 0 ? _d : false;
|
|
908
|
+
if (isCurrentlyOpen) {
|
|
909
|
+
this.docBrowserShouldBeOpen = true;
|
|
910
|
+
}
|
|
911
|
+
// Emit page changed event to trigger button state sync and toolbar refresh
|
|
912
|
+
const pageData = {
|
|
913
|
+
index: this.currentIndex,
|
|
914
|
+
viewable: this.viewables[this.currentIndex],
|
|
915
|
+
shouldRestoreDocBrowser: this.docBrowserShouldBeOpen,
|
|
916
|
+
fromDocBrowser: true,
|
|
917
|
+
};
|
|
918
|
+
// Use callback first (direct communication)
|
|
919
|
+
if (this.onPageChangeCallback) {
|
|
920
|
+
this.onPageChangeCallback(pageData);
|
|
921
|
+
}
|
|
922
|
+
// Also emit via EventBus as fallback
|
|
923
|
+
this.eventBus.emit(EVENT_NAMES.PAGE_CHANGED, pageData);
|
|
924
|
+
// Update pagination state multiple times to ensure it sticks
|
|
925
|
+
const updateTimes = [10, 50, 100, 150, 200, 250, 300, 350, 400, 500];
|
|
926
|
+
updateTimes.forEach((delay) => {
|
|
927
|
+
setTimeout(() => {
|
|
928
|
+
this.updatePaginationState();
|
|
929
|
+
}, delay);
|
|
930
|
+
});
|
|
931
|
+
}
|
|
932
|
+
else if (index !== -1) {
|
|
933
|
+
// Same page - but user might have clicked thumbnail while panel is open
|
|
934
|
+
const docBrowserExt = this.viewer.getExtension('Autodesk.DocumentBrowser');
|
|
935
|
+
const isCurrentlyOpen = (_g = (_f = (_e = docBrowserExt === null || docBrowserExt === void 0 ? void 0 : docBrowserExt.ui) === null || _e === void 0 ? void 0 : _e.panel) === null || _f === void 0 ? void 0 : _f.isVisible()) !== null && _g !== void 0 ? _g : false;
|
|
936
|
+
if (isCurrentlyOpen && !this.docBrowserShouldBeOpen) {
|
|
937
|
+
this.docBrowserShouldBeOpen = true;
|
|
938
|
+
// Emit a "refresh" event to ensure button state is synced
|
|
939
|
+
const pageData = {
|
|
940
|
+
index: this.currentIndex,
|
|
941
|
+
viewable: this.viewables[this.currentIndex],
|
|
942
|
+
shouldRestoreDocBrowser: true,
|
|
943
|
+
fromDocBrowser: true,
|
|
944
|
+
refresh: true,
|
|
945
|
+
};
|
|
946
|
+
if (this.onPageChangeCallback) {
|
|
947
|
+
this.onPageChangeCallback(pageData);
|
|
948
|
+
}
|
|
949
|
+
this.eventBus.emit(EVENT_NAMES.PAGE_CHANGED, pageData);
|
|
950
|
+
}
|
|
951
|
+
// Always update pagination state
|
|
952
|
+
this.updatePaginationState();
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
updatePaginationState() {
|
|
957
|
+
// IMPORTANT: First sync currentIndex with actual loaded model
|
|
958
|
+
this.syncCurrentIndexFromModel();
|
|
959
|
+
if (!this.paginationGroup) {
|
|
960
|
+
return;
|
|
961
|
+
}
|
|
962
|
+
if (this.viewables.length <= 1) {
|
|
963
|
+
this.paginationGroup.setVisible(false);
|
|
964
|
+
if (this.paginationGroup.container) {
|
|
965
|
+
this.paginationGroup.container.style.display = 'none';
|
|
966
|
+
}
|
|
967
|
+
return;
|
|
968
|
+
}
|
|
969
|
+
this.paginationGroup.setVisible(true);
|
|
970
|
+
if (this.paginationGroup.container) {
|
|
971
|
+
this.paginationGroup.container.style.display = '';
|
|
972
|
+
}
|
|
973
|
+
const totalBtn = this.paginationGroup.getControl(PAGINATION_BUTTONS.LABEL.id);
|
|
974
|
+
if (totalBtn) {
|
|
975
|
+
const current = this.viewables.length > 0 ? this.currentIndex + 1 : 0;
|
|
976
|
+
const total = this.viewables.length;
|
|
977
|
+
totalBtn.setToolTip(`Page ${current} of ${total}`);
|
|
978
|
+
const domElem = totalBtn.container;
|
|
979
|
+
if (domElem) {
|
|
980
|
+
const newContent = `<div style="display: flex; align-items: center; justify-content: center; height: 100%; padding: 0 10px; color: white; font-size: 14px; white-space: nowrap;">${current} / ${total}</div>`;
|
|
981
|
+
domElem.innerHTML = newContent;
|
|
982
|
+
// Use requestAnimationFrame to ensure DOM update
|
|
983
|
+
requestAnimationFrame(() => {
|
|
984
|
+
domElem.innerHTML = newContent;
|
|
985
|
+
});
|
|
986
|
+
// Also try direct text update as fallback
|
|
987
|
+
setTimeout(() => {
|
|
988
|
+
if (domElem.innerHTML !== newContent) {
|
|
989
|
+
domElem.innerHTML = newContent;
|
|
990
|
+
}
|
|
991
|
+
}, 20);
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
else {
|
|
995
|
+
// Try to get the control again in case it was just created
|
|
996
|
+
setTimeout(() => {
|
|
997
|
+
const totalBtnRetry = this.paginationGroup.getControl(PAGINATION_BUTTONS.LABEL.id);
|
|
998
|
+
if (totalBtnRetry) {
|
|
999
|
+
const current = this.viewables.length > 0 ? this.currentIndex + 1 : 0;
|
|
1000
|
+
const total = this.viewables.length;
|
|
1001
|
+
totalBtnRetry.setToolTip(`Page ${current} of ${total}`);
|
|
1002
|
+
const domElem = totalBtnRetry.container;
|
|
1003
|
+
if (domElem) {
|
|
1004
|
+
domElem.innerHTML = `<div style="display: flex; align-items: center; justify-content: center; height: 100%; padding: 0 10px; color: white; font-size: 14px; white-space: nowrap;">${current} / ${total}</div>`;
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
}, 50);
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
/**
|
|
1011
|
+
* Sync currentIndex with the actual loaded model (without emitting events)
|
|
1012
|
+
* This is used to ensure pagination display shows correct page number
|
|
1013
|
+
*/
|
|
1014
|
+
syncCurrentIndexFromModel() {
|
|
1015
|
+
var _a;
|
|
1016
|
+
if (!this.viewer.model || this.viewables.length === 0) {
|
|
1017
|
+
return;
|
|
1018
|
+
}
|
|
1019
|
+
const currentNode = this.viewer.model.getDocumentNode();
|
|
1020
|
+
if (currentNode) {
|
|
1021
|
+
const currentGuid = ((_a = currentNode.data) === null || _a === void 0 ? void 0 : _a.guid) || currentNode.guid;
|
|
1022
|
+
const index = this.viewables.findIndex((v) => {
|
|
1023
|
+
var _a;
|
|
1024
|
+
const vGuid = ((_a = v.data) === null || _a === void 0 ? void 0 : _a.guid) || v.guid;
|
|
1025
|
+
return vGuid === currentGuid;
|
|
1026
|
+
});
|
|
1027
|
+
if (index !== -1 && index !== this.currentIndex) {
|
|
1028
|
+
this.currentIndex = index;
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
/**
|
|
1033
|
+
* Update thumbnail selection in Document Browser panel
|
|
1034
|
+
* This ensures the correct thumbnail is highlighted after navigation
|
|
1035
|
+
*/
|
|
1036
|
+
updateThumbnailSelection(targetViewable) {
|
|
1037
|
+
var _a;
|
|
1038
|
+
try {
|
|
1039
|
+
const targetGuid = ((_a = targetViewable.data) === null || _a === void 0 ? void 0 : _a.guid) || targetViewable.guid;
|
|
1040
|
+
// Find all thumbnails
|
|
1041
|
+
const thumbnails = document.querySelectorAll('.viewer-ext-docbrowser-thumbnail');
|
|
1042
|
+
let selectedThumbnail = null;
|
|
1043
|
+
thumbnails.forEach((thumbnail) => {
|
|
1044
|
+
const bubbleGuid = thumbnail.getAttribute('bubble-guid');
|
|
1045
|
+
if (bubbleGuid === targetGuid) {
|
|
1046
|
+
thumbnail.classList.add('viewer-ext-docbrowser-thumbnail-selected');
|
|
1047
|
+
selectedThumbnail = thumbnail;
|
|
1048
|
+
}
|
|
1049
|
+
else {
|
|
1050
|
+
thumbnail.classList.remove('viewer-ext-docbrowser-thumbnail-selected');
|
|
1051
|
+
}
|
|
1052
|
+
});
|
|
1053
|
+
// CRITICAL: Scroll to selected thumbnail
|
|
1054
|
+
if (selectedThumbnail) {
|
|
1055
|
+
this.scrollToThumbnail(selectedThumbnail);
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
catch (error) {
|
|
1059
|
+
// Silent error handling
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
/**
|
|
1063
|
+
* Scroll the Document Browser panel to show the selected thumbnail
|
|
1064
|
+
*/
|
|
1065
|
+
scrollToThumbnail(thumbnail) {
|
|
1066
|
+
try {
|
|
1067
|
+
// Find the scroll container
|
|
1068
|
+
const scrollContainer = document.querySelector('.docking-panel-scroll');
|
|
1069
|
+
if (!scrollContainer) {
|
|
1070
|
+
return;
|
|
1071
|
+
}
|
|
1072
|
+
// Get thumbnail position relative to container
|
|
1073
|
+
const thumbnailRect = thumbnail.getBoundingClientRect();
|
|
1074
|
+
const containerRect = scrollContainer.getBoundingClientRect();
|
|
1075
|
+
// Calculate scroll position to center the thumbnail in the viewport
|
|
1076
|
+
const thumbnailTop = thumbnailRect.top - containerRect.top + scrollContainer.scrollTop;
|
|
1077
|
+
const containerHeight = scrollContainer.clientHeight;
|
|
1078
|
+
const thumbnailHeight = thumbnailRect.height;
|
|
1079
|
+
const targetScrollTop = thumbnailTop - containerHeight / 2 + thumbnailHeight / 2;
|
|
1080
|
+
// Smooth scroll to target position
|
|
1081
|
+
scrollContainer.scrollTo({
|
|
1082
|
+
top: targetScrollTop,
|
|
1083
|
+
behavior: 'smooth',
|
|
1084
|
+
});
|
|
1085
|
+
}
|
|
1086
|
+
catch (error) {
|
|
1087
|
+
// Silent error handling
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
/**
|
|
1091
|
+
* Try to use DocumentBrowser API to change page without refreshing panel
|
|
1092
|
+
* Returns true if successful, false otherwise
|
|
1093
|
+
*/
|
|
1094
|
+
tryUseDocBrowserAPI(targetIndex) {
|
|
1095
|
+
var _a, _b, _c;
|
|
1096
|
+
const docBrowserExt = this.viewer.getExtension('Autodesk.DocumentBrowser');
|
|
1097
|
+
const isOpen = (_c = (_b = (_a = docBrowserExt === null || docBrowserExt === void 0 ? void 0 : docBrowserExt.ui) === null || _a === void 0 ? void 0 : _a.panel) === null || _b === void 0 ? void 0 : _b.isVisible()) !== null && _c !== void 0 ? _c : false;
|
|
1098
|
+
if (!isOpen || targetIndex < 0 || targetIndex >= this.viewables.length) {
|
|
1099
|
+
return false;
|
|
1100
|
+
}
|
|
1101
|
+
try {
|
|
1102
|
+
const targetViewable = this.viewables[targetIndex];
|
|
1103
|
+
if (!targetViewable) {
|
|
1104
|
+
return false;
|
|
1105
|
+
}
|
|
1106
|
+
// Try _changeModelFn (internal method used by DocumentBrowser)
|
|
1107
|
+
if (docBrowserExt.ui &&
|
|
1108
|
+
typeof docBrowserExt.ui._changeModelFn === 'function') {
|
|
1109
|
+
docBrowserExt.ui._changeModelFn(targetViewable);
|
|
1110
|
+
// Update thumbnail selection after model change
|
|
1111
|
+
setTimeout(() => {
|
|
1112
|
+
this.updateThumbnailSelection(targetViewable);
|
|
1113
|
+
}, 100);
|
|
1114
|
+
return true;
|
|
1115
|
+
}
|
|
1116
|
+
// Fallback: Try _changeModel
|
|
1117
|
+
if (docBrowserExt.ui &&
|
|
1118
|
+
typeof docBrowserExt.ui._changeModel === 'function') {
|
|
1119
|
+
docBrowserExt.ui._changeModel(targetViewable);
|
|
1120
|
+
return true;
|
|
1121
|
+
}
|
|
1122
|
+
// Fallback: Try setCurrentViewable
|
|
1123
|
+
if (docBrowserExt &&
|
|
1124
|
+
typeof docBrowserExt.setCurrentViewable === 'function') {
|
|
1125
|
+
docBrowserExt.setCurrentViewable(targetViewable);
|
|
1126
|
+
return true;
|
|
1127
|
+
}
|
|
1128
|
+
return false;
|
|
1129
|
+
}
|
|
1130
|
+
catch (error) {
|
|
1131
|
+
return false;
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
loadCurrentViewable() {
|
|
1135
|
+
var _a, _b, _c, _d, _e;
|
|
1136
|
+
if (this.viewables.length === 0 || !this.viewables[this.currentIndex]) {
|
|
1137
|
+
return;
|
|
1138
|
+
}
|
|
1139
|
+
const viewer = this.viewer;
|
|
1140
|
+
// Check current Document Browser state before loading new page
|
|
1141
|
+
const docBrowserExt = viewer.getExtension('Autodesk.DocumentBrowser');
|
|
1142
|
+
const isCurrentlyOpen = (_c = (_b = (_a = docBrowserExt === null || docBrowserExt === void 0 ? void 0 : docBrowserExt.ui) === null || _a === void 0 ? void 0 : _a.panel) === null || _b === void 0 ? void 0 : _b.isVisible()) !== null && _c !== void 0 ? _c : false;
|
|
1143
|
+
if (isCurrentlyOpen) {
|
|
1144
|
+
this.docBrowserShouldBeOpen = true;
|
|
1145
|
+
}
|
|
1146
|
+
// OPTIMIZATION: If Document Browser is open, try to use its API to navigate
|
|
1147
|
+
// This prevents the panel from being refreshed (same behavior as manual thumbnail click)
|
|
1148
|
+
if (isCurrentlyOpen) {
|
|
1149
|
+
const apiSuccess = this.tryUseDocBrowserAPI(this.currentIndex);
|
|
1150
|
+
if (apiSuccess) {
|
|
1151
|
+
// Update pagination state after a short delay
|
|
1152
|
+
setTimeout(() => {
|
|
1153
|
+
this.updatePaginationState();
|
|
1154
|
+
}, 100);
|
|
1155
|
+
return;
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
// FALLBACK: Use traditional loadDocumentNode if Document Browser is closed
|
|
1159
|
+
const onGeometryLoaded = () => {
|
|
1160
|
+
viewer.removeEventListener(this.Autodesk.Viewing.GEOMETRY_LOADED_EVENT, onGeometryLoaded);
|
|
1161
|
+
setTimeout(() => {
|
|
1162
|
+
const pageData = {
|
|
1163
|
+
index: this.currentIndex,
|
|
1164
|
+
viewable: this.viewables[this.currentIndex],
|
|
1165
|
+
shouldRestoreDocBrowser: this.docBrowserShouldBeOpen,
|
|
1166
|
+
fromPagination: true,
|
|
1167
|
+
};
|
|
1168
|
+
if (this.onPageChangeCallback) {
|
|
1169
|
+
this.onPageChangeCallback(pageData);
|
|
1170
|
+
}
|
|
1171
|
+
this.eventBus.emit(EVENT_NAMES.PAGE_CHANGED, pageData);
|
|
1172
|
+
this.updatePaginationState();
|
|
1173
|
+
}, TOOLBAR_REFRESH_INTERVALS.GEOMETRY_SETTLE);
|
|
1174
|
+
};
|
|
1175
|
+
viewer.addEventListener(this.Autodesk.Viewing.GEOMETRY_LOADED_EVENT, onGeometryLoaded);
|
|
1176
|
+
viewer
|
|
1177
|
+
.loadDocumentNode(viewer.model.getDocumentNode().getDocument(), this.viewables[this.currentIndex])
|
|
1178
|
+
.then(() => {
|
|
1179
|
+
const pageData = {
|
|
1180
|
+
index: this.currentIndex,
|
|
1181
|
+
viewable: this.viewables[this.currentIndex],
|
|
1182
|
+
shouldRestoreDocBrowser: this.docBrowserShouldBeOpen,
|
|
1183
|
+
immediate: true,
|
|
1184
|
+
fromPagination: true,
|
|
1185
|
+
};
|
|
1186
|
+
if (this.onPageChangeCallback) {
|
|
1187
|
+
this.onPageChangeCallback(pageData);
|
|
1188
|
+
}
|
|
1189
|
+
this.eventBus.emit(EVENT_NAMES.PAGE_CHANGED, pageData);
|
|
1190
|
+
this.updatePaginationState();
|
|
1191
|
+
})
|
|
1192
|
+
.catch((err) => {
|
|
1193
|
+
// Silent error handling
|
|
1194
|
+
});
|
|
1195
|
+
(_e = (_d = this.config).onPageChange) === null || _e === void 0 ? void 0 : _e.call(_d, this.currentIndex);
|
|
1196
|
+
}
|
|
1197
|
+
createPaginationGroup(toolbar) {
|
|
1198
|
+
this.paginationGroup = new this.Autodesk.Viewing.UI.ControlGroup(TOOLBAR_CONTROL_GROUP_IDS.PAGINATION);
|
|
1199
|
+
toolbar.addControl(this.paginationGroup);
|
|
1200
|
+
const prevBtn = new this.Autodesk.Viewing.UI.Button(PAGINATION_BUTTONS.PREV.id);
|
|
1201
|
+
prevBtn.setIcon(PAGINATION_BUTTONS.PREV.icon);
|
|
1202
|
+
prevBtn.addClass('custom-prev-btn');
|
|
1203
|
+
prevBtn.setToolTip(PAGINATION_BUTTONS.PREV.tooltip);
|
|
1204
|
+
prevBtn.onClick = () => this.previousPage();
|
|
1205
|
+
this.paginationGroup.addControl(prevBtn);
|
|
1206
|
+
const labelBtn = new this.Autodesk.Viewing.UI.Button(PAGINATION_BUTTONS.LABEL.id);
|
|
1207
|
+
labelBtn.setToolTip(PAGINATION_BUTTONS.LABEL.tooltip);
|
|
1208
|
+
this.paginationGroup.addControl(labelBtn);
|
|
1209
|
+
const nextBtn = new this.Autodesk.Viewing.UI.Button(PAGINATION_BUTTONS.NEXT.id);
|
|
1210
|
+
nextBtn.setIcon(PAGINATION_BUTTONS.NEXT.icon);
|
|
1211
|
+
nextBtn.addClass('custom-next-btn');
|
|
1212
|
+
nextBtn.setToolTip(PAGINATION_BUTTONS.NEXT.tooltip);
|
|
1213
|
+
nextBtn.onClick = () => this.nextPage();
|
|
1214
|
+
this.paginationGroup.addControl(nextBtn);
|
|
1215
|
+
// Immediately update pagination state after creating group
|
|
1216
|
+
setTimeout(() => {
|
|
1217
|
+
this.updatePaginationState();
|
|
1218
|
+
}, 10);
|
|
1219
|
+
}
|
|
1220
|
+
getPaginationGroup() {
|
|
1221
|
+
return this.paginationGroup;
|
|
1222
|
+
}
|
|
1223
|
+
setPaginationGroup(group) {
|
|
1224
|
+
this.paginationGroup = group;
|
|
1225
|
+
}
|
|
1226
|
+
cleanup() {
|
|
1227
|
+
if (this.modelAddedHandler) {
|
|
1228
|
+
this.viewer.removeEventListener(this.Autodesk.Viewing.MODEL_ADDED_EVENT, this.modelAddedHandler);
|
|
1229
|
+
}
|
|
1230
|
+
if (this.viewer.toolbar && this.paginationGroup) {
|
|
1231
|
+
this.viewer.toolbar.removeControl(this.paginationGroup);
|
|
1232
|
+
this.paginationGroup = null;
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
424
1236
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
1237
|
+
/**
|
|
1238
|
+
* ToolbarManager
|
|
1239
|
+
*
|
|
1240
|
+
* @file ToolbarManager.ts
|
|
1241
|
+
* @description Manages custom toolbar creation and button state
|
|
1242
|
+
* Extracted from ToolbarExtension.js lines 256-479
|
|
1243
|
+
*/
|
|
1244
|
+
class ToolbarManager {
|
|
1245
|
+
constructor(viewer, Autodesk, config) {
|
|
1246
|
+
this.subToolbar = null;
|
|
1247
|
+
this.viewer = viewer;
|
|
1248
|
+
this.Autodesk = Autodesk;
|
|
1249
|
+
this.config = config;
|
|
1250
|
+
this.eventBus = EventBus.getInstance();
|
|
1251
|
+
}
|
|
1252
|
+
createToolbar() {
|
|
1253
|
+
const toolbar = this.viewer.toolbar;
|
|
1254
|
+
if (!toolbar)
|
|
1255
|
+
return;
|
|
1256
|
+
this.subToolbar = new this.Autodesk.Viewing.UI.ControlGroup(TOOLBAR_CONTROL_GROUP_IDS.TOOLS);
|
|
1257
|
+
const panBtn = this.createPanButton();
|
|
1258
|
+
const docBtn = this.createDocBrowserButton();
|
|
1259
|
+
const dlBtn = this.createDownloadButton();
|
|
1260
|
+
this.subToolbar.addControl(panBtn);
|
|
1261
|
+
this.subToolbar.addControl(docBtn);
|
|
1262
|
+
this.subToolbar.addControl(dlBtn);
|
|
1263
|
+
toolbar.addControl(this.subToolbar);
|
|
1264
|
+
}
|
|
1265
|
+
createPanButton() {
|
|
1266
|
+
const btn = new this.Autodesk.Viewing.UI.Button(CUSTOM_TOOLBAR_BUTTONS.PAN.id);
|
|
1267
|
+
btn.setIcon(CUSTOM_TOOLBAR_BUTTONS.PAN.icon);
|
|
1268
|
+
btn.addClass('custom-pan-btn');
|
|
1269
|
+
btn.setToolTip(CUSTOM_TOOLBAR_BUTTONS.PAN.tooltip);
|
|
1270
|
+
btn.onClick = () => {
|
|
1271
|
+
var _a, _b;
|
|
1272
|
+
(_b = (_a = this.config).onPanClick) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
1273
|
+
this.activatePanButton();
|
|
1274
|
+
};
|
|
1275
|
+
return btn;
|
|
1276
|
+
}
|
|
1277
|
+
createDocBrowserButton() {
|
|
1278
|
+
const btn = new this.Autodesk.Viewing.UI.Button(CUSTOM_TOOLBAR_BUTTONS.DOC_BROWSER.id);
|
|
1279
|
+
btn.setIcon(CUSTOM_TOOLBAR_BUTTONS.DOC_BROWSER.icon);
|
|
1280
|
+
btn.addClass('custom-doc-browser-btn');
|
|
1281
|
+
btn.setToolTip(CUSTOM_TOOLBAR_BUTTONS.DOC_BROWSER.tooltip);
|
|
1282
|
+
btn.onClick = () => {
|
|
1283
|
+
var _a, _b;
|
|
1284
|
+
(_b = (_a = this.config).onDocBrowserClick) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
1285
|
+
this.eventBus.emit(EVENT_NAMES.TOOLBAR_BUTTON_CLICKED, {
|
|
1286
|
+
buttonId: CUSTOM_TOOLBAR_BUTTONS.DOC_BROWSER.id,
|
|
1287
|
+
});
|
|
1288
|
+
};
|
|
1289
|
+
return btn;
|
|
1290
|
+
}
|
|
1291
|
+
createDownloadButton() {
|
|
1292
|
+
const btn = new this.Autodesk.Viewing.UI.Button(CUSTOM_TOOLBAR_BUTTONS.DOWNLOAD.id);
|
|
1293
|
+
btn.setIcon(CUSTOM_TOOLBAR_BUTTONS.DOWNLOAD.icon);
|
|
1294
|
+
btn.addClass('custom-download-btn');
|
|
1295
|
+
btn.setToolTip(CUSTOM_TOOLBAR_BUTTONS.DOWNLOAD.tooltip);
|
|
1296
|
+
btn.onClick = () => {
|
|
1297
|
+
var _a, _b;
|
|
1298
|
+
(_b = (_a = this.config).onDownloadClick) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
1299
|
+
this.eventBus.emit(EVENT_NAMES.DOWNLOAD_REQUESTED, {
|
|
1300
|
+
filePath: this.config.filePath,
|
|
1301
|
+
});
|
|
1302
|
+
};
|
|
1303
|
+
return btn;
|
|
1304
|
+
}
|
|
1305
|
+
updateButtonState(buttonId, isActive) {
|
|
1306
|
+
if (!this.subToolbar)
|
|
1307
|
+
return;
|
|
1308
|
+
const btn = this.subToolbar.getControl(buttonId);
|
|
1309
|
+
if (btn) {
|
|
1310
|
+
const newState = isActive
|
|
1311
|
+
? this.Autodesk.Viewing.UI.Button.State.ACTIVE
|
|
1312
|
+
: this.Autodesk.Viewing.UI.Button.State.INACTIVE;
|
|
1313
|
+
btn.setState(newState);
|
|
1314
|
+
}
|
|
442
1315
|
}
|
|
1316
|
+
activatePanButton() {
|
|
1317
|
+
try {
|
|
1318
|
+
this.viewer.setActiveNavigationTool('pan');
|
|
1319
|
+
// Note: Removed EVENT_NAMES.PAN_ACTIVATED emit as no listeners were registered
|
|
1320
|
+
// If you need this event, add a listener first
|
|
1321
|
+
}
|
|
1322
|
+
catch (e) {
|
|
1323
|
+
const originalPanBtn = document.getElementById('toolbar-panTool');
|
|
1324
|
+
if (originalPanBtn) {
|
|
1325
|
+
originalPanBtn.click();
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
this.updateButtonState(CUSTOM_TOOLBAR_BUTTONS.PAN.id, true);
|
|
1329
|
+
const customPanBtn = document.querySelector('.custom-pan-btn');
|
|
1330
|
+
if (customPanBtn) {
|
|
1331
|
+
customPanBtn.classList.add('active');
|
|
1332
|
+
customPanBtn.classList.remove('inactive');
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
hideDefaultGroups() {
|
|
1336
|
+
const toolbar = this.viewer.toolbar;
|
|
1337
|
+
if (!toolbar)
|
|
1338
|
+
return;
|
|
1339
|
+
DEFAULT_HIDDEN_TOOLBAR_GROUPS.forEach((id) => {
|
|
1340
|
+
const group = toolbar.getControl(id);
|
|
1341
|
+
if (group) {
|
|
1342
|
+
group.setVisible(false);
|
|
1343
|
+
}
|
|
1344
|
+
});
|
|
1345
|
+
}
|
|
1346
|
+
getToolbar() {
|
|
1347
|
+
return this.subToolbar;
|
|
1348
|
+
}
|
|
1349
|
+
cleanup() {
|
|
1350
|
+
if (this.viewer.toolbar && this.subToolbar) {
|
|
1351
|
+
this.viewer.toolbar.removeControl(this.subToolbar);
|
|
1352
|
+
this.subToolbar = null;
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
}
|
|
443
1356
|
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
1357
|
+
// CRITICAL: Global state to persist across extension unload/reload cycles
|
|
1358
|
+
// This is necessary because Forge Viewer unloads extensions when navigating via thumbnails
|
|
1359
|
+
var GLOBAL_STATE = {
|
|
1360
|
+
viewables: null,
|
|
1361
|
+
docBrowserShouldBeOpen: false,
|
|
1362
|
+
currentIndex: 0
|
|
1363
|
+
};
|
|
1364
|
+
function registerToolbarExtension(Autodesk) {
|
|
1365
|
+
function ToolbarExtension(viewer, options) {
|
|
1366
|
+
var _this = this;
|
|
1367
|
+
Autodesk.Viewing.Extension.call(this, viewer, options);
|
|
1368
|
+
this.viewer = viewer;
|
|
1369
|
+
this.Autodesk = Autodesk;
|
|
1370
|
+
this.options = options;
|
|
1371
|
+
// Initialize EventBus
|
|
1372
|
+
this.eventBus = EventBus.getInstance();
|
|
1373
|
+
// Initialize Managers (Dependency Injection)
|
|
1374
|
+
// CRITICAL: Pass EventBus instance to ensure all managers use the same instance
|
|
1375
|
+
this.paginationManager = new PaginationManager(viewer, Autodesk, {}, this.eventBus);
|
|
1376
|
+
// Register page change callback (direct communication instead of EventBus)
|
|
1377
|
+
this.paginationManager.setPageChangeCallback(function (data) {
|
|
1378
|
+
_this.onPageChanged(data);
|
|
1379
|
+
});
|
|
1380
|
+
this.toolbarManager = new ToolbarManager(viewer, Autodesk, {
|
|
1381
|
+
Autodesk: Autodesk,
|
|
1382
|
+
filePath: options.filePath,
|
|
1383
|
+
onPanClick: function onPanClick() {
|
|
1384
|
+
return _this.handlePanClick();
|
|
1385
|
+
},
|
|
1386
|
+
onDocBrowserClick: function onDocBrowserClick() {
|
|
1387
|
+
return _this.handleDocBrowserClick();
|
|
1388
|
+
},
|
|
1389
|
+
onDownloadClick: function onDownloadClick() {
|
|
1390
|
+
return _this.handleDownloadClick();
|
|
1391
|
+
}
|
|
451
1392
|
});
|
|
452
|
-
|
|
453
|
-
|
|
1393
|
+
this.docBrowserManager = new DocumentBrowserManager(viewer, {
|
|
1394
|
+
autoOpen: true,
|
|
1395
|
+
persistState: true,
|
|
1396
|
+
defaultTab: 'thumbnails'
|
|
1397
|
+
}, this.eventBus);
|
|
1398
|
+
// Initialize Services
|
|
1399
|
+
this.downloadService = new DownloadService();
|
|
1400
|
+
// Toolbar healing interval
|
|
1401
|
+
this._toolbarInterval = null;
|
|
1402
|
+
this._buttonStateInterval = null;
|
|
1403
|
+
// Store viewables temporarily if toolbar not ready yet
|
|
1404
|
+
this._pendingViewables = null;
|
|
1405
|
+
this._toolbarReady = false;
|
|
1406
|
+
// Track if page change is in progress
|
|
1407
|
+
this._isPageChanging = false;
|
|
1408
|
+
// Bind event handlers
|
|
1409
|
+
this.onToolbarCreatedHandler = this.onToolbarCreated.bind(this);
|
|
1410
|
+
this.onGeometryLoadedHandler = this.onGeometryLoaded.bind(this);
|
|
1411
|
+
this.onPageChangedHandler = this.onPageChanged.bind(this);
|
|
1412
|
+
this.onViewablesSetHandler = this.onViewablesSet.bind(this);
|
|
1413
|
+
}
|
|
1414
|
+
ToolbarExtension.prototype = Object.create(Autodesk.Viewing.Extension.prototype);
|
|
1415
|
+
ToolbarExtension.prototype.constructor = ToolbarExtension;
|
|
1416
|
+
ToolbarExtension.prototype.load = function () {
|
|
1417
|
+
var _this2 = this;
|
|
1418
|
+
this.viewer.addEventListener(Autodesk.Viewing.TOOLBAR_CREATED_EVENT, this.onToolbarCreatedHandler);
|
|
1419
|
+
this.viewer.addEventListener(Autodesk.Viewing.GEOMETRY_LOADED_EVENT, this.onGeometryLoadedHandler);
|
|
1420
|
+
// Subscribe to EventBus events
|
|
1421
|
+
this.eventBus.on(EVENT_NAMES.PAGE_CHANGED, this.onPageChangedHandler);
|
|
1422
|
+
this.eventBus.on(EVENT_NAMES.VIEWABLES_SET, this.onViewablesSetHandler);
|
|
1423
|
+
// Subscribe to Document Browser open/close events
|
|
1424
|
+
this.eventBus.on(EVENT_NAMES.DOC_BROWSER_OPENED, function () {
|
|
1425
|
+
GLOBAL_STATE.docBrowserShouldBeOpen = true;
|
|
1426
|
+
setTimeout(function () {
|
|
1427
|
+
return _this2.syncDocBrowserButtonState();
|
|
1428
|
+
}, TOOLBAR_REFRESH_INTERVALS.DOM_SETTLE);
|
|
1429
|
+
});
|
|
1430
|
+
this.eventBus.on(EVENT_NAMES.DOC_BROWSER_CLOSED, function () {
|
|
1431
|
+
GLOBAL_STATE.docBrowserShouldBeOpen = false;
|
|
1432
|
+
setTimeout(function () {
|
|
1433
|
+
return _this2.syncDocBrowserButtonState();
|
|
1434
|
+
}, TOOLBAR_REFRESH_INTERVALS.DOM_SETTLE);
|
|
1435
|
+
});
|
|
1436
|
+
// CRITICAL: Restore state from GLOBAL_STATE if available
|
|
1437
|
+
// This handles the case where extension was unloaded and reloaded (e.g., thumbnail navigation)
|
|
1438
|
+
if (GLOBAL_STATE.viewables && GLOBAL_STATE.viewables.length > 0) {
|
|
1439
|
+
// Restore viewables to pagination manager
|
|
1440
|
+
this._pendingViewables = GLOBAL_STATE.viewables;
|
|
1441
|
+
// Restore Document Browser state
|
|
1442
|
+
if (GLOBAL_STATE.docBrowserShouldBeOpen) {
|
|
1443
|
+
this.docBrowserManager.setShouldRemainOpen(true);
|
|
1444
|
+
// CRITICAL: Sync button state immediately and repeatedly after load
|
|
1445
|
+
// This ensures button is active when extension reloads after using _changeModelFn
|
|
1446
|
+
var syncTimes = [50, 100, 150, 200, 300, 400, 500];
|
|
1447
|
+
syncTimes.forEach(function (delay) {
|
|
1448
|
+
setTimeout(function () {
|
|
1449
|
+
_this2.syncDocBrowserButtonState();
|
|
1450
|
+
}, delay);
|
|
1451
|
+
});
|
|
1452
|
+
}
|
|
454
1453
|
}
|
|
1454
|
+
return true;
|
|
455
1455
|
};
|
|
456
|
-
ToolbarExtension.prototype.
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
} catch (e) {
|
|
465
|
-
// Fallback: click the original pan button
|
|
466
|
-
var originalPanBtn = document.getElementById('toolbar-panTool');
|
|
467
|
-
if (originalPanBtn) {
|
|
468
|
-
originalPanBtn.click();
|
|
469
|
-
}
|
|
1456
|
+
ToolbarExtension.prototype.unload = function () {
|
|
1457
|
+
// CRITICAL: Save state to GLOBAL_STATE before unloading
|
|
1458
|
+
// This allows state to persist across unload/reload cycles (e.g., thumbnail navigation)
|
|
1459
|
+
var totalPages = this.paginationManager.getTotalPages();
|
|
1460
|
+
if (totalPages > 0) {
|
|
1461
|
+
GLOBAL_STATE.viewables = this.paginationManager.getViewables();
|
|
1462
|
+
GLOBAL_STATE.currentIndex = this.paginationManager.getCurrentPage();
|
|
1463
|
+
GLOBAL_STATE.docBrowserShouldBeOpen = this.docBrowserManager.getShouldRemainOpen();
|
|
470
1464
|
}
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
}
|
|
482
|
-
}
|
|
1465
|
+
// Cleanup event listeners
|
|
1466
|
+
this.viewer.removeEventListener(Autodesk.Viewing.TOOLBAR_CREATED_EVENT, this.onToolbarCreatedHandler);
|
|
1467
|
+
this.viewer.removeEventListener(Autodesk.Viewing.GEOMETRY_LOADED_EVENT, this.onGeometryLoadedHandler);
|
|
1468
|
+
// Unsubscribe from EventBus
|
|
1469
|
+
this.eventBus.off(EVENT_NAMES.PAGE_CHANGED, this.onPageChangedHandler);
|
|
1470
|
+
this.eventBus.off(EVENT_NAMES.VIEWABLES_SET, this.onViewablesSetHandler);
|
|
1471
|
+
// Cleanup intervals
|
|
1472
|
+
if (this._toolbarInterval) {
|
|
1473
|
+
clearInterval(this._toolbarInterval);
|
|
1474
|
+
this._toolbarInterval = null;
|
|
483
1475
|
}
|
|
1476
|
+
return true;
|
|
484
1477
|
};
|
|
485
|
-
|
|
1478
|
+
/**
|
|
1479
|
+
* Called when toolbar is created
|
|
1480
|
+
* Delegates to managers for setup
|
|
1481
|
+
*/
|
|
1482
|
+
ToolbarExtension.prototype.onToolbarCreated = function () {
|
|
486
1483
|
var _this3 = this;
|
|
1484
|
+
// Initial toolbar setup
|
|
487
1485
|
this.refreshToolbar();
|
|
1486
|
+
// Add listener for geometry loaded to refresh toolbar
|
|
488
1487
|
this.viewer.addEventListener(Autodesk.Viewing.GEOMETRY_LOADED_EVENT, function () {
|
|
489
1488
|
setTimeout(function () {
|
|
490
1489
|
_this3.refreshToolbar();
|
|
491
|
-
|
|
492
|
-
}, 100);
|
|
1490
|
+
}, TOOLBAR_REFRESH_INTERVALS.GEOMETRY_SETTLE);
|
|
493
1491
|
});
|
|
1492
|
+
// Add listener for toolbar created event
|
|
494
1493
|
this.viewer.addEventListener(Autodesk.Viewing.TOOLBAR_CREATED_EVENT, function () {
|
|
495
1494
|
setTimeout(function () {
|
|
496
1495
|
_this3.refreshToolbar();
|
|
497
|
-
},
|
|
1496
|
+
}, TOOLBAR_REFRESH_INTERVALS.BUTTON_STATE_CHECK);
|
|
498
1497
|
});
|
|
499
|
-
|
|
500
|
-
this.
|
|
501
|
-
if (_this3.viewer && _this3.viewer.toolbar) {
|
|
502
|
-
var toolGroup = _this3.viewer.toolbar.getControl('custom-tool-group');
|
|
503
|
-
var pagGroup = _this3.viewer.toolbar.getControl('custom-pagination-group');
|
|
504
|
-
if (!toolGroup || !pagGroup) {
|
|
505
|
-
_this3.refreshToolbar();
|
|
506
|
-
}
|
|
507
|
-
_this3.checkButtonState();
|
|
508
|
-
}
|
|
509
|
-
}, 200);
|
|
510
|
-
};
|
|
511
|
-
ToolbarExtension.prototype.checkButtonState = function () {
|
|
512
|
-
var viewer = this.viewer;
|
|
513
|
-
if (!viewer.toolbar) return;
|
|
514
|
-
var toolGroup = viewer.toolbar.getControl('custom-tool-group');
|
|
515
|
-
if (!toolGroup) return;
|
|
516
|
-
|
|
517
|
-
// Check Document Browser button state
|
|
518
|
-
var docBtn = toolGroup.getControl('custom-doc-browser-btn');
|
|
519
|
-
if (docBtn) {
|
|
520
|
-
var ext = viewer.getExtension('Autodesk.DocumentBrowser');
|
|
521
|
-
var isVisible = ext && ext.ui && ext.ui.panel && ext.ui.panel.isVisible();
|
|
522
|
-
var newState = isVisible ? Autodesk.Viewing.UI.Button.State.ACTIVE : Autodesk.Viewing.UI.Button.State.INACTIVE;
|
|
523
|
-
if (docBtn.getState() !== newState) {
|
|
524
|
-
docBtn.setState(newState);
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
// Force CSS class update to ensure visual state is correct
|
|
528
|
-
if (docBtn.container) {
|
|
529
|
-
if (isVisible) {
|
|
530
|
-
docBtn.container.classList.add('active');
|
|
531
|
-
docBtn.container.classList.remove('inactive');
|
|
532
|
-
} else {
|
|
533
|
-
docBtn.container.classList.remove('active');
|
|
534
|
-
docBtn.container.classList.add('inactive');
|
|
535
|
-
}
|
|
536
|
-
}
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
// Check Pan button state - ensure it stays active
|
|
540
|
-
var panBtn = toolGroup.getControl('custom-pan-btn');
|
|
541
|
-
if (panBtn) {
|
|
542
|
-
// Check if pan tool is the active tool
|
|
543
|
-
var activeTool = viewer.getActiveNavigationTool();
|
|
544
|
-
var isPanActive = activeTool === 'pan';
|
|
545
|
-
if (isPanActive) {
|
|
546
|
-
panBtn.setState(Autodesk.Viewing.UI.Button.State.ACTIVE);
|
|
547
|
-
if (panBtn.container) {
|
|
548
|
-
panBtn.container.classList.add('active');
|
|
549
|
-
panBtn.container.classList.remove('inactive');
|
|
550
|
-
}
|
|
551
|
-
}
|
|
552
|
-
}
|
|
553
|
-
};
|
|
554
|
-
var originalUnload = ToolbarExtension.prototype.unload;
|
|
555
|
-
ToolbarExtension.prototype.unload = function () {
|
|
556
|
-
if (this._toolbarInterval) {
|
|
557
|
-
clearInterval(this._toolbarInterval);
|
|
558
|
-
this._toolbarInterval = null;
|
|
559
|
-
}
|
|
560
|
-
return originalUnload.call(this);
|
|
1498
|
+
// Start toolbar healing mechanism (also handles button state sync)
|
|
1499
|
+
this.startToolbarHealing();
|
|
561
1500
|
};
|
|
1501
|
+
/**
|
|
1502
|
+
* Refresh toolbar - recreate if needed
|
|
1503
|
+
* This is the core method that handles toolbar persistence
|
|
1504
|
+
*/
|
|
562
1505
|
ToolbarExtension.prototype.refreshToolbar = function () {
|
|
563
|
-
var
|
|
564
|
-
var toolbar = viewer.toolbar;
|
|
1506
|
+
var _this4 = this;
|
|
1507
|
+
var toolbar = this.viewer.toolbar;
|
|
565
1508
|
if (!toolbar) return;
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
if (group) {
|
|
570
|
-
group.setVisible(false);
|
|
571
|
-
if (group.container) {
|
|
572
|
-
group.container.style.display = 'none';
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
|
-
});
|
|
1509
|
+
// Hide default groups
|
|
1510
|
+
this.toolbarManager.hideDefaultGroups();
|
|
1511
|
+
// Check and recreate tool group if needed
|
|
576
1512
|
var toolGroup = toolbar.getControl('custom-tool-group');
|
|
577
|
-
var pagGroup = toolbar.getControl('custom-pagination-group');
|
|
578
1513
|
if (!toolGroup) {
|
|
579
|
-
|
|
580
|
-
this.subToolbar = null;
|
|
581
|
-
}
|
|
582
|
-
this.createToolGroup(toolbar);
|
|
1514
|
+
this.toolbarManager.createToolbar();
|
|
583
1515
|
} else {
|
|
584
1516
|
toolGroup.setVisible(true);
|
|
585
|
-
this.subToolbar = toolGroup;
|
|
586
1517
|
}
|
|
1518
|
+
// Check and recreate pagination group if needed
|
|
1519
|
+
var pagGroup = toolbar.getControl('custom-pagination-group');
|
|
587
1520
|
if (!pagGroup) {
|
|
588
|
-
|
|
589
|
-
this.paginationGroup = null;
|
|
590
|
-
}
|
|
591
|
-
this.createPaginationGroup(toolbar);
|
|
1521
|
+
this.paginationManager.createPaginationGroup(toolbar);
|
|
592
1522
|
} else {
|
|
593
|
-
//
|
|
594
|
-
this.
|
|
1523
|
+
// Ensure pagination manager has correct reference to existing group
|
|
1524
|
+
this.paginationManager.setPaginationGroup(pagGroup);
|
|
1525
|
+
pagGroup.setVisible(true);
|
|
1526
|
+
}
|
|
1527
|
+
// Mark toolbar as ready
|
|
1528
|
+
this._toolbarReady = true;
|
|
1529
|
+
// Process pending viewables if any
|
|
1530
|
+
if (this._pendingViewables) {
|
|
1531
|
+
this.paginationManager.setViewables(this._pendingViewables);
|
|
1532
|
+
if (this._pendingViewables.length > 1) {
|
|
1533
|
+
this.docBrowserManager.autoOpenIfMultiPage(true);
|
|
1534
|
+
}
|
|
1535
|
+
this._pendingViewables = null;
|
|
595
1536
|
}
|
|
1537
|
+
// Show toolbar
|
|
596
1538
|
toolbar.setVisible(true);
|
|
597
|
-
|
|
1539
|
+
// ALWAYS update pagination state after toolbar refresh
|
|
1540
|
+
// Use multiple timeouts to ensure it sticks
|
|
1541
|
+
var updateTimes = [10, 30, 50, 100];
|
|
1542
|
+
updateTimes.forEach(function (delay) {
|
|
1543
|
+
setTimeout(function () {
|
|
1544
|
+
_this4.paginationManager.updatePaginationState();
|
|
1545
|
+
}, delay);
|
|
1546
|
+
});
|
|
1547
|
+
// CRITICAL: Sync Document Browser button state after toolbar refresh
|
|
1548
|
+
// This ensures button is active if panel is open (especially after reload)
|
|
1549
|
+
var syncTimes = [50, 100, 150, 200];
|
|
1550
|
+
syncTimes.forEach(function (delay) {
|
|
1551
|
+
setTimeout(function () {
|
|
1552
|
+
var isOpen = _this4.docBrowserManager.isOpen();
|
|
1553
|
+
if (isOpen) {
|
|
1554
|
+
_this4.syncDocBrowserButtonState();
|
|
1555
|
+
}
|
|
1556
|
+
}, delay);
|
|
1557
|
+
});
|
|
1558
|
+
// Activate pan tool
|
|
1559
|
+
this.toolbarManager.activatePanButton();
|
|
598
1560
|
};
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
var
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
1561
|
+
/**
|
|
1562
|
+
* Called when geometry is loaded
|
|
1563
|
+
* Restores button states
|
|
1564
|
+
*/
|
|
1565
|
+
ToolbarExtension.prototype.onGeometryLoaded = function () {
|
|
1566
|
+
var _this5 = this;
|
|
1567
|
+
setTimeout(function () {
|
|
1568
|
+
_this5.toolbarManager.activatePanButton();
|
|
1569
|
+
_this5.paginationManager.updatePaginationState();
|
|
1570
|
+
}, TOOLBAR_REFRESH_INTERVALS.GEOMETRY_SETTLE);
|
|
1571
|
+
};
|
|
1572
|
+
/**
|
|
1573
|
+
* Called when page changes
|
|
1574
|
+
* Restores Document Browser and toolbar states
|
|
1575
|
+
*/
|
|
1576
|
+
ToolbarExtension.prototype.onPageChanged = function (data) {
|
|
1577
|
+
var _this6 = this;
|
|
1578
|
+
if (!data || data.test) {
|
|
1579
|
+
return;
|
|
1580
|
+
}
|
|
1581
|
+
// Mark that page change is in progress to prevent premature pagination updates
|
|
1582
|
+
this._isPageChanging = true;
|
|
1583
|
+
// CRITICAL FIX: If page changed from Document Browser (thumbnails), sync button state IMMEDIATELY
|
|
1584
|
+
// This ensures the button is active when user navigates via thumbnails
|
|
1585
|
+
if (data.fromDocBrowser) {
|
|
1586
|
+
// Sync button state right away (before any other operations)
|
|
1587
|
+
this.syncDocBrowserButtonState();
|
|
1588
|
+
// Force recreate pagination group to ensure display updates
|
|
1589
|
+
var toolbar = this.viewer.toolbar;
|
|
1590
|
+
if (toolbar) {
|
|
1591
|
+
var pagGroup = toolbar.getControl('custom-pagination-group');
|
|
1592
|
+
if (pagGroup) {
|
|
1593
|
+
toolbar.removeControl(pagGroup);
|
|
1594
|
+
this.paginationManager.setPaginationGroup(null);
|
|
1595
|
+
}
|
|
617
1596
|
}
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
this.
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
if (ext && ext.ui) {
|
|
628
|
-
ext.ui.togglePanel();
|
|
629
|
-
// Update flag based on new state
|
|
1597
|
+
}
|
|
1598
|
+
// Refresh toolbar to ensure both tool and pagination groups are restored
|
|
1599
|
+
this.refreshToolbar();
|
|
1600
|
+
// Restore Document Browser panel if needed
|
|
1601
|
+
if (data.shouldRestoreDocBrowser) {
|
|
1602
|
+
this.docBrowserManager.restoreState();
|
|
1603
|
+
// Force sync button state multiple times after restore
|
|
1604
|
+
var syncDelays = data.fromDocBrowser ? [10, 30, 50, 80, 100, 150, 200, 250, 300] : [50, 100, 150, 200, 250, 300];
|
|
1605
|
+
syncDelays.forEach(function (delay) {
|
|
630
1606
|
setTimeout(function () {
|
|
631
|
-
|
|
632
|
-
},
|
|
1607
|
+
_this6.syncDocBrowserButtonState();
|
|
1608
|
+
}, delay);
|
|
1609
|
+
});
|
|
1610
|
+
}
|
|
1611
|
+
// Sync Document Browser button state and update pagination display at multiple intervals
|
|
1612
|
+
var syncIntervals = data.fromDocBrowser ? [10, 30, 50, 80, 100, 150, 200, 250, 300] : [50, 100, 150, 200, 250, 300];
|
|
1613
|
+
syncIntervals.forEach(function (delay, index) {
|
|
1614
|
+
setTimeout(function () {
|
|
1615
|
+
_this6.syncDocBrowserButtonState();
|
|
1616
|
+
_this6.paginationManager.updatePaginationState();
|
|
1617
|
+
if (index === 0) {
|
|
1618
|
+
_this6._isPageChanging = false;
|
|
1619
|
+
}
|
|
1620
|
+
}, delay);
|
|
1621
|
+
});
|
|
1622
|
+
// Additional pagination updates at various intervals to handle race conditions
|
|
1623
|
+
var updateIntervals = data.fromDocBrowser ? [60, 100, 120, 150, 180, 200, 250, 300, 350, 400, 500] : [100, 150, 200, 300];
|
|
1624
|
+
updateIntervals.forEach(function (delay) {
|
|
1625
|
+
setTimeout(function () {
|
|
1626
|
+
_this6.paginationManager.updatePaginationState();
|
|
1627
|
+
}, delay);
|
|
1628
|
+
});
|
|
1629
|
+
};
|
|
1630
|
+
/**
|
|
1631
|
+
* Called when viewables are set (from FileLoader)
|
|
1632
|
+
* Sets viewables to pagination manager and auto-opens Document Browser
|
|
1633
|
+
*/
|
|
1634
|
+
ToolbarExtension.prototype.onViewablesSet = function (data) {
|
|
1635
|
+
if (data && data.viewables) {
|
|
1636
|
+
// CRITICAL: Save viewables to GLOBAL_STATE for persistence across unload/reload
|
|
1637
|
+
GLOBAL_STATE.viewables = data.viewables;
|
|
1638
|
+
// Check if toolbar is ready
|
|
1639
|
+
if (this._toolbarReady) {
|
|
1640
|
+
// Toolbar ready - set viewables immediately
|
|
1641
|
+
this.paginationManager.setViewables(data.viewables);
|
|
1642
|
+
// Auto-open Document Browser if multi-page
|
|
1643
|
+
if (data.viewables.length > 1) {
|
|
1644
|
+
GLOBAL_STATE.docBrowserShouldBeOpen = true;
|
|
1645
|
+
this.docBrowserManager.autoOpenIfMultiPage(true);
|
|
1646
|
+
}
|
|
1647
|
+
} else {
|
|
1648
|
+
// Toolbar not ready yet - store viewables for later
|
|
1649
|
+
this._pendingViewables = data.viewables;
|
|
633
1650
|
}
|
|
634
|
-
}
|
|
635
|
-
this.subToolbar.addControl(docBrowserBtn);
|
|
636
|
-
var downloadBtn = new Autodesk.Viewing.UI.Button('custom-download-btn');
|
|
637
|
-
downloadBtn.setIcon('adsk-icon-custom-download');
|
|
638
|
-
downloadBtn.setToolTip('Download File');
|
|
639
|
-
downloadBtn.onClick = /*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee2() {
|
|
640
|
-
var fileUrl, response, blob, urlPath, filename, blobUrl, anchor, _t2;
|
|
641
|
-
return _regenerator().w(function (_context2) {
|
|
642
|
-
while (1) switch (_context2.p = _context2.n) {
|
|
643
|
-
case 0:
|
|
644
|
-
if (!(_this4.options && _this4.options.filePath)) {
|
|
645
|
-
_context2.n = 7;
|
|
646
|
-
break;
|
|
647
|
-
}
|
|
648
|
-
_context2.p = 1;
|
|
649
|
-
fileUrl = _this4.options.filePath;
|
|
650
|
-
_context2.n = 2;
|
|
651
|
-
return fetch(fileUrl);
|
|
652
|
-
case 2:
|
|
653
|
-
response = _context2.v;
|
|
654
|
-
if (response.ok) {
|
|
655
|
-
_context2.n = 3;
|
|
656
|
-
break;
|
|
657
|
-
}
|
|
658
|
-
throw new Error("HTTP error! status: ".concat(response.status));
|
|
659
|
-
case 3:
|
|
660
|
-
_context2.n = 4;
|
|
661
|
-
return response.blob();
|
|
662
|
-
case 4:
|
|
663
|
-
blob = _context2.v;
|
|
664
|
-
urlPath = new URL(fileUrl).pathname;
|
|
665
|
-
filename = decodeURIComponent(urlPath.split('/').pop()) || 'document.pdf';
|
|
666
|
-
blobUrl = URL.createObjectURL(blob);
|
|
667
|
-
anchor = document.createElement('a');
|
|
668
|
-
anchor.href = blobUrl;
|
|
669
|
-
anchor.download = filename;
|
|
670
|
-
anchor.style.display = 'none';
|
|
671
|
-
document.body.appendChild(anchor);
|
|
672
|
-
anchor.click();
|
|
673
|
-
document.body.removeChild(anchor);
|
|
674
|
-
URL.revokeObjectURL(blobUrl);
|
|
675
|
-
_context2.n = 6;
|
|
676
|
-
break;
|
|
677
|
-
case 5:
|
|
678
|
-
_context2.p = 5;
|
|
679
|
-
_t2 = _context2.v;
|
|
680
|
-
console.error('Download error:', _t2);
|
|
681
|
-
alert('Unable to download file: ' + _t2.message);
|
|
682
|
-
case 6:
|
|
683
|
-
_context2.n = 8;
|
|
684
|
-
break;
|
|
685
|
-
case 7:
|
|
686
|
-
console.warn('FilePath not available in extension options');
|
|
687
|
-
alert('File path not available for download');
|
|
688
|
-
case 8:
|
|
689
|
-
return _context2.a(2);
|
|
690
|
-
}
|
|
691
|
-
}, _callee2, null, [[1, 5]]);
|
|
692
|
-
}));
|
|
693
|
-
this.subToolbar.addControl(downloadBtn);
|
|
1651
|
+
}
|
|
694
1652
|
};
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
1653
|
+
/**
|
|
1654
|
+
* Start toolbar healing mechanism
|
|
1655
|
+
* Polls every 8ms to check if toolbar needs recreation
|
|
1656
|
+
*/
|
|
1657
|
+
ToolbarExtension.prototype.startToolbarHealing = function () {
|
|
1658
|
+
var _this7 = this;
|
|
1659
|
+
if (this._toolbarInterval) {
|
|
1660
|
+
clearInterval(this._toolbarInterval);
|
|
1661
|
+
}
|
|
1662
|
+
// Store last known Document Browser state to detect changes
|
|
1663
|
+
this._lastDocBrowserState = null;
|
|
1664
|
+
this._healingCounter = 0;
|
|
1665
|
+
this._toolbarInterval = setInterval(function () {
|
|
1666
|
+
if (_this7.viewer && _this7.viewer.toolbar) {
|
|
1667
|
+
_this7._healingCounter++;
|
|
1668
|
+
var toolbar = _this7.viewer.toolbar;
|
|
1669
|
+
var toolGroup = toolbar.getControl('custom-tool-group');
|
|
1670
|
+
var pagGroup = toolbar.getControl('custom-pagination-group');
|
|
1671
|
+
// Refresh toolbar if either group is missing
|
|
1672
|
+
if (!toolGroup || !pagGroup) {
|
|
1673
|
+
_this7.refreshToolbar();
|
|
1674
|
+
}
|
|
1675
|
+
// Check if Document Browser panel state changed
|
|
1676
|
+
var currentDocBrowserState = _this7.docBrowserManager.isOpen();
|
|
1677
|
+
if (_this7._lastDocBrowserState !== null && _this7._lastDocBrowserState !== currentDocBrowserState) {
|
|
1678
|
+
// Update shouldRemainOpen flag based on current state
|
|
1679
|
+
if (currentDocBrowserState) {
|
|
1680
|
+
_this7.docBrowserManager.setShouldRemainOpen(true);
|
|
1681
|
+
}
|
|
1682
|
+
// Sync button state immediately when state changes
|
|
1683
|
+
_this7.syncDocBrowserButtonState();
|
|
1684
|
+
}
|
|
1685
|
+
_this7._lastDocBrowserState = currentDocBrowserState;
|
|
1686
|
+
// CRITICAL: Sync button state periodically (every ~50ms = every 6 healing cycles)
|
|
1687
|
+
// This catches thumbnail navigation where panel stays open but button might lose active state
|
|
1688
|
+
if (_this7._healingCounter % 6 === 0) {
|
|
1689
|
+
_this7.syncDocBrowserButtonState();
|
|
1690
|
+
}
|
|
1691
|
+
// ADDITIONAL: If panel is currently open, ensure button is always active
|
|
1692
|
+
// This is the ultimate fallback to handle thumbnail navigation
|
|
1693
|
+
if (currentDocBrowserState && _this7._healingCounter % 3 === 0) {
|
|
1694
|
+
// Check button state and force sync if needed (every ~24ms = every 3 healing cycles)
|
|
1695
|
+
var _toolGroup = toolbar.getControl('custom-tool-group');
|
|
1696
|
+
if (_toolGroup) {
|
|
1697
|
+
var docBtn = _toolGroup.getControl('custom-doc-browser-btn');
|
|
1698
|
+
if (docBtn) {
|
|
1699
|
+
var currentBtnState = docBtn.getState();
|
|
1700
|
+
var ACTIVE_STATE = _this7.Autodesk.Viewing.UI.Button.State.ACTIVE;
|
|
1701
|
+
// If panel is open but button is not active, force sync
|
|
1702
|
+
if (currentBtnState !== ACTIVE_STATE) {
|
|
1703
|
+
_this7.syncDocBrowserButtonState();
|
|
1704
|
+
}
|
|
1705
|
+
}
|
|
1706
|
+
}
|
|
1707
|
+
}
|
|
707
1708
|
}
|
|
708
|
-
};
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
1709
|
+
}, TOOLBAR_REFRESH_INTERVALS.HEALING_POLL);
|
|
1710
|
+
};
|
|
1711
|
+
/**
|
|
1712
|
+
* Start button state synchronization
|
|
1713
|
+
* Polls to keep custom button states in sync with actual states
|
|
1714
|
+
*/
|
|
1715
|
+
ToolbarExtension.prototype.startButtonStateSync = function () {
|
|
1716
|
+
var _this8 = this;
|
|
1717
|
+
if (this._buttonStateInterval) {
|
|
1718
|
+
clearInterval(this._buttonStateInterval);
|
|
1719
|
+
}
|
|
1720
|
+
this._buttonStateInterval = setInterval(function () {
|
|
1721
|
+
_this8.syncDocBrowserButtonState();
|
|
1722
|
+
}, TOOLBAR_REFRESH_INTERVALS.BUTTON_STATE_CHECK);
|
|
1723
|
+
};
|
|
1724
|
+
/**
|
|
1725
|
+
* Set Document Browser button active state
|
|
1726
|
+
*/
|
|
1727
|
+
ToolbarExtension.prototype.setDocBrowserButtonActive = function (isActive) {
|
|
1728
|
+
var toolbar = this.viewer.toolbar;
|
|
1729
|
+
if (!toolbar) return;
|
|
1730
|
+
var toolGroup = toolbar.getControl('custom-tool-group');
|
|
1731
|
+
if (!toolGroup) return;
|
|
1732
|
+
var docBtn = toolGroup.getControl(CUSTOM_TOOLBAR_BUTTONS.DOC_BROWSER.id);
|
|
1733
|
+
if (!docBtn) return;
|
|
1734
|
+
var newState = isActive ? Autodesk.Viewing.UI.Button.State.ACTIVE : Autodesk.Viewing.UI.Button.State.INACTIVE;
|
|
1735
|
+
docBtn.setState(newState);
|
|
1736
|
+
};
|
|
1737
|
+
/**
|
|
1738
|
+
* Sync Document Browser button state with panel visibility
|
|
1739
|
+
*/
|
|
1740
|
+
ToolbarExtension.prototype.syncDocBrowserButtonState = function () {
|
|
1741
|
+
var toolbar = this.viewer.toolbar;
|
|
1742
|
+
if (!toolbar) return;
|
|
1743
|
+
var toolGroup = toolbar.getControl('custom-tool-group');
|
|
1744
|
+
if (!toolGroup) return;
|
|
1745
|
+
var docBtn = toolGroup.getControl(CUSTOM_TOOLBAR_BUTTONS.DOC_BROWSER.id);
|
|
1746
|
+
if (!docBtn) return;
|
|
1747
|
+
var isOpen = this.docBrowserManager.isOpen();
|
|
1748
|
+
var ACTIVE_STATE = Autodesk.Viewing.UI.Button.State.ACTIVE;
|
|
1749
|
+
var INACTIVE_STATE = Autodesk.Viewing.UI.Button.State.INACTIVE;
|
|
1750
|
+
var expectedState = isOpen ? ACTIVE_STATE : INACTIVE_STATE;
|
|
1751
|
+
// Set button state via API
|
|
1752
|
+
docBtn.setState(expectedState);
|
|
1753
|
+
// Also update DOM class directly to ensure visual state
|
|
1754
|
+
var btnElement = docBtn.container;
|
|
1755
|
+
if (btnElement) {
|
|
1756
|
+
if (isOpen) {
|
|
1757
|
+
btnElement.classList.add('active');
|
|
1758
|
+
btnElement.classList.remove('inactive');
|
|
1759
|
+
} else {
|
|
1760
|
+
btnElement.classList.remove('active');
|
|
1761
|
+
btnElement.classList.add('inactive');
|
|
721
1762
|
}
|
|
722
|
-
}
|
|
723
|
-
|
|
1763
|
+
}
|
|
1764
|
+
};
|
|
1765
|
+
/**
|
|
1766
|
+
* Handle Pan button click
|
|
1767
|
+
*/
|
|
1768
|
+
ToolbarExtension.prototype.handlePanClick = function () {
|
|
1769
|
+
this.toolbarManager.activatePanButton();
|
|
724
1770
|
};
|
|
1771
|
+
/**
|
|
1772
|
+
* Handle Document Browser button click
|
|
1773
|
+
*/
|
|
1774
|
+
ToolbarExtension.prototype.handleDocBrowserClick = function () {
|
|
1775
|
+
var _this9 = this;
|
|
1776
|
+
this.docBrowserManager.togglePanel();
|
|
1777
|
+
// Sync button state with new panel state at multiple intervals
|
|
1778
|
+
[50, 100, 150, 200].forEach(function (delay) {
|
|
1779
|
+
setTimeout(function () {
|
|
1780
|
+
_this9.syncDocBrowserButtonState();
|
|
1781
|
+
}, delay);
|
|
1782
|
+
});
|
|
1783
|
+
};
|
|
1784
|
+
/**
|
|
1785
|
+
* Handle Download button click
|
|
1786
|
+
*/
|
|
1787
|
+
ToolbarExtension.prototype.handleDownloadClick = /*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee() {
|
|
1788
|
+
var _t;
|
|
1789
|
+
return _regenerator().w(function (_context) {
|
|
1790
|
+
while (1) switch (_context.p = _context.n) {
|
|
1791
|
+
case 0:
|
|
1792
|
+
_context.p = 0;
|
|
1793
|
+
_context.n = 1;
|
|
1794
|
+
return this.downloadService.downloadFile(this.options.filePath);
|
|
1795
|
+
case 1:
|
|
1796
|
+
_context.n = 3;
|
|
1797
|
+
break;
|
|
1798
|
+
case 2:
|
|
1799
|
+
_context.p = 2;
|
|
1800
|
+
_t = _context.v;
|
|
1801
|
+
console.error('Download failed:', _t);
|
|
1802
|
+
case 3:
|
|
1803
|
+
return _context.a(2);
|
|
1804
|
+
}
|
|
1805
|
+
}, _callee, this, [[0, 2]]);
|
|
1806
|
+
}));
|
|
1807
|
+
// Register extension
|
|
725
1808
|
Autodesk.Viewing.theExtensionManager.registerExtension('ToolbarExtension', ToolbarExtension);
|
|
726
1809
|
}
|
|
727
1810
|
|
|
@@ -814,156 +1897,110 @@ function styleInject(css, ref) {
|
|
|
814
1897
|
}
|
|
815
1898
|
}
|
|
816
1899
|
|
|
817
|
-
var css_248z$1 = "#navTools,\n#modelTools,\n#settingsTools,\n#measureTools {\n display: none !important;\n visibility: hidden !important;\n}\n#guiviewer3d-toolbar {\n display: flex !important;\n align-items: center !important;\n justify-content: center !important;\n gap: 20px;\n position: fixed !important;\n bottom: 10px !important;\n left: 50% !important;\n transform: translateX(-50%) !important;\n width: auto !important;\n border-radius: 4px !important;\n padding: 8px 12px !important;\n}\n#custom-tool-group {\n display: flex !important;\n margin: 0 !important;\n padding: 0 !important;\n}\n#custom-pagination-group {\n display: flex;\n position: relative !important;\n margin: 0 !important;\n padding: 0 !important;\n transform: none !important;\n left: auto !important;\n}\n
|
|
1900
|
+
var css_248z$1 = "#navTools,\n#modelTools,\n#settingsTools,\n#measureTools {\n display: none !important;\n visibility: hidden !important;\n}\n#guiviewer3d-toolbar {\n display: flex !important;\n align-items: center !important;\n justify-content: center !important;\n gap: 20px;\n position: fixed !important;\n bottom: 10px !important;\n left: 50% !important;\n transform: translateX(-50%) !important;\n width: auto !important;\n border-radius: 4px !important;\n padding: 8px 12px !important;\n}\n#custom-tool-group {\n display: flex !important;\n margin: 0 !important;\n padding: 0 !important;\n}\n#custom-pagination-group {\n display: flex;\n position: relative !important;\n margin: 0 !important;\n padding: 0 !important;\n transform: none !important;\n left: auto !important;\n}\n";
|
|
818
1901
|
styleInject(css_248z$1);
|
|
819
1902
|
|
|
820
|
-
var css_248z = "/* Custom icon styles for toolbar using SVG */\n
|
|
1903
|
+
var css_248z = "/* Custom icon styles for toolbar using SVG */\n/* Download icon - custom SVG (white, thicker stroke) */\n.adsk-icon-custom-download::before {\n content: '';\n display: inline-block;\n width: 24px;\n height: 24px;\n background-image: url('data:image/svg+xml;utf8,\\\n<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"%23ffffff\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\\\n <path d=\"M4.75 17.25a.75.75 0 0 1 .75.75v2.25c0 .138.112.25.25.25h12.5a.25.25 0 0 0 .25-.25V18a.75.75 0 0 1 1.5 0v2.25A1.75 1.75 0 0 1 18.25 22H5.75A1.75 1.75 0 0 1 4 20.25V18a.75.75 0 0 1 .75-.75Z\"/>\\\n <path d=\"M5.22 9.97a.749.749 0 0 1 1.06 0l4.97 4.969V2.75a.75.75 0 0 1 1.5 0v12.189l4.97-4.969a.749.749 0 1 1 1.06 1.06l-6.25 6.25a.749.749 0 0 1-1.06 0l-6.25-6.25a.749.749 0 0 1 0-1.06Z\"/>\\\n</svg>');\n background-repeat: no-repeat;\n background-position: center;\n background-size: 16px 16px;\n}\n/* Previous page icon - custom SVG (white, filled) */\n.adsk-icon-custom-prev::before {\n content: '';\n display: inline-block;\n width: 24px;\n height: 24px;\n background-image: url('data:image/svg+xml;utf8,\\\n<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 1024 1024\">\\\n<g transform=\"translate(512 512) scale(1.2) translate(-512 -512)\">\\\n<path fill=\"%23FFFFFF\" d=\"M685.248 104.704a64 64 0 010 90.496L368.448 512l316.8 316.8a64 64 0 01-90.496 90.496L232.704 557.248a64 64 0 010-90.496l362.048-362.048a64 64 0 0190.496 0z\"/>\\\n</g>\\\n</svg>');\n background-repeat: no-repeat;\n background-position: center;\n background-size: 16px 16px;\n}\n/* Next page icon - custom SVG (white, filled) */\n.adsk-icon-custom-next::before {\n content: '';\n display: inline-block;\n width: 24px;\n height: 24px;\n background-image: url('data:image/svg+xml;utf8,\\\n<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 1024 1024\">\\\n<g transform=\"translate(512 512) scale(1.2) translate(-512 -512)\">\\\n<path fill=\"%23FFFFFF\" d=\"M338.752 104.704a64 64 0 000 90.496l316.8 316.8-316.8 316.8a64 64 0 0090.496 90.496l362.048-362.048a64 64 0 000-90.496L429.248 104.704a64 64 0 00-90.496 0z\"/>\\\n</g>\\\n</svg>');\n background-repeat: no-repeat;\n background-position: center;\n background-size: 16px 16px;\n}\n/* Fallback for adsk-icon-caret-left and adsk-icon-caret-right if not defined */\n.adsk-icon-caret-left::before {\n content: '[';\n font-family: 'adsk-viewing';\n}\n.adsk-icon-caret-right::before {\n content: ']';\n font-family: 'adsk-viewing';\n}\n/* Comment icon - custom SVG (Smiley) */\n.adsk-icon-custom-comment::before {\n content: '';\n display: inline-block;\n width: 24px;\n height: 24px;\n background-image: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"%23FFFFFF\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"10\"></circle><path d=\"M8 14s1.5 2 4 2 4-2 4-2\"></path><line x1=\"9\" y1=\"9\" x2=\"9.01\" y2=\"9\"></line><line x1=\"15\" y1=\"9\" x2=\"15.01\" y2=\"9\"></line></svg>');\n background-repeat: no-repeat;\n background-position: center;\n background-size: contain;\n}\n/* Save icon - custom SVG */\n.adsk-icon-custom-save::before {\n content: '';\n display: inline-block;\n width: 24px;\n height: 24px;\n background-image: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"%23FFFFFF\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z\"></path><polyline points=\"17 21 17 13 7 13 7 21\"></polyline><polyline points=\"7 3 7 8 15 8\"></polyline></svg>');\n background-repeat: no-repeat;\n background-position: center;\n background-size: contain;\n}\n";
|
|
821
1904
|
styleInject(css_248z);
|
|
822
1905
|
|
|
823
1906
|
var ViewerForgePDF = function ViewerForgePDF(_ref) {
|
|
824
1907
|
var filePath = _ref.filePath,
|
|
825
1908
|
fileExt = _ref.fileExt,
|
|
826
1909
|
setViewer = _ref.setViewer;
|
|
827
|
-
|
|
828
|
-
|
|
1910
|
+
// Load Forge Viewer CSS and JS from CDN
|
|
1911
|
+
useCss(FORGE_STYLE_URL);
|
|
1912
|
+
var status = useScript(FORGE_SCRIPT_URL);
|
|
829
1913
|
react.useEffect(function () {
|
|
830
|
-
if (status
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
return
|
|
835
|
-
|
|
836
|
-
var validExts = ['pdf', 'dwf', 'dwfx'];
|
|
837
|
-
if (!validExts.includes(fileExt.toLowerCase())) {
|
|
838
|
-
antd.message.warning('Only support pdf, dwf, dwfx format');
|
|
839
|
-
return;
|
|
840
|
-
}
|
|
841
|
-
Autodesk.Viewing.Initializer({
|
|
842
|
-
env: 'Local'
|
|
843
|
-
}, /*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee() {
|
|
844
|
-
var viewerDiv, viewer, isDWF, handleLoadSuccess, xhr;
|
|
845
|
-
return _regenerator().w(function (_context) {
|
|
846
|
-
while (1) switch (_context.n) {
|
|
1914
|
+
if (status !== 'ready' || !window.Autodesk || !filePath || !fileExt) return;
|
|
1915
|
+
var initializeViewer = /*#__PURE__*/function () {
|
|
1916
|
+
var _ref2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee2() {
|
|
1917
|
+
var Autodesk;
|
|
1918
|
+
return _regenerator().w(function (_context2) {
|
|
1919
|
+
while (1) switch (_context2.p = _context2.n) {
|
|
847
1920
|
case 0:
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
viewer.loadExtension('ToolbarExtension', {
|
|
857
|
-
filePath: filePath
|
|
858
|
-
});
|
|
859
|
-
viewer.addEventListener(Autodesk.Viewing.GEOMETRY_LOADED_EVENT, function (e) {
|
|
860
|
-
e.target.loadExtension('Autodesk.Viewing.MarkupsCore');
|
|
861
|
-
e.target.loadExtension('Autodesk.Viewing.MarkupsGui');
|
|
862
|
-
});
|
|
863
|
-
isDWF = fileExt.toLowerCase() === 'pdf' ? 'Autodesk.PDF' : 'Autodesk.DWF';
|
|
864
|
-
_context.n = 1;
|
|
865
|
-
return viewer.loadExtension(isDWF);
|
|
1921
|
+
_context2.p = 0;
|
|
1922
|
+
Autodesk = window.Autodesk; // Validate file extension
|
|
1923
|
+
if (SUPPORTED_FILE_EXTENSIONS.includes(fileExt.toLowerCase())) {
|
|
1924
|
+
_context2.n = 1;
|
|
1925
|
+
break;
|
|
1926
|
+
}
|
|
1927
|
+
antd.message.warning('Only support pdf, dwf, dwfx format');
|
|
1928
|
+
return _context2.a(2);
|
|
866
1929
|
case 1:
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
var toolbarExt = viewer.getExtension('ToolbarExtension');
|
|
883
|
-
if (toolbarExt && typeof toolbarExt.setViewables === 'function') {
|
|
884
|
-
toolbarExt.setViewables(viewables);
|
|
885
|
-
}
|
|
886
|
-
if (viewer.toolbar) {
|
|
887
|
-
viewer.toolbar.setVisible(true);
|
|
888
|
-
}
|
|
889
|
-
if (viewables.length > 1) {
|
|
890
|
-
// Auto-open Document Browser by clicking the original button
|
|
891
|
-
setTimeout(function () {
|
|
892
|
-
var originalDocBtn = document.getElementById('toolbar-documentModels');
|
|
893
|
-
if (originalDocBtn && !originalDocBtn.classList.contains('active')) {
|
|
894
|
-
originalDocBtn.click();
|
|
895
|
-
}
|
|
896
|
-
|
|
897
|
-
// Apply custom styling to the panel
|
|
898
|
-
var documentBrowser = viewer.getExtension('Autodesk.DocumentBrowser');
|
|
899
|
-
if (documentBrowser && documentBrowser.ui && documentBrowser.ui.panel) {
|
|
900
|
-
documentBrowser.ui.panel.container.style.top = '0';
|
|
901
|
-
documentBrowser.ui.panel.container.style.left = 'unset';
|
|
902
|
-
documentBrowser.ui.panel.container.style.right = '0px';
|
|
903
|
-
documentBrowser.ui.panel.container.style.width = '200px';
|
|
904
|
-
documentBrowser.ui.panel.container.style.height = '80%';
|
|
905
|
-
documentBrowser.ui.panel.container.style.minHeight = '400px';
|
|
1930
|
+
// Initialize Forge Viewer
|
|
1931
|
+
Autodesk.Viewing.Initializer({
|
|
1932
|
+
env: 'Local'
|
|
1933
|
+
}, /*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee() {
|
|
1934
|
+
var viewerDiv, viewer, fileLoader;
|
|
1935
|
+
return _regenerator().w(function (_context) {
|
|
1936
|
+
while (1) switch (_context.n) {
|
|
1937
|
+
case 0:
|
|
1938
|
+
// Register custom toolbar extension
|
|
1939
|
+
registerToolbarExtension(Autodesk);
|
|
1940
|
+
// Create viewer instance
|
|
1941
|
+
viewerDiv = document.getElementById('forgeViewerPDF');
|
|
1942
|
+
if (viewerDiv) {
|
|
1943
|
+
_context.n = 1;
|
|
1944
|
+
break;
|
|
906
1945
|
}
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
1946
|
+
return _context.a(2);
|
|
1947
|
+
case 1:
|
|
1948
|
+
viewerDiv.innerHTML = '';
|
|
1949
|
+
viewer = new Autodesk.Viewing.GuiViewer3D(viewerDiv);
|
|
1950
|
+
viewer.start();
|
|
1951
|
+
// Load required extensions
|
|
1952
|
+
_context.n = 2;
|
|
1953
|
+
return viewer.loadExtension('Autodesk.DocumentBrowser');
|
|
1954
|
+
case 2:
|
|
1955
|
+
_context.n = 3;
|
|
1956
|
+
return viewer.loadExtension('Autodesk.Viewing.MarkupsCore');
|
|
1957
|
+
case 3:
|
|
1958
|
+
_context.n = 4;
|
|
1959
|
+
return viewer.loadExtension('Autodesk.Viewing.MarkupsGui');
|
|
1960
|
+
case 4:
|
|
1961
|
+
_context.n = 5;
|
|
1962
|
+
return viewer.loadExtension('ToolbarExtension', {
|
|
1963
|
+
filePath: filePath
|
|
1964
|
+
});
|
|
1965
|
+
case 5:
|
|
1966
|
+
// Initialize FileLoader service
|
|
1967
|
+
fileLoader = new FileLoader(viewer); // Note: ToolbarExtension now subscribes to VIEWABLES_SET directly
|
|
1968
|
+
// No need to forward events here - prevents infinite recursion
|
|
1969
|
+
// Load the document using FileLoader service
|
|
1970
|
+
_context.n = 6;
|
|
1971
|
+
return fileLoader.loadFile(filePath, fileExt.toLowerCase(), {
|
|
1972
|
+
onSuccess: function onSuccess() {
|
|
1973
|
+
// Pass viewer instance to parent component
|
|
1974
|
+
if (setViewer) {
|
|
1975
|
+
setViewer(viewer);
|
|
1976
|
+
}
|
|
1977
|
+
},
|
|
1978
|
+
onError: function onError(error) {
|
|
1979
|
+
antd.message.error('Failed to load document');
|
|
916
1980
|
}
|
|
917
|
-
}
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
// Ensure Custom Pan tool is active by default
|
|
922
|
-
setTimeout(function () {
|
|
923
|
-
var customPanBtn = document.getElementById('custom-pan-btn');
|
|
924
|
-
if (customPanBtn) {
|
|
925
|
-
customPanBtn.click();
|
|
926
|
-
// Double check visual state
|
|
927
|
-
if (!customPanBtn.classList.contains('active')) {
|
|
928
|
-
customPanBtn.classList.add('active');
|
|
929
|
-
customPanBtn.classList.remove('inactive');
|
|
930
|
-
}
|
|
931
|
-
} else {
|
|
932
|
-
var _viewer$toolbar;
|
|
933
|
-
// Fallback if custom button not found immediately, try accessing via viewer toolbar
|
|
934
|
-
var toolGroup = (_viewer$toolbar = viewer.toolbar) === null || _viewer$toolbar === void 0 ? void 0 : _viewer$toolbar.getControl('custom-tool-group');
|
|
935
|
-
var panBtnConf = toolGroup === null || toolGroup === void 0 ? void 0 : toolGroup.getControl('custom-pan-btn');
|
|
936
|
-
if (panBtnConf) {
|
|
937
|
-
panBtnConf.setState(1); // ACTIVE
|
|
938
|
-
}
|
|
939
|
-
}
|
|
940
|
-
}, 600);
|
|
941
|
-
if (setViewer) setViewer(viewer);
|
|
942
|
-
} catch (err) {
|
|
943
|
-
console.error('Error in handleLoadSuccess:', err);
|
|
944
|
-
}
|
|
945
|
-
};
|
|
946
|
-
if (isDWF === 'Autodesk.DWF') {
|
|
947
|
-
xhr = new XMLHttpRequest();
|
|
948
|
-
xhr.open('GET', filePath, true);
|
|
949
|
-
xhr.responseType = 'blob';
|
|
950
|
-
xhr.onload = function () {
|
|
951
|
-
if (this.status === 200) {
|
|
952
|
-
var myBlob = this.response;
|
|
953
|
-
var url1 = window.URL.createObjectURL(myBlob);
|
|
954
|
-
viewer.loadModel(url1 + '#.dwf', {}, handleLoadSuccess);
|
|
1981
|
+
});
|
|
1982
|
+
case 6:
|
|
1983
|
+
return _context.a(2);
|
|
955
1984
|
}
|
|
956
|
-
};
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
}
|
|
1985
|
+
}, _callee);
|
|
1986
|
+
})));
|
|
1987
|
+
_context2.n = 3;
|
|
1988
|
+
break;
|
|
961
1989
|
case 2:
|
|
962
|
-
|
|
1990
|
+
_context2.p = 2;
|
|
1991
|
+
_context2.v;
|
|
1992
|
+
antd.message.error('Failed to initialize viewer');
|
|
1993
|
+
case 3:
|
|
1994
|
+
return _context2.a(2);
|
|
963
1995
|
}
|
|
964
|
-
},
|
|
965
|
-
}))
|
|
966
|
-
|
|
1996
|
+
}, _callee2, null, [[0, 2]]);
|
|
1997
|
+
}));
|
|
1998
|
+
return function initializeViewer() {
|
|
1999
|
+
return _ref2.apply(this, arguments);
|
|
2000
|
+
};
|
|
2001
|
+
}();
|
|
2002
|
+
initializeViewer();
|
|
2003
|
+
// No cleanup needed - ToolbarExtension handles its own subscriptions
|
|
967
2004
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
968
2005
|
}, [status, filePath, fileExt]);
|
|
969
2006
|
return /*#__PURE__*/jsxRuntime.jsx("div", {
|