UNPKG

@itwin/core-frontend

Version:
609 lines 30.5 kB
"use strict"; /*--------------------------------------------------------------------------------------------- * Copyright (c) Bentley Systems, Incorporated. All rights reserved. * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ /** @packageDocumentation * @module IModelApp */ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.IModelApp = exports.ITWINJS_CORE_VERSION = void 0; // @ts-expect-error package.json will resolve from the lib/{cjs,esm} dir without copying it into the build output we deliver // eslint-disable-next-line @itwin/import-within-package const package_json_1 = __importDefault(require("../../package.json")); /** @public */ exports.ITWINJS_CORE_VERSION = package_json_1.default.version; const COPYRIGHT_NOTICE = `Copyright © 2017-${new Date().getFullYear()} <a href="https://www.bentley.com" target="_blank" rel="noopener noreferrer">Bentley Systems, Inc.</a>`; const appui_abstract_1 = require("@itwin/appui-abstract"); const core_bentley_1 = require("@itwin/core-bentley"); const core_common_1 = require("@itwin/core-common"); const core_i18n_1 = require("@itwin/core-i18n"); const webgl_compatibility_1 = require("@itwin/webgl-compatibility"); const AccuDraw_1 = require("./AccuDraw"); const AccuSnap_1 = require("./AccuSnap"); const auxCoordState = __importStar(require("./AuxCoordSys")); const categorySelectorState = __importStar(require("./CategorySelectorState")); const ExtensionAdmin_1 = require("./extension/ExtensionAdmin"); const displayStyleState = __importStar(require("./DisplayStyleState")); const drawingViewState = __importStar(require("./DrawingViewState")); const ElementLocateManager_1 = require("./ElementLocateManager"); const EntityState_1 = require("./EntityState"); const FrontendLoggerCategory_1 = require("./common/FrontendLoggerCategory"); const modelselector = __importStar(require("./ModelSelectorState")); const modelState = __importStar(require("./ModelState")); const NotificationManager_1 = require("./NotificationManager"); const QuantityFormatter_1 = require("./quantity-formatting/QuantityFormatter"); const RenderSystem_1 = require("./render/RenderSystem"); const System_1 = require("./internal/render/webgl/System"); const sheetState = __importStar(require("./SheetViewState")); const spatialViewState = __importStar(require("./SpatialViewState")); const TentativePoint_1 = require("./TentativePoint"); const RealityDataSource_1 = require("./RealityDataSource"); const internal_1 = require("./tile/internal"); const accudrawTool = __importStar(require("./tools/AccuDrawTool")); const clipViewTool = __importStar(require("./tools/ClipViewTool")); const idleTool = __importStar(require("./tools/IdleTool")); const measureTool = __importStar(require("./tools/MeasureTool")); const selectTool = __importStar(require("./tools/SelectTool")); const Tool_1 = require("./tools/Tool"); const ToolAdmin_1 = require("./tools/ToolAdmin"); const viewTool = __importStar(require("./tools/ViewTool")); const ViewManager_1 = require("./ViewManager"); const viewState = __importStar(require("./ViewState")); require("./IModeljs-css"); /** * Global singleton that connects the user interface with the iTwin.js services. There can be only one IModelApp active in a session. All * members of IModelApp are static, and it serves as a singleton object for gaining access to session information. * * Before any interactive operations may be performed by the `@itwin/core-frontend package`, [[IModelApp.startup]] must be called and awaited. * Applications may customize the frontend behavior of iTwin.js by supplying options to [[IModelApp.startup]]. * * @public */ class IModelApp { static _initialized = false; static _accuDraw; static _accuSnap; static _applicationId; static _applicationVersion; static _localization; static _locateManager; static _notifications; static _quantityFormatter; static _renderSystem; static _userPreferences; static _tentativePoint; static _tileAdmin; static _toolAdmin; static _viewManager; static _uiAdmin; static _noRender; static _wantEventLoop = false; static _animationRequested = false; static _animationInterval = core_bentley_1.BeDuration.fromSeconds(1); static _animationIntervalId; static _securityOptions; static _mapLayerFormatRegistry; static _terrainProviderRegistry; static _realityDataSourceProviders; static _hubAccess; static _realityDataAccess; static _publicPath; static _formatsProviderManager; // No instances of IModelApp may be created. All members are static and must be on the singleton object IModelApp. constructor() { } /** Event raised just before the frontend IModelApp is to be [[shutdown]]. */ static onBeforeShutdown = new core_bentley_1.BeEvent(); /** Event raised after IModelApp [[startup]] completes. */ static onAfterStartup = new core_bentley_1.BeEvent(); /** The AuthorizationClient used to obtain [AccessToken]($bentley)s. */ static authorizationClient; /** The [[ToolRegistry]] for this session. */ static tools = new Tool_1.ToolRegistry(); /** A uniqueId for this session */ static sessionId; /** The [[MapLayerFormatRegistry]] for this session. */ static get mapLayerFormatRegistry() { return this._mapLayerFormatRegistry; } /** The [[TerrainProviderRegistry]] for this session. */ static get terrainProviderRegistry() { return this._terrainProviderRegistry; } /** The [[RealityDataSourceProviderRegistry]] for this session. * @beta */ static get realityDataSourceProviders() { return this._realityDataSourceProviders; } /** The [[RenderSystem]] for this session. */ static get renderSystem() { return this._renderSystem; } /** The [[ViewManager]] for this session. */ static get viewManager() { return this._viewManager; } /** The [[NotificationManager]] for this session. */ static get notifications() { return this._notifications; } /** The [[TileAdmin]] for this session. */ static get tileAdmin() { return this._tileAdmin; } /** The [[QuantityFormatter]] for this session. */ static get quantityFormatter() { return this._quantityFormatter; } /** The [[ToolAdmin]] for this session. */ static get toolAdmin() { return this._toolAdmin; } /** The [[AccuDraw]] for this session. */ static get accuDraw() { return this._accuDraw; } /** The [[AccuSnap]] for this session. */ static get accuSnap() { return this._accuSnap; } static get locateManager() { return this._locateManager; } /** The [[TentativePoint]] for this session]]. */ static get tentativePoint() { return this._tentativePoint; } /** The [[Localization]] for this session. */ static get localization() { return this._localization; } /** The [[UserPreferencesAccess]] for this session. * @beta */ static get userPreferences() { return this._userPreferences; } /** The Id of this application. Applications must set this to the Global Product Registry ID (GPRID) for usage logging. */ static get applicationId() { return this._applicationId; } /** The version of this application. Must be set for usage logging. */ static get applicationVersion() { return this._applicationVersion; } /** True after [[startup]] has been called, until [[shutdown]] is called. */ static get initialized() { return this._initialized; } /** Provides access to IModelHub services. */ static get hubAccess() { return this._hubAccess; } /** Provides access to the RealityData service implementation for this IModelApp * @beta */ static get realityDataAccess() { return this._realityDataAccess; } /** Whether the [renderSystem[]] has been successfully initialized. * This will always be `false` before calling [[startup]] and after calling [[shutdown]]. * In rare circumstances (e.g., while executing in a headless test environment) it may remain `false` due to a failure to * obtain a [WebGL rendering context](https://www.google.com/search?channel=fs&client=ubuntu-sn&q=mdn+webglrenderingcontext). * As long as you have called [[startup]], you can generally assume it to be `true`. */ static get hasRenderSystem() { return this._renderSystem !== undefined && this._renderSystem.isValid; } /** The [[UiAdmin]] for this session. */ static get uiAdmin() { return this._uiAdmin; } /** The requested security options for the frontend. */ static get securityOptions() { return this._securityOptions; } /** If present, overrides where public assets are fetched. The default is to fetch assets relative to the current URL. * The path should always end with a trailing `/`. */ static get publicPath() { return this._publicPath; } /** The [[FormatsProvider]] for this session. * @param provider The provider to use for formatting quantities. * @beta */ static get formatsProvider() { return this._formatsProviderManager; } static set formatsProvider(provider) { this._formatsProviderManager.formatsProvider = provider; } /** @alpha */ static extensionAdmin = this._createExtensionAdmin(); /** Map of classFullName to EntityState class */ static _entityClasses = new Map(); /** Register all of the subclasses of EntityState from a module. * @internal */ static registerModuleEntities(moduleObj) { for (const thisMember in moduleObj) { // eslint-disable-line guard-for-in const thisEntityState = moduleObj[thisMember]; if (thisEntityState.prototype instanceof EntityState_1.EntityState) { this.registerEntityState(thisEntityState.classFullName, thisEntityState); } } } /** Register an EntityState class by its classFullName * @internal */ static registerEntityState(classFullName, classType) { const lowerName = classFullName.toLowerCase(); if (this._entityClasses.has(lowerName)) { const errMsg = `Class ${classFullName} is already registered. Make sure static schemaName and className members are correct on class ${classType.name}`; core_bentley_1.Logger.logError(FrontendLoggerCategory_1.FrontendLoggerCategory.IModelConnection, errMsg); throw new Error(errMsg); } this._entityClasses.set(lowerName, classType); } /** @internal */ static lookupEntityClass(classFullName) { return this._entityClasses.get(classFullName.toLowerCase()); } /** * Obtain WebGL rendering compatibility information for the client system. This information describes whether the client meets the * minimum rendering capabilities. It also describes whether the system lacks any optional capabilities that could improve quality * and/or performance. * @note As of 4.x, iTwin.js requires WebGL 2. If the client does not support WebGL 2, the `status` field of the returned compatibility info will be [WebGLRenderCompatibilityStatus.CannotCreateContext]($webgl-compatibility). */ static queryRenderCompatibility() { return (0, webgl_compatibility_1.queryRenderCompatibility)(true, (canvas, useWebGL2, inputContextAttributes) => System_1.System.createContext(canvas, useWebGL2, inputContextAttributes)); } /** * This method must be called before any other `@itwin/core-frontend` methods are used. * Somewhere in your startup code, call [[IModelApp.startup]]. E.g.: * ``` ts * await IModelApp.startup( {applicationId: myAppId} ); * ``` * @param opts The options for configuring IModelApp */ static async startup(opts) { if (this._initialized) return; // we're already initialized, do nothing. this._initialized = true; opts = opts ?? {}; this._securityOptions = opts.security ?? {}; if (process.env.NODE_ENV === "development") { // Make IModelApp globally accessible for debugging purposes. We'll remove it on shutdown. window.iModelAppForDebugger = this; } this.sessionId = opts.sessionId ?? core_bentley_1.Guid.createValue(); this._applicationId = opts.applicationId ?? "2686"; // Default to product id of iTwin.js this._applicationVersion = opts.applicationVersion ?? "1.0.0"; this.authorizationClient = opts.authorizationClient; this._hubAccess = opts.hubAccess; this._noRender = opts.noRender ?? false; this._setupRpcRequestContext(); this._localization = opts.localization ?? new core_i18n_1.ITwinLocalization(); const toolsNs = "CoreTools"; await this.localization.initialize(["iModelJs", toolsNs]); [ selectTool, idleTool, viewTool, clipViewTool, measureTool, accudrawTool, ].forEach((tool) => this.tools.registerModule(tool, toolsNs)); this.registerEntityState(EntityState_1.EntityState.classFullName, EntityState_1.EntityState); [ modelState, sheetState, viewState, drawingViewState, spatialViewState, displayStyleState, modelselector, categorySelectorState, auxCoordState, ].forEach((module) => this.registerModuleEntities(module)); this._renderSystem = (opts.renderSys instanceof RenderSystem_1.RenderSystem) ? opts.renderSys : this.createRenderSys(opts.renderSys); if (opts.userPreferences) this._userPreferences = opts.userPreferences; this._viewManager = opts.viewManager ?? new ViewManager_1.ViewManager(); this._tileAdmin = await internal_1.TileAdmin.create(opts.tileAdmin); this._notifications = opts.notifications ?? new NotificationManager_1.NotificationManager(); this._toolAdmin = opts.toolAdmin ?? new ToolAdmin_1.ToolAdmin(); this._accuDraw = opts.accuDraw ?? new AccuDraw_1.AccuDraw(); this._accuSnap = opts.accuSnap ?? new AccuSnap_1.AccuSnap(); this._locateManager = opts.locateManager ?? new ElementLocateManager_1.ElementLocateManager(); this._tentativePoint = opts.tentativePoint ?? new TentativePoint_1.TentativePoint(); this._quantityFormatter = opts.quantityFormatter ?? new QuantityFormatter_1.QuantityFormatter(); this._uiAdmin = opts.uiAdmin ?? new appui_abstract_1.UiAdmin(); this._mapLayerFormatRegistry = new internal_1.MapLayerFormatRegistry(opts.mapLayerOptions); this._terrainProviderRegistry = new internal_1.TerrainProviderRegistry(); this._realityDataSourceProviders = new RealityDataSource_1.RealityDataSourceProviderRegistry(); this._realityDataAccess = opts.realityDataAccess; this._formatsProviderManager = new QuantityFormatter_1.FormatsProviderManager(opts.formatsProvider ?? new QuantityFormatter_1.QuantityTypeFormatsProvider()); this._publicPath = opts.publicPath ?? ""; [ this.renderSystem, this.viewManager, this.toolAdmin, this.accuDraw, this.accuSnap, this.locateManager, this.tentativePoint, this.uiAdmin, ].forEach((sys) => sys.onInitialized()); await this.quantityFormatter.onInitialized(); this.onAfterStartup.raiseEvent(); } /** Must be called before the application exits to release any held resources. */ static async shutdown() { if (!this._initialized) return; // notify listeners that this IModelApp is about to be shut down. this.onBeforeShutdown.raiseEvent(); this.onBeforeShutdown.clear(); if (process.env.NODE_ENV === "development") { window.iModelAppForDebugger = undefined; } this._wantEventLoop = false; window.removeEventListener("resize", () => IModelApp.requestNextAnimation()); this.clearIntervalAnimation(); [this.toolAdmin, this.viewManager, this.tileAdmin].forEach((sys) => sys.onShutDown()); this.tools.shutdown(); this._renderSystem = (0, core_bentley_1.dispose)(this._renderSystem); this._entityClasses.clear(); this.authorizationClient = undefined; this._initialized = false; this.quantityFormatter[Symbol.dispose](); this.resetFormatsProvider(); this.onAfterStartup.clear(); } /** Controls how frequently the application polls for changes that may require a new animation frame to be requested. * Such changes include resizing a Viewport or changing the device pixel ratio by zooming in or out in the browser. * The default interval is 1 second. It may be desirable to override the default for specific apps and/or devices. * - Increasing the interval can conserve battery life on battery-powered devices at the expense of slower response to resize events. * - An application that only displays a single Viewport whose dimensions only change when the dimensions of the application window change, and which does not support changing application zoom level, could disable the interval altogether. * @param interval The interval at which to poll for changes. If undefined (or negative), the application will never poll. If zero, the application will poll as frequently as possible. * @beta */ static get animationInterval() { return IModelApp._animationInterval; } static set animationInterval(interval) { if (undefined !== interval && interval.isTowardsPast) interval = undefined; if (interval !== IModelApp._animationInterval) { IModelApp._animationInterval = interval; if (IModelApp._wantEventLoop) IModelApp.requestIntervalAnimation(); } } /** Request that the event loop execute on the next [animation frame](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame). * There is generally no reason for applications to invoke this method directly. */ static requestNextAnimation() { // Only want to call requestAnimationFrame if it is defined. Need to check whether current iModelApp is a NoRenderApp. if (IModelApp._noRender) return; if (!IModelApp._animationRequested) { IModelApp._animationRequested = true; requestAnimationFrame(() => IModelApp.eventLoop()); } } /** @internal */ static clearIntervalAnimation() { if (undefined !== IModelApp._animationIntervalId) { window.clearInterval(IModelApp._animationIntervalId); IModelApp._animationIntervalId = undefined; } } /** @internal */ static requestIntervalAnimation() { IModelApp.clearIntervalAnimation(); if (undefined !== IModelApp.animationInterval) IModelApp._animationIntervalId = window.setInterval(() => { IModelApp.requestNextAnimation(); }, IModelApp.animationInterval.milliseconds); } /** @internal */ static startEventLoop() { if (!IModelApp._wantEventLoop) { IModelApp._wantEventLoop = true; window.addEventListener("resize", () => IModelApp.requestNextAnimation()); IModelApp.requestIntervalAnimation(); IModelApp.requestNextAnimation(); } } /** Strictly for tests. @internal */ static stopEventLoop() { this._wantEventLoop = false; } /** The main event processing loop for Tools and rendering. */ static eventLoop() { IModelApp._animationRequested = false; if (!IModelApp._wantEventLoop) // flag turned on at startup return; try { IModelApp.toolAdmin.processEvent(); // eslint-disable-line @typescript-eslint/no-floating-promises IModelApp.viewManager.renderLoop(); IModelApp.tileAdmin.process(); } catch (exception) { ToolAdmin_1.ToolAdmin.exceptionHandler(exception); // eslint-disable-line @typescript-eslint/no-floating-promises IModelApp._wantEventLoop = false; IModelApp._animationRequested = true; // unrecoverable after exception, don't request any further frames. window.removeEventListener("resize", () => IModelApp.requestNextAnimation()); } } /** Get the user's access token for this IModelApp, or a blank string if none is available. * @note Access tokens expire periodically and are automatically refreshed, if possible. Therefore tokens should not be saved, and the value * returned by this method may change over time throughout the course of a session. */ static async getAccessToken() { try { return (await this.authorizationClient?.getAccessToken()) ?? ""; } catch { return ""; } } /** @internal */ static createRenderSys(opts) { return System_1.System.create(opts); } static _setupRpcRequestContext() { core_common_1.RpcConfiguration.requestContext.getId = (_request) => { return core_bentley_1.Guid.createValue(); }; core_common_1.RpcConfiguration.requestContext.serialize = async (_request) => { const id = _request.id; const serialized = { id, applicationId: this.applicationId, applicationVersion: this.applicationVersion, sessionId: this.sessionId, authorization: core_bentley_1.ProcessDetector.isMobileAppFrontend ? "" : await this.getAccessToken(), }; const csrf = IModelApp.securityOptions.csrfProtection; if (csrf && csrf.enabled) { const cookieName = csrf.cookieName || "XSRF-TOKEN"; const cookieValue = document.cookie.split("; ").find((r) => r.startsWith(`${cookieName}=`)); if (cookieValue) { const headerName = csrf.headerName || "X-XSRF-TOKEN"; const headerValue = cookieValue.split("=")[1]; serialized.csrfToken = { headerName, headerValue }; } } return serialized; }; } /** Shortcut for creating an HTMLElement with optional parent, className, id, innerHTML, innerText. */ static makeHTMLElement(type, opt) { const el = document.createElement(type); if (undefined !== opt) { if (undefined !== opt.className) el.className = opt.className; if (undefined !== opt.id) el.id = opt.id; if (undefined !== opt.innerHTML) el.innerHTML = opt.innerHTML; if (undefined !== opt.innerText) el.innerText = opt.innerText; if (undefined !== opt.parent) opt.parent.appendChild(el); } return el; } /** Shortcut for making a modal dialog on top of the root of the application. The returned HTMLDivElement will be placed topmost, all other application * windows will be covered with a semi-transparent background that intercepts all key/mouse/touch events until the modal is dismissed. * @param options The options that describe how the modal should work. */ static makeModalDiv(options) { const root = options.rootDiv ? options.rootDiv : document.body; // create the overlay div to "black out" the application to indicate everything is inactive until the modal has been dismissed. const overlay = IModelApp.makeHTMLElement("div", { parent: root, className: "imodeljs-modal-overlay" }); overlay.tabIndex = -1; // so we can catch keystroke events // function to remove modal dialog const stop = (ev) => { root.removeChild(overlay); ev.stopPropagation(); }; if (options.autoClose) { overlay.onclick = overlay.oncontextmenu = stop; overlay.onkeydown = overlay.onkeyup = (ev) => { switch (ev.key) { case "Enter": case "Escape": stop(ev); return; } ev.stopPropagation(); }; overlay.focus(); } const modal = IModelApp.makeHTMLElement("div", { parent: overlay, className: "imodeljs-modal" }); if (undefined !== options.width) { modal.style.width = `${options.width}px`; // allow the dialog to be smaller than the width modal.style.maxWidth = `min(100% - (2 * var(--width-border)), ${options.width}px)`; } if (options.closeBox) { const close = IModelApp.makeHTMLElement("p", { parent: modal, className: "imodeljs-modal-close" }); close.innerText = "\u00d7"; // unicode "times" symbol close.onclick = stop; } return { modal, stop }; } /** Applications may implement this method to supply a Logo Card. * @beta */ static applicationLogoCard; /** Make a new Logo Card. Call this method from your implementation of [[IModelApp.applicationLogoCard]] * @param opts Options for Logo Card * @beta */ static makeLogoCard(opts) { const card = IModelApp.makeHTMLElement("tr"); const iconCell = IModelApp.makeHTMLElement("td", { parent: card, className: "logo-card-logo" }); if (undefined !== opts.iconSrc) { if (typeof opts.iconSrc === "string") { const logo = IModelApp.makeHTMLElement("img"); logo.src = opts.iconSrc; logo.width = opts.iconWidth ? opts.iconWidth : 64; opts.iconSrc = logo; } iconCell.appendChild(opts.iconSrc); } const noticeCell = IModelApp.makeHTMLElement("td", { parent: card, className: "logo-card-message" }); if (undefined !== opts.heading) { if (typeof opts.heading === "string") IModelApp.makeHTMLElement("h2", { parent: noticeCell, innerHTML: opts.heading, className: "logo-card-header" }); else noticeCell.appendChild(opts.heading); } if (undefined !== opts.notice) { if (typeof opts.notice === "string") IModelApp.makeHTMLElement("p", { parent: noticeCell, innerHTML: opts.notice, className: "logo-cards" }); else noticeCell.appendChild(opts.notice); } return card; } /** Make the logo card for the library itself. This card gets placed at the top of the stack. * @internal */ static makeIModelJsLogoCard() { return this.makeLogoCard({ iconSrc: `${this.publicPath}images/about-imodeljs.svg`, heading: `<span style="font-weight:normal">${this.localization.getLocalizedString("iModelJs:Notices.PoweredBy")}</span>&nbsp;iTwin.js`, notice: `${exports.ITWINJS_CORE_VERSION}<br>${COPYRIGHT_NOTICE}`, }); } /** Format the tooltip strings returned by [[IModelConnection.getToolTipMessage]]. * @alpha */ static formatElementToolTip(msg) { let out = ""; msg.forEach((line) => out += `${IModelApp.localization?.getLocalizedKeys(line)}<br>`); const div = document.createElement("div"); div.innerHTML = out; return div; } /** Localize an error status * @param status one of the status values from [BentleyStatus]($core-bentley), [IModelStatus]($core-bentley) or [DbResult]($core-bentley) * @returns a localized error message * @beta */ static translateStatus(status) { let key; if (typeof status !== "number") { key = { scope: "Errors", val: "IllegalValue" }; } else { key = { scope: "BentleyStatus", val: core_bentley_1.BentleyStatus[status] }; if (!key.val) key = { scope: "IModelStatus", val: core_bentley_1.IModelStatus[status] }; if (!key.val) key = { scope: "DbResult", val: core_bentley_1.DbResult[status] }; if (!key.val) key = { scope: "Errors", val: "Status", status: status.toString() }; } return this.localization.getLocalizedString(`iModelJs:${key.scope}.${key.val}`, key); } /** * Resets the formatsProvider back to the default [[QuantityTypeFormatsProvider]]. * @beta */ static resetFormatsProvider() { this.formatsProvider = new QuantityFormatter_1.QuantityTypeFormatsProvider(); } /** * Creates an instance of the ExtensionAdmin * and registers an event to execute after startup is complete * @returns an instance of ExtensionAdmin */ static _createExtensionAdmin() { const extensionAdmin = new ExtensionAdmin_1.ExtensionAdmin(); IModelApp.onAfterStartup.addListener(extensionAdmin.onStartup); return extensionAdmin; } } exports.IModelApp = IModelApp; //# sourceMappingURL=IModelApp.js.map