diff -r 000000000000 -r 6474c204b198 browser/devtools/profiler/cleopatra/js/tree.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/browser/devtools/profiler/cleopatra/js/tree.js Wed Dec 31 06:09:35 2014 +0100
@@ -0,0 +1,702 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+var kMaxChunkDuration = 30; // ms
+
+function escapeHTML(html) {
+ var pre = document.createElementNS("http://www.w3.org/1999/xhtml", "pre");
+ var text = document.createTextNode(html);
+ pre.appendChild(text);
+ return pre.innerHTML;
+}
+
+RegExp.escape = function(text) {
+ return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
+}
+
+var requestAnimationFrame = window.webkitRequestAnimationFrame ||
+ window.mozRequestAnimationFrame ||
+ window.oRequestAnimationFrame ||
+ window.msRequestAnimationFrame ||
+ function(callback, element) {
+ return window.setTimeout(callback, 1000 / 60);
+ };
+
+var cancelAnimationFrame = window.webkitCancelAnimationFrame ||
+ window.mozCancelAnimationFrame ||
+ window.oCancelAnimationFrame ||
+ window.msCancelAnimationFrame ||
+ function(req) {
+ window.clearTimeout(req);
+ };
+
+function TreeView() {
+ this._eventListeners = {};
+ this._pendingActions = [];
+ this._pendingActionsProcessingCallback = null;
+
+ this._container = document.createElement("div");
+ this._container.className = "treeViewContainer";
+ this._container.setAttribute("tabindex", "0"); // make it focusable
+
+ this._header = document.createElement("ul");
+ this._header.className = "treeHeader";
+ this._container.appendChild(this._header);
+
+ this._verticalScrollbox = document.createElement("div");
+ this._verticalScrollbox.className = "treeViewVerticalScrollbox";
+ this._container.appendChild(this._verticalScrollbox);
+
+ this._leftColumnBackground = document.createElement("div");
+ this._leftColumnBackground.className = "leftColumnBackground";
+ this._verticalScrollbox.appendChild(this._leftColumnBackground);
+
+ this._horizontalScrollbox = document.createElement("div");
+ this._horizontalScrollbox.className = "treeViewHorizontalScrollbox";
+ this._verticalScrollbox.appendChild(this._horizontalScrollbox);
+
+ this._styleElement = document.createElement("style");
+ this._styleElement.setAttribute("type", "text/css");
+ this._container.appendChild(this._styleElement);
+
+ this._contextMenu = document.createElement("menu");
+ this._contextMenu.setAttribute("type", "context");
+ this._contextMenu.id = "contextMenuForTreeView" + TreeView.instanceCounter++;
+ this._container.appendChild(this._contextMenu);
+
+ this._busyCover = document.createElement("div");
+ this._busyCover.className = "busyCover";
+ this._container.appendChild(this._busyCover);
+ this._abortToggleAll = false;
+ this.initSelection = true;
+
+ var self = this;
+ this._container.onkeydown = function (e) {
+ self._onkeypress(e);
+ };
+ this._container.onkeypress = function (e) {
+ // on key down gives us '8' and mapping shift+8='*' may not be portable.
+ if (String.fromCharCode(e.charCode) == '*')
+ self._onkeypress(e);
+ };
+ this._container.onclick = function (e) {
+ self._onclick(e);
+ };
+ this._verticalScrollbox.addEventListener("contextmenu", function(event) {
+ self._populateContextMenu(event);
+ }, true);
+ this._setUpScrolling();
+};
+TreeView.instanceCounter = 0;
+
+TreeView.prototype = {
+ getContainer: function TreeView_getContainer() {
+ return this._container;
+ },
+ setColumns: function TreeView_setColumns(columns) {
+ this._header.innerHTML = "";
+ for (var i = 0; i < columns.length; i++) {
+ var li = document.createElement("li");
+ li.className = "treeColumnHeader treeColumnHeader" + i;
+ li.id = columns[i].name + "Header";
+ li.textContent = columns[i].title;
+ this._header.appendChild(li);
+ }
+ },
+ dataIsOutdated: function TreeView_dataIsOutdated() {
+ this._busyCover.classList.add("busy");
+ },
+ display: function TreeView_display(data, resources, filterByName) {
+ this._busyCover.classList.remove("busy");
+ this._filterByName = filterByName;
+ this._resources = resources;
+ this._addResourceIconStyles();
+ this._filterByNameReg = null; // lazy init
+ if (this._filterByName === "")
+ this._filterByName = null;
+ this._horizontalScrollbox.innerHTML = "";
+ this._horizontalScrollbox.data = data[0].getData();
+ if (this._pendingActionsProcessingCallback) {
+ cancelAnimationFrame(this._pendingActionsProcessingCallback);
+ this._pendingActionsProcessingCallback = 0;
+ }
+ this._pendingActions = [];
+
+ this._pendingActions.push({
+ parentElement: this._horizontalScrollbox,
+ parentNode: null,
+ data: data[0].getData()
+ });
+ this._processPendingActionsChunk();
+ changeFocus(this._container);
+ },
+ // Provide a snapshot of the reverse selection to restore with 'invert callback'
+ getReverseSelectionSnapshot: function TreeView__getReverseSelectionSnapshot(isJavascriptOnly) {
+ var snapshot = [];
+
+ if (!this._selectedNode) {
+ return snapshot;
+ }
+
+ var curr = this._selectedNode.data;
+
+ while(curr) {
+ if (isJavascriptOnly && curr.isJSFrame || !isJavascriptOnly) {
+ snapshot.push(curr.name);
+ //dump(JSON.stringify(curr.name) + "\n");
+ }
+ if (curr.treeChildren && curr.treeChildren.length >= 1) {
+ curr = curr.treeChildren[0].getData();
+ } else {
+ break;
+ }
+ }
+
+ return snapshot.reverse();
+ },
+ // Provide a snapshot of the current selection to restore
+ getSelectionSnapshot: function TreeView__getSelectionSnapshot(isJavascriptOnly) {
+ var snapshot = [];
+ var curr = this._selectedNode;
+
+ while(curr) {
+ if (isJavascriptOnly && curr.data.isJSFrame || !isJavascriptOnly) {
+ snapshot.push(curr.data.name);
+ //dump(JSON.stringify(curr.data.name) + "\n");
+ }
+ curr = curr.treeParent;
+ }
+
+ return snapshot.reverse();
+ },
+ setSelection: function TreeView_setSelection(frames) {
+ this.restoreSelectionSnapshot(frames, false);
+ },
+ // Take a selection snapshot and restore the selection
+ restoreSelectionSnapshot: function TreeView_restoreSelectionSnapshot(snapshot, allowNonContiguous) {
+ var currNode = this._horizontalScrollbox.firstChild;
+ if (currNode.data.name == snapshot[0] || snapshot[0] == "(total)") {
+ snapshot.shift();
+ }
+ //dump("len: " + snapshot.length + "\n");
+ next_level: while (currNode && snapshot.length > 0) {
+ this._toggle(currNode, false, true);
+ this._syncProcessPendingActionProcessing();
+ for (var i = 0; i < currNode.treeChildren.length; i++) {
+ if (currNode.treeChildren[i].data.name == snapshot[0]) {
+ snapshot.shift();
+ this._toggle(currNode, false, true);
+ currNode = currNode.treeChildren[i];
+ continue next_level;
+ }
+ }
+ if (allowNonContiguous) {
+ // We need to do a Breadth-first search to find a match
+ var pendingSearch = [currNode.data];
+ while (pendingSearch.length > 0) {
+ var node = pendingSearch.shift();
+ if (!node.treeChildren)
+ continue;
+ for (var i = 0; i < node.treeChildren.length; i++) {
+ var childNode = node.treeChildren[i].getData();
+ if (childNode.name == snapshot[0]) {
+ //dump("found: " + childNode.name + "\n");
+ snapshot.shift();
+ var nodesToToggle = [childNode];
+ while (nodesToToggle[0].name != currNode.data.name) {
+ nodesToToggle.splice(0, 0, nodesToToggle[0].parent);
+ }
+ var lastToggle = currNode;
+ for (var j = 0; j < nodesToToggle.length; j++) {
+ for (var k = 0; k < lastToggle.treeChildren.length; k++) {
+ if (lastToggle.treeChildren[k].data.name == nodesToToggle[j].name) {
+ //dump("Expend: " + nodesToToggle[j].name + "\n");
+ this._toggle(lastToggle.treeChildren[k], false, true);
+ lastToggle = lastToggle.treeChildren[k];
+ this._syncProcessPendingActionProcessing();
+ }
+ }
+ }
+ currNode = lastToggle;
+ continue next_level;
+ }
+ //dump("pending: " + childNode.name + "\n");
+ pendingSearch.push(childNode);
+ }
+ }
+ }
+ break; // Didn't find child node matching
+ }
+
+ if (currNode == this._horizontalScrollbox) {
+ PROFILERERROR("Failed to restore selection, could not find root.\n");
+ return;
+ }
+
+ this._toggle(currNode, true, true);
+ this._select(currNode);
+ },
+ _processPendingActionsChunk: function TreeView__processPendingActionsChunk(isSync) {
+ this._pendingActionsProcessingCallback = 0;
+
+ var startTime = Date.now();
+ var endTime = startTime + kMaxChunkDuration;
+ while ((isSync == true || Date.now() < endTime) && this._pendingActions.length > 0) {
+ this._processOneAction(this._pendingActions.shift());
+ }
+ this._scrollHeightChanged();
+
+ this._schedulePendingActionProcessing();
+ },
+ _schedulePendingActionProcessing: function TreeView__schedulePendingActionProcessing() {
+ if (!this._pendingActionsProcessingCallback && this._pendingActions.length > 0) {
+ var self = this;
+ this._pendingActionsProcessingCallback = requestAnimationFrame(function () {
+ self._processPendingActionsChunk();
+ });
+ }
+ },
+ _syncProcessPendingActionProcessing: function TreeView__syncProcessPendingActionProcessing() {
+ this._processPendingActionsChunk(true);
+ },
+ _processOneAction: function TreeView__processOneAction(action) {
+ var li = this._createTree(action.parentElement, action.parentNode, action.data);
+ if ("allChildrenCollapsedValue" in action) {
+ if (this._abortToggleAll)
+ return;
+ this._toggleAll(li, action.allChildrenCollapsedValue, true);
+ }
+ },
+ addEventListener: function TreeView_addEventListener(eventName, callbackFunction) {
+ if (!(eventName in this._eventListeners))
+ this._eventListeners[eventName] = [];
+ if (this._eventListeners[eventName].indexOf(callbackFunction) != -1)
+ return;
+ this._eventListeners[eventName].push(callbackFunction);
+ },
+ removeEventListener: function TreeView_removeEventListener(eventName, callbackFunction) {
+ if (!(eventName in this._eventListeners))
+ return;
+ var index = this._eventListeners[eventName].indexOf(callbackFunction);
+ if (index == -1)
+ return;
+ this._eventListeners[eventName].splice(index, 1);
+ },
+ _fireEvent: function TreeView__fireEvent(eventName, eventObject) {
+ if (!(eventName in this._eventListeners))
+ return;
+ this._eventListeners[eventName].forEach(function (callbackFunction) {
+ callbackFunction(eventObject);
+ });
+ },
+ _setUpScrolling: function TreeView__setUpScrolling() {
+ var waitingForPaint = false;
+ var accumulatedDeltaX = 0;
+ var accumulatedDeltaY = 0;
+ var self = this;
+ function scrollListener(e) {
+ if (!waitingForPaint) {
+ requestAnimationFrame(function () {
+ self._horizontalScrollbox.scrollLeft += accumulatedDeltaX;
+ self._verticalScrollbox.scrollTop += accumulatedDeltaY;
+ accumulatedDeltaX = 0;
+ accumulatedDeltaY = 0;
+ waitingForPaint = false;
+ });
+ waitingForPaint = true;
+ }
+ if (e.axis == e.HORIZONTAL_AXIS) {
+ accumulatedDeltaX += e.detail;
+ } else {
+ accumulatedDeltaY += e.detail;
+ }
+ e.preventDefault();
+ }
+ this._verticalScrollbox.addEventListener("MozMousePixelScroll", scrollListener, false);
+ this._verticalScrollbox.cleanUp = function () {
+ self._verticalScrollbox.removeEventListener("MozMousePixelScroll", scrollListener, false);
+ };
+ },
+ _scrollHeightChanged: function TreeView__scrollHeightChanged() {
+ if (!this._pendingScrollHeightChanged) {
+ var self = this;
+ this._pendingScrollHeightChanged = setTimeout(function() {
+ self._leftColumnBackground.style.height = self._horizontalScrollbox.getBoundingClientRect().height + 'px';
+ self._pendingScrollHeightChanged = null;
+ }, 0);
+ }
+ },
+ _createTree: function TreeView__createTree(parentElement, parentNode, data) {
+ var div = document.createElement("div");
+ div.className = "treeViewNode collapsed";
+ var hasChildren = ("children" in data) && (data.children.length > 0);
+ if (!hasChildren)
+ div.classList.add("leaf");
+ var treeLine = document.createElement("div");
+ treeLine.className = "treeLine";
+ treeLine.innerHTML = this._HTMLForFunction(data);
+ div.depth = parentNode ? parentNode.depth + 1 : 0;
+ div.style.marginLeft = div.depth + "em";
+ // When this item is toggled we will expand its children
+ div.pendingExpand = [];
+ div.treeLine = treeLine;
+ div.data = data;
+ // Useful for debugging
+ //this.uniqueID = this.uniqueID || 0;
+ //div.id = "Node" + this.uniqueID++;
+ div.appendChild(treeLine);
+ div.treeChildren = [];
+ div.treeParent = parentNode;
+ if (hasChildren) {
+ for (var i = 0; i < data.children.length; ++i) {
+ div.pendingExpand.push({parentElement: this._horizontalScrollbox, parentNode: div, data: data.children[i].getData() });
+ }
+ }
+ if (parentNode) {
+ parentNode.treeChildren.push(div);
+ }
+ if (parentNode != null) {
+ var nextTo;
+ if (parentNode.treeChildren.length > 1) {
+ nextTo = parentNode.treeChildren[parentNode.treeChildren.length-2].nextSibling;
+ } else {
+ nextTo = parentNode.nextSibling;
+ }
+ parentElement.insertBefore(div, nextTo);
+ } else {
+ parentElement.appendChild(div);
+ }
+ return div;
+ },
+ _addResourceIconStyles: function TreeView__addResourceIconStyles() {
+ var styles = [];
+ for (var resourceName in this._resources) {
+ var resource = this._resources[resourceName];
+ if (resource.icon) {
+ styles.push('.resourceIcon[data-resource="' + resourceName + '"] { background-image: url("' + resource.icon + '"); }');
+ }
+ }
+ this._styleElement.textContent = styles.join("\n");
+ },
+ _populateContextMenu: function TreeView__populateContextMenu(event) {
+ this._verticalScrollbox.setAttribute("contextmenu", "");
+
+ var target = event.target;
+ if (target.classList.contains("expandCollapseButton") ||
+ target.classList.contains("focusCallstackButton"))
+ return;
+
+ var li = this._getParentTreeViewNode(target);
+ if (!li)
+ return;
+
+ this._select(li);
+
+ this._contextMenu.innerHTML = "";
+
+ var self = this;
+ this._contextMenuForFunction(li.data).forEach(function (menuItem) {
+ var menuItemNode = document.createElement("menuitem");
+ menuItemNode.onclick = (function (menuItem) {
+ return function() {
+ self._contextMenuClick(li.data, menuItem);
+ };
+ })(menuItem);
+ menuItemNode.label = menuItem;
+ self._contextMenu.appendChild(menuItemNode);
+ });
+
+ this._verticalScrollbox.setAttribute("contextmenu", this._contextMenu.id);
+ },
+ _contextMenuClick: function TreeView__contextMenuClick(node, menuItem) {
+ this._fireEvent("contextMenuClick", { node: node, menuItem: menuItem });
+ },
+ _contextMenuForFunction: function TreeView__contextMenuForFunction(node) {
+ // TODO move me outside tree.js
+ var menu = [];
+ if (node.library && (
+ node.library.toLowerCase() == "lib_xul" ||
+ node.library.toLowerCase() == "lib_xul.dll"
+ )) {
+ menu.push("View Source");
+ }
+ if (node.isJSFrame && node.scriptLocation) {
+ menu.push("View JS Source");
+ }
+ menu.push("Focus Frame");
+ menu.push("Focus Callstack");
+ menu.push("Google Search");
+ menu.push("Plugin View: Pie");
+ menu.push("Plugin View: Tree");
+ return menu;
+ },
+ _HTMLForFunction: function TreeView__HTMLForFunction(node) {
+ var nodeName = escapeHTML(node.name);
+ var resource = this._resources[node.library] || {};
+ var libName = escapeHTML(resource.name || "");
+ if (this._filterByName) {
+ if (!this._filterByNameReg) {
+ this._filterByName = RegExp.escape(this._filterByName);
+ this._filterByNameReg = new RegExp("(" + this._filterByName + ")","gi");
+ }
+ nodeName = nodeName.replace(this._filterByNameReg, "$1");
+ libName = libName.replace(this._filterByNameReg, "$1");
+ }
+ var samplePercentage;
+ if (isNaN(node.ratio)) {
+ samplePercentage = "";
+ } else {
+ samplePercentage = (100 * node.ratio).toFixed(1) + "%";
+ }
+ return ' ' +
+ '' + node.counter + ' ' +
+ '' + samplePercentage + ' ' +
+ '' + node.selfCounter + ' ' +
+ ' ' +
+ '' + nodeName + '' +
+ '' + libName + '' +
+ ((nodeName === '(total)' || gHideSourceLinks) ? '' :
+ '');
+ },
+ _resolveChildren: function TreeView__resolveChildren(div, childrenCollapsedValue) {
+ while (div.pendingExpand != null && div.pendingExpand.length > 0) {
+ var pendingExpand = div.pendingExpand.shift();
+ pendingExpand.allChildrenCollapsedValue = childrenCollapsedValue;
+ this._pendingActions.push(pendingExpand);
+ this._schedulePendingActionProcessing();
+ }
+ },
+ _showChild: function TreeView__showChild(div, isVisible) {
+ for (var i = 0; i < div.treeChildren.length; i++) {
+ div.treeChildren[i].style.display = isVisible?"":"none";
+ if (!isVisible) {
+ div.treeChildren[i].classList.add("collapsed");
+ this._showChild(div.treeChildren[i], isVisible);
+ }
+ }
+ },
+ _toggle: function TreeView__toggle(div, /* optional */ newCollapsedValue, /* optional */ suppressScrollHeightNotification) {
+ var currentCollapsedValue = this._isCollapsed(div);
+ if (newCollapsedValue === undefined)
+ newCollapsedValue = !currentCollapsedValue;
+ if (newCollapsedValue) {
+ div.classList.add("collapsed");
+ this._showChild(div, false);
+ } else {
+ this._resolveChildren(div, true);
+ div.classList.remove("collapsed");
+ this._showChild(div, true);
+ }
+ if (!suppressScrollHeightNotification)
+ this._scrollHeightChanged();
+ },
+ _toggleAll: function TreeView__toggleAll(subtreeRoot, /* optional */ newCollapsedValue, /* optional */ suppressScrollHeightNotification) {
+
+ // Reset abort
+ this._abortToggleAll = false;
+
+ // Expands / collapses all child nodes, too.
+
+ if (newCollapsedValue === undefined)
+ newCollapsedValue = !this._isCollapsed(subtreeRoot);
+ if (!newCollapsedValue) {
+ // expanding
+ this._resolveChildren(subtreeRoot, newCollapsedValue);
+ }
+ this._toggle(subtreeRoot, newCollapsedValue, true);
+ for (var i = 0; i < subtreeRoot.treeChildren.length; ++i) {
+ this._toggleAll(subtreeRoot.treeChildren[i], newCollapsedValue, true);
+ }
+ if (!suppressScrollHeightNotification)
+ this._scrollHeightChanged();
+ },
+ _getParent: function TreeView__getParent(div) {
+ return div.treeParent;
+ },
+ _getFirstChild: function TreeView__getFirstChild(div) {
+ if (this._isCollapsed(div))
+ return null;
+ var child = div.treeChildren[0];
+ return child;
+ },
+ _getLastChild: function TreeView__getLastChild(div) {
+ if (this._isCollapsed(div))
+ return div;
+ var lastChild = div.treeChildren[div.treeChildren.length-1];
+ if (lastChild == null)
+ return div;
+ return this._getLastChild(lastChild);
+ },
+ _getPrevSib: function TreeView__getPevSib(div) {
+ if (div.treeParent == null)
+ return null;
+ var nodeIndex = div.treeParent.treeChildren.indexOf(div);
+ if (nodeIndex == 0)
+ return null;
+ return div.treeParent.treeChildren[nodeIndex-1];
+ },
+ _getNextSib: function TreeView__getNextSib(div) {
+ if (div.treeParent == null)
+ return null;
+ var nodeIndex = div.treeParent.treeChildren.indexOf(div);
+ if (nodeIndex == div.treeParent.treeChildren.length - 1)
+ return this._getNextSib(div.treeParent);
+ return div.treeParent.treeChildren[nodeIndex+1];
+ },
+ _scheduleScrollIntoView: function TreeView__scheduleScrollIntoView(element, maxImportantWidth) {
+ // Schedule this on the animation frame otherwise we may run this more then once per frames
+ // causing more work then needed.
+ var self = this;
+ if (self._pendingAnimationFrame != null) {
+ return;
+ }
+ self._pendingAnimationFrame = requestAnimationFrame(function anim_frame() {
+ cancelAnimationFrame(self._pendingAnimationFrame);
+ self._pendingAnimationFrame = null;
+ self._scrollIntoView(element, maxImportantWidth);
+ });
+ },
+ _scrollIntoView: function TreeView__scrollIntoView(element, maxImportantWidth) {
+ // Make sure that element is inside the visible part of our scrollbox by
+ // adjusting the scroll positions. If element is wider or
+ // higher than the scroll port, the left and top edges are prioritized over
+ // the right and bottom edges.
+ // If maxImportantWidth is set, parts of the beyond this widths are
+ // considered as not important; they'll not be moved into view.
+
+ if (maxImportantWidth === undefined)
+ maxImportantWidth = Infinity;
+
+ var visibleRect = {
+ left: this._horizontalScrollbox.getBoundingClientRect().left + 150, // TODO: un-hardcode 150
+ top: this._verticalScrollbox.getBoundingClientRect().top,
+ right: this._horizontalScrollbox.getBoundingClientRect().right,
+ bottom: this._verticalScrollbox.getBoundingClientRect().bottom
+ }
+ var r = element.getBoundingClientRect();
+ var right = Math.min(r.right, r.left + maxImportantWidth);
+ var leftCutoff = visibleRect.left - r.left;
+ var rightCutoff = right - visibleRect.right;
+ var topCutoff = visibleRect.top - r.top;
+ var bottomCutoff = r.bottom - visibleRect.bottom;
+ if (leftCutoff > 0)
+ this._horizontalScrollbox.scrollLeft -= leftCutoff;
+ else if (rightCutoff > 0)
+ this._horizontalScrollbox.scrollLeft += Math.min(rightCutoff, -leftCutoff);
+ if (topCutoff > 0)
+ this._verticalScrollbox.scrollTop -= topCutoff;
+ else if (bottomCutoff > 0)
+ this._verticalScrollbox.scrollTop += Math.min(bottomCutoff, -topCutoff);
+ },
+ _select: function TreeView__select(li) {
+ if (this._selectedNode != null) {
+ this._selectedNode.treeLine.classList.remove("selected");
+ this._selectedNode = null;
+ }
+ if (li) {
+ li.treeLine.classList.add("selected");
+ this._selectedNode = li;
+ var functionName = li.treeLine.querySelector(".functionName");
+ this._scheduleScrollIntoView(functionName, 400);
+ this._fireEvent("select", li.data);
+ }
+ updateDocumentURL();
+ },
+ _isCollapsed: function TreeView__isCollapsed(div) {
+ return div.classList.contains("collapsed");
+ },
+ _getParentTreeViewNode: function TreeView__getParentTreeViewNode(node) {
+ while (node) {
+ if (node.nodeType != node.ELEMENT_NODE)
+ break;
+ if (node.classList.contains("treeViewNode"))
+ return node;
+ node = node.parentNode;
+ }
+ return null;
+ },
+ _onclick: function TreeView__onclick(event) {
+ var target = event.target;
+ var node = this._getParentTreeViewNode(target);
+ if (!node)
+ return;
+ if (target.classList.contains("expandCollapseButton")) {
+ if (event.altKey)
+ this._toggleAll(node);
+ else
+ this._toggle(node);
+ } else if (target.classList.contains("focusCallstackButton")) {
+ this._fireEvent("focusCallstackButtonClicked", node.data);
+ } else {
+ this._select(node);
+ if (event.detail == 2) // dblclick
+ this._toggle(node);
+ }
+ },
+ _onkeypress: function TreeView__onkeypress(event) {
+ if (event.ctrlKey || event.altKey || event.metaKey)
+ return;
+
+ this._abortToggleAll = true;
+
+ var selected = this._selectedNode;
+ if (event.keyCode < 37 || event.keyCode > 40) {
+ if (event.keyCode != 0 ||
+ String.fromCharCode(event.charCode) != '*') {
+ return;
+ }
+ }
+ event.stopPropagation();
+ event.preventDefault();
+ if (!selected)
+ return;
+ if (event.keyCode == 37) { // KEY_LEFT
+ var isCollapsed = this._isCollapsed(selected);
+ if (!isCollapsed) {
+ this._toggle(selected);
+ } else {
+ var parent = this._getParent(selected);
+ if (parent != null) {
+ this._select(parent);
+ }
+ }
+ } else if (event.keyCode == 38) { // KEY_UP
+ var prevSib = this._getPrevSib(selected);
+ var parent = this._getParent(selected);
+ if (prevSib != null) {
+ this._select(this._getLastChild(prevSib));
+ } else if (parent != null) {
+ this._select(parent);
+ }
+ } else if (event.keyCode == 39) { // KEY_RIGHT
+ var isCollapsed = this._isCollapsed(selected);
+ if (isCollapsed) {
+ this._toggle(selected);
+ this._syncProcessPendingActionProcessing();
+ } else {
+ // Do KEY_DOWN
+ var nextSib = this._getNextSib(selected);
+ var child = this._getFirstChild(selected);
+ if (child != null) {
+ this._select(child);
+ } else if (nextSib) {
+ this._select(nextSib);
+ }
+ }
+ } else if (event.keyCode == 40) { // KEY_DOWN
+ var nextSib = this._getNextSib(selected);
+ var child = this._getFirstChild(selected);
+ if (child != null) {
+ this._select(child);
+ } else if (nextSib) {
+ this._select(nextSib);
+ }
+ } else if (String.fromCharCode(event.charCode) == '*') {
+ this._toggleAll(selected);
+ }
+ },
+};
+