survey-analytics 2.2.2 → 2.2.4

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 (55) hide show
  1. package/fesm/shared.mjs +10 -2
  2. package/fesm/shared.mjs.map +1 -1
  3. package/fesm/shared2.mjs +748 -262
  4. package/fesm/shared2.mjs.map +1 -1
  5. package/fesm/survey.analytics.core.mjs +2 -2
  6. package/fesm/survey.analytics.mjs +315 -538
  7. package/fesm/survey.analytics.mjs.map +1 -1
  8. package/fesm/survey.analytics.tabulator.mjs +1 -1
  9. package/package.json +8 -8
  10. package/survey-analytics-tabulator.types/analytics-localization/english.d.ts +2 -0
  11. package/survey-analytics-tabulator.types/localizationManager.d.ts +2 -0
  12. package/survey-analytics-tabulator.types/utils/index.d.ts +1 -1
  13. package/survey-analytics.types/alternativeVizualizersWrapper.d.ts +2 -0
  14. package/survey-analytics.types/analytics-localization/english.d.ts +2 -0
  15. package/survey-analytics.types/boolean.d.ts +0 -1
  16. package/survey-analytics.types/entries/summary.core.d.ts +3 -1
  17. package/survey-analytics.types/histogram.d.ts +1 -1
  18. package/survey-analytics.types/localizationManager.d.ts +2 -0
  19. package/survey-analytics.types/matrix.d.ts +0 -1
  20. package/survey-analytics.types/pivot.d.ts +64 -0
  21. package/survey-analytics.types/plotly/chart-adapter.d.ts +13 -0
  22. package/survey-analytics.types/plotly/index.d.ts +2 -7
  23. package/survey-analytics.types/plotly/legacy.d.ts +33 -0
  24. package/survey-analytics.types/plotly/setup.d.ts +5 -3
  25. package/survey-analytics.types/{plotly/ranking.d.ts → ranking.d.ts} +2 -2
  26. package/survey-analytics.types/selectBase.d.ts +3 -1
  27. package/survey-analytics.types/utils/index.d.ts +1 -1
  28. package/survey-analytics.types/visualizationManager.d.ts +3 -0
  29. package/survey-analytics.types/visualizerBase.d.ts +13 -1
  30. package/survey.analytics.core.css +7 -1
  31. package/survey.analytics.core.css.map +1 -1
  32. package/survey.analytics.core.js +659 -51
  33. package/survey.analytics.core.js.map +1 -1
  34. package/survey.analytics.core.min.css +2 -2
  35. package/survey.analytics.core.min.js +1 -1
  36. package/survey.analytics.core.min.js.LICENSE.txt +1 -1
  37. package/survey.analytics.css +7 -1
  38. package/survey.analytics.css.map +1 -1
  39. package/survey.analytics.js +1048 -817
  40. package/survey.analytics.js.map +1 -1
  41. package/survey.analytics.min.css +2 -2
  42. package/survey.analytics.min.js +1 -1
  43. package/survey.analytics.min.js.LICENSE.txt +1 -1
  44. package/survey.analytics.tabulator.css +1 -1
  45. package/survey.analytics.tabulator.js +10 -2
  46. package/survey.analytics.tabulator.js.map +1 -1
  47. package/survey.analytics.tabulator.min.css +1 -1
  48. package/survey.analytics.tabulator.min.js +1 -1
  49. package/survey.analytics.tabulator.min.js.LICENSE.txt +1 -1
  50. package/survey-analytics.types/plotly/boolean.d.ts +0 -16
  51. package/survey-analytics.types/plotly/histogram.d.ts +0 -13
  52. package/survey-analytics.types/plotly/matrix.d.ts +0 -11
  53. package/survey-analytics.types/plotly/matrixdropdown-grouped.d.ts +0 -11
  54. package/survey-analytics.types/plotly/rating.d.ts +0 -20
  55. package/survey-analytics.types/plotly/selectBase.d.ts +0 -26
package/fesm/shared2.mjs CHANGED
@@ -1,11 +1,11 @@
1
1
  /*!
2
- * surveyjs - SurveyJS Dashboard library v2.2.2
2
+ * surveyjs - SurveyJS Dashboard library v2.2.4
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
- import { Event, QuestionCommentModel, settings, ItemValue, hasLicense, surveyLocalization, IsTouch, Helpers } from 'survey-core';
8
7
  import { D as DocumentHelper, l as localization, f as createLoadingIndicator, a as DataHelper, e as svgTemplate, d as createCommercialLicenseLink, t as toPrecision } from './shared.mjs';
8
+ import { Event, QuestionCommentModel, settings, ItemValue, hasLicense, surveyLocalization, IsTouch, Helpers } from 'survey-core';
9
9
 
10
10
  /******************************************************************************
11
11
  Copyright (c) Microsoft Corporation.
@@ -240,8 +240,15 @@ class VisualizationManager {
240
240
  static registerAltVisualizerSelector(constructor) {
241
241
  VisualizationManager.alternativesVisualizer = constructor;
242
242
  }
243
+ static getPivotVisualizerConstructor() {
244
+ return VisualizationManager.pivotVisualizer || VisualizerBase;
245
+ }
246
+ static registerPivotVisualizer(constructor) {
247
+ VisualizationManager.pivotVisualizer = constructor;
248
+ }
243
249
  }
244
250
  VisualizationManager.alternativesVisualizer = undefined;
251
+ VisualizationManager.pivotVisualizer = undefined;
245
252
  VisualizationManager.vizualizers = {};
246
253
 
247
254
  /**
@@ -349,6 +356,7 @@ class VisualizerBase {
349
356
  this.contentContainer = undefined;
350
357
  this.footerContainer = undefined;
351
358
  this._supportSelection = false;
359
+ this._chartAdapter = undefined;
352
360
  /**
353
361
  * An event that is raised after the visualizer's content is rendered.
354
362
  *
@@ -447,12 +455,12 @@ class VisualizerBase {
447
455
  get hasFooter() {
448
456
  return (!!this.question && (this.question.hasComment || this.question.hasOther));
449
457
  }
450
- createVisualizer(question, options) {
458
+ createVisualizer(question, options, data) {
451
459
  let visualizerOptions = Object.assign({}, options || this.options);
452
460
  if (visualizerOptions.dataProvider === undefined) {
453
461
  visualizerOptions.dataProvider = this.dataProvider;
454
462
  }
455
- return VisualizerFactory.createVisualizer(question, this.data, visualizerOptions);
463
+ return VisualizerFactory.createVisualizer(question, data || this.data, visualizerOptions);
456
464
  }
457
465
  /**
458
466
  * Allows you to access the footer visualizer. Returns `undefined` if the footer is absent.
@@ -666,9 +674,10 @@ class VisualizerBase {
666
674
  if (!!this.options && typeof this.options.destroyContent === "function") {
667
675
  this.options.destroyContent(container, this);
668
676
  }
669
- else {
670
- container.innerHTML = "";
677
+ else if (this._chartAdapter) {
678
+ this._chartAdapter.destroy(container.children[0]);
671
679
  }
680
+ container.innerHTML = "";
672
681
  }
673
682
  renderHeader(container) {
674
683
  if (!!this.options && typeof this.options.renderHeader === "function") {
@@ -682,10 +691,16 @@ class VisualizerBase {
682
691
  }
683
692
  renderContentAsync(container) {
684
693
  return __awaiter(this, void 0, void 0, function* () {
685
- return new Promise((resolve, reject) => {
694
+ if (this._chartAdapter) {
695
+ const chartNode = DocumentHelper.createElement("div");
696
+ container.innerHTML = "";
697
+ container.appendChild(chartNode);
698
+ yield this._chartAdapter.create(chartNode);
699
+ }
700
+ else {
686
701
  container.innerText = localization.getString("noVisualizerForQuestion");
687
- resolve(container);
688
- });
702
+ }
703
+ return container;
689
704
  });
690
705
  }
691
706
  ensureQuestionIsReady() {
@@ -764,10 +779,31 @@ class VisualizerBase {
764
779
  targetElement.appendChild(this.footerContainer);
765
780
  this.renderFooter(this.footerContainer);
766
781
  }
767
- updateContent() {
782
+ updateToolbar() {
783
+ if (!!this.toolbarContainer) {
784
+ PostponeHelper.postpone(() => {
785
+ this.destroyToolbar(this.toolbarContainer);
786
+ this.renderToolbar(this.toolbarContainer);
787
+ });
788
+ }
789
+ }
790
+ isSupportSoftUpdateContent() {
791
+ return false;
792
+ }
793
+ softUpdateContent() {
794
+ }
795
+ hardUpdateContent() {
768
796
  this.destroyContent(this.contentContainer);
769
797
  this.renderContent(this.contentContainer);
770
798
  }
799
+ updateContent() {
800
+ if (!this.isSupportSoftUpdateContent()) {
801
+ this.hardUpdateContent();
802
+ }
803
+ else {
804
+ this.softUpdateContent();
805
+ }
806
+ }
771
807
  /**
772
808
  * Re-renders the visualizer and its content.
773
809
  */
