viewImpl.ts 34.7 KB
Newer Older
E
Erich Gamma 已提交
1 2 3 4 5 6
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
'use strict';

A
Alex Dima 已提交
7
import {onUnexpectedError} from 'vs/base/common/errors';
A
Alex Dima 已提交
8
import {EventEmitter, EmitterEvent, IEventEmitter} from 'vs/base/common/eventEmitter';
J
Joao Moreno 已提交
9
import {IDisposable, dispose} from 'vs/base/common/lifecycle';
A
Alex Dima 已提交
10 11 12
import * as timer from 'vs/base/common/timer';
import * as browser from 'vs/base/browser/browser';
import * as dom from 'vs/base/browser/dom';
A
Alex Dima 已提交
13
import {StyleMutator} from 'vs/base/browser/styleMutator';
14
import {IKeybindingContextKey, IKeybindingService} from 'vs/platform/keybinding/common/keybinding';
15
import {ICommandService} from 'vs/platform/commands/common/commands';
A
Alex Dima 已提交
16 17
import {Range} from 'vs/editor/common/core/range';
import * as editorCommon from 'vs/editor/common/editorCommon';
E
Erich Gamma 已提交
18
import {ViewEventHandler} from 'vs/editor/common/viewModel/viewEventHandler';
A
Alex Dima 已提交
19
import {Configuration} from 'vs/editor/browser/config/configuration';
20
import {KeyboardHandler, IKeyboardHandlerHelper} from 'vs/editor/browser/controller/keyboardHandler';
E
Erich Gamma 已提交
21
import {PointerHandler} from 'vs/editor/browser/controller/pointerHandler';
A
Alex Dima 已提交
22
import * as editorBrowser from 'vs/editor/browser/editorBrowser';
A
Alex Dima 已提交
23
import {ViewController, TriggerCursorHandler} from 'vs/editor/browser/view/viewController';
A
Alex Dima 已提交
24
import {ViewEventDispatcher} from 'vs/editor/browser/view/viewEventDispatcher';
E
Erich Gamma 已提交
25
import {ContentViewOverlays, MarginViewOverlays} from 'vs/editor/browser/view/viewOverlays';
A
Alex Dima 已提交
26
import {LayoutProvider} from 'vs/editor/browser/viewLayout/layoutProvider';
E
Erich Gamma 已提交
27 28 29 30 31
import {ViewContentWidgets} from 'vs/editor/browser/viewParts/contentWidgets/contentWidgets';
import {CurrentLineHighlightOverlay} from 'vs/editor/browser/viewParts/currentLineHighlight/currentLineHighlight';
import {DecorationsOverlay} from 'vs/editor/browser/viewParts/decorations/decorations';
import {GlyphMarginOverlay} from 'vs/editor/browser/viewParts/glyphMargin/glyphMargin';
import {LineNumbersOverlay} from 'vs/editor/browser/viewParts/lineNumbers/lineNumbers';
32
import {IndentGuidesOverlay} from 'vs/editor/browser/viewParts/indentGuides/indentGuides';
A
Alex Dima 已提交
33 34 35 36 37
import {ViewLines} from 'vs/editor/browser/viewParts/lines/viewLines';
import {LinesDecorationsOverlay} from 'vs/editor/browser/viewParts/linesDecorations/linesDecorations';
import {ViewOverlayWidgets} from 'vs/editor/browser/viewParts/overlayWidgets/overlayWidgets';
import {DecorationsOverviewRuler} from 'vs/editor/browser/viewParts/overviewRuler/decorationsOverviewRuler';
import {OverviewRuler} from 'vs/editor/browser/viewParts/overviewRuler/overviewRuler';
38
import {Rulers} from 'vs/editor/browser/viewParts/rulers/rulers';
A
Alex Dima 已提交
39 40 41 42
import {ScrollDecorationViewPart} from 'vs/editor/browser/viewParts/scrollDecoration/scrollDecoration';
import {SelectionsOverlay} from 'vs/editor/browser/viewParts/selections/selections';
import {ViewCursors} from 'vs/editor/browser/viewParts/viewCursors/viewCursors';
import {ViewZones} from 'vs/editor/browser/viewParts/viewZones/viewZones';
A
Alex Dima 已提交
43
import {ViewPart} from 'vs/editor/browser/view/viewPart';
A
Alex Dima 已提交
44
import {ViewContext, IViewEventHandler} from 'vs/editor/common/view/viewContext';
45 46 47 48
import {IViewModel} from 'vs/editor/common/viewModel/viewModel';
import {ViewLinesViewportData} from 'vs/editor/common/viewLayout/viewLinesViewportData';
import {IRenderingContext} from 'vs/editor/common/view/renderingContext';
import {IPointerHandlerHelper} from 'vs/editor/browser/controller/mouseHandler';
E
Erich Gamma 已提交
49

A
Alex Dima 已提交
50
export class View extends ViewEventHandler implements editorBrowser.IView, IDisposable {
E
Erich Gamma 已提交
51 52 53

	private eventDispatcher:ViewEventDispatcher;

A
Alex Dima 已提交
54
	private listenersToRemove:IDisposable[];
A
Alex Dima 已提交
55
	private listenersToDispose:IDisposable[];
E
Erich Gamma 已提交
56 57

	private layoutProvider: LayoutProvider;
58
	public _context: ViewContext;
E
Erich Gamma 已提交
59 60 61 62 63 64 65 66

	// The view lines
	private viewLines: ViewLines;

	// These are parts, but we must do some API related calls on them, so we keep a reference
	private viewZones: ViewZones;
	private contentWidgets: ViewContentWidgets;
	private overlayWidgets: ViewOverlayWidgets;
A
Alex Dima 已提交
67
	private viewParts: ViewPart[];
E
Erich Gamma 已提交
68 69 70 71

	private keyboardHandler: KeyboardHandler;
	private pointerHandler: PointerHandler;

A
Alex Dima 已提交
72
	private outgoingEventBus: EventEmitter;
E
Erich Gamma 已提交
73 74 75 76 77 78 79 80 81 82 83 84 85 86

	// Dom nodes
	private linesContent: HTMLElement;
	public domNode: HTMLElement;
	public textArea: HTMLTextAreaElement;
	private textAreaCover: HTMLElement;
	private linesContentContainer: HTMLElement;
	private overflowGuardContainer: HTMLElement;

