blob: e4e1997d537559f10be126c911daae094a979a24 [file] [log] [blame]
class ChartsPage extends PageWithHeading {
constructor(toolbar)
{
console.assert(toolbar instanceof ChartsToolbar);
super('Charts', toolbar);
this._paneList = [];
this._paneListChanged = false;
this._mainDomain = null;
this._currentRepositoryId = null;
toolbar.setAddPaneCallback(this.insertPaneAfter.bind(this));
}
routeName() { return 'charts'; }
static createStateForDashboardItem(platformId, metricId, startTime)
{
var state = {paneList: [[platformId, metricId]], since: startTime};
return state;
}
static createDomainForAnalysisTask(task)
{
var diff = (task.endTime() - task.startTime()) * 0.1;
return [task.startTime() - diff, task.endTime() + diff];
}
static createStateForAnalysisTask(task)
{
var domain = this.createDomainForAnalysisTask(task);
var state = {
paneList: [[task.platform().id(), task.metric().id()]],
since: Math.round(task.startTime() - (Date.now() - task.startTime()) * 0.1),
zoom: domain,
};
return state;
}
static createStateForConfigurationList(configurationList, startTime)
{
console.assert(configurationList instanceof Array);
var state = {paneList: configurationList};
return state;
}
open(state)
{
this.toolbar().setNumberOfDaysCallback(this.setNumberOfDaysFromToolbar.bind(this));
super.open(state);
}
serializeState()
{
var state = {since: this.toolbar().startTime()};
var serializedPaneList = [];
for (var pane of this._paneList)
serializedPaneList.push(pane.serializeState());
if (this._mainDomain)
state['zoom'] = this._mainDomain;
if (serializedPaneList.length)
state['paneList'] = serializedPaneList;
var repository = this._currentRepositoryId;
if (repository)
state['repository'] = this._currentRepositoryId;
return state;
}
updateFromSerializedState(state, isOpen)
{
var paneList = [];
if (state.paneList instanceof Array)
paneList = state.paneList;
var newPaneList = this._updateChartPanesFromSerializedState(paneList);
if (newPaneList) {
this._paneList = newPaneList;
this._paneListChanged = true;
this.enqueueToRender();
}
this._updateDomainsFromSerializedState(state);
this._currentRepositoryId = parseInt(state['repository']);
var currentRepository = Repository.findById(this._currentRepositoryId);
console.assert(this._paneList.length == paneList.length);
for (var i = 0; i < this._paneList.length; i++) {
this._paneList[i].updateFromSerializedState(state.paneList[i], isOpen);
this._paneList[i].setOpenRepository(currentRepository);
}
}
_updateDomainsFromSerializedState(state)
{
var since = parseFloat(state.since);
var zoom = state.zoom;
if (typeof(zoom) == 'string' && zoom.indexOf('-'))
zoom = zoom.split('-')
if (zoom instanceof Array && zoom.length >= 2)
zoom = zoom.map(function (value) { return parseFloat(value); });
if (zoom && since)
since = Math.min(zoom[0], since);
else if (zoom)
since = zoom[0] - (zoom[1] - zoom[0]) / 2;
this.toolbar().setStartTime(since);
this.toolbar().enqueueToRender();
this._mainDomain = zoom || null;
this._updateOverviewDomain();
this._updateMainDomain();
}
_updateChartPanesFromSerializedState(paneList)
{
var paneMap = {}
for (var pane of this._paneList)
paneMap[pane.platformId() + '-' + pane.metricId()] = pane;
var newPaneList = [];
var createdNewPane = false;
for (var paneInfo of paneList) {
var platformId = parseInt(paneInfo[0]);
var metricId = parseInt(paneInfo[1]);
var existingPane = paneMap[platformId + '-' + metricId];
if (existingPane)
newPaneList.push(existingPane);
else {
newPaneList.push(new ChartPane(this, platformId, metricId));
createdNewPane = true;
}
}
if (createdNewPane || newPaneList.length !== this._paneList.length)
return newPaneList;
for (var i = 0; i < newPaneList.length; i++) {
if (newPaneList[i] != this._paneList[i])
return newPaneList;
}
return null;
}
setNumberOfDaysFromToolbar(numberOfDays, shouldUpdateState)
{
this.toolbar().setNumberOfDays(numberOfDays, true);
this.toolbar().enqueueToRender();
this._updateOverviewDomain();
this._updateMainDomain();
if (shouldUpdateState)
this.scheduleUrlStateUpdate();
}
setMainDomainFromOverviewSelection(domain, originatingPane, shouldUpdateState)
{
this._mainDomain = domain;
this._updateMainDomain(originatingPane);
if (shouldUpdateState)
this.scheduleUrlStateUpdate();
}
setMainDomainFromZoom(selection, originatingPane)
{
this._mainDomain = selection;
this._updateMainDomain(originatingPane);
this.scheduleUrlStateUpdate();
}
mainChartSelectionDidChange(pane, shouldUpdateState)
{
if (shouldUpdateState)
this.scheduleUrlStateUpdate();
}
mainChartIndicatorDidChange(pane, shouldUpdateState)
{
if (shouldUpdateState)
this.scheduleUrlStateUpdate();
}
graphOptionsDidChange(pane)
{
this.scheduleUrlStateUpdate();
}
setOpenRepository(repository)
{
this._currentRepositoryId = repository ? repository.id() : null;
for (var pane of this._paneList)
pane.setOpenRepository(repository);
this.scheduleUrlStateUpdate();
}
_updateOverviewDomain()
{
var startTime = this.toolbar().startTime();
var endTime = this.toolbar().endTime();
for (var pane of this._paneList)
pane.setOverviewDomain(startTime, endTime);
}
_updateMainDomain(originatingPane)
{
var startTime = this.toolbar().startTime();
var endTime = this.toolbar().endTime();
if (this._mainDomain) {
startTime = this._mainDomain[0];
endTime = this._mainDomain[1];
}
for (var pane of this._paneList) {
pane.setMainDomain(startTime, endTime);
if (pane != originatingPane) // Don't mess up the selection state.
pane.setOverviewSelection(this._mainDomain);
}
}
closePane(pane)
{
var index = this._paneList.indexOf(pane);
console.assert(index >= 0);
this._paneList.splice(index, 1);
this._didMutatePaneList(false);
}
insertPaneAfter(platform, metric, referencePane)
{
var newPane = new ChartPane(this, platform.id(), metric.id());
if (referencePane) {
var index = this._paneList.indexOf(referencePane);
console.assert(index >= 0);
this._paneList.splice(index + 1, 0, newPane);
} else
this._paneList.unshift(newPane);
this._didMutatePaneList(true);
}
alternatePlatforms(platform, metric)
{
var existingPlatforms = {};
for (var pane of this._paneList) {
if (pane.metricId() == metric.id())
existingPlatforms[pane.platformId()] = true;
}
return metric.platforms().filter(function (platform) {
return !existingPlatforms[platform.id()];
});
}
insertBreakdownPanesAfter(platform, metric, referencePane)
{
console.assert(referencePane);
var childMetrics = metric.childMetrics();
var index = this._paneList.indexOf(referencePane);
console.assert(index >= 0);
var args = [index + 1, 0];
for (var metric of childMetrics)
args.push(new ChartPane(this, platform.id(), metric.id()));
this._paneList.splice.apply(this._paneList, args);
this._didMutatePaneList(true);
}
canBreakdown(platform, metric)
{
var childMetrics = metric.childMetrics();
if (!childMetrics.length)
return false;
var existingMetrics = {};
for (var pane of this._paneList) {
if (pane.platformId() == platform.id())
existingMetrics[pane.metricId()] = true;
}
for (var metric of childMetrics) {
if (!existingMetrics[metric.id()])
return true;
}
return false;
}
_didMutatePaneList(addedNewPane)
{
this._paneListChanged = true;
if (addedNewPane) {
this._updateOverviewDomain();
this._updateMainDomain();
}
this.enqueueToRender();
this.scheduleUrlStateUpdate();
}
render()
{
super.render();
if (this._paneListChanged)
this.renderReplace(this.content().querySelector('.pane-list'), this._paneList);
for (var pane of this._paneList)
pane.enqueueToRender();
this._paneListChanged = false;
}
static htmlTemplate()
{
return `<div class="pane-list"></div>`;
}
}