UNPKG

webglimpse

Version:

Webglimpse is a data visualization library for the web.

1,301 lines 236 kB
/* * Copyright (c) 2014, Metron, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * 3. Neither the name of the copyright holder nor the names of its contributors * may be used to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ import { OrderedStringSet, OrderedSet } from '../util/ordered_set'; import { parseCssColor } from '../color'; import { Notification, Notification2 } from '../util/notification'; import { formatTime_ISO8601, hasval, parseTime_PMILLIS } from '../util/util'; import { indexOf } from '../util/sorted_arrays'; import { FillPattern } from './timeline_events_row'; import { Axis1D } from '../plot/axis'; export class TimelineCursorModel { constructor(cursor) { this._cursorGuid = cursor.cursorGuid; this._attrsChanged = new Notification(); this.setAttrs(cursor); } get labeledTimeseriesGuids() { return this._labeledTimeseriesGuids; } get cursorGuid() { return this._cursorGuid; } get attrsChanged() { return this._attrsChanged; } get lineColor() { return this._lineColor; } get textColor() { return this._textColor; } get showVerticalLine() { return this._showVerticalLine; } get showHorizontalLine() { return this._showHorizontalLine; } get showCursorText() { return this._showCursorText; } setAttrs(cursor) { this._labeledTimeseriesGuids = new OrderedStringSet(cursor.labeledTimeseriesGuids || []); this._lineColor = (hasval(cursor.lineColor) ? parseCssColor(cursor.lineColor) : null); this._textColor = (hasval(cursor.textColor) ? parseCssColor(cursor.textColor) : null); this._showVerticalLine = cursor.showVerticalLine; this._showHorizontalLine = cursor.showHorizontalLine; this._showCursorText = cursor.showCursorText; this._attrsChanged.fire(); } snapshot() { return { cursorGuid: this._cursorGuid, labeledTimeseriesGuids: this._labeledTimeseriesGuids.toArray(), lineColor: (hasval(this._lineColor) ? this._lineColor.cssString : null), textColor: (hasval(this._textColor) ? this._textColor.cssString : null), showVerticalLine: this._showVerticalLine, showHorizontalLine: this._showHorizontalLine, showCursorText: this._showCursorText }; } } export class TimelineAnnotationModel { constructor(annotation) { this._annotationGuid = annotation.annotationGuid; this._attrsChanged = new Notification(); this.setAttrs(annotation); } get annotationGuid() { return this._annotationGuid; } get attrsChanged() { return this._attrsChanged; } setLocation(time_PMILLIS, y) { if (time_PMILLIS !== this._time_PMILLIS || y !== this.y) { this._y = y; this._time_PMILLIS = time_PMILLIS; this._attrsChanged.fire(); } } get time_PMILLIS() { return this._time_PMILLIS; } set time_PMILLIS(time_PMILLIS) { if (time_PMILLIS !== this._time_PMILLIS) { this._time_PMILLIS = time_PMILLIS; this._attrsChanged.fire(); } } get y() { return this._y; } set y(y) { if (y !== this.y) { this._y = y; this._attrsChanged.fire(); } } get label() { return this._label; } set label(label) { if (label !== this.label) { this._label = label; this._attrsChanged.fire(); } } get styleGuid() { return this._styleGuid; } set styleGuid(styleGuid) { if (styleGuid !== this.styleGuid) { this._styleGuid = styleGuid; this._attrsChanged.fire(); } } get pickable() { return this._pickable; } set pickable(pickable) { if (pickable !== this.pickable) { this._pickable = pickable; this._attrsChanged.fire(); } } setAttrs(annotation) { // Don't both checking whether values are going to change -- it's not that important, and it would be obnoxious here this._time_PMILLIS = hasval(annotation.time_ISO8601) ? parseTime_PMILLIS(annotation.time_ISO8601) : undefined; this._y = annotation.y; this._label = annotation.label; this._styleGuid = annotation.styleGuid; this._pickable = hasval(annotation.pickable) ? annotation.pickable : true; this._attrsChanged.fire(); } snapshot() { return { annotationGuid: this._annotationGuid, label: this._label, styleGuid: this._styleGuid, time_ISO8601: formatTime_ISO8601(this._time_PMILLIS), y: this._y, pickable: this._pickable, }; } } export class TimelineTimeseriesModel { constructor(timeseries) { this._timeseriesGuid = timeseries.timeseriesGuid; this._attrsChanged = new Notification(); this.setAttrs(timeseries); this._fragmentGuids = new OrderedStringSet(timeseries.fragmentGuids || []); } get timeseriesGuid() { return this._timeseriesGuid; } get attrsChanged() { return this._attrsChanged; } setAttrs(timeseries) { // Don't both checking whether values are going to change -- it's not that important, and it would be obnoxious here this._uiHint = timeseries.uiHint; this._baseline = timeseries.baseline; this._lineColor = (hasval(timeseries.lineColor) ? parseCssColor(timeseries.lineColor) : null); this._pointColor = (hasval(timeseries.pointColor) ? parseCssColor(timeseries.pointColor) : null); this._lineThickness = timeseries.lineThickness; this._pointSize = timeseries.pointSize; this._dash = timeseries.dash; this._fragmentGuids = new OrderedStringSet(timeseries.fragmentGuids || []); this._attrsChanged.fire(); } get baseline() { return this._baseline; } set baseline(baseline) { if (baseline !== this._baseline) { this._baseline = baseline; this._attrsChanged.fire(); } } get lineColor() { return this._lineColor; } set lineColor(lineColor) { if (lineColor !== this._lineColor) { this._lineColor = lineColor; this._attrsChanged.fire(); } } get pointColor() { return this._pointColor; } set pointColor(pointColor) { if (pointColor !== this._pointColor) { this._pointColor = pointColor; this._attrsChanged.fire(); } } get lineThickness() { return this._lineThickness; } set lineThickness(lineThickness) { if (lineThickness !== this._lineThickness) { this._lineThickness = lineThickness; this._attrsChanged.fire(); } } get pointSize() { return this._pointSize; } set pointSize(pointSize) { if (pointSize !== this._pointSize) { this._pointSize = pointSize; this._attrsChanged.fire(); } } get dash() { return this._dash; } set dash(dash) { if (dash !== this._dash) { this._dash = dash; this._attrsChanged.fire(); } } get uiHint() { return this._uiHint; } set uiHint(uiHint) { if (uiHint !== this._uiHint) { this._uiHint = uiHint; this._attrsChanged.fire(); } } get fragmentGuids() { return this._fragmentGuids; } set fragmentGuids(fragmentGuids) { if (fragmentGuids !== this._fragmentGuids) { this._fragmentGuids = fragmentGuids; this._attrsChanged.fire(); } } snapshot() { return { timeseriesGuid: this._timeseriesGuid, uiHint: this._uiHint, baseline: this._baseline, lineColor: (hasval(this._lineColor) ? this._lineColor.cssString : null), pointColor: (hasval(this._pointColor) ? this._pointColor.cssString : null), lineThickness: this._lineThickness, pointSize: this._pointSize, dash: this._dash, fragmentGuids: this._fragmentGuids.toArray(), }; } } export class TimelineTimeseriesFragmentModel { constructor(fragment) { this._fragmentGuid = fragment.fragmentGuid; this._attrsChanged = new Notification(); this._dataChanged = new Notification2(); this.setAttrs(fragment); } get fragmentGuid() { return this._fragmentGuid; } get dataChanged() { return this._dataChanged; } setAttrs(fragment) { this._userEditMode = fragment.userEditMode; this._times_PMILLIS = hasval(fragment.times_ISO8601) ? fragment.times_ISO8601.map(parseTime_PMILLIS) : []; this._data = hasval(fragment.data) ? fragment.data.slice() : []; this._dataChanged.fire(0, this._data.length); this._attrsChanged.fire(); } get data() { return this._data; } set data(data) { if (data !== this._data) { this._data = data; this._dataChanged.fire(0, this._data.length); } } get times_PMILLIS() { return this._times_PMILLIS; } // Time should only be modified in a way which keeps the _times_PMILLIS // array sorted. This is currently not enforced by the model. set times_PMILLIS(times_PMILLIS) { if (times_PMILLIS !== this._times_PMILLIS) { this._times_PMILLIS = times_PMILLIS; this._dataChanged.fire(0, this._data.length); } } // Time should only be modified in a way which keeps the _times_PMILLIS // array sorted. This is currently not enforced by the model. setAllData(data, times_PMILLIS) { if (data !== this._data || times_PMILLIS !== this._times_PMILLIS) { this._data = data; this._times_PMILLIS = times_PMILLIS; this._dataChanged.fire(0, this._data.length); } } // Handles adjusting the _times_PMILLIS and _data arrays if the new time // requires them to be rearranged to stay in time order. Returns the new // index assigned to the data point. setData(index, value, time) { if (this._data[index] !== value || (hasval(time) && this._times_PMILLIS[index] !== time)) { if (hasval(time)) { // the new time value would maintain the sorted order of the array if ((index === 0 || time > this._times_PMILLIS[index - 1]) && (index === this._times_PMILLIS.length - 1 || time < this._times_PMILLIS[index + 1])) { this._times_PMILLIS[index] = time; this._data[index] = value; this._dataChanged.fire(index, index + 1); } else { // remove the current point at index this._times_PMILLIS.splice(index, 1); this._data.splice(index, 1); // find the index to reinsert new data at index = indexOf(this._times_PMILLIS, time); if (index < 0) { index = -index - 1; } this._times_PMILLIS.splice(index, 0, time); this._data.splice(index, 0, value); this._dataChanged.fire(index, index + 1); } } else { this._data[index] = value; this._dataChanged.fire(index, index + 1); } } return index; } get start_PMILLIS() { return this._times_PMILLIS[0]; } get end_PMILLIS() { return this._times_PMILLIS.slice(-1)[0]; } get userEditMode() { return this._userEditMode; } set userEditMode(userEditMode) { if (userEditMode !== this._userEditMode) { this._userEditMode = userEditMode; this._attrsChanged.fire(); } } snapshot() { return { userEditMode: this._userEditMode, fragmentGuid: this._fragmentGuid, data: this._data.slice(), times_ISO8601: this._times_PMILLIS.map(formatTime_ISO8601) }; } } export class TimelineEventModel { constructor(event) { this._eventGuid = event.eventGuid; this._attrsChanged = new Notification(); this.setAttrs(event); } get eventGuid() { return this._eventGuid; } get attrsChanged() { return this._attrsChanged; } setAttrs(event) { // Don't both checking whether values are going to change -- it's not that important, and it would be obnoxious here this._startLimit_PMILLIS = (hasval(event.startLimit_ISO8601) ? Math.trunc(parseTime_PMILLIS(event.startLimit_ISO8601)) : null); this._endLimit_PMILLIS = (hasval(event.endLimit_ISO8601) ? Math.trunc(parseTime_PMILLIS(event.endLimit_ISO8601)) : null); this._start_PMILLIS = Math.trunc(parseTime_PMILLIS(event.start_ISO8601)); this._end_PMILLIS = Math.trunc(parseTime_PMILLIS(event.end_ISO8601)); this._label = event.label; this._labelIcon = event.labelIcon; this._userEditable = (hasval(event.userEditable) ? event.userEditable : false); this._styleGuid = event.styleGuid; this._order = event.order; this._topMargin = event.topMargin; this._bottomMargin = event.bottomMargin; this._fgColor = (hasval(event.fgColor) ? parseCssColor(event.fgColor) : null); this._bgColor = (hasval(event.bgColor) ? parseCssColor(event.bgColor) : null); this._bgSecondaryColor = (hasval(event.bgSecondaryColor) ? parseCssColor(event.bgSecondaryColor) : null); this._borderColor = (hasval(event.borderColor) ? parseCssColor(event.borderColor) : null); this._borderSecondaryColor = (hasval(event.borderSecondaryColor) ? parseCssColor(event.borderSecondaryColor) : null); this._labelTopMargin = event.labelTopMargin; this._labelBottomMargin = event.labelBottomMargin; this._labelVAlign = event.labelVAlign; this._labelVPos = event.labelVPos; this._labelHAlign = event.labelHAlign; this._labelHPos = event.labelHPos; this._isBorderDashed = (hasval(event.isBorderDashed) ? event.isBorderDashed : false); this._fillPattern = (hasval(event.fillPattern) ? FillPattern[event.fillPattern] : FillPattern.solid); this._attrsChanged.fire(); } setInterval(start_PMILLIS, end_PMILLIS) { if (start_PMILLIS !== this._start_PMILLIS || end_PMILLIS !== this._end_PMILLIS) { const initial_start_PMILLIS = this._start_PMILLIS; const initial_end_PMILLIS = this._end_PMILLIS; const underStartLimit = hasval(this._startLimit_PMILLIS) && start_PMILLIS < this._startLimit_PMILLIS; const overEndLimit = hasval(this._endLimit_PMILLIS) && end_PMILLIS > this._endLimit_PMILLIS; const duration_PMILLIS = end_PMILLIS - start_PMILLIS; const durationLimit_PMILLIS = this._endLimit_PMILLIS - this._startLimit_PMILLIS; // If both limits are present and the event is larger than the total distance between them // then shrink the event to fit between the limits. if (hasval(this._startLimit_PMILLIS) && hasval(this._endLimit_PMILLIS) && durationLimit_PMILLIS < duration_PMILLIS) { this._start_PMILLIS = Math.trunc(this._startLimit_PMILLIS); this._end_PMILLIS = Math.trunc(this._endLimit_PMILLIS); } // Otherwise shift the event to comply with the limits without adjusting its total duration else if (underStartLimit) { this._start_PMILLIS = Math.trunc(this._startLimit_PMILLIS); this._end_PMILLIS = Math.trunc(this._start_PMILLIS + duration_PMILLIS); } else if (overEndLimit) { this._end_PMILLIS = Math.trunc(this._endLimit_PMILLIS); this._start_PMILLIS = Math.trunc(this._end_PMILLIS - duration_PMILLIS); } else { this._end_PMILLIS = Math.trunc(end_PMILLIS); this._start_PMILLIS = Math.trunc(start_PMILLIS); } // its possible due to the limits that the values didn't actually change // only fire attrsChanged if one of the values did actually change if (initial_start_PMILLIS !== this._start_PMILLIS || initial_end_PMILLIS !== this._end_PMILLIS) { this._attrsChanged.fire(); } } } limit_start_PMILLIS(start_PMILLIS) { return hasval(this._startLimit_PMILLIS) ? Math.max(start_PMILLIS, this._startLimit_PMILLIS) : start_PMILLIS; } limit_end_PMILLIS(end_PMILLIS) { return hasval(this._endLimit_PMILLIS) ? Math.min(end_PMILLIS, this._endLimit_PMILLIS) : end_PMILLIS; } get start_PMILLIS() { return this._start_PMILLIS; } set start_PMILLIS(start_PMILLIS) { if (start_PMILLIS !== this._start_PMILLIS) { this._start_PMILLIS = Math.trunc(this.limit_start_PMILLIS(start_PMILLIS)); this._attrsChanged.fire(); } } get end_PMILLIS() { return this._end_PMILLIS; } set end_PMILLIS(end_PMILLIS) { if (end_PMILLIS !== this._end_PMILLIS) { this._end_PMILLIS = Math.trunc(this.limit_end_PMILLIS(end_PMILLIS)); this._attrsChanged.fire(); } } get startLimit_PMILLIS() { return this._startLimit_PMILLIS; } set startLimit_PMILLIS(startLimit_PMILLIS) { if (startLimit_PMILLIS !== this._startLimit_PMILLIS) { this._startLimit_PMILLIS = Math.trunc(startLimit_PMILLIS); this._start_PMILLIS = Math.trunc(this.limit_start_PMILLIS(this._start_PMILLIS)); this._attrsChanged.fire(); } } get endLimit_PMILLIS() { return this._endLimit_PMILLIS; } set endLimit_PMILLIS(endLimit_PMILLIS) { if (endLimit_PMILLIS !== this._endLimit_PMILLIS) { this._endLimit_PMILLIS = Math.trunc(endLimit_PMILLIS); this._end_PMILLIS = Math.trunc(this.limit_end_PMILLIS(this._end_PMILLIS)); this._attrsChanged.fire(); } } get label() { return this._label; } set label(label) { if (label !== this._label) { this._label = label; this._attrsChanged.fire(); } } get labelIcon() { return this._labelIcon; } set labelIcon(labelIcon) { if (labelIcon !== this._labelIcon) { this._labelIcon = labelIcon; this._attrsChanged.fire(); } } get userEditable() { return this._userEditable; } set userEditable(userEditable) { if (userEditable !== this._userEditable) { this._userEditable = userEditable; this._attrsChanged.fire(); } } get styleGuid() { return this._styleGuid; } set styleGuid(styleGuid) { if (styleGuid !== this._styleGuid) { this._styleGuid = styleGuid; this._attrsChanged.fire(); } } get order() { return this._order; } set order(orderVal) { if (orderVal !== this._order) { this._order = orderVal; this._attrsChanged.fire(); } } get topMargin() { return this._topMargin; } set topMargin(topMargin) { if (topMargin !== this._topMargin) { this._topMargin = topMargin; this._attrsChanged.fire(); } } get bottomMargin() { return this._bottomMargin; } set bottomMargin(bottomMargin) { if (bottomMargin !== this._bottomMargin) { this._bottomMargin = bottomMargin; this._attrsChanged.fire(); } } get fgColor() { return this._fgColor; } set fgColor(fgColor) { if (fgColor !== this._fgColor) { this._fgColor = fgColor; this._attrsChanged.fire(); } } get bgColor() { return this._bgColor; } set bgColor(bgColor) { if (bgColor !== this._bgColor) { this._bgColor = bgColor; this._attrsChanged.fire(); } } get bgSecondaryColor() { return this._bgSecondaryColor; } set bgSecondaryColor(bgSecondaryColor) { if (bgSecondaryColor !== this._bgSecondaryColor) { this._bgSecondaryColor = bgSecondaryColor; this._attrsChanged.fire(); } } get borderColor() { return this._borderColor; } set borderColor(borderColor) { if (borderColor !== this._borderColor) { this._borderColor = borderColor; this._attrsChanged.fire(); } } get borderSecondaryColor() { return this._borderSecondaryColor; } set borderSecondaryColor(borderSecondaryColor) { if (borderSecondaryColor !== this._borderSecondaryColor) { this._borderSecondaryColor = borderSecondaryColor; this._attrsChanged.fire(); } } get labelTopMargin() { return this._labelTopMargin; } set labelTopMargin(labelTopMargin) { if (labelTopMargin !== this._labelTopMargin) { this._labelTopMargin = labelTopMargin; this._attrsChanged.fire(); } } get labelBottomMargin() { return this._labelBottomMargin; } set labelBottomMargin(labelBottomMargin) { if (labelBottomMargin !== this._labelBottomMargin) { this._labelBottomMargin = labelBottomMargin; this._attrsChanged.fire(); } } get labelVAlign() { return this._labelVAlign; } set labelVAlign(labelVAlign) { if (labelVAlign !== this._labelVAlign) { this._labelVAlign = labelVAlign; this._attrsChanged.fire(); } } get labelVPos() { return this._labelVPos; } set labelVPos(labelVPos) { if (labelVPos !== this._labelVPos) { this._labelVPos = labelVPos; this._attrsChanged.fire(); } } get labelHAlign() { return this._labelHAlign; } set labelHAlign(labelHAlign) { if (labelHAlign !== this._labelHAlign) { this._labelHAlign = labelHAlign; this._attrsChanged.fire(); } } get labelHPos() { return this._labelHPos; } set labelHPos(labelHPos) { if (labelHPos !== this._labelHPos) { this._labelHPos = labelHPos; this._attrsChanged.fire(); } } get isBorderDashed() { return this._isBorderDashed; } set isBorderDashed(isBorderDashed) { if (isBorderDashed !== this._isBorderDashed) { this._isBorderDashed = isBorderDashed; this._attrsChanged.fire(); } } get fillPattern() { return this._fillPattern; } set fillPattern(fillPattern) { if (fillPattern !== this._fillPattern) { this._fillPattern = fillPattern; this._attrsChanged.fire(); } } snapshot() { return { eventGuid: this._eventGuid, startLimit_ISO8601: (hasval(this._startLimit_PMILLIS) ? formatTime_ISO8601(this._startLimit_PMILLIS) : null), endLimit_ISO8601: (hasval(this._endLimit_PMILLIS) ? formatTime_ISO8601(this._endLimit_PMILLIS) : null), start_ISO8601: formatTime_ISO8601(this._start_PMILLIS), end_ISO8601: formatTime_ISO8601(this._end_PMILLIS), label: this._label, labelIcon: this._labelIcon, userEditable: this._userEditable, styleGuid: this._styleGuid, order: this._order, topMargin: this._topMargin, bottomMargin: this._bottomMargin, bgColor: (hasval(this._bgColor) ? this._bgColor.cssString : null), bgSecondaryColor: (hasval(this._bgSecondaryColor) ? this._bgSecondaryColor.cssString : null), fgColor: (hasval(this._fgColor) ? this._fgColor.cssString : null), borderColor: (hasval(this._borderColor) ? this._borderColor.cssString : null), borderSecondaryColor: (hasval(this._borderSecondaryColor) ? this.borderSecondaryColor.cssString : null), labelTopMargin: this._labelTopMargin, labelBottomMargin: this._labelBottomMargin, labelVAlign: this._labelVAlign, labelVPos: this._labelVPos, labelHAlign: this._labelHAlign, labelHPos: this._labelHPos, isBorderDashed: this._isBorderDashed, fillPattern: FillPattern[this._fillPattern] }; } } export class TimelineRowModel { constructor(row) { this._rowGuid = row.rowGuid; this._attrsChanged = new Notification(); const min = hasval(row.yMin) ? row.yMin : 0; const max = hasval(row.yMax) ? row.yMax : 1; this._dataAxis = new Axis1D(min, max); this.setAttrs(row); this._eventGuids = new OrderedStringSet(row.eventGuids || []); this._timeseriesGuids = new OrderedStringSet(row.timeseriesGuids || []); this._annotationGuids = new OrderedStringSet(row.annotationGuids || []); } get rowGuid() { return this._rowGuid; } get attrsChanged() { return this._attrsChanged; } setAttrs(row) { // Don't both checking whether values are going to change -- it's not that important, and it would be obnoxious here this._label = row.label; this._truncate = row.truncate; this._uiHint = row.uiHint; this._hidden = row.hidden; this._rowHeight = row.rowHeight; this._cursorGuid = row.cursorGuid; this._bgColor = (hasval(row.bgColor) ? parseCssColor(row.bgColor) : null); this._fgLabelColor = (hasval(row.fgLabelColor) ? parseCssColor(row.fgLabelColor) : null); this._bgLabelColor = (hasval(row.bgLabelColor) ? parseCssColor(row.bgLabelColor) : null); this._labelFont = row.labelFont; this._cursor = row.cursor; this._highlighted = hasval(row.highlighted) ? row.highlighted : false; this._highlightColor = hasval(row.highlightColor) ? row.highlightColor : parseCssColor('white'); this._highlightWidth = row.highlightWidth; this._highlightInsets = row.highlightInsets; this._attrsChanged.fire(); } get cursorGuid() { return this._cursorGuid; } set cursorGuid(cursorGuid) { this._cursorGuid = cursorGuid; this._attrsChanged.fire(); } get rowHeight() { return this._rowHeight; } set rowHeight(rowHeight) { this._rowHeight = rowHeight; this._attrsChanged.fire(); } get hidden() { return this._hidden; } set hidden(hidden) { this._hidden = hidden; this._attrsChanged.fire(); } get dataAxis() { return this._dataAxis; } set dataAxis(dataAxis) { this._dataAxis = dataAxis; this._attrsChanged.fire(); } get label() { return this._label; } set label(label) { if (label !== this._label) { this._label = label; this._attrsChanged.fire(); } } get truncate() { return this._truncate; } set truncate(truncate) { if (truncate !== this._truncate) { this._truncate = truncate; this._attrsChanged.fire(); } } get uiHint() { return this._uiHint; } set uiHint(uiHint) { if (uiHint !== this._uiHint) { this._uiHint = uiHint; this._attrsChanged.fire(); } } get bgColor() { return this._bgColor; } set bgColor(bgColor) { if (bgColor !== this._bgColor) { this._bgColor = bgColor; this._attrsChanged.fire(); } } get bgLabelColor() { return this._bgLabelColor; } set bgLabelColor(bgLabelColor) { if (bgLabelColor !== this._bgLabelColor) { this._bgLabelColor = bgLabelColor; this._attrsChanged.fire(); } } get fgLabelColor() { return this._fgLabelColor; } set fgLabelColor(fgLabelColor) { if (fgLabelColor !== this._fgLabelColor) { this._fgLabelColor = fgLabelColor; this._attrsChanged.fire(); } } get labelFont() { return this._labelFont; } set labelFont(labelFont) { if (labelFont !== this._labelFont) { this._labelFont = labelFont; this._attrsChanged.fire(); } } get cursor() { return this._cursor; } set cursor(cursor) { if (cursor !== this._cursor) { this._cursor = cursor; this._attrsChanged.fire(); } } get highlighted() { return this._highlighted; } set highlighted(highlighted) { if (highlighted !== this._highlighted) { this._highlighted = highlighted; this._attrsChanged.fire(); } } get highlightColor() { return this._highlightColor; } set highlightColor(highlightColor) { if (highlightColor !== this._highlightColor) { this._highlightColor = highlightColor; this._attrsChanged.fire(); } } get highlightWidth() { return this._highlightWidth; } set highlightWidth(highlightWidth) { if (highlightWidth !== this._highlightWidth) { this._highlightWidth = highlightWidth; this._attrsChanged.fire(); } } get highlightInsets() { return this._highlightInsets; } set highlightInsets(highlightInsets) { if (highlightInsets !== this._highlightInsets) { this._highlightInsets = highlightInsets; this._attrsChanged.fire(); } } get eventGuids() { return this._eventGuids; } get timeseriesGuids() { return this._timeseriesGuids; } get annotationGuids() { return this._annotationGuids; } snapshot() { return { rowGuid: this._rowGuid, label: this._label, truncate: this._truncate, rowHeight: this._rowHeight, hidden: this._hidden, uiHint: this._uiHint, eventGuids: this._eventGuids.toArray(), timeseriesGuids: this._timeseriesGuids.toArray(), annotationGuids: this._annotationGuids.toArray(), cursorGuid: this._cursorGuid, bgColor: (hasval(this._bgColor) ? this._bgColor.cssString : null), bgLabelColor: (hasval(this._bgLabelColor) ? this._bgLabelColor.cssString : null), fgLabelColor: (hasval(this._fgLabelColor) ? this._fgLabelColor.cssString : null), labelFont: this._labelFont, cursor: this._cursor, highlighted: (hasval(this._highlighted) ? this._highlighted : false), highlightColor: hasval(this.highlightColor) ? this.highlightColor : parseCssColor('white'), highlightWidth: this._highlightWidth, highlightInsets: this._highlightInsets }; } } export class TimelineGroupModel { constructor(group) { this._groupGuid = group.groupGuid; this._attrsChanged = new Notification(); this.setAttrs(group); this._rowGuids = new OrderedStringSet(group.rowGuids); } get groupGuid() { return this._groupGuid; } get rollupGuid() { return this._rollupGuid; } set rollupGuid(rollupGuid) { this._rollupGuid = rollupGuid; this._attrsChanged.fire(); } get attrsChanged() { return this._attrsChanged; } setAttrs(group) { // Don't both checking whether values are going to change -- it's not that important, and it would be obnoxious here this._rollupGuid = group.rollupGuid; this._hidden = group.hidden; this._label = group.label; this._collapsed = group.collapsed; this._highlighted = hasval(group.highlighted) ? group.highlighted : false; this._highlightColor = hasval(group.highlightColor) ? group.highlightColor : parseCssColor('white'); this._dashPattern = hasval(group.dashPattern) ? group.dashPattern : 0xFFFF; this._dashLength = hasval(group.dashLength) ? group.dashLength : 16; this._highlightWidth = group.highlightWidth; this._highlightInsets = group.highlightInsets; this._labelFont = group.labelFont; this._cursor = group.cursor; this._attrsChanged.fire(); } get hidden() { return this._hidden; } set hidden(hidden) { this._hidden = hidden; this._attrsChanged.fire(); } get label() { return this._label; } set label(label) { if (label !== this._label) { this._label = label; this._attrsChanged.fire(); } } get collapsed() { return this._collapsed; } set collapsed(collapsed) { if (collapsed !== this._collapsed) { this._collapsed = collapsed; this._attrsChanged.fire(); } } get highlighted() { return this._highlighted; } set highlighted(highlighted) { if (highlighted !== this._highlighted) { this._highlighted = highlighted; this._attrsChanged.fire(); } } get highlightColor() { return this._highlightColor; } set highlightColor(highlightColor) { if (highlightColor !== this._highlightColor) { this._highlightColor = highlightColor; this._attrsChanged.fire(); } } get highlightWidth() { return this._highlightWidth; } set highlightWidth(highlightWidth) { if (highlightWidth !== this._highlightWidth) { this._highlightWidth = highlightWidth; this._attrsChanged.fire(); } } get highlightInsets() { return this._highlightInsets; } set highlightInsets(highlightInsets) { if (highlightInsets !== this._highlightInsets) { this._highlightInsets = highlightInsets; this._attrsChanged.fire(); } } get dashPattern() { return this._dashPattern; } set dashPattern(dashPattern) { if (dashPattern !== this._dashPattern) { this._dashPattern = dashPattern; this._attrsChanged.fire(); } } get dashLength() { return this._dashLength; } set dashLength(dashLength) { if (dashLength !== this._dashLength) { this._dashLength = dashLength; this._attrsChanged.fire(); } } get labelFont() { return this._labelFont; } set labelFont(labelFont) { if (labelFont !== this._labelFont) { this._labelFont = labelFont; this._attrsChanged.fire(); } } get cursor() { return this._cursor; } set cursor(cursor) { if (cursor !== this._cursor) { this._cursor = cursor; this._attrsChanged.fire(); } } get rowGuids() { return this._rowGuids; } snapshot() { return { groupGuid: this._groupGuid, rollupGuid: this._rollupGuid, label: this._label, hidden: this._hidden, collapsed: (hasval(this._collapsed) ? this._collapsed : false), highlighted: (hasval(this._highlighted) ? this._highlighted : false), highlightColor: hasval(this.highlightColor) ? this.highlightColor : parseCssColor('white'), highlightWidth: this._highlightWidth, highlightInsets: this._highlightInsets, dashPattern: hasval(this.dashPattern) ? this.dashPattern : 0xFFFF, dashLength: hasval(this.dashLength) ? this.dashLength : 16, labelFont: this._labelFont, cursor: this._cursor, rowGuids: this._rowGuids.toArray() }; } } export class TimelineRootModel { constructor(root) { this._attrsChanged = new Notification(); this.setAttrs(root); this._groupGuids = new OrderedStringSet(root.groupGuids); this._topPinnedRowGuids = new OrderedStringSet(root.topPinnedRowGuids || []); this._bottomPinnedRowGuids = new OrderedStringSet(root.bottomPinnedRowGuids || []); this._maximizedRowGuids = new OrderedStringSet(root.maximizedRowGuids || []); } get attrsChanged() { return this._attrsChanged; } setAttrs(root) { // Don't both checking whether values are going to change -- it's not that important, and it would be obnoxious here // No attrs yet this._attrsChanged.fire(); } get groupGuids() { return this._groupGuids; } get topPinnedRowGuids() { return this._topPinnedRowGuids; } get bottomPinnedRowGuids() { return this._bottomPinnedRowGuids; } get maximizedRowGuids() { return this._maximizedRowGuids; } snapshot() { return { groupGuids: this._groupGuids.toArray(), topPinnedRowGuids: this._topPinnedRowGuids.toArray(), bottomPinnedRowGuids: this._bottomPinnedRowGuids.toArray(), maximizedRowGuids: this._maximizedRowGuids.toArray() }; } } export class TimelineModel { constructor(timeline) { const cursors = (hasval(timeline) && hasval(timeline.cursors) ? timeline.cursors : []); this._cursors = new OrderedSet([], (g) => g.cursorGuid); for (let n = 0; n < cursors.length; n++) { this._cursors.add(new TimelineCursorModel(cursors[n])); } const annotations = (hasval(timeline) && hasval(timeline.annotations) ? timeline.annotations : []); this._annotations = new OrderedSet([], (g) => g.annotationGuid); for (let n = 0; n < annotations.length; n++) { this._annotations.add(new TimelineAnnotationModel(annotations[n])); } const timeseriesFragments = (hasval(timeline) && hasval(timeline.timeseriesFragments) ? timeline.timeseriesFragments : []); this._timeseriesFragments = new OrderedSet([], (e) => e.fragmentGuid); for (let n = 0; n < timeseriesFragments.length; n++) { this._timeseriesFragments.add(new TimelineTimeseriesFragmentModel(timeseriesFragments[n])); } const timeseries = (hasval(timeline) && hasval(timeline.timeseries) ? timeline.timeseries : []); this._timeseries = new OrderedSet([], (e) => e.timeseriesGuid); for (let n = 0; n < timeseries.length; n++) { this._timeseries.add(new TimelineTimeseriesModel(timeseries[n])); } const events = (hasval(timeline) && hasval(timeline.events) ? timeline.events : []); this._events = new OrderedSet([], (e) => e.eventGuid); for (let n = 0; n < events.length; n++) { this._events.add(new TimelineEventModel(events[n])); } const rows = (hasval(timeline) && hasval(timeline.rows) ? timeline.rows : []); this._rows = new OrderedSet([], (r) => r.rowGuid); for (let n = 0; n < rows.length; n++) { this._rows.add(new TimelineRowModel(rows[n])); } const groups = (hasval(timeline) && hasval(timeline.groups) ? timeline.groups : []); this._groups = new OrderedSet([], (g) => g.groupGuid); for (let n = 0; n < groups.length; n++) { this._groups.add(new TimelineGroupModel(groups[n])); } const root = (hasval(timeline) && hasval(timeline.root) ? timeline.root : newEmptyTimelineRoot()); this._root = new TimelineRootModel(root); } get cursors() { return this._cursors; } get annotations() { return this._annotations; } get timeseriesFragments() { return this._timeseriesFragments; } get timeseriesSets() { return this._timeseries; } get events() { return this._events; } get rows() { return this._rows; } get groups() { return this._groups; } get root() { return this._root; } cursor(cursorGuid) { return this._cursors.valueFor(cursorGuid); } annotation(annotationGuid) { return this._annotations.valueFor(annotationGuid); } timeseriesFragment(fragmentGuid) { return this._timeseriesFragments.valueFor(fragmentGuid); } timeseries(timeseriesGuid) { return this._timeseries.valueFor(timeseriesGuid); } event(eventGuid) { return this._events.valueFor(eventGuid); } row(rowGuid) { return this._rows.valueFor(rowGuid); } group(groupGuid) { return this._groups.valueFor(groupGuid); } replace(newTimeline) { // Purge removed items // const freshRoot = newTimeline.root; this._root.groupGuids.retainValues(freshRoot.groupGuids); this._root.topPinnedRowGuids.retainValues(freshRoot.topPinnedRowGuids); this._root.bottomPinnedRowGuids.retainValues(freshRoot.bottomPinnedRowGuids); this._root.maximizedRowGuids.retainValues(freshRoot.maximizedRowGuids); const freshGroups = newTimeline.groups; const retainedGroupGuids = []; for (let n = 0; n < freshGroups.length; n++) { const freshGroup = freshGroups[n]; const groupGuid = freshGroup.groupGuid; const oldGroup = this._groups.valueFor(groupGuid); if (hasval(oldGroup)) { oldGroup.rowGuids.retainValues(freshGroup.rowGuids); retainedGroupGuids.push(groupGuid); } } this._groups.retainIds(retainedGroupGuids); const freshRows = newTimeline.rows; const retainedRowGuids = []; for (let n = 0; n < freshRows.length; n++) { const freshRow = freshRows[n]; const rowGuid = freshRow.rowGuid; const oldRow = this._rows.valueFor(rowGuid); if (hasval(oldRow)) { oldRow.eventGuids.retainValues(freshRow.eventGuids || []); retainedRowGuids.push(rowGuid); } } this._rows.retainIds(retainedRowGuids); const freshEvents = newTimeline.events; const retainedEventGuids = []; for (let n = 0; n < freshEvents.length; n++) { const freshEvent = freshEvents[n]; const eventGuid = freshEvent.eventGuid; const oldEvent = this._events.valueFor(eventGuid); if (hasval(oldEvent)) { retainedEventGuids.push(eventGuid); } } this._events.retainIds(retainedEventGuids); const freshTimeseriesSet = newTimeline.timeseries; const retainedTimeseriesGuids = []; for (let n = 0; n < freshTimeseriesSet.length; n++) { const freshTimeseries = freshTimeseriesSet[n]; const timeseriesGuid = freshTimeseries.timeseriesGuid; const oldTimeseries = this._timeseries.valueFor(timeseriesGuid); if (hasval(oldTimeseries)) { retainedTimeseriesGuids.push(timeseriesGuid); } } this._timeseries.retainIds(retainedTimeseriesGuids); const freshTimeseriesFragments = newTimeline.timeseriesFragments; const retainedTimeseriesFragmentGuids = []; for (let n = 0; n < freshTimeseriesFragments.length; n++) { const freshTimeseriesFragment = freshTimeseriesFragments[n]; const fragmentGuid = freshTimeseriesFragment.fragmentGuid; const oldTimeseriesFragment = this._timeseriesFragments.valueFor(fragmentGuid); if (hasval(oldTimeseriesFragment)) { retainedTimeseriesFragmentGuids.push(fragmentGuid); } } this._timeseriesFragments.retainIds(retainedTimeseriesFragmentGuids); const freshAnnotations = newTimeline.annotations; const retainedAnnotationGuids = []; for (let n = 0; n < freshAnnotations.length; n++) { const freshAnnotation = freshAnnotations[n]; const annotationGuid = freshAnnotation.annotationGuid; const oldAnnotation = this._annotations.valueFor(annotationGuid); if (hasval(oldAnnotation)) { retainedAnnotationGuids.push(annotationGuid); } } this._annotations.retainIds(retainedAnnotationGuids); const freshCursors = newTimeline.cursors; const retainedCursorGuids = []; for (let n = 0; n < freshCursors.length; n++) { const freshCursor = freshCursors[n]; const cursorGuid = freshCursor.cursorGuid; const oldCursor = this._cursors.valueFor(cursorGuid); if (hasval(oldCursor)) { retainedCursorGuids.push(cursorGuid); } } this._cursors.retainIds(retainedCursorGuids); // Add new items // for (let n = 0; n < freshCursors.length; n++) { const freshCursor = freshCursors[n]; const oldCursor = this._cursors.valueFor(freshCursor.cursorGuid); if (hasval(oldCursor)) { oldCursor.setAttrs(freshCursor); } else { this._cursors.add(new TimelineCursorModel(freshCursor)); } } for (let n = 0; n < freshAnnotations.length; n++) { const freshAnnotation = freshAnnotations[n]; const oldAnnotation = this._annotations.valueFor(freshAnnotation.annotationGuid); if (hasval(oldAnnotation)) { oldAnnotation.setAttrs(freshAnnotation); } else { this._annotations.add(new TimelineAnnotationModel(freshAnnotation)); } } for (let n = 0; n < freshTimeseriesFragments.length; n++) { const freshTimeseriesFragment = freshTimeseriesFragments[n]; const oldTimeseriesFragment = this._timeseriesFragments.valueFor(freshTimeseriesFragment.fragmentGuid); if (hasval(oldTimeseriesFragment)) { oldTimeseriesFragment.setAttrs(freshTimeseriesFragment); } else { this._timeseriesFragments.add(new TimelineTimeseriesFragmentModel(freshTimeseriesFragment)); } } for (let n = 0; n < freshTimeseriesSet.length; n++) { const freshTimeseries = freshTimeseriesSet[n]; const oldTimeseries = this._timeseries.valueFor(freshTimeseries.timeseriesGuid); if (hasval(oldTimeseries)) { oldTimeseries.setAttrs(freshTimeseries); } else { this._timeseries.add(new TimelineTimeseriesModel(freshTimeseries)); } } for (let