	// Actual mutable state
	private hasFocus:boolean;
	private _isDisposed: boolean;

	private handleAccumulatedModelEventsTimeout:number;
A
Alex Dima 已提交
87
	private accumulatedModelEvents: EmitterEvent[];
A
Alex Dima 已提交
88
	private _renderAnimationFrame: IDisposable;
E
Erich Gamma 已提交
89 90 91 92

	private _keybindingService: IKeybindingService;
	private _editorTextFocusContextKey: IKeybindingContextKey<boolean>;

A
Alex Dima 已提交
93 94
	constructor(
		keybindingService: IKeybindingService,
95
		commandService: ICommandService,
A
Alex Dima 已提交
96 97 98 99
		configuration:Configuration,
		model:IViewModel,
		triggerCursorHandler:TriggerCursorHandler
	) {
E
Erich Gamma 已提交
100 101 102
		super();
		this._isDisposed = false;
		this._renderAnimationFrame = null;
A
Alex Dima 已提交
103
		this.outgoingEventBus = new EventEmitter();
E
Erich Gamma 已提交
104

105
		var viewController = new ViewController(model, triggerCursorHandler, this.outgoingEventBus, commandService);
E
Erich Gamma 已提交
106 107 108 109 110 111 112 113 114

		this.listenersToRemove = [];
		this.listenersToDispose = [];

		// The event dispatcher will always go through _renderOnce before dispatching any events
		this.eventDispatcher = new ViewEventDispatcher((callback:()=>void) => this._renderOnce(callback));

		// These two dom nodes must be constructed up front, since references are needed in the layout provider (scrolling & co.)
		this.linesContent = document.createElement('div');
A
Alex Dima 已提交
115
		this.linesContent.className = editorBrowser.ClassNames.LINES_CONTENT + ' monaco-editor-background';
E
Erich Gamma 已提交
116
		this.domNode = document.createElement('div');
117
		this.domNode.className = configuration.editor.viewInfo.editorClassName;
E
Erich Gamma 已提交
118 119

		this.overflowGuardContainer = document.createElement('div');
A
Alex Dima 已提交
120
		this.overflowGuardContainer.className = editorBrowser.ClassNames.OVERFLOW_GUARD;
E
Erich Gamma 已提交
121 122 123 124 125 126 127 128 129

		// The layout provider has such responsibilities as:
		// - scrolling (i.e. viewport / full size) & co.
		// - whitespaces (a.k.a. view zones) management & co.
		// - line heights updating & co.
		this.layoutProvider = new LayoutProvider(configuration, model, this.eventDispatcher, this.linesContent, this.domNode, this.overflowGuardContainer);
		this.eventDispatcher.addEventHandler(this.layoutProvider);

		// The view context is passed on to most classes (basically to reduce param. counts in ctors)
130 131
		this._context = new ViewContext(
				configuration, model, this.eventDispatcher,
A
Alex Dima 已提交
132 133
				(eventHandler:IViewEventHandler) => this.eventDispatcher.addEventHandler(eventHandler),
				(eventHandler:IViewEventHandler) => this.eventDispatcher.removeEventHandler(eventHandler)
E
Erich Gamma 已提交
134 135 136 137 138 139
		);

		this.createTextArea(keybindingService);
		this.createViewParts();

		// Keyboard handler
140
		this.keyboardHandler = new KeyboardHandler(this._context, viewController, this.createKeyboardHandlerHelper());
E
Erich Gamma 已提交
141 142

		// Pointer handler
143
		this.pointerHandler = new PointerHandler(this._context, viewController, this.createPointerHandlerHelper());
E
Erich Gamma 已提交
144 145 146 147 148 149 150 151 152 153

		this.hasFocus = false;
		this.codeEditorHelper = null;

		this.eventDispatcher.addEventHandler(this);

		// The view lines rendering calls model.getLineTokens() that might emit events that its tokens have changed.
		// This delayed processing of incoming model events acts as a guard against undesired/unexpected recursion.
		this.handleAccumulatedModelEventsTimeout = -1;
		this.accumulatedModelEvents = [];
A
Alex Dima 已提交
154
		this.listenersToRemove.push(model.addBulkListener2((events:EmitterEvent[]) => {
E
Erich Gamma 已提交
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
			this.accumulatedModelEvents = this.accumulatedModelEvents.concat(events);
			if (this.handleAccumulatedModelEventsTimeout === -1) {
				this.handleAccumulatedModelEventsTimeout = setTimeout(() => {
					this.handleAccumulatedModelEventsTimeout = -1;
					this._flushAnyAccumulatedEvents();
				});
			}
		}));
	}

	private _flushAnyAccumulatedEvents(): void {
		var toEmit = this.accumulatedModelEvents;
		this.accumulatedModelEvents = [];
		if (toEmit.length > 0) {
			this.eventDispatcher.emitMany(toEmit);
		}
	}

