shell.ts 19.7 KB
Newer Older
E
Erich Gamma 已提交
1 2 3 4 5 6 7 8 9
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

'use strict';

import 'vs/css!./media/shell';

10
import * as nls from 'vs/nls';
11
import {TPromise} from 'vs/base/common/winjs.base';
J
Joao Moreno 已提交
12
import * as platform from 'vs/base/common/platform';
E
Erich Gamma 已提交
13 14
import {Dimension, Builder, $} from 'vs/base/browser/builder';
import dom = require('vs/base/browser/dom');
15
import aria = require('vs/base/browser/ui/aria/aria');
16
import {dispose, IDisposable, Disposables} from 'vs/base/common/lifecycle';
E
Erich Gamma 已提交
17
import errors = require('vs/base/common/errors');
18
import {toErrorMessage} from 'vs/base/common/errorMessage';
19 20
import product from 'vs/platform/product';
import pkg from 'vs/platform/package';
E
Erich Gamma 已提交
21 22
import {ContextViewService} from 'vs/platform/contextview/browser/contextViewService';
import timer = require('vs/base/common/timer');
23
import {Workbench} from 'vs/workbench/electron-browser/workbench';
24
import {Storage, inMemoryLocalStorageInstance} from 'vs/workbench/common/storage';
25
import {ITelemetryService, NullTelemetryService, loadExperiments} from 'vs/platform/telemetry/common/telemetry';
26
import {ITelemetryAppenderChannel, TelemetryAppenderClient} from 'vs/platform/telemetry/common/telemetryIpc';
27
import {TelemetryService, ITelemetryServiceConfig} from  'vs/platform/telemetry/common/telemetryService';
28
import {IdleMonitor, UserStatus} from  'vs/platform/telemetry/browser/idleMonitor';
J
Joao Moreno 已提交
29
import ErrorTelemetry from 'vs/platform/telemetry/browser/errorTelemetry';
30
import {resolveWorkbenchCommonProperties} from 'vs/platform/telemetry/node/workbenchCommonProperties';
E
Erich Gamma 已提交
31 32
import {ElectronIntegration} from 'vs/workbench/electron-browser/integration';
import {Update} from 'vs/workbench/electron-browser/update';
A
Alex Dima 已提交
33
import {WorkspaceStats} from 'vs/workbench/services/telemetry/common/workspaceStats';
E
Erich Gamma 已提交
34 35
import {IWindowService, WindowService} from 'vs/workbench/services/window/electron-browser/windowService';
import {MessageService} from 'vs/workbench/services/message/electron-browser/messageService';
J
Joao Moreno 已提交
36 37
import {IRequestService} from 'vs/platform/request/common/request';
import {RequestService} from 'vs/platform/request/node/requestService';
E
Erich Gamma 已提交
38 39 40 41
import {IConfigurationService} from 'vs/platform/configuration/common/configuration';
import {SearchService} from 'vs/workbench/services/search/node/searchService';
import {LifecycleService} from 'vs/workbench/services/lifecycle/electron-browser/lifecycleService';
import {MainThreadService} from 'vs/workbench/services/thread/electron-browser/threadService';
42
import {MarkerService} from 'vs/platform/markers/common/markerService';
E
Erich Gamma 已提交
43 44
import {IModelService} from 'vs/editor/common/services/modelService';
import {ModelServiceImpl} from 'vs/editor/common/services/modelServiceImpl';
45 46
import {ICompatWorkerService} from 'vs/editor/common/services/compatWorkerService';
import {MainThreadCompatWorkerService} from 'vs/editor/common/services/compatWorkerServiceMain';
E
Erich Gamma 已提交
47 48
import {CodeEditorServiceImpl} from 'vs/editor/browser/services/codeEditorServiceImpl';
import {ICodeEditorService} from 'vs/editor/common/services/codeEditorService';
A
Alex Dima 已提交
49 50
import {IntegrityServiceImpl} from 'vs/platform/integrity/node/integrityServiceImpl';
import {IIntegrityService} from 'vs/platform/integrity/common/integrity';
51 52
import {EditorWorkerServiceImpl} from 'vs/editor/common/services/editorWorkerServiceImpl';
import {IEditorWorkerService} from 'vs/editor/common/services/editorWorkerService';
53
import {MainProcessExtensionService} from 'vs/workbench/api/node/mainThreadExtensionService';
E
Erich Gamma 已提交
54
import {IOptions} from 'vs/workbench/common/options';
M
Martin Aeschlimann 已提交
55
import {IStorageService} from 'vs/platform/storage/common/storage';
56
import {ServiceCollection} from 'vs/platform/instantiation/common/serviceCollection';
57
import {InstantiationService} from 'vs/platform/instantiation/common/instantiationService';
58
import {IContextViewService} from 'vs/platform/contextview/browser/contextView';
E
Erich Gamma 已提交
59 60
import {IEventService} from 'vs/platform/event/common/event';
import {ILifecycleService} from 'vs/platform/lifecycle/common/lifecycle';
B
Benjamin Pasero 已提交
61
import {IMarkerService} from 'vs/platform/markers/common/markers';
62
import {IEnvironmentService} from 'vs/platform/environment/common/environment';
J
Joao Moreno 已提交
63
import {IMessageService, IChoiceService, Severity} from 'vs/platform/message/common/message';
S
Sandeep Somavarapu 已提交
64
import {ChoiceChannel} from 'vs/platform/message/common/messageIpc';
E
Erich Gamma 已提交
65
import {ISearchService} from 'vs/platform/search/common/search';
66
import {IThreadService} from 'vs/workbench/services/thread/common/threadService';
67 68
import {ICommandService} from 'vs/platform/commands/common/commands';
import {CommandService} from 'vs/platform/commands/common/commandService';
69
import {IWorkspaceContextService, IWorkspace} from 'vs/platform/workspace/common/workspace';
A
Alex Dima 已提交
70
import {IExtensionService} from 'vs/platform/extensions/common/extensions';
E
Erich Gamma 已提交
71 72
import {MainThreadModeServiceImpl} from 'vs/editor/common/services/modeServiceImpl';
import {IModeService} from 'vs/editor/common/services/modeService';
73
import {IUntitledEditorService, UntitledEditorService} from 'vs/workbench/services/untitled/common/untitledEditorService';
E
Erich Gamma 已提交
74
import {CrashReporter} from 'vs/workbench/electron-browser/crashReporter';
M
Martin Aeschlimann 已提交
75 76
import {IThemeService} from 'vs/workbench/services/themes/common/themeService';
import {ThemeService} from 'vs/workbench/services/themes/electron-browser/themeService';
J
Joao Moreno 已提交
77
import {getDelayedChannel} from 'vs/base/parts/ipc/common/ipc';
J
Joao Moreno 已提交
78 79 80
import {connect as connectNet} from 'vs/base/parts/ipc/node/ipc.net';
import {Client as ElectronIPCClient} from 'vs/base/parts/ipc/common/ipc.electron';
import {ipcRenderer} from 'electron';
J
Joao Moreno 已提交
81 82
import {IExtensionManagementChannel, ExtensionManagementChannelClient} from 'vs/platform/extensionManagement/common/extensionManagementIpc';
import {IExtensionManagementService} from 'vs/platform/extensionManagement/common/extensionManagement';
J
Joao Moreno 已提交
83 84
import {URLChannelClient} from 'vs/platform/url/common/urlIpc';
import {IURLService} from 'vs/platform/url/common/url';
85
import {ReloadWindowAction} from 'vs/workbench/electron-browser/actions';
86 87

