monaco-editor
Version:
A browser based code editor
1,140 lines (1,139 loc) • 59.4 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
}
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
import * as Platform from '../../../common/platform.js';
import * as Browser from '../../../browser/browser.js';
import * as Lifecycle from '../../../common/lifecycle.js';
import * as DOM from '../../../browser/dom.js';
import * as Diff from '../../../common/diff/diff.js';
import * as Touch from '../../../browser/touch.js';
import * as strings from '../../../common/strings.js';
import * as Mouse from '../../../browser/mouseEvent.js';
import * as Keyboard from '../../../browser/keyboardEvent.js';
import * as dnd from './treeDnd.js';
import { ArrayIterator, MappedIterator } from '../../../common/iterator.js';
import { ScrollableElement } from '../../../browser/ui/scrollbar/scrollableElement.js';
import { HeightMap } from './treeViewModel.js';
import * as _ from './tree.js';
import { Emitter } from '../../../common/event.js';
import { DataTransfers } from '../../../browser/dnd.js';
import { DefaultTreestyler } from './treeDefaults.js';
import { Delayer, timeout } from '../../../common/async.js';
function removeFromParent(element) {
try {
element.parentElement.removeChild(element);
}
catch (e) {
// this will throw if this happens due to a blur event, nasty business
}
}
var RowCache = /** @class */ (function () {
function RowCache(context) {
this.context = context;
this._cache = { '': [] };
}
RowCache.prototype.alloc = function (templateId) {
var result = this.cache(templateId).pop();
if (!result) {
var content = document.createElement('div');
content.className = 'content';
var row = document.createElement('div');
row.appendChild(content);
var templateData = null;
try {
templateData = this.context.renderer.renderTemplate(this.context.tree, templateId, content);
}
catch (err) {
console.error('Tree usage error: exception while rendering template');
console.error(err);
}
result = {
element: row,
templateId: templateId,
templateData: templateData
};
}
return result;
};
RowCache.prototype.release = function (templateId, row) {
removeFromParent(row.element);
this.cache(templateId).push(row);
};
RowCache.prototype.cache = function (templateId) {
return this._cache[templateId] || (this._cache[templateId] = []);
};
RowCache.prototype.garbageCollect = function () {
var _this = this;
if (this._cache) {
Object.keys(this._cache).forEach(function (templateId) {
_this._cache[templateId].forEach(function (cachedRow) {
_this.context.renderer.disposeTemplate(_this.context.tree, templateId, cachedRow.templateData);
cachedRow.element = null;
cachedRow.templateData = null;
});
delete _this._cache[templateId];
});
}
};
RowCache.prototype.dispose = function () {
this.garbageCollect();
this._cache = null;
this.context = null;
};
return RowCache;
}());
export { RowCache };
var ViewItem = /** @class */ (function () {
function ViewItem(context, model) {
var _this = this;
this.width = 0;
this.context = context;
this.model = model;
this.id = this.model.id;
this.row = null;
this.top = 0;
this.height = model.getHeight();
this._styles = {};
model.getAllTraits().forEach(function (t) { return _this._styles[t] = true; });
if (model.isExpanded()) {
this.addClass('expanded');
}
}
Object.defineProperty(ViewItem.prototype, "expanded", {
set: function (value) {
value ? this.addClass('expanded') : this.removeClass('expanded');
},
enumerable: true,
configurable: true
});
Object.defineProperty(ViewItem.prototype, "loading", {
set: function (value) {
value ? this.addClass('loading') : this.removeClass('loading');
},
enumerable: true,
configurable: true
});
Object.defineProperty(ViewItem.prototype, "draggable", {
get: function () {
return this._draggable;
},
set: function (value) {
this._draggable = value;
this.render(true);
},
enumerable: true,
configurable: true
});
Object.defineProperty(ViewItem.prototype, "dropTarget", {
set: function (value) {
value ? this.addClass('drop-target') : this.removeClass('drop-target');
},
enumerable: true,
configurable: true
});
Object.defineProperty(ViewItem.prototype, "element", {
get: function () {
return this.row && this.row.element;
},
enumerable: true,
configurable: true
});
Object.defineProperty(ViewItem.prototype, "templateId", {
get: function () {
return this._templateId || (this._templateId = (this.context.renderer.getTemplateId && this.context.renderer.getTemplateId(this.context.tree, this.model.getElement())));
},
enumerable: true,
configurable: true
});
ViewItem.prototype.addClass = function (name) {
this._styles[name] = true;
this.render(true);
};
ViewItem.prototype.removeClass = function (name) {
delete this._styles[name]; // is this slow?
this.render(true);
};
ViewItem.prototype.render = function (skipUserRender) {
var _this = this;
if (skipUserRender === void 0) { skipUserRender = false; }
if (!this.model || !this.element) {
return;
}
var classes = ['monaco-tree-row'];
classes.push.apply(classes, Object.keys(this._styles));
if (this.model.hasChildren()) {
classes.push('has-children');
}
this.element.className = classes.join(' ');
this.element.draggable = this.draggable;
this.element.style.height = this.height + 'px';
// ARIA
this.element.setAttribute('role', 'treeitem');
var accessibility = this.context.accessibilityProvider;
var ariaLabel = accessibility.getAriaLabel(this.context.tree, this.model.getElement());
if (ariaLabel) {
this.element.setAttribute('aria-label', ariaLabel);
}
if (accessibility.getPosInSet && accessibility.getSetSize) {
this.element.setAttribute('aria-setsize', accessibility.getSetSize());
this.element.setAttribute('aria-posinset', accessibility.getPosInSet(this.context.tree, this.model.getElement()));
}
if (this.model.hasTrait('focused')) {
var base64Id = strings.safeBtoa(this.model.id);
this.element.setAttribute('aria-selected', 'true');
this.element.setAttribute('id', base64Id);
}
else {
this.element.setAttribute('aria-selected', 'false');
this.element.removeAttribute('id');
}
if (this.model.hasChildren()) {
this.element.setAttribute('aria-expanded', String(!!this._styles['expanded']));
}
else {
this.element.removeAttribute('aria-expanded');
}
this.element.setAttribute('aria-level', String(this.model.getDepth()));
if (this.context.options.paddingOnRow) {
this.element.style.paddingLeft = this.context.options.twistiePixels + ((this.model.getDepth() - 1) * this.context.options.indentPixels) + 'px';
}
else {
this.element.style.paddingLeft = ((this.model.getDepth() - 1) * this.context.options.indentPixels) + 'px';
this.row.element.firstElementChild.style.paddingLeft = this.context.options.twistiePixels + 'px';
}
var uri = this.context.dnd.getDragURI(this.context.tree, this.model.getElement());
if (uri !== this.uri) {
if (this.unbindDragStart) {
this.unbindDragStart.dispose();
this.unbindDragStart = null;
}
if (uri) {
this.uri = uri;
this.draggable = true;
this.unbindDragStart = DOM.addDisposableListener(this.element, 'dragstart', function (e) {
_this.onDragStart(e);
});
}
else {
this.uri = null;
}
}
if (!skipUserRender && this.element) {
var style = window.getComputedStyle(this.element);
var paddingLeft = parseFloat(style.paddingLeft);
if (this.context.horizontalScrolling) {
this.element.style.width = 'fit-content';
}
try {
this.context.renderer.renderElement(this.context.tree, this.model.getElement(), this.templateId, this.row.templateData);
}
catch (err) {
console.error('Tree usage error: exception while rendering element');
console.error(err);
}
if (this.context.horizontalScrolling) {
this.width = DOM.getContentWidth(this.element) + paddingLeft;
this.element.style.width = '';
}
}
};
ViewItem.prototype.insertInDOM = function (container, afterElement) {
if (!this.row) {
this.row = this.context.cache.alloc(this.templateId);
// used in reverse lookup from HTMLElement to Item
this.element[TreeView.BINDING] = this;
}
if (this.element.parentElement) {
return;
}
if (afterElement === null) {
container.appendChild(this.element);
}
else {
try {
container.insertBefore(this.element, afterElement);
}
catch (e) {
console.warn('Failed to locate previous tree element');
container.appendChild(this.element);
}
}
this.render();
};
ViewItem.prototype.removeFromDOM = function () {
if (!this.row) {
return;
}
if (this.unbindDragStart) {
this.unbindDragStart.dispose();
this.unbindDragStart = null;
}
this.uri = null;
this.element[TreeView.BINDING] = null;
this.context.cache.release(this.templateId, this.row);
this.row = null;
};
ViewItem.prototype.dispose = function () {
this.row = null;
this.model = null;
};
return ViewItem;
}());
export { ViewItem };
var RootViewItem = /** @class */ (function (_super) {
__extends(RootViewItem, _super);
function RootViewItem(context, model, wrapper) {
var _this = _super.call(this, context, model) || this;
_this.row = {
element: wrapper,
templateData: null,
templateId: null
};
return _this;
}
RootViewItem.prototype.render = function () {
if (!this.model || !this.element) {
return;
}
var classes = ['monaco-tree-wrapper'];
classes.push.apply(classes, Object.keys(this._styles));
if (this.model.hasChildren()) {
classes.push('has-children');
}
this.element.className = classes.join(' ');
};
RootViewItem.prototype.insertInDOM = function (container, afterElement) {
// noop
};
RootViewItem.prototype.removeFromDOM = function () {
// noop
};
return RootViewItem;
}(ViewItem));
function reactionEquals(one, other) {
if (!one && !other) {
return true;
}
else if (!one || !other) {
return false;
}
else if (one.accept !== other.accept) {
return false;
}
else if (one.bubble !== other.bubble) {
return false;
}
else if (one.effect !== other.effect) {
return false;
}
else {
return true;
}
}
var TreeView = /** @class */ (function (_super) {
__extends(TreeView, _super);
function TreeView(context, container) {
var _this = _super.call(this) || this;
_this.lastClickTimeStamp = 0;
_this.contentWidthUpdateDelayer = new Delayer(50);
_this.isRefreshing = false;
_this.refreshingPreviousChildrenIds = {};
_this.currentDropDisposable = Lifecycle.Disposable.None;
_this._onDOMFocus = new Emitter();
_this._onDOMBlur = new Emitter();
_this._onDidScroll = new Emitter();
TreeView.counter++;
_this.instance = TreeView.counter;
var horizontalScrollMode = typeof context.options.horizontalScrollMode === 'undefined' ? 2 /* Hidden */ : context.options.horizontalScrollMode;
_this.horizontalScrolling = horizontalScrollMode !== 2 /* Hidden */;
_this.context = {
dataSource: context.dataSource,
renderer: context.renderer,
controller: context.controller,
dnd: context.dnd,
filter: context.filter,
sorter: context.sorter,
tree: context.tree,
accessibilityProvider: context.accessibilityProvider,
options: context.options,
cache: new RowCache(context),
horizontalScrolling: _this.horizontalScrolling
};
_this.modelListeners = [];
_this.viewListeners = [];
_this.model = null;
_this.items = {};
_this.domNode = document.createElement('div');
_this.domNode.className = "monaco-tree no-focused-item monaco-tree-instance-" + _this.instance;
// to allow direct tabbing into the tree instead of first focusing the tree
_this.domNode.tabIndex = context.options.preventRootFocus ? -1 : 0;
_this.styleElement = DOM.createStyleSheet(_this.domNode);
_this.treeStyler = context.styler;
if (!_this.treeStyler) {
_this.treeStyler = new DefaultTreestyler(_this.styleElement, "monaco-tree-instance-" + _this.instance);
}
// ARIA
_this.domNode.setAttribute('role', 'tree');
if (_this.context.options.ariaLabel) {
_this.domNode.setAttribute('aria-label', _this.context.options.ariaLabel);
}
if (_this.context.options.alwaysFocused) {
DOM.addClass(_this.domNode, 'focused');
}
if (!_this.context.options.paddingOnRow) {
DOM.addClass(_this.domNode, 'no-row-padding');
}
_this.wrapper = document.createElement('div');
_this.wrapper.className = 'monaco-tree-wrapper';
_this.scrollableElement = new ScrollableElement(_this.wrapper, {
alwaysConsumeMouseWheel: true,
horizontal: horizontalScrollMode,
vertical: (typeof context.options.verticalScrollMode !== 'undefined' ? context.options.verticalScrollMode : 1 /* Auto */),
useShadows: context.options.useShadows
});
_this.scrollableElement.onScroll(function (e) {
_this.render(e.scrollTop, e.height, e.scrollLeft, e.width, e.scrollWidth);
_this._onDidScroll.fire();
});
if (Browser.isIE) {
_this.wrapper.style.msTouchAction = 'none';
_this.wrapper.style.msContentZooming = 'none';
}
else {
Touch.Gesture.addTarget(_this.wrapper);
}
_this.rowsContainer = document.createElement('div');
_this.rowsContainer.className = 'monaco-tree-rows';
if (context.options.showTwistie) {
_this.rowsContainer.className += ' show-twisties';
}
var focusTracker = DOM.trackFocus(_this.domNode);
_this.viewListeners.push(focusTracker.onDidFocus(function () { return _this.onFocus(); }));
_this.viewListeners.push(focusTracker.onDidBlur(function () { return _this.onBlur(); }));
_this.viewListeners.push(focusTracker);
_this.viewListeners.push(DOM.addDisposableListener(_this.domNode, 'keydown', function (e) { return _this.onKeyDown(e); }));
_this.viewListeners.push(DOM.addDisposableListener(_this.domNode, 'keyup', function (e) { return _this.onKeyUp(e); }));
_this.viewListeners.push(DOM.addDisposableListener(_this.domNode, 'mousedown', function (e) { return _this.onMouseDown(e); }));
_this.viewListeners.push(DOM.addDisposableListener(_this.domNode, 'mouseup', function (e) { return _this.onMouseUp(e); }));
_this.viewListeners.push(DOM.addDisposableListener(_this.wrapper, 'auxclick', function (e) {
if (e && e.button === 1) {
_this.onMouseMiddleClick(e);
}
}));
_this.viewListeners.push(DOM.addDisposableListener(_this.wrapper, 'click', function (e) { return _this.onClick(e); }));
_this.viewListeners.push(DOM.addDisposableListener(_this.domNode, 'contextmenu', function (e) { return _this.onContextMenu(e); }));
_this.viewListeners.push(DOM.addDisposableListener(_this.wrapper, Touch.EventType.Tap, function (e) { return _this.onTap(e); }));
_this.viewListeners.push(DOM.addDisposableListener(_this.wrapper, Touch.EventType.Change, function (e) { return _this.onTouchChange(e); }));
if (Browser.isIE) {
_this.viewListeners.push(DOM.addDisposableListener(_this.wrapper, 'MSPointerDown', function (e) { return _this.onMsPointerDown(e); }));
_this.viewListeners.push(DOM.addDisposableListener(_this.wrapper, 'MSGestureTap', function (e) { return _this.onMsGestureTap(e); }));
// these events come too fast, we throttle them
_this.viewListeners.push(DOM.addDisposableThrottledListener(_this.wrapper, 'MSGestureChange', function (e) { return _this.onThrottledMsGestureChange(e); }, function (lastEvent, event) {
event.stopPropagation();
event.preventDefault();
var result = { translationY: event.translationY, translationX: event.translationX };
if (lastEvent) {
result.translationY += lastEvent.translationY;
result.translationX += lastEvent.translationX;
}
return result;
}));
}
_this.viewListeners.push(DOM.addDisposableListener(window, 'dragover', function (e) { return _this.onDragOver(e); }));
_this.viewListeners.push(DOM.addDisposableListener(_this.wrapper, 'drop', function (e) { return _this.onDrop(e); }));
_this.viewListeners.push(DOM.addDisposableListener(window, 'dragend', function (e) { return _this.onDragEnd(e); }));
_this.viewListeners.push(DOM.addDisposableListener(window, 'dragleave', function (e) { return _this.onDragOver(e); }));
_this.wrapper.appendChild(_this.rowsContainer);
_this.domNode.appendChild(_this.scrollableElement.getDomNode());
container.appendChild(_this.domNode);
_this.lastRenderTop = 0;
_this.lastRenderHeight = 0;
_this.didJustPressContextMenuKey = false;
_this.currentDropTarget = null;
_this.currentDropTargets = [];
_this.shouldInvalidateDropReaction = false;
_this.dragAndDropScrollInterval = null;
_this.dragAndDropScrollTimeout = null;
_this.onHiddenScrollTop = null;
_this.onRowsChanged();
_this.layout();
_this.setupMSGesture();
_this.applyStyles(context.options);
return _this;
}
Object.defineProperty(TreeView.prototype, "onDOMFocus", {
get: function () { return this._onDOMFocus.event; },
enumerable: true,
configurable: true
});
TreeView.prototype.applyStyles = function (styles) {
this.treeStyler.style(styles);
};
TreeView.prototype.createViewItem = function (item) {
return new ViewItem(this.context, item);
};
TreeView.prototype.getHTMLElement = function () {
return this.domNode;
};
TreeView.prototype.focus = function () {
this.domNode.focus();
};
TreeView.prototype.isFocused = function () {
return document.activeElement === this.domNode;
};
TreeView.prototype.blur = function () {
this.domNode.blur();
};
TreeView.prototype.setupMSGesture = function () {
var _this = this;
if (window.MSGesture) {
this.msGesture = new MSGesture();
setTimeout(function () { return _this.msGesture.target = _this.wrapper; }, 100); // TODO@joh, TODO@IETeam
}
};
TreeView.prototype.isTreeVisible = function () {
return this.onHiddenScrollTop === null;
};
TreeView.prototype.layout = function (height, width) {
if (!this.isTreeVisible()) {
return;
}
this.viewHeight = height || DOM.getContentHeight(this.wrapper); // render
this.scrollHeight = this.getContentHeight();
if (this.horizontalScrolling) {
this.viewWidth = width || DOM.getContentWidth(this.wrapper);
}
};
TreeView.prototype.render = function (scrollTop, viewHeight, scrollLeft, viewWidth, scrollWidth) {
var i;
var stop;
var renderTop = scrollTop;
var renderBottom = scrollTop + viewHeight;
var thisRenderBottom = this.lastRenderTop + this.lastRenderHeight;
// when view scrolls down, start rendering from the renderBottom
for (i = this.indexAfter(renderBottom) - 1, stop = this.indexAt(Math.max(thisRenderBottom, renderTop)); i >= stop; i--) {
this.insertItemInDOM(this.itemAtIndex(i));
}
// when view scrolls up, start rendering from either this.renderTop or renderBottom
for (i = Math.min(this.indexAt(this.lastRenderTop), this.indexAfter(renderBottom)) - 1, stop = this.indexAt(renderTop); i >= stop; i--) {
this.insertItemInDOM(this.itemAtIndex(i));
}
// when view scrolls down, start unrendering from renderTop
for (i = this.indexAt(this.lastRenderTop), stop = Math.min(this.indexAt(renderTop), this.indexAfter(thisRenderBottom)); i < stop; i++) {
this.removeItemFromDOM(this.itemAtIndex(i));
}
// when view scrolls up, start unrendering from either renderBottom this.renderTop
for (i = Math.max(this.indexAfter(renderBottom), this.indexAt(this.lastRenderTop)), stop = this.indexAfter(thisRenderBottom); i < stop; i++) {
this.removeItemFromDOM(this.itemAtIndex(i));
}
var topItem = this.itemAtIndex(this.indexAt(renderTop));
if (topItem) {
this.rowsContainer.style.top = (topItem.top - renderTop) + 'px';
}
if (this.horizontalScrolling) {
this.rowsContainer.style.left = -scrollLeft + 'px';
this.rowsContainer.style.width = Math.max(scrollWidth, viewWidth) + "px";
}
this.lastRenderTop = renderTop;
this.lastRenderHeight = renderBottom - renderTop;
};
TreeView.prototype.setModel = function (newModel) {
this.releaseModel();
this.model = newModel;
this.model.onRefresh(this.onRefreshing, this, this.modelListeners);
this.model.onDidRefresh(this.onRefreshed, this, this.modelListeners);
this.model.onSetInput(this.onClearingInput, this, this.modelListeners);
this.model.onDidSetInput(this.onSetInput, this, this.modelListeners);
this.model.onDidFocus(this.onModelFocusChange, this, this.modelListeners);
this.model.onRefreshItemChildren(this.onItemChildrenRefreshing, this, this.modelListeners);
this.model.onDidRefreshItemChildren(this.onItemChildrenRefreshed, this, this.modelListeners);
this.model.onDidRefreshItem(this.onItemRefresh, this, this.modelListeners);
this.model.onExpandItem(this.onItemExpanding, this, this.modelListeners);
this.model.onDidExpandItem(this.onItemExpanded, this, this.modelListeners);
this.model.onCollapseItem(this.onItemCollapsing, this, this.modelListeners);
this.model.onDidRevealItem(this.onItemReveal, this, this.modelListeners);
this.model.onDidAddTraitItem(this.onItemAddTrait, this, this.modelListeners);
this.model.onDidRemoveTraitItem(this.onItemRemoveTrait, this, this.modelListeners);
};
TreeView.prototype.onRefreshing = function () {
this.isRefreshing = true;
};
TreeView.prototype.onRefreshed = function () {
this.isRefreshing = false;
this.onRowsChanged();
};
TreeView.prototype.onRowsChanged = function (scrollTop) {
if (scrollTop === void 0) { scrollTop = this.scrollTop; }
if (this.isRefreshing) {
return;
}
this.scrollTop = scrollTop;
this.updateScrollWidth();
};
TreeView.prototype.updateScrollWidth = function () {
var _this = this;
if (!this.horizontalScrolling) {
return;
}
this.contentWidthUpdateDelayer.trigger(function () {
var keys = Object.keys(_this.items);
var scrollWidth = 0;
for (var _i = 0, keys_1 = keys; _i < keys_1.length; _i++) {
var key = keys_1[_i];
scrollWidth = Math.max(scrollWidth, _this.items[key].width);
}
_this.scrollWidth = scrollWidth + 10 /* scrollbar */;
});
};
TreeView.prototype.focusNextPage = function (eventPayload) {
var _this = this;
var lastPageIndex = this.indexAt(this.scrollTop + this.viewHeight);
lastPageIndex = lastPageIndex === 0 ? 0 : lastPageIndex - 1;
var lastPageElement = this.itemAtIndex(lastPageIndex).model.getElement();
var currentlyFocusedElement = this.model.getFocus();
if (currentlyFocusedElement !== lastPageElement) {
this.model.setFocus(lastPageElement, eventPayload);
}
else {
var previousScrollTop = this.scrollTop;
this.scrollTop += this.viewHeight;
if (this.scrollTop !== previousScrollTop) {
// Let the scroll event listener run
setTimeout(function () {
_this.focusNextPage(eventPayload);
}, 0);
}
}
};
TreeView.prototype.focusPreviousPage = function (eventPayload) {
var _this = this;
var firstPageIndex;
if (this.scrollTop === 0) {
firstPageIndex = this.indexAt(this.scrollTop);
}
else {
firstPageIndex = this.indexAfter(this.scrollTop - 1);
}
var firstPageElement = this.itemAtIndex(firstPageIndex).model.getElement();
var currentlyFocusedElement = this.model.getFocus();
if (currentlyFocusedElement !== firstPageElement) {
this.model.setFocus(firstPageElement, eventPayload);
}
else {
var previousScrollTop = this.scrollTop;
this.scrollTop -= this.viewHeight;
if (this.scrollTop !== previousScrollTop) {
// Let the scroll event listener run
setTimeout(function () {
_this.focusPreviousPage(eventPayload);
}, 0);
}
}
};
Object.defineProperty(TreeView.prototype, "viewHeight", {
get: function () {
var scrollDimensions = this.scrollableElement.getScrollDimensions();
return scrollDimensions.height;
},
set: function (height) {
this.scrollableElement.setScrollDimensions({ height: height });
},
enumerable: true,
configurable: true
});
Object.defineProperty(TreeView.prototype, "scrollHeight", {
set: function (scrollHeight) {
this.scrollableElement.setScrollDimensions({ scrollHeight: scrollHeight });
},
enumerable: true,
configurable: true
});
Object.defineProperty(TreeView.prototype, "viewWidth", {
get: function () {
var scrollDimensions = this.scrollableElement.getScrollDimensions();
return scrollDimensions.width;
},
set: function (viewWidth) {
this.scrollableElement.setScrollDimensions({ width: viewWidth });
},
enumerable: true,
configurable: true
});
Object.defineProperty(TreeView.prototype, "scrollWidth", {
set: function (scrollWidth) {
this.scrollableElement.setScrollDimensions({ scrollWidth: scrollWidth });
},
enumerable: true,
configurable: true
});
Object.defineProperty(TreeView.prototype, "scrollTop", {
get: function () {
var scrollPosition = this.scrollableElement.getScrollPosition();
return scrollPosition.scrollTop;
},
set: function (scrollTop) {
this.scrollableElement.setScrollDimensions({
scrollHeight: this.getContentHeight()
});
this.scrollableElement.setScrollPosition({
scrollTop: scrollTop
});
},
enumerable: true,
configurable: true
});
// Events
TreeView.prototype.onClearingInput = function (e) {
var item = e.item;
if (item) {
this.onRemoveItems(new MappedIterator(item.getNavigator(), function (item) { return item && item.id; }));
this.onRowsChanged();
}
};
TreeView.prototype.onSetInput = function (e) {
this.context.cache.garbageCollect();
this.inputItem = new RootViewItem(this.context, e.item, this.wrapper);
};
TreeView.prototype.onItemChildrenRefreshing = function (e) {
var item = e.item;
var viewItem = this.items[item.id];
if (viewItem && this.context.options.showLoading) {
viewItem.loadingTimer = setTimeout(function () {
viewItem.loadingTimer = 0;
viewItem.loading = true;
}, TreeView.LOADING_DECORATION_DELAY);
}
if (!e.isNested) {
var childrenIds = [];
var navigator = item.getNavigator();
var childItem;
while (childItem = navigator.next()) {
childrenIds.push(childItem.id);
}
this.refreshingPreviousChildrenIds[item.id] = childrenIds;
}
};
TreeView.prototype.onItemChildrenRefreshed = function (e) {
var _this = this;
var item = e.item;
var viewItem = this.items[item.id];
if (viewItem) {
if (viewItem.loadingTimer) {
clearTimeout(viewItem.loadingTimer);
viewItem.loadingTimer = 0;
}
viewItem.loading = false;
}
if (!e.isNested) {
var previousChildrenIds = this.refreshingPreviousChildrenIds[item.id];
var afterModelItems = [];
var navigator = item.getNavigator();
var childItem;
while (childItem = navigator.next()) {
afterModelItems.push(childItem);
}
var skipDiff = Math.abs(previousChildrenIds.length - afterModelItems.length) > 1000;
var diff = void 0;
var doToInsertItemsAlreadyExist = void 0;
if (!skipDiff) {
var lcs = new Diff.LcsDiff({
getLength: function () { return previousChildrenIds.length; },
getElementAtIndex: function (i) { return previousChildrenIds[i]; }
}, {
getLength: function () { return afterModelItems.length; },
getElementAtIndex: function (i) { return afterModelItems[i].id; }
}, null);
diff = lcs.ComputeDiff(false);
// this means that the result of the diff algorithm would result
// in inserting items that were already registered. this can only
// happen if the data provider returns bad ids OR if the sorting
// of the elements has changed
doToInsertItemsAlreadyExist = diff.some(function (d) {
if (d.modifiedLength > 0) {
for (var i = d.modifiedStart, len = d.modifiedStart + d.modifiedLength; i < len; i++) {
if (_this.items.hasOwnProperty(afterModelItems[i].id)) {
return true;
}
}
}
return false;
});
}
// 50 is an optimization number, at some point we're better off
// just replacing everything
if (!skipDiff && !doToInsertItemsAlreadyExist && diff.length < 50) {
for (var i = 0, len = diff.length; i < len; i++) {
var diffChange = diff[i];
if (diffChange.originalLength > 0) {
this.onRemoveItems(new ArrayIterator(previousChildrenIds, diffChange.originalStart, diffChange.originalStart + diffChange.originalLength));
}
if (diffChange.modifiedLength > 0) {
var beforeItem = afterModelItems[diffChange.modifiedStart - 1] || item;
beforeItem = beforeItem.getDepth() > 0 ? beforeItem : null;
this.onInsertItems(new ArrayIterator(afterModelItems, diffChange.modifiedStart, diffChange.modifiedStart + diffChange.modifiedLength), beforeItem ? beforeItem.id : null);
}
}
}
else if (skipDiff || diff.length) {
this.onRemoveItems(new ArrayIterator(previousChildrenIds));
this.onInsertItems(new ArrayIterator(afterModelItems), item.getDepth() > 0 ? item.id : null);
}
if (skipDiff || diff.length) {
this.onRowsChanged();
}
}
};
TreeView.prototype.onItemRefresh = function (item) {
this.onItemsRefresh([item]);
};
TreeView.prototype.onItemsRefresh = function (items) {
var _this = this;
this.onRefreshItemSet(items.filter(function (item) { return _this.items.hasOwnProperty(item.id); }));
this.onRowsChanged();
};
TreeView.prototype.onItemExpanding = function (e) {
var viewItem = this.items[e.item.id];
if (viewItem) {
viewItem.expanded = true;
}
};
TreeView.prototype.onItemExpanded = function (e) {
var item = e.item;
var viewItem = this.items[item.id];
if (viewItem) {
viewItem.expanded = true;
var height = this.onInsertItems(item.getNavigator(), item.id);
var scrollTop = this.scrollTop;
if (viewItem.top + viewItem.height <= this.scrollTop) {
scrollTop += height;
}
this.onRowsChanged(scrollTop);
}
};
TreeView.prototype.onItemCollapsing = function (e) {
var item = e.item;
var viewItem = this.items[item.id];
if (viewItem) {
viewItem.expanded = false;
this.onRemoveItems(new MappedIterator(item.getNavigator(), function (item) { return item && item.id; }));
this.onRowsChanged();
}
};
TreeView.prototype.onItemReveal = function (e) {
var item = e.item;
var relativeTop = e.relativeTop;
var viewItem = this.items[item.id];
if (viewItem) {
if (relativeTop !== null) {
relativeTop = relativeTop < 0 ? 0 : relativeTop;
relativeTop = relativeTop > 1 ? 1 : relativeTop;
// y = mx + b
var m = viewItem.height - this.viewHeight;
this.scrollTop = m * relativeTop + viewItem.top;
}
else {
var viewItemBottom = viewItem.top + viewItem.height;
var wrapperBottom = this.scrollTop + this.viewHeight;
if (viewItem.top < this.scrollTop) {
this.scrollTop = viewItem.top;
}
else if (viewItemBottom >= wrapperBottom) {
this.scrollTop = viewItemBottom - this.viewHeight;
}
}
}
};
TreeView.prototype.onItemAddTrait = function (e) {
var item = e.item;
var trait = e.trait;
var viewItem = this.items[item.id];
if (viewItem) {
viewItem.addClass(trait);
}
if (trait === 'highlighted') {
DOM.addClass(this.domNode, trait);
// Ugly Firefox fix: input fields can't be selected if parent nodes are draggable
if (viewItem) {
this.highlightedItemWasDraggable = !!viewItem.draggable;
if (viewItem.draggable) {
viewItem.draggable = false;
}
}
}
};
TreeView.prototype.onItemRemoveTrait = function (e) {
var item = e.item;
var trait = e.trait;
var viewItem = this.items[item.id];
if (viewItem) {
viewItem.removeClass(trait);
}
if (trait === 'highlighted') {
DOM.removeClass(this.domNode, trait);
// Ugly Firefox fix: input fields can't be selected if parent nodes are draggable
if (this.highlightedItemWasDraggable) {
viewItem.draggable = true;
}
this.highlightedItemWasDraggable = false;
}
};
TreeView.prototype.onModelFocusChange = function () {
var focus = this.model && this.model.getFocus();
DOM.toggleClass(this.domNode, 'no-focused-item', !focus);
// ARIA
if (focus) {
this.domNode.setAttribute('aria-activedescendant', strings.safeBtoa(this.context.dataSource.getId(this.context.tree, focus)));
}
else {
this.domNode.removeAttribute('aria-activedescendant');
}
};
// HeightMap "events"
TreeView.prototype.onInsertItem = function (item) {
var _this = this;
item.onDragStart = function (e) { _this.onDragStart(item, e); };
item.needsRender = true;
this.refreshViewItem(item);
this.items[item.id] = item;
};
TreeView.prototype.onRefreshItem = function (item, needsRender) {
if (needsRender === void 0) { needsRender = false; }
item.needsRender = item.needsRender || needsRender;
this.refreshViewItem(item);
};
TreeView.prototype.onRemoveItem = function (item) {
this.removeItemFromDOM(item);
item.dispose();
delete this.items[item.id];
};
// ViewItem refresh
TreeView.prototype.refreshViewItem = function (item) {
item.render();
if (this.shouldBeRendered(item)) {
this.insertItemInDOM(item);
}
else {
this.removeItemFromDOM(item);
}
};
// DOM Events
TreeView.prototype.onClick = function (e) {
if (this.lastPointerType && this.lastPointerType !== 'mouse') {
return;
}
var event = new Mouse.StandardMouseEvent(e);
var item = this.getItemAround(event.target);
if (!item) {
return;
}
if (Browser.isIE && Date.now() - this.lastClickTimeStamp < 300) {
// IE10+ doesn't set the detail property correctly. While IE10 simply
// counts the number of clicks, IE11 reports always 1. To align with
// other browser, we set the value to 2 if clicks events come in a 300ms
// sequence.
event.detail = 2;
}
this.lastClickTimeStamp = Date.now();
this.context.controller.onClick(this.context.tree, item.model.getElement(), event);
};
TreeView.prototype.onMouseMiddleClick = function (e) {
if (!this.context.controller.onMouseMiddleClick) {
return;
}
var event = new Mouse.StandardMouseEvent(e);
var item = this.getItemAround(event.target);
if (!item) {
return;
}
this.context.controller.onMouseMiddleClick(this.context.tree, item.model.getElement(), event);
};
TreeView.prototype.onMouseDown = function (e) {
this.didJustPressContextMenuKey = false;
if (!this.context.controller.onMouseDown) {
return;
}
if (this.lastPointerType && this.lastPointerType !== 'mouse') {
return;
}
var event = new Mouse.StandardMouseEvent(e);
if (event.ctrlKey && Platform.isNative && Platform.isMacintosh) {
return;
}
var item = this.getItemAround(event.target);
if (!item) {
return;
}
this.context.controller.onMouseDown(this.context.tree, item.model.getElement(), event);
};
TreeView.prototype.onMouseUp = function (e) {
if (!this.context.controller.onMouseUp) {
return;
}
if (this.lastPointerType && this.lastPointerType !== 'mouse') {
return;
}
var event = new Mouse.StandardMouseEvent(e);
if (event.ctrlKey && Platform.isNative && Platform.isMacintosh) {
return;
}
var item = this.getItemAround(event.target);
if (!item) {
return;
}
this.context.controller.onMouseUp(this.context.tree, item.model.getElement(), event);
};
TreeView.prototype.onTap = function (e) {
var item = this.getItemAround(e.initialTarget);
if (!item) {
return;
}
this.context.controller.onTap(this.context.tree, item.model.getElement(), e);
};
TreeView.prototype.onTouchChange = function (event) {
event.preventDefault();
event.stopPropagation();
this.scrollTop -= event.translationY;
};
TreeView.prototype.onContextMenu = function (event) {
var resultEvent;
var element;
if (event instanceof KeyboardEvent || this.didJustPressContextMenuKey) {
this.didJustPressContextMenuKey = false;
var keyboardEvent = new Keyboard.StandardKeyboardEvent(event);
element = this.model.getFocus();
var position;
if (!element) {
element = this.model.getInput();
position = DOM.getDomNodePagePosition(this.inputItem.element);
}
else {
var id = this.context.dataSource.getId(this.context.tree, element);
var viewItem = this.items[id];
position = DOM.getDomNodePagePosition(viewItem.element);
}
resultEvent = new _.KeyboardContextMenuEvent(position.left + position.width, position.top, keyboardEvent);
}
else {
var mouseEvent = new Mouse.StandardMouseEvent(event);
var item = this.getItemAround(mouseEvent.target);
if (!item) {
return;
}
element = item.model.getElement();
resultEvent = new _.MouseContextMenuEvent(mouseEvent);
}
this.context.controller.onContextMenu(this.context.tree, element, resultEvent);
};
TreeView.prototype.onKeyDown = function (e) {
var event = new Keyboard.StandardKeyboardEvent(e);
this.didJustPressContextMenuKey = event.keyCode === 58 /* ContextMenu */ || (event.shiftKey && event.keyCode === 68 /* F10 */);
if (event.target && event.target.tagName && event.target.tagName.toLowerCase() === 'input') {
return; // Ignore event if target is a form input field (avoids browser specific issues)
}
if (this.didJustPressContextMenuKey) {
event.preventDefault();
event.stopPropagation();
}
this.context.controller.onKeyDown(this.context.tree, event);
};
TreeView.prototype.onKeyUp = function (e) {
if (this.didJustPressContextMenuKey) {
this.onContextMenu(e);
}
this.didJustPressContextMenuKey = false;
this.context.controller.onKeyUp(this.context.tree, new Keyboard.StandardKeyboardEvent(e));
};
TreeView.prototype.onDragStart = function (item, e) {
if (this.model.getHighlight()) {
return;
}
var element = item.model.getElement();
var selection = this.model.getSelection();
var elements;
if (selection.indexOf(element) > -1) {
elements = selection;
}
else {
elements = [element];
}
e.dataTransfer.effectAllowed = 'copyMove';
e.dataTransfer.setData(DataTransfers.RESOURCES, JSON.stringify([item.uri]));
if (e.dataTransfer.setDragImage) {
var label = void 0;
if (this.context.dnd.getDragLabel) {
label = this.context.dnd.getDragLabel(this.context.tree, elements);
}
else {
label = String(elements.length);
}
var dragImage_1 = document.createElement('div');
dragImage_1.className = 'monaco-tree-drag-image';
dragImage_1.textContent = label;
document.body.appendChild(dragImage_1);
e.dataTransfer.setDragImage(dragImage_1, -10, -10);
setTimeout(function () { return document.body.removeChild(dragImage_1); }, 0);
}
this.currentDragAndDropData = new dnd.ElementsDragAndDropData(elements);
TreeView.currentExternalDragAndDropData = new dnd.ExternalElementsDragAndDropData(elements);
this.context.dnd.onDragStart(this.context.tree, this.currentDragAndDropData, new Mouse.DragMouseEvent(e));
};
TreeView.prototype.setupDragAndDropScrollInterval = function () {
var _this = this;
var viewTop = DOM.getTopLeftOffset(this.wrapper).top;
if (!this.dragAndDropScrollInterval) {
this.dragAndDropScrollInterval = window.setInterval(function () {
if (_this.dragAndDropMouseY === undefined) {
return;
}
var diff = _this.dragAndDropMouseY - viewTop;
var scrollDiff = 0;
var upperLimit = _this.viewHeight - 35;
if (diff < 35) {
scrollDiff = Math.max(-14, 0.2 * (diff - 35));
}
else if (diff > upperLimit) {
scrollDiff = Math.min(14, 0.2 * (diff - upperLimit));
}
_this.scrollTop += scrollDiff;
}, 10);
this.cancelDragAndDropScrollTimeout();
this.dragAndDropScrollTimeout = window.setTimeout(function () {
_this.cancelDragAndDropScrollInterval();
_this.dragAndDropScrollTimeout = null;
}, 1000);
}
};
TreeView.prototype.cancelDragAndDropScrollInterval = function () {
if (this.dragAndDropScrollInterval) {
window.clearInterval(this.dragAndDropScrollInterval);
this.dragAndDropScrollInterval = null;
}
this.cancelDragAndDropScrollTimeout();
};
TreeView.prototype.cancelDragAndDropScrollTimeout = function () {
if (this.dragAndDropScrollTimeout) {
window.clearTimeout(this.dragAndDropScrollTimeout);
this.dragAndDropScrollTimeout = null;
}
};
TreeView.prototype.onDragOver = function (e) {
var _this = this;
var event = new Mouse.DragMouseEvent(e);
var viewItem = this.getItemAround(event.target);
if (!viewItem || (event.posx === 0 && event.posy === 0 && event.browserEvent.type === DOM.EventType.DRAG_LEAVE)) {
// dragging outside of tree
if (this.currentDropTarget) {