	private createTextArea(keybindingService: IKeybindingService): void {
		// Text Area (The focus will always be in the textarea when the cursor is blinking)
		this.textArea = <HTMLTextAreaElement>document.createElement('textarea');
		this._keybindingService = keybindingService.createScoped(this.textArea);
A
Alex Dima 已提交
177 178
		this._editorTextFocusContextKey = this._keybindingService.createKey(editorCommon.KEYBINDING_CONTEXT_EDITOR_TEXT_FOCUS, undefined);
		this.textArea.className = editorBrowser.ClassNames.TEXTAREA;
E
Erich Gamma 已提交
179 180 181 182
		this.textArea.setAttribute('wrap', 'off');
		this.textArea.setAttribute('autocorrect', 'off');
		this.textArea.setAttribute('autocapitalize', 'off');
		this.textArea.setAttribute('spellcheck', 'false');
183
		this.textArea.setAttribute('aria-label', this._context.configuration.editor.viewInfo.ariaLabel);
E
Erich Gamma 已提交
184 185
		this.textArea.setAttribute('role', 'textbox');
		this.textArea.setAttribute('aria-multiline', 'true');
A
Alex Dima 已提交
186 187 188
		this.textArea.setAttribute('aria-haspopup', 'false');
		this.textArea.setAttribute('aria-autocomplete', 'both');

A
Alex Dima 已提交
189 190
		StyleMutator.setTop(this.textArea, 0);
		StyleMutator.setLeft(this.textArea, 0);
E
Erich Gamma 已提交
191

A
Alex Dima 已提交
192 193
		this.listenersToDispose.push(dom.addDisposableListener(this.textArea, 'focus', () => this._setHasFocus(true)));
		this.listenersToDispose.push(dom.addDisposableListener(this.textArea, 'blur', () => this._setHasFocus(false)));
E
Erich Gamma 已提交
194 195 196 197 198

		// On top of the text area, we position a dom node to cover it up
		// (there have been reports of tiny blinking cursors)
		// (in WebKit the textarea is 1px by 1px because it cannot handle input to a 0x0 textarea)
		this.textAreaCover = document.createElement('div');
199
		if (this._context.configuration.editor.viewInfo.glyphMargin) {
A
Alex Dima 已提交
200
			this.textAreaCover.className = 'monaco-editor-background ' + editorBrowser.ClassNames.GLYPH_MARGIN + ' ' + editorBrowser.ClassNames.TEXTAREA_COVER;
E
Erich Gamma 已提交
201
		} else {
202
			if (this._context.configuration.editor.viewInfo.lineNumbers) {
A
Alex Dima 已提交
203
				this.textAreaCover.className = 'monaco-editor-background ' + editorBrowser.ClassNames.LINE_NUMBERS + ' ' + editorBrowser.ClassNames.TEXTAREA_COVER;
E
Erich Gamma 已提交
204
			} else {
A
Alex Dima 已提交
205
				this.textAreaCover.className = 'monaco-editor-background ' + editorBrowser.ClassNames.TEXTAREA_COVER;
E
Erich Gamma 已提交
206 207 208
			}
		}
		this.textAreaCover.style.position = 'absolute';
A
Alex Dima 已提交
209 210 211 212
		StyleMutator.setWidth(this.textAreaCover, 1);
		StyleMutator.setHeight(this.textAreaCover, 1);
		StyleMutator.setTop(this.textAreaCover, 0);
		StyleMutator.setLeft(this.textAreaCover, 0);
E
Erich Gamma 已提交
213 214 215 216 217 218
	}

	private createViewParts(): void {
		this.viewParts = [];

		// View Lines
219
		this.viewLines = new ViewLines(this._context, this.layoutProvider);
E
Erich Gamma 已提交
220 221

		// View Zones
222
		this.viewZones = new ViewZones(this._context, this.layoutProvider);
E
Erich Gamma 已提交
223 224 225 226
		this.viewParts.push(this.viewZones);

		// Decorations overview ruler
		var decorationsOverviewRuler = new DecorationsOverviewRuler(
227
				this._context, this.layoutProvider.getScrollHeight(),
E
Erich Gamma 已提交
228 229 230 231 232
				(lineNumber:number) => this.layoutProvider.getVerticalOffsetForLineNumber(lineNumber)
		);
		this.viewParts.push(decorationsOverviewRuler);


233
		var scrollDecoration = new ScrollDecorationViewPart(this._context);
E
Erich Gamma 已提交
234 235
		this.viewParts.push(scrollDecoration);

236
		var contentViewOverlays = new ContentViewOverlays(this._context, this.layoutProvider);
E
Erich Gamma 已提交
237
		this.viewParts.push(contentViewOverlays);
238 239 240
		contentViewOverlays.addDynamicOverlay(new CurrentLineHighlightOverlay(this._context, this.layoutProvider));
		contentViewOverlays.addDynamicOverlay(new SelectionsOverlay(this._context));
		contentViewOverlays.addDynamicOverlay(new DecorationsOverlay(this._context));
241
		contentViewOverlays.addDynamicOverlay(new IndentGuidesOverlay(this._context));
E
Erich Gamma 已提交
242

243
		var marginViewOverlays = new MarginViewOverlays(this._context, this.layoutProvider);
E
Erich Gamma 已提交
244
		this.viewParts.push(marginViewOverlays);
245 246 247
		marginViewOverlays.addDynamicOverlay(new GlyphMarginOverlay(this._context));
		marginViewOverlays.addDynamicOverlay(new LinesDecorationsOverlay(this._context));
		marginViewOverlays.addDynamicOverlay(new LineNumbersOverlay(this._context));
E
Erich Gamma 已提交
248 249 250


		// Content widgets
251
		this.contentWidgets = new ViewContentWidgets(this._context, this.domNode);
E
Erich Gamma 已提交
252 253
		this.viewParts.push(this.contentWidgets);

254
		var viewCursors = new ViewCursors(this._context);
E
Erich Gamma 已提交
255 256 257
		this.viewParts.push(viewCursors);

		// Overlay widgets
258
		this.overlayWidgets = new ViewOverlayWidgets(this._context);
E
Erich Gamma 已提交
259 260
		this.viewParts.push(this.overlayWidgets);

261
		var rulers = new Rulers(this._context, this.layoutProvider);
262 263
		this.viewParts.push(rulers);

E
Erich Gamma 已提交
264 265 266 267 268 269 270 271 272 273 274
		// -------------- Wire dom nodes up

		this.linesContentContainer = this.layoutProvider.getScrollbarContainerDomNode();
		this.linesContentContainer.style.position = 'absolute';

		if (decorationsOverviewRuler) {
			var overviewRulerData = this.layoutProvider.getOverviewRulerInsertData();
			overviewRulerData.parent.insertBefore(decorationsOverviewRuler.getDomNode(), overviewRulerData.insertBefore);
		}

		this.linesContent.appendChild(contentViewOverlays.getDomNode());
275
		this.linesContent.appendChild(rulers.domNode);
E
Erich Gamma 已提交
276
		this.linesContent.appendChild(this.viewZones.domNode);
A
Alex Dima 已提交
277
		this.linesContent.appendChild(this.viewLines.getDomNode());
E
Erich Gamma 已提交
278 279 280 281 282 283 284 285 286
		this.linesContent.appendChild(this.contentWidgets.domNode);
		this.linesContent.appendChild(viewCursors.getDomNode());
		this.overflowGuardContainer.appendChild(marginViewOverlays.getDomNode());
		this.overflowGuardContainer.appendChild(this.linesContentContainer);
		this.overflowGuardContainer.appendChild(scrollDecoration.getDomNode());
		this.overflowGuardContainer.appendChild(this.overlayWidgets.domNode);
		this.overflowGuardContainer.appendChild(this.textArea);
		this.overflowGuardContainer.appendChild(this.textAreaCover);
		this.domNode.appendChild(this.overflowGuardContainer);
287
		this.domNode.appendChild(this.contentWidgets.overflowingContentWidgetsDomNode);
E
Erich Gamma 已提交
288 289 290 291 292 293 294
	}

