From 27e0c85b01d799ac4268b82fad863b21bcf57e88 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Fri, 5 Oct 2018 22:13:45 +0200 Subject: [PATCH 1/4] entity list auto scroll --- scripts/system/html/js/entityList.js | 35 +++++++++++++++---- scripts/system/html/js/listView.js | 18 ++++++++++ scripts/system/libraries/entityList.js | 7 ++-- .../system/libraries/entitySelectionTool.js | 8 ++--- 4 files changed, 55 insertions(+), 13 deletions(-) diff --git a/scripts/system/html/js/entityList.js b/scripts/system/html/js/entityList.js index 0ced016d26..0c87470f57 100644 --- a/scripts/system/html/js/entityList.js +++ b/scripts/system/html/js/entityList.js @@ -194,7 +194,7 @@ function loaded() { if (selectedIndex >= 0) { selection = []; selection = selection.concat(selectedEntities); - selection.splice(selectedIndex, 1) + selection.splice(selectedIndex, 1); } else { selection = selection.concat(selectedEntities); } @@ -453,7 +453,10 @@ function loaded() { } } - function updateSelectedEntities(selectedIDs) { + function updateSelectedEntities(selectedIDs, autoScroll) { + // force autoScroll to be a boolean + autoScroll = !!autoScroll; + let notFound = false; // reset all currently selected entities and their rows first @@ -482,6 +485,26 @@ function loaded() { } }); + if (autoScroll && selectedIDs.length > 0) { + let firstItem = Number.MAX_VALUE; + let lastItem = -1; + let itemFound = false; + visibleEntities.forEach(function(entity, index) { + if (selectedIDs.indexOf(entity.id) !== -1) { + if (firstItem > index) { + firstItem = index; + } + if (lastItem < index) { + lastItem = index; + } + itemFound = true; + } + }); + if (itemFound) { + entityList.scrollToRow(firstItem, lastItem); + } + } + refreshFooter(); return notFound; @@ -640,8 +663,8 @@ function loaded() { data = JSON.parse(data); if (data.type === "clearEntityList") { clearEntities(); - } else if (data.type === "selectionUpdate") { - let notFound = updateSelectedEntities(data.selectedIDs); + } else if (data.type === "selectionUpdate" && data.caller !== "entityList") { + let notFound = updateSelectedEntities(data.selectedIDs, true); if (notFound) { refreshEntities(); } @@ -653,13 +676,13 @@ function loaded() { clearEntities(); } else { updateEntityData(newEntities); - updateSelectedEntities(data.selectedIDs); + updateSelectedEntities(data.selectedIDs, true); } } }); } else if (data.type === "removeEntities" && data.deletedIDs !== undefined && data.selectedIDs !== undefined) { removeEntities(data.deletedIDs); - updateSelectedEntities(data.selectedIDs); + updateSelectedEntities(data.selectedIDs, true); } else if (data.type === "deleted" && data.ids) { removeEntities(data.ids); } diff --git a/scripts/system/html/js/listView.js b/scripts/system/html/js/listView.js index 10dc37efba..d11452e111 100644 --- a/scripts/system/html/js/listView.js +++ b/scripts/system/html/js/listView.js @@ -152,6 +152,24 @@ ListView.prototype = { this.refresh(); } }, + + scrollToRow(rowIndex, lastRowIndex) { + lastRowIndex = lastRowIndex ? lastRowIndex : rowIndex; + let boundingTop = rowIndex * this.rowHeight; + let boundingBottom = (lastRowIndex * this.rowHeight) + this.rowHeight; + if ((boundingBottom - boundingTop) > this.elTableScroll.clientHeight) { + boundingBottom = boundingTop + this.elTableScroll.clientHeight; + } + + let currentVisibleAreaTop = this.elTableScroll.scrollTop; + let currentVisibleAreaBottom = currentVisibleAreaTop + this.elTableScroll.clientHeight; + + if (boundingTop < currentVisibleAreaTop) { + this.elTableScroll.scrollTop = boundingTop; + } else if (boundingBottom > currentVisibleAreaBottom) { + this.elTableScroll.scrollTop = boundingBottom - (this.elTableScroll.clientHeight); + } + }, refresh: function() { // block refreshing before rows are initialized diff --git a/scripts/system/libraries/entityList.js b/scripts/system/libraries/entityList.js index 30e952723f..04b19aa280 100644 --- a/scripts/system/libraries/entityList.js +++ b/scripts/system/libraries/entityList.js @@ -98,7 +98,7 @@ EntityListTool = function(shouldUseEditTabletApp) { that.setVisible(!visible); }; - selectionManager.addEventListener(function() { + selectionManager.addEventListener(function(isSelectionUpdate, caller) { var selectedIDs = []; for (var i = 0; i < selectionManager.selections.length; i++) { @@ -107,7 +107,8 @@ EntityListTool = function(shouldUseEditTabletApp) { emitJSONScriptEvent({ type: 'selectionUpdate', - selectedIDs: selectedIDs + selectedIDs: selectedIDs, + caller: caller }); }); @@ -224,7 +225,7 @@ EntityListTool = function(shouldUseEditTabletApp) { for (var i = 0; i < ids.length; i++) { entityIDs.push(ids[i]); } - selectionManager.setSelections(entityIDs); + selectionManager.setSelections(entityIDs, "entityList"); if (data.focus) { cameraManager.enable(); cameraManager.focus(selectionManager.worldPosition, diff --git a/scripts/system/libraries/entitySelectionTool.js b/scripts/system/libraries/entitySelectionTool.js index 843d3e986f..4302a531b5 100644 --- a/scripts/system/libraries/entitySelectionTool.js +++ b/scripts/system/libraries/entitySelectionTool.js @@ -136,7 +136,7 @@ SelectionManager = (function() { return that.selections.length > 0; }; - that.setSelections = function(entityIDs) { + that.setSelections = function(entityIDs, caller) { that.selections = []; for (var i = 0; i < entityIDs.length; i++) { var entityID = entityIDs[i]; @@ -144,7 +144,7 @@ SelectionManager = (function() { Selection.addToSelectedItemsList(HIGHLIGHT_LIST_NAME, "entity", entityID); } - that._update(true); + that._update(true, caller); }; that.addEntity = function(entityID, toggleSelection) { @@ -477,7 +477,7 @@ SelectionManager = (function() { undoHistory.pushCommand(undo, copiedProperties, redo, copiedProperties); } - that._update = function(selectionUpdated) { + that._update = function(selectionUpdated, caller) { var properties = null; if (that.selections.length === 0) { that.localDimensions = null; @@ -542,7 +542,7 @@ SelectionManager = (function() { for (var j = 0; j < listeners.length; j++) { try { - listeners[j](selectionUpdated === true); + listeners[j](selectionUpdated === true, caller); } catch (e) { print("ERROR: entitySelectionTool.update got exception: " + JSON.stringify(e)); } From 776b8b8fc7161060b037e12e661dedf122897235 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Tue, 9 Oct 2018 00:59:53 +0200 Subject: [PATCH 2/4] CR and style fixes --- scripts/system/edit.js | 42 +++++++++---------- scripts/system/html/js/entityList.js | 29 ++++++------- scripts/system/html/js/listView.js | 14 +++++-- scripts/system/libraries/entityList.js | 9 ++-- .../system/libraries/entitySelectionTool.js | 38 ++++++++--------- 5 files changed, 69 insertions(+), 63 deletions(-) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index b911541f79..0d63b92b63 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -385,7 +385,7 @@ var toolBar = (function () { Entities.editEntity(entityID, { position: position }); - selectionManager._update(); + selectionManager._update(false, this); } else if (dimensionsCheckCount < MAX_DIMENSIONS_CHECKS) { Script.setTimeout(dimensionsCheckFunction, DIMENSIONS_CHECK_INTERVAL); } @@ -397,9 +397,9 @@ var toolBar = (function () { properties.type + " would be out of bounds."); } - selectionManager.clearSelections(); + selectionManager.clearSelections(this); entityListTool.sendUpdate(); - selectionManager.setSelections([entityID]); + selectionManager.setSelections([entityID], this); Window.setFocus(); @@ -550,7 +550,7 @@ var toolBar = (function () { } deletedEntityTimer = Script.setTimeout(function () { if (entitiesToDelete.length > 0) { - selectionManager.removeEntities(entitiesToDelete); + selectionManager.removeEntities(entitiesToDelete, this); } entityListTool.removeEntities(entitiesToDelete, selectionManager.selections); entitiesToDelete = []; @@ -866,7 +866,7 @@ var toolBar = (function () { gridTool.setVisible(false); grid.setEnabled(false); propertiesTool.setVisible(false); - selectionManager.clearSelections(); + selectionManager.clearSelections(this); cameraManager.disable(); selectionDisplay.disableTriggerMapping(); tablet.landscape = false; @@ -994,7 +994,7 @@ function handleOverlaySelectionToolUpdates(channel, message, sender) { var entity = entityIconOverlayManager.findEntity(data.overlayID); if (entity !== null) { - selectionManager.setSelections([entity]); + selectionManager.setSelections([entity], this); } } } @@ -1141,7 +1141,7 @@ function mouseClickEvent(event) { if (result === null || result === undefined) { if (!event.isShifted) { - selectionManager.clearSelections(); + selectionManager.clearSelections(this); } return; } @@ -1193,9 +1193,9 @@ function mouseClickEvent(event) { } if (!event.isShifted) { - selectionManager.setSelections([foundEntity]); + selectionManager.setSelections([foundEntity], this); } else { - selectionManager.addEntity(foundEntity, true); + selectionManager.addEntity(foundEntity, true, this); } if (wantDebug) { @@ -1493,7 +1493,7 @@ function selectAllEtitiesInCurrentSelectionBox(keepIfTouching) { } } } - selectionManager.setSelections(entities); + selectionManager.setSelections(entities, this); } } @@ -1633,7 +1633,7 @@ function deleteSelectedEntities() { } if (savedProperties.length > 0) { - SelectionManager.clearSelections(); + SelectionManager.clearSelections(this); pushCommandForSelections([], savedProperties); entityListTool.deleteEntities(deletedIDs); } @@ -1650,7 +1650,7 @@ function toggleSelectedEntitiesLocked() { }); } entityListTool.sendUpdate(); - selectionManager._update(); + selectionManager._update(false, this); } } @@ -1664,7 +1664,7 @@ function toggleSelectedEntitiesVisible() { }); } entityListTool.sendUpdate(); - selectionManager._update(); + selectionManager._update(false, this); } } @@ -1861,7 +1861,7 @@ function importSVO(importURL) { } if (isActive) { - selectionManager.setSelections(pastedEntityIDs); + selectionManager.setSelections(pastedEntityIDs, this); } } else { Window.notifyEditError("Can't import entities: entities would be out of bounds."); @@ -1909,7 +1909,7 @@ function deleteKey(value) { } function deselectKey(value) { if (value === 0) { // on release - selectionManager.clearSelections(); + selectionManager.clearSelections(this); } } function toggleKey(value) { @@ -2069,7 +2069,7 @@ function applyEntityProperties(data) { // We might be getting an undo while edit.js is disabled. If that is the case, don't set // our selections, causing the edit widgets to display. if (isActive) { - selectionManager.setSelections(selectedEntityIDs); + selectionManager.setSelections(selectedEntityIDs, this); } } @@ -2272,7 +2272,7 @@ var PropertiesTool = function (opts) { } } pushCommandForSelections(); - selectionManager._update(); + selectionManager._update(false, this); } else if (data.type === 'parent') { parentSelectedEntities(); } else if (data.type === 'unparent') { @@ -2301,7 +2301,7 @@ var PropertiesTool = function (opts) { }); } pushCommandForSelections(); - selectionManager._update(); + selectionManager._update(false, this); } } else if (data.action === "moveAllToGrid") { if (selectionManager.hasSelection()) { @@ -2321,7 +2321,7 @@ var PropertiesTool = function (opts) { }); } pushCommandForSelections(); - selectionManager._update(); + selectionManager._update(false, this); } } else if (data.action === "resetToNaturalDimensions") { if (selectionManager.hasSelection()) { @@ -2342,7 +2342,7 @@ var PropertiesTool = function (opts) { } } pushCommandForSelections(); - selectionManager._update(); + selectionManager._update(false, this); } } else if (data.action === "previewCamera") { if (selectionManager.hasSelection()) { @@ -2360,7 +2360,7 @@ var PropertiesTool = function (opts) { }); } pushCommandForSelections(); - selectionManager._update(); + selectionManager._update(false, this); } } else if (data.action === "reloadClientScripts") { if (selectionManager.hasSelection()) { diff --git a/scripts/system/html/js/entityList.js b/scripts/system/html/js/entityList.js index 0c87470f57..a75bd7e4b9 100644 --- a/scripts/system/html/js/entityList.js +++ b/scripts/system/html/js/entityList.js @@ -13,7 +13,7 @@ const DESCENDING_STRING = '▾'; const LOCKED_GLYPH = ""; const VISIBLE_GLYPH = ""; const TRANSPARENCY_GLYPH = ""; -const BAKED_GLYPH = "" +const BAKED_GLYPH = ""; const SCRIPT_GLYPH = "k"; const BYTES_PER_MEGABYTE = 1024 * 1024; const IMAGE_MODEL_NAME = 'default-image-model.fbx'; @@ -54,10 +54,10 @@ const COMPARE_ASCENDING = function(a, b) { } return 1; -} +}; const COMPARE_DESCENDING = function(a, b) { return COMPARE_ASCENDING(b, a); -} +}; // List of all entities var entities = []; @@ -156,22 +156,22 @@ function loaded() { }; elRefresh.onclick = function() { refreshEntities(); - } + }; elToggleLocked.onclick = function() { EventBridge.emitWebEvent(JSON.stringify({ type: 'toggleLocked' })); - } + }; elToggleVisible.onclick = function() { EventBridge.emitWebEvent(JSON.stringify({ type: 'toggleVisible' })); - } + }; elExport.onclick = function() { EventBridge.emitWebEvent(JSON.stringify({ type: 'export'})); - } + }; elPal.onclick = function() { EventBridge.emitWebEvent(JSON.stringify({ type: 'pal' })); - } + }; elDelete.onclick = function() { EventBridge.emitWebEvent(JSON.stringify({ type: 'delete' })); - } + }; elFilter.onkeyup = refreshEntityList; elFilter.onpaste = refreshEntityList; elFilter.onchange = onFilterChange; @@ -227,7 +227,7 @@ function loaded() { } } - updateSelectedEntities(selection); + updateSelectedEntities(selection, false); EventBridge.emitWebEvent(JSON.stringify({ type: "selectionUpdate", @@ -289,7 +289,7 @@ function loaded() { hasScript: entity.hasScript, elRow: null, // if this entity has a visible row element assigned to it selected: false // if this entity is selected for edit regardless of having a visible row - } + }; entities.push(entityData); entitiesByID[entityData.id] = entityData; @@ -418,7 +418,7 @@ function loaded() { isBaked: document.querySelector('#entity-isBaked .sort-order'), drawCalls: document.querySelector('#entity-drawCalls .sort-order'), hasScript: document.querySelector('#entity-hasScript .sort-order'), - } + }; function setSortColumn(column) { PROFILE("set-sort-column", function() { if (currentSortColumn === column) { @@ -454,9 +454,6 @@ function loaded() { } function updateSelectedEntities(selectedIDs, autoScroll) { - // force autoScroll to be a boolean - autoScroll = !!autoScroll; - let notFound = false; // reset all currently selected entities and their rows first @@ -663,7 +660,7 @@ function loaded() { data = JSON.parse(data); if (data.type === "clearEntityList") { clearEntities(); - } else if (data.type === "selectionUpdate" && data.caller !== "entityList") { + } else if (data.type === "selectionUpdate") { let notFound = updateSelectedEntities(data.selectedIDs, true); if (notFound) { refreshEntities(); diff --git a/scripts/system/html/js/listView.js b/scripts/system/html/js/listView.js index d11452e111..62163ffe16 100644 --- a/scripts/system/html/js/listView.js +++ b/scripts/system/html/js/listView.js @@ -38,7 +38,7 @@ function ListView(elTableBody, elTableScroll, elTableHeaderRow, createRowFunctio this.lastRowShiftScrollTop = 0; this.initialize(); -}; +} ListView.prototype = { getNumRows: function() { @@ -153,9 +153,15 @@ ListView.prototype = { } }, - scrollToRow(rowIndex, lastRowIndex) { - lastRowIndex = lastRowIndex ? lastRowIndex : rowIndex; - let boundingTop = rowIndex * this.rowHeight; + /** + * Scrolls firstRowIndex with least effort, also tries to make the window include the other selections in case lastRowIndex is set. + * In the case that firstRowIndex and lastRowIndex are already within the visible bounds then nothing will happen. + * @param {number} firstRowIndex - The row that will be scrolled to. + * @param {number} lastRowIndex - The last index of the bound. + */ + scrollToRow: function (firstRowIndex, lastRowIndex) { + lastRowIndex = lastRowIndex ? lastRowIndex : firstRowIndex; + let boundingTop = firstRowIndex * this.rowHeight; let boundingBottom = (lastRowIndex * this.rowHeight) + this.rowHeight; if ((boundingBottom - boundingTop) > this.elTableScroll.clientHeight) { boundingBottom = boundingTop + this.elTableScroll.clientHeight; diff --git a/scripts/system/libraries/entityList.js b/scripts/system/libraries/entityList.js index 04b19aa280..045990b99b 100644 --- a/scripts/system/libraries/entityList.js +++ b/scripts/system/libraries/entityList.js @@ -99,6 +99,10 @@ EntityListTool = function(shouldUseEditTabletApp) { }; selectionManager.addEventListener(function(isSelectionUpdate, caller) { + if (caller === that) { + // ignore events that we emitted from the entity list itself + return; + } var selectedIDs = []; for (var i = 0; i < selectionManager.selections.length; i++) { @@ -107,8 +111,7 @@ EntityListTool = function(shouldUseEditTabletApp) { emitJSONScriptEvent({ type: 'selectionUpdate', - selectedIDs: selectedIDs, - caller: caller + selectedIDs: selectedIDs }); }); @@ -225,7 +228,7 @@ EntityListTool = function(shouldUseEditTabletApp) { for (var i = 0; i < ids.length; i++) { entityIDs.push(ids[i]); } - selectionManager.setSelections(entityIDs, "entityList"); + selectionManager.setSelections(entityIDs, that); if (data.focus) { cameraManager.enable(); cameraManager.focus(selectionManager.worldPosition, diff --git a/scripts/system/libraries/entitySelectionTool.js b/scripts/system/libraries/entitySelectionTool.js index 4302a531b5..73d84cf293 100644 --- a/scripts/system/libraries/entitySelectionTool.js +++ b/scripts/system/libraries/entitySelectionTool.js @@ -40,7 +40,7 @@ SelectionManager = (function() { Messages.messageReceived.connect(handleEntitySelectionToolUpdates); } - // FUNCTION: HANDLE ENTITY SELECTION TOOL UDPATES + // FUNCTION: HANDLE ENTITY SELECTION TOOL UPDATES function handleEntitySelectionToolUpdates(channel, message, sender) { if (channel !== 'entityToolUpdates') { return; @@ -63,7 +63,7 @@ SelectionManager = (function() { if (wantDebug) { print("setting selection to " + messageParsed.entityID); } - that.setSelections([messageParsed.entityID]); + that.setSelections([messageParsed.entityID], that); } } else if (messageParsed.method === "clearSelection") { if (!SelectionDisplay.triggered() || SelectionDisplay.triggeredHand === messageParsed.hand) { @@ -147,7 +147,7 @@ SelectionManager = (function() { that._update(true, caller); }; - that.addEntity = function(entityID, toggleSelection) { + that.addEntity = function(entityID, toggleSelection, caller) { if (entityID) { var idx = -1; for (var i = 0; i < that.selections.length; i++) { @@ -165,7 +165,7 @@ SelectionManager = (function() { } } - that._update(true); + that._update(true, caller); }; function removeEntityByID(entityID) { @@ -176,21 +176,21 @@ SelectionManager = (function() { } } - that.removeEntity = function (entityID) { + that.removeEntity = function (entityID, caller) { removeEntityByID(entityID); - that._update(true); + that._update(true, caller); }; - that.removeEntities = function(entityIDs) { + that.removeEntities = function(entityIDs, caller) { for (var i = 0, length = entityIDs.length; i < length; i++) { removeEntityByID(entityIDs[i]); } - that._update(true); + that._update(true, caller); }; - that.clearSelections = function() { + that.clearSelections = function(caller) { that.selections = []; - that._update(true); + that._update(true, caller); }; that.addChildrenEntities = function(parentEntityID, entityList) { @@ -985,7 +985,7 @@ SelectionDisplay = (function() { that.pressedHand = NO_HAND; that.triggered = function() { return that.triggeredHand !== NO_HAND; - } + }; function pointingAtDesktopWindowOrTablet(hand) { var pointingAtDesktopWindow = (hand === Controller.Standard.RightHand && SelectionManager.pointingAtDesktopWindowRight) || @@ -1032,7 +1032,7 @@ SelectionDisplay = (function() { that.disableTriggerMapping = function() { that.triggerClickMapping.disable(); that.triggerPressMapping.disable(); - } + }; Script.scriptEnding.connect(that.disableTriggerMapping); // FUNCTION DEF(s): Intersection Check Helpers @@ -1234,7 +1234,7 @@ SelectionDisplay = (function() { if (wantDebug) { print(" Trigger SelectionManager::update"); } - SelectionManager._update(); + SelectionManager._update(false, that); if (wantDebug) { print("=============== eST::MouseMoveEvent END ======================="); @@ -1299,7 +1299,7 @@ SelectionDisplay = (function() { lastMouseEvent.isControl = event.isControl; lastMouseEvent.isAlt = event.isAlt; activeTool.onMove(lastMouseEvent); - SelectionManager._update(); + SelectionManager._update(false, this); } }; @@ -1315,7 +1315,7 @@ SelectionDisplay = (function() { lastMouseEvent.isControl = event.isControl; lastMouseEvent.isAlt = event.isAlt; activeTool.onMove(lastMouseEvent); - SelectionManager._update(); + SelectionManager._update(false, this); } }; @@ -2179,7 +2179,7 @@ SelectionDisplay = (function() { } } - SelectionManager._update(); + SelectionManager._update(false, this); } }); } @@ -2301,7 +2301,7 @@ SelectionDisplay = (function() { previousPickRay = pickRay; - SelectionManager._update(); + SelectionManager._update(false, this); } }); } @@ -2488,7 +2488,7 @@ SelectionDisplay = (function() { previousPickRay = pickRay; - SelectionManager._update(); + SelectionManager._update(false, this); } }); } @@ -2599,7 +2599,7 @@ SelectionDisplay = (function() { previousPickRay = pickRay; - SelectionManager._update(); + SelectionManager._update(false, this); } }); } From 8e1cfb5a067bde0d5676ba3d1024d53cc17ba4ec Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Mon, 22 Oct 2018 23:03:26 +0200 Subject: [PATCH 3/4] fix double click selection --- scripts/system/html/js/entityList.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/scripts/system/html/js/entityList.js b/scripts/system/html/js/entityList.js index a75bd7e4b9..55871533e2 100644 --- a/scripts/system/html/js/entityList.js +++ b/scripts/system/html/js/entityList.js @@ -239,11 +239,16 @@ function loaded() { } function onRowDoubleClicked() { + let selection = [this.dataset.entityID]; + updateSelectedEntities(selection, false); + EventBridge.emitWebEvent(JSON.stringify({ type: "selectionUpdate", focus: true, - entityIds: [this.dataset.entityID], + entityIds: selection, })); + + refreshFooter(); } function decimalMegabytes(number) { From 5f35a324805d68c97748f5ac65c8b7e97282e15d Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Mon, 22 Oct 2018 23:09:41 +0200 Subject: [PATCH 4/4] remove duplicate calls to refresh footer --- scripts/system/html/js/entityList.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/scripts/system/html/js/entityList.js b/scripts/system/html/js/entityList.js index 55871533e2..62c8d6ca6f 100644 --- a/scripts/system/html/js/entityList.js +++ b/scripts/system/html/js/entityList.js @@ -234,8 +234,6 @@ function loaded() { focus: false, entityIds: selection, })); - - refreshFooter(); } function onRowDoubleClicked() { @@ -247,8 +245,6 @@ function loaded() { focus: true, entityIds: selection, })); - - refreshFooter(); } function decimalMegabytes(number) {