// self registering services
88
import 'vs/platform/opener/browser/opener.contribution';
89

90 91 92 93 94 95 96
/**
 * Services that we require for the Shell
 */
export interface ICoreServices {
	contextService: IWorkspaceContextService;
	eventService: IEventService;
	configurationService: IConfigurationService;
97
	environmentService: IEnvironmentService;
98 99
}

E
Erich Gamma 已提交
100
/**
B
Benjamin Pasero 已提交
101
 * The workbench shell contains the workbench with a rich header containing navigation and the activity bar.
E
Erich Gamma 已提交
102 103 104
 * With the Shell being the top level element in the page, it is also responsible for driving the layouting.
 */
export class WorkbenchShell {
B
Benjamin Pasero 已提交
105
	private storageService: IStorageService;
106
	private messageService: MessageService;
107
	private eventService: IEventService;
108
	private environmentService:IEnvironmentService;
B
Benjamin Pasero 已提交
109 110 111
	private contextViewService: ContextViewService;
	private windowService: IWindowService;
	private threadService: MainThreadService;
112
	private configurationService: IConfigurationService;
M
Martin Aeschlimann 已提交
113
	private themeService: ThemeService;
114
	private contextService: IWorkspaceContextService;
115
	private telemetryService: ITelemetryService;
E
Erich Gamma 已提交
116 117