	private _flushAccumulatedAndRenderNow(): void {
		this._flushAnyAccumulatedEvents();
		this._renderNow();
	}

295
	private createPointerHandlerHelper(): IPointerHandlerHelper {
E
Erich Gamma 已提交
296 297 298 299 300 301 302 303 304 305 306
		return {
			viewDomNode: this.domNode,
			linesContentDomNode: this.linesContent,

			focusTextArea: () => {
				if (this._isDisposed) {
					throw new Error('ViewImpl.pointerHandler.focusTextArea: View is disposed');
				}
				this.focus();
			},

307 308 309 310
			isDirty: (): boolean => {
				return (this.accumulatedModelEvents.length > 0);
			},

E
Erich Gamma 已提交
311 312 313 314 315 316
			getScrollLeft: () => {
				if (this._isDisposed) {
					throw new Error('ViewImpl.pointerHandler.getScrollLeft: View is disposed');
				}
				return this.layoutProvider.getScrollLeft();
			},
317 318 319 320 321 322 323 324
			getScrollTop: () => {
				if (this._isDisposed) {
					throw new Error('ViewImpl.pointerHandler.getScrollTop: View is disposed');
				}
				return this.layoutProvider.getScrollTop();
			},

			setScrollPosition: (position:editorCommon.INewScrollPosition) => {
E
Erich Gamma 已提交
325
				if (this._isDisposed) {
326
					throw new Error('ViewImpl.pointerHandler.setScrollPosition: View is disposed');
E
Erich Gamma 已提交
327
				}
328
				this.layoutProvider.setScrollPosition(position);
E
Erich Gamma 已提交
329 330 331 332 333 334 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 363 364 365 366 367 368 369 370 371 372 373 374
			},

			isAfterLines: (verticalOffset: number) => {
				if (this._isDisposed) {
					throw new Error('ViewImpl.pointerHandler.isAfterLines: View is disposed');
				}
				return this.layoutProvider.isAfterLines(verticalOffset);
			},
			getLineNumberAtVerticalOffset: (verticalOffset: number) => {
				if (this._isDisposed) {
					throw new Error('ViewImpl.pointerHandler.getLineNumberAtVerticalOffset: View is disposed');
				}
				return this.layoutProvider.getLineNumberAtVerticalOffset(verticalOffset);
			},
			getVerticalOffsetForLineNumber: (lineNumber: number) => {
				if (this._isDisposed) {
					throw new Error('ViewImpl.pointerHandler.getVerticalOffsetForLineNumber: View is disposed');
				}
				return this.layoutProvider.getVerticalOffsetForLineNumber(lineNumber);
			},
			getWhitespaceAtVerticalOffset: (verticalOffset: number) => {
				if (this._isDisposed) {
					throw new Error('ViewImpl.pointerHandler.getWhitespaceAtVerticalOffset: View is disposed');
				}
				return this.layoutProvider.getWhitespaceAtVerticalOffset(verticalOffset);
			},
			shouldSuppressMouseDownOnViewZone: (viewZoneId: number) => {
				if (this._isDisposed) {
					throw new Error('ViewImpl.pointerHandler.shouldSuppressMouseDownOnViewZone: View is disposed');
				}
				return this.viewZones.shouldSuppressMouseDownOnViewZone(viewZoneId);
			},

			getPositionFromDOMInfo: (spanNode: HTMLElement, offset: number) => {
				if (this._isDisposed) {
					throw new Error('ViewImpl.pointerHandler.getPositionFromDOMInfo: View is disposed');
				}
				this._flushAccumulatedAndRenderNow();
				return this.viewLines.getPositionFromDOMInfo(spanNode, offset);
			},

			visibleRangeForPosition2: (lineNumber: number, column: number) => {
				if (this._isDisposed) {
					throw new Error('ViewImpl.pointerHandler.visibleRangeForPosition2: View is disposed');
				}
				this._flushAccumulatedAndRenderNow();
A
Alex Dima 已提交
375
				var visibleRanges = this.viewLines.visibleRangesForRange2(new Range(lineNumber, column, lineNumber, column), 0);
E
Erich Gamma 已提交
376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391
				if (!visibleRanges) {
					return null;
				}
				return visibleRanges[0];
			},

			getLineWidth: (lineNumber: number) => {
				if (this._isDisposed) {
					throw new Error('ViewImpl.pointerHandler.getLineWidth: View is disposed');
				}
				this._flushAccumulatedAndRenderNow();
				return this.viewLines.getLineWidth(lineNumber);
			}
		};
	}

392
	private createKeyboardHandlerHelper(): IKeyboardHandlerHelper {
E
Erich Gamma 已提交
393 394 395 396 397 398 399 400 401
		return {
			viewDomNode: this.domNode,
			textArea: this.textArea,
			visibleRangeForPositionRelativeToEditor: (lineNumber: number, column: number) => {
				if (this._isDisposed) {
					throw new Error('ViewImpl.keyboardHandler.visibleRangeForPositionRelativeToEditor: View is disposed');
				}
				this._flushAccumulatedAndRenderNow();
				var linesViewPortData = this.layoutProvider.getLinesViewportData();
A
Alex Dima 已提交
402
				var visibleRanges = this.viewLines.visibleRangesForRange2(new Range(lineNumber, column, lineNumber, column), linesViewPortData.visibleRangesDeltaTop);
E
Erich Gamma 已提交
403 404 405 406
				if (!visibleRanges) {
					return null;
				}
				return visibleRanges[0];
407 408 409
			},
			flushAnyAccumulatedEvents: () => {
				this._flushAnyAccumulatedEvents();
E
Erich Gamma 已提交
410 411 412 413
			}
		};
	}

A
Alex Dima 已提交
414 415 416 417 418 419 420 421 422 423 424 425 426 427
	public setAriaActiveDescendant(id:string): void {
		if (id) {
			this.textArea.setAttribute('role', 'combobox');
			if (this.textArea.getAttribute('aria-activedescendant') !== id) {
				this.textArea.setAttribute('aria-haspopup', 'true');
				this.textArea.setAttribute('aria-activedescendant', id);
			}
		} else {
			this.textArea.setAttribute('role', 'textbox');
			this.textArea.removeAttribute('aria-activedescendant');
			this.textArea.removeAttribute('aria-haspopup');
		}
	}

E
Erich Gamma 已提交
428 429
	// --- begin event handlers

430
	public onLayoutChanged(layoutInfo:editorCommon.EditorLayoutInfo): boolean {
A
Alex Dima 已提交
431
		if (browser.isChrome) {
A
tslint  
Alex Dima 已提交
432
			/* tslint:disable:no-unused-variable */
E
Erich Gamma 已提交
433 434 435
			// Access overflowGuardContainer.clientWidth to prevent relayouting bug in Chrome
			// See Bug 19676: Editor misses a layout event
			var clientWidth = this.overflowGuardContainer.clientWidth + 'px';
A
tslint  
Alex Dima 已提交
436
			/* tslint:enable:no-unused-variable */
E
Erich Gamma 已提交
437
		}
A
Alex Dima 已提交
438 439
		StyleMutator.setWidth(this.domNode, layoutInfo.width);
		StyleMutator.setHeight(this.domNode, layoutInfo.height);
E
Erich Gamma 已提交
440

A
Alex Dima 已提交
441 442
		StyleMutator.setWidth(this.overflowGuardContainer, layoutInfo.width);
		StyleMutator.setHeight(this.overflowGuardContainer, layoutInfo.height);
E
Erich Gamma 已提交
443

A
Alex Dima 已提交
444 445
		StyleMutator.setWidth(this.linesContent, 1000000);
		StyleMutator.setHeight(this.linesContent, 1000000);
E
Erich Gamma 已提交
446

A
Alex Dima 已提交
447 448 449
		StyleMutator.setLeft(this.linesContentContainer, layoutInfo.contentLeft);
		StyleMutator.setWidth(this.linesContentContainer, layoutInfo.contentWidth);
		StyleMutator.setHeight(this.linesContentContainer, layoutInfo.contentHeight);
E
Erich Gamma 已提交
450

A
Alex Dima 已提交
451
		this.outgoingEventBus.emit(editorCommon.EventType.ViewLayoutChanged, layoutInfo);
E
Erich Gamma 已提交
452 453
		return false;
	}
A
Alex Dima 已提交
454
	public onConfigurationChanged(e: editorCommon.IConfigurationChangedEvent): boolean {
455 456
		if (e.viewInfo.editorClassName) {
			this.domNode.className = this._context.configuration.editor.viewInfo.editorClassName;
E
Erich Gamma 已提交
457
		}
458 459
		if (e.viewInfo.ariaLabel) {
			this.textArea.setAttribute('aria-label', this._context.configuration.editor.viewInfo.ariaLabel);
460
		}
E
Erich Gamma 已提交
461 462
		return false;
	}
A
Alex Dima 已提交
463
	public onScrollChanged(e:editorCommon.IScrollEvent): boolean {
A
Alex Dima 已提交
464
		this.outgoingEventBus.emit('scroll', e);
465
		return false;
E
Erich Gamma 已提交
466 467
	}
	public onViewFocusChanged(isFocused:boolean): boolean {
A
Alex Dima 已提交
468
		dom.toggleClass(this.domNode, 'focused', isFocused);
E
Erich Gamma 已提交
469 470
		if (isFocused) {
			this._editorTextFocusContextKey.set(true);
A
Alex Dima 已提交
471
			this.outgoingEventBus.emit(editorCommon.EventType.ViewFocusGained, {});
E
Erich Gamma 已提交
472 473
		} else {
			this._editorTextFocusContextKey.reset();
A
Alex Dima 已提交
474
			this.outgoingEventBus.emit(editorCommon.EventType.ViewFocusLost, {});
E
Erich Gamma 已提交
475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493
		}
		return false;
	}
	// --- end event handlers

