survey-analytics 1.12.54 → 1.12.56

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.
Files changed (47) hide show
  1. package/fesm/shared.mjs +479 -15
  2. package/fesm/shared.mjs.map +1 -1
  3. package/fesm/shared2.mjs +783 -247
  4. package/fesm/shared2.mjs.map +1 -1
  5. package/fesm/survey.analytics.core.mjs +2 -2
  6. package/fesm/survey.analytics.mjs +27 -8
  7. package/fesm/survey.analytics.mjs.map +1 -1
  8. package/fesm/survey.analytics.mongo.mjs +221 -0
  9. package/fesm/survey.analytics.mongo.mjs.map +1 -0
  10. package/fesm/survey.analytics.tabulator.mjs +68 -26
  11. package/fesm/survey.analytics.tabulator.mjs.map +1 -1
  12. package/package.json +2 -2
  13. package/survey-analytics-tabulator.types/analytics-localization/finnish.d.ts +16 -0
  14. package/survey-analytics-tabulator.types/analytics-localization/swedish.d.ts +16 -0
  15. package/survey-analytics.types/analytics-localization/finnish.d.ts +16 -0
  16. package/survey-analytics.types/analytics-localization/swedish.d.ts +16 -0
  17. package/survey-analytics.types/entries/mongo.d.ts +1 -0
  18. package/survey-analytics.types/entries/summary.core.d.ts +1 -0
  19. package/survey-analytics.types/mongo/index.d.ts +16 -0
  20. package/survey-analytics.types/mongo/pipelines.d.ts +1 -0
  21. package/survey-analytics.types/mongo/result-transformers.d.ts +35 -0
  22. package/survey-analytics.types/statisticCalculators.d.ts +12 -2
  23. package/survey-analytics.types/visualizationComposite.d.ts +8 -0
  24. package/survey.analytics.core.css +1 -1
  25. package/survey.analytics.core.d.ts +1 -0
  26. package/survey.analytics.core.js +1365 -271
  27. package/survey.analytics.core.js.map +1 -1
  28. package/survey.analytics.core.min.css +1 -1
  29. package/survey.analytics.core.min.js +1 -1
  30. package/survey.analytics.core.min.js.LICENSE.txt +1 -1
  31. package/survey.analytics.css +1 -1
  32. package/survey.analytics.datatables.css +1 -1
  33. package/survey.analytics.datatables.js +1 -1
  34. package/survey.analytics.datatables.min.css +1 -1
  35. package/survey.analytics.datatables.min.js.LICENSE.txt +1 -1
  36. package/survey.analytics.js +1 -1
  37. package/survey.analytics.min.css +1 -1
  38. package/survey.analytics.min.js.LICENSE.txt +1 -1
  39. package/survey.analytics.mongo.d.ts +1 -0
  40. package/survey.analytics.mongo.js +359 -0
  41. package/survey.analytics.mongo.js.map +1 -0
  42. package/survey.analytics.mongo.min.js +2 -0
  43. package/survey.analytics.mongo.min.js.LICENSE.txt +5 -0
  44. package/survey.analytics.tabulator.css +1 -1
  45. package/survey.analytics.tabulator.js +1 -1
  46. package/survey.analytics.tabulator.min.css +1 -1
  47. package/survey.analytics.tabulator.min.js.LICENSE.txt +1 -1
package/fesm/shared2.mjs CHANGED
@@ -1,11 +1,11 @@
1
1
  /*!
2
- * surveyjs - SurveyJS Dashboard library v2.3.6
2
+ * surveyjs - SurveyJS Dashboard library v2.3.11
3
3
  * Copyright (c) 2015-2025 Devsoft Baltic OÜ - http://surveyjs.io/
4
4
  * License: MIT (http://www.opensource.org/licenses/mit-license.php)
5
5
  */
6
6
 
7
7
  import { D as DocumentHelper, l as localization, g as createLoadingIndicator, b as DataHelper, e as createCommercialLicenseLink, f as svgTemplate, t as toPrecision } from './shared.mjs';
8
- import { Event, hasLicense, QuestionCommentModel, settings, ItemValue, surveyLocalization, IsTouch } from 'survey-core';
8
+ import { Event, QuestionCustomModel, QuestionCompositeModel, hasLicense, QuestionCommentModel, settings, ItemValue, QuestionRatingModel, surveyLocalization, IsTouch } from 'survey-core';
9
9
 
10
10
  /******************************************************************************
11
11
  Copyright (c) Microsoft Corporation.
@@ -43,6 +43,10 @@ class DataProvider {
43
43
  constructor(_data = []) {
44
44
  this._data = _data;
45
45
  this.filterValues = {};
46
+ /**
47
+ * Fires when data has been changed.
48
+ */
49
+ this.onFilterChanged = new Event();
46
50
  /**
47
51
  * Fires when data has been changed.
48
52
  */
