tsichart-core 2.0.0-beta.7 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +1312 -8
- package/dist/index.js +1125 -1507
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1125 -1507
- package/dist/index.mjs.map +1 -1
- package/dist/index.umd.js +1171 -1549
- package/dist/index.umd.js.map +1 -1
- package/dist/styles/index.css +9442 -7369
- package/dist/styles/index.css.map +1 -1
- package/package.json +1 -1
package/dist/index.umd.js
CHANGED
|
@@ -29013,7 +29013,7 @@
|
|
|
29013
29013
|
swimLaneLabelHeightPadding: 8,
|
|
29014
29014
|
labelLeftPadding: 28
|
|
29015
29015
|
};
|
|
29016
|
-
const CharactersToEscapeForExactSearchInstance = ['"', '`', '\'', '!', '(', ')', '^', '[', '{', ':', '
|
|
29016
|
+
const CharactersToEscapeForExactSearchInstance = ['"', '`', '\'', '!', '(', ')', '^', '[', '{', ':', '}', ']', '~', '/', '\\', '@', '#', '$', '%', '&', '*', ';', '=', '.', '_', '-', '<', '>', ',', '?'];
|
|
29017
29017
|
const NONNUMERICTOPMARGIN = 8;
|
|
29018
29018
|
const LINECHARTTOPPADDING = 16;
|
|
29019
29019
|
const GRIDCONTAINERCLASS = 'tsi-gridContainer';
|
|
@@ -29457,50 +29457,89 @@
|
|
|
29457
29457
|
}
|
|
29458
29458
|
return hclColor.toString();
|
|
29459
29459
|
}
|
|
29460
|
+
/**
|
|
29461
|
+
* Creates an array of colors for split-by series
|
|
29462
|
+
* @param displayState - The current display state
|
|
29463
|
+
* @param aggKey - The aggregate key
|
|
29464
|
+
* @param ignoreIsOnlyAgg - Whether to ignore the "only aggregate" optimization
|
|
29465
|
+
* @returns Array of color strings for each split-by
|
|
29466
|
+
*/
|
|
29460
29467
|
static createSplitByColors(displayState, aggKey, ignoreIsOnlyAgg = false) {
|
|
29461
|
-
|
|
29468
|
+
const splitBys = displayState[aggKey]?.splitBys;
|
|
29469
|
+
if (!splitBys) {
|
|
29470
|
+
return [];
|
|
29471
|
+
}
|
|
29472
|
+
const splitByCount = Object.keys(splitBys).length;
|
|
29473
|
+
// Early return for single split-by
|
|
29474
|
+
if (splitByCount === 1) {
|
|
29462
29475
|
return [displayState[aggKey].color];
|
|
29463
|
-
var isOnlyAgg = Object.keys(displayState).reduce((accum, currAgg) => {
|
|
29464
|
-
if (currAgg == aggKey)
|
|
29465
|
-
return accum;
|
|
29466
|
-
if (displayState[currAgg]["visible"] == false)
|
|
29467
|
-
return accum && true;
|
|
29468
|
-
return false;
|
|
29469
|
-
}, true);
|
|
29470
|
-
if (isOnlyAgg && !ignoreIsOnlyAgg) {
|
|
29471
|
-
return this.generateColors(Object.keys(displayState[aggKey]["splitBys"]).length);
|
|
29472
29476
|
}
|
|
29473
|
-
|
|
29474
|
-
|
|
29475
|
-
|
|
29476
|
-
|
|
29477
|
-
|
|
29478
|
-
|
|
29477
|
+
// Create cache key for memoization
|
|
29478
|
+
const cacheKey = `${aggKey}_${splitByCount}_${displayState[aggKey].color}_${ignoreIsOnlyAgg}`;
|
|
29479
|
+
if (this.splitByColorCache.has(cacheKey)) {
|
|
29480
|
+
return this.splitByColorCache.get(cacheKey);
|
|
29481
|
+
}
|
|
29482
|
+
const isOnlyVisibleAgg = !ignoreIsOnlyAgg && this.isOnlyVisibleAggregate(displayState, aggKey);
|
|
29483
|
+
let colors;
|
|
29484
|
+
if (isOnlyVisibleAgg) {
|
|
29485
|
+
// Generate distinct colors when this is the only visible aggregate
|
|
29486
|
+
colors = this.generateColors(splitByCount);
|
|
29487
|
+
}
|
|
29488
|
+
else {
|
|
29489
|
+
// Generate color variations based on aggregate color
|
|
29490
|
+
colors = this.generateSplitByColorVariations(displayState[aggKey].color, splitByCount);
|
|
29491
|
+
}
|
|
29492
|
+
// Cache the result
|
|
29493
|
+
this.splitByColorCache.set(cacheKey, colors);
|
|
29494
|
+
// Limit cache size to prevent memory leaks
|
|
29495
|
+
if (this.splitByColorCache.size > 100) {
|
|
29496
|
+
const firstKey = this.splitByColorCache.keys().next().value;
|
|
29497
|
+
this.splitByColorCache.delete(firstKey);
|
|
29498
|
+
}
|
|
29499
|
+
return colors;
|
|
29500
|
+
}
|
|
29501
|
+
/**
|
|
29502
|
+
* Helper method to check if an aggregate is the only visible one
|
|
29503
|
+
*/
|
|
29504
|
+
static isOnlyVisibleAggregate(displayState, aggKey) {
|
|
29505
|
+
for (const currAgg in displayState) {
|
|
29506
|
+
if (currAgg !== aggKey && displayState[currAgg]?.visible !== false) {
|
|
29507
|
+
return false;
|
|
29508
|
+
}
|
|
29509
|
+
}
|
|
29510
|
+
return true;
|
|
29511
|
+
}
|
|
29512
|
+
/**
|
|
29513
|
+
* Helper method to generate color variations for split-bys
|
|
29514
|
+
*/
|
|
29515
|
+
static generateSplitByColorVariations(baseColor, count) {
|
|
29516
|
+
const baseHcl = hcl$2(baseColor);
|
|
29517
|
+
const interpolateColor = linear()
|
|
29518
|
+
.domain([0, count])
|
|
29519
|
+
.range([baseHcl.darker().l, baseHcl.brighter().l]);
|
|
29520
|
+
const colors = new Array(count);
|
|
29521
|
+
for (let i = 0; i < count; i++) {
|
|
29522
|
+
const newColor = hcl$2(baseColor);
|
|
29479
29523
|
newColor.l = interpolateColor(i);
|
|
29480
|
-
colors
|
|
29524
|
+
colors[i] = newColor.formatHex();
|
|
29481
29525
|
}
|
|
29482
29526
|
return colors;
|
|
29483
29527
|
}
|
|
29528
|
+
/**
|
|
29529
|
+
* Clears the split-by color cache (useful when display state changes significantly)
|
|
29530
|
+
*/
|
|
29531
|
+
static clearSplitByColorCache() {
|
|
29532
|
+
this.splitByColorCache.clear();
|
|
29533
|
+
}
|
|
29484
29534
|
static colorSplitBy(displayState, splitByIndex, aggKey, ignoreIsOnlyAgg = false) {
|
|
29485
|
-
|
|
29486
|
-
|
|
29487
|
-
|
|
29488
|
-
|
|
29489
|
-
|
|
29490
|
-
|
|
29491
|
-
return accum && true;
|
|
29492
|
-
return false;
|
|
29493
|
-
}, true);
|
|
29494
|
-
if (isOnlyAgg && !ignoreIsOnlyAgg) {
|
|
29495
|
-
var splitByColors = this.generateColors(Object.keys(displayState[aggKey]["splitBys"]).length);
|
|
29496
|
-
return splitByColors[splitByIndex];
|
|
29535
|
+
const colors = this.createSplitByColors(displayState, aggKey, ignoreIsOnlyAgg);
|
|
29536
|
+
if (typeof splitByIndex === 'number' &&
|
|
29537
|
+
Number.isInteger(splitByIndex) &&
|
|
29538
|
+
splitByIndex >= 0 &&
|
|
29539
|
+
splitByIndex < colors.length) {
|
|
29540
|
+
return colors[splitByIndex];
|
|
29497
29541
|
}
|
|
29498
|
-
|
|
29499
|
-
var interpolateColor = linear().domain([0, Object.keys(displayState[aggKey]["splitBys"]).length])
|
|
29500
|
-
.range([hcl$2(aggColor).darker().l, hcl$2(aggColor).brighter().l]);
|
|
29501
|
-
const newColor = hcl$2(aggColor);
|
|
29502
|
-
newColor.l = interpolateColor(splitByIndex);
|
|
29503
|
-
return newColor.formatHex();
|
|
29542
|
+
return displayState[aggKey]?.color || '#000000';
|
|
29504
29543
|
}
|
|
29505
29544
|
static getTheme(theme) {
|
|
29506
29545
|
return theme ? 'tsi-' + theme : 'tsi-dark';
|
|
@@ -29924,6 +29963,7 @@
|
|
|
29924
29963
|
}
|
|
29925
29964
|
}
|
|
29926
29965
|
Utils.guidForNullTSID = Utils.guid();
|
|
29966
|
+
Utils.splitByColorCache = new Map();
|
|
29927
29967
|
Utils.equalToEventTarget = (function (current, event) {
|
|
29928
29968
|
return (current == event.target);
|
|
29929
29969
|
});
|
|
@@ -30409,35 +30449,41 @@
|
|
|
30409
30449
|
}
|
|
30410
30450
|
}
|
|
30411
30451
|
|
|
30412
|
-
|
|
30413
|
-
|
|
30452
|
+
/**
|
|
30453
|
+
* Constants for Legend component layout and behavior
|
|
30454
|
+
*/
|
|
30455
|
+
const LEGEND_CONSTANTS = {
|
|
30456
|
+
/** Height in pixels for each numeric split-by item (includes type selector dropdown) */
|
|
30457
|
+
NUMERIC_SPLITBY_HEIGHT: 44,
|
|
30458
|
+
/** Height in pixels for each non-numeric (categorical/events) split-by item */
|
|
30459
|
+
NON_NUMERIC_SPLITBY_HEIGHT: 24,
|
|
30460
|
+
/** Height in pixels for the series name label header */
|
|
30461
|
+
NAME_LABEL_HEIGHT: 24,
|
|
30462
|
+
/** Buffer distance in pixels from scroll edge before triggering "load more" */
|
|
30463
|
+
SCROLL_BUFFER: 40,
|
|
30464
|
+
/** Number of split-by items to load per batch when paginating */
|
|
30465
|
+
BATCH_SIZE: 20,
|
|
30466
|
+
/** Minimum height in pixels for aggregate container */
|
|
30467
|
+
MIN_AGGREGATE_HEIGHT: 201,
|
|
30468
|
+
/** Minimum width in pixels for each series label in compact mode */
|
|
30469
|
+
MIN_SERIES_WIDTH: 124,
|
|
30470
|
+
};
|
|
30414
30471
|
class Legend extends Component {
|
|
30415
30472
|
constructor(drawChart, renderTarget, legendWidth) {
|
|
30416
30473
|
super(renderTarget);
|
|
30417
30474
|
this.renderSplitBys = (aggKey, aggSelection, dataType, noSplitBys) => {
|
|
30418
|
-
|
|
30419
|
-
|
|
30420
|
-
var firstSplitByType = firstSplitBy ? firstSplitBy.visibleType : null;
|
|
30421
|
-
Object.keys(this.chartComponentData.displayState[aggKey].splitBys).reduce((isSame, curr) => {
|
|
30422
|
-
return (firstSplitByType == this.chartComponentData.displayState[aggKey].splitBys[curr].visibleType) && isSame;
|
|
30423
|
-
}, true);
|
|
30424
|
-
let showMoreSplitBys = () => {
|
|
30425
|
-
const oldShownSplitBys = this.chartComponentData.displayState[aggKey].shownSplitBys;
|
|
30426
|
-
this.chartComponentData.displayState[aggKey].shownSplitBys = Math.min(oldShownSplitBys + 20, splitByLabelData.length);
|
|
30427
|
-
if (oldShownSplitBys != this.chartComponentData.displayState[aggKey].shownSplitBys) {
|
|
30428
|
-
this.renderSplitBys(aggKey, aggSelection, dataType, noSplitBys);
|
|
30429
|
-
}
|
|
30430
|
-
};
|
|
30475
|
+
const splitByLabelData = Object.keys(this.chartComponentData.timeArrays[aggKey]);
|
|
30476
|
+
const showMoreSplitBys = () => this.handleShowMoreSplitBys(aggKey, splitByLabelData, aggSelection, dataType, noSplitBys);
|
|
30431
30477
|
let splitByContainer = aggSelection.selectAll(".tsi-splitByContainer").data([aggKey]);
|
|
30432
|
-
|
|
30478
|
+
const splitByContainerEntered = splitByContainer.enter().append("div")
|
|
30433
30479
|
.merge(splitByContainer)
|
|
30434
30480
|
.classed("tsi-splitByContainer", true);
|
|
30435
|
-
|
|
30481
|
+
const splitByLabels = splitByContainerEntered.selectAll('.tsi-splitByLabel')
|
|
30436
30482
|
.data(splitByLabelData.slice(0, this.chartComponentData.displayState[aggKey].shownSplitBys), function (d) {
|
|
30437
30483
|
return d;
|
|
30438
30484
|
});
|
|
30439
|
-
|
|
30440
|
-
|
|
30485
|
+
const self = this;
|
|
30486
|
+
const splitByLabelsEntered = splitByLabels
|
|
30441
30487
|
.enter()
|
|
30442
30488
|
.append("div")
|
|
30443
30489
|
.merge(splitByLabels)
|
|
@@ -30451,135 +30497,60 @@
|
|
|
30451
30497
|
}
|
|
30452
30498
|
})
|
|
30453
30499
|
.on("click", function (event, splitBy) {
|
|
30454
|
-
|
|
30455
|
-
self.toggleSplitByVisible(aggKey, splitBy);
|
|
30456
|
-
}
|
|
30457
|
-
else {
|
|
30458
|
-
self.toggleSticky(aggKey, splitBy);
|
|
30459
|
-
}
|
|
30460
|
-
self.drawChart();
|
|
30500
|
+
self.handleSplitByClick(aggKey, splitBy);
|
|
30461
30501
|
})
|
|
30462
30502
|
.on("mouseover", function (event, splitBy) {
|
|
30463
30503
|
event.stopPropagation();
|
|
30464
|
-
self.
|
|
30504
|
+
self.handleSplitByMouseOver(aggKey, splitBy);
|
|
30465
30505
|
})
|
|
30466
30506
|
.on("mouseout", function (event) {
|
|
30467
30507
|
event.stopPropagation();
|
|
30468
|
-
self.
|
|
30469
|
-
.attr("stroke-opacity", 1)
|
|
30470
|
-
.attr("fill-opacity", 1);
|
|
30471
|
-
self.labelMouseout(self.svgSelection, aggKey);
|
|
30508
|
+
self.handleSplitByMouseOut(aggKey);
|
|
30472
30509
|
})
|
|
30473
30510
|
.attr("class", (splitBy, i) => {
|
|
30474
|
-
|
|
30475
|
-
|
|
30476
|
-
return `tsi-splitByLabel
|
|
30511
|
+
const compact = (dataType !== DataTypes.Numeric) ? 'tsi-splitByLabelCompact' : '';
|
|
30512
|
+
const shown = Utils.getAgVisible(self.chartComponentData.displayState, aggKey, splitBy) ? 'shown' : '';
|
|
30513
|
+
return `tsi-splitByLabel ${compact} ${shown}`;
|
|
30477
30514
|
})
|
|
30478
|
-
.classed("stickied", (splitBy, i) =>
|
|
30479
|
-
|
|
30480
|
-
return aggKey == self.chartComponentData.stickiedKey.aggregateKey && splitBy == self.chartComponentData.stickiedKey.splitBy;
|
|
30481
|
-
}
|
|
30482
|
-
});
|
|
30483
|
-
var colors = Utils.createSplitByColors(self.chartComponentData.displayState, aggKey, self.chartOptions.keepSplitByColor);
|
|
30515
|
+
.classed("stickied", (splitBy, i) => self.isStickied(aggKey, splitBy));
|
|
30516
|
+
// Use helper methods to render each split-by element
|
|
30484
30517
|
splitByLabelsEntered.each(function (splitBy, j) {
|
|
30485
|
-
|
|
30518
|
+
const selection = select(this);
|
|
30519
|
+
// Add color key (conditionally based on data type and legend state)
|
|
30486
30520
|
if (dataType === DataTypes.Numeric || noSplitBys || self.legendState === 'compact') {
|
|
30487
|
-
|
|
30488
|
-
|
|
30489
|
-
.append("div")
|
|
30490
|
-
.attr("class", 'tsi-colorKey')
|
|
30491
|
-
.merge(colorKey);
|
|
30492
|
-
if (dataType === DataTypes.Numeric) {
|
|
30493
|
-
colorKeyEntered.style('background-color', (d) => {
|
|
30494
|
-
return d;
|
|
30495
|
-
});
|
|
30496
|
-
}
|
|
30497
|
-
else {
|
|
30498
|
-
self.createNonNumericColorKey(dataType, colorKeyEntered, aggKey);
|
|
30499
|
-
}
|
|
30500
|
-
select(this).classed('tsi-nonCompactNonNumeric', (dataType === DataTypes.Categorical || dataType === DataTypes.Events) && this.legendState !== 'compact');
|
|
30501
|
-
colorKey.exit().remove();
|
|
30521
|
+
self.addColorKey(selection, aggKey, splitBy, dataType);
|
|
30522
|
+
selection.classed('tsi-nonCompactNonNumeric', (dataType === DataTypes.Categorical || dataType === DataTypes.Events) && self.legendState !== 'compact');
|
|
30502
30523
|
}
|
|
30503
30524
|
else {
|
|
30504
|
-
|
|
30505
|
-
}
|
|
30506
|
-
if (select(this).select('.tsi-eyeIcon').empty()) {
|
|
30507
|
-
select(this).append("button")
|
|
30508
|
-
.attr("class", "tsi-eyeIcon")
|
|
30509
|
-
.attr('aria-label', () => {
|
|
30510
|
-
let showOrHide = self.chartComponentData.displayState[aggKey].splitBys[splitBy].visible ? self.getString('hide series') : self.getString('show series');
|
|
30511
|
-
return `${showOrHide} ${splitBy} ${self.getString('in group')} ${self.chartComponentData.displayState[aggKey].name}`;
|
|
30512
|
-
})
|
|
30513
|
-
.attr('title', () => self.getString('Show/Hide values'))
|
|
30514
|
-
.on("click", function (event) {
|
|
30515
|
-
event.stopPropagation();
|
|
30516
|
-
self.toggleSplitByVisible(aggKey, splitBy);
|
|
30517
|
-
select(this)
|
|
30518
|
-
.classed("shown", Utils.getAgVisible(self.chartComponentData.displayState, aggKey, splitBy));
|
|
30519
|
-
self.drawChart();
|
|
30520
|
-
});
|
|
30521
|
-
}
|
|
30522
|
-
if (select(this).select('.tsi-seriesName').empty()) {
|
|
30523
|
-
let seriesName = select(this)
|
|
30524
|
-
.append('div')
|
|
30525
|
-
.attr('class', 'tsi-seriesName');
|
|
30526
|
-
Utils.appendFormattedElementsFromString(seriesName, noSplitBys ? (self.chartComponentData.displayState[aggKey].name) : splitBy);
|
|
30525
|
+
selection.selectAll('.tsi-colorKey').remove();
|
|
30527
30526
|
}
|
|
30527
|
+
// Add eye icon
|
|
30528
|
+
self.addEyeIcon(selection, aggKey, splitBy);
|
|
30529
|
+
// Add series name
|
|
30530
|
+
self.addSeriesName(selection, aggKey, splitBy);
|
|
30531
|
+
// Add series type selection for numeric data
|
|
30528
30532
|
if (dataType === DataTypes.Numeric) {
|
|
30529
|
-
|
|
30530
|
-
select(this).append("select")
|
|
30531
|
-
.attr('aria-label', `${self.getString("Series type selection for")} ${splitBy} ${self.getString('in group')} ${self.chartComponentData.displayState[aggKey].name}`)
|
|
30532
|
-
.attr('class', 'tsi-seriesTypeSelection')
|
|
30533
|
-
.on("change", function (data) {
|
|
30534
|
-
var seriesType = select(this).property("value");
|
|
30535
|
-
self.chartComponentData.displayState[aggKey].splitBys[splitBy].visibleType = seriesType;
|
|
30536
|
-
self.drawChart();
|
|
30537
|
-
})
|
|
30538
|
-
.on("click", (event) => {
|
|
30539
|
-
event.stopPropagation();
|
|
30540
|
-
});
|
|
30541
|
-
}
|
|
30542
|
-
select(this).select('.tsi-seriesTypeSelection')
|
|
30543
|
-
.each(function (d) {
|
|
30544
|
-
var typeLabels = select(this).selectAll('option')
|
|
30545
|
-
.data(data => self.chartComponentData.displayState[aggKey].splitBys[splitBy].types.map((type) => {
|
|
30546
|
-
return {
|
|
30547
|
-
type: type,
|
|
30548
|
-
aggKey: aggKey,
|
|
30549
|
-
splitBy: splitBy,
|
|
30550
|
-
visibleMeasure: Utils.getAgVisibleMeasure(self.chartComponentData.displayState, aggKey, splitBy)
|
|
30551
|
-
};
|
|
30552
|
-
}));
|
|
30553
|
-
typeLabels
|
|
30554
|
-
.enter()
|
|
30555
|
-
.append("option")
|
|
30556
|
-
.attr("class", "seriesTypeLabel")
|
|
30557
|
-
.merge(typeLabels)
|
|
30558
|
-
.property("selected", (data) => {
|
|
30559
|
-
return ((data.type == Utils.getAgVisibleMeasure(self.chartComponentData.displayState, data.aggKey, data.splitBy)) ?
|
|
30560
|
-
" selected" : "");
|
|
30561
|
-
})
|
|
30562
|
-
.text((data) => data.type);
|
|
30563
|
-
typeLabels.exit().remove();
|
|
30564
|
-
});
|
|
30533
|
+
self.addSeriesTypeSelection(selection, aggKey, splitBy);
|
|
30565
30534
|
}
|
|
30566
30535
|
else {
|
|
30567
|
-
|
|
30536
|
+
selection.selectAll('.tsi-seriesTypeSelection').remove();
|
|
30568
30537
|
}
|
|
30569
30538
|
});
|
|
30570
30539
|
splitByLabels.exit().remove();
|
|
30571
|
-
|
|
30540
|
+
// Show more button
|
|
30541
|
+
const shouldShowMore = self.chartComponentData.displayState[aggKey].shownSplitBys < splitByLabelData.length;
|
|
30572
30542
|
splitByContainerEntered.selectAll('.tsi-legendShowMore').remove();
|
|
30573
30543
|
if (this.legendState === 'shown' && shouldShowMore) {
|
|
30574
30544
|
splitByContainerEntered.append('button')
|
|
30575
30545
|
.text(this.getString('Show more'))
|
|
30576
30546
|
.attr('class', 'tsi-legendShowMore')
|
|
30577
|
-
.style('display',
|
|
30547
|
+
.style('display', 'block')
|
|
30578
30548
|
.on('click', showMoreSplitBys);
|
|
30579
30549
|
}
|
|
30550
|
+
// Scroll handler for infinite scrolling
|
|
30580
30551
|
splitByContainerEntered.on("scroll", function () {
|
|
30581
30552
|
if (self.chartOptions.legend === 'shown') {
|
|
30582
|
-
if (this.scrollTop + this.clientHeight +
|
|
30553
|
+
if (this.scrollTop + this.clientHeight + LEGEND_CONSTANTS.SCROLL_BUFFER > this.scrollHeight) {
|
|
30583
30554
|
showMoreSplitBys();
|
|
30584
30555
|
}
|
|
30585
30556
|
}
|
|
@@ -30604,10 +30575,125 @@
|
|
|
30604
30575
|
};
|
|
30605
30576
|
this.drawChart = drawChart;
|
|
30606
30577
|
this.legendWidth = legendWidth;
|
|
30607
|
-
this.legendElement = select(renderTarget)
|
|
30578
|
+
this.legendElement = select(renderTarget)
|
|
30579
|
+
.insert("div", ":first-child")
|
|
30608
30580
|
.attr("class", "tsi-legend")
|
|
30609
|
-
.style("left", "0px")
|
|
30610
|
-
|
|
30581
|
+
.style("left", "0px");
|
|
30582
|
+
// Note: width is set conditionally in draw() based on legendState
|
|
30583
|
+
// to allow CSS to control width in compact mode
|
|
30584
|
+
}
|
|
30585
|
+
getHeightPerSplitBy(aggKey) {
|
|
30586
|
+
const dataType = this.chartComponentData.displayState[aggKey].dataType;
|
|
30587
|
+
return dataType === DataTypes.Numeric
|
|
30588
|
+
? LEGEND_CONSTANTS.NUMERIC_SPLITBY_HEIGHT
|
|
30589
|
+
: LEGEND_CONSTANTS.NON_NUMERIC_SPLITBY_HEIGHT;
|
|
30590
|
+
}
|
|
30591
|
+
addColorKey(selection, aggKey, splitBy, dataType) {
|
|
30592
|
+
const colors = Utils.createSplitByColors(this.chartComponentData.displayState, aggKey, this.chartOptions.keepSplitByColor);
|
|
30593
|
+
const splitByKeys = Object.keys(this.chartComponentData.timeArrays[aggKey]);
|
|
30594
|
+
const splitByIndex = splitByKeys.indexOf(splitBy);
|
|
30595
|
+
const color = this.chartComponentData.isFromHeatmap
|
|
30596
|
+
? this.chartComponentData.displayState[aggKey].color
|
|
30597
|
+
: colors[splitByIndex];
|
|
30598
|
+
const colorKey = selection.selectAll('.tsi-colorKey').data([color]);
|
|
30599
|
+
const colorKeyEntered = colorKey.enter()
|
|
30600
|
+
.append('div')
|
|
30601
|
+
.attr('class', 'tsi-colorKey')
|
|
30602
|
+
.merge(colorKey);
|
|
30603
|
+
if (dataType === DataTypes.Numeric) {
|
|
30604
|
+
colorKeyEntered.style('background-color', d => d);
|
|
30605
|
+
}
|
|
30606
|
+
else {
|
|
30607
|
+
this.createNonNumericColorKey(dataType, colorKeyEntered, aggKey);
|
|
30608
|
+
}
|
|
30609
|
+
colorKey.exit().remove();
|
|
30610
|
+
}
|
|
30611
|
+
addEyeIcon(selection, aggKey, splitBy) {
|
|
30612
|
+
if (selection.select('.tsi-eyeIcon').empty()) {
|
|
30613
|
+
selection.append('button')
|
|
30614
|
+
.attr('class', 'tsi-eyeIcon')
|
|
30615
|
+
.attr('aria-label', () => {
|
|
30616
|
+
const showOrHide = this.chartComponentData.displayState[aggKey].splitBys[splitBy].visible
|
|
30617
|
+
? this.getString('hide series')
|
|
30618
|
+
: this.getString('show series');
|
|
30619
|
+
return `${showOrHide} ${splitBy} ${this.getString('in group')} ${this.chartComponentData.displayState[aggKey].name}`;
|
|
30620
|
+
})
|
|
30621
|
+
.attr('title', () => this.getString('Show/Hide values'))
|
|
30622
|
+
.on('click', (event) => {
|
|
30623
|
+
event.stopPropagation();
|
|
30624
|
+
this.toggleSplitByVisible(aggKey, splitBy);
|
|
30625
|
+
this.drawChart();
|
|
30626
|
+
});
|
|
30627
|
+
}
|
|
30628
|
+
selection.select('.tsi-eyeIcon')
|
|
30629
|
+
.classed('shown', Utils.getAgVisible(this.chartComponentData.displayState, aggKey, splitBy));
|
|
30630
|
+
}
|
|
30631
|
+
addSeriesName(selection, aggKey, splitBy) {
|
|
30632
|
+
if (selection.select('.tsi-seriesName').empty()) {
|
|
30633
|
+
const seriesName = selection.append('div')
|
|
30634
|
+
.attr('class', 'tsi-seriesName');
|
|
30635
|
+
const noSplitBys = Object.keys(this.chartComponentData.timeArrays[aggKey]).length === 1
|
|
30636
|
+
&& Object.keys(this.chartComponentData.timeArrays[aggKey])[0] === '';
|
|
30637
|
+
const displayText = noSplitBys
|
|
30638
|
+
? this.chartComponentData.displayState[aggKey].name
|
|
30639
|
+
: splitBy;
|
|
30640
|
+
Utils.appendFormattedElementsFromString(seriesName, displayText);
|
|
30641
|
+
}
|
|
30642
|
+
}
|
|
30643
|
+
addSeriesTypeSelection(selection, aggKey, splitBy) {
|
|
30644
|
+
if (selection.select('.tsi-seriesTypeSelection').empty()) {
|
|
30645
|
+
selection.append('select')
|
|
30646
|
+
.attr('aria-label', `${this.getString('Series type selection for')} ${splitBy} ${this.getString('in group')} ${this.chartComponentData.displayState[aggKey].name}`)
|
|
30647
|
+
.attr('class', 'tsi-seriesTypeSelection')
|
|
30648
|
+
.on('change', (event) => {
|
|
30649
|
+
const seriesType = select(event.target).property('value');
|
|
30650
|
+
this.chartComponentData.displayState[aggKey].splitBys[splitBy].visibleType = seriesType;
|
|
30651
|
+
this.drawChart();
|
|
30652
|
+
})
|
|
30653
|
+
.on('click', (event) => {
|
|
30654
|
+
event.stopPropagation();
|
|
30655
|
+
});
|
|
30656
|
+
}
|
|
30657
|
+
selection.select('.tsi-seriesTypeSelection')
|
|
30658
|
+
.each((d, i, nodes) => {
|
|
30659
|
+
const typeLabels = select(nodes[i])
|
|
30660
|
+
.selectAll('option')
|
|
30661
|
+
.data(this.chartComponentData.displayState[aggKey].splitBys[splitBy].types.map(type => ({
|
|
30662
|
+
type,
|
|
30663
|
+
aggKey,
|
|
30664
|
+
splitBy,
|
|
30665
|
+
visibleMeasure: Utils.getAgVisibleMeasure(this.chartComponentData.displayState, aggKey, splitBy)
|
|
30666
|
+
})));
|
|
30667
|
+
typeLabels.enter()
|
|
30668
|
+
.append('option')
|
|
30669
|
+
.attr('class', 'seriesTypeLabel')
|
|
30670
|
+
.merge(typeLabels)
|
|
30671
|
+
.property('selected', (data) => data.type === Utils.getAgVisibleMeasure(this.chartComponentData.displayState, data.aggKey, data.splitBy))
|
|
30672
|
+
.text((data) => data.type);
|
|
30673
|
+
typeLabels.exit().remove();
|
|
30674
|
+
});
|
|
30675
|
+
}
|
|
30676
|
+
handleSplitByClick(aggKey, splitBy) {
|
|
30677
|
+
if (this.legendState === 'compact') {
|
|
30678
|
+
this.toggleSplitByVisible(aggKey, splitBy);
|
|
30679
|
+
}
|
|
30680
|
+
else {
|
|
30681
|
+
this.toggleSticky(aggKey, splitBy);
|
|
30682
|
+
}
|
|
30683
|
+
this.drawChart();
|
|
30684
|
+
}
|
|
30685
|
+
handleSplitByMouseOver(aggKey, splitBy) {
|
|
30686
|
+
this.labelMouseover(aggKey, splitBy);
|
|
30687
|
+
}
|
|
30688
|
+
handleSplitByMouseOut(aggKey) {
|
|
30689
|
+
this.svgSelection.selectAll(".tsi-valueElement")
|
|
30690
|
+
.attr("stroke-opacity", 1)
|
|
30691
|
+
.attr("fill-opacity", 1);
|
|
30692
|
+
this.labelMouseout(this.svgSelection, aggKey);
|
|
30693
|
+
}
|
|
30694
|
+
isStickied(aggKey, splitBy) {
|
|
30695
|
+
const stickied = this.chartComponentData.stickiedKey;
|
|
30696
|
+
return stickied?.aggregateKey === aggKey && stickied?.splitBy === splitBy;
|
|
30611
30697
|
}
|
|
30612
30698
|
labelMouseoutWrapper(labelMouseout, svgSelection, event) {
|
|
30613
30699
|
return (svgSelection, aggKey) => {
|
|
@@ -30649,14 +30735,11 @@
|
|
|
30649
30735
|
return d == aggKey;
|
|
30650
30736
|
}).node();
|
|
30651
30737
|
var prospectiveScrollTop = Math.max((indexOfSplitBy - 1) * this.getHeightPerSplitBy(aggKey), 0);
|
|
30652
|
-
if (splitByNode.scrollTop < prospectiveScrollTop - (splitByNode.clientHeight -
|
|
30738
|
+
if (splitByNode.scrollTop < prospectiveScrollTop - (splitByNode.clientHeight - LEGEND_CONSTANTS.SCROLL_BUFFER) || splitByNode.scrollTop > prospectiveScrollTop) {
|
|
30653
30739
|
splitByNode.scrollTop = prospectiveScrollTop;
|
|
30654
30740
|
}
|
|
30655
30741
|
}
|
|
30656
30742
|
}
|
|
30657
|
-
getHeightPerSplitBy(aggKey) {
|
|
30658
|
-
return (this.chartComponentData.displayState[aggKey].dataType === DataTypes.Numeric ? NUMERICSPLITBYHEIGHT : NONNUMERICSPLITBYHEIGHT);
|
|
30659
|
-
}
|
|
30660
30743
|
createGradient(gradientKey, svg, values) {
|
|
30661
30744
|
let gradient = svg.append('defs').append('linearGradient')
|
|
30662
30745
|
.attr('id', gradientKey).attr('x1', '0%').attr('x2', '0%').attr('y1', '0%').attr('y2', '100%');
|
|
@@ -30675,10 +30758,6 @@
|
|
|
30675
30758
|
.attr("stop-opacity", 1);
|
|
30676
30759
|
});
|
|
30677
30760
|
}
|
|
30678
|
-
isNonNumeric(aggKey) {
|
|
30679
|
-
let dataType = this.chartComponentData.displayState[aggKey].dataType;
|
|
30680
|
-
return (dataType === DataTypes.Categorical || dataType === DataTypes.Events);
|
|
30681
|
-
}
|
|
30682
30761
|
createNonNumericColorKey(dataType, colorKey, aggKey) {
|
|
30683
30762
|
if (dataType === DataTypes.Categorical) {
|
|
30684
30763
|
this.createCategoricalColorKey(colorKey, aggKey);
|
|
@@ -30734,6 +30813,13 @@
|
|
|
30734
30813
|
rect.attr('fill', "url(#" + gradientKey + ")");
|
|
30735
30814
|
}
|
|
30736
30815
|
}
|
|
30816
|
+
handleShowMoreSplitBys(aggKey, splitByLabelData, aggSelection, dataType, noSplitBys) {
|
|
30817
|
+
const oldShownSplitBys = this.chartComponentData.displayState[aggKey].shownSplitBys;
|
|
30818
|
+
this.chartComponentData.displayState[aggKey].shownSplitBys = Math.min(oldShownSplitBys + LEGEND_CONSTANTS.BATCH_SIZE, splitByLabelData.length);
|
|
30819
|
+
if (oldShownSplitBys !== this.chartComponentData.displayState[aggKey].shownSplitBys) {
|
|
30820
|
+
this.renderSplitBys(aggKey, aggSelection, dataType, noSplitBys);
|
|
30821
|
+
}
|
|
30822
|
+
}
|
|
30737
30823
|
draw(legendState, chartComponentData, labelMouseover, svgSelection, options, labelMouseoutAction = null, stickySeriesAction = null, event) {
|
|
30738
30824
|
this.chartOptions.setOptions(options);
|
|
30739
30825
|
this.chartComponentData = chartComponentData;
|
|
@@ -30748,6 +30834,13 @@
|
|
|
30748
30834
|
legend.style('visibility', this.legendState != 'hidden')
|
|
30749
30835
|
.classed('compact', this.legendState == 'compact')
|
|
30750
30836
|
.classed('hidden', this.legendState == 'hidden');
|
|
30837
|
+
// Set width conditionally - let CSS handle compact mode width
|
|
30838
|
+
if (this.legendState !== 'compact') {
|
|
30839
|
+
legend.style('width', `${this.legendWidth}px`);
|
|
30840
|
+
}
|
|
30841
|
+
else {
|
|
30842
|
+
legend.style('width', null); // Remove inline width style in compact mode
|
|
30843
|
+
}
|
|
30751
30844
|
let seriesNames = Object.keys(this.chartComponentData.displayState);
|
|
30752
30845
|
var seriesLabels = legend.selectAll(".tsi-seriesLabel")
|
|
30753
30846
|
.data(seriesNames, d => d);
|
|
@@ -30758,7 +30851,7 @@
|
|
|
30758
30851
|
return "tsi-seriesLabel " + (this.chartComponentData.displayState[d]["visible"] ? " shown" : "");
|
|
30759
30852
|
})
|
|
30760
30853
|
.style("min-width", () => {
|
|
30761
|
-
return Math.min(
|
|
30854
|
+
return Math.min(LEGEND_CONSTANTS.MIN_SERIES_WIDTH, this.legendElement.node().clientWidth / seriesNames.length) + 'px';
|
|
30762
30855
|
})
|
|
30763
30856
|
.style("border-color", function (d, i) {
|
|
30764
30857
|
if (select(this).classed("shown"))
|
|
@@ -30766,9 +30859,8 @@
|
|
|
30766
30859
|
return "lightgray";
|
|
30767
30860
|
});
|
|
30768
30861
|
var self = this;
|
|
30769
|
-
const heightPerNameLabel = 25;
|
|
30770
30862
|
const usableLegendHeight = legend.node().clientHeight;
|
|
30771
|
-
var prospectiveAggregateHeight = Math.ceil(Math.max(
|
|
30863
|
+
var prospectiveAggregateHeight = Math.ceil(Math.max(LEGEND_CONSTANTS.MIN_AGGREGATE_HEIGHT, (usableLegendHeight / seriesLabelsEntered.size())));
|
|
30772
30864
|
var contentHeight = 0;
|
|
30773
30865
|
seriesLabelsEntered.each(function (aggKey, i) {
|
|
30774
30866
|
let heightPerSplitBy = self.getHeightPerSplitBy(aggKey);
|
|
@@ -30824,12 +30916,12 @@
|
|
|
30824
30916
|
seriesNameLabel.exit().remove();
|
|
30825
30917
|
var splitByContainerHeight;
|
|
30826
30918
|
if (splitByLabelData.length > (prospectiveAggregateHeight / heightPerSplitBy)) {
|
|
30827
|
-
splitByContainerHeight = prospectiveAggregateHeight -
|
|
30828
|
-
contentHeight += splitByContainerHeight +
|
|
30919
|
+
splitByContainerHeight = prospectiveAggregateHeight - LEGEND_CONSTANTS.NAME_LABEL_HEIGHT;
|
|
30920
|
+
contentHeight += splitByContainerHeight + LEGEND_CONSTANTS.NAME_LABEL_HEIGHT;
|
|
30829
30921
|
}
|
|
30830
30922
|
else if (splitByLabelData.length > 1 || (splitByLabelData.length === 1 && splitByLabelData[0] !== "")) {
|
|
30831
|
-
splitByContainerHeight = splitByLabelData.length * heightPerSplitBy +
|
|
30832
|
-
contentHeight += splitByContainerHeight +
|
|
30923
|
+
splitByContainerHeight = splitByLabelData.length * heightPerSplitBy + LEGEND_CONSTANTS.NAME_LABEL_HEIGHT;
|
|
30924
|
+
contentHeight += splitByContainerHeight + LEGEND_CONSTANTS.NAME_LABEL_HEIGHT;
|
|
30833
30925
|
}
|
|
30834
30926
|
else {
|
|
30835
30927
|
splitByContainerHeight = heightPerSplitBy;
|
|
@@ -30842,43 +30934,28 @@
|
|
|
30842
30934
|
select(this).style("height", "unset");
|
|
30843
30935
|
}
|
|
30844
30936
|
var splitByContainer = select(this).selectAll(".tsi-splitByContainer").data([aggKey]);
|
|
30845
|
-
|
|
30937
|
+
splitByContainer.enter().append("div")
|
|
30846
30938
|
.merge(splitByContainer)
|
|
30847
30939
|
.classed("tsi-splitByContainer", true);
|
|
30848
30940
|
let aggSelection = select(this);
|
|
30849
30941
|
self.renderSplitBys(aggKey, aggSelection, dataType, noSplitBys);
|
|
30850
|
-
|
|
30851
|
-
if (self.chartOptions.legend == "shown") {
|
|
30852
|
-
if (this.scrollTop + this.clientHeight + 40 > this.scrollHeight) {
|
|
30853
|
-
const oldShownSplitBys = self.chartComponentData.displayState[aggKey].shownSplitBys;
|
|
30854
|
-
self.chartComponentData.displayState[aggKey].shownSplitBys = Math.min(oldShownSplitBys + 20, splitByLabelData.length);
|
|
30855
|
-
if (oldShownSplitBys != self.chartComponentData.displayState[aggKey].shownSplitBys) {
|
|
30856
|
-
self.renderSplitBys(aggKey, aggSelection, dataType, noSplitBys);
|
|
30857
|
-
}
|
|
30858
|
-
}
|
|
30859
|
-
}
|
|
30860
|
-
});
|
|
30942
|
+
// Compact mode horizontal scroll handler
|
|
30861
30943
|
select(this).on('scroll', function () {
|
|
30862
30944
|
if (self.chartOptions.legend == "compact") {
|
|
30863
|
-
if (this.scrollLeft + this.clientWidth +
|
|
30864
|
-
|
|
30865
|
-
self.chartComponentData.displayState[aggKey].shownSplitBys = Math.min(oldShownSplitBys + 20, splitByLabelData.length);
|
|
30866
|
-
if (oldShownSplitBys != self.chartComponentData.displayState[aggKey].shownSplitBys) {
|
|
30867
|
-
this.renderSplitBys(dataType);
|
|
30868
|
-
}
|
|
30945
|
+
if (this.scrollLeft + this.clientWidth + LEGEND_CONSTANTS.SCROLL_BUFFER > this.scrollWidth) {
|
|
30946
|
+
self.handleShowMoreSplitBys(aggKey, splitByLabelData, aggSelection, dataType, noSplitBys);
|
|
30869
30947
|
}
|
|
30870
30948
|
}
|
|
30871
30949
|
});
|
|
30872
30950
|
splitByContainer.exit().remove();
|
|
30873
30951
|
});
|
|
30874
30952
|
if (this.chartOptions.legend == 'shown') {
|
|
30875
|
-
legend.node().clientHeight;
|
|
30876
30953
|
//minSplitBysForFlexGrow: the minimum number of split bys for flex-grow to be triggered
|
|
30877
30954
|
if (contentHeight < usableLegendHeight) {
|
|
30878
30955
|
this.legendElement.classed("tsi-flexLegend", true);
|
|
30879
30956
|
seriesLabelsEntered.each(function (d) {
|
|
30880
30957
|
let heightPerSplitBy = self.getHeightPerSplitBy(d);
|
|
30881
|
-
var minSplitByForFlexGrow = (prospectiveAggregateHeight -
|
|
30958
|
+
var minSplitByForFlexGrow = (prospectiveAggregateHeight - LEGEND_CONSTANTS.NAME_LABEL_HEIGHT) / heightPerSplitBy;
|
|
30882
30959
|
var splitBysCount = Object.keys(self.chartComponentData.displayState[String(select(this).data()[0])].splitBys).length;
|
|
30883
30960
|
if (splitBysCount > minSplitByForFlexGrow) {
|
|
30884
30961
|
select(this).style("flex-grow", 1);
|
|
@@ -30891,6 +30968,12 @@
|
|
|
30891
30968
|
}
|
|
30892
30969
|
seriesLabels.exit().remove();
|
|
30893
30970
|
}
|
|
30971
|
+
destroy() {
|
|
30972
|
+
this.legendElement.remove();
|
|
30973
|
+
// Note: Virtual list cleanup will be added when virtual scrolling is implemented
|
|
30974
|
+
// this.virtualLists.forEach(list => list.destroy());
|
|
30975
|
+
// this.virtualLists.clear();
|
|
30976
|
+
}
|
|
30894
30977
|
}
|
|
30895
30978
|
|
|
30896
30979
|
class ChartComponentData {
|
|
@@ -33114,6 +33197,8 @@
|
|
|
33114
33197
|
this.drawChart = drawChart;
|
|
33115
33198
|
this.contextMenuElement = select(renderTarget).insert("div", ":first-child")
|
|
33116
33199
|
.attr("class", "tsi-contextMenu")
|
|
33200
|
+
.attr("aria-label", "Context Menu")
|
|
33201
|
+
.attr("role", "menu")
|
|
33117
33202
|
.style("left", "0px")
|
|
33118
33203
|
.style("top", "0px");
|
|
33119
33204
|
}
|
|
@@ -33202,6 +33287,7 @@
|
|
|
33202
33287
|
var actionElementsEntered = actionElements.enter()
|
|
33203
33288
|
.append("div")
|
|
33204
33289
|
.attr("class", `tsi-actionElement`)
|
|
33290
|
+
.attr("role", "menuitem")
|
|
33205
33291
|
.classed('tsi-hasSubMenu', d => d.isNested)
|
|
33206
33292
|
.merge(actionElements)
|
|
33207
33293
|
.text(d => d.name)
|
|
@@ -33328,6 +33414,7 @@
|
|
|
33328
33414
|
}).data([theme]);
|
|
33329
33415
|
this.tooltipDiv = tooltip.enter().append('div')
|
|
33330
33416
|
.attr('class', 'tsi-tooltip')
|
|
33417
|
+
.attr('role', 'tooltip')
|
|
33331
33418
|
.merge(tooltip)
|
|
33332
33419
|
.each(function (d) {
|
|
33333
33420
|
select(this).selectAll("*").remove();
|
|
@@ -36702,6 +36789,10 @@
|
|
|
36702
36789
|
label.enter()
|
|
36703
36790
|
.append("text")
|
|
36704
36791
|
.attr("class", (d) => `tsi-swimLaneLabel-${lane} tsi-swimLaneLabel ${onClickPresentAndValid(d) ? 'tsi-boldOnHover' : ''}`)
|
|
36792
|
+
.attr("role", "heading")
|
|
36793
|
+
.attr("aria-roledescription", this.getString("Swimlane label"))
|
|
36794
|
+
.attr("aria-label", d => d.label)
|
|
36795
|
+
.attr("aria-level", "3")
|
|
36705
36796
|
.merge(label)
|
|
36706
36797
|
.style("text-anchor", "middle")
|
|
36707
36798
|
.attr("transform", d => `translate(${(-this.horizontalLabelOffset + swimlaneLabelConstants.labelLeftPadding)},${(d.offset + d.height / 2)}) rotate(-90)`)
|
|
@@ -36726,13 +36817,12 @@
|
|
|
36726
36817
|
});
|
|
36727
36818
|
}
|
|
36728
36819
|
render(data, options, aggregateExpressionOptions) {
|
|
36729
|
-
console.log('LineChart render called a');
|
|
36730
36820
|
super.render(data, options, aggregateExpressionOptions);
|
|
36731
36821
|
this.originalSwimLanes = this.aggregateExpressionOptions.map((aEO) => {
|
|
36732
36822
|
return aEO.swimLane;
|
|
36733
36823
|
});
|
|
36734
36824
|
this.originalSwimLaneOptions = options.swimLaneOptions;
|
|
36735
|
-
this.hasBrush = options && (options.brushMoveAction || options.brushMoveEndAction || options.brushContextMenuActions);
|
|
36825
|
+
this.hasBrush = !!(options && (options.brushMoveAction || options.brushMoveEndAction || options.brushContextMenuActions));
|
|
36736
36826
|
this.chartOptions.setOptions(options);
|
|
36737
36827
|
this.chartMargins.right = this.chartOptions.labelSeriesWithMarker ? (SERIESLABELWIDTH + 8) : LINECHARTCHARTMARGINS.right;
|
|
36738
36828
|
this.width = this.getWidth();
|
|
@@ -36781,6 +36871,7 @@
|
|
|
36781
36871
|
.attr("type", "button")
|
|
36782
36872
|
.on("click", function () {
|
|
36783
36873
|
self.overwriteSwimLanes();
|
|
36874
|
+
// cast to any to avoid TS incompatibility when spreading chartOptions instance into ILineChartOptions
|
|
36784
36875
|
self.render(self.data, { ...self.chartOptions, yAxisState: self.nextStackedState() }, self.aggregateExpressionOptions);
|
|
36785
36876
|
select(this).attr("aria-label", () => self.getString("set axis state to") + ' ' + self.nextStackedState());
|
|
36786
36877
|
setTimeout(() => select(this).node().focus(), 200);
|
|
@@ -36807,6 +36898,7 @@
|
|
|
36807
36898
|
this.svgSelection = this.targetElement.append("svg")
|
|
36808
36899
|
.attr("class", "tsi-lineChartSVG tsi-chartSVG")
|
|
36809
36900
|
.attr('title', this.getString('Line chart'))
|
|
36901
|
+
.attr("role", "img")
|
|
36810
36902
|
.attr("height", this.height);
|
|
36811
36903
|
var g = this.svgSelection.append("g")
|
|
36812
36904
|
.classed("svgGroup", true)
|
|
@@ -37192,1301 +37284,76 @@
|
|
|
37192
37284
|
}
|
|
37193
37285
|
}
|
|
37194
37286
|
|
|
37195
|
-
|
|
37196
|
-
|
|
37197
|
-
|
|
37198
|
-
|
|
37199
|
-
|
|
37200
|
-
|
|
37201
|
-
|
|
37202
|
-
|
|
37203
|
-
|
|
37204
|
-
|
|
37205
|
-
|
|
37206
|
-
|
|
37207
|
-
|
|
37208
|
-
|
|
37209
|
-
|
|
37210
|
-
|
|
37211
|
-
|
|
37212
|
-
|
|
37213
|
-
|
|
37214
|
-
|
|
37215
|
-
|
|
37216
|
-
|
|
37217
|
-
|
|
37218
|
-
|
|
37219
|
-
|
|
37220
|
-
|
|
37221
|
-
|
|
37222
|
-
|
|
37223
|
-
|
|
37224
|
-
|
|
37225
|
-
|
|
37226
|
-
|
|
37227
|
-
|
|
37228
|
-
|
|
37229
|
-
|
|
37230
|
-
|
|
37231
|
-
|
|
37232
|
-
|
|
37233
|
-
|
|
37234
|
-
|
|
37235
|
-
|
|
37236
|
-
|
|
37237
|
-
|
|
37238
|
-
|
|
37239
|
-
|
|
37240
|
-
}
|
|
37241
|
-
},
|
|
37242
|
-
|
|
37243
|
-
removeEvent = function(el, e, callback, capture)
|
|
37244
|
-
{
|
|
37245
|
-
if (hasEventListeners) {
|
|
37246
|
-
el.removeEventListener(e, callback, !!capture);
|
|
37247
|
-
} else {
|
|
37248
|
-
el.detachEvent('on' + e, callback);
|
|
37249
|
-
}
|
|
37250
|
-
},
|
|
37251
|
-
|
|
37252
|
-
trim = function(str)
|
|
37253
|
-
{
|
|
37254
|
-
return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g,'');
|
|
37255
|
-
},
|
|
37256
|
-
|
|
37257
|
-
hasClass = function(el, cn)
|
|
37258
|
-
{
|
|
37259
|
-
return (' ' + el.className + ' ').indexOf(' ' + cn + ' ') !== -1;
|
|
37260
|
-
},
|
|
37261
|
-
|
|
37262
|
-
addClass = function(el, cn)
|
|
37263
|
-
{
|
|
37264
|
-
if (!hasClass(el, cn)) {
|
|
37265
|
-
el.className = (el.className === '') ? cn : el.className + ' ' + cn;
|
|
37266
|
-
}
|
|
37267
|
-
},
|
|
37268
|
-
|
|
37269
|
-
removeClass = function(el, cn)
|
|
37270
|
-
{
|
|
37271
|
-
el.className = trim((' ' + el.className + ' ').replace(' ' + cn + ' ', ' '));
|
|
37272
|
-
},
|
|
37273
|
-
|
|
37274
|
-
isArray = function(obj)
|
|
37275
|
-
{
|
|
37276
|
-
return (/Array/).test(Object.prototype.toString.call(obj));
|
|
37277
|
-
},
|
|
37278
|
-
|
|
37279
|
-
isDate = function(obj)
|
|
37280
|
-
{
|
|
37281
|
-
return (/Date/).test(Object.prototype.toString.call(obj)) && !isNaN(obj.getTime());
|
|
37282
|
-
},
|
|
37283
|
-
|
|
37284
|
-
isWeekend = function(date)
|
|
37285
|
-
{
|
|
37286
|
-
var day = date.getDay();
|
|
37287
|
-
return day === 0 || day === 6;
|
|
37288
|
-
},
|
|
37289
|
-
|
|
37290
|
-
isLeapYear = function(year)
|
|
37291
|
-
{
|
|
37292
|
-
// solution by Matti Virkkunen: http://stackoverflow.com/a/4881951
|
|
37293
|
-
return year % 4 === 0 && year % 100 !== 0 || year % 400 === 0;
|
|
37294
|
-
},
|
|
37295
|
-
|
|
37296
|
-
getDaysInMonth = function(year, month)
|
|
37297
|
-
{
|
|
37298
|
-
return [31, isLeapYear(year) ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month];
|
|
37299
|
-
},
|
|
37300
|
-
|
|
37301
|
-
setToStartOfDay = function(date)
|
|
37302
|
-
{
|
|
37303
|
-
if (isDate(date)) date.setHours(0,0,0,0);
|
|
37304
|
-
},
|
|
37305
|
-
|
|
37306
|
-
compareDates = function(a,b)
|
|
37307
|
-
{
|
|
37308
|
-
// weak date comparison (use setToStartOfDay(date) to ensure correct result)
|
|
37309
|
-
return a.getTime() === b.getTime();
|
|
37310
|
-
},
|
|
37311
|
-
|
|
37312
|
-
extend = function(to, from, overwrite)
|
|
37313
|
-
{
|
|
37314
|
-
var prop, hasProp;
|
|
37315
|
-
for (prop in from) {
|
|
37316
|
-
hasProp = to[prop] !== undefined;
|
|
37317
|
-
if (hasProp && typeof from[prop] === 'object' && from[prop] !== null && from[prop].nodeName === undefined) {
|
|
37318
|
-
if (isDate(from[prop])) {
|
|
37319
|
-
if (overwrite) {
|
|
37320
|
-
to[prop] = new Date(from[prop].getTime());
|
|
37321
|
-
}
|
|
37322
|
-
}
|
|
37323
|
-
else if (isArray(from[prop])) {
|
|
37324
|
-
if (overwrite) {
|
|
37325
|
-
to[prop] = from[prop].slice(0);
|
|
37326
|
-
}
|
|
37327
|
-
} else {
|
|
37328
|
-
to[prop] = extend({}, from[prop], overwrite);
|
|
37329
|
-
}
|
|
37330
|
-
} else if (overwrite || !hasProp) {
|
|
37331
|
-
to[prop] = from[prop];
|
|
37332
|
-
}
|
|
37333
|
-
}
|
|
37334
|
-
return to;
|
|
37335
|
-
},
|
|
37336
|
-
|
|
37337
|
-
fireEvent = function(el, eventName, data)
|
|
37338
|
-
{
|
|
37339
|
-
var ev;
|
|
37340
|
-
|
|
37341
|
-
if (document.createEvent) {
|
|
37342
|
-
ev = document.createEvent('HTMLEvents');
|
|
37343
|
-
ev.initEvent(eventName, true, false);
|
|
37344
|
-
ev = extend(ev, data);
|
|
37345
|
-
el.dispatchEvent(ev);
|
|
37346
|
-
} else if (document.createEventObject) {
|
|
37347
|
-
ev = document.createEventObject();
|
|
37348
|
-
ev = extend(ev, data);
|
|
37349
|
-
el.fireEvent('on' + eventName, ev);
|
|
37350
|
-
}
|
|
37351
|
-
},
|
|
37352
|
-
|
|
37353
|
-
adjustCalendar = function(calendar) {
|
|
37354
|
-
if (calendar.month < 0) {
|
|
37355
|
-
calendar.year -= Math.ceil(Math.abs(calendar.month)/12);
|
|
37356
|
-
calendar.month += 12;
|
|
37357
|
-
}
|
|
37358
|
-
if (calendar.month > 11) {
|
|
37359
|
-
calendar.year += Math.floor(Math.abs(calendar.month)/12);
|
|
37360
|
-
calendar.month -= 12;
|
|
37361
|
-
}
|
|
37362
|
-
return calendar;
|
|
37363
|
-
},
|
|
37364
|
-
|
|
37365
|
-
/**
|
|
37366
|
-
* defaults and localisation
|
|
37367
|
-
*/
|
|
37368
|
-
defaults = {
|
|
37369
|
-
|
|
37370
|
-
// bind the picker to a form field
|
|
37371
|
-
field: null,
|
|
37372
|
-
|
|
37373
|
-
// automatically show/hide the picker on `field` focus (default `true` if `field` is set)
|
|
37374
|
-
bound: undefined,
|
|
37375
|
-
|
|
37376
|
-
// position of the datepicker, relative to the field (default to bottom & left)
|
|
37377
|
-
// ('bottom' & 'left' keywords are not used, 'top' & 'right' are modifier on the bottom/left position)
|
|
37378
|
-
position: 'bottom left',
|
|
37379
|
-
|
|
37380
|
-
// automatically fit in the viewport even if it means repositioning from the position option
|
|
37381
|
-
reposition: true,
|
|
37382
|
-
|
|
37383
|
-
// the default output format for `.toString()` and `field` value
|
|
37384
|
-
format: 'YYYY-MM-DD',
|
|
37385
|
-
|
|
37386
|
-
// the toString function which gets passed a current date object and format
|
|
37387
|
-
// and returns a string
|
|
37388
|
-
toString: null,
|
|
37389
|
-
|
|
37390
|
-
// used to create date object from current input string
|
|
37391
|
-
parse: null,
|
|
37392
|
-
|
|
37393
|
-
// the initial date to view when first opened
|
|
37394
|
-
defaultDate: null,
|
|
37395
|
-
|
|
37396
|
-
// make the `defaultDate` the initial selected value
|
|
37397
|
-
setDefaultDate: false,
|
|
37398
|
-
|
|
37399
|
-
// first day of week (0: Sunday, 1: Monday etc)
|
|
37400
|
-
firstDay: 0,
|
|
37401
|
-
|
|
37402
|
-
// the default flag for moment's strict date parsing
|
|
37403
|
-
formatStrict: false,
|
|
37404
|
-
|
|
37405
|
-
// the minimum/earliest date that can be selected
|
|
37406
|
-
minDate: null,
|
|
37407
|
-
// the maximum/latest date that can be selected
|
|
37408
|
-
maxDate: null,
|
|
37409
|
-
|
|
37410
|
-
// number of years either side, or array of upper/lower range
|
|
37411
|
-
yearRange: 10,
|
|
37412
|
-
|
|
37413
|
-
// show week numbers at head of row
|
|
37414
|
-
showWeekNumber: false,
|
|
37415
|
-
|
|
37416
|
-
// Week picker mode
|
|
37417
|
-
pickWholeWeek: false,
|
|
37418
|
-
|
|
37419
|
-
// used internally (don't config outside)
|
|
37420
|
-
minYear: 0,
|
|
37421
|
-
maxYear: 9999,
|
|
37422
|
-
minMonth: undefined,
|
|
37423
|
-
maxMonth: undefined,
|
|
37424
|
-
|
|
37425
|
-
startRange: null,
|
|
37426
|
-
endRange: null,
|
|
37427
|
-
|
|
37428
|
-
isRTL: false,
|
|
37429
|
-
|
|
37430
|
-
// Additional text to append to the year in the calendar title
|
|
37431
|
-
yearSuffix: '',
|
|
37432
|
-
|
|
37433
|
-
// Render the month after year in the calendar title
|
|
37434
|
-
showMonthAfterYear: false,
|
|
37435
|
-
|
|
37436
|
-
// Render days of the calendar grid that fall in the next or previous month
|
|
37437
|
-
showDaysInNextAndPreviousMonths: false,
|
|
37438
|
-
|
|
37439
|
-
// Allows user to select days that fall in the next or previous month
|
|
37440
|
-
enableSelectionDaysInNextAndPreviousMonths: false,
|
|
37441
|
-
|
|
37442
|
-
// how many months are visible
|
|
37443
|
-
numberOfMonths: 1,
|
|
37444
|
-
|
|
37445
|
-
// when numberOfMonths is used, this will help you to choose where the main calendar will be (default `left`, can be set to `right`)
|
|
37446
|
-
// only used for the first display or when a selected date is not visible
|
|
37447
|
-
mainCalendar: 'left',
|
|
37448
|
-
|
|
37449
|
-
// Specify a DOM element to render the calendar in
|
|
37450
|
-
container: undefined,
|
|
37451
|
-
|
|
37452
|
-
// Blur field when date is selected
|
|
37453
|
-
blurFieldOnSelect : true,
|
|
37454
|
-
|
|
37455
|
-
// internationalization
|
|
37456
|
-
i18n: {
|
|
37457
|
-
previousMonth : 'Previous Month',
|
|
37458
|
-
nextMonth : 'Next Month',
|
|
37459
|
-
months : ['January','February','March','April','May','June','July','August','September','October','November','December'],
|
|
37460
|
-
weekdays : ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'],
|
|
37461
|
-
weekdaysShort : ['Sun','Mon','Tue','Wed','Thu','Fri','Sat']
|
|
37462
|
-
},
|
|
37463
|
-
|
|
37464
|
-
// Theme Classname
|
|
37465
|
-
theme: null,
|
|
37466
|
-
|
|
37467
|
-
// events array
|
|
37468
|
-
events: [],
|
|
37469
|
-
|
|
37470
|
-
// callback function
|
|
37471
|
-
onSelect: null,
|
|
37472
|
-
onOpen: null,
|
|
37473
|
-
onClose: null,
|
|
37474
|
-
onDraw: null,
|
|
37475
|
-
|
|
37476
|
-
// Enable keyboard input
|
|
37477
|
-
keyboardInput: true
|
|
37478
|
-
},
|
|
37479
|
-
|
|
37480
|
-
|
|
37481
|
-
/**
|
|
37482
|
-
* templating functions to abstract HTML rendering
|
|
37483
|
-
*/
|
|
37484
|
-
renderDayName = function(opts, day, abbr)
|
|
37485
|
-
{
|
|
37486
|
-
day += opts.firstDay;
|
|
37487
|
-
while (day >= 7) {
|
|
37488
|
-
day -= 7;
|
|
37489
|
-
}
|
|
37490
|
-
return abbr ? opts.i18n.weekdaysShort[day] : opts.i18n.weekdays[day];
|
|
37491
|
-
},
|
|
37492
|
-
|
|
37493
|
-
renderDay = function(opts)
|
|
37494
|
-
{
|
|
37495
|
-
var arr = [];
|
|
37496
|
-
var ariaSelected = 'false';
|
|
37497
|
-
if (opts.isEmpty) {
|
|
37498
|
-
if (opts.showDaysInNextAndPreviousMonths) {
|
|
37499
|
-
arr.push('is-outside-current-month');
|
|
37500
|
-
|
|
37501
|
-
if(!opts.enableSelectionDaysInNextAndPreviousMonths) {
|
|
37502
|
-
arr.push('is-selection-disabled');
|
|
37503
|
-
}
|
|
37504
|
-
|
|
37505
|
-
} else {
|
|
37506
|
-
return '<td class="is-empty"></td>';
|
|
37507
|
-
}
|
|
37508
|
-
}
|
|
37509
|
-
if (opts.isDisabled) {
|
|
37510
|
-
arr.push('is-disabled');
|
|
37511
|
-
}
|
|
37512
|
-
if (opts.isToday) {
|
|
37513
|
-
arr.push('is-today');
|
|
37514
|
-
}
|
|
37515
|
-
if (opts.isSelected) {
|
|
37516
|
-
arr.push('is-selected');
|
|
37517
|
-
ariaSelected = 'true';
|
|
37518
|
-
}
|
|
37519
|
-
if (opts.hasEvent) {
|
|
37520
|
-
arr.push('has-event');
|
|
37521
|
-
}
|
|
37522
|
-
if (opts.isInRange) {
|
|
37523
|
-
arr.push('is-inrange');
|
|
37524
|
-
}
|
|
37525
|
-
if (opts.isStartRange) {
|
|
37526
|
-
arr.push('is-startrange');
|
|
37527
|
-
}
|
|
37528
|
-
if (opts.isEndRange) {
|
|
37529
|
-
arr.push('is-endrange');
|
|
37530
|
-
}
|
|
37531
|
-
return '<td data-day="' + opts.day + '" class="' + arr.join(' ') + '" aria-selected="' + ariaSelected + '">' +
|
|
37532
|
-
'<button tabIndex="-1" class="pika-button pika-day" type="button" ' +
|
|
37533
|
-
'data-pika-year="' + opts.year + '" data-pika-month="' + opts.month + '" data-pika-day="' + opts.day + '">' +
|
|
37534
|
-
opts.day +
|
|
37535
|
-
'</button>' +
|
|
37536
|
-
'</td>';
|
|
37537
|
-
},
|
|
37538
|
-
|
|
37539
|
-
renderWeek = function (d, m, y) {
|
|
37540
|
-
// Lifted from http://javascript.about.com/library/blweekyear.htm, lightly modified.
|
|
37541
|
-
var onejan = new Date(y, 0, 1),
|
|
37542
|
-
weekNum = Math.ceil((((new Date(y, m, d) - onejan) / 86400000) + onejan.getDay()+1)/7);
|
|
37543
|
-
return '<td class="pika-week">' + weekNum + '</td>';
|
|
37544
|
-
},
|
|
37545
|
-
|
|
37546
|
-
renderRow = function(days, isRTL, pickWholeWeek, isRowSelected)
|
|
37547
|
-
{
|
|
37548
|
-
return '<tr class="pika-row' + (pickWholeWeek ? ' pick-whole-week' : '') + (isRowSelected ? ' is-selected' : '') + '">' + (isRTL ? days.reverse() : days).join('') + '</tr>';
|
|
37549
|
-
},
|
|
37550
|
-
|
|
37551
|
-
renderBody = function(rows)
|
|
37552
|
-
{
|
|
37553
|
-
return '<tbody>' + rows.join('') + '</tbody>';
|
|
37554
|
-
},
|
|
37555
|
-
|
|
37556
|
-
renderHead = function(opts)
|
|
37557
|
-
{
|
|
37558
|
-
var i, arr = [];
|
|
37559
|
-
if (opts.showWeekNumber) {
|
|
37560
|
-
arr.push('<th></th>');
|
|
37561
|
-
}
|
|
37562
|
-
for (i = 0; i < 7; i++) {
|
|
37563
|
-
arr.push('<th scope="col"><abbr title="' + renderDayName(opts, i) + '">' + renderDayName(opts, i, true) + '</abbr></th>');
|
|
37564
|
-
}
|
|
37565
|
-
return '<thead><tr>' + (opts.isRTL ? arr.reverse() : arr).join('') + '</tr></thead>';
|
|
37566
|
-
},
|
|
37567
|
-
|
|
37568
|
-
renderTitle = function(instance, c, year, month, refYear, randId)
|
|
37569
|
-
{
|
|
37570
|
-
var i, j, arr,
|
|
37571
|
-
opts = instance._o,
|
|
37572
|
-
isMinYear = year === opts.minYear,
|
|
37573
|
-
isMaxYear = year === opts.maxYear,
|
|
37574
|
-
html = '<div id="' + randId + '" class="pika-title">',
|
|
37575
|
-
monthHtml,
|
|
37576
|
-
yearHtml,
|
|
37577
|
-
prev = true,
|
|
37578
|
-
next = true;
|
|
37579
|
-
|
|
37580
|
-
for (arr = [], i = 0; i < 12; i++) {
|
|
37581
|
-
arr.push('<option value="' + (year === refYear ? i - c : 12 + i - c) + '"' +
|
|
37582
|
-
(i === month ? ' selected="selected"': '') +
|
|
37583
|
-
((isMinYear && i < opts.minMonth) || (isMaxYear && i > opts.maxMonth) ? 'disabled="disabled"' : '') + '>' +
|
|
37584
|
-
opts.i18n.months[i] + '</option>');
|
|
37585
|
-
}
|
|
37586
|
-
|
|
37587
|
-
monthHtml = '<div class="pika-label">' + opts.i18n.months[month] + '<select aria-label="select month" class="pika-select pika-select-month" tabindex="-1">' + arr.join('') + '</select></div>';
|
|
37588
|
-
|
|
37589
|
-
if (isArray(opts.yearRange)) {
|
|
37590
|
-
i = opts.yearRange[0];
|
|
37591
|
-
j = opts.yearRange[1] + 1;
|
|
37592
|
-
} else {
|
|
37593
|
-
i = year - opts.yearRange;
|
|
37594
|
-
j = 1 + year + opts.yearRange;
|
|
37595
|
-
}
|
|
37596
|
-
|
|
37597
|
-
for (arr = []; i < j && i <= opts.maxYear; i++) {
|
|
37598
|
-
if (i >= opts.minYear) {
|
|
37599
|
-
arr.push('<option value="' + i + '"' + (i === year ? ' selected="selected"': '') + '>' + (i) + '</option>');
|
|
37600
|
-
}
|
|
37601
|
-
}
|
|
37602
|
-
yearHtml = '<div class="pika-label">' + year + opts.yearSuffix + '<select aria-label="select year" class="pika-select pika-select-year" tabindex="-1">' + arr.join('') + '</select></div>';
|
|
37603
|
-
|
|
37604
|
-
if (opts.showMonthAfterYear) {
|
|
37605
|
-
html += yearHtml + monthHtml;
|
|
37606
|
-
} else {
|
|
37607
|
-
html += monthHtml + yearHtml;
|
|
37608
|
-
}
|
|
37609
|
-
|
|
37610
|
-
if (isMinYear && (month === 0 || opts.minMonth >= month)) {
|
|
37611
|
-
prev = false;
|
|
37612
|
-
}
|
|
37613
|
-
|
|
37614
|
-
if (isMaxYear && (month === 11 || opts.maxMonth <= month)) {
|
|
37615
|
-
next = false;
|
|
37616
|
-
}
|
|
37617
|
-
|
|
37618
|
-
if (c === 0) {
|
|
37619
|
-
html += '<button tabIndex="-1" class="pika-prev' + (prev ? '' : ' is-disabled') + '" type="button">' + opts.i18n.previousMonth + '</button>';
|
|
37620
|
-
}
|
|
37621
|
-
if (c === (instance._o.numberOfMonths - 1) ) {
|
|
37622
|
-
html += '<button tabIndex="-1" class="pika-next' + (next ? '' : ' is-disabled') + '" type="button">' + opts.i18n.nextMonth + '</button>';
|
|
37623
|
-
}
|
|
37624
|
-
|
|
37625
|
-
return html += '</div>';
|
|
37626
|
-
},
|
|
37287
|
+
class TimezonePicker extends ChartComponent {
|
|
37288
|
+
constructor(renderTarget) {
|
|
37289
|
+
super(renderTarget);
|
|
37290
|
+
this.timeZones = ["Local", "UTC", "Africa/Algiers", "Africa/Cairo", "Africa/Casablanca", "Africa/Harare", "Africa/Johannesburg", "Africa/Lagos", "Africa/Nairobi", "Africa/Windhoek", "America/Anchorage", "America/Bogota", "America/Buenos Aires", "America/Caracas", "America/Chicago", "America/Chihuahua", "America/Denver", "America/Edmonton", "America/Godthab", "America/Guatemala", "America/Halifax", "America/Indiana/Indianapolis", "America/Los Angeles", "America/Manaus", "America/Mexico City", "America/Montevideo", "America/New York", "America/Phoenix", "America/Santiago", "America/Sao Paulo", "America/St Johns", "America/Tijuana", "America/Toronto", "America/Vancouver", "America/Winnipeg", "Asia/Amman", "Asia/Beirut", "Asia/Baghdad", "Asia/Baku", "Asia/Bangkok", "Asia/Calcutta", "Asia/Colombo", "Asia/Dhaka", "Asia/Dubai", "Asia/Ho Chi Minh", "Asia/Hong Kong", "Asia/Irkutsk", "Asia/Istanbul", "Asia/Jakarta", "Asia/Jerusalem", "Asia/Kabul", "Asia/Karachi", "Asia/Kathmandu", "Asia/Krasnoyarsk", "Asia/Kuala Lumpur", "Asia/Kuwait", "Asia/Magadan", "Asia/Muscat", "Asia/Novosibirsk", "Asia/Qatar", "Asia/Rangoon", "Asia/Seoul", "Asia/Shanghai", "Asia/Singapore", "Asia/Taipei", "Asia/Tbilisi", "Asia/Tehran", "Asia/Tokyo", "Asia/Vladivostok", "Asia/Yakutsk", "Asia/Yekaterinburg", "Asia/Yerevan", "Atlantic/Azores", "Atlantic/Cape Verde", "Atlantic/South Georgia", "Australia/Adelaide", "Australia/Brisbane", "Australia/Canberra", "Australia/Darwin", "Australia/Hobart", "Australia/Melbourne", "Australia/Perth", "Australia/Queensland", "Australia/Sydney", "Europe/Amsterdam", "Europe/Andorra", "Europe/Athens", "Europe/Belfast", "Europe/Belgrade", "Europe/Berlin", "Europe/Brussels", "Europe/Budapest", "Europe/Dublin", "Europe/Helsinki", "Europe/Kiev", "Europe/Lisbon", "Europe/London", "Europe/Luxembourg", "Europe/Madrid", "Europe/Minsk", "Europe/Monaco", "Europe/Moscow", "Europe/Oslo", "Europe/Paris", "Europe/Rome", "Europe/Stockholm", "Europe/Vienna", "Europe/Warsaw", "Europe/Zurich", "Pacific/Auckland", "Pacific/Fiji", "Pacific/Guam", "Pacific/Honolulu", "Pacific/Midway", "Pacific/Tongatapu"];
|
|
37291
|
+
}
|
|
37292
|
+
sortTimezones() {
|
|
37293
|
+
let filteredTimezones = this.timeZones.filter((tz) => {
|
|
37294
|
+
return !(tz.toLowerCase() == 'local' || tz == 'UTC');
|
|
37295
|
+
});
|
|
37296
|
+
filteredTimezones.sort((a, b) => {
|
|
37297
|
+
let aOffset = moment$1.tz(new Date(), a.split(' ').join('_')).utcOffset();
|
|
37298
|
+
let bOffset = moment$1.tz(new Date(), b.split(' ').join('_')).utcOffset();
|
|
37299
|
+
if (aOffset < bOffset) {
|
|
37300
|
+
return -1;
|
|
37301
|
+
}
|
|
37302
|
+
if (aOffset > bOffset) {
|
|
37303
|
+
return 1;
|
|
37304
|
+
}
|
|
37305
|
+
return 0;
|
|
37306
|
+
});
|
|
37307
|
+
this.timeZones = ['Local', 'UTC'].concat(filteredTimezones);
|
|
37308
|
+
}
|
|
37309
|
+
render(onTimezoneSelect, defaultTimeZone = null) {
|
|
37310
|
+
this.targetElement = select(this.renderTarget)
|
|
37311
|
+
.classed("tsi-timezonePicker", true);
|
|
37312
|
+
var timezoneSelection = this.targetElement.append("select")
|
|
37313
|
+
.attr("class", "tsi-timezonePicker tsi-select");
|
|
37314
|
+
this.sortTimezones();
|
|
37315
|
+
var options = timezoneSelection.selectAll("option")
|
|
37316
|
+
.data(this.timeZones)
|
|
37317
|
+
.enter()
|
|
37318
|
+
.append("option")
|
|
37319
|
+
.attr('value', d => d)
|
|
37320
|
+
.text((tz) => Utils.convertTimezoneToLabel(tz, this.getString('Local')));
|
|
37321
|
+
timezoneSelection.on("change", function (d) {
|
|
37322
|
+
var timezone = select(this).node().value.replace(/\s/g, "_");
|
|
37323
|
+
onTimezoneSelect(timezone);
|
|
37324
|
+
});
|
|
37325
|
+
defaultTimeZone = defaultTimeZone.replace(/_/g, " ");
|
|
37326
|
+
if (defaultTimeZone != null) {
|
|
37327
|
+
options.filter((d) => d == defaultTimeZone).attr("selected", true);
|
|
37328
|
+
}
|
|
37329
|
+
return;
|
|
37330
|
+
}
|
|
37331
|
+
}
|
|
37627
37332
|
|
|
37628
|
-
|
|
37629
|
-
|
|
37630
|
-
return '<table cellpadding="0" cellspacing="0" class="pika-table" role="grid" aria-labelledby="' + randId + '">' + renderHead(opts) + renderBody(data) + '</table>';
|
|
37631
|
-
},
|
|
37333
|
+
var momentExports = requireMoment();
|
|
37334
|
+
var moment = /*@__PURE__*/getDefaultExportFromCjs(momentExports);
|
|
37632
37335
|
|
|
37633
|
-
|
|
37634
|
-
|
|
37635
|
-
|
|
37636
|
-
|
|
37637
|
-
|
|
37638
|
-
|
|
37639
|
-
|
|
37640
|
-
|
|
37641
|
-
|
|
37642
|
-
|
|
37643
|
-
|
|
37644
|
-
|
|
37645
|
-
|
|
37646
|
-
|
|
37647
|
-
|
|
37648
|
-
|
|
37649
|
-
|
|
37650
|
-
|
|
37651
|
-
|
|
37652
|
-
|
|
37653
|
-
|
|
37654
|
-
if (hasClass(target, 'pika-button') && !hasClass(target, 'is-empty') && !hasClass(target.parentNode, 'is-disabled')) {
|
|
37655
|
-
self.setDate(new Date(target.getAttribute('data-pika-year'), target.getAttribute('data-pika-month'), target.getAttribute('data-pika-day')));
|
|
37656
|
-
if (opts.bound) {
|
|
37657
|
-
sto(function() {
|
|
37658
|
-
self.hide();
|
|
37659
|
-
if (opts.blurFieldOnSelect && opts.field) {
|
|
37660
|
-
opts.field.blur();
|
|
37661
|
-
}
|
|
37662
|
-
}, 100);
|
|
37663
|
-
}
|
|
37664
|
-
}
|
|
37665
|
-
else if (hasClass(target, 'pika-prev')) {
|
|
37666
|
-
self.prevMonth();
|
|
37667
|
-
}
|
|
37668
|
-
else if (hasClass(target, 'pika-next')) {
|
|
37669
|
-
self.nextMonth();
|
|
37670
|
-
}
|
|
37671
|
-
}
|
|
37672
|
-
if (!hasClass(target, 'pika-select')) {
|
|
37673
|
-
// if this is touch event prevent mouse events emulation
|
|
37674
|
-
if (e.preventDefault) {
|
|
37675
|
-
e.preventDefault();
|
|
37676
|
-
} else {
|
|
37677
|
-
e.returnValue = false;
|
|
37678
|
-
return false;
|
|
37679
|
-
}
|
|
37680
|
-
} else {
|
|
37681
|
-
self._c = true;
|
|
37682
|
-
}
|
|
37683
|
-
};
|
|
37684
|
-
|
|
37685
|
-
self._onChange = function(e)
|
|
37686
|
-
{
|
|
37687
|
-
e = e || window.event;
|
|
37688
|
-
var target = e.target || e.srcElement;
|
|
37689
|
-
if (!target) {
|
|
37690
|
-
return;
|
|
37691
|
-
}
|
|
37692
|
-
if (hasClass(target, 'pika-select-month')) {
|
|
37693
|
-
self.gotoMonth(target.value);
|
|
37694
|
-
}
|
|
37695
|
-
else if (hasClass(target, 'pika-select-year')) {
|
|
37696
|
-
self.gotoYear(target.value);
|
|
37697
|
-
}
|
|
37698
|
-
};
|
|
37699
|
-
|
|
37700
|
-
self._onKeyChange = function(e)
|
|
37701
|
-
{
|
|
37702
|
-
e = e || window.event;
|
|
37703
|
-
// ignore if event comes from input box
|
|
37704
|
-
if (self.isVisible() && e.target && e.target.type !== 'text') {
|
|
37705
|
-
|
|
37706
|
-
switch(e.keyCode){
|
|
37707
|
-
case 13:
|
|
37708
|
-
case 27:
|
|
37709
|
-
if (opts.field) {
|
|
37710
|
-
opts.field.blur();
|
|
37711
|
-
}
|
|
37712
|
-
break;
|
|
37713
|
-
case 37:
|
|
37714
|
-
e.preventDefault();
|
|
37715
|
-
self.adjustDate('subtract', 1);
|
|
37716
|
-
break;
|
|
37717
|
-
case 38:
|
|
37718
|
-
self.adjustDate('subtract', 7);
|
|
37719
|
-
break;
|
|
37720
|
-
case 39:
|
|
37721
|
-
self.adjustDate('add', 1);
|
|
37722
|
-
break;
|
|
37723
|
-
case 40:
|
|
37724
|
-
self.adjustDate('add', 7);
|
|
37725
|
-
break;
|
|
37726
|
-
}
|
|
37727
|
-
}
|
|
37728
|
-
};
|
|
37729
|
-
|
|
37730
|
-
self._onInputChange = function(e)
|
|
37731
|
-
{
|
|
37732
|
-
var date;
|
|
37733
|
-
|
|
37734
|
-
if (e.firedBy === self) {
|
|
37735
|
-
return;
|
|
37736
|
-
}
|
|
37737
|
-
if (opts.parse) {
|
|
37738
|
-
date = opts.parse(opts.field.value, opts.format);
|
|
37739
|
-
} else if (hasMoment) {
|
|
37740
|
-
date = moment(opts.field.value, opts.format, opts.formatStrict);
|
|
37741
|
-
date = (date && date.isValid()) ? date.toDate() : null;
|
|
37742
|
-
}
|
|
37743
|
-
else {
|
|
37744
|
-
date = new Date(Date.parse(opts.field.value));
|
|
37745
|
-
}
|
|
37746
|
-
// if (isDate(date)) {
|
|
37747
|
-
// self.setDate(date);
|
|
37748
|
-
// }
|
|
37749
|
-
// if (!self._v) {
|
|
37750
|
-
// self.show();
|
|
37751
|
-
// }
|
|
37752
|
-
};
|
|
37753
|
-
|
|
37754
|
-
self._onInputFocus = function()
|
|
37755
|
-
{
|
|
37756
|
-
self.show();
|
|
37757
|
-
};
|
|
37758
|
-
|
|
37759
|
-
self._onInputClick = function()
|
|
37760
|
-
{
|
|
37761
|
-
self.show();
|
|
37762
|
-
};
|
|
37763
|
-
|
|
37764
|
-
self._onInputBlur = function()
|
|
37765
|
-
{
|
|
37766
|
-
// IE allows pika div to gain focus; catch blur the input field
|
|
37767
|
-
var pEl = document.activeElement;
|
|
37768
|
-
do {
|
|
37769
|
-
if (hasClass(pEl, 'pika-single')) {
|
|
37770
|
-
return;
|
|
37771
|
-
}
|
|
37772
|
-
}
|
|
37773
|
-
while ((pEl = pEl.parentNode));
|
|
37774
|
-
|
|
37775
|
-
if (!self._c) {
|
|
37776
|
-
self._b = sto(function() {
|
|
37777
|
-
self.hide();
|
|
37778
|
-
}, 50);
|
|
37779
|
-
}
|
|
37780
|
-
self._c = false;
|
|
37781
|
-
};
|
|
37782
|
-
|
|
37783
|
-
self._onClick = function(e)
|
|
37784
|
-
{
|
|
37785
|
-
e = e || window.event;
|
|
37786
|
-
var target = e.target || e.srcElement,
|
|
37787
|
-
pEl = target;
|
|
37788
|
-
if (!target) {
|
|
37789
|
-
return;
|
|
37790
|
-
}
|
|
37791
|
-
if (!hasEventListeners && hasClass(target, 'pika-select')) {
|
|
37792
|
-
if (!target.onchange) {
|
|
37793
|
-
target.setAttribute('onchange', 'return;');
|
|
37794
|
-
addEvent(target, 'change', self._onChange);
|
|
37795
|
-
}
|
|
37796
|
-
}
|
|
37797
|
-
do {
|
|
37798
|
-
if (hasClass(pEl, 'pika-single') || pEl === opts.trigger) {
|
|
37799
|
-
return;
|
|
37800
|
-
}
|
|
37801
|
-
}
|
|
37802
|
-
while ((pEl = pEl.parentNode));
|
|
37803
|
-
if (self._v && target !== opts.trigger && pEl !== opts.trigger) {
|
|
37804
|
-
self.hide();
|
|
37805
|
-
}
|
|
37806
|
-
};
|
|
37807
|
-
|
|
37808
|
-
self.el = document.createElement('div');
|
|
37809
|
-
self.el.className = 'pika-single' + (opts.isRTL ? ' is-rtl' : '') + (opts.theme ? ' ' + opts.theme : '');
|
|
37810
|
-
|
|
37811
|
-
addEvent(self.el, 'mousedown', self._onMouseDown, true);
|
|
37812
|
-
addEvent(self.el, 'touchend', self._onMouseDown, true);
|
|
37813
|
-
addEvent(self.el, 'change', self._onChange);
|
|
37814
|
-
|
|
37815
|
-
if (opts.keyboardInput) {
|
|
37816
|
-
addEvent(document, 'keydown', self._onKeyChange);
|
|
37817
|
-
}
|
|
37818
|
-
|
|
37819
|
-
if (opts.field) {
|
|
37820
|
-
if (opts.container) {
|
|
37821
|
-
opts.container.appendChild(self.el);
|
|
37822
|
-
} else if (opts.bound) {
|
|
37823
|
-
document.body.appendChild(self.el);
|
|
37824
|
-
} else {
|
|
37825
|
-
opts.field.parentNode.insertBefore(self.el, opts.field.nextSibling);
|
|
37826
|
-
}
|
|
37827
|
-
addEvent(opts.field, 'change', self._onInputChange);
|
|
37828
|
-
|
|
37829
|
-
if (!opts.defaultDate) {
|
|
37830
|
-
if (hasMoment && opts.field.value) {
|
|
37831
|
-
opts.defaultDate = moment(opts.field.value, opts.format).toDate();
|
|
37832
|
-
} else {
|
|
37833
|
-
opts.defaultDate = new Date(Date.parse(opts.field.value));
|
|
37834
|
-
}
|
|
37835
|
-
opts.setDefaultDate = true;
|
|
37836
|
-
}
|
|
37837
|
-
}
|
|
37838
|
-
|
|
37839
|
-
var defDate = opts.defaultDate;
|
|
37840
|
-
|
|
37841
|
-
if (isDate(defDate)) {
|
|
37842
|
-
if (opts.setDefaultDate) {
|
|
37843
|
-
self.setDate(defDate, true);
|
|
37844
|
-
} else {
|
|
37845
|
-
self.gotoDate(defDate);
|
|
37846
|
-
}
|
|
37847
|
-
} else {
|
|
37848
|
-
self.gotoDate(new Date());
|
|
37849
|
-
}
|
|
37850
|
-
|
|
37851
|
-
if (opts.bound) {
|
|
37852
|
-
this.hide();
|
|
37853
|
-
self.el.className += ' is-bound';
|
|
37854
|
-
addEvent(opts.trigger, 'click', self._onInputClick);
|
|
37855
|
-
addEvent(opts.trigger, 'focus', self._onInputFocus);
|
|
37856
|
-
addEvent(opts.trigger, 'blur', self._onInputBlur);
|
|
37857
|
-
} else {
|
|
37858
|
-
this.show();
|
|
37859
|
-
}
|
|
37860
|
-
};
|
|
37861
|
-
|
|
37862
|
-
|
|
37863
|
-
/**
|
|
37864
|
-
* public Pikaday API
|
|
37865
|
-
*/
|
|
37866
|
-
Pikaday.prototype = {
|
|
37867
|
-
|
|
37868
|
-
|
|
37869
|
-
/**
|
|
37870
|
-
* configure functionality
|
|
37871
|
-
*/
|
|
37872
|
-
config: function(options)
|
|
37873
|
-
{
|
|
37874
|
-
if (!this._o) {
|
|
37875
|
-
this._o = extend({}, defaults, true);
|
|
37876
|
-
}
|
|
37877
|
-
|
|
37878
|
-
var opts = extend(this._o, options, true);
|
|
37879
|
-
|
|
37880
|
-
opts.isRTL = !!opts.isRTL;
|
|
37881
|
-
|
|
37882
|
-
opts.field = (opts.field && opts.field.nodeName) ? opts.field : null;
|
|
37883
|
-
|
|
37884
|
-
opts.theme = (typeof opts.theme) === 'string' && opts.theme ? opts.theme : null;
|
|
37885
|
-
|
|
37886
|
-
opts.bound = !!(opts.bound !== undefined ? opts.field && opts.bound : opts.field);
|
|
37887
|
-
|
|
37888
|
-
opts.trigger = (opts.trigger && opts.trigger.nodeName) ? opts.trigger : opts.field;
|
|
37889
|
-
|
|
37890
|
-
opts.disableWeekends = !!opts.disableWeekends;
|
|
37891
|
-
|
|
37892
|
-
opts.disableDayFn = (typeof opts.disableDayFn) === 'function' ? opts.disableDayFn : null;
|
|
37893
|
-
|
|
37894
|
-
var nom = parseInt(opts.numberOfMonths, 10) || 1;
|
|
37895
|
-
opts.numberOfMonths = nom > 4 ? 4 : nom;
|
|
37896
|
-
|
|
37897
|
-
if (!isDate(opts.minDate)) {
|
|
37898
|
-
opts.minDate = false;
|
|
37899
|
-
}
|
|
37900
|
-
if (!isDate(opts.maxDate)) {
|
|
37901
|
-
opts.maxDate = false;
|
|
37902
|
-
}
|
|
37903
|
-
if ((opts.minDate && opts.maxDate) && opts.maxDate < opts.minDate) {
|
|
37904
|
-
opts.maxDate = opts.minDate = false;
|
|
37905
|
-
}
|
|
37906
|
-
if (opts.minDate) {
|
|
37907
|
-
this.setMinDate(opts.minDate);
|
|
37908
|
-
}
|
|
37909
|
-
if (opts.maxDate) {
|
|
37910
|
-
this.setMaxDate(opts.maxDate);
|
|
37911
|
-
}
|
|
37912
|
-
|
|
37913
|
-
if (isArray(opts.yearRange)) {
|
|
37914
|
-
var fallback = new Date().getFullYear() - 10;
|
|
37915
|
-
opts.yearRange[0] = parseInt(opts.yearRange[0], 10) || fallback;
|
|
37916
|
-
opts.yearRange[1] = parseInt(opts.yearRange[1], 10) || fallback;
|
|
37917
|
-
} else {
|
|
37918
|
-
opts.yearRange = Math.abs(parseInt(opts.yearRange, 10)) || defaults.yearRange;
|
|
37919
|
-
if (opts.yearRange > 100) {
|
|
37920
|
-
opts.yearRange = 100;
|
|
37921
|
-
}
|
|
37922
|
-
}
|
|
37923
|
-
|
|
37924
|
-
return opts;
|
|
37925
|
-
},
|
|
37926
|
-
|
|
37927
|
-
/**
|
|
37928
|
-
* return a formatted string of the current selection (using Moment.js if available)
|
|
37929
|
-
*/
|
|
37930
|
-
toString: function(format)
|
|
37931
|
-
{
|
|
37932
|
-
format = format || this._o.format;
|
|
37933
|
-
if (!isDate(this._d)) {
|
|
37934
|
-
return '';
|
|
37935
|
-
}
|
|
37936
|
-
if (this._o.toString) {
|
|
37937
|
-
return this._o.toString(this._d, format);
|
|
37938
|
-
}
|
|
37939
|
-
if (hasMoment) {
|
|
37940
|
-
return moment(this._d).format(format);
|
|
37941
|
-
}
|
|
37942
|
-
return this._d.toDateString();
|
|
37943
|
-
},
|
|
37944
|
-
|
|
37945
|
-
/**
|
|
37946
|
-
* return a Moment.js object of the current selection (if available)
|
|
37947
|
-
*/
|
|
37948
|
-
getMoment: function()
|
|
37949
|
-
{
|
|
37950
|
-
return hasMoment ? moment(this._d) : null;
|
|
37951
|
-
},
|
|
37952
|
-
|
|
37953
|
-
/**
|
|
37954
|
-
* set the current selection from a Moment.js object (if available)
|
|
37955
|
-
*/
|
|
37956
|
-
setMoment: function(date, preventOnSelect)
|
|
37957
|
-
{
|
|
37958
|
-
if (hasMoment && moment.isMoment(date)) {
|
|
37959
|
-
this.setDate(date.toDate(), preventOnSelect);
|
|
37960
|
-
}
|
|
37961
|
-
},
|
|
37962
|
-
|
|
37963
|
-
/**
|
|
37964
|
-
* return a Date object of the current selection
|
|
37965
|
-
*/
|
|
37966
|
-
getDate: function()
|
|
37967
|
-
{
|
|
37968
|
-
return isDate(this._d) ? new Date(this._d.getTime()) : null;
|
|
37969
|
-
},
|
|
37970
|
-
|
|
37971
|
-
/**
|
|
37972
|
-
* set the current selection
|
|
37973
|
-
*/
|
|
37974
|
-
setDate: function(date, preventOnSelect)
|
|
37975
|
-
{
|
|
37976
|
-
if (!date) {
|
|
37977
|
-
this._d = null;
|
|
37978
|
-
|
|
37979
|
-
if (this._o.field) {
|
|
37980
|
-
this._o.field.value = '';
|
|
37981
|
-
fireEvent(this._o.field, 'change', { firedBy: this });
|
|
37982
|
-
}
|
|
37983
|
-
|
|
37984
|
-
return this.draw();
|
|
37985
|
-
}
|
|
37986
|
-
if (typeof date === 'string') {
|
|
37987
|
-
date = new Date(Date.parse(date));
|
|
37988
|
-
}
|
|
37989
|
-
if (!isDate(date)) {
|
|
37990
|
-
return;
|
|
37991
|
-
}
|
|
37992
|
-
|
|
37993
|
-
var min = this._o.minDate,
|
|
37994
|
-
max = this._o.maxDate;
|
|
37995
|
-
|
|
37996
|
-
if (isDate(min) && date < min) {
|
|
37997
|
-
date = min;
|
|
37998
|
-
} else if (isDate(max) && date > max) {
|
|
37999
|
-
date = max;
|
|
38000
|
-
}
|
|
38001
|
-
|
|
38002
|
-
this._d = new Date(date.getTime());
|
|
38003
|
-
setToStartOfDay(this._d);
|
|
38004
|
-
this.gotoDate(this._d);
|
|
38005
|
-
|
|
38006
|
-
if (this._o.field) {
|
|
38007
|
-
this._o.field.value = this.toString();
|
|
38008
|
-
fireEvent(this._o.field, 'change', { firedBy: this });
|
|
38009
|
-
}
|
|
38010
|
-
if (!preventOnSelect && typeof this._o.onSelect === 'function') {
|
|
38011
|
-
this._o.onSelect.call(this, this.getDate());
|
|
38012
|
-
}
|
|
38013
|
-
},
|
|
38014
|
-
|
|
38015
|
-
/**
|
|
38016
|
-
* change view to a specific date
|
|
38017
|
-
*/
|
|
38018
|
-
gotoDate: function(date)
|
|
38019
|
-
{
|
|
38020
|
-
var newCalendar = true;
|
|
38021
|
-
|
|
38022
|
-
if (!isDate(date)) {
|
|
38023
|
-
return;
|
|
38024
|
-
}
|
|
38025
|
-
|
|
38026
|
-
if (this.calendars) {
|
|
38027
|
-
var firstVisibleDate = new Date(this.calendars[0].year, this.calendars[0].month, 1),
|
|
38028
|
-
lastVisibleDate = new Date(this.calendars[this.calendars.length-1].year, this.calendars[this.calendars.length-1].month, 1),
|
|
38029
|
-
visibleDate = date.getTime();
|
|
38030
|
-
// get the end of the month
|
|
38031
|
-
lastVisibleDate.setMonth(lastVisibleDate.getMonth()+1);
|
|
38032
|
-
lastVisibleDate.setDate(lastVisibleDate.getDate()-1);
|
|
38033
|
-
newCalendar = (visibleDate < firstVisibleDate.getTime() || lastVisibleDate.getTime() < visibleDate);
|
|
38034
|
-
}
|
|
38035
|
-
|
|
38036
|
-
if (newCalendar) {
|
|
38037
|
-
this.calendars = [{
|
|
38038
|
-
month: date.getMonth(),
|
|
38039
|
-
year: date.getFullYear()
|
|
38040
|
-
}];
|
|
38041
|
-
if (this._o.mainCalendar === 'right') {
|
|
38042
|
-
this.calendars[0].month += 1 - this._o.numberOfMonths;
|
|
38043
|
-
}
|
|
38044
|
-
}
|
|
38045
|
-
|
|
38046
|
-
this.adjustCalendars();
|
|
38047
|
-
},
|
|
38048
|
-
|
|
38049
|
-
adjustDate: function(sign, days) {
|
|
38050
|
-
|
|
38051
|
-
var day = this.getDate() || new Date();
|
|
38052
|
-
var difference = parseInt(days)*24*60*60*1000;
|
|
38053
|
-
|
|
38054
|
-
var newDay;
|
|
38055
|
-
|
|
38056
|
-
if (sign === 'add') {
|
|
38057
|
-
newDay = new Date(day.valueOf() + difference);
|
|
38058
|
-
} else if (sign === 'subtract') {
|
|
38059
|
-
newDay = new Date(day.valueOf() - difference);
|
|
38060
|
-
}
|
|
38061
|
-
|
|
38062
|
-
this.setDate(newDay);
|
|
38063
|
-
},
|
|
38064
|
-
|
|
38065
|
-
adjustCalendars: function() {
|
|
38066
|
-
this.calendars[0] = adjustCalendar(this.calendars[0]);
|
|
38067
|
-
for (var c = 1; c < this._o.numberOfMonths; c++) {
|
|
38068
|
-
this.calendars[c] = adjustCalendar({
|
|
38069
|
-
month: this.calendars[0].month + c,
|
|
38070
|
-
year: this.calendars[0].year
|
|
38071
|
-
});
|
|
38072
|
-
}
|
|
38073
|
-
this.draw();
|
|
38074
|
-
},
|
|
38075
|
-
|
|
38076
|
-
gotoToday: function()
|
|
38077
|
-
{
|
|
38078
|
-
this.gotoDate(new Date());
|
|
38079
|
-
},
|
|
38080
|
-
|
|
38081
|
-
/**
|
|
38082
|
-
* change view to a specific month (zero-index, e.g. 0: January)
|
|
38083
|
-
*/
|
|
38084
|
-
gotoMonth: function(month)
|
|
38085
|
-
{
|
|
38086
|
-
if (!isNaN(month)) {
|
|
38087
|
-
this.calendars[0].month = parseInt(month, 10);
|
|
38088
|
-
this.adjustCalendars();
|
|
38089
|
-
}
|
|
38090
|
-
},
|
|
38091
|
-
|
|
38092
|
-
nextMonth: function()
|
|
38093
|
-
{
|
|
38094
|
-
this.calendars[0].month++;
|
|
38095
|
-
this.adjustCalendars();
|
|
38096
|
-
},
|
|
38097
|
-
|
|
38098
|
-
prevMonth: function()
|
|
38099
|
-
{
|
|
38100
|
-
this.calendars[0].month--;
|
|
38101
|
-
this.adjustCalendars();
|
|
38102
|
-
},
|
|
38103
|
-
|
|
38104
|
-
/**
|
|
38105
|
-
* change view to a specific full year (e.g. "2012")
|
|
38106
|
-
*/
|
|
38107
|
-
gotoYear: function(year)
|
|
38108
|
-
{
|
|
38109
|
-
if (!isNaN(year)) {
|
|
38110
|
-
this.calendars[0].year = parseInt(year, 10);
|
|
38111
|
-
this.adjustCalendars();
|
|
38112
|
-
}
|
|
38113
|
-
},
|
|
38114
|
-
|
|
38115
|
-
/**
|
|
38116
|
-
* change the minDate
|
|
38117
|
-
*/
|
|
38118
|
-
setMinDate: function(value)
|
|
38119
|
-
{
|
|
38120
|
-
if(value instanceof Date) {
|
|
38121
|
-
setToStartOfDay(value);
|
|
38122
|
-
this._o.minDate = value;
|
|
38123
|
-
this._o.minYear = value.getFullYear();
|
|
38124
|
-
this._o.minMonth = value.getMonth();
|
|
38125
|
-
} else {
|
|
38126
|
-
this._o.minDate = defaults.minDate;
|
|
38127
|
-
this._o.minYear = defaults.minYear;
|
|
38128
|
-
this._o.minMonth = defaults.minMonth;
|
|
38129
|
-
this._o.startRange = defaults.startRange;
|
|
38130
|
-
}
|
|
38131
|
-
|
|
38132
|
-
this.draw();
|
|
38133
|
-
},
|
|
38134
|
-
|
|
38135
|
-
/**
|
|
38136
|
-
* change the maxDate
|
|
38137
|
-
*/
|
|
38138
|
-
setMaxDate: function(value)
|
|
38139
|
-
{
|
|
38140
|
-
if(value instanceof Date) {
|
|
38141
|
-
setToStartOfDay(value);
|
|
38142
|
-
this._o.maxDate = value;
|
|
38143
|
-
this._o.maxYear = value.getFullYear();
|
|
38144
|
-
this._o.maxMonth = value.getMonth();
|
|
38145
|
-
} else {
|
|
38146
|
-
this._o.maxDate = defaults.maxDate;
|
|
38147
|
-
this._o.maxYear = defaults.maxYear;
|
|
38148
|
-
this._o.maxMonth = defaults.maxMonth;
|
|
38149
|
-
this._o.endRange = defaults.endRange;
|
|
38150
|
-
}
|
|
38151
|
-
|
|
38152
|
-
this.draw();
|
|
38153
|
-
},
|
|
38154
|
-
|
|
38155
|
-
setStartRange: function(value)
|
|
38156
|
-
{
|
|
38157
|
-
this._o.startRange = value;
|
|
38158
|
-
},
|
|
38159
|
-
|
|
38160
|
-
setEndRange: function(value)
|
|
38161
|
-
{
|
|
38162
|
-
this._o.endRange = value;
|
|
38163
|
-
},
|
|
38164
|
-
|
|
38165
|
-
/**
|
|
38166
|
-
* refresh the HTML
|
|
38167
|
-
*/
|
|
38168
|
-
draw: function(force)
|
|
38169
|
-
{
|
|
38170
|
-
if (!this._v && !force) {
|
|
38171
|
-
return;
|
|
38172
|
-
}
|
|
38173
|
-
var opts = this._o,
|
|
38174
|
-
minYear = opts.minYear,
|
|
38175
|
-
maxYear = opts.maxYear,
|
|
38176
|
-
minMonth = opts.minMonth,
|
|
38177
|
-
maxMonth = opts.maxMonth,
|
|
38178
|
-
html = '',
|
|
38179
|
-
randId;
|
|
38180
|
-
|
|
38181
|
-
if (this._y <= minYear) {
|
|
38182
|
-
this._y = minYear;
|
|
38183
|
-
if (!isNaN(minMonth) && this._m < minMonth) {
|
|
38184
|
-
this._m = minMonth;
|
|
38185
|
-
}
|
|
38186
|
-
}
|
|
38187
|
-
if (this._y >= maxYear) {
|
|
38188
|
-
this._y = maxYear;
|
|
38189
|
-
if (!isNaN(maxMonth) && this._m > maxMonth) {
|
|
38190
|
-
this._m = maxMonth;
|
|
38191
|
-
}
|
|
38192
|
-
}
|
|
38193
|
-
|
|
38194
|
-
for (var c = 0; c < opts.numberOfMonths; c++) {
|
|
38195
|
-
randId = 'pika-title-' + Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 2);
|
|
38196
|
-
html += '<div class="pika-lendar">' + renderTitle(this, c, this.calendars[c].year, this.calendars[c].month, this.calendars[0].year, randId) + this.render(this.calendars[c].year, this.calendars[c].month, randId) + '</div>';
|
|
38197
|
-
}
|
|
38198
|
-
|
|
38199
|
-
this.el.innerHTML = html;
|
|
38200
|
-
|
|
38201
|
-
if (opts.bound) {
|
|
38202
|
-
if(opts.field.type !== 'hidden') {
|
|
38203
|
-
sto(function() {
|
|
38204
|
-
opts.trigger.focus();
|
|
38205
|
-
}, 1);
|
|
38206
|
-
}
|
|
38207
|
-
}
|
|
38208
|
-
|
|
38209
|
-
if (typeof this._o.onDraw === 'function') {
|
|
38210
|
-
this._o.onDraw(this);
|
|
38211
|
-
}
|
|
38212
|
-
|
|
38213
|
-
if (opts.bound) {
|
|
38214
|
-
// let the screen reader user know to use arrow keys
|
|
38215
|
-
opts.field.setAttribute('aria-label', 'Use the arrow keys to pick a date');
|
|
38216
|
-
}
|
|
38217
|
-
},
|
|
38218
|
-
|
|
38219
|
-
adjustPosition: function()
|
|
38220
|
-
{
|
|
38221
|
-
var field, pEl, width, height, viewportWidth, viewportHeight, scrollTop, left, top, clientRect;
|
|
38222
|
-
|
|
38223
|
-
if (this._o.container) return;
|
|
38224
|
-
|
|
38225
|
-
this.el.style.position = 'absolute';
|
|
38226
|
-
|
|
38227
|
-
field = this._o.trigger;
|
|
38228
|
-
pEl = field;
|
|
38229
|
-
width = this.el.offsetWidth;
|
|
38230
|
-
height = this.el.offsetHeight;
|
|
38231
|
-
viewportWidth = window.innerWidth || document.documentElement.clientWidth;
|
|
38232
|
-
viewportHeight = window.innerHeight || document.documentElement.clientHeight;
|
|
38233
|
-
scrollTop = window.pageYOffset || document.body.scrollTop || document.documentElement.scrollTop;
|
|
38234
|
-
|
|
38235
|
-
if (typeof field.getBoundingClientRect === 'function') {
|
|
38236
|
-
clientRect = field.getBoundingClientRect();
|
|
38237
|
-
left = clientRect.left + window.pageXOffset;
|
|
38238
|
-
top = clientRect.bottom + window.pageYOffset;
|
|
38239
|
-
} else {
|
|
38240
|
-
left = pEl.offsetLeft;
|
|
38241
|
-
top = pEl.offsetTop + pEl.offsetHeight;
|
|
38242
|
-
while((pEl = pEl.offsetParent)) {
|
|
38243
|
-
left += pEl.offsetLeft;
|
|
38244
|
-
top += pEl.offsetTop;
|
|
38245
|
-
}
|
|
38246
|
-
}
|
|
38247
|
-
|
|
38248
|
-
// default position is bottom & left
|
|
38249
|
-
if ((this._o.reposition && left + width > viewportWidth) ||
|
|
38250
|
-
(
|
|
38251
|
-
this._o.position.indexOf('right') > -1 &&
|
|
38252
|
-
left - width + field.offsetWidth > 0
|
|
38253
|
-
)
|
|
38254
|
-
) {
|
|
38255
|
-
left = left - width + field.offsetWidth;
|
|
38256
|
-
}
|
|
38257
|
-
if ((this._o.reposition && top + height > viewportHeight + scrollTop) ||
|
|
38258
|
-
(
|
|
38259
|
-
this._o.position.indexOf('top') > -1 &&
|
|
38260
|
-
top - height - field.offsetHeight > 0
|
|
38261
|
-
)
|
|
38262
|
-
) {
|
|
38263
|
-
top = top - height - field.offsetHeight;
|
|
38264
|
-
}
|
|
38265
|
-
|
|
38266
|
-
this.el.style.left = left + 'px';
|
|
38267
|
-
this.el.style.top = top + 'px';
|
|
38268
|
-
},
|
|
38269
|
-
|
|
38270
|
-
/**
|
|
38271
|
-
* render HTML for a particular month
|
|
38272
|
-
*/
|
|
38273
|
-
render: function(year, month, randId)
|
|
38274
|
-
{
|
|
38275
|
-
var opts = this._o,
|
|
38276
|
-
now = new Date(),
|
|
38277
|
-
days = getDaysInMonth(year, month),
|
|
38278
|
-
before = new Date(year, month, 1).getDay(),
|
|
38279
|
-
data = [],
|
|
38280
|
-
row = [];
|
|
38281
|
-
setToStartOfDay(now);
|
|
38282
|
-
if (opts.firstDay > 0) {
|
|
38283
|
-
before -= opts.firstDay;
|
|
38284
|
-
if (before < 0) {
|
|
38285
|
-
before += 7;
|
|
38286
|
-
}
|
|
38287
|
-
}
|
|
38288
|
-
var previousMonth = month === 0 ? 11 : month - 1,
|
|
38289
|
-
nextMonth = month === 11 ? 0 : month + 1,
|
|
38290
|
-
yearOfPreviousMonth = month === 0 ? year - 1 : year,
|
|
38291
|
-
yearOfNextMonth = month === 11 ? year + 1 : year,
|
|
38292
|
-
daysInPreviousMonth = getDaysInMonth(yearOfPreviousMonth, previousMonth);
|
|
38293
|
-
var cells = days + before,
|
|
38294
|
-
after = cells;
|
|
38295
|
-
while(after > 7) {
|
|
38296
|
-
after -= 7;
|
|
38297
|
-
}
|
|
38298
|
-
cells += 7 - after;
|
|
38299
|
-
var isWeekSelected = false;
|
|
38300
|
-
for (var i = 0, r = 0; i < cells; i++)
|
|
38301
|
-
{
|
|
38302
|
-
var day = new Date(year, month, 1 + (i - before)),
|
|
38303
|
-
isSelected = isDate(this._d) ? compareDates(day, this._d) : false,
|
|
38304
|
-
isToday = compareDates(day, now),
|
|
38305
|
-
hasEvent = opts.events.indexOf(day.toDateString()) !== -1 ? true : false,
|
|
38306
|
-
isEmpty = i < before || i >= (days + before),
|
|
38307
|
-
dayNumber = 1 + (i - before),
|
|
38308
|
-
monthNumber = month,
|
|
38309
|
-
yearNumber = year,
|
|
38310
|
-
isStartRange = opts.startRange && compareDates(opts.startRange, day),
|
|
38311
|
-
isEndRange = opts.endRange && compareDates(opts.endRange, day),
|
|
38312
|
-
isInRange = opts.startRange && opts.endRange && opts.startRange < day && day < opts.endRange,
|
|
38313
|
-
isDisabled = (opts.minDate && day < opts.minDate) ||
|
|
38314
|
-
(opts.maxDate && day > opts.maxDate) ||
|
|
38315
|
-
(opts.disableWeekends && isWeekend(day)) ||
|
|
38316
|
-
(opts.disableDayFn && opts.disableDayFn(day));
|
|
38317
|
-
|
|
38318
|
-
if (isEmpty) {
|
|
38319
|
-
if (i < before) {
|
|
38320
|
-
dayNumber = daysInPreviousMonth + dayNumber;
|
|
38321
|
-
monthNumber = previousMonth;
|
|
38322
|
-
yearNumber = yearOfPreviousMonth;
|
|
38323
|
-
} else {
|
|
38324
|
-
dayNumber = dayNumber - days;
|
|
38325
|
-
monthNumber = nextMonth;
|
|
38326
|
-
yearNumber = yearOfNextMonth;
|
|
38327
|
-
}
|
|
38328
|
-
}
|
|
38329
|
-
|
|
38330
|
-
var dayConfig = {
|
|
38331
|
-
day: dayNumber,
|
|
38332
|
-
month: monthNumber,
|
|
38333
|
-
year: yearNumber,
|
|
38334
|
-
hasEvent: hasEvent,
|
|
38335
|
-
isSelected: isSelected,
|
|
38336
|
-
isToday: isToday,
|
|
38337
|
-
isDisabled: isDisabled,
|
|
38338
|
-
isEmpty: isEmpty,
|
|
38339
|
-
isStartRange: isStartRange,
|
|
38340
|
-
isEndRange: isEndRange,
|
|
38341
|
-
isInRange: isInRange,
|
|
38342
|
-
showDaysInNextAndPreviousMonths: opts.showDaysInNextAndPreviousMonths,
|
|
38343
|
-
enableSelectionDaysInNextAndPreviousMonths: opts.enableSelectionDaysInNextAndPreviousMonths
|
|
38344
|
-
};
|
|
38345
|
-
|
|
38346
|
-
if (opts.pickWholeWeek && isSelected) {
|
|
38347
|
-
isWeekSelected = true;
|
|
38348
|
-
}
|
|
38349
|
-
|
|
38350
|
-
row.push(renderDay(dayConfig));
|
|
38351
|
-
|
|
38352
|
-
if (++r === 7) {
|
|
38353
|
-
if (opts.showWeekNumber) {
|
|
38354
|
-
row.unshift(renderWeek(i - before, month, year));
|
|
38355
|
-
}
|
|
38356
|
-
data.push(renderRow(row, opts.isRTL, opts.pickWholeWeek, isWeekSelected));
|
|
38357
|
-
row = [];
|
|
38358
|
-
r = 0;
|
|
38359
|
-
isWeekSelected = false;
|
|
38360
|
-
}
|
|
38361
|
-
}
|
|
38362
|
-
return renderTable(opts, data, randId);
|
|
38363
|
-
},
|
|
38364
|
-
|
|
38365
|
-
isVisible: function()
|
|
38366
|
-
{
|
|
38367
|
-
return this._v;
|
|
38368
|
-
},
|
|
38369
|
-
|
|
38370
|
-
show: function()
|
|
38371
|
-
{
|
|
38372
|
-
if (!this.isVisible()) {
|
|
38373
|
-
this._v = true;
|
|
38374
|
-
this.draw();
|
|
38375
|
-
removeClass(this.el, 'is-hidden');
|
|
38376
|
-
if (this._o.bound) {
|
|
38377
|
-
addEvent(document, 'click', this._onClick);
|
|
38378
|
-
this.adjustPosition();
|
|
38379
|
-
}
|
|
38380
|
-
if (typeof this._o.onOpen === 'function') {
|
|
38381
|
-
this._o.onOpen.call(this);
|
|
38382
|
-
}
|
|
38383
|
-
}
|
|
38384
|
-
},
|
|
38385
|
-
|
|
38386
|
-
hide: function()
|
|
38387
|
-
{
|
|
38388
|
-
var v = this._v;
|
|
38389
|
-
if (v !== false) {
|
|
38390
|
-
if (this._o.bound) {
|
|
38391
|
-
removeEvent(document, 'click', this._onClick);
|
|
38392
|
-
}
|
|
38393
|
-
this.el.style.position = 'static'; // reset
|
|
38394
|
-
this.el.style.left = 'auto';
|
|
38395
|
-
this.el.style.top = 'auto';
|
|
38396
|
-
addClass(this.el, 'is-hidden');
|
|
38397
|
-
this._v = false;
|
|
38398
|
-
if (v !== undefined && typeof this._o.onClose === 'function') {
|
|
38399
|
-
this._o.onClose.call(this);
|
|
38400
|
-
}
|
|
38401
|
-
}
|
|
38402
|
-
},
|
|
38403
|
-
|
|
38404
|
-
/**
|
|
38405
|
-
* GAME OVER
|
|
38406
|
-
*/
|
|
38407
|
-
destroy: function()
|
|
38408
|
-
{
|
|
38409
|
-
var opts = this._o;
|
|
38410
|
-
|
|
38411
|
-
this.hide();
|
|
38412
|
-
removeEvent(this.el, 'mousedown', this._onMouseDown, true);
|
|
38413
|
-
removeEvent(this.el, 'touchend', this._onMouseDown, true);
|
|
38414
|
-
removeEvent(this.el, 'change', this._onChange);
|
|
38415
|
-
if (opts.keyboardInput) {
|
|
38416
|
-
removeEvent(document, 'keydown', this._onKeyChange);
|
|
38417
|
-
}
|
|
38418
|
-
if (opts.field) {
|
|
38419
|
-
removeEvent(opts.field, 'change', this._onInputChange);
|
|
38420
|
-
if (opts.bound) {
|
|
38421
|
-
removeEvent(opts.trigger, 'click', this._onInputClick);
|
|
38422
|
-
removeEvent(opts.trigger, 'focus', this._onInputFocus);
|
|
38423
|
-
removeEvent(opts.trigger, 'blur', this._onInputBlur);
|
|
38424
|
-
}
|
|
38425
|
-
}
|
|
38426
|
-
if (this.el.parentNode) {
|
|
38427
|
-
this.el.parentNode.removeChild(this.el);
|
|
38428
|
-
}
|
|
38429
|
-
}
|
|
38430
|
-
|
|
38431
|
-
};
|
|
38432
|
-
|
|
38433
|
-
return Pikaday;
|
|
38434
|
-
}));
|
|
38435
|
-
} (pikaday$1));
|
|
38436
|
-
return pikaday$1.exports;
|
|
38437
|
-
}
|
|
38438
|
-
|
|
38439
|
-
var pikadayExports = /*@__PURE__*/ requirePikaday();
|
|
38440
|
-
var Pikaday = /*@__PURE__*/getDefaultExportFromCjs(pikadayExports);
|
|
38441
|
-
|
|
38442
|
-
var momentExports = requireMoment();
|
|
38443
|
-
var moment = /*@__PURE__*/getDefaultExportFromCjs(momentExports);
|
|
38444
|
-
|
|
38445
|
-
class TimezonePicker extends ChartComponent {
|
|
38446
|
-
constructor(renderTarget) {
|
|
38447
|
-
super(renderTarget);
|
|
38448
|
-
this.timeZones = ["Local", "UTC", "Africa/Algiers", "Africa/Cairo", "Africa/Casablanca", "Africa/Harare", "Africa/Johannesburg", "Africa/Lagos", "Africa/Nairobi", "Africa/Windhoek", "America/Anchorage", "America/Bogota", "America/Buenos Aires", "America/Caracas", "America/Chicago", "America/Chihuahua", "America/Denver", "America/Edmonton", "America/Godthab", "America/Guatemala", "America/Halifax", "America/Indiana/Indianapolis", "America/Los Angeles", "America/Manaus", "America/Mexico City", "America/Montevideo", "America/New York", "America/Phoenix", "America/Santiago", "America/Sao Paulo", "America/St Johns", "America/Tijuana", "America/Toronto", "America/Vancouver", "America/Winnipeg", "Asia/Amman", "Asia/Beirut", "Asia/Baghdad", "Asia/Baku", "Asia/Bangkok", "Asia/Calcutta", "Asia/Colombo", "Asia/Dhaka", "Asia/Dubai", "Asia/Ho Chi Minh", "Asia/Hong Kong", "Asia/Irkutsk", "Asia/Istanbul", "Asia/Jakarta", "Asia/Jerusalem", "Asia/Kabul", "Asia/Karachi", "Asia/Kathmandu", "Asia/Krasnoyarsk", "Asia/Kuala Lumpur", "Asia/Kuwait", "Asia/Magadan", "Asia/Muscat", "Asia/Novosibirsk", "Asia/Qatar", "Asia/Rangoon", "Asia/Seoul", "Asia/Shanghai", "Asia/Singapore", "Asia/Taipei", "Asia/Tbilisi", "Asia/Tehran", "Asia/Tokyo", "Asia/Vladivostok", "Asia/Yakutsk", "Asia/Yekaterinburg", "Asia/Yerevan", "Atlantic/Azores", "Atlantic/Cape Verde", "Atlantic/South Georgia", "Australia/Adelaide", "Australia/Brisbane", "Australia/Canberra", "Australia/Darwin", "Australia/Hobart", "Australia/Melbourne", "Australia/Perth", "Australia/Queensland", "Australia/Sydney", "Europe/Amsterdam", "Europe/Andorra", "Europe/Athens", "Europe/Belfast", "Europe/Belgrade", "Europe/Berlin", "Europe/Brussels", "Europe/Budapest", "Europe/Dublin", "Europe/Helsinki", "Europe/Kiev", "Europe/Lisbon", "Europe/London", "Europe/Luxembourg", "Europe/Madrid", "Europe/Minsk", "Europe/Monaco", "Europe/Moscow", "Europe/Oslo", "Europe/Paris", "Europe/Rome", "Europe/Stockholm", "Europe/Vienna", "Europe/Warsaw", "Europe/Zurich", "Pacific/Auckland", "Pacific/Fiji", "Pacific/Guam", "Pacific/Honolulu", "Pacific/Midway", "Pacific/Tongatapu"];
|
|
38449
|
-
}
|
|
38450
|
-
sortTimezones() {
|
|
38451
|
-
let filteredTimezones = this.timeZones.filter((tz) => {
|
|
38452
|
-
return !(tz.toLowerCase() == 'local' || tz == 'UTC');
|
|
38453
|
-
});
|
|
38454
|
-
filteredTimezones.sort((a, b) => {
|
|
38455
|
-
let aOffset = moment$1.tz(new Date(), a.split(' ').join('_')).utcOffset();
|
|
38456
|
-
let bOffset = moment$1.tz(new Date(), b.split(' ').join('_')).utcOffset();
|
|
38457
|
-
if (aOffset < bOffset) {
|
|
38458
|
-
return -1;
|
|
38459
|
-
}
|
|
38460
|
-
if (aOffset > bOffset) {
|
|
38461
|
-
return 1;
|
|
38462
|
-
}
|
|
38463
|
-
return 0;
|
|
38464
|
-
});
|
|
38465
|
-
this.timeZones = ['Local', 'UTC'].concat(filteredTimezones);
|
|
38466
|
-
}
|
|
38467
|
-
render(onTimezoneSelect, defaultTimeZone = null) {
|
|
38468
|
-
this.targetElement = select(this.renderTarget)
|
|
38469
|
-
.classed("tsi-timezonePicker", true);
|
|
38470
|
-
var timezoneSelection = this.targetElement.append("select")
|
|
38471
|
-
.attr("class", "tsi-timezonePicker tsi-select");
|
|
38472
|
-
this.sortTimezones();
|
|
38473
|
-
var options = timezoneSelection.selectAll("option")
|
|
38474
|
-
.data(this.timeZones)
|
|
38475
|
-
.enter()
|
|
38476
|
-
.append("option")
|
|
38477
|
-
.attr('value', d => d)
|
|
38478
|
-
.text((tz) => Utils.convertTimezoneToLabel(tz, this.getString('Local')));
|
|
38479
|
-
timezoneSelection.on("change", function (d) {
|
|
38480
|
-
var timezone = select(this).node().value.replace(/\s/g, "_");
|
|
38481
|
-
onTimezoneSelect(timezone);
|
|
38482
|
-
});
|
|
38483
|
-
defaultTimeZone = defaultTimeZone.replace(/_/g, " ");
|
|
38484
|
-
if (defaultTimeZone != null) {
|
|
38485
|
-
options.filter((d) => d == defaultTimeZone).attr("selected", true);
|
|
38486
|
-
}
|
|
38487
|
-
return;
|
|
38488
|
-
}
|
|
38489
|
-
}
|
|
37336
|
+
// Ensure moment is available globally for Pikaday
|
|
37337
|
+
if (typeof window !== 'undefined') {
|
|
37338
|
+
window.moment = moment;
|
|
37339
|
+
}
|
|
37340
|
+
// Export a function to safely create Pikaday instances
|
|
37341
|
+
function createPikaday(options) {
|
|
37342
|
+
if (typeof window === 'undefined') {
|
|
37343
|
+
console.warn('Pikaday requires a browser environment');
|
|
37344
|
+
return null;
|
|
37345
|
+
}
|
|
37346
|
+
const Pikaday = window.Pikaday;
|
|
37347
|
+
if (!Pikaday) {
|
|
37348
|
+
console.error('Pikaday not available. Make sure pikaday.js is loaded.');
|
|
37349
|
+
return null;
|
|
37350
|
+
}
|
|
37351
|
+
if (!moment || !window.moment) {
|
|
37352
|
+
console.error('Moment.js not available. Pikaday requires moment.js.');
|
|
37353
|
+
return null;
|
|
37354
|
+
}
|
|
37355
|
+
return new Pikaday(options);
|
|
37356
|
+
}
|
|
38490
37357
|
|
|
38491
37358
|
class DateTimePicker extends ChartComponent {
|
|
38492
37359
|
constructor(renderTarget) {
|
|
@@ -38758,8 +37625,8 @@
|
|
|
38758
37625
|
weekdays: moment.localeData().weekdays(),
|
|
38759
37626
|
weekdaysShort: moment.localeData().weekdaysMin()
|
|
38760
37627
|
};
|
|
38761
|
-
|
|
38762
|
-
this.calendarPicker =
|
|
37628
|
+
// Use the safe Pikaday wrapper
|
|
37629
|
+
this.calendarPicker = createPikaday({
|
|
38763
37630
|
bound: false,
|
|
38764
37631
|
container: this.calendar.node(),
|
|
38765
37632
|
field: this.calendar.node(),
|
|
@@ -38795,6 +37662,11 @@
|
|
|
38795
37662
|
maxDate: this.convertToCalendarDate(this.maxMillis),
|
|
38796
37663
|
defaultDate: Utils.adjustDateFromTimezoneOffset(new Date(this.fromMillis))
|
|
38797
37664
|
});
|
|
37665
|
+
// Check if Pikaday was created successfully
|
|
37666
|
+
if (!this.calendarPicker) {
|
|
37667
|
+
console.error('Failed to create Pikaday calendar. Check moment.js availability.');
|
|
37668
|
+
return;
|
|
37669
|
+
}
|
|
38798
37670
|
}
|
|
38799
37671
|
setSelectedQuickTimes() {
|
|
38800
37672
|
let isSelected = d => {
|
|
@@ -39055,7 +37927,30 @@
|
|
|
39055
37927
|
this.pickerIsVisible = false;
|
|
39056
37928
|
}
|
|
39057
37929
|
buttonDateTimeFormat(millis) {
|
|
39058
|
-
|
|
37930
|
+
const date = new Date(millis);
|
|
37931
|
+
const locale = this.chartOptions.dateLocale || 'en-US';
|
|
37932
|
+
const is24Hour = this.chartOptions.is24HourTime !== false;
|
|
37933
|
+
const formatOptions = {
|
|
37934
|
+
year: 'numeric',
|
|
37935
|
+
month: '2-digit',
|
|
37936
|
+
day: '2-digit',
|
|
37937
|
+
hour: '2-digit',
|
|
37938
|
+
minute: '2-digit',
|
|
37939
|
+
second: '2-digit',
|
|
37940
|
+
hour12: !is24Hour
|
|
37941
|
+
};
|
|
37942
|
+
try {
|
|
37943
|
+
if (this.chartOptions.offset && this.chartOptions.offset !== 'Local') {
|
|
37944
|
+
formatOptions.timeZone = this.getTimezoneFromOffset(this.chartOptions.offset);
|
|
37945
|
+
}
|
|
37946
|
+
const baseFormat = date.toLocaleString(locale, formatOptions);
|
|
37947
|
+
const milliseconds = date.getMilliseconds().toString().padStart(3, '0');
|
|
37948
|
+
return `${baseFormat}.${milliseconds}`;
|
|
37949
|
+
}
|
|
37950
|
+
catch (error) {
|
|
37951
|
+
console.warn(`Failed to format date for locale ${locale}:`, error);
|
|
37952
|
+
return Utils.timeFormat(!this.chartOptions.minutesForTimeLabels, !this.chartOptions.minutesForTimeLabels, this.chartOptions.offset, this.chartOptions.is24HourTime, 0, null, this.chartOptions.dateLocale)(millis);
|
|
37953
|
+
}
|
|
39059
37954
|
}
|
|
39060
37955
|
render(chartOptions, minMillis, maxMillis, onSet = null) {
|
|
39061
37956
|
this.chartOptions.setOptions(chartOptions);
|
|
@@ -39075,11 +37970,22 @@
|
|
|
39075
37970
|
}
|
|
39076
37971
|
super.themify(select(this.renderTarget), this.chartOptions.theme);
|
|
39077
37972
|
}
|
|
37973
|
+
getTimezoneFromOffset(offset) {
|
|
37974
|
+
const timezoneMap = {
|
|
37975
|
+
'UTC': 'UTC',
|
|
37976
|
+
'EST': 'America/New_York',
|
|
37977
|
+
'PST': 'America/Los_Angeles',
|
|
37978
|
+
'CST': 'America/Chicago',
|
|
37979
|
+
'MST': 'America/Denver'
|
|
37980
|
+
};
|
|
37981
|
+
return timezoneMap[offset] || 'UTC';
|
|
37982
|
+
}
|
|
39078
37983
|
}
|
|
39079
37984
|
|
|
39080
37985
|
class DateTimeButtonRange extends DateTimeButton {
|
|
39081
37986
|
constructor(renderTarget) {
|
|
39082
37987
|
super(renderTarget);
|
|
37988
|
+
this.clickOutsideHandler = null;
|
|
39083
37989
|
}
|
|
39084
37990
|
setButtonText(fromMillis, toMillis, isRelative, quickTime) {
|
|
39085
37991
|
let fromString = this.buttonDateTimeFormat(fromMillis);
|
|
@@ -39099,10 +38005,38 @@
|
|
|
39099
38005
|
onClose() {
|
|
39100
38006
|
this.dateTimePickerContainer.style("display", "none");
|
|
39101
38007
|
this.dateTimeButton.node().focus();
|
|
38008
|
+
this.removeClickOutsideHandler();
|
|
38009
|
+
}
|
|
38010
|
+
removeClickOutsideHandler() {
|
|
38011
|
+
if (this.clickOutsideHandler) {
|
|
38012
|
+
document.removeEventListener('click', this.clickOutsideHandler);
|
|
38013
|
+
this.clickOutsideHandler = null;
|
|
38014
|
+
}
|
|
38015
|
+
}
|
|
38016
|
+
setupClickOutsideHandler() {
|
|
38017
|
+
// Remove any existing handler first
|
|
38018
|
+
this.removeClickOutsideHandler();
|
|
38019
|
+
// Add handler after a small delay to prevent the opening click from immediately closing the picker
|
|
38020
|
+
setTimeout(() => {
|
|
38021
|
+
this.clickOutsideHandler = (event) => {
|
|
38022
|
+
const pickerElement = this.dateTimePickerContainer.node();
|
|
38023
|
+
const buttonElement = this.dateTimeButton.node();
|
|
38024
|
+
const target = event.target;
|
|
38025
|
+
// Check if click is outside both the picker and the button
|
|
38026
|
+
if (pickerElement && buttonElement &&
|
|
38027
|
+
!pickerElement.contains(target) &&
|
|
38028
|
+
!buttonElement.contains(target)) {
|
|
38029
|
+
this.onClose();
|
|
38030
|
+
}
|
|
38031
|
+
};
|
|
38032
|
+
document.addEventListener('click', this.clickOutsideHandler);
|
|
38033
|
+
}, 0);
|
|
39102
38034
|
}
|
|
39103
38035
|
render(chartOptions = {}, minMillis, maxMillis, fromMillis = null, toMillis = null, onSet = null, onCancel = null) {
|
|
39104
38036
|
super.render(chartOptions, minMillis, maxMillis, onSet);
|
|
39105
|
-
select(this.renderTarget)
|
|
38037
|
+
let container = select(this.renderTarget);
|
|
38038
|
+
container.classed('tsi-dateTimeContainerRange', true);
|
|
38039
|
+
container.style('position', 'relative');
|
|
39106
38040
|
this.fromMillis = fromMillis;
|
|
39107
38041
|
this.toMillis = toMillis;
|
|
39108
38042
|
this.onCancel = onCancel ? onCancel : () => { };
|
|
@@ -39128,6 +38062,7 @@
|
|
|
39128
38062
|
this.onClose();
|
|
39129
38063
|
this.onCancel();
|
|
39130
38064
|
});
|
|
38065
|
+
this.setupClickOutsideHandler();
|
|
39131
38066
|
}
|
|
39132
38067
|
});
|
|
39133
38068
|
}
|
|
@@ -39203,6 +38138,7 @@
|
|
|
39203
38138
|
}
|
|
39204
38139
|
//transformation of buckets created by the UX client to buckets for the availabilityChart
|
|
39205
38140
|
createDisplayBuckets(fromMillis, toMillis) {
|
|
38141
|
+
//TODO: "" key is confusing, should be "count" or something similar
|
|
39206
38142
|
var keysInRange = Object.keys(this.transformedAvailability[0].availabilityCount[""]).reduce((inRangeObj, timestamp, i, timestamps) => {
|
|
39207
38143
|
var currTSMillis = (new Date(timestamp)).valueOf();
|
|
39208
38144
|
var nextTSMillis = currTSMillis + this.bucketSize;
|
|
@@ -39255,6 +38191,7 @@
|
|
|
39255
38191
|
this.bucketSize = null;
|
|
39256
38192
|
}
|
|
39257
38193
|
}
|
|
38194
|
+
//TODO: should have proper types for parameters
|
|
39258
38195
|
render(transformedAvailability, chartOptions, rawAvailability = {}) {
|
|
39259
38196
|
this.setChartOptions(chartOptions);
|
|
39260
38197
|
this.rawAvailability = rawAvailability;
|
|
@@ -43431,7 +42368,7 @@
|
|
|
43431
42368
|
super(renderTarget);
|
|
43432
42369
|
this.chartOptions = new ChartOptions(); // TODO handle onkeyup and oninput in chart options
|
|
43433
42370
|
}
|
|
43434
|
-
render(
|
|
42371
|
+
render(chartOptions) {
|
|
43435
42372
|
this.chartOptions.setOptions(chartOptions);
|
|
43436
42373
|
let targetElement = select(this.renderTarget);
|
|
43437
42374
|
targetElement.html("");
|
|
@@ -43785,20 +42722,102 @@
|
|
|
43785
42722
|
}
|
|
43786
42723
|
}
|
|
43787
42724
|
|
|
42725
|
+
// Centralized renderer for the hierarchy tree. Keeps a stable D3 data-join and
|
|
42726
|
+
// updates existing DOM nodes instead of fully recreating them on each render.
|
|
42727
|
+
class TreeRenderer {
|
|
42728
|
+
static render(owner, data, target) {
|
|
42729
|
+
// Ensure an <ul> exists for this target (one list per level)
|
|
42730
|
+
let list = target.select('ul');
|
|
42731
|
+
if (list.empty()) {
|
|
42732
|
+
list = target.append('ul').attr('role', target === owner.hierarchyElem ? 'tree' : 'group');
|
|
42733
|
+
}
|
|
42734
|
+
const entries = Object.keys(data).map(k => ({ key: k, item: data[k] }));
|
|
42735
|
+
const liSelection = list.selectAll('li').data(entries, (d) => d && d.key);
|
|
42736
|
+
liSelection.exit().remove();
|
|
42737
|
+
const liEnter = liSelection.enter().append('li')
|
|
42738
|
+
.attr('role', 'none')
|
|
42739
|
+
.classed('tsi-leaf', (d) => !!d.item.isLeaf);
|
|
42740
|
+
const liMerged = liEnter.merge(liSelection);
|
|
42741
|
+
const setSize = entries.length;
|
|
42742
|
+
liMerged.each((d, i, nodes) => {
|
|
42743
|
+
const entry = d;
|
|
42744
|
+
const li = select(nodes[i]);
|
|
42745
|
+
if (owner.selectedIds && owner.selectedIds.includes(entry.item.id)) {
|
|
42746
|
+
li.classed('tsi-selected', true);
|
|
42747
|
+
}
|
|
42748
|
+
else {
|
|
42749
|
+
li.classed('tsi-selected', false);
|
|
42750
|
+
}
|
|
42751
|
+
// determine instance vs hierarchy node by presence of isLeaf flag
|
|
42752
|
+
const isInstance = !!entry.item.isLeaf;
|
|
42753
|
+
const nodeNameToCheckIfExists = isInstance ? owner.instanceNodeString(entry.item) : entry.key;
|
|
42754
|
+
const displayName = (entry.item && (entry.item.displayName || nodeNameToCheckIfExists)) || nodeNameToCheckIfExists;
|
|
42755
|
+
li.attr('data-display-name', displayName);
|
|
42756
|
+
let itemElem = li.select('.tsi-hierarchyItem');
|
|
42757
|
+
if (itemElem.empty()) {
|
|
42758
|
+
const newListElem = owner.createHierarchyItemElem(entry.item, entry.key);
|
|
42759
|
+
li.node().appendChild(newListElem.node());
|
|
42760
|
+
itemElem = li.select('.tsi-hierarchyItem');
|
|
42761
|
+
}
|
|
42762
|
+
itemElem.attr('aria-label', isInstance ? owner.getAriaLabel(entry.item) : entry.key);
|
|
42763
|
+
itemElem.attr('title', isInstance ? owner.getAriaLabel(entry.item) : entry.key);
|
|
42764
|
+
itemElem.attr('aria-expanded', String(entry.item.isExpanded));
|
|
42765
|
+
// accessibility: set treeitem level and position in set
|
|
42766
|
+
const ariaLevel = String(((entry.item && typeof entry.item.level === 'number') ? entry.item.level : 0) + 1);
|
|
42767
|
+
itemElem.attr('aria-level', ariaLevel);
|
|
42768
|
+
itemElem.attr('aria-posinset', String(i + 1));
|
|
42769
|
+
itemElem.attr('aria-setsize', String(setSize));
|
|
42770
|
+
if (!isInstance) {
|
|
42771
|
+
itemElem.select('.tsi-caret-icon').attr('style', `left: ${(entry.item.level) * 18 + 20}px`);
|
|
42772
|
+
itemElem.select('.tsi-name').text(entry.key);
|
|
42773
|
+
itemElem.select('.tsi-instanceCount').text(entry.item.cumulativeInstanceCount);
|
|
42774
|
+
}
|
|
42775
|
+
else {
|
|
42776
|
+
const nameSpan = itemElem.select('.tsi-name');
|
|
42777
|
+
nameSpan.html('');
|
|
42778
|
+
Utils.appendFormattedElementsFromString(nameSpan, owner.instanceNodeStringToDisplay(entry.item));
|
|
42779
|
+
}
|
|
42780
|
+
entry.item.node = li;
|
|
42781
|
+
if (entry.item.children) {
|
|
42782
|
+
entry.item.isExpanded = true;
|
|
42783
|
+
li.classed('tsi-expanded', true);
|
|
42784
|
+
// recurse using TreeRenderer to keep rendering logic centralized
|
|
42785
|
+
TreeRenderer.render(owner, entry.item.children, li);
|
|
42786
|
+
}
|
|
42787
|
+
else {
|
|
42788
|
+
li.classed('tsi-expanded', false);
|
|
42789
|
+
li.selectAll('ul').remove();
|
|
42790
|
+
}
|
|
42791
|
+
});
|
|
42792
|
+
}
|
|
42793
|
+
}
|
|
42794
|
+
|
|
43788
42795
|
class HierarchyNavigation extends Component {
|
|
43789
42796
|
constructor(renderTarget) {
|
|
43790
42797
|
super(renderTarget);
|
|
43791
42798
|
this.path = [];
|
|
42799
|
+
// debounce + request cancellation fields
|
|
42800
|
+
this.debounceTimer = null;
|
|
42801
|
+
this.debounceDelay = 250; // ms
|
|
42802
|
+
this.requestCounter = 0; // increments for each outgoing request
|
|
42803
|
+
this.latestRequestId = 0; // id of the most recent request
|
|
43792
42804
|
//selectedIds
|
|
43793
42805
|
this.selectedIds = [];
|
|
43794
42806
|
this.searchEnabled = true;
|
|
43795
|
-
this.
|
|
42807
|
+
this.autocompleteEnabled = true; // Enable/disable autocomplete suggestions
|
|
42808
|
+
// Search mode state
|
|
42809
|
+
this.isSearchMode = false;
|
|
42810
|
+
// Paths that should be auto-expanded (Set of path strings like "Factory North/Building A")
|
|
42811
|
+
this.pathsToAutoExpand = new Set();
|
|
42812
|
+
this.renderSearchResult = async (r, payload, target) => {
|
|
43796
42813
|
const hierarchyData = r.hierarchyNodes?.hits?.length
|
|
43797
42814
|
? this.fillDataRecursively(r.hierarchyNodes, payload, payload)
|
|
43798
42815
|
: {};
|
|
43799
42816
|
const instancesData = r.instances?.hits?.length
|
|
43800
42817
|
? r.instances.hits.reduce((acc, i) => {
|
|
43801
|
-
|
|
42818
|
+
const inst = new InstanceNode(i.timeSeriesId, i.name, payload.path.length - this.path.length, i.id, i.description);
|
|
42819
|
+
inst.displayName = this.instanceNodeStringToDisplay(i) || '';
|
|
42820
|
+
acc[this.instanceNodeIdentifier(i)] = inst;
|
|
43802
42821
|
return acc;
|
|
43803
42822
|
}, {})
|
|
43804
42823
|
: {};
|
|
@@ -43809,7 +42828,17 @@
|
|
|
43809
42828
|
}
|
|
43810
42829
|
hitCountElem.text(r.hierarchyNodes.hitCount);
|
|
43811
42830
|
}
|
|
43812
|
-
|
|
42831
|
+
const merged = { ...hierarchyData, ...instancesData };
|
|
42832
|
+
this.renderTree(merged, target);
|
|
42833
|
+
// Auto-expand nodes that should be expanded and load their children
|
|
42834
|
+
for (const key in hierarchyData) {
|
|
42835
|
+
const node = hierarchyData[key];
|
|
42836
|
+
if (node.isExpanded && !node.children) {
|
|
42837
|
+
// This node should be expanded but doesn't have children loaded yet
|
|
42838
|
+
// We need to trigger expansion after the node is rendered
|
|
42839
|
+
await this.autoExpandNode(node);
|
|
42840
|
+
}
|
|
42841
|
+
}
|
|
43813
42842
|
};
|
|
43814
42843
|
this.hierarchyNodeIdentifier = (hName) => {
|
|
43815
42844
|
return hName ? hName : '(' + this.getString("Empty") + ')';
|
|
@@ -43831,12 +42860,20 @@
|
|
|
43831
42860
|
const targetElement = select(this.renderTarget).text('');
|
|
43832
42861
|
this.hierarchyNavWrapper = this.createHierarchyNavWrapper(targetElement);
|
|
43833
42862
|
this.selectedIds = preselectedIds;
|
|
42863
|
+
// Allow disabling autocomplete via options
|
|
42864
|
+
if (hierarchyNavOptions.autocompleteEnabled !== undefined) {
|
|
42865
|
+
this.autocompleteEnabled = hierarchyNavOptions.autocompleteEnabled;
|
|
42866
|
+
}
|
|
42867
|
+
// Pre-compute paths that need to be auto-expanded for preselected instances
|
|
42868
|
+
if (preselectedIds && preselectedIds.length > 0) {
|
|
42869
|
+
await this.computePathsToAutoExpand(preselectedIds);
|
|
42870
|
+
}
|
|
43834
42871
|
//render search wrapper
|
|
43835
|
-
|
|
42872
|
+
this.renderSearchBox();
|
|
43836
42873
|
super.themify(this.hierarchyNavWrapper, this.chartOptions.theme);
|
|
43837
42874
|
const results = this.createResultsWrapper(this.hierarchyNavWrapper);
|
|
43838
42875
|
this.hierarchyElem = this.createHierarchyElem(results);
|
|
43839
|
-
this.pathSearchAndRenderResult({ search: { payload: this.requestPayload() }, render: { target: this.hierarchyElem } });
|
|
42876
|
+
await this.pathSearchAndRenderResult({ search: { payload: this.requestPayload() }, render: { target: this.hierarchyElem } });
|
|
43840
42877
|
}
|
|
43841
42878
|
createHierarchyNavWrapper(targetElement) {
|
|
43842
42879
|
return targetElement.append('div').attr('class', 'tsi-hierarchy-nav-wrapper');
|
|
@@ -43844,8 +42881,129 @@
|
|
|
43844
42881
|
createResultsWrapper(hierarchyNavWrapper) {
|
|
43845
42882
|
return hierarchyNavWrapper.append('div').classed('tsi-hierarchy-or-list-wrapper', true);
|
|
43846
42883
|
}
|
|
42884
|
+
// create hierarchy container and attach keyboard handler
|
|
43847
42885
|
createHierarchyElem(results) {
|
|
43848
|
-
|
|
42886
|
+
const sel = results.append('div').classed('tsi-hierarchy', true).attr("role", "navigation").on('scroll', () => { });
|
|
42887
|
+
// attach keydown listener for keyboard navigation (delegated)
|
|
42888
|
+
// use native event to preserve focus handling
|
|
42889
|
+
const node = sel.node();
|
|
42890
|
+
if (node) {
|
|
42891
|
+
node.addEventListener('keydown', (ev) => this.onKeyDown(ev));
|
|
42892
|
+
}
|
|
42893
|
+
return sel;
|
|
42894
|
+
}
|
|
42895
|
+
// Keyboard navigation handlers and helpers
|
|
42896
|
+
onKeyDown(ev) {
|
|
42897
|
+
const key = ev.key;
|
|
42898
|
+
const active = document.activeElement;
|
|
42899
|
+
const container = this.hierarchyElem?.node();
|
|
42900
|
+
if (!container)
|
|
42901
|
+
return;
|
|
42902
|
+
const isInside = active && container.contains(active);
|
|
42903
|
+
if (!isInside && (key === 'ArrowDown' || key === 'ArrowUp')) {
|
|
42904
|
+
// focus first visible item on navigation keys
|
|
42905
|
+
const visible = this.getVisibleItemElems();
|
|
42906
|
+
if (visible.length) {
|
|
42907
|
+
this.focusItem(visible[0]);
|
|
42908
|
+
ev.preventDefault();
|
|
42909
|
+
}
|
|
42910
|
+
return;
|
|
42911
|
+
}
|
|
42912
|
+
if (!active)
|
|
42913
|
+
return;
|
|
42914
|
+
const current = active.classList && active.classList.contains('tsi-hierarchyItem') ? active : active.closest('.tsi-hierarchyItem');
|
|
42915
|
+
if (!current)
|
|
42916
|
+
return;
|
|
42917
|
+
switch (key) {
|
|
42918
|
+
case 'ArrowDown':
|
|
42919
|
+
this.focusNext(current);
|
|
42920
|
+
ev.preventDefault();
|
|
42921
|
+
break;
|
|
42922
|
+
case 'ArrowUp':
|
|
42923
|
+
this.focusPrev(current);
|
|
42924
|
+
ev.preventDefault();
|
|
42925
|
+
break;
|
|
42926
|
+
case 'ArrowRight':
|
|
42927
|
+
this.handleArrowRight(current);
|
|
42928
|
+
ev.preventDefault();
|
|
42929
|
+
break;
|
|
42930
|
+
case 'ArrowLeft':
|
|
42931
|
+
this.handleArrowLeft(current);
|
|
42932
|
+
ev.preventDefault();
|
|
42933
|
+
break;
|
|
42934
|
+
case 'Enter':
|
|
42935
|
+
case ' ':
|
|
42936
|
+
// activate (toggle expand or select)
|
|
42937
|
+
current.click();
|
|
42938
|
+
ev.preventDefault();
|
|
42939
|
+
break;
|
|
42940
|
+
}
|
|
42941
|
+
}
|
|
42942
|
+
getVisibleItemElems() {
|
|
42943
|
+
if (!this.hierarchyElem)
|
|
42944
|
+
return [];
|
|
42945
|
+
const root = this.hierarchyElem.node();
|
|
42946
|
+
if (!root)
|
|
42947
|
+
return [];
|
|
42948
|
+
const items = Array.from(root.querySelectorAll('.tsi-hierarchyItem'));
|
|
42949
|
+
return items.filter(i => i.offsetParent !== null && getComputedStyle(i).display !== 'none');
|
|
42950
|
+
}
|
|
42951
|
+
focusItem(elem) {
|
|
42952
|
+
if (!this.hierarchyElem)
|
|
42953
|
+
return;
|
|
42954
|
+
const root = this.hierarchyElem.node();
|
|
42955
|
+
if (!root)
|
|
42956
|
+
return;
|
|
42957
|
+
const items = Array.from(root.querySelectorAll('.tsi-hierarchyItem'));
|
|
42958
|
+
items.forEach(i => i.setAttribute('tabindex', '-1'));
|
|
42959
|
+
elem.setAttribute('tabindex', '0');
|
|
42960
|
+
elem.focus();
|
|
42961
|
+
}
|
|
42962
|
+
focusNext(current) {
|
|
42963
|
+
const visible = this.getVisibleItemElems();
|
|
42964
|
+
const idx = visible.indexOf(current);
|
|
42965
|
+
if (idx >= 0 && idx < visible.length - 1) {
|
|
42966
|
+
this.focusItem(visible[idx + 1]);
|
|
42967
|
+
}
|
|
42968
|
+
}
|
|
42969
|
+
focusPrev(current) {
|
|
42970
|
+
const visible = this.getVisibleItemElems();
|
|
42971
|
+
const idx = visible.indexOf(current);
|
|
42972
|
+
if (idx > 0) {
|
|
42973
|
+
this.focusItem(visible[idx - 1]);
|
|
42974
|
+
}
|
|
42975
|
+
}
|
|
42976
|
+
handleArrowRight(current) {
|
|
42977
|
+
const caret = current.querySelector('.tsi-caret-icon');
|
|
42978
|
+
const expanded = current.getAttribute('aria-expanded') === 'true';
|
|
42979
|
+
if (caret && !expanded) {
|
|
42980
|
+
// expand
|
|
42981
|
+
current.click();
|
|
42982
|
+
return;
|
|
42983
|
+
}
|
|
42984
|
+
// if already expanded, move to first child
|
|
42985
|
+
if (caret && expanded) {
|
|
42986
|
+
const li = current.closest('li');
|
|
42987
|
+
const childLi = li?.querySelector('ul > li');
|
|
42988
|
+
const childItem = childLi?.querySelector('.tsi-hierarchyItem');
|
|
42989
|
+
if (childItem)
|
|
42990
|
+
this.focusItem(childItem);
|
|
42991
|
+
}
|
|
42992
|
+
}
|
|
42993
|
+
handleArrowLeft(current) {
|
|
42994
|
+
const caret = current.querySelector('.tsi-caret-icon');
|
|
42995
|
+
const expanded = current.getAttribute('aria-expanded') === 'true';
|
|
42996
|
+
if (caret && expanded) {
|
|
42997
|
+
// collapse
|
|
42998
|
+
current.click();
|
|
42999
|
+
return;
|
|
43000
|
+
}
|
|
43001
|
+
// move focus to parent
|
|
43002
|
+
const li = current.closest('li');
|
|
43003
|
+
const parentLi = li?.parentElement?.closest('li');
|
|
43004
|
+
const parentItem = parentLi?.querySelector('.tsi-hierarchyItem');
|
|
43005
|
+
if (parentItem)
|
|
43006
|
+
this.focusItem(parentItem);
|
|
43849
43007
|
}
|
|
43850
43008
|
// prepares the parameters for search request
|
|
43851
43009
|
requestPayload(hierarchy = null) {
|
|
@@ -43854,32 +43012,7 @@
|
|
|
43854
43012
|
}
|
|
43855
43013
|
// renders tree for both 'Navigate' and 'Filter' mode (with Hierarchy View option selected), locInTarget refers to the 'show more' element -either hierarchy or instance- within the target
|
|
43856
43014
|
renderTree(data, target) {
|
|
43857
|
-
|
|
43858
|
-
Object.keys(data).forEach(el => {
|
|
43859
|
-
let nodeNameToCheckIfExists = data[el] instanceof InstanceNode ? this.instanceNodeString(data[el]) : el;
|
|
43860
|
-
let li;
|
|
43861
|
-
if (list.selectAll(".tsi-name").nodes().find(e => e.innerText === nodeNameToCheckIfExists)) {
|
|
43862
|
-
li = null;
|
|
43863
|
-
}
|
|
43864
|
-
else {
|
|
43865
|
-
li = list.append('li').classed('tsi-leaf', data[el].isLeaf);
|
|
43866
|
-
//if the node is already selected, we want to highlight it
|
|
43867
|
-
if (this.selectedIds && this.selectedIds.includes(data[el].id)) {
|
|
43868
|
-
li.classed('tsi-selected', true);
|
|
43869
|
-
}
|
|
43870
|
-
}
|
|
43871
|
-
if (!li)
|
|
43872
|
-
return;
|
|
43873
|
-
li.attr("role", "none");
|
|
43874
|
-
let newListElem = this.createHierarchyItemElem(data[el], el);
|
|
43875
|
-
li.node().appendChild(newListElem.node());
|
|
43876
|
-
data[el].node = li;
|
|
43877
|
-
if (data[el].children) {
|
|
43878
|
-
data[el].isExpanded = true;
|
|
43879
|
-
data[el].node.classed('tsi-expanded', true);
|
|
43880
|
-
this.renderTree(data[el].children, data[el].node);
|
|
43881
|
-
}
|
|
43882
|
-
});
|
|
43015
|
+
TreeRenderer.render(this, data, target);
|
|
43883
43016
|
}
|
|
43884
43017
|
renderSearchBox() {
|
|
43885
43018
|
this.searchWrapperElem = this.hierarchyNavWrapper.append('div').classed('tsi-hierarchy-search', true);
|
|
@@ -43888,40 +43021,140 @@
|
|
|
43888
43021
|
let input = inputWrapper
|
|
43889
43022
|
.append("input")
|
|
43890
43023
|
.attr("class", "tsi-searchInput")
|
|
43891
|
-
.attr("aria-label", this.getString("Search
|
|
43892
|
-
.attr("aria-describedby", "tsi-search-desc")
|
|
43024
|
+
.attr("aria-label", this.getString("Search"))
|
|
43025
|
+
.attr("aria-describedby", "tsi-hierarchy-search-desc")
|
|
43893
43026
|
.attr("role", "combobox")
|
|
43894
43027
|
.attr("aria-owns", "tsi-search-results")
|
|
43895
43028
|
.attr("aria-expanded", "false")
|
|
43896
43029
|
.attr("aria-haspopup", "listbox")
|
|
43897
|
-
.attr("placeholder", this.getString("Search
|
|
43030
|
+
.attr("placeholder", this.getString("Search") + "...");
|
|
43031
|
+
// Add ARIA description for screen readers
|
|
43032
|
+
inputWrapper
|
|
43033
|
+
.append("span")
|
|
43034
|
+
.attr("id", "tsi-hierarchy-search-desc")
|
|
43035
|
+
.style("display", "none")
|
|
43036
|
+
.text(this.getString("Search suggestion instruction") || "Use arrow keys to navigate suggestions");
|
|
43037
|
+
// Add live region for search results info
|
|
43038
|
+
inputWrapper
|
|
43039
|
+
.append("div")
|
|
43040
|
+
.attr("class", "tsi-search-results-info")
|
|
43041
|
+
.attr("aria-live", "assertive");
|
|
43042
|
+
// Add clear button
|
|
43043
|
+
let clear = inputWrapper
|
|
43044
|
+
.append("div")
|
|
43045
|
+
.attr("class", "tsi-clear")
|
|
43046
|
+
.attr("tabindex", "0")
|
|
43047
|
+
.attr("role", "button")
|
|
43048
|
+
.attr("aria-label", "Clear Search")
|
|
43049
|
+
.on("click keydown", function (event) {
|
|
43050
|
+
if (Utils.isKeyDownAndNotEnter(event)) {
|
|
43051
|
+
return;
|
|
43052
|
+
}
|
|
43053
|
+
input.node().value = "";
|
|
43054
|
+
self.exitSearchMode();
|
|
43055
|
+
self.ap.close();
|
|
43056
|
+
select(this).classed("tsi-shown", false);
|
|
43057
|
+
});
|
|
43058
|
+
// Initialize Awesomplete for autocomplete (only if enabled)
|
|
43059
|
+
let Awesomplete = window.Awesomplete;
|
|
43060
|
+
if (this.autocompleteEnabled && Awesomplete) {
|
|
43061
|
+
this.ap = new Awesomplete(input.node(), {
|
|
43062
|
+
minChars: 1,
|
|
43063
|
+
maxItems: 10,
|
|
43064
|
+
autoFirst: true
|
|
43065
|
+
});
|
|
43066
|
+
}
|
|
43067
|
+
else {
|
|
43068
|
+
// Create a dummy object if autocomplete is disabled
|
|
43069
|
+
this.ap = {
|
|
43070
|
+
list: [],
|
|
43071
|
+
close: () => { },
|
|
43072
|
+
evaluate: () => { }
|
|
43073
|
+
};
|
|
43074
|
+
}
|
|
43898
43075
|
let self = this;
|
|
43076
|
+
let noSuggest = false;
|
|
43077
|
+
let justAwesompleted = false;
|
|
43078
|
+
// Handle autocomplete selection (only if enabled)
|
|
43079
|
+
if (this.autocompleteEnabled) {
|
|
43080
|
+
input.node().addEventListener("awesomplete-selectcomplete", (event) => {
|
|
43081
|
+
noSuggest = true;
|
|
43082
|
+
const selectedValue = event.text.value;
|
|
43083
|
+
// Trigger search with selected value
|
|
43084
|
+
self.performDeepSearch(selectedValue);
|
|
43085
|
+
justAwesompleted = true;
|
|
43086
|
+
});
|
|
43087
|
+
}
|
|
43899
43088
|
input.on("keydown", (event) => {
|
|
43089
|
+
// Handle ESC key to clear the search box
|
|
43090
|
+
if (event.key === 'Escape') {
|
|
43091
|
+
const inputElement = event.target;
|
|
43092
|
+
inputElement.value = '';
|
|
43093
|
+
// Trigger input event to clear search results
|
|
43094
|
+
self.exitSearchMode();
|
|
43095
|
+
self.ap.close();
|
|
43096
|
+
clear.classed("tsi-shown", false);
|
|
43097
|
+
return;
|
|
43098
|
+
}
|
|
43900
43099
|
this.chartOptions.onKeydown(event, this.ap);
|
|
43901
43100
|
});
|
|
43902
|
-
|
|
43101
|
+
input.node().addEventListener("keyup", function (event) {
|
|
43102
|
+
if (justAwesompleted) {
|
|
43103
|
+
justAwesompleted = false;
|
|
43104
|
+
return;
|
|
43105
|
+
}
|
|
43106
|
+
let key = event.which || event.keyCode;
|
|
43107
|
+
if (key === 13) {
|
|
43108
|
+
noSuggest = true;
|
|
43109
|
+
}
|
|
43110
|
+
});
|
|
43111
|
+
// Debounced input handler to reduce work while typing
|
|
43903
43112
|
input.on("input", function (event) {
|
|
43904
|
-
|
|
43905
|
-
|
|
43906
|
-
|
|
43907
|
-
self.
|
|
43908
|
-
self.
|
|
43113
|
+
const val = event.target.value;
|
|
43114
|
+
// always clear existing timer
|
|
43115
|
+
if (self.debounceTimer) {
|
|
43116
|
+
clearTimeout(self.debounceTimer);
|
|
43117
|
+
self.debounceTimer = null;
|
|
43118
|
+
}
|
|
43119
|
+
// Show/hide clear button
|
|
43120
|
+
clear.classed("tsi-shown", val.length > 0);
|
|
43121
|
+
if (!val || val.length === 0) {
|
|
43122
|
+
// Exit search mode and restore navigation view
|
|
43123
|
+
self.exitSearchMode();
|
|
43124
|
+
self.ap.close();
|
|
43125
|
+
return;
|
|
43126
|
+
}
|
|
43127
|
+
// Populate autocomplete suggestions with instance leaves (only if enabled)
|
|
43128
|
+
if (self.autocompleteEnabled && !noSuggest && val.length >= 1) {
|
|
43129
|
+
self.fetchAutocompleteSuggestions(val);
|
|
43909
43130
|
}
|
|
43910
43131
|
else {
|
|
43911
|
-
|
|
43912
|
-
self.filterTree(searchText);
|
|
43132
|
+
self.ap.close();
|
|
43913
43133
|
}
|
|
43134
|
+
// Use deep search for comprehensive results
|
|
43135
|
+
self.debounceTimer = setTimeout(() => {
|
|
43136
|
+
self.performDeepSearch(val);
|
|
43137
|
+
}, self.debounceDelay);
|
|
43138
|
+
noSuggest = false;
|
|
43914
43139
|
});
|
|
43915
43140
|
}
|
|
43916
43141
|
async pathSearchAndRenderResult({ search: { payload, bubbleUpReject = false }, render: { target, locInTarget = null } }) {
|
|
43142
|
+
const requestId = ++this.requestCounter;
|
|
43143
|
+
this.latestRequestId = requestId;
|
|
43917
43144
|
try {
|
|
43918
43145
|
const result = await this.searchFunction(payload);
|
|
43146
|
+
if (requestId !== this.latestRequestId) {
|
|
43147
|
+
return;
|
|
43148
|
+
}
|
|
43919
43149
|
if (result.error) {
|
|
43920
43150
|
throw result.error;
|
|
43921
43151
|
}
|
|
43922
|
-
this.renderSearchResult(result, payload, target);
|
|
43152
|
+
await this.renderSearchResult(result, payload, target);
|
|
43923
43153
|
}
|
|
43924
43154
|
catch (err) {
|
|
43155
|
+
if (requestId !== this.latestRequestId) {
|
|
43156
|
+
return;
|
|
43157
|
+
}
|
|
43925
43158
|
this.chartOptions.onError("Error in hierarchy navigation", "Failed to complete search", err instanceof XMLHttpRequest ? err : null);
|
|
43926
43159
|
if (bubbleUpReject) {
|
|
43927
43160
|
throw err;
|
|
@@ -43929,11 +43162,18 @@
|
|
|
43929
43162
|
}
|
|
43930
43163
|
}
|
|
43931
43164
|
filterTree(searchText) {
|
|
43932
|
-
|
|
43933
|
-
|
|
43165
|
+
const nodes = this.hierarchyElem.selectAll('ul').nodes();
|
|
43166
|
+
if (!nodes || !nodes.length)
|
|
43167
|
+
return;
|
|
43168
|
+
const tree = nodes[0];
|
|
43169
|
+
if (!tree)
|
|
43170
|
+
return;
|
|
43171
|
+
const list = tree.querySelectorAll('li');
|
|
43172
|
+
const needle = searchText.toLowerCase();
|
|
43934
43173
|
list.forEach((li) => {
|
|
43935
|
-
|
|
43936
|
-
|
|
43174
|
+
const attrName = li.getAttribute('data-display-name');
|
|
43175
|
+
let name = attrName && attrName.length ? attrName : (li.querySelector('.tsi-name')?.textContent || '');
|
|
43176
|
+
if (name.toLowerCase().includes(needle)) {
|
|
43937
43177
|
li.style.display = 'block';
|
|
43938
43178
|
}
|
|
43939
43179
|
else {
|
|
@@ -43941,11 +43181,300 @@
|
|
|
43941
43181
|
}
|
|
43942
43182
|
});
|
|
43943
43183
|
}
|
|
43184
|
+
// Fetch autocomplete suggestions for instances (leaves)
|
|
43185
|
+
async fetchAutocompleteSuggestions(searchText) {
|
|
43186
|
+
if (!searchText || searchText.length < 1) {
|
|
43187
|
+
this.ap.list = [];
|
|
43188
|
+
return;
|
|
43189
|
+
}
|
|
43190
|
+
try {
|
|
43191
|
+
// Call server search to get instance suggestions
|
|
43192
|
+
const payload = {
|
|
43193
|
+
path: this.path,
|
|
43194
|
+
searchTerm: searchText,
|
|
43195
|
+
recursive: true,
|
|
43196
|
+
includeInstances: true,
|
|
43197
|
+
// Limit results for autocomplete
|
|
43198
|
+
maxResults: 10
|
|
43199
|
+
};
|
|
43200
|
+
const results = await this.searchFunction(payload);
|
|
43201
|
+
if (results.error) {
|
|
43202
|
+
this.ap.list = [];
|
|
43203
|
+
return;
|
|
43204
|
+
}
|
|
43205
|
+
// Extract instance names for autocomplete suggestions
|
|
43206
|
+
const suggestions = [];
|
|
43207
|
+
if (results.instances?.hits) {
|
|
43208
|
+
results.instances.hits.forEach((i) => {
|
|
43209
|
+
const displayName = this.instanceNodeStringToDisplay(i);
|
|
43210
|
+
const pathStr = i.hierarchyPath && i.hierarchyPath.length > 0
|
|
43211
|
+
? i.hierarchyPath.join(' > ') + ' > '
|
|
43212
|
+
: '';
|
|
43213
|
+
suggestions.push({
|
|
43214
|
+
label: pathStr + displayName,
|
|
43215
|
+
value: displayName
|
|
43216
|
+
});
|
|
43217
|
+
});
|
|
43218
|
+
}
|
|
43219
|
+
// Update Awesomplete list
|
|
43220
|
+
this.ap.list = suggestions;
|
|
43221
|
+
}
|
|
43222
|
+
catch (err) {
|
|
43223
|
+
// Silently fail for autocomplete - don't interrupt user experience
|
|
43224
|
+
this.ap.list = [];
|
|
43225
|
+
}
|
|
43226
|
+
}
|
|
43227
|
+
// Perform deep search across entire hierarchy using server-side search
|
|
43228
|
+
async performDeepSearch(searchText) {
|
|
43229
|
+
if (!searchText || searchText.length < 2) {
|
|
43230
|
+
this.exitSearchMode();
|
|
43231
|
+
return;
|
|
43232
|
+
}
|
|
43233
|
+
this.isSearchMode = true;
|
|
43234
|
+
const requestId = ++this.requestCounter;
|
|
43235
|
+
this.latestRequestId = requestId;
|
|
43236
|
+
try {
|
|
43237
|
+
// Call server search with recursive flag
|
|
43238
|
+
const payload = {
|
|
43239
|
+
path: this.path,
|
|
43240
|
+
searchTerm: searchText,
|
|
43241
|
+
recursive: true, // Search entire subtree
|
|
43242
|
+
includeInstances: true
|
|
43243
|
+
};
|
|
43244
|
+
const results = await this.searchFunction(payload);
|
|
43245
|
+
if (requestId !== this.latestRequestId)
|
|
43246
|
+
return; // Stale request
|
|
43247
|
+
if (results.error) {
|
|
43248
|
+
throw results.error;
|
|
43249
|
+
}
|
|
43250
|
+
// Render search results in flat list view
|
|
43251
|
+
this.renderSearchResults(results, searchText);
|
|
43252
|
+
}
|
|
43253
|
+
catch (err) {
|
|
43254
|
+
if (requestId !== this.latestRequestId)
|
|
43255
|
+
return;
|
|
43256
|
+
this.chartOptions.onError("Search failed", "Unable to search hierarchy", err instanceof XMLHttpRequest ? err : null);
|
|
43257
|
+
}
|
|
43258
|
+
}
|
|
43259
|
+
// Render search results with breadcrumb paths
|
|
43260
|
+
renderSearchResults(results, searchText) {
|
|
43261
|
+
this.hierarchyElem.selectAll('*').remove();
|
|
43262
|
+
const flatResults = [];
|
|
43263
|
+
// Flatten hierarchy results with full paths
|
|
43264
|
+
if (results.hierarchyNodes?.hits) {
|
|
43265
|
+
results.hierarchyNodes.hits.forEach((h) => {
|
|
43266
|
+
flatResults.push({
|
|
43267
|
+
type: 'hierarchy',
|
|
43268
|
+
name: h.name,
|
|
43269
|
+
path: h.path || [],
|
|
43270
|
+
id: h.id,
|
|
43271
|
+
cumulativeInstanceCount: h.cumulativeInstanceCount,
|
|
43272
|
+
highlightedName: this.highlightMatch(h.name, searchText),
|
|
43273
|
+
node: h
|
|
43274
|
+
});
|
|
43275
|
+
});
|
|
43276
|
+
}
|
|
43277
|
+
// Flatten instance results with full paths
|
|
43278
|
+
if (results.instances?.hits) {
|
|
43279
|
+
results.instances.hits.forEach((i) => {
|
|
43280
|
+
const displayName = this.instanceNodeStringToDisplay(i);
|
|
43281
|
+
flatResults.push({
|
|
43282
|
+
type: 'instance',
|
|
43283
|
+
name: i.name,
|
|
43284
|
+
path: i.hierarchyPath || [],
|
|
43285
|
+
id: i.id,
|
|
43286
|
+
timeSeriesId: i.timeSeriesId,
|
|
43287
|
+
description: i.description,
|
|
43288
|
+
highlightedName: this.highlightMatch(displayName, searchText),
|
|
43289
|
+
node: i
|
|
43290
|
+
});
|
|
43291
|
+
});
|
|
43292
|
+
}
|
|
43293
|
+
// Render flat list with breadcrumbs
|
|
43294
|
+
const searchList = this.hierarchyElem
|
|
43295
|
+
.append('div')
|
|
43296
|
+
.classed('tsi-search-results', true);
|
|
43297
|
+
if (flatResults.length === 0) {
|
|
43298
|
+
searchList.append('div')
|
|
43299
|
+
.classed('tsi-noResults', true)
|
|
43300
|
+
.text(this.getString('No results'));
|
|
43301
|
+
return;
|
|
43302
|
+
}
|
|
43303
|
+
searchList.append('div')
|
|
43304
|
+
.classed('tsi-search-results-header', true)
|
|
43305
|
+
.html(`<strong>${flatResults.length}</strong> ${this.getString('results found') || 'results found'}`);
|
|
43306
|
+
const resultItems = searchList.selectAll('.tsi-search-result-item')
|
|
43307
|
+
.data(flatResults)
|
|
43308
|
+
.enter()
|
|
43309
|
+
.append('div')
|
|
43310
|
+
.classed('tsi-search-result-item', true)
|
|
43311
|
+
.attr('tabindex', '0')
|
|
43312
|
+
.attr('role', 'option')
|
|
43313
|
+
.attr('aria-label', (d) => {
|
|
43314
|
+
const pathStr = d.path.length > 0 ? d.path.join(' > ') + ' > ' : '';
|
|
43315
|
+
return pathStr + d.name;
|
|
43316
|
+
});
|
|
43317
|
+
const self = this;
|
|
43318
|
+
resultItems.each(function (d) {
|
|
43319
|
+
const item = select(this);
|
|
43320
|
+
// Breadcrumb path
|
|
43321
|
+
if (d.path.length > 0) {
|
|
43322
|
+
item.append('div')
|
|
43323
|
+
.classed('tsi-search-breadcrumb', true)
|
|
43324
|
+
.text(d.path.join(' > '));
|
|
43325
|
+
}
|
|
43326
|
+
// Highlighted name
|
|
43327
|
+
item.append('div')
|
|
43328
|
+
.classed('tsi-search-result-name', true)
|
|
43329
|
+
.html(d.highlightedName);
|
|
43330
|
+
// Instance description or count
|
|
43331
|
+
if (d.type === 'instance' && d.description) {
|
|
43332
|
+
item.append('div')
|
|
43333
|
+
.classed('tsi-search-result-description', true)
|
|
43334
|
+
.text(d.description);
|
|
43335
|
+
}
|
|
43336
|
+
else if (d.type === 'hierarchy') {
|
|
43337
|
+
item.append('div')
|
|
43338
|
+
.classed('tsi-search-result-count', true)
|
|
43339
|
+
.text(`${d.cumulativeInstanceCount || 0} instances`);
|
|
43340
|
+
}
|
|
43341
|
+
});
|
|
43342
|
+
// Click handlers
|
|
43343
|
+
resultItems.on('click keydown', function (event, d) {
|
|
43344
|
+
if (Utils.isKeyDownAndNotEnter(event))
|
|
43345
|
+
return;
|
|
43346
|
+
if (d.type === 'instance') {
|
|
43347
|
+
// Handle instance selection
|
|
43348
|
+
if (self.chartOptions.onInstanceClick) {
|
|
43349
|
+
const inst = new InstanceNode(d.timeSeriesId, d.name, d.path.length, d.id, d.description);
|
|
43350
|
+
// Update selection state
|
|
43351
|
+
if (self.selectedIds && self.selectedIds.includes(d.id)) {
|
|
43352
|
+
self.selectedIds = self.selectedIds.filter(id => id !== d.id);
|
|
43353
|
+
select(this).classed('tsi-selected', false);
|
|
43354
|
+
}
|
|
43355
|
+
else {
|
|
43356
|
+
self.selectedIds.push(d.id);
|
|
43357
|
+
select(this).classed('tsi-selected', true);
|
|
43358
|
+
}
|
|
43359
|
+
self.chartOptions.onInstanceClick(inst);
|
|
43360
|
+
}
|
|
43361
|
+
}
|
|
43362
|
+
else {
|
|
43363
|
+
// Navigate to hierarchy node - exit search and expand to that path
|
|
43364
|
+
self.navigateToPath(d.path);
|
|
43365
|
+
}
|
|
43366
|
+
});
|
|
43367
|
+
// Apply selection state to already-selected instances
|
|
43368
|
+
resultItems.each(function (d) {
|
|
43369
|
+
if (d.type === 'instance' && self.selectedIds && self.selectedIds.includes(d.id)) {
|
|
43370
|
+
select(this).classed('tsi-selected', true);
|
|
43371
|
+
}
|
|
43372
|
+
});
|
|
43373
|
+
}
|
|
43374
|
+
// Exit search mode and restore tree
|
|
43375
|
+
exitSearchMode() {
|
|
43376
|
+
this.isSearchMode = false;
|
|
43377
|
+
this.hierarchyElem.selectAll('*').remove();
|
|
43378
|
+
this.pathSearchAndRenderResult({
|
|
43379
|
+
search: { payload: this.requestPayload() },
|
|
43380
|
+
render: { target: this.hierarchyElem }
|
|
43381
|
+
});
|
|
43382
|
+
}
|
|
43383
|
+
// Navigate to a specific path in the hierarchy
|
|
43384
|
+
async navigateToPath(targetPath) {
|
|
43385
|
+
this.exitSearchMode();
|
|
43386
|
+
// For now, just exit search mode and return to root
|
|
43387
|
+
// In a more advanced implementation, this would progressively
|
|
43388
|
+
// expand nodes along the path to reveal the target
|
|
43389
|
+
// This would require waiting for each level to load before expanding the next
|
|
43390
|
+
}
|
|
43391
|
+
// Pre-compute which paths need to be auto-expanded for preselected instances
|
|
43392
|
+
async computePathsToAutoExpand(instanceIds) {
|
|
43393
|
+
if (!instanceIds || instanceIds.length === 0) {
|
|
43394
|
+
return;
|
|
43395
|
+
}
|
|
43396
|
+
// console.log('[HierarchyNavigation] Computing paths to auto-expand for:', instanceIds);
|
|
43397
|
+
try {
|
|
43398
|
+
this.pathsToAutoExpand.clear();
|
|
43399
|
+
for (const instanceId of instanceIds) {
|
|
43400
|
+
// Search for this specific instance
|
|
43401
|
+
const result = await this.searchFunction({
|
|
43402
|
+
path: this.path,
|
|
43403
|
+
searchTerm: instanceId,
|
|
43404
|
+
recursive: true,
|
|
43405
|
+
includeInstances: true
|
|
43406
|
+
});
|
|
43407
|
+
if (result?.instances?.hits) {
|
|
43408
|
+
for (const instance of result.instances.hits) {
|
|
43409
|
+
// Match by ID
|
|
43410
|
+
if (instance.id === instanceId ||
|
|
43411
|
+
(instance.id && instance.id.includes(instanceId))) {
|
|
43412
|
+
if (instance.hierarchyPath && instance.hierarchyPath.length > 0) {
|
|
43413
|
+
// Add all parent paths that need to be expanded
|
|
43414
|
+
const hierarchyPath = instance.hierarchyPath;
|
|
43415
|
+
for (let i = 1; i <= hierarchyPath.length; i++) {
|
|
43416
|
+
const pathArray = hierarchyPath.slice(0, i);
|
|
43417
|
+
const pathKey = pathArray.join('/');
|
|
43418
|
+
this.pathsToAutoExpand.add(pathKey);
|
|
43419
|
+
}
|
|
43420
|
+
}
|
|
43421
|
+
}
|
|
43422
|
+
}
|
|
43423
|
+
}
|
|
43424
|
+
}
|
|
43425
|
+
// console.log('[HierarchyNavigation] Paths to auto-expand:', Array.from(this.pathsToAutoExpand));
|
|
43426
|
+
}
|
|
43427
|
+
catch (err) {
|
|
43428
|
+
console.warn('Failed to compute paths to auto-expand:', err);
|
|
43429
|
+
}
|
|
43430
|
+
}
|
|
43431
|
+
// Check if a path should be auto-expanded
|
|
43432
|
+
shouldAutoExpand(pathArray) {
|
|
43433
|
+
if (this.pathsToAutoExpand.size === 0) {
|
|
43434
|
+
return false;
|
|
43435
|
+
}
|
|
43436
|
+
const pathKey = pathArray.join('/');
|
|
43437
|
+
return this.pathsToAutoExpand.has(pathKey);
|
|
43438
|
+
}
|
|
43439
|
+
// Auto-expand a node by triggering its expand function
|
|
43440
|
+
async autoExpandNode(node) {
|
|
43441
|
+
if (!node || !node.expand || !node.node) {
|
|
43442
|
+
return;
|
|
43443
|
+
}
|
|
43444
|
+
try {
|
|
43445
|
+
// Wait for the DOM node to be available
|
|
43446
|
+
await new Promise(resolve => setTimeout(resolve, 10));
|
|
43447
|
+
// Mark as expanded visually
|
|
43448
|
+
node.node.classed('tsi-expanded', true);
|
|
43449
|
+
// Call the expand function to load children
|
|
43450
|
+
await node.expand();
|
|
43451
|
+
// console.log(`[HierarchyNavigation] Auto-expanded node: ${node.path.join('/')}`);
|
|
43452
|
+
}
|
|
43453
|
+
catch (err) {
|
|
43454
|
+
console.warn(`Failed to auto-expand node ${node.path.join('/')}:`, err);
|
|
43455
|
+
}
|
|
43456
|
+
}
|
|
43457
|
+
// Highlight search term in text
|
|
43458
|
+
highlightMatch(text, searchTerm) {
|
|
43459
|
+
if (!text)
|
|
43460
|
+
return '';
|
|
43461
|
+
const escapedTerm = searchTerm.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
43462
|
+
const regex = new RegExp(`(${escapedTerm})`, 'gi');
|
|
43463
|
+
return text.replace(regex, '<mark>$1</mark>');
|
|
43464
|
+
}
|
|
43944
43465
|
// creates in-depth data object using the server response for hierarchyNodes to show in the tree all expanded, considering UntilChildren
|
|
43945
43466
|
fillDataRecursively(hierarchyNodes, payload, payloadForContinuation = null) {
|
|
43946
43467
|
let data = {};
|
|
43947
43468
|
hierarchyNodes.hits.forEach((h) => {
|
|
43948
43469
|
let hierarchy = new HierarchyNode(h.name, payload.path, payload.path.length - this.path.length, h.cumulativeInstanceCount, h.id);
|
|
43470
|
+
// cache display name on node for client-side filtering
|
|
43471
|
+
hierarchy.displayName = h.name || '';
|
|
43472
|
+
// Check if this path should be auto-expanded
|
|
43473
|
+
const shouldExpand = this.shouldAutoExpand(hierarchy.path);
|
|
43474
|
+
if (shouldExpand) {
|
|
43475
|
+
hierarchy.isExpanded = true;
|
|
43476
|
+
//console.log(`[HierarchyNavigation] Auto-expanding node: ${hierarchy.path.join('/')}`);
|
|
43477
|
+
}
|
|
43949
43478
|
hierarchy.expand = () => {
|
|
43950
43479
|
hierarchy.isExpanded = true;
|
|
43951
43480
|
hierarchy.node.classed('tsi-expanded', true);
|
|
@@ -43969,7 +43498,7 @@
|
|
|
43969
43498
|
.attr('style', `padding-left: ${hORi.isLeaf ? hORi.level * 18 + 20 : (hORi.level + 1) * 18 + 20}px`)
|
|
43970
43499
|
.attr('tabindex', 0)
|
|
43971
43500
|
//.attr('arialabel', isHierarchyNode ? key : Utils.getTimeSeriesIdString(hORi))
|
|
43972
|
-
.attr('
|
|
43501
|
+
.attr('aria-label', isHierarchyNode ? key : self.getAriaLabel(hORi))
|
|
43973
43502
|
.attr('title', isHierarchyNode ? key : self.getAriaLabel(hORi))
|
|
43974
43503
|
.attr("role", "treeitem").attr('aria-expanded', hORi.isExpanded)
|
|
43975
43504
|
.on('click keydown', async function (event) {
|
|
@@ -44021,6 +43550,8 @@
|
|
|
44021
43550
|
return hORi.description || hORi.name || hORi.id || Utils.getTimeSeriesIdString(hORi);
|
|
44022
43551
|
}
|
|
44023
43552
|
}
|
|
43553
|
+
// TreeRenderer has been moved to its own module: ./TreeRenderer
|
|
43554
|
+
// The rendering logic was extracted to reduce file size and improve testability.
|
|
44024
43555
|
class HierarchyNode {
|
|
44025
43556
|
constructor(name, parentPath, level, cumulativeInstanceCount = null, id = null) {
|
|
44026
43557
|
this.name = name;
|
|
@@ -44265,6 +43796,7 @@
|
|
|
44265
43796
|
class DateTimeButtonSingle extends DateTimeButton {
|
|
44266
43797
|
constructor(renderTarget) {
|
|
44267
43798
|
super(renderTarget);
|
|
43799
|
+
this.clickOutsideHandler = null;
|
|
44268
43800
|
this.sDTPOnSet = (millis = null) => {
|
|
44269
43801
|
if (millis !== null) {
|
|
44270
43802
|
this.dateTimeButton.text(this.buttonDateTimeFormat(millis));
|
|
@@ -44277,6 +43809,32 @@
|
|
|
44277
43809
|
closeSDTP() {
|
|
44278
43810
|
this.dateTimePickerContainer.style("display", "none");
|
|
44279
43811
|
this.dateTimeButton.node().focus();
|
|
43812
|
+
this.removeClickOutsideHandler();
|
|
43813
|
+
}
|
|
43814
|
+
removeClickOutsideHandler() {
|
|
43815
|
+
if (this.clickOutsideHandler) {
|
|
43816
|
+
document.removeEventListener('click', this.clickOutsideHandler);
|
|
43817
|
+
this.clickOutsideHandler = null;
|
|
43818
|
+
}
|
|
43819
|
+
}
|
|
43820
|
+
setupClickOutsideHandler() {
|
|
43821
|
+
// Remove any existing handler first
|
|
43822
|
+
this.removeClickOutsideHandler();
|
|
43823
|
+
// Add handler after a small delay to prevent the opening click from immediately closing the picker
|
|
43824
|
+
setTimeout(() => {
|
|
43825
|
+
this.clickOutsideHandler = (event) => {
|
|
43826
|
+
const pickerElement = this.dateTimePickerContainer.node();
|
|
43827
|
+
const buttonElement = this.dateTimeButton.node();
|
|
43828
|
+
const target = event.target;
|
|
43829
|
+
// Check if click is outside both the picker and the button
|
|
43830
|
+
if (pickerElement && buttonElement &&
|
|
43831
|
+
!pickerElement.contains(target) &&
|
|
43832
|
+
!buttonElement.contains(target)) {
|
|
43833
|
+
this.closeSDTP();
|
|
43834
|
+
}
|
|
43835
|
+
};
|
|
43836
|
+
document.addEventListener('click', this.clickOutsideHandler);
|
|
43837
|
+
}, 0);
|
|
44280
43838
|
}
|
|
44281
43839
|
render(chartOptions = {}, minMillis, maxMillis, selectedMillis = null, onSet = null) {
|
|
44282
43840
|
super.render(chartOptions, minMillis, maxMillis, onSet);
|
|
@@ -44286,12 +43844,11 @@
|
|
|
44286
43844
|
if (!this.dateTimePicker) {
|
|
44287
43845
|
this.dateTimePicker = new SingleDateTimePicker(this.dateTimePickerContainer.node());
|
|
44288
43846
|
}
|
|
44289
|
-
let targetElement = select(this.renderTarget);
|
|
44290
|
-
(targetElement.select(".tsi-dateTimePickerContainer")).selectAll("*");
|
|
44291
43847
|
this.dateTimeButton.on("click", () => {
|
|
44292
43848
|
this.chartOptions.dTPIsModal = true;
|
|
44293
43849
|
this.dateTimePickerContainer.style("display", "block");
|
|
44294
43850
|
this.dateTimePicker.render(this.chartOptions, this.minMillis, this.maxMillis, this.selectedMillis, this.sDTPOnSet);
|
|
43851
|
+
this.setupClickOutsideHandler();
|
|
44295
43852
|
});
|
|
44296
43853
|
}
|
|
44297
43854
|
}
|
|
@@ -44589,8 +44146,16 @@
|
|
|
44589
44146
|
class PlaybackControls extends Component {
|
|
44590
44147
|
constructor(renderTarget, initialTimeStamp = null) {
|
|
44591
44148
|
super(renderTarget);
|
|
44592
|
-
this.
|
|
44593
|
-
this.
|
|
44149
|
+
this.playbackInterval = null;
|
|
44150
|
+
this.playButton = null;
|
|
44151
|
+
this.handleElement = null;
|
|
44152
|
+
this.controlsContainer = null;
|
|
44153
|
+
this.track = null;
|
|
44154
|
+
this.selectTimeStampCallback = null;
|
|
44155
|
+
this.wasPlayingWhenDragStarted = false;
|
|
44156
|
+
this.rafId = null;
|
|
44157
|
+
this.handleRadius = PlaybackControls.CONSTANTS.HANDLE_RADIUS;
|
|
44158
|
+
this.minimumPlaybackInterval = PlaybackControls.CONSTANTS.MINIMUM_PLAYBACK_INTERVAL_MS;
|
|
44594
44159
|
this.playbackInterval = null;
|
|
44595
44160
|
this.selectedTimeStamp = initialTimeStamp;
|
|
44596
44161
|
}
|
|
@@ -44598,6 +44163,21 @@
|
|
|
44598
44163
|
return this.selectedTimeStamp;
|
|
44599
44164
|
}
|
|
44600
44165
|
render(start, end, onSelectTimeStamp, options, playbackSettings) {
|
|
44166
|
+
// Validate inputs
|
|
44167
|
+
if (!(start instanceof Date) || !(end instanceof Date)) {
|
|
44168
|
+
throw new TypeError('start and end must be Date objects');
|
|
44169
|
+
}
|
|
44170
|
+
if (start >= end) {
|
|
44171
|
+
throw new RangeError('start must be before end');
|
|
44172
|
+
}
|
|
44173
|
+
if (!onSelectTimeStamp || typeof onSelectTimeStamp !== 'function') {
|
|
44174
|
+
throw new TypeError('onSelectTimeStamp must be a function');
|
|
44175
|
+
}
|
|
44176
|
+
// Clean up any pending animation frames before re-rendering
|
|
44177
|
+
if (this.rafId !== null) {
|
|
44178
|
+
cancelAnimationFrame(this.rafId);
|
|
44179
|
+
this.rafId = null;
|
|
44180
|
+
}
|
|
44601
44181
|
this.end = end;
|
|
44602
44182
|
this.selectTimeStampCallback = onSelectTimeStamp;
|
|
44603
44183
|
this.chartOptions.setOptions(options);
|
|
@@ -44659,6 +44239,9 @@
|
|
|
44659
44239
|
this.playButton = this.controlsContainer.append('button')
|
|
44660
44240
|
.classed('tsi-play-button', this.playbackInterval === null)
|
|
44661
44241
|
.classed('tsi-pause-button', this.playbackInterval !== null)
|
|
44242
|
+
// Accessibility attributes
|
|
44243
|
+
.attr('aria-label', 'Play/Pause playback')
|
|
44244
|
+
.attr('title', 'Play/Pause playback')
|
|
44662
44245
|
.on('click', () => {
|
|
44663
44246
|
if (this.playbackInterval === null) {
|
|
44664
44247
|
this.play();
|
|
@@ -44710,6 +44293,27 @@
|
|
|
44710
44293
|
this.updateSelection(handlePosition, this.selectedTimeStamp);
|
|
44711
44294
|
this.selectTimeStampCallback(this.selectedTimeStamp);
|
|
44712
44295
|
}
|
|
44296
|
+
/**
|
|
44297
|
+
* Cleanup resources to prevent memory leaks
|
|
44298
|
+
*/
|
|
44299
|
+
destroy() {
|
|
44300
|
+
this.pause();
|
|
44301
|
+
// Cancel any pending animation frames
|
|
44302
|
+
if (this.rafId !== null) {
|
|
44303
|
+
cancelAnimationFrame(this.rafId);
|
|
44304
|
+
this.rafId = null;
|
|
44305
|
+
}
|
|
44306
|
+
// Remove event listeners
|
|
44307
|
+
if (this.controlsContainer) {
|
|
44308
|
+
this.controlsContainer.selectAll('*').on('.', null);
|
|
44309
|
+
}
|
|
44310
|
+
// Clear DOM references
|
|
44311
|
+
this.playButton = null;
|
|
44312
|
+
this.handleElement = null;
|
|
44313
|
+
this.controlsContainer = null;
|
|
44314
|
+
this.track = null;
|
|
44315
|
+
this.selectTimeStampCallback = null;
|
|
44316
|
+
}
|
|
44713
44317
|
clamp(number, min, max) {
|
|
44714
44318
|
let clamped = Math.max(number, min);
|
|
44715
44319
|
return Math.min(clamped, max);
|
|
@@ -44718,9 +44322,17 @@
|
|
|
44718
44322
|
this.wasPlayingWhenDragStarted = this.wasPlayingWhenDragStarted ||
|
|
44719
44323
|
(this.playbackInterval !== null);
|
|
44720
44324
|
this.pause();
|
|
44721
|
-
|
|
44722
|
-
|
|
44723
|
-
this.
|
|
44325
|
+
// Use requestAnimationFrame to batch DOM updates for better performance
|
|
44326
|
+
// Cancel any pending animation frame to prevent stacking updates
|
|
44327
|
+
if (this.rafId !== null) {
|
|
44328
|
+
cancelAnimationFrame(this.rafId);
|
|
44329
|
+
}
|
|
44330
|
+
this.rafId = requestAnimationFrame(() => {
|
|
44331
|
+
const handlePosition = this.clamp(positionX, 0, this.trackWidth);
|
|
44332
|
+
this.selectedTimeStamp = this.timeStampToPosition.invert(handlePosition);
|
|
44333
|
+
this.updateSelection(handlePosition, this.selectedTimeStamp);
|
|
44334
|
+
this.rafId = null;
|
|
44335
|
+
});
|
|
44724
44336
|
}
|
|
44725
44337
|
onDragEnd() {
|
|
44726
44338
|
this.selectTimeStampCallback(this.selectedTimeStamp);
|
|
@@ -44743,6 +44355,12 @@
|
|
|
44743
44355
|
.text(this.timeFormatter(timeStamp));
|
|
44744
44356
|
}
|
|
44745
44357
|
}
|
|
44358
|
+
PlaybackControls.CONSTANTS = {
|
|
44359
|
+
HANDLE_RADIUS: 7,
|
|
44360
|
+
MINIMUM_PLAYBACK_INTERVAL_MS: 1000,
|
|
44361
|
+
HANDLE_PADDING: 8,
|
|
44362
|
+
AXIS_OFFSET: 6,
|
|
44363
|
+
};
|
|
44746
44364
|
class TimeAxis extends TemporalXAxisComponent {
|
|
44747
44365
|
constructor(renderTarget) {
|
|
44748
44366
|
super(renderTarget);
|
|
@@ -45009,6 +44627,10 @@
|
|
|
45009
44627
|
}
|
|
45010
44628
|
}
|
|
45011
44629
|
|
|
44630
|
+
// Ensure moment is available globally for Pikaday and other components
|
|
44631
|
+
if (typeof window !== 'undefined') {
|
|
44632
|
+
window.moment = moment;
|
|
44633
|
+
}
|
|
45012
44634
|
class UXClient {
|
|
45013
44635
|
constructor() {
|
|
45014
44636
|
// Public facing components have class constructors exposed as public UXClient members.
|