@@ -958,6 +994,7 @@ class VisualizerBase {
958
994
  }
959
995
  }
960
996
  VisualizerBase.suppressVisualizerStubRendering = false;
997
+ VisualizerBase.chartAdapterType = undefined;
961
998
  // public static otherCommentQuestionType = "comment"; // TODO: make it configureable - allow choose what kind of question/visualizer will be used for comments/others
962
999
  VisualizerBase.otherCommentCollapsed = true;
963
1000
  VisualizerBase.customColors = [];
@@ -1031,6 +1068,131 @@ function defaultStatisticsCalculator(data, dataInfo) {
1031
1068
  return dataInfo.dataNames.length > 1 ? statistics : statistics[0];
1032
1069
  }
1033
1070
 
1071
+ class NumberModel extends VisualizerBase {
1072
+ constructor(question, data, options = {}, name) {
1073
+ super(question, data, options, name || "number");
1074
+ if (VisualizerBase.chartAdapterType) {
1075
+ this._chartAdapter = new VisualizerBase.chartAdapterType(this);
1076
+ this.chartTypes = this._chartAdapter.getChartTypes();
1077
+ this.chartType = this.chartTypes[0];
1078
+ }
1079
+ this.registerToolbarItem("changeChartType", () => {
1080
+ if (this.chartTypes.length > 1) {
1081
+ return DocumentHelper.createSelector(this.chartTypes.map((chartType) => {
1082
+ return {
1083
+ value: chartType,
1084
+ text: localization.getString("chartType_" + chartType),
1085
+ };
1086
+ }), (option) => this.chartType === option.value, (e) => {
1087
+ this.setChartType(e.target.value);
1088
+ });
1089
+ }
1090
+ return null;
1091
+ });
1092
+ }
1093
+ onDataChanged() {
1094
+ this._resultAverage = undefined;
1095
+ this._resultMin = undefined;
1096
+ this._resultMax = undefined;
1097
+ super.onDataChanged();
1098
+ }
1099
+ onChartTypeChanged() { }
1100
+ setChartType(chartType) {
1101
+ if (this.chartTypes.indexOf(chartType) !== -1 &&
1102
+ this.chartType !== chartType) {
1103
+ this.chartType = chartType;
1104
+ this.onChartTypeChanged();
1105
+ if (!!this.contentContainer) {
1106
+ this.destroyContent(this.contentContainer);
1107
+ this.renderContent(this.contentContainer);
1108
+ }
1109
+ this.invokeOnUpdate();
1110
+ }
1111
+ }
1112
+ destroy() {
1113
+ this._resultAverage = undefined;
1114
+ this._resultMin = undefined;
1115
+ this._resultMax = undefined;
1116
+ super.destroy();
1117
+ }
1118
+ generateText(maxValue, minValue, stepsCount) {
1119
+ let texts = [];
1120
+ if (stepsCount === 5) {
1121
+ texts = [
1122
+ "very high (" + maxValue + ")",
1123
+ "high",
1124
+ "medium",
1125
+ "low",
1126
+ "very low (" + minValue + ")",
1127
+ ];
1128
+ }
1129
+ else {
1130
+ texts.push(maxValue);
1131
+ for (let i = 0; i < stepsCount - 2; i++) {
1132
+ texts.push("");
1133
+ }
1134
+ texts.push(minValue);
1135
+ }
1136
+ if (!!NumberModel.generateTextsCallback) {
1137
+ return NumberModel.generateTextsCallback(this.question, maxValue, minValue, stepsCount, texts);
1138
+ }
1139
+ return texts;
1140
+ }
1141
+ generateValues(maxValue, stepsCount) {
1142
+ const values = [];
1143
+ for (let i = 0; i < stepsCount; i++) {
1144
+ values.push(maxValue / stepsCount);
1145
+ }
1146
+ values.push(maxValue);
1147
+ return values;
1148
+ }
1149
+ generateColors(maxValue, minValue, stepsCount) {
1150
+ const palette = this.getColors();
1151
+ const colors = [];
1152
+ for (let i = 0; i < stepsCount; i++) {
1153
+ colors.push(palette[i]);
1154
+ }
1155
+ colors.push("rgba(255, 255, 255, 0)");
1156
+ return colors;
1157
+ }
1158
+ convertFromExternalData(externalCalculatedData) {
1159
+ return [externalCalculatedData.value || 0, externalCalculatedData.minValue || 0, externalCalculatedData.maxValue || 0];
1160
+ }
1161
+ getCalculatedValuesCore() {
1162
+ if (this._resultAverage === undefined ||
1163
+ this._resultMin === undefined ||
1164
+ this._resultMax === undefined) {
1165
+ this._resultMin = Number.MAX_VALUE;
1166
+ this._resultMax = -Number.MAX_VALUE;
1167
+ this._resultAverage = 0;
1168
+ let actualAnswerCount = 0;
1169
+ this.data.forEach((rowData) => {
1170
+ if (rowData[this.question.name] !== undefined) {
1171
+ const questionValue = +rowData[this.question.name];
1172
+ actualAnswerCount++;
1173
+ this._resultAverage += questionValue;
1174
+ if (this._resultMin > questionValue) {
1175
+ this._resultMin = questionValue;
1176
+ }
1177
+ if (this._resultMax < questionValue) {
1178
+ this._resultMax = questionValue;
1179
+ }
1180
+ }
1181
+ });
1182
+ if (actualAnswerCount > 0) {
1183
+ this._resultAverage = this._resultAverage / actualAnswerCount;
1184
+ }
1185
+ this._resultAverage = Math.ceil(this._resultAverage * 100) / 100;
1186
+ }
1187
+ return [this._resultAverage, this._resultMin, this._resultMax];
1188
+ }
1189
+ }
1190
+ NumberModel.stepsCount = 5;
1191
+ NumberModel.showAsPercentage = false;
1192
+ VisualizationManager.registerVisualizer("number", NumberModel, 200);
1193
+ VisualizationManager.registerVisualizer("rating", NumberModel, 200);
1194
+ VisualizationManager.registerVisualizer("expression", NumberModel);
1195
+
1034
1196
  function hideEmptyAnswersInData(answersData) {
1035
1197
  const result = {
1036
1198
  datasets: [],
@@ -1056,9 +1218,9 @@ function hideEmptyAnswersInData(answersData) {
1056
1218
  seriesDataExistence.length = answersData.seriesLabels.length;
1057
1219
  const valuesDataExistence = [];
1058
1220
  valuesDataExistence.length = answersData.labels.length;
1059
- for (var valueIndex = 0; valueIndex < answersData.labels.length; valueIndex++) {
1060
- for (var seriesIndex = 0; seriesIndex < answersData.seriesLabels.length; seriesIndex++) {
1061
- if (answersData.datasets[valueIndex][seriesIndex] != 0) {
1221
+ for (var seriesIndex = 0; seriesIndex < answersData.seriesLabels.length; seriesIndex++) {
1222
+ for (var valueIndex = 0; valueIndex < answersData.labels.length; valueIndex++) {
1223
+ if (answersData.datasets[seriesIndex][valueIndex] != 0) {
1062
1224
  seriesDataExistence[seriesIndex] = true;
1063
1225
  valuesDataExistence[valueIndex] = true;
1064
1226
  }
@@ -1075,14 +1237,14 @@ function hideEmptyAnswersInData(answersData) {
1075
1237
  result.seriesLabels.push(answersData.seriesLabels[seriesIndex]);
1076
1238
  }
1077
1239
  }
1078
- for (var valueIndex = 0; valueIndex < answersData.labels.length; valueIndex++) {
1079
- if (valuesDataExistence[valueIndex]) {
1240
+ for (var seriesIndex = 0; seriesIndex < answersData.datasets.length; seriesIndex++) {
1241
+ if (seriesDataExistence[seriesIndex]) {
1080
1242
  const dataset = [];
1081
1243
  const texts = [];
1082
- for (var seriesIndex = 0; seriesIndex < answersData.datasets.length; seriesIndex++) {
1083
- if (seriesDataExistence[seriesIndex]) {
1084
- dataset.push(answersData.datasets[valueIndex][seriesIndex]);
1085
- texts.push(answersData.texts[valueIndex][seriesIndex]);
1244
+ for (var valueIndex = 0; valueIndex < answersData.labels.length; valueIndex++) {
1245
+ if (valuesDataExistence[valueIndex]) {
1246
+ dataset.push(answersData.datasets[seriesIndex][valueIndex]);
1247
+ texts.push(answersData.texts[seriesIndex][valueIndex]);
1086
1248
  }
1087
1249
  }
1088
1250
  result.datasets.push(dataset);
@@ -1119,17 +1281,34 @@ class SelectBase extends VisualizerBase {
1119
1281
  * options fields can be modified
1120
1282
  */
1121
1283
  this.onAnswersDataReady = new Event();
1122
- question.visibleChoicesChangedCallback = () => {
1123
- this.dataProvider.raiseDataChanged();
1124
- };
1284
+ if (!!question) { // TODO: move somewhere else
1285
+ question.visibleChoicesChangedCallback = () => {
1286
+ this.dataProvider.raiseDataChanged();
1287
+ };
1288
+ }
1125
1289
  this._showPercentages = this.options.showPercentages === true;
1126
1290
  this._showOnlyPercentages = this.options.showOnlyPercentages === true;
1127
1291
  if (this.options.percentagePrecision) {
1128
1292
  this._percentagePrecision = this.options.percentagePrecision;
1129
1293
  }
1294
+ if (this.options.transposeData !== undefined) {
1295
+ this._transposeData = this.options.transposeData;
1296
+ }
1130
1297
  this._hideEmptyAnswers = this.options.hideEmptyAnswers === true;
1131
1298
  this._answersOrder = this.options.answersOrder || "default";
1132
1299
  this._showMissingAnswers = this.isSupportMissingAnswers() && this.options.showMissingAnswers === true;
1300
+ if (this.options.allowExperimentalFeatures) ;
1301
+ if (VisualizerBase.chartAdapterType) {
1302
+ this._chartAdapter = new VisualizerBase.chartAdapterType(this);
1303
+ this.chartTypes = this._chartAdapter.getChartTypes();
1304
+ if (this.getSeriesValues().length > 0 && this.chartTypes.indexOf("stackedbar") === -1) {
1305
+ this.chartTypes.push("stackedbar");
1306
+ }
1307
+ this._chartType = this.chartTypes[0];
1308
+ if (this.chartTypes.indexOf(this.options.defaultChartType) !== -1) {
1309
+ this._chartType = this.options.defaultChartType;
1310
+ }
1311
+ }
1133
1312
  this.registerToolbarItem("changeChartType", () => {
1134
1313
  if (this.chartTypes.length > 1) {
1135
1314
  return DocumentHelper.createSelector(this.chartTypes.map((chartType) => {
@@ -1307,6 +1486,16 @@ class SelectBase extends VisualizerBase {
1307
1486
  const selectBaseQuestion = this.question;
1308
1487
  return resultValues.map((value) => ItemValue.getTextOrHtmlByValue(selectBaseQuestion.choices, value)).join(", ");
1309
1488
  }
1489
+ isSupportSoftUpdateContent() {
1490
+ return true;
1491
+ }
1492
+ softUpdateContent() {
1493
+ var _a;
1494
+ const chartNode = (_a = this.contentContainer) === null || _a === void 0 ? void 0 : _a.children[0];
1495
+ if (chartNode) {
1496
+ this._chartAdapter.update(chartNode);
1497
+ }
1498
+ }
1310
1499
  getSelectedItemByText(itemText) {
1311
1500
  const selBase = this.question;
1312
1501
  if (this.question.hasOther && itemText == selBase.otherText) {
@@ -1562,9 +1751,9 @@ class SelectBase extends VisualizerBase {
1562
1751
  const series = this.getSeriesValues();
1563
1752
  const innerCalculatedData = [];
1564
1753
  if (series.length > 0) {
1565
- for (let i = 0; i < values.length; i++) {
1754
+ for (let j = 0; j < series.length; j++) {
1566
1755
  const seriesData = [];
1567
- for (let j = 0; j < series.length; j++) {
1756
+ for (let i = 0; i < values.length; i++) {
1568
1757
  if (!!externalCalculatedData[series[j]]) {
1569
1758
  seriesData.push(externalCalculatedData[series[j]][values[i]] || 0);
1570
1759
  }
@@ -1617,10 +1806,252 @@ class SelectBase extends VisualizerBase {
1617
1806
  }
1618
1807
  SelectBase.topNValuesDefaults = [-1, 5, 10, 20];
1619
1808
  SelectBase._stateProperties = ["chartType", "answersOrder", "hideEmptyAnswers", "topN"];
1809
+ VisualizationManager.registerVisualizer("checkbox", SelectBase);
1810
+ VisualizationManager.registerVisualizer("radiogroup", SelectBase);
1811
+ VisualizationManager.registerVisualizer("dropdown", SelectBase);
1812
+ VisualizationManager.registerVisualizer("imagepicker", SelectBase);
1813
+ VisualizationManager.registerVisualizer("tagbox", SelectBase);
1814
+
1815
+ class BooleanModel extends SelectBase {
1816
+ constructor(question, data, options, name) {
1817
+ super(question, data, options, name || "boolean");
1818
+ }
1819
+ getCorrectAnswerText() {
1820
+ const correctAnswerValue = this.booleanQuestion.correctAnswer;
1821
+ if (this.booleanQuestion.valueTrue !== undefined && this.booleanQuestion.valueTrue === correctAnswerValue || !!correctAnswerValue) {
1822
+ return this.booleanQuestion.locLabelTrue.textOrHtml;
1823
+ }
1824
+ if (this.booleanQuestion.valueFalse !== undefined && this.booleanQuestion.valueFalse === correctAnswerValue || !correctAnswerValue) {
1825
+ return this.booleanQuestion.locLabelFalse.textOrHtml;
1826
+ }
1827
+ return correctAnswerValue;
1828
+ }
1829
+ get booleanQuestion() {
1830
+ return this.question;
1831
+ }
1832
+ getSelectedItemByText(itemText) {
1833
+ const labels = this.getLabels();
1834
+ const values = this.getValues();
1835
+ return new ItemValue(values[labels.indexOf(itemText)], itemText);
1836
+ }
1837
+ getValues() {
1838
+ const values = [
1839
+ this.booleanQuestion.valueTrue !== undefined
1840
+ ? this.booleanQuestion.valueTrue
1841
+ : true,
1842
+ this.booleanQuestion.valueFalse !== undefined
1843
+ ? this.booleanQuestion.valueFalse
1844
+ : false,
1845
+ ];
1846
+ if (this.showMissingAnswers) {
1847
+ values.push(undefined);
1848
+ }
1849
+ return values;
1850
+ }
1851
+ getLabels() {
1852
+ var labels = [].concat(this.getValues());
1853
+ if (this.booleanQuestion.labelTrue !== undefined) {
1854
+ labels[0] = this.booleanQuestion.locLabelTrue.textOrHtml;
1855
+ }
1856
+ if (this.booleanQuestion.labelFalse !== undefined) {
1857
+ labels[1] = this.booleanQuestion.locLabelFalse.textOrHtml;
1858
+ }
1859
+ if (this.showMissingAnswers) {
1860
+ labels[2] = localization.getString("missingAnswersLabel");
1861
+ }
1862
+ return labels;
1863
+ }
1864
+ }
1865
+ BooleanModel.trueColor = "";
1866
+ BooleanModel.falseColor = "";
1867
+ VisualizationManager.registerVisualizer("boolean", BooleanModel);
1868
+
1869
+ class HistogramModel extends SelectBase {
1870
+ constructor(question, data, options, name) {
1871
+ super(question, data, options, name || "histogram");
1872
+ this.valueType = "number";
1873
+ this._cachedValues = undefined;
1874
+ this._continiousData = undefined;
1875
+ this._cachedIntervals = undefined;
1876
+ this._intervalPrecision = 2;
1877
+ this._transposeData = false;
1878
+ if (this.options.intervalPrecision !== undefined) {
1879
+ this._intervalPrecision = this.options.intervalPrecision;
1880
+ }
1881
+ const questionType = question.getType();
1882
+ if (questionType === "text" && (question["inputType"] === "date" || question["inputType"] === "datetime")) {
1883
+ this.valueType = "date";
1884
+ }
1885
+ else {
1886
+ this.valueType = "number";
1887
+ }
1888
+ }
1889
+ reset() {
1890
+ this._continiousData = undefined;
1891
+ this._cachedValues = undefined;
1892
+ this._cachedIntervals = undefined;
1893
+ }
1894
+ getContiniousValue(value) {
1895
+ if (this.valueType === "date") {
1896
+ return Date.parse(value);
1897
+ }
1898
+ return parseFloat(value);
1899
+ }
1900
+ getString(value) {
1901
+ if (this.valueType === "date") {
1902
+ return new Date(value).toLocaleDateString();
1903
+ }
1904
+ return "" + value;
1905
+ }
1906
+ toPrecision(value) {
1907
+ const base = Math.pow(10, this._intervalPrecision);
1908
+ return Math.round(base * value) / base;
1909
+ }
1910
+ getSelectedItemByText(itemText) {
1911
+ if (this.hasCustomIntervals || this.getContiniousValues().length > HistogramModel.UseIntervalsFrom) {
1912
+ const interval = this.intervals.filter(interval => interval.label === itemText)[0];
1913
+ return new ItemValue(interval, interval !== undefined ? interval.label : "");
1914
+ }
1915
+ const labels = this.getLabels();
1916
+ const labelIndex = labels.indexOf(itemText);
1917
+ return new ItemValue(this.getValues()[labelIndex], labels[labelIndex]);
1918
+ }
1919
+ /**
1920
+ * Updates visualizer data.
1921
+ */
1922
+ updateData(data) {
1923
+ this.reset();
1924
+ super.updateData(data);
1925
+ }
1926
+ onDataChanged() {
1927
+ this.reset();
1928
+ super.onDataChanged();
1929
+ }
1930
+ getContiniousValues() {
1931
+ if (this._cachedValues === undefined) {
1932
+ const series = this.getSeriesValues();
1933
+ if (series.length === 0) {
1934
+ series.push("");
1935
+ }
1936
+ this._continiousData = {};
1937
+ series.forEach(seriesValue => this._continiousData[seriesValue] = []);
1938
+ const hash = {};
1939
+ this.data.forEach(dataItem => {
1940
+ const answerData = dataItem[this.name];
1941
+ if (answerData !== undefined) {
1942
+ const seriesValue = dataItem[DataProvider.seriesMarkerKey] || "";
1943
+ // TODO: _continiousData should be sorted in order to speed-up statistics calculation in the getData function
1944
+ this._continiousData[seriesValue].push(this.getContiniousValue(answerData));
1945
+ hash[answerData] = answerData;
1946
+ }
1947
+ });
1948
+ this._cachedValues = Object.keys(hash).map(key => ({ original: hash[key], continious: this.getContiniousValue(key) }));
1949
+ this._cachedValues.sort((a, b) => a.continious - b.continious);
1950
+ }
1951
+ return this._cachedValues;
1952
+ }
1953
+ isSupportMissingAnswers() {
1954
+ return false;
1955
+ }
1956
+ get needUseRateValues() {
1957
+ return this.question.getType() == "rating" && Array.isArray(this.question["rateValues"]) && this.question["rateValues"].length > 0;
1958
+ }
1959
+ getValues() {
1960
+ return this.intervals.map(interval => interval.start);
1961
+ }
1962
+ getLabels() {
1963
+ return this.intervals.map(interval => interval.label);
1964
+ }
1965
+ get hasCustomIntervals() {
1966
+ return !!this.questionOptions && Array.isArray(this.questionOptions.intervals);
1967
+ }
1968
+ get intervals() {
1969
+ if (this.hasCustomIntervals) {
1970
+ return this.questionOptions.intervals;
1971
+ }
1972
+ if (this.question.getType() == "rating") {
1973
+ if (this.needUseRateValues) {
1974
+ const rateValues = this.question["rateValues"];
1975
+ rateValues.sort((iv1, iv2) => iv1.value - iv2.value);
1976
+ return rateValues.map((rateValue, i) => ({
1977
+ start: rateValue.value,
1978
+ end: i < rateValues.length - 1 ? rateValues[i + 1].value : rateValue.value + 1,
1979
+ label: rateValue.text
1980
+ }));
1981
+ }
1982
+ else {
1983
+ const rateIntervals = [];
1984
+ for (let i = (this.question["rateMin"] || 0); i <= (this.question["rateMax"] || (HistogramModel.IntervalsCount - 1)); i += (this.question["rateStep"] || 1)) {
1985
+ rateIntervals.push({
1986
+ start: i,
1987
+ end: i + 1,
1988
+ label: "" + (!!this.question["rateMin"] && !!this.question["rateMax"] ? i : (i + "-" + (i + 1)))
1989
+ });
1990
+ }
1991
+ return rateIntervals;
1992
+ }
1993
+ }
1994
+ if (this._cachedIntervals === undefined) {
1995
+ const continiousValues = this.getContiniousValues();
1996
+ this._cachedIntervals = [];
1997
+ if (continiousValues.length) {
1998
+ let start = continiousValues[0].continious;
1999
+ const end = continiousValues[continiousValues.length - 1].continious;
2000
+ const intervalsCount = HistogramModel.IntervalsCount;
2001
+ const delta = (end - start) / intervalsCount;
2002
+ for (let i = 0; i < intervalsCount; ++i) {
2003
+ const next = start + delta;
2004
+ const istart = this.toPrecision(start);
2005
+ const inext = this.toPrecision(next);
2006
+ this._cachedIntervals.push({
2007
+ start: istart,
2008
+ end: i < intervalsCount - 1 ? inext : inext + delta / 100,
2009
+ label: "" + this.getString(istart) + "-" + this.getString(inext)
2010
+ });
2011
+ start = next;
2012
+ }
2013
+ }
2014
+ }
2015
+ return this._cachedIntervals;
2016
+ }
2017
+ convertFromExternalData(externalCalculatedData) {
2018
+ return [externalCalculatedData];
2019
+ }
2020
+ getCalculatedValuesCore() {
2021
+ this.getContiniousValues();
2022
+ const intervals = this.intervals;
2023
+ const statistics = [];
2024
+ const series = this.getSeriesValues();
2025
+ if (series.length === 0) {
2026
+ series.push("");
2027
+ }
2028
+ for (var i = 0; i < series.length; ++i) {
2029
+ statistics.push(intervals.map(i => 0));
2030
+ this._continiousData[series[i]].forEach(dataValue => {
2031
+ for (let j = 0; j < intervals.length; ++j) {
2032
+ if (intervals[j].start <= dataValue && (dataValue < intervals[j].end || j == intervals.length - 1)) {
2033
+ statistics[i][j]++;
2034
+ break;
2035
+ }
2036
+ }
2037
+ });
2038
+ }
2039
+ return statistics;
2040
+ }
2041
+ getValueType() {
2042
+ return this.valueType;
2043
+ }
2044
+ }
2045
+ HistogramModel.IntervalsCount = 10;
2046
+ HistogramModel.UseIntervalsFrom = 10;
2047
+ VisualizationManager.registerVisualizer("date", HistogramModel);
2048
+ VisualizationManager.registerVisualizer("number", HistogramModel, 100);
2049
+ VisualizationManager.registerVisualizer("rating", HistogramModel, 100);
1620
2050
 
1621
2051
  class Matrix extends SelectBase {
1622
2052
  constructor(question, data, options, name) {
1623
2053
  super(question, data, options, name || "matrix");
2054
+ this._transposeData = true;
1624
2055
  // this.getAnswersData();
1625
2056
  }
1626
2057
  get matrixQuestion() {
@@ -1662,124 +2093,141 @@ class Matrix extends SelectBase {
1662
2093
  }
1663
2094
  hideEmptyAnswersInData(answersData) {
1664
2095
  const result = {
1665
- datasets: [],
1666
- labels: [],
1667
- colors: [],
1668
- texts: [],
1669
- seriesLabels: [],
1670
- };
1671
- const hasAnswersInAllSeriesArr = this.getHasAnswersInAllSeriesArray(answersData.datasets);
1672
- for (let i = 0; i < answersData.datasets.length; i++) {
1673
- const hasAnswersInSeries = this.getHasAnswersInSeries(answersData.datasets[i]);
1674
- if (hasAnswersInSeries) {
1675
- result.labels.push(answersData.labels[i]);
1676
- result.colors.push(answersData.colors[i]);
1677
- }
1678
- else {
1679
- continue;
1680
- }
1681
- const datasets = [];
1682
- const texts = [];
1683
- for (let j = 0; j < answersData.datasets[0].length; j++) {
1684
- if (hasAnswersInAllSeriesArr[j]) {
1685
- datasets.push(answersData.datasets[i][j]);
1686
- texts.push(answersData.texts[i][j]);
1687
- }
1688
- }
1689
- result.datasets.push(datasets);
1690
- result.texts.push(texts);
1691
- }
1692
- for (let i = 0; i < answersData.datasets[0].length; i++) {
1693
- if (hasAnswersInAllSeriesArr[i]) {
1694
- result.seriesLabels.push(answersData.seriesLabels[i]);
1695
- }
1696
- }
1697
- return result;
1698
- }
1699
- getCalculatedValuesCore() {
1700
- const statistics = super.getCalculatedValuesCore();
1701
- const series = this.getSeriesValues();
1702
- const values = this.getValues();
1703
- const preparedData = [];
1704
- values.forEach((val, valueIndex) => {
1705
- const seriesData = series.map((seriesName, seriesIndex) => statistics[seriesIndex][valueIndex]);
1706
- preparedData.push(seriesData);
1707
- });
1708
- return preparedData;
1709
- }
1710
- }
1711
-
1712
- class BooleanModel extends SelectBase {
1713
- constructor(question, data, options, name) {
1714
- super(question, data, options, name || "boolean");
1715
- }
1716
- getCorrectAnswerText() {
1717
- const correctAnswerValue = this.booleanQuestion.correctAnswer;
1718
- if (this.booleanQuestion.valueTrue !== undefined && this.booleanQuestion.valueTrue === correctAnswerValue || !!correctAnswerValue) {
1719
- return this.booleanQuestion.locLabelTrue.textOrHtml;
1720
- }
1721
- if (this.booleanQuestion.valueFalse !== undefined && this.booleanQuestion.valueFalse === correctAnswerValue || !correctAnswerValue) {
1722
- return this.booleanQuestion.locLabelFalse.textOrHtml;
1723
- }
1724
- return correctAnswerValue;
1725
- }
1726
- get booleanQuestion() {
1727
- return this.question;
1728
- }
1729
- getSelectedItemByText(itemText) {
1730
- const labels = this.getLabels();
1731
- const values = this.getValues();
1732
- return new ItemValue(values[labels.indexOf(itemText)], itemText);
1733
- }
1734
- getValues() {
1735
- const values = [
1736
- this.booleanQuestion.valueTrue !== undefined
1737
- ? this.booleanQuestion.valueTrue
1738
- : true,
1739
- this.booleanQuestion.valueFalse !== undefined
1740
- ? this.booleanQuestion.valueFalse
1741
- : false,
1742
- ];
1743
- if (this.showMissingAnswers) {
1744
- values.push(undefined);
1745
- }
1746
- return values;
1747
- }
1748
- getLabels() {
1749
- var labels = [].concat(this.getValues());
1750
- if (this.booleanQuestion.labelTrue !== undefined) {
1751
- labels[0] = this.booleanQuestion.locLabelTrue.textOrHtml;
1752
- }
1753
- if (this.booleanQuestion.labelFalse !== undefined) {
1754
- labels[1] = this.booleanQuestion.locLabelFalse.textOrHtml;
2096
+ datasets: [],
2097
+ labels: [],
2098
+ colors: [],
2099
+ texts: [],
2100
+ seriesLabels: [],
2101
+ };
2102
+ const hasAnswersInAllSeriesArr = this.getHasAnswersInAllSeriesArray(answersData.datasets);
2103
+ for (let i = 0; i < answersData.datasets.length; i++) {
2104
+ const hasAnswersInSeries = this.getHasAnswersInSeries(answersData.datasets[i]);
2105
+ if (hasAnswersInSeries) {
2106
+ result.labels.push(answersData.labels[i]);
2107
+ result.colors.push(answersData.colors[i]);
2108
+ }
2109
+ else {
2110
+ continue;
2111
+ }
2112
+ const datasets = [];
2113
+ const texts = [];
2114
+ for (let j = 0; j < answersData.datasets[0].length; j++) {
2115
+ if (hasAnswersInAllSeriesArr[j]) {
2116
+ datasets.push(answersData.datasets[i][j]);
2117
+ texts.push(answersData.texts[i][j]);
2118
+ }
2119
+ }
2120
+ result.datasets.push(datasets);
2121
+ result.texts.push(texts);
1755
2122
  }
1756
- if (this.showMissingAnswers) {
1757
- labels[2] = localization.getString("missingAnswersLabel");
2123
+ for (let i = 0; i < answersData.datasets[0].length; i++) {
2124
+ if (hasAnswersInAllSeriesArr[i]) {
2125
+ result.seriesLabels.push(answersData.seriesLabels[i]);
2126
+ }
1758
2127
  }
1759
- return labels;
2128
+ return result;
1760
2129
  }
1761
2130
  }
1762
- BooleanModel.trueColor = "";
1763
- BooleanModel.falseColor = "";
2131
+ VisualizationManager.registerVisualizer("matrix", Matrix);
1764
2132
 
1765
- class HistogramModel extends SelectBase {
1766
- constructor(question, data, options, name) {
1767
- super(question, data, options, name || "histogram");
1768
- this.valueType = "number";
2133
+ class PivotModel extends SelectBase {
2134
+ constructor(questions, data, options, name) {
2135
+ super(null, data, options, name || "pivot");
2136
+ this.questions = questions;
2137
+ this.valueType = "enum";
1769
2138
  this._cachedValues = undefined;
1770
2139
  this._continiousData = undefined;
1771
2140
  this._cachedIntervals = undefined;
1772
2141
  this._intervalPrecision = 2;
2142
+ this.axisYSelectors = [];
2143
+ this.axisYQuestionNames = [];
2144
+ this.questionsY = [];
2145
+ this.questions = this.questions.filter((question) => ["matrixdropdown", "matrixdynamic", "matrix", "file", "signature", "multipletext", "comment", "html", "image"].indexOf(question.getType()) === -1);
1773
2146
  if (this.options.intervalPrecision !== undefined) {
1774
2147
  this._intervalPrecision = this.options.intervalPrecision;
1775
2148
  }
2149
+ this.axisXQuestionName = this.questions.length > 0 ? this.questions[0].name : undefined;
2150
+ this.registerToolbarItem("axisXSelector", () => this.axisXSelector = DocumentHelper.createSelector(this.questions.map((question) => {
2151
+ return {
2152
+ value: question.name,
2153
+ text: question.title || question.name,
2154
+ };
2155
+ }), (option) => this.axisXQuestionName === option.value, (e) => { this.axisXQuestionName = e.target.value; this.setupPivot(); }, localization.getString("axisXSelectorTitle")));
2156
+ this.registerToolbarItem("axisYSelector0", this.createYSelecterGenerator());
2157
+ this.setupPivot();
2158
+ }
2159
+ createYSelecterGenerator() {
2160
+ const selectorIndex = this.axisYSelectors.length;
2161
+ return () => {
2162
+ let selector = this.axisYSelectors[selectorIndex];
2163
+ if (!selector) {
2164
+ selector = this.createAxisYSelector(selectorIndex);
2165
+ this.axisYSelectors.push(selector);
2166
+ }
2167
+ return selector;
2168
+ };
2169
+ }
2170
+ setAxisQuestions(...axisQuestionNames) {
2171
+ if (axisQuestionNames.length < 1) {
2172
+ return;
2173
+ }
2174
+ this.axisXQuestionName = axisQuestionNames[0];
2175
+ this.axisYQuestionNames = axisQuestionNames.splice(1);
2176
+ this.setupPivot();
2177
+ }
2178
+ onAxisYSelectorChanged(index, value) {
2179
+ this.axisYQuestionNames[index] = value;
2180
+ if (index < this.axisYSelectors.length - 1) {
2181
+ if (!value) {
2182
+ for (let i = index + 1; i < this.axisYSelectors.length; ++i) {
2183
+ this.unregisterToolbarItem("axisYSelector" + i);
2184
+ }
2185
+ this.axisYSelectors = this.axisYSelectors.slice(0, index + 1);
2186
+ this.axisYQuestionNames = this.axisYQuestionNames.slice(0, index + 1);
2187
+ this.updateToolbar();
2188
+ }
2189
+ }
2190
+ else {
2191
+ if (!!value) {
2192
+ this.registerToolbarItem("axisYSelector" + this.axisYSelectors.length, this.createYSelecterGenerator());
2193
+ this.updateToolbar();
2194
+ }
2195
+ }
2196
+ this.setupPivot();
2197
+ }
2198
+ createAxisYSelector(selectorIndex) {
2199
+ const selector = DocumentHelper.createSelector([{ value: "", text: "Not selected" }].concat(this.questions.map((question) => {
2200
+ return {
2201
+ value: question.name,
2202
+ text: question.title || question.name,
2203
+ };
2204
+ })), (option) => this.axisYQuestionNames[selectorIndex] === option.value, (e) => { this.onAxisYSelectorChanged(selectorIndex, e.target.value); }, selectorIndex ? undefined : localization.getString("axisYSelectorTitle"));
2205
+ return selector;
2206
+ }
2207
+ getQuestionValueType(question) {
1776
2208
  const questionType = question.getType();
1777
2209
  if (questionType === "text" && (question["inputType"] === "date" || question["inputType"] === "datetime")) {
1778
- this.valueType = "date";
2210
+ return "date";
1779
2211
  }
1780
- else {
1781
- this.valueType = "number";
2212
+ else if (questionType === "text" || questionType === "rating" || questionType === "expression" || questionType === "range") {
2213
+ return "number";
2214
+ }
2215
+ return "enum";
2216
+ }
2217
+ setupPivot() {
2218
+ const questionX = this.questions.filter((q) => q.name === this.axisXQuestionName)[0];
2219
+ if (!questionX) {
2220
+ return;
1782
2221
  }
2222
+ this.question = questionX;
2223
+ this.valueType = this.getQuestionValueType(questionX);
2224
+ this.questionsY = this.axisYQuestionNames.map((name) => {
2225
+ const questionY = this.questions.filter((q) => q.name === name)[0];
2226
+ if (!!questionY) {
2227
+ return this.getQuestionValueType(questionY) === "enum" ? new SelectBase(questionY, []) : new VisualizerBase(questionY, []);
2228
+ }
2229
+ }).filter((q) => !!q);
2230
+ this.onDataChanged();
1783
2231
  }
1784
2232
  reset() {
1785
2233
  this._continiousData = undefined;
@@ -1803,7 +2251,7 @@ class HistogramModel extends SelectBase {
1803
2251
  return Math.round(base * value) / base;
1804
2252
  }
1805
2253
  getSelectedItemByText(itemText) {
1806
- if (this.hasCustomIntervals || this.getContiniousValues().length > HistogramModel.UseIntervalsFrom) {
2254
+ if (this.hasCustomIntervals || this.getContiniousValues().length > PivotModel.UseIntervalsFrom) {
1807
2255
  const interval = this.intervals.filter(interval => interval.label === itemText)[0];
1808
2256
  return new ItemValue(interval, interval !== undefined ? interval.label : "");
1809
2257
  }
@@ -1824,37 +2272,74 @@ class HistogramModel extends SelectBase {
1824
2272
  }
1825
2273
  getContiniousValues() {
1826
2274
  if (this._cachedValues === undefined) {
1827
- const series = this.getSeriesValues();
1828
- if (series.length === 0) {
1829
- series.push("");
2275
+ this._continiousData = [];
2276
+ if (this.valueType === "enum") {
2277
+ this._cachedValues = [];
2278
+ return this._cachedValues;
1830
2279
  }
1831
- this._continiousData = {};
1832
- series.forEach(seriesValue => this._continiousData[seriesValue] = []);
1833
2280
  const hash = {};
1834
2281
  this.data.forEach(dataItem => {
1835
2282
  const answerData = dataItem[this.name];
1836
2283
  if (answerData !== undefined) {
1837
- const seriesValue = dataItem[DataProvider.seriesMarkerKey] || "";
1838
2284
  // TODO: _continiousData should be sorted in order to speed-up statistics calculation in the getData function
1839
- this._continiousData[seriesValue].push(this.getContiniousValue(answerData));
1840
- hash[answerData] = answerData;
2285
+ this._continiousData.push({ continious: this.getContiniousValue(answerData), row: dataItem });
2286
+ hash[answerData] = { value: answerData, row: dataItem };
1841
2287
  }
1842
2288
  });
1843
- this._cachedValues = Object.keys(hash).map(key => ({ original: hash[key], continious: this.getContiniousValue(key) }));
2289
+ this._cachedValues = Object.keys(hash).map(key => ({ original: hash[key].value, continious: this.getContiniousValue(key), row: hash[key].row }));
1844
2290
  this._cachedValues.sort((a, b) => a.continious - b.continious);
1845
2291
  }
1846
2292
  return this._cachedValues;
1847
2293
  }
2294
+ isSupportAnswersOrder() {
2295
+ return false;
2296
+ }
1848
2297
  isSupportMissingAnswers() {
1849
2298
  return false;
1850
2299
  }
1851
2300
  get needUseRateValues() {
1852
2301
  return this.question.getType() == "rating" && Array.isArray(this.question["rateValues"]) && this.question["rateValues"].length > 0;
1853
2302
  }
2303
+ getSeriesValues() {
2304
+ if (!this.questionsY || this.questionsY.length === 0) {
2305
+ return this.options.seriesValues || [];
2306
+ }
2307
+ const seriesValues = [];
2308
+ this.questionsY.forEach(q => {
2309
+ if (this.getQuestionValueType(q.question) === "enum") {
2310
+ seriesValues.push.apply(seriesValues, q.getValues().reverse());
2311
+ }
2312
+ else {
2313
+ seriesValues.push(q.question.name);
2314
+ }
2315
+ });
2316
+ return seriesValues;
2317
+ }
2318
+ getSeriesLabels() {
2319
+ if (this.questionsY.length === 0) {
2320
+ return this.getSeriesValues();
2321
+ }
2322
+ const seriesLabels = [];
2323
+ this.questionsY.forEach(q => {
2324
+ if (this.getQuestionValueType(q.question) === "enum") {
2325
+ seriesLabels.push.apply(seriesLabels, q.getLabels().reverse());
2326
+ }
2327
+ else {
2328
+ seriesLabels.push(q.question.title || q.question.name);
2329
+ }
2330
+ });
2331
+ return seriesLabels;
2332
+ }
1854
2333
  getValues() {
2334
+ if (this.valueType === "enum") {
2335
+ return super.getValues().reverse();
2336
+ }
1855
2337
  return this.intervals.map(interval => interval.start);
1856
2338
  }
1857
2339
  getLabels() {
2340
+ if (this.valueType === "enum") {
2341
+ return super.getLabels().reverse();
2342
+ }
1858
2343
  return this.intervals.map(interval => interval.label);
1859
2344
  }
1860
2345
  get hasCustomIntervals() {
@@ -1876,7 +2361,7 @@ class HistogramModel extends SelectBase {
1876
2361
  }
1877
2362
  else {
1878
2363
  const rateIntervals = [];
1879
- for (let i = (this.question["rateMin"] || 0); i <= (this.question["rateMax"] || (HistogramModel.IntervalsCount - 1)); i += (this.question["rateStep"] || 1)) {
2364
+ for (let i = (this.question["rateMin"] || 0); i <= (this.question["rateMax"] || (PivotModel.IntervalsCount - 1)); i += (this.question["rateStep"] || 1)) {
1880
2365
  rateIntervals.push({
1881
2366
  start: i,
1882
2367
  end: i + 1,
@@ -1892,7 +2377,7 @@ class HistogramModel extends SelectBase {
1892
2377
  if (continiousValues.length) {
1893
2378
  let start = continiousValues[0].continious;
1894
2379
  const end = continiousValues[continiousValues.length - 1].continious;
1895
- const intervalsCount = HistogramModel.IntervalsCount;
2380
+ const intervalsCount = PivotModel.IntervalsCount;
1896
2381
  const delta = (end - start) / intervalsCount;
1897
2382
  for (let i = 0; i < intervalsCount; ++i) {
1898
2383
  const next = start + delta;
@@ -1912,147 +2397,131 @@ class HistogramModel extends SelectBase {
1912
2397
  convertFromExternalData(externalCalculatedData) {
1913
2398
  return [externalCalculatedData];
1914
2399
  }
2400
+ getSeriesValueIndexes() {
2401
+ const seriesValueIndexes = {};
2402
+ let valueIndex = 0;
2403
+ for (var i = 0; i < this.questionsY.length; ++i) {
2404
+ const questionValueType = this.getQuestionValueType(this.questionsY[i].question);
2405
+ if (questionValueType === "enum") {
2406
+ this.questionsY[i].getValues().reverse().forEach((value) => {
2407
+ seriesValueIndexes[this.questionsY[i].name + "_" + value] = valueIndex++;
2408
+ });
2409
+ }
2410
+ else {
2411
+ seriesValueIndexes[this.questionsY[i].name] = valueIndex++;
2412
+ }
2413
+ }
2414
+ return seriesValueIndexes;
2415
+ }
2416
+ updateStatisticsSeriesValue(statistics, dataRow, valueIndex, seriesValueIndexes) {
2417
+ for (let j = 0; j < this.questionsY.length; ++j) {
2418
+ if (dataRow[this.questionsY[j].name] !== undefined) {
2419
+ const questionValueType = this.getQuestionValueType(this.questionsY[j].question);
2420
+ if (questionValueType === "enum" || questionValueType === "date") {
2421
+ const seriesValueIndex = seriesValueIndexes[this.questionsY[j].name + "_" + dataRow[this.questionsY[j].name]];
2422
+ statistics[seriesValueIndex][valueIndex]++;
2423
+ }
2424
+ else {
2425
+ const seriesValueIndex = seriesValueIndexes[this.questionsY[j].name];
2426
+ statistics[seriesValueIndex][valueIndex] += parseFloat(dataRow[this.questionsY[j].name]);
2427
+ }
2428
+ }
2429
+ }
2430
+ }
1915
2431
  getCalculatedValuesCore() {
1916
- this.getContiniousValues();
1917
- const intervals = this.intervals;
1918
2432
  const statistics = [];
1919
2433
  const series = this.getSeriesValues();
1920
2434
  if (series.length === 0) {
1921
2435
  series.push("");
1922
2436
  }
1923
- for (var i = 0; i < series.length; ++i) {
1924
- statistics.push(intervals.map(i => 0));
1925
- this._continiousData[series[i]].forEach(dataValue => {
1926
- for (let j = 0; j < intervals.length; ++j) {
1927
- if (intervals[j].start <= dataValue && (dataValue < intervals[j].end || j == intervals.length - 1)) {
1928
- statistics[i][j]++;
1929
- break;
2437
+ const seriesValueIndexes = this.getSeriesValueIndexes();
2438
+ if (this.valueType === "enum") {
2439
+ const values = this.getValues();
2440
+ const valueIndexes = {};
2441
+ values.forEach((value, index) => {
2442
+ valueIndexes[value] = index;
2443
+ });
2444
+ for (var i = 0; i < series.length; ++i) {
2445
+ statistics.push(values.map(i => 0));
2446
+ }
2447
+ this.data.forEach(dataRow => {
2448
+ const answerData = dataRow[this.name];
2449
+ if (answerData !== undefined && valueIndexes[answerData] !== undefined) {
2450
+ const valueIndex = valueIndexes[answerData];
2451
+ if (this.questionsY.length === 0) {
2452
+ statistics[0][valueIndex]++;
2453
+ }
2454
+ else {
2455
+ this.updateStatisticsSeriesValue(statistics, dataRow, valueIndex, seriesValueIndexes);
1930
2456
  }
1931
2457
  }
1932
2458
  });
1933
2459
  }
1934
- return statistics;
1935
- }
1936
- }
1937
- HistogramModel.IntervalsCount = 10;
1938
- HistogramModel.UseIntervalsFrom = 10;
1939
-
1940
- class NumberModel extends VisualizerBase {
1941
- constructor(question, data, options = {}, name) {
1942
- super(question, data, options, name || "number");
1943
- this.registerToolbarItem("changeChartType", () => {
1944
- if (this.chartTypes.length > 1) {
1945
- return DocumentHelper.createSelector(this.chartTypes.map((chartType) => {
1946
- return {
1947
- value: chartType,
1948
- text: localization.getString("chartType_" + chartType),
1949
- };
1950
- }), (option) => this.chartType === option.value, (e) => {
1951
- this.setChartType(e.target.value);
1952
- });
1953
- }
1954
- return null;
1955
- });
1956
- }
1957
- onDataChanged() {
1958
- this._resultAverage = undefined;
1959
- this._resultMin = undefined;
1960
- this._resultMax = undefined;
1961
- super.onDataChanged();
1962
- }
1963
- onChartTypeChanged() { }
1964
- setChartType(chartType) {
1965
- if (this.chartTypes.indexOf(chartType) !== -1 &&
1966
- this.chartType !== chartType) {
1967
- this.chartType = chartType;
1968
- this.onChartTypeChanged();
1969
- if (!!this.contentContainer) {
1970
- this.destroyContent(this.contentContainer);
1971
- this.renderContent(this.contentContainer);
1972
- }
1973
- this.invokeOnUpdate();
1974
- }
1975
- }
1976
- destroy() {
1977
- this._resultAverage = undefined;
1978
- this._resultMin = undefined;
1979
- this._resultMax = undefined;
1980
- super.destroy();
1981
- }
1982
- generateText(maxValue, minValue, stepsCount) {
1983
- let texts = [];
1984
- if (stepsCount === 5) {
1985
- texts = [
1986
- "very high (" + maxValue + ")",
1987
- "high",
1988
- "medium",
1989
- "low",
1990
- "very low (" + minValue + ")",
1991
- ];
1992
- }
1993
2460
  else {
1994
- texts.push(maxValue);
1995
- for (let i = 0; i < stepsCount - 2; i++) {
1996
- texts.push("");
2461
+ this.getContiniousValues();
2462
+ const intervals = this.intervals;
2463
+ for (var i = 0; i < series.length; ++i) {
2464
+ statistics.push(intervals.map(i => 0));
1997
2465
  }
1998
- texts.push(minValue);
1999
- }
2000
- if (!!NumberModel.generateTextsCallback) {
2001
- return NumberModel.generateTextsCallback(this.question, maxValue, minValue, stepsCount, texts);
2466
+ this._continiousData.forEach(dataValue => {
2467
+ for (let valueIndex = 0; valueIndex < intervals.length; ++valueIndex) {
2468
+ if (intervals[valueIndex].start <= dataValue.continious && (dataValue.continious < intervals[valueIndex].end || valueIndex == intervals.length - 1)) {
2469
+ if (this.questionsY.length === 0) {
2470
+ statistics[0][valueIndex]++;
2471
+ }
2472
+ else {
2473
+ this.updateStatisticsSeriesValue(statistics, dataValue.row, valueIndex, seriesValueIndexes);
2474
+ }
2475
+ break;
2476
+ }
2477
+ }
2478
+ });
2002
2479
  }
2003
- return texts;
2480
+ return statistics;
2004
2481
  }
2005
- generateValues(maxValue, stepsCount) {
2006
- const values = [];
2007
- for (let i = 0; i < stepsCount; i++) {
2008
- values.push(maxValue / stepsCount);
2009
- }
2010
- values.push(maxValue);
2011
- return values;
2482
+ getValueType() {
2483
+ return this.valueType;
2012
2484
  }
2013
- generateColors(maxValue, minValue, stepsCount) {
2014
- const palette = this.getColors();
2015
- const colors = [];
2016
- for (let i = 0; i < stepsCount; i++) {
2017
- colors.push(palette[i]);
2018
- }
2019
- colors.push("rgba(255, 255, 255, 0)");
2020
- return colors;
2485
+ isSupportSoftUpdateContent() {
2486
+ return false;
2021
2487
  }
2022
- convertFromExternalData(externalCalculatedData) {
2023
- return [externalCalculatedData.value || 0, externalCalculatedData.minValue || 0, externalCalculatedData.maxValue || 0];
2488
+ }
2489
+ PivotModel.IntervalsCount = 10;
2490
+ PivotModel.UseIntervalsFrom = 10;
2491
+ VisualizationManager.registerPivotVisualizer(PivotModel);
2492
+
2493
+ class RankingModel extends SelectBase {
2494
+ getQuestionResults() {
2495
+ const name = this.question.name;
2496
+ return this.data.map((dataItem) => dataItem[name]);
2497
+ }
2498
+ getEmptyData() {
2499
+ const choices = this.getValues();
2500
+ let data = [];
2501
+ data.length = choices.length;
2502
+ data.fill(0);
2503
+ return data;
2024
2504
  }
2025
2505
  getCalculatedValuesCore() {
2026
- if (this._resultAverage === undefined ||
2027
- this._resultMin === undefined ||
2028
- this._resultMax === undefined) {
2029
- this._resultMin = Number.MAX_VALUE;
2030
- this._resultMax = -Number.MAX_VALUE;
2031
- this._resultAverage = 0;
2032
- let actualAnswerCount = 0;
2033
- this.data.forEach((rowData) => {
2034
- if (rowData[this.question.name] !== undefined) {
2035
- const questionValue = +rowData[this.question.name];
2036
- actualAnswerCount++;
2037
- this._resultAverage += questionValue;
2038
- if (this._resultMin > questionValue) {
2039
- this._resultMin = questionValue;
2040
- }
2041
- if (this._resultMax < questionValue) {
2042
- this._resultMax = questionValue;
2043
- }
2044
- }
2045
- });
2046
- if (actualAnswerCount > 0) {
2047
- this._resultAverage = this._resultAverage / actualAnswerCount;
2048
- }
2049
- this._resultAverage = Math.ceil(this._resultAverage * 100) / 100;
2050
- }
2051
- return [this._resultAverage, this._resultMin, this._resultMax];
2506
+ const results = this.getQuestionResults();
2507
+ const choices = this.getValues();
2508
+ let plotlyData = this.getEmptyData();
2509
+ results.forEach((result) => {
2510
+ this.applyResultToPlotlyData(result, plotlyData, choices);
2511
+ });
2512
+ return [plotlyData];
2513
+ }
2514
+ applyResultToPlotlyData(result, plotlyData, choices) {
2515
+ if (!result || !plotlyData || !choices)
2516
+ return;
2517
+ result.forEach((resultValue, resultValueIndex, result) => {
2518
+ let index = choices.indexOf(resultValue);
2519
+ plotlyData[index] =
2520
+ +plotlyData[index] + (result.length - resultValueIndex);
2521
+ });
2052
2522
  }
2053
2523
  }
2054
- NumberModel.stepsCount = 5;
2055
- NumberModel.showAsPercentage = false;
2524
+ VisualizationManager.registerVisualizer("ranking", RankingModel);
2056
2525
 
2057
2526
  class AlternativeVisualizersWrapper extends VisualizerBase {
2058
2527
  updateVisualizerSelector() {
@@ -2188,6 +2657,12 @@ class AlternativeVisualizersWrapper extends VisualizerBase {
2188
2657
  this.visualizer.setState(state.state);
2189
2658
  }
2190
2659
  }
2660
+ getValues() {
2661
+ return this.visualizer.getValues();
2662
+ }
2663
+ getLabels() {
2664
+ return this.visualizer.getLabels();
2665
+ }
2191
2666
  getCalculatedValues() {
2192
2667
  return this.visualizer.getCalculatedValues();
2193
2668
  }
@@ -9443,7 +9918,15 @@ class VisualizationPanel extends VisualizerBase {
9443
9918
  }
9444
9919
  buildVisualizers(questions) {
9445
9920
  questions.forEach((question) => {
9446
- const visualizer = this.createVisualizer(question);
9921
+ let visualizerOptions = Object.assign({}, this.options);
9922
+ let visualizerData = this.surveyData;
9923
+ let visualizer;
9924
+ if (Array.isArray(question)) {
9925
+ visualizer = new (VisualizationManager.getPivotVisualizerConstructor())(question, visualizerData, visualizerOptions);
9926
+ }
9927
+ else {
9928
+ visualizer = this.createVisualizer(question, visualizerOptions, visualizerData);
9929
+ }
9447
9930
  if (!visualizer) {
9448
9931
  return;
9449
9932
  }
@@ -9510,6 +9993,7 @@ class VisualizationPanel extends VisualizerBase {
9510
9993
  setLocale(newLocale) {
9511
9994
  super.setLocale(newLocale);
9512
9995
  (this.questions || []).forEach((question) => {
9996
+ question = Array.isArray(question) ? question[0] : question;
9513
9997
  const element = this.getElement(question.name);
9514
9998
  if (!!element) {
9515
9999
  element.displayName = this.processText(question.title);
@@ -9556,6 +10040,7 @@ class VisualizationPanel extends VisualizerBase {
9556
10040
  }
9557
10041
  buildElements(questions) {
9558
10042
  return (questions || []).map((question) => {
10043
+ question = Array.isArray(question) ? question[0] : question;
9559
10044
  return {
9560
10045
  name: question.name,
9561
10046
  displayName: this.processText(question.title),
@@ -9756,7 +10241,7 @@ class VisualizationPanel extends VisualizerBase {
9756
10241
  this._settingState = true;
9757
10242
  try {
9758
10243
  if (Array.isArray(newState.elements)) {
9759
- const questionNames = this.questions.map(q => q.name);
10244
+ const questionNames = this.questions.map(q => Array.isArray(q) ? q[0].name : q.name);
9760
10245
  this._elements = [].concat(newState.elements.filter(e => (questionNames.indexOf(e.name) !== -1)));
9761
10246
  }
9762
10247
  if (typeof newState.locale !== "undefined")
@@ -9890,6 +10375,7 @@ class VisualizationMatrixDropdown extends VisualizerBase {
9890
10375
  this._childOptions.disableLocaleSwitch = true;
9891
10376
  this._childOptions.dataProvider = undefined;
9892
10377
  this._childOptions.allowDynamicLayout = false;
10378
+ this._childOptions.transposeData = true;
9893
10379
  this._childOptions.seriesValues = question.rows.map((row) => row.value);
9894
10380
  this._childOptions.seriesLabels = question.rows.map((row) => row.text);
9895
10381
  const innerQuestions = this.getQuestions();
@@ -11941,5 +12427,5 @@ NpsVisualizer.DetractorScore = 6;
11941
12427
  NpsVisualizer.PromoterScore = 9;
11942
12428
  // VisualizationManager.registerVisualizer("rating", NpsVisualizer);
11943
12429
 
11944
- export { AlternativeVisualizersWrapper as A, BooleanModel as B, DataProvider as D, HistogramModel as H, Matrix as M, NumberModel as N, PostponeHelper as P, SelectBase as S, TextTableAdapter as T, VisualizerFactory as V, WordCloudAdapter as W, __awaiter as _, VisualizerBase as a, VisualizationManager as b, VisualizationPanel as c, defaultStatisticsCalculator as d, VisualizationPanelDynamic as e, VisualizationMatrixDynamic as f, VisualizationMatrixDropdown as g, hideEmptyAnswersInData as h, WordCloud as i, Text as j, StatisticsTableAdapter as k, StatisticsTable as l, NpsVisualizerWidget as m, NpsAdapter as n, NpsVisualizer as o, textHelper as t };
12430
+ 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, defaultStatisticsCalculator as d, VisualizationPanelDynamic as e, VisualizationMatrixDynamic as f, VisualizationMatrixDropdown as g, hideEmptyAnswersInData as h, WordCloud as i, Text as j, StatisticsTableAdapter as k, StatisticsTable as l, NpsVisualizerWidget as m, NpsAdapter as n, NpsVisualizer as o, PivotModel as p, textHelper as t };
11945
12431
  //# sourceMappingURL=shared2.mjs.map