	public dispose(): void {
		this._isDisposed = true;
		if (this.handleAccumulatedModelEventsTimeout !== -1) {
			clearTimeout(this.handleAccumulatedModelEventsTimeout);
			this.handleAccumulatedModelEventsTimeout = -1;
		}
		if (this._renderAnimationFrame !== null) {
			this._renderAnimationFrame.dispose();
			this._renderAnimationFrame = null;
		}
		this.accumulatedModelEvents = [];

		this.eventDispatcher.removeEventHandler(this);
		this.outgoingEventBus.dispose();
A
Alex Dima 已提交
494
		this.listenersToRemove = dispose(this.listenersToRemove);
J
Joao Moreno 已提交
495
		this.listenersToDispose = dispose(this.listenersToDispose);
E
Erich Gamma 已提交
496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513

		this.keyboardHandler.dispose();
		this.pointerHandler.dispose();

		this.viewLines.dispose();

		// Destroy IViewPart second
		for (var i = 0, len = this.viewParts.length; i < len; i++) {
			this.viewParts[i].dispose();
		}
		this.viewParts = [];

		this.layoutProvider.dispose();
		this._keybindingService.dispose();
	}

	// --- begin Code Editor APIs

A
Alex Dima 已提交
514 515
	private codeEditorHelper:editorBrowser.ICodeEditorHelper;
	public getCodeEditorHelper(): editorBrowser.ICodeEditorHelper {
E
Erich Gamma 已提交
516 517
		if (!this.codeEditorHelper) {
			this.codeEditorHelper = {
518
				getScrollWidth: () => {
E
Erich Gamma 已提交
519
					if (this._isDisposed) {
520
						throw new Error('ViewImpl.codeEditorHelper.getScrollWidth: View is disposed');
E
Erich Gamma 已提交
521
					}
522
					return this.layoutProvider.getScrollWidth();
E
Erich Gamma 已提交
523 524 525 526 527 528 529
				},
				getScrollLeft: () => {
					if (this._isDisposed) {
						throw new Error('ViewImpl.codeEditorHelper.getScrollLeft: View is disposed');
					}
					return this.layoutProvider.getScrollLeft();
				},
530

E
Erich Gamma 已提交
531 532 533 534 535 536
				getScrollHeight: () => {
					if (this._isDisposed) {
						throw new Error('ViewImpl.codeEditorHelper.getScrollHeight: View is disposed');
					}
					return this.layoutProvider.getScrollHeight();
				},
537
				getScrollTop: () => {
E
Erich Gamma 已提交
538
					if (this._isDisposed) {
539
						throw new Error('ViewImpl.codeEditorHelper.getScrollTop: View is disposed');
E
Erich Gamma 已提交
540
					}
541
					return this.layoutProvider.getScrollTop();
E
Erich Gamma 已提交
542
				},
543 544 545 546 547 548 549 550

				setScrollPosition: (position:editorCommon.INewScrollPosition) => {
					if (this._isDisposed) {
						throw new Error('ViewImpl.codeEditorHelper.setScrollPosition: View is disposed');
					}
					this.layoutProvider.setScrollPosition(position);
				},

E
Erich Gamma 已提交
551 552 553 554
				getVerticalOffsetForPosition: (modelLineNumber:number, modelColumn:number) => {
					if (this._isDisposed) {
						throw new Error('ViewImpl.codeEditorHelper.getVerticalOffsetForPosition: View is disposed');
					}
555
					var modelPosition = this._context.model.validateModelPosition({
E
Erich Gamma 已提交
556 557 558
						lineNumber: modelLineNumber,
						column: modelColumn
					});
559
					var viewPosition = this._context.model.convertModelPositionToViewPosition(modelPosition.lineNumber, modelPosition.column);
E
Erich Gamma 已提交
560 561 562 563 564 565 566 567 568 569 570 571
					return this.layoutProvider.getVerticalOffsetForLineNumber(viewPosition.lineNumber);
				},
				delegateVerticalScrollbarMouseDown: (browserEvent: MouseEvent) => {
					if (this._isDisposed) {
						throw new Error('ViewImpl.codeEditorHelper.delegateVerticalScrollbarMouseDown: View is disposed');
					}
					this.layoutProvider.delegateVerticalScrollbarMouseDown(browserEvent);
				},
				getOffsetForColumn: (modelLineNumber: number, modelColumn: number) => {
					if (this._isDisposed) {
						throw new Error('ViewImpl.codeEditorHelper.getOffsetForColumn: View is disposed');
					}
572
					var modelPosition = this._context.model.validateModelPosition({
E
Erich Gamma 已提交
573 574 575
						lineNumber: modelLineNumber,
						column: modelColumn
					});
576
					var viewPosition = this._context.model.convertModelPositionToViewPosition(modelPosition.lineNumber, modelPosition.column);
E
Erich Gamma 已提交
577
					this._flushAccumulatedAndRenderNow();
A
Alex Dima 已提交
578
					var visibleRanges = this.viewLines.visibleRangesForRange2(new Range(viewPosition.lineNumber, viewPosition.column, viewPosition.lineNumber, viewPosition.column), 0);
E
Erich Gamma 已提交
579 580 581 582 583 584 585 586 587 588
					if (!visibleRanges) {
						return -1;
					}
					return visibleRanges[0].left;
				}
			};
		}
		return this.codeEditorHelper;
	}

589
	public getCenteredRangeInViewport(): Range {
E
Erich Gamma 已提交
590 591 592 593
		if (this._isDisposed) {
			throw new Error('ViewImpl.getCenteredRangeInViewport: View is disposed');
		}
		var viewLineNumber = this.layoutProvider.getCenteredViewLineNumberInViewport();
594
		var viewModel = this._context.model;
E
Erich Gamma 已提交
595 596 597 598 599 600 601 602
		var currentCenteredViewRange = new Range(viewLineNumber, 1, viewLineNumber, viewModel.getLineMaxColumn(viewLineNumber));
		return viewModel.convertViewRangeToModelRange(currentCenteredViewRange);
	}

//	public getLineInfoProvider():view.ILineInfoProvider {
//		return this.viewLines;
//	}

A
Alex Dima 已提交
603
	public getInternalEventBus(): IEventEmitter {
E
Erich Gamma 已提交
604 605 606 607 608 609
		if (this._isDisposed) {
			throw new Error('ViewImpl.getInternalEventBus: View is disposed');
		}
		return this.outgoingEventBus;
	}

A
Alex Dima 已提交
610
	public saveState(): editorCommon.IViewState {
E
Erich Gamma 已提交
611 612 613 614 615 616
		if (this._isDisposed) {
			throw new Error('ViewImpl.saveState: View is disposed');
		}
		return this.layoutProvider.saveState();
	}

A
Alex Dima 已提交
617
	public restoreState(state: editorCommon.IViewState): void {
E
Erich Gamma 已提交
618 619 620 621 622 623 624 625 626 627 628
		if (this._isDisposed) {
			throw new Error('ViewImpl.restoreState: View is disposed');
		}
		this._flushAnyAccumulatedEvents();
		return this.layoutProvider.restoreState(state);
	}