	private container: HTMLElement;
118
	private toUnbind: IDisposable[];
E
Erich Gamma 已提交
119 120 121 122 123 124 125 126 127
	private previousErrorValue: string;
	private previousErrorTime: number;
	private content: HTMLElement;
	private contentsContainer: Builder;

	private workspace: IWorkspace;
	private options: IOptions;
	private workbench: Workbench;

128
	constructor(container: HTMLElement, workspace: IWorkspace, services: ICoreServices, options: IOptions) {
E
Erich Gamma 已提交
129 130 131
		this.container = container;

		this.workspace = workspace;
132 133 134 135 136
		this.options = options;

		this.contextService = services.contextService;
		this.eventService = services.eventService;
		this.configurationService = services.configurationService;
137
		this.environmentService = services.environmentService;
E
Erich Gamma 已提交
138 139 140 141 142 143 144

		this.toUnbind = [];
		this.previousErrorTime = 0;
	}

	private createContents(parent: Builder): Builder {

145
		// ARIA
B
Benjamin Pasero 已提交
146
		aria.setARIAContainer(document.body);
147

E
Erich Gamma 已提交
148
		// Workbench Container
149
		const workbenchContainer = $(parent).div();
E
Erich Gamma 已提交
150 151

		// Instantiation service with services
152
		const [instantiationService, serviceCollection] = this.initServiceCollection(parent.getHTMLElement());
E
Erich Gamma 已提交
153 154

		//crash reporting
155 156 157
		if (!!product.crashReporter) {
			const crashReporter = instantiationService.createInstance(CrashReporter, pkg.version, product.commit);
			crashReporter.start(product.crashReporter);
E
Erich Gamma 已提交
158 159 160
		}

		// Workbench
161
		this.workbench = instantiationService.createInstance(Workbench, workbenchContainer.getHTMLElement(), this.workspace, this.options, serviceCollection);
E
Erich Gamma 已提交
162
		this.workbench.startup({
163 164
			onWorkbenchStarted: (customKeybindingsCount) => {
				this.onWorkbenchStarted(customKeybindingsCount);
E
Erich Gamma 已提交
165 166 167 168 169 170 171 172 173 174
			}
		});

		// Electron integration
		this.workbench.getInstantiationService().createInstance(ElectronIntegration).integrate(this.container);

		// Update
		this.workbench.getInstantiationService().createInstance(Update);

		// Handle case where workbench is not starting up properly
175
		const timeoutHandle = setTimeout(() => {
E
Erich Gamma 已提交
176 177 178 179 180 181 182 183 184 185
			console.warn('Workbench did not finish loading in 10 seconds, that might be a problem that should be reported.');
		}, 10000);

		this.workbench.joinCreation().then(() => {
			clearTimeout(timeoutHandle);
		});

		return workbenchContainer;
	}

186
	private onWorkbenchStarted(customKeybindingsCount: number): void {
B
Benjamin Pasero 已提交
187 188

		// Log to telemetry service
189
		const windowSize = {
B
Benjamin Pasero 已提交
190 191 192 193 194 195 196 197 198 199 200
			innerHeight: window.innerHeight,
			innerWidth: window.innerWidth,
			outerHeight: window.outerHeight,
			outerWidth: window.outerWidth
		};

		this.telemetryService.publicLog('workspaceLoad',
			{
				userAgent: navigator.userAgent,
				windowSize: windowSize,
				emptyWorkbench: !this.contextService.getWorkspace(),
201
				customKeybindingsCount,
M
Martin Aeschlimann 已提交
202
				theme: this.themeService.getColorTheme(),
203 204
				language: platform.language,
				experiments: this.telemetryService.getExperiments()
B
Benjamin Pasero 已提交
205 206
			});

207
		const workspaceStats: WorkspaceStats = <WorkspaceStats>this.workbench.getInstantiationService().createInstance(WorkspaceStats);
B
Benjamin Pasero 已提交
208
		workspaceStats.reportWorkspaceTags();
J
Joao Moreno 已提交
209 210 211 212

		if ((platform.isLinux || platform.isMacintosh) && process.getuid() === 0) {
			this.messageService.show(Severity.Warning, nls.localize('runningAsRoot', "It is recommended not to run Code as 'root'."));
		}
B
Benjamin Pasero 已提交
213 214
	}

215
	private initServiceCollection(container: HTMLElement): [InstantiationService, ServiceCollection] {
J
Joao Moreno 已提交
216 217
		const disposables = new Disposables();

J
Joao Moreno 已提交
218
		const mainProcessClient = new ElectronIPCClient(ipcRenderer);
J
Joao Moreno 已提交
219
		disposables.add(mainProcessClient);
J
Joao Moreno 已提交
220

221 222 223 224
		const serviceCollection = new ServiceCollection();
		serviceCollection.set(IEventService, this.eventService);
		serviceCollection.set(IWorkspaceContextService, this.contextService);
		serviceCollection.set(IConfigurationService, this.configurationService);
225
		serviceCollection.set(IEnvironmentService, this.environmentService);
226

227
		const instantiationService = new InstantiationService(serviceCollection, true);
228

229 230 231
		this.windowService = instantiationService.createInstance(WindowService);
		serviceCollection.set(IWindowService, this.windowService);

232 233
		const sharedProcess = connectNet(this.environmentService.sharedIPCHandle, `window:${ this.windowService.getWindowId() }`);
		sharedProcess.done(client => {
S
Sandeep Somavarapu 已提交
234 235 236

			client.registerChannel('choice', new ChoiceChannel(this.messageService));

237 238 239 240 241 242 243 244
			client.onClose(() => {
				this.messageService.show(Severity.Error, {
					message: nls.localize('sharedProcessCrashed', "The shared process terminated unexpectedly. Please reload the window to recover."),
					actions: [instantiationService.createInstance(ReloadWindowAction, ReloadWindowAction.ID, ReloadWindowAction.LABEL)]
				});
			});
		}, errors.onUnexpectedError);

245
		// Storage
246
		const disableWorkspaceStorage = this.environmentService.extensionTestsPath || (!this.workspace && !this.environmentService.extensionDevelopmentPath); // without workspace or in any extension test, we use inMemory storage unless we develop an extension where we want to preserve state
247 248
		this.storageService = instantiationService.createInstance(Storage, window.localStorage, disableWorkspaceStorage ? inMemoryLocalStorageInstance : window.localStorage);
		serviceCollection.set(IStorageService, this.storageService);
E
Erich Gamma 已提交
249

250
		// Telemetry
251
		if (this.environmentService.isBuilt && !this.environmentService.extensionDevelopmentPath && !!product.enableTelemetry) {
252
			const channel = getDelayedChannel<ITelemetryAppenderChannel>(sharedProcess.then(c => c.getChannel('telemetryAppender')));
253 254
			const commit = product.commit;
			const version = pkg.version;
255

256
			const config: ITelemetryServiceConfig = {
257
				appender: new TelemetryAppenderClient(channel),
258
				commonProperties: resolveWorkbenchCommonProperties(this.storageService, commit, version),
259 260
				piiPaths: [this.environmentService.appRoot, this.environmentService.extensionsPath],
				experiments: loadExperiments(this.storageService)
261
			};
262

J
Joao Moreno 已提交
263
			const telemetryService = instantiationService.createInstance(TelemetryService, config);
264 265
			this.telemetryService = telemetryService;

J
Joao Moreno 已提交
266
			const errorTelemetry = new ErrorTelemetry(telemetryService);
267
			const idleMonitor = new IdleMonitor(2 * 60 * 1000); // 2 minutes
268

269 270 271 272
			const listener = idleMonitor.onStatusChange(status =>
				this.telemetryService.publicLog(status === UserStatus.Active
					? TelemetryService.IDLE_STOP_EVENT_NAME
					: TelemetryService.IDLE_START_EVENT_NAME
273
				));
274

275
			disposables.add(telemetryService, errorTelemetry, listener, idleMonitor);
276
		} else {
277
			NullTelemetryService._experiments = loadExperiments(this.storageService);
278
			this.telemetryService = NullTelemetryService;
279
		}
E
Erich Gamma 已提交
280

281
		serviceCollection.set(ITelemetryService, this.telemetryService);
E
Erich Gamma 已提交
282

283
		this.messageService = instantiationService.createInstance(MessageService, container);
284
		serviceCollection.set(IMessageService, this.messageService);
J
Joao Moreno 已提交
285
		serviceCollection.set(IChoiceService, this.messageService);
E
Erich Gamma 已提交
286

287
		const lifecycleService = instantiationService.createInstance(LifecycleService);
288 289
		this.toUnbind.push(lifecycleService.onShutdown(() => disposables.dispose()));
		serviceCollection.set(ILifecycleService, lifecycleService);
290

291 292
		this.threadService = instantiationService.createInstance(MainThreadService);
		serviceCollection.set(IThreadService, this.threadService);
293

294
		const extensionService = instantiationService.createInstance(MainProcessExtensionService);
295
		serviceCollection.set(IExtensionService, extensionService);
E
Erich Gamma 已提交
296

297 298
		serviceCollection.set(ICommandService, new CommandService(instantiationService, extensionService));

299 300
		this.contextViewService = instantiationService.createInstance(ContextViewService, this.container);
		serviceCollection.set(IContextViewService, this.contextViewService);
E
Erich Gamma 已提交
301

J
Joao Moreno 已提交
302
		const requestService = instantiationService.createInstance(RequestService);
303
		serviceCollection.set(IRequestService, requestService);
E
Erich Gamma 已提交
304

305
		const markerService = instantiationService.createInstance(MarkerService);
306
		serviceCollection.set(IMarkerService, markerService);
E
Erich Gamma 已提交
307

308
		const modeService = instantiationService.createInstance(MainThreadModeServiceImpl);
309
		serviceCollection.set(IModeService, modeService);
310

311
		const modelService = instantiationService.createInstance(ModelServiceImpl);
312
		serviceCollection.set(IModelService, modelService);
313

314
		const compatWorkerService = instantiationService.createInstance(MainThreadCompatWorkerService);
315 316
		serviceCollection.set(ICompatWorkerService, compatWorkerService);

317
		const editorWorkerService = instantiationService.createInstance(EditorWorkerServiceImpl);
318
		serviceCollection.set(IEditorWorkerService, editorWorkerService);
319

320
		const untitledEditorService = instantiationService.createInstance(UntitledEditorService);
321 322 323
		serviceCollection.set(IUntitledEditorService, untitledEditorService);

		this.themeService = instantiationService.createInstance(ThemeService);
324
		serviceCollection.set(IThemeService, this.themeService);
325

326
		const searchService = instantiationService.createInstance(SearchService);
327 328
		serviceCollection.set(ISearchService, searchService);

329
		const codeEditorService = instantiationService.createInstance(CodeEditorServiceImpl);
330 331
		serviceCollection.set(ICodeEditorService, codeEditorService);

A
Alex Dima 已提交
332 333 334
		const integrityService = instantiationService.createInstance(IntegrityServiceImpl);
		serviceCollection.set(IIntegrityService, integrityService);

335
		const extensionManagementChannel = getDelayedChannel<IExtensionManagementChannel>(sharedProcess.then(c => c.getChannel('extensions')));
J
Joao Moreno 已提交
336
		const extensionManagementChannelClient = new ExtensionManagementChannelClient(extensionManagementChannel);
337
		serviceCollection.set(IExtensionManagementService, extensionManagementChannelClient);
338

J
Joao Moreno 已提交
339
		const urlChannel = mainProcessClient.getChannel('url');
J
Joao Moreno 已提交
340
		const urlChannelClient = new URLChannelClient(urlChannel, this.windowService.getWindowId());
J
Joao Moreno 已提交
341 342
		serviceCollection.set(IURLService, urlChannelClient);

343
		return [instantiationService, serviceCollection];
E
Erich Gamma 已提交
344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
	}

