virool-pivot
Version:
A web-based exploratory visualization UI for Druid.io
841 lines (699 loc) • 27.8 kB
text/typescript
import { List, OrderedSet, Iterable } from 'immutable';
import { compressToBase64, decompressFromBase64 } from 'lz-string';
import { Class, Instance, isInstanceOf, immutableEqual } from 'immutable-class';
import { Timezone, Duration, minute } from 'chronoshift';
import { $, Expression, RefExpression, TimeRange, ApplyAction, SortAction, Set } from 'plywood';
import { hasOwnProperty } from '../../../common/utils/general/general';
import { DataSource } from '../data-source/data-source';
import { Filter, FilterJS } from '../filter/filter';
import { FilterClause } from '../filter-clause/filter-clause';
import { Highlight, HighlightJS } from '../highlight/highlight';
import { Splits, SplitsJS } from '../splits/splits';
import { SplitCombine } from '../split-combine/split-combine';
import { Dimension } from '../dimension/dimension';
import { Measure } from '../measure/measure';
import { Colors, ColorsJS } from '../colors/colors';
import { Manifest, Resolve } from '../manifest/manifest';
const HASH_VERSION = 2;
function constrainDimensions(dimensions: OrderedSet<string>, dataSource: DataSource): OrderedSet<string> {
return <OrderedSet<string>>dimensions.filter((dimensionName) => Boolean(dataSource.getDimension(dimensionName)));
}
function constrainMeasures(measures: OrderedSet<string>, dataSource: DataSource): OrderedSet<string> {
return <OrderedSet<string>>measures.filter((measureName) => Boolean(dataSource.getMeasure(measureName)));
}
function addToSetInOrder<T>(order: Iterable<any, T>, setToAdd: OrderedSet<T>, thing: T): OrderedSet<T> {
return OrderedSet(order.toArray().filter((name) => setToAdd.has(name) || name === thing));
}
export interface VisualizationAndResolve {
visualization: Manifest;
resolve: Resolve;
}
/**
* FairGame - Run all visualizations pretending that there is no current
* UnfairGame - Run all visualizations but mark current vis as current
* KeepAlways - Just keep the current one
*/
export enum VisStrategy {
FairGame,
UnfairGame,
KeepAlways
}
export interface EssenceValue {
visualizations?: List<Manifest>;
dataSource?: DataSource;
visualization: Manifest;
timezone: Timezone;
filter: Filter;
splits: Splits;
multiMeasureMode: boolean;
singleMeasure: string;
selectedMeasures: OrderedSet<string>;
pinnedDimensions: OrderedSet<string>;
colors: Colors;
pinnedSort: string;
compare: Filter;
highlight: Highlight;
}
export interface EssenceJS {
visualization?: string;
timezone?: string;
filter?: FilterJS;
splits?: SplitsJS;
multiMeasureMode?: boolean;
singleMeasure?: string;
selectedMeasures?: string[];
pinnedDimensions?: string[];
colors?: ColorsJS;
pinnedSort?: string;
compare?: FilterJS;
highlight?: HighlightJS;
}
export interface EssenceContext {
dataSource: DataSource;
visualizations: List<Manifest>;
}
var check: Class<EssenceValue, EssenceJS>;
export class Essence implements Instance<EssenceValue, EssenceJS> {
static isEssence(candidate: any): candidate is Essence {
return isInstanceOf(candidate, Essence);
}
static getBestVisualization(visualizations: List<Manifest>, dataSource: DataSource, splits: Splits, colors: Colors, currentVisualization: Manifest): VisualizationAndResolve {
var visAndResolves = visualizations.toArray().map((visualization) => {
return {
visualization,
resolve: visualization.handleCircumstance(dataSource, splits, colors, visualization === currentVisualization)
};
});
return visAndResolves.sort((vr1, vr2) => Resolve.compare(vr1.resolve, vr2.resolve))[0];
}
static fromHash(hash: string, context: EssenceContext): Essence {
var parts = hash.split('/');
if (parts.length < 3) return null;
var visualization = parts.shift();
var version = parseInt(parts.shift(), 10);
if (version > HASH_VERSION) return null;
var jsArray: any[] = null;
try {
jsArray = JSON.parse('[' + decompressFromBase64(parts.join('/')) + ']');
} catch (e) {
return null;
}
if (!Array.isArray(jsArray)) return null;
if (version === 1) { // Upgrade to version 2
jsArray.splice(3, 0, false, null); // Insert null at position 3 (between splits and selectedMeasures)
}
var jsArrayLength = jsArray.length;
if (!(8 <= jsArrayLength && jsArrayLength <= 11)) return null;
var essence: Essence;
try {
essence = Essence.fromJS({
visualization: visualization,
timezone: jsArray[0],
filter: jsArray[1],
splits: jsArray[2],
multiMeasureMode: jsArray[3],
singleMeasure: jsArray[4],
selectedMeasures: jsArray[5],
pinnedDimensions: jsArray[6],
pinnedSort: jsArray[7],
colors: jsArray[8] || null,
compare: jsArray[9] || null,
highlight: jsArray[10] || null
}, context);
} catch (e) {
return null;
}
return essence;
}
static fromDataSource(dataSource: DataSource, context: EssenceContext): Essence {
var timezone = dataSource.defaultTimezone;
var splits = Splits.EMPTY;
var { defaultSplits } = dataSource.options;
if (defaultSplits) {
splits = Splits.fromJS(defaultSplits, dataSource);
}
var essence = new Essence({
dataSource: context.dataSource,
visualizations: context.visualizations,
visualization: null,
timezone,
filter: null,
splits,
multiMeasureMode: false,
singleMeasure: dataSource.defaultSortMeasure,
selectedMeasures: OrderedSet(dataSource.measures.toArray().slice(0, 4).map(m => m.name)),
pinnedDimensions: dataSource.defaultPinnedDimensions,
colors: null,
pinnedSort: dataSource.defaultSortMeasure,
compare: null,
highlight: null
});
if (defaultSplits) {
essence = essence.updateSplitsWithFilter();
}
return essence;
}
static fromJS(parameters: EssenceJS, context?: EssenceContext): Essence {
if (!context) throw new Error('must have context');
const { dataSource, visualizations } = context;
var visualizationID = parameters.visualization;
var visualization = visualizations.find(v => v.id === visualizationID);
var timezone = parameters.timezone ? Timezone.fromJS(parameters.timezone) : null;
var filter = parameters.filter ? Filter.fromJS(parameters.filter).constrainToDimensions(dataSource.dimensions, dataSource.timeAttribute) : null;
var splits = Splits.fromJS(parameters.splits || [], dataSource).constrainToDimensions(dataSource.dimensions);
var defaultSortMeasureName = dataSource.defaultSortMeasure;
var multiMeasureMode = hasOwnProperty(parameters, 'multiMeasureMode') ? parameters.multiMeasureMode : !hasOwnProperty(parameters, 'singleMeasure');
var singleMeasure = dataSource.getMeasure(parameters.singleMeasure) ? parameters.singleMeasure : defaultSortMeasureName;
var selectedMeasures = constrainMeasures(OrderedSet(parameters.selectedMeasures || []), dataSource);
var pinnedDimensions = constrainDimensions(OrderedSet(parameters.pinnedDimensions || []), dataSource);
var colors = parameters.colors ? Colors.fromJS(parameters.colors) : null;
var pinnedSort = dataSource.getMeasure(parameters.pinnedSort) ? parameters.pinnedSort : defaultSortMeasureName;
var compare: Filter = null;
var compareJS = parameters.compare;
if (compareJS) {
compare = Filter.fromJS(compareJS).constrainToDimensions(dataSource.dimensions, dataSource.timeAttribute);
}
var highlight: Highlight = null;
var highlightJS = parameters.highlight;
if (highlightJS) {
highlight = Highlight.fromJS(highlightJS).constrainToDimensions(dataSource.dimensions, dataSource.timeAttribute);
}
return new Essence({
dataSource,
visualizations,
visualization,
timezone,
filter,
splits,
multiMeasureMode,
singleMeasure,
selectedMeasures,
pinnedDimensions,
colors,
pinnedSort,
compare,
highlight
});
}
public dataSource: DataSource;
public visualizations: List<Manifest>;
public visualization: Manifest;
public timezone: Timezone;
public filter: Filter;
public splits: Splits;
public multiMeasureMode: boolean;
public singleMeasure: string;
public selectedMeasures: OrderedSet<string>;
public pinnedDimensions: OrderedSet<string>;
public colors: Colors;
public pinnedSort: string;
public compare: Filter;
public highlight: Highlight;
public visResolve: Resolve;
constructor(parameters: EssenceValue) {
var {
visualizations,
dataSource,
visualization,
timezone,
filter,
splits,
multiMeasureMode,
singleMeasure,
selectedMeasures,
pinnedDimensions,
colors,
pinnedSort,
compare,
highlight
} = parameters;
if (!dataSource) throw new Error('Essence must have a dataSource');
timezone = timezone || Timezone.UTC;
if (!filter) {
if (dataSource.timeAttribute) {
filter = dataSource.defaultFilter.setSelection(
dataSource.timeAttribute,
$(FilterClause.MAX_TIME_REF_NAME).timeRange(dataSource.defaultDuration, -1)
);
} else {
filter = Filter.EMPTY;
}
}
multiMeasureMode = Boolean(multiMeasureMode);
function visibleMeasure(measureName: string): boolean {
return multiMeasureMode ? selectedMeasures.has(measureName) : measureName === singleMeasure;
}
// Wipe out the highlight if measure is not selected
if (highlight && highlight.measure && !visibleMeasure(highlight.measure)) {
highlight = null;
}
// Place vis here because it needs to know about splits and colors (and maybe later other things)
if (!visualization) {
var visAndResolve = Essence.getBestVisualization(visualizations, dataSource, splits, colors, null);
visualization = visAndResolve.visualization;
}
var visResolve = visualization.handleCircumstance(dataSource, splits, colors, true);
if (visResolve.isAutomatic()) {
var adjustment = visResolve.adjustment;
splits = adjustment.splits;
colors = adjustment.colors || null;
visResolve = visualization.handleCircumstance(dataSource, splits, colors, true);
if (!visResolve.isReady()) {
console.log(visResolve);
throw new Error(visualization.title + ' must be ready after automatic adjustment');
}
}
this.visualizations = visualizations;
this.dataSource = dataSource;
this.visualization = visualization;
this.dataSource = dataSource;
this.timezone = timezone;
this.filter = filter;
this.splits = splits;
this.multiMeasureMode = multiMeasureMode;
this.singleMeasure = singleMeasure;
this.selectedMeasures = selectedMeasures;
this.pinnedDimensions = pinnedDimensions;
this.colors = colors;
this.pinnedSort = pinnedSort;
this.highlight = highlight;
this.compare = compare;
this.visResolve = visResolve;
}
public valueOf(): EssenceValue {
return {
dataSource: this.dataSource,
visualizations: this.visualizations,
visualization: this.visualization,
timezone: this.timezone,
filter: this.filter,
splits: this.splits,
multiMeasureMode: this.multiMeasureMode,
singleMeasure: this.singleMeasure,
selectedMeasures: this.selectedMeasures,
pinnedDimensions: this.pinnedDimensions,
colors: this.colors,
pinnedSort: this.pinnedSort,
compare: this.compare,
highlight: this.highlight
};
}
public toJS(): EssenceJS {
var js: EssenceJS = {
visualization: this.visualization.id,
timezone: this.timezone.toJS(),
filter: this.filter.toJS(),
splits: this.splits.toJS(),
singleMeasure: this.singleMeasure,
selectedMeasures: this.selectedMeasures.toArray(),
pinnedDimensions: this.pinnedDimensions.toArray()
};
if (this.multiMeasureMode) js.multiMeasureMode = true;
if (this.colors) js.colors = this.colors.toJS();
var defaultSortMeasure = this.dataSource.defaultSortMeasure;
if (this.pinnedSort !== defaultSortMeasure) js.pinnedSort = this.pinnedSort;
if (this.compare) js.compare = this.compare.toJS();
if (this.highlight) js.highlight = this.highlight.toJS();
return js;
}
public toJSON(): EssenceJS {
return this.toJS();
}
public toString(): string {
return `[Essence]`;
}
public equals(other: Essence): boolean {
return Essence.isEssence(other) &&
this.dataSource.equals(other.dataSource) &&
this.visualization.id === other.visualization.id &&
this.timezone.equals(other.timezone) &&
this.filter.equals(other.filter) &&
this.splits.equals(other.splits) &&
this.multiMeasureMode === other.multiMeasureMode &&
this.singleMeasure === other.singleMeasure &&
this.selectedMeasures.equals(other.selectedMeasures) &&
this.pinnedDimensions.equals(other.pinnedDimensions) &&
immutableEqual(this.colors, other.colors) &&
this.pinnedSort === other.pinnedSort &&
immutableEqual(this.compare, other.compare) &&
immutableEqual(this.highlight, other.highlight);
}
public toHash(): string {
var js = this.toJS();
var compressed: any[] = [
js.timezone, // 0
js.filter, // 1
js.splits, // 2
js.multiMeasureMode, // 3
js.singleMeasure, // 4
js.selectedMeasures, // 5
js.pinnedDimensions, // 6
js.pinnedSort // 7
];
if (js.colors) compressed[8] = js.colors;
if (js.compare) compressed[9] = js.compare;
if (js.highlight) compressed[10] = js.highlight;
var restJSON: string[] = [];
for (var i = 0; i < compressed.length; i++) {
restJSON.push(JSON.stringify(compressed[i] || null));
}
return [
js.visualization,
HASH_VERSION,
compressToBase64(restJSON.join(','))
].join('/');
}
public getURL(urlPrefix: string): string {
return urlPrefix + this.toHash();
}
public getTimeAttribute(): RefExpression {
return this.dataSource.timeAttribute;
}
public getTimeDimension(): Dimension {
return this.dataSource.getTimeDimension();
}
public evaluateSelection(selection: Expression, now: Date = new Date()): TimeRange {
var { dataSource, timezone } = this;
var maxTime = dataSource.getMaxTimeDate();
return FilterClause.evaluate(selection, now, maxTime, timezone);
}
public getEffectiveFilter(highlightId: string = null, unfilterDimension: Dimension = null): Filter {
var { dataSource, filter, highlight, timezone } = this;
if (highlight && (highlightId !== highlight.owner)) filter = highlight.applyToFilter(filter);
if (unfilterDimension) filter = filter.remove(unfilterDimension.expression);
var maxTime = dataSource.getMaxTimeDate();
return filter.getSpecificFilter(new Date(), maxTime, timezone);
}
public getTimeSelection(): Expression {
const timeAttribute = this.getTimeAttribute();
return this.filter.getSelection(timeAttribute);
}
public isFixedMeasureMode(): boolean {
return this.visualization.measureModeNeed !== 'any';
}
public getEffectiveMultiMeasureMode(): boolean {
const { measureModeNeed } = this.visualization;
if (measureModeNeed !== 'any') {
return measureModeNeed === 'multi';
}
return this.multiMeasureMode;
}
public getEffectiveMeasures(): List<Measure> {
if (this.getEffectiveMultiMeasureMode()) {
return this.getMeasures();
} else {
return List([this.dataSource.getMeasure(this.singleMeasure)]);
}
}
public getMeasures(): List<Measure> {
var dataSource = this.dataSource;
return <List<Measure>>this.selectedMeasures.toList().map(measureName => dataSource.getMeasure(measureName));
}
public getEffectiveSelectedMeasure(): OrderedSet<string> {
if (this.getEffectiveMultiMeasureMode()) {
return this.selectedMeasures;
} else {
return OrderedSet([this.singleMeasure]);
}
}
public differentDataSource(other: Essence): boolean {
return this.dataSource !== other.dataSource;
}
public differentTimezone(other: Essence): boolean {
return !this.timezone.equals(other.timezone);
}
public differentTimezoneMatters(other: Essence): boolean {
return this.splits.timezoneDependant() && this.differentTimezone(other);
}
public differentFilter(other: Essence): boolean {
return !this.filter.equals(other.filter);
}
public differentSplits(other: Essence): boolean {
return !this.splits.equals(other.splits);
}
public differentEffectiveSplits(other: Essence): boolean {
return this.differentSplits(other) || this.differentTimezoneMatters(other);
}
public differentColors(other: Essence): boolean {
if (Boolean(this.colors) !== Boolean(other.colors)) return true;
if (!this.colors) return false;
return !this.colors.equals(other.colors);
}
public differentSelectedMeasures(other: Essence): boolean {
return !this.selectedMeasures.equals(other.selectedMeasures);
}
public differentEffectiveMeasures(other: Essence): boolean {
return !this.getEffectiveSelectedMeasure().equals(other.getEffectiveSelectedMeasure());
}
public newSelectedMeasures(other: Essence): boolean {
return !this.selectedMeasures.isSubset(other.selectedMeasures);
}
public newEffectiveMeasures(other: Essence): boolean {
return !this.getEffectiveSelectedMeasure().isSubset(other.getEffectiveSelectedMeasure());
}
public differentPinnedDimensions(other: Essence): boolean {
return !this.pinnedDimensions.equals(other.pinnedDimensions);
}
public differentPinnedSort(other: Essence): boolean {
return this.pinnedSort !== other.pinnedSort;
}
public differentCompare(other: Essence): boolean {
if (Boolean(this.compare) !== Boolean(other.compare)) return true;
return Boolean(this.compare && !this.compare.equals(other.compare));
}
public differentHighligh(other: Essence): boolean {
if (Boolean(this.highlight) !== Boolean(other.highlight)) return true;
return Boolean(this.highlight && !this.highlight.equals(other.highlight));
}
public differentEffectiveFilter(other: Essence, highlightId: string = null, unfilterDimension: Dimension = null): boolean {
var myEffectiveFilter = this.getEffectiveFilter(highlightId, unfilterDimension);
var otherEffectiveFilter = other.getEffectiveFilter(highlightId, unfilterDimension);
return !myEffectiveFilter.equals(otherEffectiveFilter);
}
public highlightOn(owner: string, measure?: string): boolean {
var { highlight } = this;
if (!highlight) return false;
return highlight.owner === owner && (!measure || highlight.measure === measure);
}
public highlightOnDifferentMeasure(owner: string, measure: string): boolean {
var { highlight } = this;
if (!highlight) return false;
return highlight.owner === owner && measure && highlight.measure !== measure;
}
public getSingleHighlightSet(): Set {
var { highlight } = this;
if (!highlight) return null;
return highlight.delta.getSingleClauseSet();
}
public getApplyForSort(sort: SortAction): ApplyAction {
var sortOn = (<RefExpression>sort.expression).name;
var sortMeasure = this.dataSource.getMeasure(sortOn);
if (!sortMeasure) return null;
return sortMeasure.toApplyAction();
}
public getCommonSort(): SortAction {
var splits = this.splits.toArray();
var commonSort: SortAction = null;
for (var split of splits) {
var sort = split.sortAction;
if (commonSort) {
if (!commonSort.equals(sort)) return null;
} else {
commonSort = sort;
}
}
return commonSort;
}
public updateDataSource(newDataSource: DataSource): Essence {
var { dataSource, visualizations } = this;
if (dataSource.equals(newDataSource)) return this; // nothing to do
if (dataSource.equalsWithoutMaxTime(newDataSource)) { // Updated maxTime
var value = this.valueOf();
value.dataSource = newDataSource;
return new Essence(value);
}
if (dataSource.name !== newDataSource.name) return Essence.fromDataSource(newDataSource, {
dataSource: newDataSource,
visualizations
});
var value = this.valueOf();
value.dataSource = newDataSource;
// Make sure that all the elements of state are still valid
value.filter = value.filter.constrainToDimensions(newDataSource.dimensions, newDataSource.timeAttribute, dataSource.timeAttribute);
value.splits = value.splits.constrainToDimensions(newDataSource.dimensions);
value.selectedMeasures = constrainMeasures(value.selectedMeasures, newDataSource);
value.pinnedDimensions = constrainDimensions(value.pinnedDimensions, newDataSource);
if (value.colors && !newDataSource.getDimension(value.colors.dimension)) {
value.colors = null;
}
var defaultSortMeasureName = newDataSource.defaultSortMeasure;
if (!newDataSource.getMeasure(value.pinnedSort)) value.pinnedSort = defaultSortMeasureName;
if (value.compare) {
value.compare = value.compare.constrainToDimensions(newDataSource.dimensions, newDataSource.timeAttribute);
}
if (value.highlight) {
value.highlight = value.highlight.constrainToDimensions(newDataSource.dimensions, newDataSource.timeAttribute);
}
return new Essence(value);
}
// Modification
public changeFilter(filter: Filter, removeHighlight = false): Essence {
var value = this.valueOf();
value.filter = filter;
if (removeHighlight) {
value.highlight = null;
}
var differentAttributes = filter.getDifferentAttributes(this.filter);
value.splits = value.splits.removeBucketingFrom(differentAttributes);
return (new Essence(value)).updateSplitsWithFilter();
}
public changeTimezone(newTimezone: Timezone): Essence {
var { timezone } = this;
if (timezone === newTimezone) return this;
var value = this.valueOf();
value.timezone = newTimezone;
return new Essence(value);
}
public changeTimeSelection(check: Expression): Essence {
var { filter } = this;
var timeAttribute = this.getTimeAttribute();
return this.changeFilter(filter.setSelection(timeAttribute, check));
}
public convertToSpecificFilter(): Essence {
var { dataSource, filter, timezone } = this;
if (!filter.isRelative()) return this;
var maxTime = dataSource.getMaxTimeDate();
return this.changeFilter(filter.getSpecificFilter(new Date(), maxTime, timezone));
}
public changeSplits(splits: Splits, strategy: VisStrategy): Essence {
var { visualizations, dataSource, visualization, visResolve, colors } = this;
splits = splits.updateWithFilter(this.getEffectiveFilter(), dataSource.dimensions);
// If in manual mode stay there, keep the vis regardless of suggested strategy
if (visResolve.isManual()) {
strategy = VisStrategy.KeepAlways;
}
if (strategy !== VisStrategy.KeepAlways) {
var visAndResolve = Essence.getBestVisualization(visualizations, dataSource, splits, colors, (strategy === VisStrategy.FairGame ? null : visualization));
visualization = visAndResolve.visualization;
}
var value = this.valueOf();
value.splits = splits;
value.visualization = visualization;
if (value.highlight) {
value.filter = value.highlight.applyToFilter(value.filter);
value.highlight = null;
}
return new Essence(value);
}
public changeSplit(splitCombine: SplitCombine, strategy: VisStrategy): Essence {
return this.changeSplits(Splits.fromSplitCombine(splitCombine), strategy);
}
public addSplit(split: SplitCombine, strategy: VisStrategy): Essence {
var { splits } = this;
return this.changeSplits(splits.addSplit(split), strategy);
}
public removeSplit(split: SplitCombine, strategy: VisStrategy): Essence {
var { splits } = this;
return this.changeSplits(splits.removeSplit(split), strategy);
}
public updateSplitsWithFilter(): Essence {
var value = this.valueOf();
value.splits = value.splits.updateWithFilter(this.getEffectiveFilter(), this.dataSource.dimensions);
return new Essence(value);
}
public changeColors(colors: Colors): Essence {
var value = this.valueOf();
value.colors = colors;
return new Essence(value);
}
public changeVisualization(visualization: Manifest): Essence {
var value = this.valueOf();
value.visualization = visualization;
return new Essence(value);
}
public pin(dimension: Dimension): Essence {
var value = this.valueOf();
value.pinnedDimensions = value.pinnedDimensions.add(dimension.name);
return new Essence(value);
}
public unpin(dimension: Dimension): Essence {
var value = this.valueOf();
value.pinnedDimensions = value.pinnedDimensions.remove(dimension.name);
return new Essence(value);
}
public getPinnedSortMeasure(): Measure {
return this.dataSource.getMeasure(this.pinnedSort);
}
public changePinnedSortMeasure(measure: Measure): Essence {
var value = this.valueOf();
value.pinnedSort = measure.name;
return new Essence(value);
}
public toggleMultiMeasureMode(): Essence {
const { dataSource, multiMeasureMode, selectedMeasures, singleMeasure } = this;
var value = this.valueOf();
value.multiMeasureMode = !multiMeasureMode;
if (multiMeasureMode) {
// Ensure that the singleMeasure is in the selectedMeasures
if (selectedMeasures.size && !selectedMeasures.has(singleMeasure)) {
value.singleMeasure = selectedMeasures.first();
}
} else {
value.selectedMeasures = addToSetInOrder(dataSource.measures.map(m => m.name), value.selectedMeasures, singleMeasure);
}
return new Essence(value);
}
public changeSingleMeasure(measure: Measure): Essence {
if (measure.name === this.singleMeasure) return this;
var value = this.valueOf();
value.singleMeasure = measure.name;
value.pinnedSort = measure.name;
return new Essence(value);
}
public toggleSelectedMeasure(measure: Measure): Essence {
var dataSource = this.dataSource;
var value = this.valueOf();
var selectedMeasures = value.selectedMeasures;
var measureName = measure.name;
if (selectedMeasures.has(measureName)) {
value.selectedMeasures = selectedMeasures.delete(measureName);
} else {
value.selectedMeasures = addToSetInOrder(dataSource.measures.map(m => m.name), selectedMeasures, measureName);
}
return new Essence(value);
}
public toggleEffectiveMeasure(measure: Measure): Essence {
if (this.getEffectiveMultiMeasureMode()) {
return this.toggleSelectedMeasure(measure);
} else {
return this.changeSingleMeasure(measure);
}
}
public acceptHighlight(): Essence {
var { highlight } = this;
if (!highlight) return this;
return this.changeFilter(highlight.applyToFilter(this.filter), true);
}
public changeHighlight(owner: string, measure: string, delta: Filter): Essence {
var { highlight } = this;
// If there is already a highlight from someone else accept it
var value: EssenceValue;
if (highlight && highlight.owner !== owner) {
value = this.changeFilter(highlight.applyToFilter(this.filter)).valueOf();
} else {
value = this.valueOf();
}
value.highlight = new Highlight({
owner,
delta,
measure
});
return new Essence(value);
}
public dropHighlight(): Essence {
var value = this.valueOf();
value.highlight = null;
return new Essence(value);
}
}
check = Essence;