	public focus(): void {
		if (this._isDisposed) {
			throw new Error('ViewImpl.focus: View is disposed');
		}
629
		this.keyboardHandler.focusTextArea();
E
Erich Gamma 已提交
630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646

		// IE does not trigger the focus event immediately, so we must help it a little bit
		this._setHasFocus(true);
	}

	public isFocused(): boolean {
		if (this._isDisposed) {
			throw new Error('ViewImpl.isFocused: View is disposed');
		}
		return this.hasFocus;
	}

	public createOverviewRuler(cssClassName: string, minimumHeight: number, maximumHeight: number): OverviewRuler {
		if (this._isDisposed) {
			throw new Error('ViewImpl.createOverviewRuler: View is disposed');
		}
		return new OverviewRuler(
647
				this._context, cssClassName, this.layoutProvider.getScrollHeight(), minimumHeight, maximumHeight,
E
Erich Gamma 已提交
648 649 650 651
				(lineNumber:number) => this.layoutProvider.getVerticalOffsetForLineNumber(lineNumber)
		);
	}

A
Alex Dima 已提交
652
	public change(callback: (changeAccessor: editorBrowser.IViewZoneChangeAccessor) => any): boolean {
E
Erich Gamma 已提交
653 654 655 656 657 658 659
		if (this._isDisposed) {
			throw new Error('ViewImpl.change: View is disposed');
		}
		var zonesHaveChanged = false;
		this._renderOnce(() => {
			// Handle events to avoid "adjusting" newly inserted view zones
			this._flushAnyAccumulatedEvents();
A
Alex Dima 已提交
660 661
			var changeAccessor:editorBrowser.IViewZoneChangeAccessor = {
				addZone: (zone:editorBrowser.IViewZone): number => {
E
Erich Gamma 已提交
662 663 664 665 666 667 668 669 670 671 672
					zonesHaveChanged = true;
					return this.viewZones.addZone(zone);
				},
				removeZone: (id:number): void => {
					zonesHaveChanged = this.viewZones.removeZone(id) || zonesHaveChanged;
				},
				layoutZone: (id: number): void => {
					zonesHaveChanged = this.viewZones.layoutZone(id) || zonesHaveChanged;
				}
			};

A
Alex Dima 已提交
673
			var r: any = safeInvoke1Arg(callback, changeAccessor);
E
Erich Gamma 已提交
674 675 676 677 678 679

			// Invalidate changeAccessor
			changeAccessor.addZone = null;
			changeAccessor.removeZone = null;

			if (zonesHaveChanged) {
680
				this._context.privateViewEventBus.emit(editorCommon.EventType.ViewZonesChanged, null);
E
Erich Gamma 已提交
681 682 683 684 685 686 687
			}

			return r;
		});
		return zonesHaveChanged;
	}

A
Alex Dima 已提交
688
	public getWhitespaces(): editorCommon.IEditorWhitespace[]{
E
Erich Gamma 已提交
689 690 691 692 693 694
		if (this._isDisposed) {
			throw new Error('ViewImpl.getWhitespaces: View is disposed');
		}
		return this.layoutProvider.getWhitespaces();
	}

A
Alex Dima 已提交
695
	public addContentWidget(widgetData: editorBrowser.IContentWidgetData): void {
E
Erich Gamma 已提交
696 697 698 699 700 701 702 703 704
		if (this._isDisposed) {
			throw new Error('ViewImpl.addContentWidget: View is disposed');
		}
		this._renderOnce(() => {
			this.contentWidgets.addWidget(widgetData.widget);
			this.layoutContentWidget(widgetData);
		});
	}

A
Alex Dima 已提交
705
	public layoutContentWidget(widgetData: editorBrowser.IContentWidgetData): void {
E
Erich Gamma 已提交
706 707 708
		if (this._isDisposed) {
			throw new Error('ViewImpl.layoutContentWidget: View is disposed');
		}
709

710 711 712 713 714
		this._renderOnce(() => {
			let newPosition = widgetData.position ? widgetData.position.position : null;
			let newPreference = widgetData.position ? widgetData.position.preference : null;
			this.contentWidgets.setWidgetPosition(widgetData.widget, newPosition, newPreference);
		});
E
Erich Gamma 已提交
715 716
	}

A
Alex Dima 已提交
717
	public removeContentWidget(widgetData: editorBrowser.IContentWidgetData): void {
E
Erich Gamma 已提交
718 719 720 721 722 723 724 725
		if (this._isDisposed) {
			throw new Error('ViewImpl.removeContentWidget: View is disposed');
		}
		this._renderOnce(() => {
			this.contentWidgets.removeWidget(widgetData.widget);
		});
	}

A
Alex Dima 已提交
726
	public addOverlayWidget(widgetData: editorBrowser.IOverlayWidgetData): void {
E
Erich Gamma 已提交
727 728 729 730 731 732 733 734 735
		if (this._isDisposed) {
			throw new Error('ViewImpl.addOverlayWidget: View is disposed');
		}
		this._renderOnce(() => {
			this.overlayWidgets.addWidget(widgetData.widget);
			this.layoutOverlayWidget(widgetData);
		});
	}

A
Alex Dima 已提交
736
	public layoutOverlayWidget(widgetData: editorBrowser.IOverlayWidgetData): void {
E
Erich Gamma 已提交
737 738 739
		if (this._isDisposed) {
			throw new Error('ViewImpl.layoutOverlayWidget: View is disposed');
		}
740 741 742 743 744 745

		let newPreference = widgetData.position ? widgetData.position.preference : null;
		let shouldRender = this.overlayWidgets.setWidgetPosition(widgetData.widget, newPreference);
		if (shouldRender) {
			this._scheduleRender();
		}
E
Erich Gamma 已提交
746 747
	}

A
Alex Dima 已提交
748
	public removeOverlayWidget(widgetData: editorBrowser.IOverlayWidgetData): void {
E
Erich Gamma 已提交
749 750 751 752 753 754 755 756
		if (this._isDisposed) {
			throw new Error('ViewImpl.removeOverlayWidget: View is disposed');
		}
		this._renderOnce(() => {
			this.overlayWidgets.removeWidget(widgetData.widget);
		});
	}

757
	public render(now:boolean, everything:boolean): void {
E
Erich Gamma 已提交
758 759 760
		if (this._isDisposed) {
			throw new Error('ViewImpl.render: View is disposed');
		}
761 762 763 764
		if (everything) {
			// Force a render with a layout event
			this.layoutProvider.emitLayoutChangedEvent();
		}
765 766 767
		if (now) {
			this._flushAccumulatedAndRenderNow();
		}
E
Erich Gamma 已提交
768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783
	}