	public open(): void {

		// Listen on unexpected errors
		errors.setUnexpectedErrorHandler((error: any) => {
			this.onUnexpectedError(error);
		});

		// Shell Class for CSS Scoping
		$(this.container).addClass('monaco-shell');

		// Controls
		this.content = $('.monaco-shell-content').appendTo(this.container).getHTMLElement();

		// Handle Load Performance Timers
		this.writeTimers();

		// Create Contents
		this.contentsContainer = this.createContents($(this.content));

		// Layout
		this.layout();

		// Listeners
		this.registerListeners();

		// Enable theme support
M
Martin Aeschlimann 已提交
372
		this.themeService.initialize(this.container).then(null, error => {
373 374
			errors.onUnexpectedError(error);
		});
E
Erich Gamma 已提交
375 376 377 378 379 380 381 382 383
	}

	private registerListeners(): void {

		// Resize
		$(window).on(dom.EventType.RESIZE, () => this.layout(), this.toUnbind);
	}

	private writeTimers(): void {
384
		const timers = (<any>window).MonacoEnvironment.timers;
E
Erich Gamma 已提交
385
		if (timers) {
386
			const events: timer.IExistingTimerEvent[] = [];
E
Erich Gamma 已提交
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 419 420 421 422

			// Window
			if (timers.vscodeStart) {
				events.push({
					startTime: timers.vscodeStart,
					stopTime: timers.beforeLoad,
					topic: 'Startup',
					name: 'VSCode Startup',
					description: 'Time it takes to create a window and startup VSCode'
				});
			}

			// Load
			events.push({
				startTime: timers.beforeLoad,
				stopTime: timers.afterLoad,
				topic: 'Startup',
				name: 'Load Modules',
				description: 'Time it takes to load VSCodes main modules'
			});

			// Ready
			events.push({
				startTime: timers.beforeReady,
				stopTime: timers.afterReady,
				topic: 'Startup',
				name: 'Event DOMContentLoaded',
				description: 'Time it takes for the DOM to emit DOMContentLoaded event'
			});

			// Write to Timer
			timer.getTimeKeeper().setInitialCollectedEvents(events, timers.start);
		}
	}

