UNPKG

@theia/core

Version:

Theia is a cloud & desktop IDE framework implemented in TypeScript.

320 lines • 14.6 kB
"use strict"; // ***************************************************************************** // Copyright (C) 2017 TypeFox and others. // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License v. 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0. // // This Source Code may also be made available under the following Secondary // Licenses when the conditions for such availability set forth in the Eclipse // Public License v. 2.0 are satisfied: GNU General Public License, version 2 // with the GNU Classpath Exception which is available at // https://www.gnu.org/software/classpath/license.html. // // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; var __param = (this && this.__param) || function (paramIndex, decorator) { return function (target, key) { decorator(target, key, paramIndex); } }; Object.defineProperty(exports, "__esModule", { value: true }); exports.FrontendApplication = void 0; const inversify_1 = require("inversify"); const common_1 = require("../common"); const keybinding_1 = require("./keybinding"); const widgets_1 = require("./widgets"); const application_shell_1 = require("./shell/application-shell"); const shell_layout_restorer_1 = require("./shell/shell-layout-restorer"); const frontend_application_state_1 = require("./frontend-application-state"); const browser_1 = require("./browser"); const core_preferences_1 = require("./core-preferences"); const window_service_1 = require("./window/window-service"); const tooltip_service_1 = require("./tooltip-service"); const frontend_application_contribution_1 = require("./frontend-application-contribution"); const TIMER_WARNING_THRESHOLD = 100; let FrontendApplication = class FrontendApplication { constructor(commands, menus, keybindings, layoutRestorer, contributions, _shell, stateService) { this.commands = commands; this.menus = menus; this.keybindings = keybindings; this.layoutRestorer = layoutRestorer; this.contributions = contributions; this._shell = _shell; this.stateService = stateService; } get shell() { return this._shell; } /** * Start the frontend application. * * Start up consists of the following steps: * - start frontend contributions * - attach the application shell to the host element * - initialize the application shell layout * - reveal the application shell if it was hidden by a startup indicator */ async start() { const startup = this.backendStopwatch.start('frontend'); await this.measure('startContributions', () => this.startContributions(), 'Start frontend contributions', false); this.stateService.state = 'started_contributions'; const host = await this.getHost(); this.attachShell(host); this.attachTooltip(host); await (0, browser_1.animationFrame)(); this.stateService.state = 'attached_shell'; await this.measure('initializeLayout', () => this.initializeLayout(), 'Initialize the workbench layout', false); this.stateService.state = 'initialized_layout'; await this.fireOnDidInitializeLayout(); await this.measure('revealShell', () => this.revealShell(host), 'Replace loading indicator with ready workbench UI (animation)', false); this.registerEventListeners(); this.stateService.state = 'ready'; startup.then(idToken => this.backendStopwatch.stop(idToken, 'Frontend application start', [])); } /** * Return a promise to the host element to which the application shell is attached. */ getHost() { if (document.body) { return Promise.resolve(document.body); } return new Promise(resolve => window.addEventListener('load', () => resolve(document.body), { once: true })); } /** * Return an HTML element that indicates the startup phase, e.g. with an animation or a splash screen. */ getStartupIndicator(host) { const startupElements = host.getElementsByClassName('theia-preload'); return startupElements.length === 0 ? undefined : startupElements[0]; } /** * Register global event listeners. */ registerEventListeners() { this.windowsService.onUnload(() => { this.stateService.state = 'closing_window'; this.layoutRestorer.storeLayout(this); this.stopContributions(); }); window.addEventListener('resize', () => this.shell.update()); this.keybindings.registerEventListeners(window); document.addEventListener('touchmove', event => { event.preventDefault(); }, { passive: false }); // Prevent forward/back navigation by scrolling in OS X if (common_1.isOSX) { document.body.addEventListener('wheel', browser_1.preventNavigation, { passive: false }); } // Prevent the default browser behavior when dragging and dropping files into the window. document.addEventListener('dragenter', event => { if (event.dataTransfer) { event.dataTransfer.dropEffect = 'none'; } event.preventDefault(); }, false); document.addEventListener('dragover', event => { if (event.dataTransfer) { event.dataTransfer.dropEffect = 'none'; } event.preventDefault(); }, false); document.addEventListener('drop', event => { event.preventDefault(); }, false); } /** * Attach the application shell to the host element. If a startup indicator is present, the shell is * inserted before that indicator so it is not visible yet. */ attachShell(host) { const ref = this.getStartupIndicator(host); widgets_1.Widget.attach(this.shell, host, ref); } /** * Attach the tooltip container to the host element. */ attachTooltip(host) { this.tooltipService.attachTo(host); } /** * If a startup indicator is present, it is first hidden with the `theia-hidden` CSS class and then * removed after a while. The delay until removal is taken from the CSS transition duration. */ revealShell(host) { const startupElem = this.getStartupIndicator(host); if (startupElem) { return new Promise(resolve => { window.requestAnimationFrame(() => { startupElem.classList.add('theia-hidden'); const preloadStyle = window.getComputedStyle(startupElem); const transitionDuration = (0, browser_1.parseCssTime)(preloadStyle.transitionDuration, 0); window.setTimeout(() => { const parent = startupElem.parentElement; if (parent) { parent.removeChild(startupElem); } resolve(); }, transitionDuration); }); }); } else { return Promise.resolve(); } } /** * Initialize the shell layout either using the layout restorer service or, if no layout has * been stored, by creating the default layout. */ async initializeLayout() { if (!await this.restoreLayout()) { // Fallback: Create the default shell layout await this.createDefaultLayout(); } await this.shell.pendingUpdates; } /** * Try to restore the shell layout from the storage service. Resolves to `true` if successful. */ async restoreLayout() { try { return await this.layoutRestorer.restoreLayout(this); } catch (error) { if (shell_layout_restorer_1.ApplicationShellLayoutMigrationError.is(error)) { console.warn(error.message); console.info('Initializing the default layout instead...'); } else { console.error('Could not restore layout', error); } return false; } } /** * Let the frontend application contributions initialize the shell layout. Override this * method in order to create an application-specific custom layout. */ async createDefaultLayout() { for (const contribution of this.contributions.getContributions()) { if (contribution.initializeLayout) { await this.measure(contribution.constructor.name + '.initializeLayout', () => contribution.initializeLayout(this)); } } } async fireOnDidInitializeLayout() { for (const contribution of this.contributions.getContributions()) { if (contribution.onDidInitializeLayout) { await this.measure(contribution.constructor.name + '.onDidInitializeLayout', () => contribution.onDidInitializeLayout(this)); } } } /** * Initialize and start the frontend application contributions. */ async startContributions() { for (const contribution of this.contributions.getContributions()) { if (contribution.initialize) { try { await this.measure(contribution.constructor.name + '.initialize', () => contribution.initialize()); } catch (error) { console.error('Could not initialize contribution', error); } } } for (const contribution of this.contributions.getContributions()) { if (contribution.configure) { try { await this.measure(contribution.constructor.name + '.configure', () => contribution.configure(this)); } catch (error) { console.error('Could not configure contribution', error); } } } /** * FIXME: * - decouple commands & menus * - consider treat commands, keybindings and menus as frontend application contributions */ await this.measure('commands.onStart', () => this.commands.onStart()); await this.measure('keybindings.onStart', () => this.keybindings.onStart()); await this.measure('menus.onStart', () => this.menus.onStart()); for (const contribution of this.contributions.getContributions()) { if (contribution.onStart) { try { await this.measure(contribution.constructor.name + '.onStart', () => contribution.onStart(this)); } catch (error) { console.error('Could not start contribution', error); } } } } /** * Stop the frontend application contributions. This is called when the window is unloaded. */ stopContributions() { console.info('>>> Stopping frontend contributions...'); for (const contribution of this.contributions.getContributions()) { if (contribution.onStop) { try { contribution.onStop(this); } catch (error) { console.error('Could not stop contribution', error); } } } console.info('<<< All frontend contributions have been stopped.'); } async measure(name, fn, message = `Frontend ${name}`, threshold = true) { return this.stopwatch.startAsync(name, message, fn, threshold ? { thresholdMillis: TIMER_WARNING_THRESHOLD, defaultLogLevel: common_1.LogLevel.DEBUG } : {}); } }; __decorate([ (0, inversify_1.inject)(core_preferences_1.CorePreferences), __metadata("design:type", Object) ], FrontendApplication.prototype, "corePreferences", void 0); __decorate([ (0, inversify_1.inject)(window_service_1.WindowService), __metadata("design:type", Object) ], FrontendApplication.prototype, "windowsService", void 0); __decorate([ (0, inversify_1.inject)(tooltip_service_1.TooltipService), __metadata("design:type", Object) ], FrontendApplication.prototype, "tooltipService", void 0); __decorate([ (0, inversify_1.inject)(common_1.Stopwatch), __metadata("design:type", common_1.Stopwatch) ], FrontendApplication.prototype, "stopwatch", void 0); __decorate([ (0, inversify_1.inject)(common_1.BackendStopwatch), __metadata("design:type", Object) ], FrontendApplication.prototype, "backendStopwatch", void 0); FrontendApplication = __decorate([ (0, inversify_1.injectable)(), __param(0, (0, inversify_1.inject)(common_1.CommandRegistry)), __param(1, (0, inversify_1.inject)(common_1.MenuModelRegistry)), __param(2, (0, inversify_1.inject)(keybinding_1.KeybindingRegistry)), __param(3, (0, inversify_1.inject)(shell_layout_restorer_1.ShellLayoutRestorer)), __param(4, (0, inversify_1.inject)(common_1.ContributionProvider)), __param(4, (0, inversify_1.named)(frontend_application_contribution_1.FrontendApplicationContribution)), __param(5, (0, inversify_1.inject)(application_shell_1.ApplicationShell)), __param(6, (0, inversify_1.inject)(frontend_application_state_1.FrontendApplicationStateService)), __metadata("design:paramtypes", [common_1.CommandRegistry, common_1.MenuModelRegistry, keybinding_1.KeybindingRegistry, shell_layout_restorer_1.ShellLayoutRestorer, Object, application_shell_1.ApplicationShell, frontend_application_state_1.FrontendApplicationStateService]) ], FrontendApplication); exports.FrontendApplication = FrontendApplication; //# sourceMappingURL=frontend-application.js.map