	public renderOnce(callback: () => any): any {
		if (this._isDisposed) {
			throw new Error('ViewImpl.renderOnce: View is disposed');
		}
		return this._renderOnce(callback);
	}

	// --- end Code Editor APIs

	private _renderOnce(callback: () => any): any {
		if (this._isDisposed) {
			throw new Error('ViewImpl._renderOnce: View is disposed');
		}
		return this.outgoingEventBus.deferredEmit(() => {
A
Alex Dima 已提交
784 785
			let r = safeInvokeNoArg(callback);
			this._scheduleRender();
E
Erich Gamma 已提交
786 787 788 789 790 791 792 793 794
			return r;
		});
	}

	private _scheduleRender(): void {
		if (this._isDisposed) {
			throw new Error('ViewImpl._scheduleRender: View is disposed');
		}
		if (this._renderAnimationFrame === null) {
A
Alex Dima 已提交
795
			this._renderAnimationFrame = dom.runAtThisOrScheduleAtNextAnimationFrame(this._onRenderScheduled.bind(this), 100);
E
Erich Gamma 已提交
796 797 798 799 800 801 802 803 804
		}
	}

	private _onRenderScheduled(): void {
		this._renderAnimationFrame = null;
		this._flushAccumulatedAndRenderNow();
	}