	public onUnexpectedError(error: any): void {
423
		const errorMsg = toErrorMessage(error, true);
E
Erich Gamma 已提交
424 425 426 427
		if (!errorMsg) {
			return;
		}

428
		const now = Date.now();
E
Erich Gamma 已提交
429 430 431 432 433 434 435 436 437 438 439
		if (errorMsg === this.previousErrorValue && now - this.previousErrorTime <= 1000) {
			return; // Return if error message identical to previous and shorter than 1 second
		}

		this.previousErrorTime = now;
		this.previousErrorValue = errorMsg;

		// Log to console
		console.error(errorMsg);

		// Show to user if friendly message provided
440
		if (error && error.friendlyMessage && this.messageService) {
B
Benjamin Pasero 已提交
441
			this.messageService.show(Severity.Error, error.friendlyMessage);
E
Erich Gamma 已提交
442 443 444
		}
	}

B
Benjamin Pasero 已提交
445
	private layout(): void {
446
		const clArea = $(this.container).getClientArea();
E
Erich Gamma 已提交
447

448
		const contentsSize = new Dimension(clArea.width, clArea.height);
E
Erich Gamma 已提交
449 450
		this.contentsContainer.size(contentsSize.width, contentsSize.height);

B
Benjamin Pasero 已提交
451
		this.contextViewService.layout();
E
Erich Gamma 已提交
452 453 454 455 456 457 458
		this.workbench.layout();
	}

	public joinCreation(): TPromise<boolean> {
		return this.workbench.joinCreation();
	}

B
Benjamin Pasero 已提交
459
	public dispose(): void {
E
Erich Gamma 已提交
460 461 462

		// Workbench
		if (this.workbench) {
B
Benjamin Pasero 已提交
463
			this.workbench.dispose();
E
Erich Gamma 已提交
464 465
		}

B
Benjamin Pasero 已提交
466
		this.contextViewService.dispose();
E
Erich Gamma 已提交
467 468

		// Listeners
J
Joao Moreno 已提交
469
		this.toUnbind = dispose(this.toUnbind);
E
Erich Gamma 已提交
470 471 472 473 474

		// Container
		$(this.container).empty();
	}
}