@@ -79,13 +83,17 @@ class DataProvider {
79
83
  const filterValueType = typeof filterValue;
80
84
  const questionValue = item[key];
81
85
  if (Array.isArray(questionValue)) {
82
- if (filterValueType !== "object")
86
+ if (filterValueType === "object") {
87
+ return !questionArrayValueContainsValue(questionValue, filterValue);
88
+ }
89
+ else {
83
90
  return questionValue.indexOf(filterValue) == -1;
91
+ }
84
92
  }
85
93
  if (typeof questionValue === "object") {
86
94
  if (filterValueType !== "object")
87
95
  return true;
88
- return !questionContainsValue(questionValue, filterValue);
96
+ return !questionValueContainsValue(questionValue, filterValue);
89
97
  }
90
98
  const seriesValue = item[DataProvider.seriesMarkerKey];
91
99
  if (!!seriesValue && filterValueType === "object") {
@@ -118,16 +126,37 @@ class DataProvider {
118
126
  var filterChanged = true;
119
127
  if (selectedValue !== undefined) {
120
128
  filterChanged = this.filterValues[questionName] !== selectedValue;
121
- this.filterValues[questionName] = selectedValue;
129
+ if (filterChanged) {
130
+ this.filterValues[questionName] = selectedValue;
131
+ }
122
132
  }
123
133
  else {
124
134
  filterChanged = this.filterValues[questionName] !== undefined;
125
- delete this.filterValues[questionName];
135
+ if (filterChanged) {
136
+ delete this.filterValues[questionName];
137
+ }
126
138
  }
127
139
  if (filterChanged) {
140
+ this.raiseFilterChanged(questionName, selectedValue);
128
141
  this.raiseDataChanged();
129
142
  }
130
143
  }
144
+ /**
145
+ * Resets filter.
146
+ */
147
+ resetFilter() {
148
+ if (Object.keys(this.filterValues).length === 0) {
149
+ return;
150
+ }
151
+ Object.keys(this.filterValues).forEach(key => delete this.filterValues[key]);
152
+ this.raiseFilterChanged();
153
+ this.raiseDataChanged();
154
+ }
155
+ raiseFilterChanged(questionName, selectedValue) {
156
+ if (!this.onFilterChanged.isEmpty) {
157
+ this.onFilterChanged.fire(this, { questionName, selectedValue });
158
+ }
159
+ }
131
160
  raiseDataChanged(questionName) {
132
161
  this._filteredData = undefined;
133
162
  if (!this.onDataChanged.isEmpty) {
@@ -137,9 +166,31 @@ class DataProvider {
137
166
  getFilters() {
138
167
  return Object.keys(this.filterValues).map(key => ({ field: key, type: "=", value: this.filterValues[key] }));
139
168
  }
169
+ fixDropdownData(dataNames) {
170
+ (this.data || []).forEach((dataItem) => {
171
+ let rawDataItem = dataItem[dataNames[0]];
172
+ if (!!rawDataItem && typeof rawDataItem === "object" && !Array.isArray(rawDataItem)) {
173
+ const arrayData = [];
174
+ Object.keys(rawDataItem).forEach((key) => {
175
+ var nestedDataItem = Object.assign({}, rawDataItem[key]);
176
+ nestedDataItem[DataProvider.seriesMarkerKey] = key;
177
+ arrayData.push(nestedDataItem);
178
+ });
179
+ dataItem[dataNames[0]] = arrayData;
180
+ }
181
+ });
182
+ }
140
183
  }
141
184
  DataProvider.seriesMarkerKey = "__sa_series_name";
142
- function questionContainsValue(questionValue, filterValue) {
185
+ function questionArrayValueContainsValue(questionValues, filterValue) {
186
+ for (let i = 0; i < questionValues.length; i++) {
187
+ if (questionValueContainsValue(questionValues[i], filterValue)) {
188
+ return true;
189
+ }
190
+ }
191
+ return false;
192
+ }
193
+ function questionValueContainsValue(questionValue, filterValue) {
143
194
  const questionValueKeys = Object.keys(questionValue);
144
195
  const filterValueKeys = Object.keys(filterValue);
145
196
  if (filterValueKeys.length > questionValueKeys.length)
@@ -282,16 +333,26 @@ class VisualizerFactory {
282
333
  static createVisualizer(question, data, options) {
283
334
  let type = question.getType();
284
335
  let creators = [];
336
+ let questionForCreator = question;
337
+ let optionsForCreator = Object.assign({}, options);
285
338
  if (type === "text" && question.inputType) {
286
339
  creators = VisualizationManager.getVisualizersByType(question.inputType, type);
287
340
  }
288
341
  else {
289
- creators = VisualizationManager.getVisualizersByType(type);
342
+ let fallbackType = undefined;
343
+ if (question instanceof QuestionCustomModel) {
344
+ fallbackType = question.getDynamicType();
345
+ // questionForCreator = question.contentQuestion;
346
+ }
347
+ else if (question instanceof QuestionCompositeModel) {
348
+ fallbackType = "composite";
349
+ }
350
+ creators = VisualizationManager.getVisualizersByType(type, fallbackType);
290
351
  }
291
- var visualizers = creators.map((creator) => new creator(question, data, options));
352
+ var visualizers = creators.map((creator) => new creator(questionForCreator, data, optionsForCreator));
292
353
  if (visualizers.length > 1) {
293
354
  const alternativesVisualizerConstructor = VisualizationManager.getAltVisualizerSelector();
294
- let visualizer = new alternativesVisualizerConstructor(visualizers, question, data, options);
355
+ let visualizer = new alternativesVisualizerConstructor(visualizers, questionForCreator, data, optionsForCreator);
295
356
  return visualizer;
296
357
  }
297
358
  return visualizers[0];
@@ -325,51 +386,58 @@ function defaultStatisticsCalculator(data, dataInfo) {
325
386
  return valuesIndex[val.value];
326
387
  return valuesIndex[val];
327
388
  };
328
- data.forEach((row) => {
329
- dataNames.forEach((dataName, index) => {
330
- const rowValue = row[dataName];
331
- if (rowValue !== undefined || processMissingAnswers) {
332
- const rowValues = Array.isArray(rowValue) ? rowValue : [rowValue];
333
- if (series.length > 0) {
334
- const rowName = row[DataProvider.seriesMarkerKey];
335
- if (rowName !== undefined) {
336
- // Series are labelled by seriesMarkerKey in row data
337
- const seriesNo = seriesIndex[rowName] || 0;
338
- rowValues.forEach((val) => {
339
- const valIndex = getValueIndex(val);
340
- statistics[index][seriesNo][valIndex]++;
341
- });
342
- }
343
- else {
344
- // Series are the keys in question value (matrix question)
345
- // TODO: think about the de-normalization and combine with the previous case
346
- rowValues.forEach((val) => {
347
- series.forEach((seriesName) => {
348
- if (val[seriesName] !== undefined) {
349
- const seriesNo = seriesIndex[seriesName] || 0;
350
- const values = Array.isArray(val[seriesName]) ? val[seriesName] : [val[seriesName]];
351
- values.forEach(value => {
352
- const valIndex = getValueIndex(value);
353
- statistics[index][seriesNo][valIndex]++;
354
- });
355
- }
356
- });
357
- });
358
- }
389
+ const processDataRow = (dataRow, dataName, index) => {
390
+ const rowValue = dataRow[dataName];
391
+ if (rowValue !== undefined || processMissingAnswers) {
392
+ const rowValues = Array.isArray(rowValue) ? rowValue : [rowValue];
393
+ if (series.length > 0) {
394
+ const rowName = dataRow[DataProvider.seriesMarkerKey];
395
+ if (rowName !== undefined) {
396
+ // Series are labelled by seriesMarkerKey in row data
397
+ const seriesNo = seriesIndex[rowName] || 0;
398
+ rowValues.forEach((val) => {
399
+ const valIndex = getValueIndex(val);
400
+ statistics[index][seriesNo][valIndex]++;
401
+ });
359
402
  }
360
403
  else {
361
- // No series
404
+ // Series are the keys in question value (matrix question)
405
+ // TODO: think about the de-normalization and combine with the previous case
362
406
  rowValues.forEach((val) => {
363
- const valIndex = getValueIndex(val);
364
- statistics[0][0][valIndex]++;
407
+ series.forEach((seriesName) => {
408
+ if (val[seriesName] !== undefined) {
409
+ const seriesNo = seriesIndex[seriesName] || 0;
410
+ const values = Array.isArray(val[seriesName]) ? val[seriesName] : [val[seriesName]];
411
+ values.forEach(value => {
412
+ const valIndex = getValueIndex(value);
413
+ statistics[index][seriesNo][valIndex]++;
414
+ });
415
+ }
416
+ });
365
417
  });
366
418
  }
367
419
  }
420
+ else {
421
+ // No series
422
+ rowValues.forEach((val) => {
423
+ const valIndex = getValueIndex(val);
424
+ statistics[0][0][valIndex]++;
425
+ });
426
+ }
427
+ }
428
+ };
429
+ data.forEach((dataRow) => {
430
+ const nestedDataRows = getNestedDataRows(dataRow, dataInfo);
431
+ nestedDataRows.forEach(nestedDataRow => {
432
+ dataNames.forEach((dataName, index) => {
433
+ processDataRow(nestedDataRow, dataName, index);
434
+ });
368
435
  });
369
436
  });
370
437
  return dataInfo.dataNames.length > 1 ? statistics : statistics[0];
371
438
  }
372
- function histogramStatisticsCalculator(data, intervals, seriesValues) {
439
+ function histogramStatisticsCalculator(data, intervals, dataInfo, aggregateDataNames = []) {
440
+ const seriesValues = dataInfo.getSeriesValues();
373
441
  const statistics = [];
374
442
  if (seriesValues.length === 0) {
375
443
  seriesValues.push("");
@@ -378,8 +446,19 @@ function histogramStatisticsCalculator(data, intervals, seriesValues) {
378
446
  statistics.push(intervals.map(i => 0));
379
447
  data[seriesValues[i]].forEach(dataValue => {
380
448
  for (let j = 0; j < intervals.length; ++j) {
381
- if (intervals[j].start <= dataValue && (dataValue < intervals[j].end || j == intervals.length - 1)) {
382
- statistics[i][j]++;
449
+ if (intervals[j].start <= dataValue.continuous && (dataValue.continuous < intervals[j].end || j == intervals.length - 1)) {
450
+ if (aggregateDataNames.length > 0) {
451
+ aggregateDataNames.forEach(aggregateDataName => {
452
+ const aggregateDataValue = dataValue.row[aggregateDataName];
453
+ const numberValue = parseFloat(aggregateDataValue);
454
+ if (aggregateDataValue !== undefined && !isNaN(numberValue)) {
455
+ statistics[i][j] += numberValue;
456
+ }
457
+ });
458
+ }
459
+ else {
460
+ statistics[i][j]++;
461
+ }
383
462
  break;
384
463
  }
385
464
  }
@@ -387,21 +466,25 @@ function histogramStatisticsCalculator(data, intervals, seriesValues) {
387
466
  }
388
467
  return statistics;
389
468
  }
390
- function mathStatisticsCalculator(data, dataName) {
469
+ function mathStatisticsCalculator(data, dataInfo) {
391
470
  let resultMin = Number.MAX_VALUE, resultMax = -Number.MAX_VALUE, resultAverage = 0;
392
471
  let actualAnswerCount = 0;
393
- data.forEach((rowData) => {
394
- if (rowData[dataName] !== undefined) {
395
- const questionValue = +rowData[dataName];
396
- actualAnswerCount++;
397
- resultAverage += questionValue;
398
- if (resultMin > questionValue) {
399
- resultMin = questionValue;
400
- }
401
- if (resultMax < questionValue) {
402
- resultMax = questionValue;
472
+ data.forEach((dataRow) => {
473
+ const nestedDataRows = getNestedDataRows(dataRow, dataInfo);
474
+ nestedDataRows.forEach(nestedDataRow => {
475
+ const answerData = nestedDataRow[dataInfo.dataNames[0]];
476
+ if (answerData !== undefined) {
477
+ const questionValue = +answerData;
478
+ actualAnswerCount++;
479
+ resultAverage += questionValue;
480
+ if (resultMin > questionValue) {
481
+ resultMin = questionValue;
482
+ }
483
+ if (resultMax < questionValue) {
484
+ resultMax = questionValue;
485
+ }
403
486
  }
404
- }
487
+ });
405
488
  });
406
489
  if (actualAnswerCount > 0) {
407
490
  resultAverage = resultAverage / actualAnswerCount;
@@ -409,6 +492,25 @@ function mathStatisticsCalculator(data, dataName) {
409
492
  resultAverage = Math.ceil(resultAverage * 100) / 100;
410
493
  return [resultAverage, resultMin, resultMax];
411
494
  }
495
+ function getNestedDataRows(dataRow, dataInfo) {
496
+ let nestedDataRows = [];
497
+ if (!dataInfo.dataPath) {
498
+ nestedDataRows = [dataRow];
499
+ }
500
+ else {
501
+ if (dataRow[dataInfo.dataPath] === undefined)
502
+ return [];
503
+ if (typeof dataRow[dataInfo.dataPath] !== "object")
504
+ return [];
505
+ if (Array.isArray(dataRow[dataInfo.dataPath])) {
506
+ nestedDataRows = dataRow[dataInfo.dataPath];
507
+ }
508
+ else {
509
+ nestedDataRows = [dataRow[dataInfo.dataPath]];
510
+ }
511
+ }
512
+ return nestedDataRows;
513
+ }
412
514
 
413
515
  class PostponeHelper {
414
516
  static postpone(fn, timeout) {
@@ -563,6 +665,9 @@ class VisualizerBase {
563
665
  get dataNames() {
564
666
  return [this.name];
565
667
  }
668
+ get dataPath() {
669
+ return this.options.dataPath;
670
+ }
566
671
  /**
567
672
  * Indicates whether the visualizer displays a header. This property is `true` when a visualized question has a correct answer.
568
673
  * @see hasFooter
@@ -689,6 +794,18 @@ class VisualizerBase {
689
794
  }
690
795
  return undefined;
691
796
  }
797
+ /**
798
+ * Returns the visualizer's title.
799
+ */
800
+ get title() {
801
+ return this.getTitle(this.question);
802
+ }
803
+ getTitle(question) {
804
+ var _a;
805
+ if (question === undefined)
806
+ return "";
807
+ return this.processText(((_a = question.locTitle) === null || _a === void 0 ? void 0 : _a.renderedHtml) ? question.locTitle.renderedHtml : question.title);
808
+ }
692
809
  /**
693
810
  * Returns the visualizer's type.
694
811
  */
@@ -717,9 +834,7 @@ class VisualizerBase {
717
834
  * @param data A data array with survey results to be visualized.
718
835
  */
719
836
  updateData(data) {
720
- if (!this.options.dataProvider) {
721
- this.dataProvider.data = data;
722
- }
837
+ this.dataProvider.data = data;
723
838
  if (this.hasFooter) {
724
839
  this.footerVisualizer.updateData(data);
725
840
  }
@@ -1172,19 +1287,21 @@ class NumberModel extends VisualizerBase {
1172
1287
  this.chartTypes = this._chartAdapter.getChartTypes();
1173
1288
  this.chartType = this.chartTypes[0];
1174
1289
  }
1175
- this.registerToolbarItem("changeChartType", () => {
1176
- if (this.chartTypes.length > 1) {
1177
- return DocumentHelper.createSelector(this.chartTypes.map((chartType) => {
1178
- return {
1179
- value: chartType,
1180
- text: localization.getString("chartType_" + chartType),
1181
- };
1182
- }), (option) => this.chartType === option.value, (e) => {
1183
- this.setChartType(e.target.value);
1184
- });
1185
- }
1186
- return null;
1187
- });
1290
+ if (this.options.allowChangeVisualizerType !== false) {
1291
+ this.registerToolbarItem("changeChartType", () => {
1292
+ if (this.chartTypes.length > 1) {
1293
+ return DocumentHelper.createSelector(this.chartTypes.map((chartType) => {
1294
+ return {
1295
+ value: chartType,
1296
+ text: localization.getString("chartType_" + chartType),
1297
+ };
1298
+ }), (option) => this.chartType === option.value, (e) => {
1299
+ this.setChartType(e.target.value);
1300
+ });
1301
+ }
1302
+ return null;
1303
+ });
1304
+ }
1188
1305
  }
1189
1306
  onDataChanged() {
1190
1307
  this._resultAverage = undefined;
@@ -1258,7 +1375,7 @@ class NumberModel extends VisualizerBase {
1258
1375
  if (this._resultAverage === undefined ||
1259
1376
  this._resultMin === undefined ||
1260
1377
  this._resultMax === undefined) {
1261
- [this._resultAverage, this._resultMin, this._resultMax] = mathStatisticsCalculator(this.surveyData, this.dataNames[0]);
1378
+ [this._resultAverage, this._resultMin, this._resultMax] = mathStatisticsCalculator(this.surveyData, this);
1262
1379
  }
1263
1380
  return [this._resultAverage, this._resultMin, this._resultMax];
1264
1381
  }
@@ -1383,19 +1500,21 @@ class SelectBase extends VisualizerBase {
1383
1500
  this._chartType = this.options.defaultChartType;
1384
1501
  }
1385
1502
  }
1386
- this.registerToolbarItem("changeChartType", () => {
1387
- if (this.chartTypes.length > 1) {
1388
- return DocumentHelper.createSelector(this.chartTypes.map((chartType) => {
1389
- return {
1390
- value: chartType,
1391
- text: localization.getString("chartType_" + chartType),
1392
- };
1393
- }), (option) => this.chartType === option.value, (e) => {
1394
- this.setChartType(e.target.value);
1395
- });
1396
- }
1397
- return null;
1398
- });
1503
+ if (this.options.allowChangeVisualizerType !== false) {
1504
+ this.registerToolbarItem("changeChartType", () => {
1505
+ if (this.chartTypes.length > 1) {
1506
+ return DocumentHelper.createSelector(this.chartTypes.map((chartType) => {
1507
+ return {
1508
+ value: chartType,
1509
+ text: localization.getString("chartType_" + chartType),
1510
+ };
1511
+ }), (option) => this.chartType === option.value, (e) => {
1512
+ this.setChartType(e.target.value);
1513
+ });
1514
+ }
1515
+ return null;
1516
+ });
1517
+ }
1399
1518
  this.registerToolbarItem("changeAnswersOrder", () => {
1400
1519
  if (this.isSupportAnswersOrder()) {
1401
1520
  this.choicesOrderSelector = DocumentHelper.createSelector([
@@ -1571,6 +1690,11 @@ class SelectBase extends VisualizerBase {
1571
1690
  }
1572
1691
  }
1573
1692
  getSelectedItemByText(itemText) {
1693
+ var _a;
1694
+ if (this.question instanceof QuestionRatingModel) {
1695
+ const rateValues = this.question.rateValues;
1696
+ return (_a = rateValues === null || rateValues === void 0 ? void 0 : rateValues.filter((choice) => choice.text === itemText)[0]) !== null && _a !== void 0 ? _a : new ItemValue(parseFloat(itemText), itemText);
1697
+ }
1574
1698
  const selBase = this.question;
1575
1699
  if (this.question.hasOther && itemText == selBase.otherText) {
1576
1700
  return selBase.otherItem;
@@ -1579,14 +1703,17 @@ class SelectBase extends VisualizerBase {
1579
1703
  return selBase.choices.filter((choice) => choice.text === itemText)[0];
1580
1704
  }
1581
1705
  }
1582
- setSelection(item) {
1706
+ onSelectionChanged(item) {
1583
1707
  var _a;
1708
+ if (this.onDataItemSelected !== undefined) {
1709
+ this.onDataItemSelected(item !== undefined ? item.value : undefined, item !== undefined ? item.text : "");
1710
+ }
1711
+ this.stateChanged("filter", (_a = this.selectedItem) === null || _a === void 0 ? void 0 : _a.value);
1712
+ }
1713
+ setSelection(item) {
1584
1714
  if (this.selectedItem !== item) {
1585
1715
  this.selectedItem = item;
1586
- if (this.onDataItemSelected !== undefined) {
1587
- this.onDataItemSelected(item !== undefined ? item.value : undefined, item !== undefined ? item.text : "");
1588
- }
1589
- this.stateChanged("filter", (_a = this.selectedItem) === null || _a === void 0 ? void 0 : _a.value);
1716
+ this.onSelectionChanged(item);
1590
1717
  }
1591
1718
  }
1592
1719
  get selection() {
@@ -1772,22 +1899,6 @@ class SelectBase extends VisualizerBase {
1772
1899
  }
1773
1900
  return percentages;
1774
1901
  }
1775
- answersDataReady(answersData) {
1776
- let result = {};
1777
- if (this.hideEmptyAnswers) {
1778
- result = hideEmptyAnswersInData(answersData);
1779
- }
1780
- else {
1781
- result = answersData;
1782
- }
1783
- if (this.topN > 0) {
1784
- result.datasets[0] = result.datasets[0].slice(-this.topN);
1785
- result.labels = result.labels.slice(-this.topN);
1786
- result.colors = result.colors.slice(-this.topN);
1787
- result.texts[0] = result.texts[0].slice(-this.topN);
1788
- }
1789
- return result;
1790
- }
1791
1902
  /**
1792
1903
  * Returns object with all infotmation for data visualization: datasets, labels, colors, additional texts (percentage).
1793
1904
  */
@@ -1823,7 +1934,15 @@ class SelectBase extends VisualizerBase {
1823
1934
  texts,
1824
1935
  seriesLabels,
1825
1936
  };
1826
- answersData = this.answersDataReady(answersData);
1937
+ if (this.hideEmptyAnswers) {
1938
+ answersData = hideEmptyAnswersInData(answersData);
1939
+ }
1940
+ if (this.topN > 0) {
1941
+ answersData.datasets[0] = answersData.datasets[0].slice(-this.topN);
1942
+ answersData.labels = answersData.labels.slice(-this.topN);
1943
+ answersData.colors = answersData.colors.slice(-this.topN);
1944
+ answersData.texts[0] = answersData.texts[0].slice(-this.topN);
1945
+ }
1827
1946
  this.onAnswersDataReady.fire(this, answersData);
1828
1947
  return answersData;
1829
1948
  });
@@ -1961,6 +2080,144 @@ BooleanModel.trueColor = "";
1961
2080
  BooleanModel.falseColor = "";
1962
2081
  VisualizationManager.registerVisualizer("boolean", BooleanModel);
1963
2082
 
2083
+ function getQuarter(date) {
2084
+ switch (Math.floor(date.getMonth() / 3) + 1) {
2085
+ case 1: return "I";
2086
+ case 2: return "II";
2087
+ case 3: return "III";
2088
+ case 4: return "IV";
2089
+ }
2090
+ }
2091
+ function getBestIntervalMode(min, max) {
2092
+ const start = new Date(min);
2093
+ const end = new Date(max);
2094
+ const totalMonths = (end.getFullYear() - start.getFullYear()) * 12 + (end.getMonth() - start.getMonth());
2095
+ if (totalMonths > 10 * 12)
2096
+ return "decades";
2097
+ if (totalMonths > 2 * 12)
2098
+ return "years";
2099
+ if (totalMonths > 1 * 12)
2100
+ return "quarters";
2101
+ if (totalMonths > 4)
2102
+ return "months";
2103
+ return "days";
2104
+ }
2105
+ const intervalCalculators = {
2106
+ decades: (min, max) => {
2107
+ const intervals = [];
2108
+ let start = new Date(min);
2109
+ start.setFullYear(Math.floor(start.getFullYear() / 10) * 10);
2110
+ start.setMonth(0);
2111
+ start.setDate(1);
2112
+ start.setHours(0, 0, 0, 0);
2113
+ let startYear = start.getFullYear();
2114
+ const end = new Date(max);
2115
+ const endYear = end.getFullYear();
2116
+ while (startYear <= endYear) {
2117
+ const intervalStart = new Date(startYear, 0, 1);
2118
+ const intervalEnd = new Date(startYear + 10, 0, 1);
2119
+ intervals.push({
2120
+ start: intervalStart.getTime(),
2121
+ end: intervalEnd.getTime(),
2122
+ label: "" + startYear + "s"
2123
+ });
2124
+ startYear += 10;
2125
+ }
2126
+ return intervals;
2127
+ },
2128
+ years: (min, max) => {
2129
+ const intervals = [];
2130
+ let start = new Date(min);
2131
+ start.setMonth(0);
2132
+ start.setDate(1);
2133
+ start.setHours(0, 0, 0, 0);
2134
+ let startYear = start.getFullYear();
2135
+ const end = new Date(max);
2136
+ const endYear = end.getFullYear();
2137
+ while (startYear <= endYear) {
2138
+ const intervalStart = new Date(startYear, 0, 1);
2139
+ const intervalEnd = new Date(startYear + 1, 0, 1);
2140
+ intervals.push({
2141
+ start: intervalStart.getTime(),
2142
+ end: intervalEnd.getTime(),
2143
+ label: "" + startYear
2144
+ });
2145
+ startYear++;
2146
+ }
2147
+ return intervals;
2148
+ },
2149
+ months: (min, max) => {
2150
+ const intervals = [];
2151
+ let start = new Date(min);
2152
+ start.setDate(1);
2153
+ start.setHours(0, 0, 0, 0);
2154
+ let startYear = start.getFullYear();
2155
+ let startMonth = start.getMonth();
2156
+ const end = new Date(max);
2157
+ const endYear = end.getFullYear();
2158
+ const endMonth = end.getMonth();
2159
+ while (startYear < endYear || (startYear === endYear && startMonth <= endMonth)) {
2160
+ const intervalStart = new Date(startYear, startMonth, 1);
2161
+ const intervalEnd = new Date(startYear, startMonth + 1, 1);
2162
+ intervals.push({
2163
+ start: intervalStart.getTime(),
2164
+ end: intervalEnd.getTime(),
2165
+ label: intervalStart.toLocaleDateString(undefined, { year: "numeric", month: "short" })
2166
+ });
2167
+ startMonth++;
2168
+ if (startMonth >= 12) {
2169
+ startMonth = 0;
2170
+ startYear++;
2171
+ }
2172
+ }
2173
+ return intervals;
2174
+ },
2175
+ quarters: (min, max) => {
2176
+ const intervals = [];
2177
+ let start = new Date(min);
2178
+ start.setDate(1);
2179
+ start.setHours(0, 0, 0, 0);
2180
+ let startYear = start.getFullYear();
2181
+ let startMonth = start.getMonth();
2182
+ const end = new Date(max);
2183
+ const endYear = end.getFullYear();
2184
+ const endMonth = end.getMonth();
2185
+ while (startYear < endYear || (startYear === endYear && startMonth <= endMonth)) {
2186
+ const intervalStart = new Date(startYear, startMonth, 1);
2187
+ const intervalEnd = new Date(startYear, startMonth + 3, 1);
2188
+ intervals.push({
2189
+ start: intervalStart.getTime(),
2190
+ end: intervalEnd.getTime(),
2191
+ label: getQuarter(intervalStart) + " " + intervalStart.getFullYear().toString()
2192
+ });
2193
+ startMonth += 3;
2194
+ if (startMonth >= 12) {
2195
+ startMonth = startMonth % 12;
2196
+ startYear++;
2197
+ }
2198
+ }
2199
+ return intervals;
2200
+ },
2201
+ days: (min, max) => {
2202
+ const intervals = [];
2203
+ let start = new Date(min);
2204
+ start.setHours(0, 0, 0, 0);
2205
+ const end = new Date(max);
2206
+ end.setHours(0, 0, 0, 0);
2207
+ while (start <= end) {
2208
+ const intervalStart = new Date(start);
2209
+ const intervalEnd = new Date(start);
2210
+ intervalEnd.setDate(intervalEnd.getDate() + 1);
2211
+ intervals.push({
2212
+ start: intervalStart.getTime(),
2213
+ end: intervalEnd.getTime(),
2214
+ label: intervalStart.toLocaleDateString()
2215
+ });
2216
+ start.setDate(start.getDate() + 1);
2217
+ }
2218
+ return intervals;
2219
+ }
2220
+ };
1964
2221
  class HistogramModel extends SelectBase {
1965
2222
  constructor(question, data, options, name) {
1966
2223
  super(question, data, options, name || "histogram");
@@ -1969,6 +2226,14 @@ class HistogramModel extends SelectBase {
1969
2226
  this._continuousData = undefined;
1970
2227
  this._cachedIntervals = undefined;
1971
2228
  this._intervalPrecision = 2;
2229
+ this.showRunningTotalsBtn = undefined;
2230
+ this.showGroupedBtn = undefined;
2231
+ this.changeIntervalsModeSelector = undefined;
2232
+ this.aggregateDataNameSelector = undefined;
2233
+ this.intervalModes = ["auto", "decades", "years", "quarters", "months", "days"];
2234
+ this._showRunningTotals = false;
2235
+ this._showGrouped = false;
2236
+ this._aggregateDataName = "";
1972
2237
  this._transposeData = false;
1973
2238
  if (this.options.intervalPrecision !== undefined) {
1974
2239
  this._intervalPrecision = this.options.intervalPrecision;
@@ -1980,6 +2245,61 @@ class HistogramModel extends SelectBase {
1980
2245
  else {
1981
2246
  this.valueType = "number";
1982
2247
  }
2248
+ this._intervalsMode = this.valueType === "date" ? "auto" : "default";
2249
+ if (this.allowChangeIntervals) {
2250
+ this.registerToolbarItem("changeIntervalsMode", () => {
2251
+ this.changeIntervalsModeSelector = DocumentHelper.createSelector(this.intervalModes.map((intervalModeValue) => {
2252
+ return {
2253
+ value: intervalModeValue,
2254
+ text: localization.getString("intervalMode_" + intervalModeValue),
2255
+ };
2256
+ }), (option) => this.intervalsMode === option.value, (e) => {
2257
+ this.intervalsMode = e.target.value;
2258
+ }, localization.getString("intervalModeTitle"));
2259
+ return this.changeIntervalsModeSelector;
2260
+ });
2261
+ }
2262
+ if (this.possibleAggregateDataNames.length > 0) {
2263
+ this.registerToolbarItem("aggregateDataName", () => {
2264
+ const choices = this.possibleAggregateDataNames.map((dataName) => {
2265
+ return typeof dataName === "string" ? { value: dataName, text: dataName } : dataName;
2266
+ });
2267
+ choices.unshift({ value: "", text: localization.getString("noneAggregateText") }),
2268
+ this.aggregateDataNameSelector = DocumentHelper.createSelector(choices, (option) => this.aggregateDataName === option.value, (e) => {
2269
+ this.aggregateDataName = e.target.value;
2270
+ }, localization.getString("selectAggregateText"));
2271
+ this.updateAggregateDataNameSelector();
2272
+ return this.aggregateDataNameSelector;
2273
+ });
2274
+ }
2275
+ if (this.allowChangeIntervals && this.options.allowRunningTotals) {
2276
+ this.registerToolbarItem("showRunningTotals", () => {
2277
+ this.showRunningTotalsBtn = DocumentHelper.createButton(() => {
2278
+ this.showRunningTotals = !this.showRunningTotals;
2279
+ });
2280
+ this.updateShowRunningTotalsBtn();
2281
+ return this.showRunningTotalsBtn;
2282
+ });
2283
+ }
2284
+ if (this.allowChangeIntervals && this.options.allowCompareDatePeriods) {
2285
+ this.registerToolbarItem("showGrouped", () => {
2286
+ this.showGroupedBtn = DocumentHelper.createButton(() => {
2287
+ this.showGrouped = !this.showGrouped;
2288
+ });
2289
+ this.updateShowGroupedBtn();
2290
+ return this.showGroupedBtn;
2291
+ });
2292
+ }
2293
+ }
2294
+ updateIntervalsModeSelector() {
2295
+ if (!!this.changeIntervalsModeSelector) {
2296
+ this.changeIntervalsModeSelector.getElementsByTagName("select")[0].value = this.intervalsMode;
2297
+ }
2298
+ }
2299
+ updateAggregateDataNameSelector() {
2300
+ if (!!this.aggregateDataNameSelector) {
2301
+ this.aggregateDataNameSelector.getElementsByTagName("select")[0].value = this.aggregateDataName;
2302
+ }
1983
2303
  }
1984
2304
  reset() {
1985
2305
  this._continuousData = undefined;
@@ -2022,6 +2342,17 @@ class HistogramModel extends SelectBase {
2022
2342
  this.reset();
2023
2343
  super.onDataChanged();
2024
2344
  }
2345
+ onSelectionChanged(item) {
2346
+ if (item !== undefined && this.onDataItemSelected !== undefined) {
2347
+ if (this.valueType === "date") {
2348
+ const currIntervalCalueIndex = this.intervalModes.indexOf(this.intervalsMode);
2349
+ if (currIntervalCalueIndex > 0 && currIntervalCalueIndex < this.intervalModes.length - 1) {
2350
+ this.intervalsMode = this.intervalModes[currIntervalCalueIndex + 1];
2351
+ }
2352
+ }
2353
+ }
2354
+ super.onSelectionChanged(item);
2355
+ }
2025
2356
  getContinuousValues() {
2026
2357
  if (this._cachedValues === undefined) {
2027
2358
  const series = this.getSeriesValues();
@@ -2031,20 +2362,26 @@ class HistogramModel extends SelectBase {
2031
2362
  this._continuousData = {};
2032
2363
  series.forEach(seriesValue => this._continuousData[seriesValue] = []);
2033
2364
  const hash = {};
2034
- this.data.forEach(dataItem => {
2035
- const answerData = dataItem[this.name];
2036
- if (answerData !== undefined) {
2037
- const seriesValue = dataItem[DataProvider.seriesMarkerKey] || "";
2038
- // TODO: _continuousData should be sorted in order to speed-up statistics calculation in the getData function
2039
- this._continuousData[seriesValue].push(this.getContinuousValue(answerData));
2040
- hash[answerData] = answerData;
2041
- }
2365
+ this.data.forEach(dataRow => {
2366
+ const nestedDataRows = getNestedDataRows(dataRow, this);
2367
+ nestedDataRows.forEach(nestedDataRow => {
2368
+ const answerData = nestedDataRow[this.dataNames[0]];
2369
+ if (answerData !== undefined) {
2370
+ const seriesValue = nestedDataRow[DataProvider.seriesMarkerKey] || "";
2371
+ // TODO: _continuousData should be sorted in order to speed-up statistics calculation in the getData function
2372
+ this._continuousData[seriesValue].push({ continuous: this.getContinuousValue(answerData), row: nestedDataRow });
2373
+ hash[answerData] = answerData;
2374
+ }
2375
+ });
2042
2376
  });
2043
- this._cachedValues = Object.keys(hash).map(key => ({ original: hash[key], continuous: this.getContinuousValue(key) }));
2377
+ this._cachedValues = Object.keys(hash).map(key => ({ original: hash[key], continuous: this.getContinuousValue(key), row: hash[key].row }));
2044
2378
  this._cachedValues.sort((a, b) => a.continuous - b.continuous);
2045
2379
  }
2046
2380
  return this._cachedValues;
2047
2381
  }
2382
+ isSupportSoftUpdateContent() {
2383
+ return false;
2384
+ }
2048
2385
  isSupportMissingAnswers() {
2049
2386
  return false;
2050
2387
  }
@@ -2092,29 +2429,220 @@ class HistogramModel extends SelectBase {
2092
2429
  if (continuousValues.length) {
2093
2430
  let start = continuousValues[0].continuous;
2094
2431
  const end = continuousValues[continuousValues.length - 1].continuous;
2095
- const intervalsCount = HistogramModel.IntervalsCount;
2096
- const delta = (end - start) / intervalsCount;
2097
- for (let i = 0; i < intervalsCount; ++i) {
2098
- const next = start + delta;
2099
- const istart = this.toPrecision(start);
2100
- const inext = this.toPrecision(next);
2101
- this._cachedIntervals.push({
2102
- start: istart,
2103
- end: i < intervalsCount - 1 ? inext : inext + delta / 100,
2104
- label: "" + this.getString(istart) + "-" + this.getString(inext)
2105
- });
2106
- start = next;
2432
+ const intervalsMode = this.intervalsMode === "auto" ? getBestIntervalMode(start, end) : this.intervalsMode;
2433
+ if (intervalCalculators[intervalsMode] !== undefined) {
2434
+ this._cachedIntervals = intervalCalculators[intervalsMode](start, end);
2435
+ }
2436
+ else {
2437
+ const intervalsCount = HistogramModel.IntervalsCount;
2438
+ const delta = (end - start) / intervalsCount;
2439
+ for (let i = 0; i < intervalsCount; ++i) {
2440
+ const next = start + delta;
2441
+ const istart = this.toPrecision(start);
2442
+ const inext = this.toPrecision(next);
2443
+ this._cachedIntervals.push({
2444
+ start: istart,
2445
+ end: i < intervalsCount - 1 ? inext : inext + delta / 100,
2446
+ label: "" + this.getString(istart) + "-" + this.getString(inext)
2447
+ });
2448
+ start = next;
2449
+ }
2107
2450
  }
2108
2451
  }
2109
2452
  }
2110
2453
  return this._cachedIntervals;
2111
2454
  }
2455
+ get intervalsMode() {
2456
+ if (this.hasCustomIntervals)
2457
+ return "custom";
2458
+ return this._intervalsMode;
2459
+ }
2460
+ set intervalsMode(val) {
2461
+ if (this.allowChangeIntervals && this._intervalsMode !== val) {
2462
+ this._intervalsMode = val;
2463
+ if (!this.canShowGroupedDateSeries) {
2464
+ this._showGrouped = false;
2465
+ }
2466
+ this.updateIntervalsModeSelector();
2467
+ this.updateShowGroupedBtn();
2468
+ this.onDataChanged();
2469
+ }
2470
+ }
2471
+ get allowChangeIntervals() {
2472
+ return this.valueType === "date" && !this.hasCustomIntervals && this.options.allowChangeIntervals === true;
2473
+ }
2474
+ get showRunningTotals() {
2475
+ return this._showRunningTotals;
2476
+ }
2477
+ set showRunningTotals(val) {
2478
+ this._showRunningTotals = val;
2479
+ this.updateShowRunningTotalsBtn();
2480
+ this.stateChanged("showRunningTotals", val);
2481
+ this.refreshContent();
2482
+ }
2483
+ updateShowRunningTotalsBtn() {
2484
+ if (!!this.showRunningTotalsBtn) {
2485
+ this.showRunningTotalsBtn.innerText = this.showRunningTotals
2486
+ ? localization.getString("noRunningTotals")
2487
+ : localization.getString("runningTotals");
2488
+ }
2489
+ }
2490
+ get showGrouped() {
2491
+ return this._showGrouped;
2492
+ }
2493
+ set showGrouped(val) {
2494
+ this._showGrouped = val;
2495
+ this.updateShowGroupedBtn();
2496
+ this.stateChanged("showGrouped", val);
2497
+ this.refreshContent();
2498
+ }
2499
+ updateShowGroupedBtn() {
2500
+ if (!!this.showGroupedBtn) {
2501
+ this.showGroupedBtn.innerText = this.showGrouped
2502
+ ? localization.getString("ungroupDateSeries")
2503
+ : localization.getString("groupDateSeries");
2504
+ this.showGroupedBtn.style.display = this.canShowGroupedDateSeries ? "inline" : "none";
2505
+ }
2506
+ }
2507
+ get canShowGroupedDateSeries() {
2508
+ return ["years", "quarters", "months"].indexOf(this.intervalsMode) !== -1;
2509
+ }
2510
+ get aggregateDataName() {
2511
+ return this._aggregateDataName;
2512
+ }
2513
+ set aggregateDataName(val) {
2514
+ if (this._aggregateDataName !== val) {
2515
+ this._aggregateDataName = val;
2516
+ this.onDataChanged();
2517
+ }
2518
+ }
2519
+ get possibleAggregateDataNames() {
2520
+ var _a, _b;
2521
+ return (_b = (_a = this.questionOptions) === null || _a === void 0 ? void 0 : _a.aggregateDataNames) !== null && _b !== void 0 ? _b : [];
2522
+ }
2112
2523
  convertFromExternalData(externalCalculatedData) {
2113
2524
  return [externalCalculatedData];
2114
2525
  }
2115
2526
  getCalculatedValuesCore() {
2116
2527
  this.getContinuousValues();
2117
- return histogramStatisticsCalculator(this._continuousData, this.intervals, this.getSeriesValues());
2528
+ return histogramStatisticsCalculator(this._continuousData, this.intervals, this, [this.aggregateDataName].filter(name => !!name));
2529
+ }
2530
+ getCalculatedValues() {
2531
+ const _super = Object.create(null, {
2532
+ getCalculatedValues: { get: () => super.getCalculatedValues }
2533
+ });
2534
+ return __awaiter(this, void 0, void 0, function* () {
2535
+ const values = yield _super.getCalculatedValues.call(this);
2536
+ const result = JSON.parse(JSON.stringify(values));
2537
+ if (this.showRunningTotals) {
2538
+ for (let i = 0; i < result.length; i++) {
2539
+ for (let j = 1; j < result[i].length; j++) {
2540
+ result[i][j] += result[i][j - 1];
2541
+ }
2542
+ }
2543
+ }
2544
+ return result;
2545
+ });
2546
+ }
2547
+ getGroupedDateAnswersData() {
2548
+ return __awaiter(this, void 0, void 0, function* () {
2549
+ let datasets = (yield this.getCalculatedValues());
2550
+ let colors = this.getColors();
2551
+ let labels = this.getLabels();
2552
+ let seriesLabels = this.getSeriesLabels();
2553
+ const intervals = [].concat(this.intervals);
2554
+ const start = new Date(intervals[0].start);
2555
+ const end = new Date(intervals[intervals.length - 1].end);
2556
+ const startYear = start.getFullYear();
2557
+ const endYear = end.getFullYear();
2558
+ if (this.intervalsMode === "years") {
2559
+ seriesLabels = [];
2560
+ const groupedDatasets = [];
2561
+ for (let year = Math.floor(startYear / 10) * 10; year <= endYear; year += 10) {
2562
+ seriesLabels.push(year.toString() + "s");
2563
+ groupedDatasets.push(new Array(10).fill(0));
2564
+ }
2565
+ for (let i = 0; i < intervals.length; i++) {
2566
+ const interval = intervals[i];
2567
+ const intervalDate = new Date(interval.start);
2568
+ const decade = Math.floor(intervalDate.getFullYear() / 10) * 10;
2569
+ const yearInDecade = intervalDate.getFullYear() - decade;
2570
+ const decadeIndex = Math.floor((decade - Math.floor(startYear / 10) * 10) / 10);
2571
+ groupedDatasets[decadeIndex][yearInDecade] = datasets[0][i];
2572
+ }
2573
+ datasets = groupedDatasets;
2574
+ labels = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"];
2575
+ }
2576
+ else if (this.intervalsMode === "quarters") {
2577
+ seriesLabels = [];
2578
+ const groupedDatasets = [];
2579
+ for (let year = startYear; year <= endYear; year++) {
2580
+ seriesLabels.push(year.toString());
2581
+ groupedDatasets.push(new Array(4).fill(0));
2582
+ }
2583
+ for (let i = 0; i < intervals.length; i++) {
2584
+ const interval = intervals[i];
2585
+ const intervalDate = new Date(interval.start);
2586
+ const quarter = Math.floor(intervalDate.getMonth() / 3);
2587
+ const year = intervalDate.getFullYear();
2588
+ const yearIndex = year - startYear;
2589
+ groupedDatasets[yearIndex][quarter] = datasets[0][i];
2590
+ }
2591
+ datasets = groupedDatasets;
2592
+ labels = ["I", "II", "III", "IV"];
2593
+ }
2594
+ else if (this.intervalsMode === "months") {
2595
+ seriesLabels = [];
2596
+ const groupedDatasets = [];
2597
+ for (let year = startYear; year <= endYear; year++) {
2598
+ seriesLabels.push(year.toString());
2599
+ groupedDatasets.push(new Array(12).fill(0));
2600
+ }
2601
+ for (let i = 0; i < intervals.length; i++) {
2602
+ const interval = intervals[i];
2603
+ const intervalDate = new Date(interval.start);
2604
+ const month = intervalDate.getMonth();
2605
+ const year = intervalDate.getFullYear();
2606
+ const yearIndex = year - startYear;
2607
+ groupedDatasets[yearIndex][month] = datasets[0][i];
2608
+ }
2609
+ datasets = groupedDatasets;
2610
+ labels = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
2611
+ }
2612
+ let texts = datasets;
2613
+ return {
2614
+ datasets,
2615
+ labels,
2616
+ colors,
2617
+ texts,
2618
+ seriesLabels,
2619
+ };
2620
+ });
2621
+ }
2622
+ /**
2623
+ * Returns object with all infotmation for data visualization: datasets, labels, colors, additional texts (percentage).
2624
+ */
2625
+ getAnswersData() {
2626
+ const _super = Object.create(null, {
2627
+ getAnswersData: { get: () => super.getAnswersData }
2628
+ });
2629
+ return __awaiter(this, void 0, void 0, function* () {
2630
+ if (!this.showGrouped) {
2631
+ return _super.getAnswersData.call(this);
2632
+ }
2633
+ const answersData = yield this.getGroupedDateAnswersData();
2634
+ const continuousValues = this.getContinuousValues();
2635
+ if (continuousValues.length) {
2636
+ let start = continuousValues[0].continuous;
2637
+ const end = continuousValues[continuousValues.length - 1].continuous;
2638
+ const intervalsMode = this.intervalsMode === "auto" ? getBestIntervalMode(start, end) : this.intervalsMode;
2639
+ if (intervalsMode === "years" && this.showGrouped) {
2640
+ answersData.labelsTitle = localization.getString("groupedYearsAxisTitle");
2641
+ }
2642
+ }
2643
+ this.onAnswersDataReady.fire(this, answersData);
2644
+ return answersData;
2645
+ });
2118
2646
  }
2119
2647
  getValueType() {
2120
2648
  return this.valueType;
@@ -2664,7 +3192,7 @@ class AlternativeVisualizersWrapper extends VisualizerBase {
2664
3192
  }
2665
3193
  }
2666
3194
  constructor(visualizers, question, data, options) {
2667
- super(question, data, options);
3195
+ super(question, data, options, "alternative");
2668
3196
  this.visualizers = visualizers;
2669
3197
  this.visualizersWithSelection = [];
2670
3198
  this.onAfterVisualizerRenderCallback = () => {
@@ -2690,12 +3218,14 @@ class AlternativeVisualizersWrapper extends VisualizerBase {
2690
3218
  this.visualizersWithSelection.push(visualizer);
2691
3219
  }
2692
3220
  });
2693
- this.registerToolbarItem("changeVisualizer", () => this.visualizerSelector = DocumentHelper.createSelector(this.visualizers.map((visualizer) => {
2694
- return {
2695
- value: visualizer.type,
2696
- text: localization.getString("visualizer_" + visualizer.type),
2697
- };
2698
- }), (option) => this.visualizer.type === option.value, (e) => this.setVisualizer(e.target.value)), 0);
3221
+ if (this.options.allowChangeVisualizerType !== false) {
3222
+ this.registerToolbarItem("changeVisualizer", () => this.visualizerSelector = DocumentHelper.createSelector(this.visualizers.map((visualizer) => {
3223
+ return {
3224
+ value: visualizer.type,
3225
+ text: localization.getString("visualizer_" + visualizer.type),
3226
+ };
3227
+ }), (option) => this.visualizer.type === option.value, (e) => this.setVisualizer(e.target.value)), 0);
3228
+ }
2699
3229
  this.visualizer = visualizers[0];
2700
3230
  this.visualizer.onAfterRender.add(this.onAfterVisualizerRenderCallback);
2701
3231
  this.visualizer.onStateChanged.add(this.onVisualizerStateChangedCallback);
@@ -9775,6 +10305,50 @@ class MuuriLayoutEngine extends LayoutEngine {
9775
10305
  }
9776
10306
  }
9777
10307
 
10308
+ class VisualizationPanelDynamic extends VisualizerBase {
10309
+ constructor(question, data, options = {}, name) {
10310
+ super(question, data, options, name || "panelDynamic");
10311
+ this._contentVisualizer = undefined;
10312
+ this.onAfterRenderPanelCallback = () => {
10313
+ this.afterRender(this.contentContainer);
10314
+ };
10315
+ this.loadingData = false;
10316
+ var options = Object.assign({}, options);
10317
+ options.allowHideQuestions = false;
10318
+ options.allowDynamicLayout = false;
10319
+ options.dataProvider = this.dataProvider;
10320
+ options.dataPath = this.dataNames[0];
10321
+ this._contentVisualizer = new VisualizationPanel(this.getQuestions(), [], options, undefined, false);
10322
+ this._contentVisualizer.onAfterRender.add(this.onAfterRenderPanelCallback);
10323
+ }
10324
+ get contentVisualizer() {
10325
+ return this._contentVisualizer;
10326
+ }
10327
+ setLocale(newLocale) {
10328
+ super.setLocale(newLocale);
10329
+ this._contentVisualizer.locale = newLocale;
10330
+ }
10331
+ resetFilter() {
10332
+ this.contentVisualizer.resetFilter();
10333
+ }
10334
+ getQuestions() {
10335
+ const paneldynamic = this.question;
10336
+ return paneldynamic.template.questions;
10337
+ }
10338
+ destroyContent(container) {
10339
+ this._contentVisualizer.clear();
10340
+ super.destroyContent(this.contentContainer);
10341
+ }
10342
+ renderContent(container) {
10343
+ this._contentVisualizer.render(container);
10344
+ }
10345
+ destroy() {
10346
+ super.destroy();
10347
+ this._contentVisualizer.onAfterRender.remove(this.onAfterRenderPanelCallback);
10348
+ }
10349
+ }
10350
+ VisualizationManager.registerVisualizer("paneldynamic", VisualizationPanelDynamic);
10351
+
9778
10352
  const questionElementClassName = "sa-question";
9779
10353
  const questionLayoutedElementClassName = "sa-question-layouted";
9780
10354
  if (!!document) {
@@ -9807,7 +10381,7 @@ class VisualizationPanel extends VisualizerBase {
9807
10381
  this.renderedQuestionsCount = 0;
9808
10382
  this.onAfterRenderQuestionCallback = (sender, options) => {
9809
10383
  this.renderedQuestionsCount++;
9810
- if (this.renderedQuestionsCount == this.questions.length) {
10384
+ if (this.renderedQuestionsCount == this.visibleElements.length) {
9811
10385
  this.renderedQuestionsCount = 0;
9812
10386
  this.layoutEngine.update();
9813
10387
  this.afterRender(this.contentContainer);
@@ -9886,7 +10460,7 @@ class VisualizationPanel extends VisualizerBase {
9886
10460
  options.layoutEngine ||
9887
10461
  new MuuriLayoutEngine(this.allowDynamicLayout, "." + questionLayoutedElementClassName, this.allowDragDrop);
9888
10462
  this._layoutEngine.onMoveCallback = (order) => this.reorderVisibleElements(order);
9889
- this.showToolbar = true;
10463
+ this.showToolbar = isRoot;
9890
10464
  if (this.options.survey) {
9891
10465
  localization.currentLocale = this.options.survey.locale;
9892
10466
  }
@@ -9903,11 +10477,7 @@ class VisualizationPanel extends VisualizerBase {
9903
10477
  if (this.supportSelection !== false) {
9904
10478
  this.registerToolbarItem("resetFilter", () => {
9905
10479
  return DocumentHelper.createButton(() => {
9906
- this.visualizers.forEach((visualizer) => {
9907
- if (visualizer instanceof SelectBase || visualizer instanceof AlternativeVisualizersWrapper) {
9908
- visualizer.setSelection(undefined);
9909
- }
9910
- });
10480
+ this.resetFilter();
9911
10481
  }, localization.getString("resetFilter"));
9912
10482
  }, 900);
9913
10483
  }
@@ -9967,6 +10537,18 @@ class VisualizationPanel extends VisualizerBase {
9967
10537
  });
9968
10538
  }
9969
10539
  }
10540
+ resetFilter() {
10541
+ var _a;
10542
+ (_a = this.dataProvider) === null || _a === void 0 ? void 0 : _a.resetFilter();
10543
+ this.visualizers.forEach((visualizer) => {
10544
+ if (visualizer instanceof SelectBase || visualizer instanceof AlternativeVisualizersWrapper) {
10545
+ visualizer.setSelection(undefined);
10546
+ }
10547
+ if (visualizer instanceof VisualizationPanelDynamic) {
10548
+ visualizer.resetFilter();
10549
+ }
10550
+ });
10551
+ }
9970
10552
  reorderVisibleElements(order) {
9971
10553
  const newElements = [];
9972
10554
  order.forEach(name => {
@@ -10068,13 +10650,15 @@ class VisualizationPanel extends VisualizerBase {
10068
10650
  buildVisualizers(questions) {
10069
10651
  questions.forEach((question) => {
10070
10652
  let visualizerOptions = Object.assign({}, this.options);
10071
- let visualizerData = this.surveyData;
10653
+ if (visualizerOptions.dataProvider === undefined) {
10654
+ visualizerOptions.dataProvider = this.dataProvider;
10655
+ }
10072
10656
  let visualizer;
10073
10657
  if (Array.isArray(question)) {
10074
- visualizer = new (VisualizationManager.getPivotVisualizerConstructor())(question, visualizerData, visualizerOptions, undefined, false);
10658
+ visualizer = new (VisualizationManager.getPivotVisualizerConstructor())(question, [], visualizerOptions, undefined, false);
10075
10659
  }
10076
10660
  else {
10077
- visualizer = this.createVisualizer(question, visualizerOptions, visualizerData);
10661
+ visualizer = this.createVisualizer(question, visualizerOptions, []);
10078
10662
  }
10079
10663
  if (!visualizer) {
10080
10664
  return;
@@ -10145,7 +10729,7 @@ class VisualizationPanel extends VisualizerBase {
10145
10729
  question = Array.isArray(question) ? question[0] : question;
10146
10730
  const element = this.getElement(question.name);
10147
10731
  if (!!element) {
10148
- element.displayName = this.processText(question.title);
10732
+ element.displayName = this.getTitle(question);
10149
10733
  }
10150
10734
  });
10151
10735
  this.visualizers.forEach(v => {
@@ -10192,7 +10776,7 @@ class VisualizationPanel extends VisualizerBase {
10192
10776
  question = Array.isArray(question) ? question[0] : question;
10193
10777
  return {
10194
10778
  name: question.name,
10195
- displayName: this.processText(question.title),
10779
+ displayName: this.getTitle(question),
10196
10780
  isVisible: true,
10197
10781
  isPublic: true,
10198
10782
  };
@@ -10360,7 +10944,23 @@ class VisualizationPanel extends VisualizerBase {
10360
10944
  * @see IVisualizationPanelOptions.allowSelection
10361
10945
  */
10362
10946
  setFilter(questionName, selectedValue) {
10363
- this.dataProvider.setFilter(questionName, selectedValue);
10947
+ if (!this.dataPath) {
10948
+ this.dataProvider.setFilter(questionName, selectedValue);
10949
+ }
10950
+ else {
10951
+ if (selectedValue !== undefined && selectedValue !== null) {
10952
+ if (typeof selectedValue === "object" && Object.keys(selectedValue)[0]) {
10953
+ const seriesValue = Object.keys(selectedValue)[0];
10954
+ this.dataProvider.setFilter(this.dataPath, { [questionName]: selectedValue[seriesValue], [DataProvider.seriesMarkerKey]: seriesValue });
10955
+ }
10956
+ else {
10957
+ this.dataProvider.setFilter(this.dataPath, { [questionName]: selectedValue });
10958
+ }
10959
+ }
10960
+ else {
10961
+ this.dataProvider.setFilter(this.dataPath, undefined);
10962
+ }
10963
+ }
10364
10964
  }
10365
10965
  getState() {
10366
10966
  return {
@@ -10449,69 +11049,9 @@ class VisualizationPanel extends VisualizerBase {
10449
11049
  }
10450
11050
  }
10451
11051
 
10452
- class VisualizationPanelDynamic extends VisualizerBase {
10453
- constructor(question, data, options = {}, name) {
10454
- super(question, data, options, name || "panelDynamic");
10455
- this._panelVisualizer = undefined;
10456
- this.onAfterRenderPanelCallback = () => {
10457
- this.afterRender(this.contentContainer);
10458
- };
10459
- this.loadingData = false;
10460
- var options = Object.assign({}, options);
10461
- options.allowDynamicLayout = false;
10462
- options.dataProvider = undefined;
10463
- this._panelVisualizer = new VisualizationPanel(this.getQuestions(), [], options, undefined, false);
10464
- this._panelVisualizer.onAfterRender.add(this.onAfterRenderPanelCallback);
10465
- this.updateData(data);
10466
- }
10467
- setLocale(newLocale) {
10468
- super.setLocale(newLocale);
10469
- this._panelVisualizer.locale = newLocale;
10470
- }
10471
- get type() {
10472
- return "panelDynamic";
10473
- }
10474
- updatePanelVisualizerData() {
10475
- let panelData = [];
10476
- this.data.forEach((dataItem) => {
10477
- if (dataItem[this.question.name] !== undefined) {
10478
- panelData = panelData.concat(dataItem[this.question.name]);
10479
- }
10480
- });
10481
- this._panelVisualizer.updateData(panelData);
10482
- }
10483
- updateData(data) {
10484
- super.updateData(data);
10485
- this.updatePanelVisualizerData();
10486
- }
10487
- onDataChanged() {
10488
- this.updatePanelVisualizerData();
10489
- super.onDataChanged();
10490
- }
10491
- getQuestions() {
10492
- const paneldynamic = this.question;
10493
- return paneldynamic.template.questions;
10494
- }
10495
- destroyContent(container) {
10496
- this._panelVisualizer.clear();
10497
- super.destroyContent(this.contentContainer);
10498
- }
10499
- renderContent(container) {
10500
- this._panelVisualizer.render(container);
10501
- }
10502
- destroy() {
10503
- super.destroy();
10504
- this._panelVisualizer.onAfterRender.remove(this.onAfterRenderPanelCallback);
10505
- }
10506
- }
10507
- VisualizationManager.registerVisualizer("paneldynamic", VisualizationPanelDynamic);
10508
-
10509
11052
  class VisualizationMatrixDynamic extends VisualizationPanelDynamic {
10510
- constructor(question, data, options) {
10511
- super(question, data, options);
10512
- }
10513
- get type() {
10514
- return "matrixDynamic";
11053
+ constructor(question, data, options, name) {
11054
+ super(question, data, options, name || "matrixDynamic");
10515
11055
  }
10516
11056
  getQuestions() {
10517
11057
  const matrixdynamic = this.question;
@@ -10537,28 +11077,29 @@ function isChoicesArraysEqual(choices1, choices2) {
10537
11077
  class VisualizationMatrixDropdown extends VisualizerBase {
10538
11078
  constructor(question, data, options = {}, name) {
10539
11079
  super(question, data, options, name || "matrixDropdown");
10540
- this._matrixDropdownVisualizer = undefined;
11080
+ this._contentVisualizer = undefined;
10541
11081
  this.onPanelAfterRenderCallback = () => {
10542
11082
  this.afterRender(this.contentContainer);
10543
11083
  };
10544
11084
  this.loadingData = false;
10545
11085
  this._childOptions = Object.assign({}, options);
10546
11086
  this._childOptions.disableLocaleSwitch = true;
10547
- this._childOptions.dataProvider = undefined;
10548
11087
  this._childOptions.allowDynamicLayout = false;
10549
11088
  this._childOptions.transposeData = true;
11089
+ this._childOptions.dataProvider = this.dataProvider;
11090
+ this._childOptions.dataPath = this.dataNames[0];
10550
11091
  this._childOptions.seriesValues = question.rows.map((row) => row.value);
10551
11092
  this._childOptions.seriesLabels = question.rows.map((row) => row.text);
11093
+ this.dataProvider.fixDropdownData(this.dataNames);
10552
11094
  if (this.canGroupColumns) {
10553
11095
  var creators = VisualizationManager.getVisualizersByType("matrixdropdown-grouped");
10554
- this._matrixDropdownVisualizer = new creators[0](this.question, [], this._childOptions);
11096
+ this._contentVisualizer = new creators[0](this.question, [], this._childOptions);
10555
11097
  }
10556
11098
  else {
10557
11099
  const innerQuestions = this.getQuestions();
10558
- this._matrixDropdownVisualizer = new VisualizationPanel(innerQuestions, [], this._childOptions, undefined, false);
11100
+ this._contentVisualizer = new VisualizationPanel(innerQuestions, [], this._childOptions, undefined, false);
10559
11101
  }
10560
- this._matrixDropdownVisualizer.onAfterRender.add(this.onPanelAfterRenderCallback);
10561
- this.updateData(data);
11102
+ this._contentVisualizer.onAfterRender.add(this.onPanelAfterRenderCallback);
10562
11103
  }
10563
11104
  get canGroupColumns() {
10564
11105
  const innerQuestions = this.getQuestions();
@@ -10568,32 +11109,14 @@ class VisualizationMatrixDropdown extends VisualizerBase {
10568
11109
  setLocale(newLocale) {
10569
11110
  super.setLocale(newLocale);
10570
11111
  this._childOptions.seriesLabels = this.question.rows.map((row) => row.text);
10571
- this._matrixDropdownVisualizer.locale = newLocale;
11112
+ this._contentVisualizer.locale = newLocale;
10572
11113
  }
10573
- get matrixDropdownVisualizer() {
10574
- return this._matrixDropdownVisualizer;
10575
- }
10576
- updateDropdownVisualizerData() {
10577
- let panelData = [];
10578
- this.data.forEach((dataItem) => {
10579
- let rawDataItem = dataItem[this.question.name];
10580
- if (!!rawDataItem) {
10581
- Object.keys(rawDataItem).forEach((key) => {
10582
- var nestedDataItem = Object.assign({}, rawDataItem[key]);
10583
- nestedDataItem[DataProvider.seriesMarkerKey] = key;
10584
- panelData.push(nestedDataItem);
10585
- });
10586
- }
10587
- });
10588
- this._matrixDropdownVisualizer.updateData(panelData);
11114
+ get contentVisualizer() {
11115
+ return this._contentVisualizer;
10589
11116
  }
10590
11117
  updateData(data) {
10591
11118
  super.updateData(data);
10592
- this.updateDropdownVisualizerData();
10593
- }
10594
- onDataChanged() {
10595
- this.updateDropdownVisualizerData();
10596
- super.onDataChanged();
11119
+ this.dataProvider.fixDropdownData(this.dataNames);
10597
11120
  }
10598
11121
  getQuestions() {
10599
11122
  const matrixdropdown = this.question;
@@ -10606,19 +11129,32 @@ class VisualizationMatrixDropdown extends VisualizerBase {
10606
11129
  });
10607
11130
  }
10608
11131
  destroyContent(container) {
10609
- this._matrixDropdownVisualizer.clear();
11132
+ this._contentVisualizer.clear();
10610
11133
  super.destroyContent(this.contentContainer);
10611
11134
  }
10612
11135
  renderContent(container) {
10613
- this._matrixDropdownVisualizer.render(container);
11136
+ this._contentVisualizer.render(container);
10614
11137
  }
10615
11138
  destroy() {
10616
11139
  super.destroy();
10617
- this._matrixDropdownVisualizer.onAfterRender.remove(this.onPanelAfterRenderCallback);
11140
+ this._contentVisualizer.onAfterRender.remove(this.onPanelAfterRenderCallback);
10618
11141
  }
10619
11142
  }
10620
11143
  VisualizationManager.registerVisualizer("matrixdropdown", VisualizationMatrixDropdown);
10621
11144
 
11145
+ class VisualizationComposite extends VisualizationPanelDynamic {
11146
+ constructor(question, data, options, name) {
11147
+ super(question, data, options, name || "composite");
11148
+ }
11149
+ getQuestions() {
11150
+ const questionComposite = this.question;
11151
+ const innerQuestions = [];
11152
+ questionComposite.contentPanel.addQuestionsToList(innerQuestions);
11153
+ return innerQuestions;
11154
+ }
11155
+ }
11156
+ VisualizationManager.registerVisualizer("composite", VisualizationComposite);
11157
+
10622
11158
  var stopWords$3 = [
10623
11159
  "...",
10624
11160
  "a",
@@ -12627,5 +13163,5 @@ NpsVisualizer.DetractorScore = 6;
12627
13163
  NpsVisualizer.PromoterScore = 9;
12628
13164
  // VisualizationManager.registerVisualizer("rating", NpsVisualizer);
12629
13165
 
12630
- export { AlternativeVisualizersWrapper as A, BooleanModel as B, DataProvider as D, HistogramModel as H, Matrix as M, NumberModel as N, PostponeHelper as P, RankingModel as R, SelectBase as S, TextTableAdapter as T, VisualizerFactory as V, WordCloudAdapter as W, __awaiter as _, VisualizerBase as a, VisualizationManager as b, VisualizationPanel as c, VisualizationPanelDynamic as d, VisualizationMatrixDynamic as e, VisualizationMatrixDropdown as f, WordCloud as g, hideEmptyAnswersInData as h, Text as i, StatisticsTableAdapter as j, StatisticsTable as k, StatisticsTableBoolean as l, NpsVisualizerWidget as m, NpsAdapter as n, NpsVisualizer as o, PivotModel as p, defaultStatisticsCalculator as q, textHelper as t };
13166
+ export { AlternativeVisualizersWrapper as A, BooleanModel as B, DataProvider as D, HistogramModel as H, Matrix as M, NumberModel as N, PostponeHelper as P, RankingModel as R, SelectBase as S, TextTableAdapter as T, VisualizerFactory as V, WordCloudAdapter as W, __awaiter as _, VisualizerBase as a, VisualizationManager as b, VisualizationPanel as c, VisualizationPanelDynamic as d, VisualizationMatrixDynamic as e, VisualizationMatrixDropdown as f, getBestIntervalMode as g, hideEmptyAnswersInData as h, intervalCalculators as i, VisualizationComposite as j, WordCloud as k, Text as l, StatisticsTableAdapter as m, StatisticsTable as n, StatisticsTableBoolean as o, NpsVisualizerWidget as p, NpsAdapter as q, NpsVisualizer as r, PivotModel as s, textHelper as t, defaultStatisticsCalculator as u };
12631
13167
  //# sourceMappingURL=shared2.mjs.map