	private _renderNow(): void {
A
Alex Dima 已提交
805
		safeInvokeNoArg(() => this._actualRender());
E
Erich Gamma 已提交
806 807
	}

808
	private createRenderingContext(linesViewportData:ViewLinesViewportData): IRenderingContext {
E
Erich Gamma 已提交
809 810 811 812 813

		var vInfo = this.layoutProvider.getCurrentViewport();

		var deltaTop = linesViewportData.visibleRangesDeltaTop;

814
		var r:IRenderingContext = {
E
Erich Gamma 已提交
815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838
			linesViewportData: linesViewportData,
			scrollWidth: this.layoutProvider.getScrollWidth(),
			scrollHeight: this.layoutProvider.getScrollHeight(),

			visibleRange: linesViewportData.visibleRange,
			bigNumbersDelta: linesViewportData.bigNumbersDelta,

			viewportWidth: vInfo.width,
			viewportHeight: vInfo.height,
			viewportLeft: vInfo.left,
			viewportTop: vInfo.top,

			getScrolledTopFromAbsoluteTop: (absoluteTop:number) => {
				return this.layoutProvider.getScrolledTopFromAbsoluteTop(absoluteTop);
			},

			getViewportVerticalOffsetForLineNumber: (lineNumber:number) => {
				var verticalOffset = this.layoutProvider.getVerticalOffsetForLineNumber(lineNumber);
				var scrolledTop = this.layoutProvider.getScrolledTopFromAbsoluteTop(verticalOffset);
				return scrolledTop;
			},

			getDecorationsInViewport: () => linesViewportData.getDecorationsInViewport(),

A
Alex Dima 已提交
839
			linesVisibleRangesForRange: (range:editorCommon.IRange, includeNewLines:boolean) => {
E
Erich Gamma 已提交
840 841 842
				return this.viewLines.linesVisibleRangesForRange(range, includeNewLines);
			},

A
Alex Dima 已提交
843
			visibleRangeForPosition: (position:editorCommon.IPosition) => {
A
Alex Dima 已提交
844
				var visibleRanges = this.viewLines.visibleRangesForRange2(new Range(position.lineNumber, position.column, position.lineNumber, position.column), deltaTop);
E
Erich Gamma 已提交
845 846 847 848 849 850 851 852 853 854 855 856 857
				if (!visibleRanges) {
					return null;
				}
				return visibleRanges[0];
			},

			lineIsVisible: (lineNumber:number) => {
				return linesViewportData.visibleRange.startLineNumber <= lineNumber && lineNumber <= linesViewportData.visibleRange.endLineNumber;
			}
		};
		return r;
	}

858 859 860 861 862 863 864 865 866 867 868
	private _getViewPartsToRender(): ViewPart[] {
		let result:ViewPart[] = [];
		for (let i = 0, len = this.viewParts.length; i < len; i++) {
			let viewPart = this.viewParts[i];
			if (viewPart.shouldRender()) {
				result.push(viewPart);
			}
		}
		return result;
	}

A
Alex Dima 已提交
869
	private _actualRender(): void {
A
Alex Dima 已提交
870
		if (!dom.isInDOM(this.domNode)) {
E
Erich Gamma 已提交
871 872
			return;
		}
A
Alex Dima 已提交
873
		let t = timer.start(timer.Topic.EDITOR, 'View.render');
E
Erich Gamma 已提交
874

875
		let viewPartsToRender = this._getViewPartsToRender();
E
Erich Gamma 已提交
876

A
Alex Dima 已提交
877 878
		if (!this.viewLines.shouldRender() && viewPartsToRender.length === 0) {
			// Nothing to render
879
			this.keyboardHandler.writeToTextArea();
A
Alex Dima 已提交
880 881 882
			t.stop();
			return;
		}
E
Erich Gamma 已提交
883

A
Alex Dima 已提交
884
		let linesViewportData = this.layoutProvider.getLinesViewportData();
E
Erich Gamma 已提交
885

A
Alex Dima 已提交
886
		if (this.viewLines.shouldRender()) {
887 888 889
			this.viewLines.renderText(linesViewportData, () => {
				this.keyboardHandler.writeToTextArea();
			});
A
Alex Dima 已提交
890
			this.viewLines.onDidRender();
891 892 893

			// Rendering of viewLines might cause scroll events to occur, so collect view parts to render again
			viewPartsToRender = this._getViewPartsToRender();
894 895
		} else {
			this.keyboardHandler.writeToTextArea();
A
Alex Dima 已提交
896
		}
E
Erich Gamma 已提交
897

A
Alex Dima 已提交
898
		let renderingContext = this.createRenderingContext(linesViewportData);
A
Alex Dima 已提交
899

A
Alex Dima 已提交
900 901 902 903 904
		// Render the rest of the parts
		for (let i = 0, len = viewPartsToRender.length; i < len; i++) {
			let viewPart = viewPartsToRender[i];
			viewPart.prepareRender(renderingContext);
		}
A
Alex Dima 已提交
905

A
Alex Dima 已提交
906 907 908 909
		for (let i = 0, len = viewPartsToRender.length; i < len; i++) {
			let viewPart = viewPartsToRender[i];
			viewPart.render(renderingContext);
			viewPart.onDidRender();
E
Erich Gamma 已提交
910 911
		}

912 913 914
		// Render the scrollbar
		this.layoutProvider.renderScrollbar();

E
Erich Gamma 已提交
915 916 917 918 919 920
		t.stop();
	}

	private _setHasFocus(newHasFocus:boolean): void {
		if (this.hasFocus !== newHasFocus) {
			this.hasFocus = newHasFocus;
921
			this._context.privateViewEventBus.emit(editorCommon.EventType.ViewFocusChanged, this.hasFocus);
E
Erich Gamma 已提交
922 923 924 925
		}
	}
}

A
Alex Dima 已提交
926 927 928 929 930 931 932 933 934 935 936 937 938 939 940
function safeInvokeNoArg(func:Function): any {
	try {
		return func();
	} catch(e) {
		onUnexpectedError(e);
	}
}

function safeInvoke1Arg(func:Function, arg1:any): any {
	try {
		return func(arg1);
	} catch(e) {
		onUnexpectedError(e);
	}
}