survey-analytics 2.5.29 → 2.5.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/fesm/shared2.mjs CHANGED
@@ -1,43 +1,11 @@
1
1
  /*!
2
- * surveyjs - SurveyJS Dashboard library v2.5.29
2
+ * surveyjs - SurveyJS Dashboard library v2.5.30
3
3
  * Copyright (c) 2015-2026 Devsoft Baltic OÜ - http://surveyjs.io/
4
4
  * License: MIT (http://www.opensource.org/licenses/mit-license.php)
5
5
  */
6
6
 
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
7
  import { Event, QuestionCustomModel, QuestionCompositeModel, QuestionCommentModel, settings, hasLicense, ItemValue, QuestionRatingModel, surveyLocalization, IsTouch } from 'survey-core';
9
-
10
- /******************************************************************************
11
- Copyright (c) Microsoft Corporation.
12
-
13
- Permission to use, copy, modify, and/or distribute this software for any
14
- purpose with or without fee is hereby granted.
15
-
16
- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
17
- REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
18
- AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
19
- INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
20
- LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
21
- OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
22
- PERFORMANCE OF THIS SOFTWARE.
23
- ***************************************************************************** */
24
- /* global Reflect, Promise, SuppressedError, Symbol, Iterator */
25
-
26
-
27
- function __awaiter(thisArg, _arguments, P, generator) {
28
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
29
- return new (P || (P = Promise))(function (resolve, reject) {
30
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
31
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
32
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
33
- step((generator = generator.apply(thisArg, _arguments || [])).next());
34
- });
35
- }
36
-
37
- typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
38
- var e = new Error(message);
39
- return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
40
- };
8
+ 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';
41
9
 
42
10
  class DataProvider {
43
11
  constructor(_data = []) {
@@ -357,6 +325,38 @@ class VisualizerFactory {
357
325
  }
358
326
  }
359
327
 
328
+ /******************************************************************************
329
+ Copyright (c) Microsoft Corporation.
330
+
331
+ Permission to use, copy, modify, and/or distribute this software for any
332
+ purpose with or without fee is hereby granted.
333
+
334
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
335
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
336
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
337
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
338
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
339
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
340
+ PERFORMANCE OF THIS SOFTWARE.
341
+ ***************************************************************************** */
342
+ /* global Reflect, Promise, SuppressedError, Symbol, Iterator */
343
+
344
+
345
+ function __awaiter(thisArg, _arguments, P, generator) {
346
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
347
+ return new (P || (P = Promise))(function (resolve, reject) {
348
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
349
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
350
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
351
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
352
+ });
353
+ }
354
+
355
+ typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
356
+ var e = new Error(message);
357
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
358
+ };
359
+
360
360
  function defaultStatisticsCalculator(data, dataInfo) {
361
361
  const dataNames = dataInfo.dataNames;
362
362
  const statistics = [];
@@ -1277,113 +1277,6 @@ VisualizerBase.colors = [
1277
1277
  ];
1278
1278
  VisualizationManager.defaultVisualizer = VisualizerBase;
1279
1279
 
1280
- class NumberModel extends VisualizerBase {
1281
- constructor(question, data, options = {}, name) {
1282
- super(question, data, options, name || "number");
1283
- if (VisualizerBase.chartAdapterType) {
1284
- this._chartAdapter = new VisualizerBase.chartAdapterType(this);
1285
- this.chartTypes = this._chartAdapter.getChartTypes();
1286
- this.chartType = this.chartTypes[0];
1287
- }
1288
- if (this.options.allowChangeVisualizerType !== false) {
1289
- this.registerToolbarItem("changeChartType", () => {
1290
- if (this.chartTypes.length > 1) {
1291
- return DocumentHelper.createSelector(this.chartTypes.map((chartType) => {
1292
- return {
1293
- value: chartType,
1294
- text: localization.getString("chartType_" + chartType),
1295
- };
1296
- }), (option) => this.chartType === option.value, (e) => {
1297
- this.setChartType(e.target.value);
1298
- });
1299
- }
1300
- return null;
1301
- });
1302
- }
1303
- }
1304
- onDataChanged() {
1305
- this._resultAverage = undefined;
1306
- this._resultMin = undefined;
1307
- this._resultMax = undefined;
1308
- super.onDataChanged();
1309
- }
1310
- onChartTypeChanged() { }
1311
- setChartType(chartType) {
1312
- if (this.chartTypes.indexOf(chartType) !== -1 &&
1313
- this.chartType !== chartType) {
1314
- this.chartType = chartType;
1315
- this.onChartTypeChanged();
1316
- if (!!this.contentContainer) {
1317
- this.destroyContent(this.contentContainer);
1318
- this.renderContent(this.contentContainer);
1319
- }
1320
- this.invokeOnUpdate();
1321
- }
1322
- }
1323
- destroy() {
1324
- this._resultAverage = undefined;
1325
- this._resultMin = undefined;
1326
- this._resultMax = undefined;
1327
- super.destroy();
1328
- }
1329
- generateText(maxValue, minValue, stepsCount) {
1330
- let texts = [];
1331
- if (stepsCount === 5) {
1332
- texts = [
1333
- "very high (" + maxValue + ")",
1334
- "high",
1335
- "medium",
1336
- "low",
1337
- "very low (" + minValue + ")",
1338
- ];
1339
- }
1340
- else {
1341
- texts.push(maxValue);
1342
- for (let i = 0; i < stepsCount - 2; i++) {
1343
- texts.push("");
1344
- }
1345
- texts.push(minValue);
1346
- }
1347
- if (!!NumberModel.generateTextsCallback) {
1348
- return NumberModel.generateTextsCallback(this.question, maxValue, minValue, stepsCount, texts);
1349
- }
1350
- return texts;
1351
- }
1352
- generateValues(maxValue, stepsCount) {
1353
- const values = [];
1354
- for (let i = 0; i < stepsCount; i++) {
1355
- values.push(maxValue / stepsCount);
1356
- }
1357
- values.push(maxValue);
1358
- return values;
1359
- }
1360
- generateColors(maxValue, minValue, stepsCount) {
1361
- const palette = this.getColors();
1362
- const colors = [];
1363
- for (let i = 0; i < stepsCount; i++) {
1364
- colors.push(palette[i]);
1365
- }
1366
- colors.push("rgba(255, 255, 255, 0)");
1367
- return colors;
1368
- }
1369
- convertFromExternalData(externalCalculatedData) {
1370
- return [externalCalculatedData.value || 0, externalCalculatedData.minValue || 0, externalCalculatedData.maxValue || 0];
1371
- }
1372
- getCalculatedValuesCore() {
1373
- if (this._resultAverage === undefined ||
1374
- this._resultMin === undefined ||
1375
- this._resultMax === undefined) {
1376
- [this._resultAverage, this._resultMin, this._resultMax] = mathStatisticsCalculator(this.surveyData, this);
1377
- }
1378
- return [this._resultAverage, this._resultMin, this._resultMax];
1379
- }
1380
- }
1381
- NumberModel.stepsCount = 5;
1382
- NumberModel.showAsPercentage = false;
1383
- VisualizationManager.registerVisualizer("number", NumberModel, 200);
1384
- VisualizationManager.registerVisualizer("rating", NumberModel, 200);
1385
- VisualizationManager.registerVisualizer("expression", NumberModel);
1386
-
1387
1280
  class FilterInfo {
1388
1281
  constructor(visualizer) {
1389
1282
  this._htmlElement = undefined;
@@ -2087,6 +1980,88 @@ VisualizationManager.registerVisualizer("imagepicker", SelectBase);
2087
1980
  VisualizationManager.registerVisualizer("tagbox", SelectBase);
2088
1981
  VisualizationManager.registerVisualizer("rating", SelectBase, 100);
2089
1982
 
1983
+ class Matrix extends SelectBase {
1984
+ constructor(question, data, options, name) {
1985
+ super(question, data, options, name || "matrix");
1986
+ this._transposeData = true;
1987
+ // this.getAnswersData();
1988
+ }
1989
+ get matrixQuestion() {
1990
+ return this.question;
1991
+ }
1992
+ isSupportMissingAnswers() {
1993
+ return false;
1994
+ }
1995
+ getSeriesValues() {
1996
+ return this.matrixQuestion.rows.map((row) => "" + row.value);
1997
+ }
1998
+ getSeriesLabels() {
1999
+ return this.matrixQuestion.rows.map((row) => ItemValue.getTextOrHtmlByValue(this.matrixQuestion.rows, row.value));
2000
+ }
2001
+ getSelectedItemByText(itemText) {
2002
+ return this.matrixQuestion.columns.filter((column) => column.text === itemText)[0];
2003
+ }
2004
+ valuesSource() {
2005
+ return this.matrixQuestion.columns;
2006
+ }
2007
+ getHasAnswersInAllSeriesArray(datasets) {
2008
+ let result = Array();
2009
+ for (let i = 0; i < datasets[0].length; i++) {
2010
+ for (let j = 0; j < datasets.length; j++) {
2011
+ if (datasets[j][i] != 0) {
2012
+ result[i] = true;
2013
+ }
2014
+ }
2015
+ }
2016
+ return result;
2017
+ }
2018
+ getHasAnswersInSeries(dataset) {
2019
+ for (let i = 0; i < dataset.length; i++) {
2020
+ if (dataset[i] != 0) {
2021
+ return true;
2022
+ }
2023
+ }
2024
+ return false;
2025
+ }
2026
+ hideEmptyAnswersInData(answersData) {
2027
+ const result = {
2028
+ datasets: [],
2029
+ labels: [],
2030
+ colors: [],
2031
+ texts: [],
2032
+ seriesLabels: [],
2033
+ };
2034
+ const hasAnswersInAllSeriesArr = this.getHasAnswersInAllSeriesArray(answersData.datasets);
2035
+ for (let i = 0; i < answersData.datasets.length; i++) {
2036
+ const hasAnswersInSeries = this.getHasAnswersInSeries(answersData.datasets[i]);
2037
+ if (hasAnswersInSeries) {
2038
+ result.labels.push(answersData.labels[i]);
2039
+ result.colors.push(answersData.colors[i]);
2040
+ }
2041
+ else {
2042
+ continue;
2043
+ }
2044
+ const datasets = [];
2045
+ const texts = [];
2046
+ for (let j = 0; j < answersData.datasets[0].length; j++) {
2047
+ if (hasAnswersInAllSeriesArr[j]) {
2048
+ datasets.push(answersData.datasets[i][j]);
2049
+ texts.push(answersData.texts[i][j]);
2050
+ }
2051
+ }
2052
+ result.datasets.push(datasets);
2053
+ result.texts.push(texts);
2054
+ }
2055
+ for (let i = 0; i < answersData.datasets[0].length; i++) {
2056
+ if (hasAnswersInAllSeriesArr[i]) {
2057
+ result.seriesLabels.push(answersData.seriesLabels[i]);
2058
+ }
2059
+ }
2060
+ return result;
2061
+ }
2062
+ }
2063
+ VisualizationManager.registerVisualizer("matrix", Matrix);
2064
+
2090
2065
  class BooleanModel extends SelectBase {
2091
2066
  constructor(question, data, options, name) {
2092
2067
  super(question, data, options, name || "boolean");
@@ -2720,707 +2695,283 @@ VisualizationManager.registerVisualizer("date", HistogramModel);
2720
2695
  VisualizationManager.registerVisualizer("number", HistogramModel, 100);
2721
2696
  VisualizationManager.registerVisualizer("rating", HistogramModel, 300);
2722
2697
 
2723
- class Matrix extends SelectBase {
2724
- constructor(question, data, options, name) {
2725
- super(question, data, options, name || "matrix");
2726
- this._transposeData = true;
2727
- // this.getAnswersData();
2728
- }
2729
- get matrixQuestion() {
2730
- return this.question;
2731
- }
2732
- isSupportMissingAnswers() {
2733
- return false;
2734
- }
2735
- getSeriesValues() {
2736
- return this.matrixQuestion.rows.map((row) => "" + row.value);
2698
+ class NumberModel extends VisualizerBase {
2699
+ constructor(question, data, options = {}, name) {
2700
+ super(question, data, options, name || "number");
2701
+ if (VisualizerBase.chartAdapterType) {
2702
+ this._chartAdapter = new VisualizerBase.chartAdapterType(this);
2703
+ this.chartTypes = this._chartAdapter.getChartTypes();
2704
+ this.chartType = this.chartTypes[0];
2705
+ }
2706
+ if (this.options.allowChangeVisualizerType !== false) {
2707
+ this.registerToolbarItem("changeChartType", () => {
2708
+ if (this.chartTypes.length > 1) {
2709
+ return DocumentHelper.createSelector(this.chartTypes.map((chartType) => {
2710
+ return {
2711
+ value: chartType,
2712
+ text: localization.getString("chartType_" + chartType),
2713
+ };
2714
+ }), (option) => this.chartType === option.value, (e) => {
2715
+ this.setChartType(e.target.value);
2716
+ });
2717
+ }
2718
+ return null;
2719
+ });
2720
+ }
2737
2721
  }
2738
- getSeriesLabels() {
2739
- return this.matrixQuestion.rows.map((row) => ItemValue.getTextOrHtmlByValue(this.matrixQuestion.rows, row.value));
2722
+ onDataChanged() {
2723
+ this._resultAverage = undefined;
2724
+ this._resultMin = undefined;
2725
+ this._resultMax = undefined;
2726
+ super.onDataChanged();
2740
2727
  }
2741
- getSelectedItemByText(itemText) {
2742
- return this.matrixQuestion.columns.filter((column) => column.text === itemText)[0];
2728
+ onChartTypeChanged() { }
2729
+ setChartType(chartType) {
2730
+ if (this.chartTypes.indexOf(chartType) !== -1 &&
2731
+ this.chartType !== chartType) {
2732
+ this.chartType = chartType;
2733
+ this.onChartTypeChanged();
2734
+ if (!!this.contentContainer) {
2735
+ this.destroyContent(this.contentContainer);
2736
+ this.renderContent(this.contentContainer);
2737
+ }
2738
+ this.invokeOnUpdate();
2739
+ }
2743
2740
  }
2744
- valuesSource() {
2745
- return this.matrixQuestion.columns;
2741
+ destroy() {
2742
+ this._resultAverage = undefined;
2743
+ this._resultMin = undefined;
2744
+ this._resultMax = undefined;
2745
+ super.destroy();
2746
2746
  }
2747
- getHasAnswersInAllSeriesArray(datasets) {
2748
- let result = Array();
2749
- for (let i = 0; i < datasets[0].length; i++) {
2750
- for (let j = 0; j < datasets.length; j++) {
2751
- if (datasets[j][i] != 0) {
2752
- result[i] = true;
2753
- }
2747
+ generateText(maxValue, minValue, stepsCount) {
2748
+ let texts = [];
2749
+ if (stepsCount === 5) {
2750
+ texts = [
2751
+ "very high (" + maxValue + ")",
2752
+ "high",
2753
+ "medium",
2754
+ "low",
2755
+ "very low (" + minValue + ")",
2756
+ ];
2757
+ }
2758
+ else {
2759
+ texts.push(maxValue);
2760
+ for (let i = 0; i < stepsCount - 2; i++) {
2761
+ texts.push("");
2754
2762
  }
2763
+ texts.push(minValue);
2755
2764
  }
2756
- return result;
2765
+ if (!!NumberModel.generateTextsCallback) {
2766
+ return NumberModel.generateTextsCallback(this.question, maxValue, minValue, stepsCount, texts);
2767
+ }
2768
+ return texts;
2757
2769
  }
2758
- getHasAnswersInSeries(dataset) {
2759
- for (let i = 0; i < dataset.length; i++) {
2760
- if (dataset[i] != 0) {
2761
- return true;
2762
- }
2770
+ generateValues(maxValue, stepsCount) {
2771
+ const values = [];
2772
+ for (let i = 0; i < stepsCount; i++) {
2773
+ values.push(maxValue / stepsCount);
2763
2774
  }
2764
- return false;
2775
+ values.push(maxValue);
2776
+ return values;
2765
2777
  }
2766
- hideEmptyAnswersInData(answersData) {
2767
- const result = {
2768
- datasets: [],
2769
- labels: [],
2770
- colors: [],
2771
- texts: [],
2772
- seriesLabels: [],
2773
- };
2774
- const hasAnswersInAllSeriesArr = this.getHasAnswersInAllSeriesArray(answersData.datasets);
2775
- for (let i = 0; i < answersData.datasets.length; i++) {
2776
- const hasAnswersInSeries = this.getHasAnswersInSeries(answersData.datasets[i]);
2777
- if (hasAnswersInSeries) {
2778
- result.labels.push(answersData.labels[i]);
2779
- result.colors.push(answersData.colors[i]);
2780
- }
2781
- else {
2782
- continue;
2783
- }
2784
- const datasets = [];
2785
- const texts = [];
2786
- for (let j = 0; j < answersData.datasets[0].length; j++) {
2787
- if (hasAnswersInAllSeriesArr[j]) {
2788
- datasets.push(answersData.datasets[i][j]);
2789
- texts.push(answersData.texts[i][j]);
2790
- }
2791
- }
2792
- result.datasets.push(datasets);
2793
- result.texts.push(texts);
2778
+ generateColors(maxValue, minValue, stepsCount) {
2779
+ const palette = this.getColors();
2780
+ const colors = [];
2781
+ for (let i = 0; i < stepsCount; i++) {
2782
+ colors.push(palette[i]);
2794
2783
  }
2795
- for (let i = 0; i < answersData.datasets[0].length; i++) {
2796
- if (hasAnswersInAllSeriesArr[i]) {
2797
- result.seriesLabels.push(answersData.seriesLabels[i]);
2798
- }
2784
+ colors.push("rgba(255, 255, 255, 0)");
2785
+ return colors;
2786
+ }
2787
+ convertFromExternalData(externalCalculatedData) {
2788
+ return [externalCalculatedData.value || 0, externalCalculatedData.minValue || 0, externalCalculatedData.maxValue || 0];
2789
+ }
2790
+ getCalculatedValuesCore() {
2791
+ if (this._resultAverage === undefined ||
2792
+ this._resultMin === undefined ||
2793
+ this._resultMax === undefined) {
2794
+ [this._resultAverage, this._resultMin, this._resultMax] = mathStatisticsCalculator(this.surveyData, this);
2799
2795
  }
2800
- return result;
2796
+ return [this._resultAverage, this._resultMin, this._resultMax];
2801
2797
  }
2802
2798
  }
2803
- VisualizationManager.registerVisualizer("matrix", Matrix);
2799
+ NumberModel.stepsCount = 5;
2800
+ NumberModel.showAsPercentage = false;
2801
+ VisualizationManager.registerVisualizer("number", NumberModel, 200);
2802
+ VisualizationManager.registerVisualizer("rating", NumberModel, 200);
2803
+ VisualizationManager.registerVisualizer("expression", NumberModel);
2804
2804
 
2805
- class PivotModel extends SelectBase {
2806
- constructor(questions, data, options, name, isRoot = true) {
2807
- super(null, data, options, name || "pivot");
2808
- this.questions = questions;
2809
- this.isRoot = isRoot;
2810
- this.valueType = "enum";
2811
- this._cachedValues = undefined;
2812
- this._continuousData = undefined;
2813
- this._cachedIntervals = undefined;
2814
- this._intervalPrecision = 2;
2815
- this.axisYSelectors = [];
2816
- this.axisYQuestionNames = [];
2817
- this.questionsY = [];
2818
- this.questions = this.questions.filter((question) => ["matrixdropdown", "matrixdynamic", "matrix", "file", "signature", "multipletext", "comment", "html", "image"].indexOf(question.getType()) === -1);
2819
- if (this.options.intervalPrecision !== undefined) {
2820
- this._intervalPrecision = this.options.intervalPrecision;
2805
+ class AlternativeVisualizersWrapper extends VisualizerBase {
2806
+ updateVisualizerSelector() {
2807
+ if (!!this.visualizerSelector) {
2808
+ this.visualizerSelector.getElementsByTagName("select")[0].value = this.visualizer.type;
2821
2809
  }
2822
- this.axisXQuestionName = this.questions.length > 0 ? this.questions[0].name : undefined;
2823
- this.registerToolbarItem("axisXSelector", () => this.axisXSelector = DocumentHelper.createSelector(this.questions.map((question) => {
2824
- return {
2825
- value: question.name,
2826
- text: question.title || question.name,
2827
- };
2828
- }), (option) => this.axisXQuestionName === option.value, (e) => {
2829
- this.axisXQuestionName = e.target.value;
2830
- this.updateQuestionsSelection();
2831
- this.updateToolbar();
2832
- this.setupPivot();
2833
- }, () => this.isXYChart() ? localization.getString("axisXSelectorTitle") : localization.getString("axisXAlternativeSelectorTitle")));
2834
- this.registerToolbarItem("axisYSelector0", this.createYSelecterGenerator());
2835
- this.setupPivot();
2836
2810
  }
2837
- createYSelecterGenerator() {
2838
- const selectorIndex = this.axisYSelectors.length;
2839
- return () => {
2840
- let selector = this.axisYSelectors[selectorIndex];
2841
- if (!selector) {
2842
- selector = this.createAxisYSelector(selectorIndex);
2843
- this.axisYSelectors.push(selector);
2844
- }
2845
- else {
2846
- selector["__updateSelect"] && selector["__updateSelect"]();
2847
- }
2848
- return selector;
2811
+ constructor(visualizers, question, data, options) {
2812
+ super(question, data, options, "alternative");
2813
+ this.visualizers = visualizers;
2814
+ this.visualizersWithSelection = [];
2815
+ this.onAfterVisualizerRenderCallback = () => {
2816
+ this.afterRender(this.contentContainer);
2849
2817
  };
2850
- }
2851
- setAxisQuestions(...axisQuestionNames) {
2852
- if (axisQuestionNames.length < 1) {
2853
- return;
2818
+ this.onVisualizerStateChangedCallback = (s, options) => {
2819
+ this.stateChanged("visualizer", options);
2820
+ };
2821
+ /**
2822
+ * The event is fired right after AlternativeVisualizersWrapper content type has been changed.
2823
+ **/
2824
+ this.onVisualizerChanged = new Event();
2825
+ this.showToolbar = false;
2826
+ this.loadingData = false;
2827
+ if (!visualizers || visualizers.length < 2) {
2828
+ throw new Error("VisualizerArrayWrapper works with visualizers collection only.");
2854
2829
  }
2855
- this.axisXQuestionName = axisQuestionNames[0];
2856
- this.axisYQuestionNames = axisQuestionNames.splice(1);
2857
- this.setupPivot();
2858
- }
2859
- onAxisYSelectorChanged(index, value) {
2860
- this.axisYQuestionNames[index] = value;
2861
- if (index < this.axisYSelectors.length - 1) {
2862
- if (!value) {
2863
- for (let i = index + 1; i < this.axisYSelectors.length; ++i) {
2864
- this.unregisterToolbarItem("axisYSelector" + i);
2865
- }
2866
- this.axisYSelectors = this.axisYSelectors.slice(0, index + 1);
2867
- this.axisYQuestionNames = this.axisYQuestionNames.slice(0, index + 1);
2830
+ this.visualizers.forEach((visualizer) => {
2831
+ visualizer.onUpdate = () => this.invokeOnUpdate();
2832
+ visualizer.onGetToolbarItemCreators = () => this.toolbarItemCreators;
2833
+ if (visualizer.supportSelection) {
2834
+ this._supportSelection = true;
2835
+ this.visualizersWithSelection.push(visualizer);
2868
2836
  }
2837
+ });
2838
+ if (this.options.allowChangeVisualizerType !== false) {
2839
+ this.registerToolbarItem("changeVisualizer", () => this.visualizerSelector = DocumentHelper.createSelector(this.visualizers.map((visualizer) => {
2840
+ return {
2841
+ value: visualizer.type,
2842
+ text: localization.getString("visualizer_" + visualizer.type),
2843
+ };
2844
+ }), (option) => this.visualizer.type === option.value, (e) => this.setVisualizer(e.target.value)), 0);
2869
2845
  }
2870
- else {
2871
- if (!!value) {
2872
- this.registerToolbarItem("axisYSelector" + this.axisYSelectors.length, this.createYSelecterGenerator());
2873
- }
2874
- }
2875
- this.updateQuestionsSelection();
2876
- this.updateToolbar();
2877
- this.setupPivot();
2846
+ this.visualizer = visualizers[0];
2847
+ this.visualizer.onAfterRender.add(this.onAfterVisualizerRenderCallback);
2848
+ this.visualizer.onStateChanged.add(this.onVisualizerStateChangedCallback);
2878
2849
  }
2879
- updateQuestionsSelection() {
2880
- const selectedQuestions = [this.axisXQuestionName];
2881
- for (let i = 0; i < this.axisYQuestionNames.length; ++i) {
2882
- const questionName = this.axisYQuestionNames[i];
2883
- if (selectedQuestions.indexOf(questionName) !== -1) {
2884
- this.onAxisYSelectorChanged(i, undefined);
2885
- break;
2850
+ get hasFooter() {
2851
+ return false;
2852
+ }
2853
+ getVisualizers() {
2854
+ return this.visualizers;
2855
+ }
2856
+ /**
2857
+ * This method selects visualizer to show by it name.
2858
+ *
2859
+ * parameters:
2860
+ * name - the name of visualizer to show,
2861
+ * quiet - set it to true if you don't want to rise a notification event
2862
+ *
2863
+ **/
2864
+ setVisualizer(type, quiet = false) {
2865
+ const visualizerCandidate = this.visualizers.filter((v) => v.type === type)[0];
2866
+ if (!!visualizerCandidate && visualizerCandidate !== this.visualizer) {
2867
+ let isFooterCollapsed;
2868
+ if (!!this.visualizer) {
2869
+ isFooterCollapsed = this.visualizer.isFooterCollapsed;
2870
+ this.visualizer.onStateChanged.remove(this.onVisualizerStateChangedCallback);
2871
+ this.visualizer.onAfterRender.remove(this.onAfterVisualizerRenderCallback);
2872
+ this.visualizer.destroy();
2886
2873
  }
2887
- else {
2888
- selectedQuestions.push(questionName);
2874
+ this.visualizer = visualizerCandidate;
2875
+ this.visualizer.isFooterCollapsed = isFooterCollapsed;
2876
+ this.refresh();
2877
+ this.visualizer.onAfterRender.add(this.onAfterVisualizerRenderCallback);
2878
+ this.visualizer.onStateChanged.add(this.onVisualizerStateChangedCallback);
2879
+ if (!quiet) {
2880
+ this.onVisualizerChanged.fire(this, { visualizer: this.visualizer });
2881
+ this.stateChanged("visualizer", type);
2889
2882
  }
2883
+ this.updateVisualizerSelector();
2890
2884
  }
2891
2885
  }
2892
- createAxisYSelector(selectorIndex) {
2893
- const getChoices = () => {
2894
- const choices = this.questions.filter(q => {
2895
- if (q.name === this.axisXQuestionName) {
2896
- return false;
2897
- }
2898
- const usedIndex = this.axisYQuestionNames.indexOf(q.name);
2899
- return usedIndex == -1 || usedIndex >= selectorIndex;
2900
- }).map((question) => {
2901
- return {
2902
- value: question.name,
2903
- text: question.title || question.name,
2904
- };
2905
- });
2906
- return [{ value: "", text: localization.getString("notSelected") }].concat(choices);
2907
- };
2908
- if (getChoices().length == 1) {
2909
- return undefined;
2910
- }
2911
- const selector = DocumentHelper.createSelector(getChoices, (option) => this.axisYQuestionNames[selectorIndex] === option.value, (e) => { this.onAxisYSelectorChanged(selectorIndex, e.target.value); }, () => selectorIndex ? undefined : (this.isXYChart() ? localization.getString("axisYSelectorTitle") : localization.getString("axisYAlternativeSelectorTitle")));
2912
- return selector;
2886
+ getVisualizer() {
2887
+ return this.visualizer;
2913
2888
  }
2914
- setChartType(chartType) {
2915
- const prev2Dchart = this.isXYChart();
2916
- super.setChartType(chartType);
2917
- if (prev2Dchart !== this.isXYChart()) {
2918
- this.updateToolbar();
2919
- }
2889
+ updateData(data) {
2890
+ super.updateData(data);
2891
+ this.visualizers.forEach((visualizer) => {
2892
+ visualizer.updateData(data);
2893
+ });
2920
2894
  }
2921
- isXYChart() {
2922
- return ["pie", "doughnut"].indexOf(this.chartType) === -1;
2895
+ set onDataItemSelected(val) {
2896
+ this.visualizersWithSelection.forEach((visualizer) => {
2897
+ visualizer.onDataItemSelected = val;
2898
+ });
2923
2899
  }
2924
- getQuestionValueType(question) {
2925
- const questionType = question.getType();
2926
- if (questionType === "text" && (question["inputType"] === "date" || question["inputType"] === "datetime")) {
2927
- return "date";
2928
- }
2929
- else if (questionType === "text" || questionType === "rating" || questionType === "expression" || questionType === "range") {
2930
- return "number";
2931
- }
2932
- return "enum";
2900
+ setSelection(item) {
2901
+ this.visualizersWithSelection.forEach((visualizer) => {
2902
+ visualizer.setSelection(item);
2903
+ });
2933
2904
  }
2934
- setupPivot() {
2935
- const questionX = this.questions.filter((q) => q.name === this.axisXQuestionName)[0];
2936
- if (!questionX) {
2937
- return;
2938
- }
2939
- this.question = questionX;
2940
- this.valueType = this.getQuestionValueType(questionX);
2941
- this.questionsY = this.axisYQuestionNames.map((name) => {
2942
- const questionY = this.questions.filter((q) => q.name === name)[0];
2943
- if (!!questionY) {
2944
- return this.getQuestionValueType(questionY) === "enum" ? new SelectBase(questionY, []) : new VisualizerBase(questionY, []);
2945
- }
2946
- }).filter((q) => !!q);
2947
- this.onDataChanged();
2905
+ get selection() {
2906
+ return this.visualizer.selection || this.selectedItem;
2948
2907
  }
2949
- reset() {
2950
- this._continuousData = undefined;
2951
- this._cachedValues = undefined;
2952
- this._cachedIntervals = undefined;
2908
+ renderContent(container) {
2909
+ this.visualizerContainer = container;
2910
+ this.visualizer.render(this.visualizerContainer);
2953
2911
  }
2954
- getContinuousValue(value) {
2955
- if (this.valueType === "date") {
2956
- return Date.parse(value);
2957
- }
2958
- return parseFloat(value);
2912
+ setBackgroundColorCore(color) {
2913
+ super.setBackgroundColorCore(color);
2914
+ this.visualizers.forEach(visualizer => visualizer.backgroundColor = color);
2959
2915
  }
2960
- getString(value) {
2961
- if (this.valueType === "date") {
2962
- return new Date(value).toLocaleDateString();
2916
+ /**
2917
+ * Returns an object with properties that describe a current visualizer state. The properties are different for each individual visualizer.
2918
+ *
2919
+ * > This method is overriden in descendant classes.
2920
+ * @see setState
2921
+ * @see resetState
2922
+ */
2923
+ getState() {
2924
+ const currentVisualizerState = this.visualizer.getState();
2925
+ const state = {
2926
+ visualizer: this.visualizer.type,
2927
+ };
2928
+ if (Object.keys(currentVisualizerState).length > 0) {
2929
+ state.state = currentVisualizerState;
2963
2930
  }
2964
- return "" + value;
2965
- }
2966
- toPrecision(value) {
2967
- const base = Math.pow(10, this._intervalPrecision);
2968
- return Math.round(base * value) / base;
2931
+ return state;
2969
2932
  }
2970
- getSelectedItemByText(itemText) {
2971
- if (this.hasCustomIntervals || this.getContinuousValues().length > PivotModel.UseIntervalsFrom) {
2972
- const interval = this.intervals.filter(interval => interval.label === itemText)[0];
2973
- return new ItemValue(interval, interval !== undefined ? interval.label : "");
2933
+ /**
2934
+ * Sets the visualizer's state.
2935
+ *
2936
+ * @see getState
2937
+ * @see resetState
2938
+ */
2939
+ setState(state) {
2940
+ if (!!state.visualizer) {
2941
+ this.setVisualizer(state.visualizer, true);
2942
+ }
2943
+ if (!!state.state) {
2944
+ this.visualizer.setState(state.state);
2974
2945
  }
2975
- const labels = this.getLabels();
2976
- const labelIndex = labels.indexOf(itemText);
2977
- return new ItemValue(this.getValues()[labelIndex], labels[labelIndex]);
2978
2946
  }
2979
2947
  /**
2980
- * Updates visualizer data.
2948
+ * Resets the visualizer's state.
2949
+ *
2950
+ * @see getState
2951
+ * @see setState
2981
2952
  */
2982
- updateData(data) {
2983
- this.reset();
2984
- super.updateData(data);
2985
- }
2986
- onDataChanged() {
2987
- this.reset();
2988
- super.onDataChanged();
2989
- }
2990
- getContinuousValues() {
2991
- if (this._cachedValues === undefined) {
2992
- this._continuousData = [];
2993
- if (this.valueType === "enum") {
2994
- this._cachedValues = [];
2995
- return this._cachedValues;
2996
- }
2997
- const hash = {};
2998
- this.data.forEach(dataItem => {
2999
- const answerData = dataItem[this.name];
3000
- if (answerData !== undefined) {
3001
- // TODO: _continuousData should be sorted in order to speed-up statistics calculation in the getData function
3002
- this._continuousData.push({ continuous: this.getContinuousValue(answerData), row: dataItem });
3003
- hash[answerData] = { value: answerData, row: dataItem };
3004
- }
3005
- });
3006
- this._cachedValues = Object.keys(hash).map(key => ({ original: hash[key].value, continuous: this.getContinuousValue(key), row: hash[key].row }));
3007
- this._cachedValues.sort((a, b) => a.continuous - b.continuous);
3008
- }
3009
- return this._cachedValues;
2953
+ resetState() {
2954
+ super.resetState();
2955
+ this.visualizers.forEach(visualizer => visualizer.resetState());
2956
+ this.setVisualizer(this.visualizers[0].type, true);
3010
2957
  }
3011
- isSupportAnswersOrder() {
3012
- return false;
2958
+ getValues() {
2959
+ return this.visualizer.getValues();
3013
2960
  }
3014
- isSupportMissingAnswers() {
3015
- return false;
2961
+ getLabels() {
2962
+ return this.visualizer.getLabels();
3016
2963
  }
3017
- get needUseRateValues() {
3018
- return this.question.getType() == "rating" && Array.isArray(this.question["rateValues"]) && this.question["rateValues"].length > 0;
2964
+ getCalculatedValues() {
2965
+ return this.visualizer.getCalculatedValues();
3019
2966
  }
3020
- getSeriesValues() {
3021
- if (!this.questionsY || this.questionsY.length === 0) {
3022
- return this.options.seriesValues || [];
3023
- }
3024
- const seriesValues = [];
3025
- this.questionsY.forEach(q => {
3026
- if (this.getQuestionValueType(q.question) === "enum") {
3027
- seriesValues.push.apply(seriesValues, q.getValues().reverse());
3028
- }
3029
- else {
3030
- seriesValues.push(q.question.name);
3031
- }
2967
+ destroy() {
2968
+ this.visualizers.forEach((visualizer) => {
2969
+ visualizer.onAfterRender.remove(this.onAfterVisualizerRenderCallback);
2970
+ visualizer.onStateChanged.remove(this.onVisualizerStateChangedCallback);
2971
+ visualizer.onUpdate = undefined;
3032
2972
  });
3033
- return seriesValues;
3034
- }
3035
- getSeriesLabels() {
3036
- if (this.questionsY.length === 0) {
3037
- return this.getSeriesValues();
3038
- }
3039
- const seriesLabels = [];
3040
- this.questionsY.forEach(q => {
3041
- if (this.getQuestionValueType(q.question) === "enum") {
3042
- seriesLabels.push.apply(seriesLabels, q.getLabels().reverse());
3043
- }
3044
- else {
3045
- seriesLabels.push(q.question.title || q.question.name);
3046
- }
3047
- });
3048
- return seriesLabels;
3049
- }
3050
- getValues() {
3051
- if (this.valueType === "enum") {
3052
- return super.getValues().reverse();
3053
- }
3054
- return this.intervals.map(interval => interval.start);
3055
- }
3056
- getLabels() {
3057
- if (this.valueType === "enum") {
3058
- return super.getLabels().reverse();
3059
- }
3060
- return this.intervals.map(interval => interval.label);
3061
- }
3062
- get hasCustomIntervals() {
3063
- return !!this.questionOptions && Array.isArray(this.questionOptions.intervals);
3064
- }
3065
- get intervals() {
3066
- if (this.hasCustomIntervals) {
3067
- return this.questionOptions.intervals;
3068
- }
3069
- if (this.question.getType() == "rating") {
3070
- if (this.needUseRateValues) {
3071
- const rateValues = this.question["rateValues"];
3072
- rateValues.sort((iv1, iv2) => iv1.value - iv2.value);
3073
- return rateValues.map((rateValue, i) => ({
3074
- start: rateValue.value,
3075
- end: i < rateValues.length - 1 ? rateValues[i + 1].value : rateValue.value + 1,
3076
- label: rateValue.text
3077
- }));
3078
- }
3079
- else {
3080
- const rateIntervals = [];
3081
- for (let i = (this.question["rateMin"] || 0); i <= (this.question["rateMax"] || (PivotModel.IntervalsCount - 1)); i += (this.question["rateStep"] || 1)) {
3082
- rateIntervals.push({
3083
- start: i,
3084
- end: i + 1,
3085
- label: "" + (!!this.question["rateMin"] && !!this.question["rateMax"] ? i : (i + "-" + (i + 1)))
3086
- });
3087
- }
3088
- return rateIntervals;
3089
- }
3090
- }
3091
- if (this._cachedIntervals === undefined) {
3092
- const continuousValues = this.getContinuousValues();
3093
- this._cachedIntervals = [];
3094
- if (continuousValues.length) {
3095
- let start = continuousValues[0].continuous;
3096
- const end = continuousValues[continuousValues.length - 1].continuous;
3097
- const intervalsCount = PivotModel.IntervalsCount;
3098
- const delta = (end - start) / intervalsCount;
3099
- for (let i = 0; i < intervalsCount; ++i) {
3100
- const next = start + delta;
3101
- const istart = this.toPrecision(start);
3102
- const inext = this.toPrecision(next);
3103
- this._cachedIntervals.push({
3104
- start: istart,
3105
- end: i < intervalsCount - 1 ? inext : inext + delta / 100,
3106
- label: "" + this.getString(istart) + "-" + this.getString(inext)
3107
- });
3108
- start = next;
3109
- }
3110
- }
3111
- }
3112
- return this._cachedIntervals;
3113
- }
3114
- convertFromExternalData(externalCalculatedData) {
3115
- return [externalCalculatedData];
3116
- }
3117
- getSeriesValueIndexes() {
3118
- const seriesValueIndexes = {};
3119
- let valueIndex = 0;
3120
- for (var i = 0; i < this.questionsY.length; ++i) {
3121
- const questionValueType = this.getQuestionValueType(this.questionsY[i].question);
3122
- if (questionValueType === "enum") {
3123
- this.questionsY[i].getValues().reverse().forEach((value) => {
3124
- seriesValueIndexes[this.questionsY[i].name + "_" + value] = valueIndex++;
3125
- });
3126
- }
3127
- else {
3128
- seriesValueIndexes[this.questionsY[i].name] = valueIndex++;
3129
- }
3130
- }
3131
- return seriesValueIndexes;
3132
- }
3133
- updateStatisticsSeriesValue(statistics, dataRow, valueIndex, seriesValueIndexes) {
3134
- for (let j = 0; j < this.questionsY.length; ++j) {
3135
- if (dataRow[this.questionsY[j].name] !== undefined) {
3136
- const questionValueType = this.getQuestionValueType(this.questionsY[j].question);
3137
- if (questionValueType === "enum" || questionValueType === "date") {
3138
- const seriesValueIndex = seriesValueIndexes[this.questionsY[j].name + "_" + dataRow[this.questionsY[j].name]];
3139
- statistics[seriesValueIndex][valueIndex]++;
3140
- }
3141
- else {
3142
- const seriesValueIndex = seriesValueIndexes[this.questionsY[j].name];
3143
- statistics[seriesValueIndex][valueIndex] += parseFloat(dataRow[this.questionsY[j].name]);
3144
- }
3145
- }
3146
- }
3147
- }
3148
- getCalculatedValuesCore() {
3149
- const statistics = [];
3150
- const series = this.getSeriesValues();
3151
- if (series.length === 0) {
3152
- series.push("");
3153
- }
3154
- const seriesValueIndexes = this.getSeriesValueIndexes();
3155
- if (this.valueType === "enum") {
3156
- const values = this.getValues();
3157
- const valueIndexes = {};
3158
- values.forEach((value, index) => {
3159
- valueIndexes[value] = index;
3160
- });
3161
- for (var i = 0; i < series.length; ++i) {
3162
- statistics.push(values.map(i => 0));
3163
- }
3164
- this.data.forEach(dataRow => {
3165
- const answerData = dataRow[this.name];
3166
- if (answerData !== undefined && valueIndexes[answerData] !== undefined) {
3167
- const valueIndex = valueIndexes[answerData];
3168
- if (this.questionsY.length === 0) {
3169
- statistics[0][valueIndex]++;
3170
- }
3171
- else {
3172
- this.updateStatisticsSeriesValue(statistics, dataRow, valueIndex, seriesValueIndexes);
3173
- }
3174
- }
3175
- });
3176
- }
3177
- else {
3178
- this.getContinuousValues();
3179
- const intervals = this.intervals;
3180
- for (var i = 0; i < series.length; ++i) {
3181
- statistics.push(intervals.map(i => 0));
3182
- }
3183
- this._continuousData.forEach(dataValue => {
3184
- for (let valueIndex = 0; valueIndex < intervals.length; ++valueIndex) {
3185
- if (intervals[valueIndex].start <= dataValue.continuous && (dataValue.continuous < intervals[valueIndex].end || valueIndex == intervals.length - 1)) {
3186
- if (this.questionsY.length === 0) {
3187
- statistics[0][valueIndex]++;
3188
- }
3189
- else {
3190
- this.updateStatisticsSeriesValue(statistics, dataValue.row, valueIndex, seriesValueIndexes);
3191
- }
3192
- break;
3193
- }
3194
- }
3195
- });
3196
- }
3197
- return statistics;
3198
- }
3199
- getValueType() {
3200
- return this.valueType;
3201
- }
3202
- isSupportSoftUpdateContent() {
3203
- return false;
3204
- }
3205
- renderToolbar(container) {
3206
- if (!this.haveCommercialLicense && this.isRoot) {
3207
- const banner = createCommercialLicenseLink();
3208
- container.appendChild(banner);
3209
- }
3210
- container.className += " sa-pivot__header";
3211
- super.renderToolbar(container);
3212
- }
3213
- }
3214
- PivotModel.IntervalsCount = 10;
3215
- PivotModel.UseIntervalsFrom = 10;
3216
- VisualizationManager.registerPivotVisualizer(PivotModel);
3217
-
3218
- class RankingModel extends SelectBase {
3219
- constructor(question, data, options, name) {
3220
- super(question, data, options, name || "ranking");
3221
- }
3222
- getQuestionResults() {
3223
- const name = this.question.name;
3224
- return this.data.map((dataItem) => dataItem[name]);
3225
- }
3226
- getEmptyData() {
3227
- const choices = this.getValues();
3228
- let data = [];
3229
- data.length = choices.length;
3230
- data.fill(0);
3231
- return data;
3232
- }
3233
- getCalculatedValuesCore() {
3234
- const results = this.getQuestionResults();
3235
- const choices = this.getValues();
3236
- let plotlyData = this.getEmptyData();
3237
- results.forEach((result) => {
3238
- this.applyResultToPlotlyData(result, plotlyData, choices);
3239
- });
3240
- return [plotlyData];
3241
- }
3242
- applyResultToPlotlyData(result, plotlyData, choices) {
3243
- if (!result || !plotlyData || !choices)
3244
- return;
3245
- result.forEach((resultValue, resultValueIndex, result) => {
3246
- let index = choices.indexOf(resultValue);
3247
- plotlyData[index] =
3248
- +plotlyData[index] + (result.length - resultValueIndex);
3249
- });
3250
- }
3251
- }
3252
- VisualizationManager.registerVisualizer("ranking", RankingModel);
3253
-
3254
- class AlternativeVisualizersWrapper extends VisualizerBase {
3255
- updateVisualizerSelector() {
3256
- if (!!this.visualizerSelector) {
3257
- this.visualizerSelector.getElementsByTagName("select")[0].value = this.visualizer.type;
3258
- }
3259
- }
3260
- constructor(visualizers, question, data, options) {
3261
- super(question, data, options, "alternative");
3262
- this.visualizers = visualizers;
3263
- this.visualizersWithSelection = [];
3264
- this.onAfterVisualizerRenderCallback = () => {
3265
- this.afterRender(this.contentContainer);
3266
- };
3267
- this.onVisualizerStateChangedCallback = (s, options) => {
3268
- this.stateChanged("visualizer", options);
3269
- };
3270
- /**
3271
- * The event is fired right after AlternativeVisualizersWrapper content type has been changed.
3272
- **/
3273
- this.onVisualizerChanged = new Event();
3274
- this.showToolbar = false;
3275
- this.loadingData = false;
3276
- if (!visualizers || visualizers.length < 2) {
3277
- throw new Error("VisualizerArrayWrapper works with visualizers collection only.");
3278
- }
3279
- this.visualizers.forEach((visualizer) => {
3280
- visualizer.onUpdate = () => this.invokeOnUpdate();
3281
- visualizer.onGetToolbarItemCreators = () => this.toolbarItemCreators;
3282
- if (visualizer.supportSelection) {
3283
- this._supportSelection = true;
3284
- this.visualizersWithSelection.push(visualizer);
3285
- }
3286
- });
3287
- if (this.options.allowChangeVisualizerType !== false) {
3288
- this.registerToolbarItem("changeVisualizer", () => this.visualizerSelector = DocumentHelper.createSelector(this.visualizers.map((visualizer) => {
3289
- return {
3290
- value: visualizer.type,
3291
- text: localization.getString("visualizer_" + visualizer.type),
3292
- };
3293
- }), (option) => this.visualizer.type === option.value, (e) => this.setVisualizer(e.target.value)), 0);
3294
- }
3295
- this.visualizer = visualizers[0];
3296
- this.visualizer.onAfterRender.add(this.onAfterVisualizerRenderCallback);
3297
- this.visualizer.onStateChanged.add(this.onVisualizerStateChangedCallback);
3298
- }
3299
- get hasFooter() {
3300
- return false;
3301
- }
3302
- getVisualizers() {
3303
- return this.visualizers;
3304
- }
3305
- /**
3306
- * This method selects visualizer to show by it name.
3307
- *
3308
- * parameters:
3309
- * name - the name of visualizer to show,
3310
- * quiet - set it to true if you don't want to rise a notification event
3311
- *
3312
- **/
3313
- setVisualizer(type, quiet = false) {
3314
- const visualizerCandidate = this.visualizers.filter((v) => v.type === type)[0];
3315
- if (!!visualizerCandidate && visualizerCandidate !== this.visualizer) {
3316
- let isFooterCollapsed;
3317
- if (!!this.visualizer) {
3318
- isFooterCollapsed = this.visualizer.isFooterCollapsed;
3319
- this.visualizer.onStateChanged.remove(this.onVisualizerStateChangedCallback);
3320
- this.visualizer.onAfterRender.remove(this.onAfterVisualizerRenderCallback);
3321
- this.visualizer.destroy();
3322
- }
3323
- this.visualizer = visualizerCandidate;
3324
- this.visualizer.isFooterCollapsed = isFooterCollapsed;
3325
- this.refresh();
3326
- this.visualizer.onAfterRender.add(this.onAfterVisualizerRenderCallback);
3327
- this.visualizer.onStateChanged.add(this.onVisualizerStateChangedCallback);
3328
- if (!quiet) {
3329
- this.onVisualizerChanged.fire(this, { visualizer: this.visualizer });
3330
- this.stateChanged("visualizer", type);
3331
- }
3332
- this.updateVisualizerSelector();
3333
- }
3334
- }
3335
- getVisualizer() {
3336
- return this.visualizer;
3337
- }
3338
- updateData(data) {
3339
- super.updateData(data);
3340
- this.visualizers.forEach((visualizer) => {
3341
- visualizer.updateData(data);
3342
- });
3343
- }
3344
- set onDataItemSelected(val) {
3345
- this.visualizersWithSelection.forEach((visualizer) => {
3346
- visualizer.onDataItemSelected = val;
3347
- });
3348
- }
3349
- setSelection(item) {
3350
- this.visualizersWithSelection.forEach((visualizer) => {
3351
- visualizer.setSelection(item);
3352
- });
3353
- }
3354
- get selection() {
3355
- return this.visualizer.selection || this.selectedItem;
3356
- }
3357
- renderContent(container) {
3358
- this.visualizerContainer = container;
3359
- this.visualizer.render(this.visualizerContainer);
3360
- }
3361
- setBackgroundColorCore(color) {
3362
- super.setBackgroundColorCore(color);
3363
- this.visualizers.forEach(visualizer => visualizer.backgroundColor = color);
3364
- }
3365
- /**
3366
- * Returns an object with properties that describe a current visualizer state. The properties are different for each individual visualizer.
3367
- *
3368
- * > This method is overriden in descendant classes.
3369
- * @see setState
3370
- * @see resetState
3371
- */
3372
- getState() {
3373
- const currentVisualizerState = this.visualizer.getState();
3374
- const state = {
3375
- visualizer: this.visualizer.type,
3376
- };
3377
- if (Object.keys(currentVisualizerState).length > 0) {
3378
- state.state = currentVisualizerState;
3379
- }
3380
- return state;
3381
- }
3382
- /**
3383
- * Sets the visualizer's state.
3384
- *
3385
- * @see getState
3386
- * @see resetState
3387
- */
3388
- setState(state) {
3389
- if (!!state.visualizer) {
3390
- this.setVisualizer(state.visualizer, true);
3391
- }
3392
- if (!!state.state) {
3393
- this.visualizer.setState(state.state);
3394
- }
3395
- }
3396
- /**
3397
- * Resets the visualizer's state.
3398
- *
3399
- * @see getState
3400
- * @see setState
3401
- */
3402
- resetState() {
3403
- super.resetState();
3404
- this.visualizers.forEach(visualizer => visualizer.resetState());
3405
- this.setVisualizer(this.visualizers[0].type, true);
3406
- }
3407
- getValues() {
3408
- return this.visualizer.getValues();
3409
- }
3410
- getLabels() {
3411
- return this.visualizer.getLabels();
3412
- }
3413
- getCalculatedValues() {
3414
- return this.visualizer.getCalculatedValues();
3415
- }
3416
- destroy() {
3417
- this.visualizers.forEach((visualizer) => {
3418
- visualizer.onAfterRender.remove(this.onAfterVisualizerRenderCallback);
3419
- visualizer.onStateChanged.remove(this.onVisualizerStateChangedCallback);
3420
- visualizer.onUpdate = undefined;
3421
- });
3422
- this.visualizer.destroy();
3423
- super.destroy();
2973
+ this.visualizer.destroy();
2974
+ super.destroy();
3424
2975
  }
3425
2976
  }
3426
2977
  VisualizationManager.registerAltVisualizerSelector(AlternativeVisualizersWrapper);
@@ -12740,469 +12291,918 @@ class WordCloudAdapter {
12740
12291
  });
12741
12292
  }
12742
12293
  destroy(node) {
12743
- if (this._wordcloud && typeof this._wordcloud.dispose === "function") {
12744
- this._wordcloud.dispose();
12745
- }
12746
- this._wordcloud = undefined;
12294
+ if (this._wordcloud && typeof this._wordcloud.dispose === "function") {
12295
+ this._wordcloud.dispose();
12296
+ }
12297
+ this._wordcloud = undefined;
12298
+ }
12299
+ }
12300
+ WordCloudAdapter.drawOutOfBound = false;
12301
+ WordCloudAdapter.shrinkToFit = true;
12302
+ WordCloudAdapter.abortThreshold = undefined;
12303
+ WordCloudAdapter.weightFactor = 20;
12304
+ WordCloudAdapter.onWordcloudCreating = new Event();
12305
+ class WordCloud extends VisualizerBase {
12306
+ constructor(question, data, options, name) {
12307
+ super(question, data, options, name || "wordcloud");
12308
+ this._wordcloudAdapter = new WordCloudAdapter(this);
12309
+ }
12310
+ convertFromExternalData(externalCalculatedData) {
12311
+ const innerCalculatedData = [];
12312
+ Object.keys(externalCalculatedData || []).forEach(word => {
12313
+ innerCalculatedData.push([word, externalCalculatedData[word]]);
12314
+ });
12315
+ return innerCalculatedData;
12316
+ }
12317
+ getCalculatedValuesCore() {
12318
+ let result = {};
12319
+ let stopWords = [];
12320
+ const locale = localization.currentLocale;
12321
+ if (locale) {
12322
+ stopWords = textHelper.getStopWords(locale);
12323
+ }
12324
+ else {
12325
+ stopWords = textHelper.getStopWords();
12326
+ }
12327
+ const clearWordRegexp = new RegExp("[.,\/#!$%\^\*;:{}=\-_`~()]", "g");
12328
+ const stopTheWord = (word) => {
12329
+ if (stopWords.indexOf(word) !== -1) {
12330
+ return "";
12331
+ }
12332
+ return word;
12333
+ };
12334
+ const processString = (row) => {
12335
+ row = "" + row;
12336
+ // if (row.length > 15) row = row.substring(0, 14) + "...";
12337
+ if (!!row) {
12338
+ row.split(" ").forEach((word) => {
12339
+ let clearedWord = (word || "").toLowerCase().replace(clearWordRegexp, "");
12340
+ clearedWord = stopTheWord(clearedWord);
12341
+ if (!!clearedWord) {
12342
+ if (!result[clearedWord]) {
12343
+ result[clearedWord] = 1;
12344
+ }
12345
+ else {
12346
+ result[clearedWord]++;
12347
+ }
12348
+ }
12349
+ });
12350
+ }
12351
+ };
12352
+ this.surveyData.forEach((dataRow) => {
12353
+ const nestedDataRows = getNestedDataRows(dataRow, this);
12354
+ nestedDataRows.forEach(row => {
12355
+ const rowValue = row[this.question.name];
12356
+ if (!!rowValue) {
12357
+ if (Array.isArray(rowValue)) {
12358
+ rowValue.forEach(processString);
12359
+ }
12360
+ else {
12361
+ if (typeof rowValue === "object") {
12362
+ Object.keys(rowValue).forEach((key) => processString(rowValue[key]));
12363
+ }
12364
+ else {
12365
+ processString(rowValue);
12366
+ }
12367
+ }
12368
+ }
12369
+ });
12370
+ });
12371
+ return Object.keys(result).map((key) => {
12372
+ return [key, result[key]];
12373
+ });
12374
+ }
12375
+ destroyContent(container) {
12376
+ this._wordcloudAdapter.destroy(container);
12377
+ super.destroyContent(container);
12378
+ }
12379
+ renderContentAsync(container) {
12380
+ return __awaiter(this, void 0, void 0, function* () {
12381
+ const chartNode = DocumentHelper.createElement("div");
12382
+ container.appendChild(chartNode);
12383
+ yield this._wordcloudAdapter.create(chartNode);
12384
+ container.innerHTML = "";
12385
+ container.appendChild(chartNode);
12386
+ return container;
12387
+ });
12388
+ }
12389
+ destroy() {
12390
+ this._wordcloudAdapter.destroy(this.contentContainer);
12391
+ super.destroy();
12392
+ }
12393
+ }
12394
+ VisualizationManager.registerVisualizer("text", WordCloud);
12395
+ VisualizationManager.registerVisualizer("comment", WordCloud);
12396
+ VisualizationManager.registerVisualizer("multipletext", WordCloud);
12397
+
12398
+ class TextTableAdapter {
12399
+ constructor(model) {
12400
+ this.model = model;
12401
+ }
12402
+ create(container) {
12403
+ return __awaiter(this, void 0, void 0, function* () {
12404
+ const { columnCount, data } = (yield this.model.getCalculatedValues());
12405
+ const emptyTextNode = DocumentHelper.createElement("p", "", {
12406
+ innerText: localization.getString("noResults"),
12407
+ });
12408
+ if (data.length === 0) {
12409
+ container.appendChild(emptyTextNode);
12410
+ return;
12411
+ }
12412
+ const tableNode = (DocumentHelper.createElement("table", "sa-text-table"));
12413
+ tableNode.style.backgroundColor = this.model.backgroundColor;
12414
+ container.appendChild(tableNode);
12415
+ if (this.model.columns) {
12416
+ var row = DocumentHelper.createElement("tr");
12417
+ this.model.columns.forEach(column => {
12418
+ var td = DocumentHelper.createElement("th", "sa-text-table__cell", {
12419
+ textContent: column.title,
12420
+ });
12421
+ row.appendChild(td);
12422
+ });
12423
+ tableNode.appendChild(row);
12424
+ }
12425
+ data.forEach((rowData) => {
12426
+ var row = DocumentHelper.createElement("tr");
12427
+ for (var i = 0; i < columnCount; i++) {
12428
+ const column = this.model.columns[i];
12429
+ var td = DocumentHelper.createElement("td", "sa-text-table__cell" + ((column === null || column === void 0 ? void 0 : column.type) == "number" ? " sa-text-table__cell--number" : ""), {
12430
+ textContent: rowData[i],
12431
+ });
12432
+ row.appendChild(td);
12433
+ }
12434
+ tableNode.appendChild(row);
12435
+ });
12436
+ container.className = "sa-text-table__container";
12437
+ container.appendChild(tableNode);
12438
+ });
12439
+ }
12440
+ destroy(node) {
12441
+ node.innerHTML = "";
12747
12442
  }
12748
12443
  }
12749
- WordCloudAdapter.drawOutOfBound = false;
12750
- WordCloudAdapter.shrinkToFit = true;
12751
- WordCloudAdapter.abortThreshold = undefined;
12752
- WordCloudAdapter.weightFactor = 20;
12753
- WordCloudAdapter.onWordcloudCreating = new Event();
12754
- class WordCloud extends VisualizerBase {
12444
+ class Text extends VisualizerBase {
12755
12445
  constructor(question, data, options, name) {
12756
- super(question, data, options, name || "wordcloud");
12757
- this._wordcloudAdapter = new WordCloudAdapter(this);
12446
+ super(question, data, options, name || "text");
12447
+ this._textTableAdapter = new TextTableAdapter(this);
12758
12448
  }
12759
- convertFromExternalData(externalCalculatedData) {
12760
- const innerCalculatedData = [];
12761
- Object.keys(externalCalculatedData || []).forEach(word => {
12762
- innerCalculatedData.push([word, externalCalculatedData[word]]);
12763
- });
12764
- return innerCalculatedData;
12449
+ get columns() {
12450
+ const columns = [];
12451
+ if (this.question.getType() == "multipletext") {
12452
+ this.question.items.forEach(item => {
12453
+ columns.push({ name: item.name, title: item.title, type: item.inputType });
12454
+ });
12455
+ }
12456
+ return columns;
12765
12457
  }
12766
12458
  getCalculatedValuesCore() {
12767
- let result = {};
12768
- let stopWords = [];
12769
- const locale = localization.currentLocale;
12770
- if (locale) {
12771
- stopWords = textHelper.getStopWords(locale);
12772
- }
12773
- else {
12774
- stopWords = textHelper.getStopWords();
12775
- }
12776
- const clearWordRegexp = new RegExp("[.,\/#!$%\^\*;:{}=\-_`~()]", "g");
12777
- const stopTheWord = (word) => {
12778
- if (stopWords.indexOf(word) !== -1) {
12779
- return "";
12780
- }
12781
- return word;
12782
- };
12783
- const processString = (row) => {
12784
- row = "" + row;
12785
- // if (row.length > 15) row = row.substring(0, 14) + "...";
12786
- if (!!row) {
12787
- row.split(" ").forEach((word) => {
12788
- let clearedWord = (word || "").toLowerCase().replace(clearWordRegexp, "");
12789
- clearedWord = stopTheWord(clearedWord);
12790
- if (!!clearedWord) {
12791
- if (!result[clearedWord]) {
12792
- result[clearedWord] = 1;
12793
- }
12794
- else {
12795
- result[clearedWord]++;
12796
- }
12797
- }
12798
- });
12799
- }
12800
- };
12459
+ let result = [];
12460
+ let columnCount = 0;
12801
12461
  this.surveyData.forEach((dataRow) => {
12802
12462
  const nestedDataRows = getNestedDataRows(dataRow, this);
12803
12463
  nestedDataRows.forEach(row => {
12804
12464
  const rowValue = row[this.question.name];
12465
+ let dataStrings = [];
12805
12466
  if (!!rowValue) {
12806
12467
  if (Array.isArray(rowValue)) {
12807
- rowValue.forEach(processString);
12468
+ dataStrings = dataStrings.concat(rowValue);
12808
12469
  }
12809
12470
  else {
12810
12471
  if (typeof rowValue === "object") {
12811
- Object.keys(rowValue).forEach((key) => processString(rowValue[key]));
12472
+ Object.keys(rowValue).forEach((key) => dataStrings.push(rowValue[key]));
12812
12473
  }
12813
12474
  else {
12814
- processString(rowValue);
12475
+ dataStrings.push(rowValue);
12815
12476
  }
12816
12477
  }
12478
+ result.push(dataStrings);
12479
+ if (dataStrings.length > columnCount) {
12480
+ columnCount = dataStrings.length;
12481
+ }
12817
12482
  }
12818
12483
  });
12819
12484
  });
12820
- return Object.keys(result).map((key) => {
12821
- return [key, result[key]];
12485
+ return { columnCount: columnCount, data: result };
12486
+ }
12487
+ destroyContent(container) {
12488
+ this._textTableAdapter.destroy(container);
12489
+ super.destroyContent(container);
12490
+ }
12491
+ renderContentAsync(container) {
12492
+ return __awaiter(this, void 0, void 0, function* () {
12493
+ const tableNode = DocumentHelper.createElement("div");
12494
+ yield this._textTableAdapter.create(tableNode);
12495
+ container.innerHTML = "";
12496
+ container.appendChild(tableNode);
12497
+ return container;
12498
+ });
12499
+ }
12500
+ destroy() {
12501
+ this._textTableAdapter.destroy(this.contentContainer);
12502
+ super.destroy();
12503
+ }
12504
+ }
12505
+ VisualizationManager.registerVisualizer("text", Text);
12506
+ VisualizationManager.registerVisualizer("comment", Text);
12507
+ VisualizationManager.registerVisualizer("multipletext", Text);
12508
+
12509
+ class StatisticsTableAdapter {
12510
+ constructor(model) {
12511
+ this.model = model;
12512
+ }
12513
+ create(container) {
12514
+ return __awaiter(this, void 0, void 0, function* () {
12515
+ let { datasets, labels, colors, texts, seriesLabels, } = yield this.model.getAnswersData();
12516
+ seriesLabels.length > 1;
12517
+ const emptyTextNode = DocumentHelper.createElement("p", "", {
12518
+ innerText: localization.getString("noResults"),
12519
+ });
12520
+ if (datasets.length === 0 || datasets[0].length === 0) {
12521
+ container.appendChild(emptyTextNode);
12522
+ return;
12523
+ }
12524
+ datasets.forEach((data, idx) => {
12525
+ const tableNode = (DocumentHelper.createElement("table", "sa-statistics-table"));
12526
+ tableNode.style.backgroundColor = this.model.backgroundColor;
12527
+ container.appendChild(tableNode);
12528
+ var headerRow = DocumentHelper.createElement("tr");
12529
+ var labelCell = DocumentHelper.createElement("th", "sa-statistics-table__cell-header", {
12530
+ textContent: localization.getString("answer"),
12531
+ });
12532
+ headerRow.appendChild(labelCell);
12533
+ var sparklineCell = DocumentHelper.createElement("th", "sa-statistics-table__cell-header", {
12534
+ textContent: localization.getString("statistics_chart"),
12535
+ });
12536
+ headerRow.appendChild(sparklineCell);
12537
+ var percentCell = DocumentHelper.createElement("th", "sa-statistics-table__cell-header", {
12538
+ textContent: localization.getString("percentage"),
12539
+ });
12540
+ headerRow.appendChild(percentCell);
12541
+ var valueCell = DocumentHelper.createElement("th", "sa-statistics-table__cell-header", {
12542
+ textContent: localization.getString("responses"),
12543
+ });
12544
+ headerRow.appendChild(valueCell);
12545
+ tableNode.appendChild(headerRow);
12546
+ for (let index = data.length - 1; index >= 0; index--) {
12547
+ var row = DocumentHelper.createElement("tr");
12548
+ var labelCell = DocumentHelper.createElement("td", "sa-statistics-table__cell", {
12549
+ textContent: labels[index],
12550
+ });
12551
+ row.appendChild(labelCell);
12552
+ var sparklineCell = DocumentHelper.createElement("td", "sa-statistics-table__cell");
12553
+ var outerBar = DocumentHelper.createElement("div", "sa-choices-sparkline");
12554
+ var innerBar = DocumentHelper.createElement("div", "sa-choices-sparkline-value");
12555
+ innerBar.style.width = texts[idx][index] + "%";
12556
+ outerBar.appendChild(innerBar);
12557
+ sparklineCell.appendChild(outerBar);
12558
+ row.appendChild(sparklineCell);
12559
+ var percentCell = DocumentHelper.createElement("td", "sa-statistics-table__cell sa-statistics-table__cell-value", {
12560
+ textContent: "" + texts[idx][index] + "%",
12561
+ });
12562
+ row.appendChild(percentCell);
12563
+ var valueCell = DocumentHelper.createElement("td", "sa-statistics-table__cell sa-statistics-table__cell-value", {
12564
+ textContent: data[index],
12565
+ });
12566
+ row.appendChild(valueCell);
12567
+ tableNode.appendChild(row);
12568
+ }
12569
+ container.className = "sa-statistics-table__container";
12570
+ container.appendChild(tableNode);
12571
+ });
12822
12572
  });
12823
12573
  }
12574
+ destroy(node) {
12575
+ node.innerHTML = "";
12576
+ }
12577
+ }
12578
+ class StatisticsTable extends SelectBase {
12579
+ constructor(question, data, options, name) {
12580
+ super(question, data, options, name || "choices");
12581
+ this._statisticsTableAdapter = new StatisticsTableAdapter(this);
12582
+ this.showPercentages = true;
12583
+ }
12824
12584
  destroyContent(container) {
12825
- this._wordcloudAdapter.destroy(container);
12585
+ this._statisticsTableAdapter.destroy(container);
12826
12586
  super.destroyContent(container);
12827
12587
  }
12828
12588
  renderContentAsync(container) {
12829
12589
  return __awaiter(this, void 0, void 0, function* () {
12830
- const chartNode = DocumentHelper.createElement("div");
12831
- container.appendChild(chartNode);
12832
- yield this._wordcloudAdapter.create(chartNode);
12590
+ const tableNode = DocumentHelper.createElement("div");
12591
+ yield this._statisticsTableAdapter.create(tableNode);
12833
12592
  container.innerHTML = "";
12834
- container.appendChild(chartNode);
12593
+ container.appendChild(tableNode);
12594
+ return container;
12595
+ });
12596
+ }
12597
+ destroy() {
12598
+ this._statisticsTableAdapter.destroy(this.contentContainer);
12599
+ super.destroy();
12600
+ }
12601
+ }
12602
+ class StatisticsTableBoolean extends BooleanModel {
12603
+ constructor(question, data, options, name) {
12604
+ super(question, data, options, name || "options");
12605
+ this._statisticsTableAdapter = new StatisticsTableAdapter(this);
12606
+ this.showPercentages = true;
12607
+ }
12608
+ destroyContent(container) {
12609
+ this._statisticsTableAdapter.destroy(container);
12610
+ super.destroyContent(container);
12611
+ }
12612
+ renderContentAsync(container) {
12613
+ return __awaiter(this, void 0, void 0, function* () {
12614
+ const tableNode = DocumentHelper.createElement("div");
12615
+ yield this._statisticsTableAdapter.create(tableNode);
12616
+ container.innerHTML = "";
12617
+ container.appendChild(tableNode);
12835
12618
  return container;
12836
12619
  });
12837
12620
  }
12838
- destroy() {
12839
- this._wordcloudAdapter.destroy(this.contentContainer);
12840
- super.destroy();
12621
+ destroy() {
12622
+ this._statisticsTableAdapter.destroy(this.contentContainer);
12623
+ super.destroy();
12624
+ }
12625
+ }
12626
+ VisualizationManager.registerVisualizer("radiogroup", StatisticsTable);
12627
+ VisualizationManager.registerVisualizer("dropdown", StatisticsTable);
12628
+ VisualizationManager.registerVisualizer("checkbox", StatisticsTable);
12629
+ VisualizationManager.registerVisualizer("boolean", StatisticsTableBoolean);
12630
+
12631
+ class NpsVisualizerWidget {
12632
+ constructor(_model, _data) {
12633
+ this._model = _model;
12634
+ this._data = _data;
12635
+ this._renderedTarget = undefined;
12636
+ }
12637
+ renderScorePart(partId, value, percent) {
12638
+ const scorePartElement = DocumentHelper.createElement("div", "sa-visualizer-nps__score-part");
12639
+ const titleElement = DocumentHelper.createElement("div", "sa-visualizer-nps__score-part-title");
12640
+ titleElement.innerText = localization.getString(partId);
12641
+ scorePartElement.appendChild(titleElement);
12642
+ const valuesElement = DocumentHelper.createElement("div", "sa-visualizer-nps__score-part-values");
12643
+ scorePartElement.appendChild(valuesElement);
12644
+ const valueElement = DocumentHelper.createElement("div", "sa-visualizer-nps__score-part-value");
12645
+ valueElement.innerText = "" + value;
12646
+ valuesElement.appendChild(valueElement);
12647
+ if (percent) {
12648
+ const percentElement = DocumentHelper.createElement("div", "sa-visualizer-nps__score-part-percent");
12649
+ percentElement.innerText = "" + percent + "%";
12650
+ valuesElement.appendChild(percentElement);
12651
+ }
12652
+ return scorePartElement;
12653
+ }
12654
+ render(target) {
12655
+ this._renderedTarget = target;
12656
+ var npsElement = DocumentHelper.createElement("div", "sa-visualizer-nps");
12657
+ npsElement.appendChild(this.renderScorePart("npsScore", this.npsScore));
12658
+ npsElement.appendChild(this.renderScorePart("npsPromoters", this._data.promoters, this.promotersPercent));
12659
+ npsElement.appendChild(this.renderScorePart("npsPassives", this._data.total - this._data.promoters - this._data.detractors, this.passivePercent));
12660
+ npsElement.appendChild(this.renderScorePart("npsDetractors", this._data.detractors, this.detractorsPercent));
12661
+ target.appendChild(npsElement);
12662
+ }
12663
+ get npsScore() {
12664
+ return toPrecision(((this._data.promoters - this._data.detractors) / this._data.total) * 100, this._model.precision);
12665
+ }
12666
+ get promotersPercent() {
12667
+ return toPrecision(this._data.promoters / this._data.total * 100, this._model.precision);
12668
+ }
12669
+ get passivePercent() {
12670
+ return toPrecision((this._data.total - this._data.promoters - this._data.detractors) / this._data.total * 100, this._model.precision);
12671
+ }
12672
+ get detractorsPercent() {
12673
+ return toPrecision(this._data.detractors / this._data.total * 100, this._model.precision);
12674
+ }
12675
+ dispose() {
12676
+ if (!!this._renderedTarget) {
12677
+ this._renderedTarget.innerHTML = "";
12678
+ this._renderedTarget = undefined;
12679
+ }
12841
12680
  }
12842
12681
  }
12843
- VisualizationManager.registerVisualizer("text", WordCloud);
12844
- VisualizationManager.registerVisualizer("comment", WordCloud);
12845
- VisualizationManager.registerVisualizer("multipletext", WordCloud);
12846
-
12847
- class TextTableAdapter {
12682
+ class NpsAdapter {
12848
12683
  constructor(model) {
12849
12684
  this.model = model;
12850
12685
  }
12851
- create(container) {
12686
+ get npsVisualizer() {
12687
+ return this._npsVisualizer;
12688
+ }
12689
+ create(element) {
12852
12690
  return __awaiter(this, void 0, void 0, function* () {
12853
- const { columnCount, data } = (yield this.model.getCalculatedValues());
12854
- const emptyTextNode = DocumentHelper.createElement("p", "", {
12855
- innerText: localization.getString("noResults"),
12856
- });
12857
- if (data.length === 0) {
12858
- container.appendChild(emptyTextNode);
12859
- return;
12860
- }
12861
- const tableNode = (DocumentHelper.createElement("table", "sa-text-table"));
12862
- tableNode.style.backgroundColor = this.model.backgroundColor;
12863
- container.appendChild(tableNode);
12864
- if (this.model.columns) {
12865
- var row = DocumentHelper.createElement("tr");
12866
- this.model.columns.forEach(column => {
12867
- var td = DocumentHelper.createElement("th", "sa-text-table__cell", {
12868
- textContent: column.title,
12869
- });
12870
- row.appendChild(td);
12871
- });
12872
- tableNode.appendChild(row);
12873
- }
12874
- data.forEach((rowData) => {
12875
- var row = DocumentHelper.createElement("tr");
12876
- for (var i = 0; i < columnCount; i++) {
12877
- const column = this.model.columns[i];
12878
- var td = DocumentHelper.createElement("td", "sa-text-table__cell" + ((column === null || column === void 0 ? void 0 : column.type) == "number" ? " sa-text-table__cell--number" : ""), {
12879
- textContent: rowData[i],
12880
- });
12881
- row.appendChild(td);
12882
- }
12883
- tableNode.appendChild(row);
12884
- });
12885
- container.className = "sa-text-table__container";
12886
- container.appendChild(tableNode);
12691
+ const data = yield this.model.getCalculatedValues();
12692
+ this._npsVisualizer = new NpsVisualizerWidget(this.model, data);
12693
+ this._npsVisualizer.render(element);
12694
+ return this._npsVisualizer;
12887
12695
  });
12888
12696
  }
12889
12697
  destroy(node) {
12890
- node.innerHTML = "";
12698
+ if (this._npsVisualizer && typeof this._npsVisualizer.dispose === "function") {
12699
+ this._npsVisualizer.dispose();
12700
+ }
12701
+ this._npsVisualizer = undefined;
12891
12702
  }
12892
12703
  }
12893
- class Text extends VisualizerBase {
12704
+ class NpsVisualizer extends VisualizerBase {
12894
12705
  constructor(question, data, options, name) {
12895
- super(question, data, options, name || "text");
12896
- this._textTableAdapter = new TextTableAdapter(this);
12897
- }
12898
- get columns() {
12899
- const columns = [];
12900
- if (this.question.getType() == "multipletext") {
12901
- this.question.items.forEach(item => {
12902
- columns.push({ name: item.name, title: item.title, type: item.inputType });
12903
- });
12904
- }
12905
- return columns;
12706
+ super(question, data, options, name || "nps");
12707
+ this.precision = 2;
12708
+ this._npsAdapter = new NpsAdapter(this);
12906
12709
  }
12907
12710
  getCalculatedValuesCore() {
12908
- let result = [];
12909
- let columnCount = 0;
12910
- this.surveyData.forEach((dataRow) => {
12911
- const nestedDataRows = getNestedDataRows(dataRow, this);
12912
- nestedDataRows.forEach(row => {
12913
- const rowValue = row[this.question.name];
12914
- let dataStrings = [];
12915
- if (!!rowValue) {
12916
- if (Array.isArray(rowValue)) {
12917
- dataStrings = dataStrings.concat(rowValue);
12918
- }
12919
- else {
12920
- if (typeof rowValue === "object") {
12921
- Object.keys(rowValue).forEach((key) => dataStrings.push(rowValue[key]));
12922
- }
12923
- else {
12924
- dataStrings.push(rowValue);
12925
- }
12926
- }
12927
- result.push(dataStrings);
12928
- if (dataStrings.length > columnCount) {
12929
- columnCount = dataStrings.length;
12930
- }
12711
+ let result = {
12712
+ detractors: 0,
12713
+ passive: 0,
12714
+ promoters: 0,
12715
+ total: 0,
12716
+ };
12717
+ this.data.forEach((row) => {
12718
+ const rowValue = row[this.question.name];
12719
+ const scoreValue = parseInt(rowValue);
12720
+ if (!Number.isNaN(scoreValue)) {
12721
+ if (scoreValue <= NpsVisualizer.DetractorScore) {
12722
+ result.detractors++;
12931
12723
  }
12932
- });
12724
+ else if (scoreValue >= NpsVisualizer.PromoterScore) {
12725
+ result.promoters++;
12726
+ }
12727
+ else {
12728
+ result.passive++;
12729
+ }
12730
+ result.total++;
12731
+ }
12933
12732
  });
12934
- return { columnCount: columnCount, data: result };
12733
+ return result;
12935
12734
  }
12936
12735
  destroyContent(container) {
12937
- this._textTableAdapter.destroy(container);
12736
+ this._npsAdapter.destroy(container);
12938
12737
  super.destroyContent(container);
12939
12738
  }
12940
12739
  renderContentAsync(container) {
12941
12740
  return __awaiter(this, void 0, void 0, function* () {
12942
- const tableNode = DocumentHelper.createElement("div");
12943
- yield this._textTableAdapter.create(tableNode);
12741
+ const npsNode = DocumentHelper.createElement("div");
12742
+ container.appendChild(npsNode);
12743
+ yield this._npsAdapter.create(npsNode);
12944
12744
  container.innerHTML = "";
12945
- container.appendChild(tableNode);
12745
+ container.appendChild(npsNode);
12946
12746
  return container;
12947
12747
  });
12948
12748
  }
12949
12749
  destroy() {
12950
- this._textTableAdapter.destroy(this.contentContainer);
12750
+ this._npsAdapter.destroy(this.contentContainer);
12951
12751
  super.destroy();
12952
12752
  }
12953
12753
  }
12954
- VisualizationManager.registerVisualizer("text", Text);
12955
- VisualizationManager.registerVisualizer("comment", Text);
12956
- VisualizationManager.registerVisualizer("multipletext", Text);
12754
+ NpsVisualizer.DetractorScore = 6;
12755
+ NpsVisualizer.PromoterScore = 9;
12756
+ // VisualizationManager.registerVisualizer("rating", NpsVisualizer);
12957
12757
 
12958
- class StatisticsTableAdapter {
12959
- constructor(model) {
12960
- this.model = model;
12758
+ class RankingModel extends SelectBase {
12759
+ constructor(question, data, options, name) {
12760
+ super(question, data, options, name || "ranking");
12761
+ }
12762
+ getQuestionResults() {
12763
+ const name = this.question.name;
12764
+ return this.data.map((dataItem) => dataItem[name]);
12765
+ }
12766
+ getEmptyData() {
12767
+ const choices = this.getValues();
12768
+ let data = [];
12769
+ data.length = choices.length;
12770
+ data.fill(0);
12771
+ return data;
12772
+ }
12773
+ getCalculatedValuesCore() {
12774
+ const results = this.getQuestionResults();
12775
+ const choices = this.getValues();
12776
+ let plotlyData = this.getEmptyData();
12777
+ results.forEach((result) => {
12778
+ this.applyResultToPlotlyData(result, plotlyData, choices);
12779
+ });
12780
+ return [plotlyData];
12781
+ }
12782
+ applyResultToPlotlyData(result, plotlyData, choices) {
12783
+ if (!result || !plotlyData || !choices)
12784
+ return;
12785
+ result.forEach((resultValue, resultValueIndex, result) => {
12786
+ let index = choices.indexOf(resultValue);
12787
+ plotlyData[index] =
12788
+ +plotlyData[index] + (result.length - resultValueIndex);
12789
+ });
12790
+ }
12791
+ }
12792
+ VisualizationManager.registerVisualizer("ranking", RankingModel);
12793
+
12794
+ class PivotModel extends SelectBase {
12795
+ constructor(questions, data, options, name, isRoot = true) {
12796
+ super(null, data, options, name || "pivot");
12797
+ this.questions = questions;
12798
+ this.isRoot = isRoot;
12799
+ this.valueType = "enum";
12800
+ this._cachedValues = undefined;
12801
+ this._continuousData = undefined;
12802
+ this._cachedIntervals = undefined;
12803
+ this._intervalPrecision = 2;
12804
+ this.axisYSelectors = [];
12805
+ this.axisYQuestionNames = [];
12806
+ this.questionsY = [];
12807
+ this.questions = this.questions.filter((question) => ["matrixdropdown", "matrixdynamic", "matrix", "file", "signature", "multipletext", "comment", "html", "image"].indexOf(question.getType()) === -1);
12808
+ if (this.options.intervalPrecision !== undefined) {
12809
+ this._intervalPrecision = this.options.intervalPrecision;
12810
+ }
12811
+ this.axisXQuestionName = this.questions.length > 0 ? this.questions[0].name : undefined;
12812
+ this.registerToolbarItem("axisXSelector", () => this.axisXSelector = DocumentHelper.createSelector(this.questions.map((question) => {
12813
+ return {
12814
+ value: question.name,
12815
+ text: question.title || question.name,
12816
+ };
12817
+ }), (option) => this.axisXQuestionName === option.value, (e) => {
12818
+ this.axisXQuestionName = e.target.value;
12819
+ this.updateQuestionsSelection();
12820
+ this.updateToolbar();
12821
+ this.setupPivot();
12822
+ }, () => this.isXYChart() ? localization.getString("axisXSelectorTitle") : localization.getString("axisXAlternativeSelectorTitle")));
12823
+ this.registerToolbarItem("axisYSelector0", this.createYSelecterGenerator());
12824
+ this.setupPivot();
12825
+ }
12826
+ createYSelecterGenerator() {
12827
+ const selectorIndex = this.axisYSelectors.length;
12828
+ return () => {
12829
+ let selector = this.axisYSelectors[selectorIndex];
12830
+ if (!selector) {
12831
+ selector = this.createAxisYSelector(selectorIndex);
12832
+ this.axisYSelectors.push(selector);
12833
+ }
12834
+ else {
12835
+ selector["__updateSelect"] && selector["__updateSelect"]();
12836
+ }
12837
+ return selector;
12838
+ };
12839
+ }
12840
+ setAxisQuestions(...axisQuestionNames) {
12841
+ if (axisQuestionNames.length < 1) {
12842
+ return;
12843
+ }
12844
+ this.axisXQuestionName = axisQuestionNames[0];
12845
+ this.axisYQuestionNames = axisQuestionNames.splice(1);
12846
+ this.setupPivot();
12847
+ }
12848
+ onAxisYSelectorChanged(index, value) {
12849
+ this.axisYQuestionNames[index] = value;
12850
+ if (index < this.axisYSelectors.length - 1) {
12851
+ if (!value) {
12852
+ for (let i = index + 1; i < this.axisYSelectors.length; ++i) {
12853
+ this.unregisterToolbarItem("axisYSelector" + i);
12854
+ }
12855
+ this.axisYSelectors = this.axisYSelectors.slice(0, index + 1);
12856
+ this.axisYQuestionNames = this.axisYQuestionNames.slice(0, index + 1);
12857
+ }
12858
+ }
12859
+ else {
12860
+ if (!!value) {
12861
+ this.registerToolbarItem("axisYSelector" + this.axisYSelectors.length, this.createYSelecterGenerator());
12862
+ }
12863
+ }
12864
+ this.updateQuestionsSelection();
12865
+ this.updateToolbar();
12866
+ this.setupPivot();
12961
12867
  }
12962
- create(container) {
12963
- return __awaiter(this, void 0, void 0, function* () {
12964
- let { datasets, labels, colors, texts, seriesLabels, } = yield this.model.getAnswersData();
12965
- seriesLabels.length > 1;
12966
- const emptyTextNode = DocumentHelper.createElement("p", "", {
12967
- innerText: localization.getString("noResults"),
12968
- });
12969
- if (datasets.length === 0 || datasets[0].length === 0) {
12970
- container.appendChild(emptyTextNode);
12971
- return;
12868
+ updateQuestionsSelection() {
12869
+ const selectedQuestions = [this.axisXQuestionName];
12870
+ for (let i = 0; i < this.axisYQuestionNames.length; ++i) {
12871
+ const questionName = this.axisYQuestionNames[i];
12872
+ if (selectedQuestions.indexOf(questionName) !== -1) {
12873
+ this.onAxisYSelectorChanged(i, undefined);
12874
+ break;
12972
12875
  }
12973
- datasets.forEach((data, idx) => {
12974
- const tableNode = (DocumentHelper.createElement("table", "sa-statistics-table"));
12975
- tableNode.style.backgroundColor = this.model.backgroundColor;
12976
- container.appendChild(tableNode);
12977
- var headerRow = DocumentHelper.createElement("tr");
12978
- var labelCell = DocumentHelper.createElement("th", "sa-statistics-table__cell-header", {
12979
- textContent: localization.getString("answer"),
12980
- });
12981
- headerRow.appendChild(labelCell);
12982
- var sparklineCell = DocumentHelper.createElement("th", "sa-statistics-table__cell-header", {
12983
- textContent: localization.getString("statistics_chart"),
12984
- });
12985
- headerRow.appendChild(sparklineCell);
12986
- var percentCell = DocumentHelper.createElement("th", "sa-statistics-table__cell-header", {
12987
- textContent: localization.getString("percentage"),
12988
- });
12989
- headerRow.appendChild(percentCell);
12990
- var valueCell = DocumentHelper.createElement("th", "sa-statistics-table__cell-header", {
12991
- textContent: localization.getString("responses"),
12992
- });
12993
- headerRow.appendChild(valueCell);
12994
- tableNode.appendChild(headerRow);
12995
- for (let index = data.length - 1; index >= 0; index--) {
12996
- var row = DocumentHelper.createElement("tr");
12997
- var labelCell = DocumentHelper.createElement("td", "sa-statistics-table__cell", {
12998
- textContent: labels[index],
12999
- });
13000
- row.appendChild(labelCell);
13001
- var sparklineCell = DocumentHelper.createElement("td", "sa-statistics-table__cell");
13002
- var outerBar = DocumentHelper.createElement("div", "sa-choices-sparkline");
13003
- var innerBar = DocumentHelper.createElement("div", "sa-choices-sparkline-value");
13004
- innerBar.style.width = texts[idx][index] + "%";
13005
- outerBar.appendChild(innerBar);
13006
- sparklineCell.appendChild(outerBar);
13007
- row.appendChild(sparklineCell);
13008
- var percentCell = DocumentHelper.createElement("td", "sa-statistics-table__cell sa-statistics-table__cell-value", {
13009
- textContent: "" + texts[idx][index] + "%",
13010
- });
13011
- row.appendChild(percentCell);
13012
- var valueCell = DocumentHelper.createElement("td", "sa-statistics-table__cell sa-statistics-table__cell-value", {
13013
- textContent: data[index],
13014
- });
13015
- row.appendChild(valueCell);
13016
- tableNode.appendChild(row);
12876
+ else {
12877
+ selectedQuestions.push(questionName);
12878
+ }
12879
+ }
12880
+ }
12881
+ createAxisYSelector(selectorIndex) {
12882
+ const getChoices = () => {
12883
+ const choices = this.questions.filter(q => {
12884
+ if (q.name === this.axisXQuestionName) {
12885
+ return false;
13017
12886
  }
13018
- container.className = "sa-statistics-table__container";
13019
- container.appendChild(tableNode);
12887
+ const usedIndex = this.axisYQuestionNames.indexOf(q.name);
12888
+ return usedIndex == -1 || usedIndex >= selectorIndex;
12889
+ }).map((question) => {
12890
+ return {
12891
+ value: question.name,
12892
+ text: question.title || question.name,
12893
+ };
13020
12894
  });
13021
- });
12895
+ return [{ value: "", text: localization.getString("notSelected") }].concat(choices);
12896
+ };
12897
+ if (getChoices().length == 1) {
12898
+ return undefined;
12899
+ }
12900
+ const selector = DocumentHelper.createSelector(getChoices, (option) => this.axisYQuestionNames[selectorIndex] === option.value, (e) => { this.onAxisYSelectorChanged(selectorIndex, e.target.value); }, () => selectorIndex ? undefined : (this.isXYChart() ? localization.getString("axisYSelectorTitle") : localization.getString("axisYAlternativeSelectorTitle")));
12901
+ return selector;
13022
12902
  }
13023
- destroy(node) {
13024
- node.innerHTML = "";
12903
+ setChartType(chartType) {
12904
+ const prev2Dchart = this.isXYChart();
12905
+ super.setChartType(chartType);
12906
+ if (prev2Dchart !== this.isXYChart()) {
12907
+ this.updateToolbar();
12908
+ }
13025
12909
  }
13026
- }
13027
- class StatisticsTable extends SelectBase {
13028
- constructor(question, data, options, name) {
13029
- super(question, data, options, name || "choices");
13030
- this._statisticsTableAdapter = new StatisticsTableAdapter(this);
13031
- this.showPercentages = true;
12910
+ isXYChart() {
12911
+ return ["pie", "doughnut"].indexOf(this.chartType) === -1;
13032
12912
  }
13033
- destroyContent(container) {
13034
- this._statisticsTableAdapter.destroy(container);
13035
- super.destroyContent(container);
12913
+ getQuestionValueType(question) {
12914
+ const questionType = question.getType();
12915
+ if (questionType === "text" && (question["inputType"] === "date" || question["inputType"] === "datetime")) {
12916
+ return "date";
12917
+ }
12918
+ else if (questionType === "text" || questionType === "rating" || questionType === "expression" || questionType === "range") {
12919
+ return "number";
12920
+ }
12921
+ return "enum";
13036
12922
  }
13037
- renderContentAsync(container) {
13038
- return __awaiter(this, void 0, void 0, function* () {
13039
- const tableNode = DocumentHelper.createElement("div");
13040
- yield this._statisticsTableAdapter.create(tableNode);
13041
- container.innerHTML = "";
13042
- container.appendChild(tableNode);
13043
- return container;
13044
- });
12923
+ setupPivot() {
12924
+ const questionX = this.questions.filter((q) => q.name === this.axisXQuestionName)[0];
12925
+ if (!questionX) {
12926
+ return;
12927
+ }
12928
+ this.question = questionX;
12929
+ this.valueType = this.getQuestionValueType(questionX);
12930
+ this.questionsY = this.axisYQuestionNames.map((name) => {
12931
+ const questionY = this.questions.filter((q) => q.name === name)[0];
12932
+ if (!!questionY) {
12933
+ return this.getQuestionValueType(questionY) === "enum" ? new SelectBase(questionY, []) : new VisualizerBase(questionY, []);
12934
+ }
12935
+ }).filter((q) => !!q);
12936
+ this.onDataChanged();
13045
12937
  }
13046
- destroy() {
13047
- this._statisticsTableAdapter.destroy(this.contentContainer);
13048
- super.destroy();
12938
+ reset() {
12939
+ this._continuousData = undefined;
12940
+ this._cachedValues = undefined;
12941
+ this._cachedIntervals = undefined;
13049
12942
  }
13050
- }
13051
- class StatisticsTableBoolean extends BooleanModel {
13052
- constructor(question, data, options, name) {
13053
- super(question, data, options, name || "options");
13054
- this._statisticsTableAdapter = new StatisticsTableAdapter(this);
13055
- this.showPercentages = true;
12943
+ getContinuousValue(value) {
12944
+ if (this.valueType === "date") {
12945
+ return Date.parse(value);
12946
+ }
12947
+ return parseFloat(value);
13056
12948
  }
13057
- destroyContent(container) {
13058
- this._statisticsTableAdapter.destroy(container);
13059
- super.destroyContent(container);
12949
+ getString(value) {
12950
+ if (this.valueType === "date") {
12951
+ return new Date(value).toLocaleDateString();
12952
+ }
12953
+ return "" + value;
13060
12954
  }
13061
- renderContentAsync(container) {
13062
- return __awaiter(this, void 0, void 0, function* () {
13063
- const tableNode = DocumentHelper.createElement("div");
13064
- yield this._statisticsTableAdapter.create(tableNode);
13065
- container.innerHTML = "";
13066
- container.appendChild(tableNode);
13067
- return container;
13068
- });
12955
+ toPrecision(value) {
12956
+ const base = Math.pow(10, this._intervalPrecision);
12957
+ return Math.round(base * value) / base;
13069
12958
  }
13070
- destroy() {
13071
- this._statisticsTableAdapter.destroy(this.contentContainer);
13072
- super.destroy();
12959
+ getSelectedItemByText(itemText) {
12960
+ if (this.hasCustomIntervals || this.getContinuousValues().length > PivotModel.UseIntervalsFrom) {
12961
+ const interval = this.intervals.filter(interval => interval.label === itemText)[0];
12962
+ return new ItemValue(interval, interval !== undefined ? interval.label : "");
12963
+ }
12964
+ const labels = this.getLabels();
12965
+ const labelIndex = labels.indexOf(itemText);
12966
+ return new ItemValue(this.getValues()[labelIndex], labels[labelIndex]);
13073
12967
  }
13074
- }
13075
- VisualizationManager.registerVisualizer("radiogroup", StatisticsTable);
13076
- VisualizationManager.registerVisualizer("dropdown", StatisticsTable);
13077
- VisualizationManager.registerVisualizer("checkbox", StatisticsTable);
13078
- VisualizationManager.registerVisualizer("boolean", StatisticsTableBoolean);
13079
-
13080
- class NpsVisualizerWidget {
13081
- constructor(_model, _data) {
13082
- this._model = _model;
13083
- this._data = _data;
13084
- this._renderedTarget = undefined;
12968
+ /**
12969
+ * Updates visualizer data.
12970
+ */
12971
+ updateData(data) {
12972
+ this.reset();
12973
+ super.updateData(data);
13085
12974
  }
13086
- renderScorePart(partId, value, percent) {
13087
- const scorePartElement = DocumentHelper.createElement("div", "sa-visualizer-nps__score-part");
13088
- const titleElement = DocumentHelper.createElement("div", "sa-visualizer-nps__score-part-title");
13089
- titleElement.innerText = localization.getString(partId);
13090
- scorePartElement.appendChild(titleElement);
13091
- const valuesElement = DocumentHelper.createElement("div", "sa-visualizer-nps__score-part-values");
13092
- scorePartElement.appendChild(valuesElement);
13093
- const valueElement = DocumentHelper.createElement("div", "sa-visualizer-nps__score-part-value");
13094
- valueElement.innerText = "" + value;
13095
- valuesElement.appendChild(valueElement);
13096
- if (percent) {
13097
- const percentElement = DocumentHelper.createElement("div", "sa-visualizer-nps__score-part-percent");
13098
- percentElement.innerText = "" + percent + "%";
13099
- valuesElement.appendChild(percentElement);
13100
- }
13101
- return scorePartElement;
12975
+ onDataChanged() {
12976
+ this.reset();
12977
+ super.onDataChanged();
13102
12978
  }
13103
- render(target) {
13104
- this._renderedTarget = target;
13105
- var npsElement = DocumentHelper.createElement("div", "sa-visualizer-nps");
13106
- npsElement.appendChild(this.renderScorePart("npsScore", this.npsScore));
13107
- npsElement.appendChild(this.renderScorePart("npsPromoters", this._data.promoters, this.promotersPercent));
13108
- npsElement.appendChild(this.renderScorePart("npsPassives", this._data.total - this._data.promoters - this._data.detractors, this.passivePercent));
13109
- npsElement.appendChild(this.renderScorePart("npsDetractors", this._data.detractors, this.detractorsPercent));
13110
- target.appendChild(npsElement);
12979
+ getContinuousValues() {
12980
+ if (this._cachedValues === undefined) {
12981
+ this._continuousData = [];
12982
+ if (this.valueType === "enum") {
12983
+ this._cachedValues = [];
12984
+ return this._cachedValues;
12985
+ }
12986
+ const hash = {};
12987
+ this.data.forEach(dataItem => {
12988
+ const answerData = dataItem[this.name];
12989
+ if (answerData !== undefined) {
12990
+ // TODO: _continuousData should be sorted in order to speed-up statistics calculation in the getData function
12991
+ this._continuousData.push({ continuous: this.getContinuousValue(answerData), row: dataItem });
12992
+ hash[answerData] = { value: answerData, row: dataItem };
12993
+ }
12994
+ });
12995
+ this._cachedValues = Object.keys(hash).map(key => ({ original: hash[key].value, continuous: this.getContinuousValue(key), row: hash[key].row }));
12996
+ this._cachedValues.sort((a, b) => a.continuous - b.continuous);
12997
+ }
12998
+ return this._cachedValues;
13111
12999
  }
13112
- get npsScore() {
13113
- return toPrecision(((this._data.promoters - this._data.detractors) / this._data.total) * 100, this._model.precision);
13000
+ isSupportAnswersOrder() {
13001
+ return false;
13114
13002
  }
13115
- get promotersPercent() {
13116
- return toPrecision(this._data.promoters / this._data.total * 100, this._model.precision);
13003
+ isSupportMissingAnswers() {
13004
+ return false;
13117
13005
  }
13118
- get passivePercent() {
13119
- return toPrecision((this._data.total - this._data.promoters - this._data.detractors) / this._data.total * 100, this._model.precision);
13006
+ get needUseRateValues() {
13007
+ return this.question.getType() == "rating" && Array.isArray(this.question["rateValues"]) && this.question["rateValues"].length > 0;
13120
13008
  }
13121
- get detractorsPercent() {
13122
- return toPrecision(this._data.detractors / this._data.total * 100, this._model.precision);
13009
+ getSeriesValues() {
13010
+ if (!this.questionsY || this.questionsY.length === 0) {
13011
+ return this.options.seriesValues || [];
13012
+ }
13013
+ const seriesValues = [];
13014
+ this.questionsY.forEach(q => {
13015
+ if (this.getQuestionValueType(q.question) === "enum") {
13016
+ seriesValues.push.apply(seriesValues, q.getValues().reverse());
13017
+ }
13018
+ else {
13019
+ seriesValues.push(q.question.name);
13020
+ }
13021
+ });
13022
+ return seriesValues;
13123
13023
  }
13124
- dispose() {
13125
- if (!!this._renderedTarget) {
13126
- this._renderedTarget.innerHTML = "";
13127
- this._renderedTarget = undefined;
13024
+ getSeriesLabels() {
13025
+ if (this.questionsY.length === 0) {
13026
+ return this.getSeriesValues();
13128
13027
  }
13028
+ const seriesLabels = [];
13029
+ this.questionsY.forEach(q => {
13030
+ if (this.getQuestionValueType(q.question) === "enum") {
13031
+ seriesLabels.push.apply(seriesLabels, q.getLabels().reverse());
13032
+ }
13033
+ else {
13034
+ seriesLabels.push(q.question.title || q.question.name);
13035
+ }
13036
+ });
13037
+ return seriesLabels;
13129
13038
  }
13130
- }
13131
- class NpsAdapter {
13132
- constructor(model) {
13133
- this.model = model;
13039
+ getValues() {
13040
+ if (this.valueType === "enum") {
13041
+ return super.getValues().reverse();
13042
+ }
13043
+ return this.intervals.map(interval => interval.start);
13134
13044
  }
13135
- get npsVisualizer() {
13136
- return this._npsVisualizer;
13045
+ getLabels() {
13046
+ if (this.valueType === "enum") {
13047
+ return super.getLabels().reverse();
13048
+ }
13049
+ return this.intervals.map(interval => interval.label);
13137
13050
  }
13138
- create(element) {
13139
- return __awaiter(this, void 0, void 0, function* () {
13140
- const data = yield this.model.getCalculatedValues();
13141
- this._npsVisualizer = new NpsVisualizerWidget(this.model, data);
13142
- this._npsVisualizer.render(element);
13143
- return this._npsVisualizer;
13144
- });
13051
+ get hasCustomIntervals() {
13052
+ return !!this.questionOptions && Array.isArray(this.questionOptions.intervals);
13145
13053
  }
13146
- destroy(node) {
13147
- if (this._npsVisualizer && typeof this._npsVisualizer.dispose === "function") {
13148
- this._npsVisualizer.dispose();
13054
+ get intervals() {
13055
+ if (this.hasCustomIntervals) {
13056
+ return this.questionOptions.intervals;
13149
13057
  }
13150
- this._npsVisualizer = undefined;
13058
+ if (this.question.getType() == "rating") {
13059
+ if (this.needUseRateValues) {
13060
+ const rateValues = this.question["rateValues"];
13061
+ rateValues.sort((iv1, iv2) => iv1.value - iv2.value);
13062
+ return rateValues.map((rateValue, i) => ({
13063
+ start: rateValue.value,
13064
+ end: i < rateValues.length - 1 ? rateValues[i + 1].value : rateValue.value + 1,
13065
+ label: rateValue.text
13066
+ }));
13067
+ }
13068
+ else {
13069
+ const rateIntervals = [];
13070
+ for (let i = (this.question["rateMin"] || 0); i <= (this.question["rateMax"] || (PivotModel.IntervalsCount - 1)); i += (this.question["rateStep"] || 1)) {
13071
+ rateIntervals.push({
13072
+ start: i,
13073
+ end: i + 1,
13074
+ label: "" + (!!this.question["rateMin"] && !!this.question["rateMax"] ? i : (i + "-" + (i + 1)))
13075
+ });
13076
+ }
13077
+ return rateIntervals;
13078
+ }
13079
+ }
13080
+ if (this._cachedIntervals === undefined) {
13081
+ const continuousValues = this.getContinuousValues();
13082
+ this._cachedIntervals = [];
13083
+ if (continuousValues.length) {
13084
+ let start = continuousValues[0].continuous;
13085
+ const end = continuousValues[continuousValues.length - 1].continuous;
13086
+ const intervalsCount = PivotModel.IntervalsCount;
13087
+ const delta = (end - start) / intervalsCount;
13088
+ for (let i = 0; i < intervalsCount; ++i) {
13089
+ const next = start + delta;
13090
+ const istart = this.toPrecision(start);
13091
+ const inext = this.toPrecision(next);
13092
+ this._cachedIntervals.push({
13093
+ start: istart,
13094
+ end: i < intervalsCount - 1 ? inext : inext + delta / 100,
13095
+ label: "" + this.getString(istart) + "-" + this.getString(inext)
13096
+ });
13097
+ start = next;
13098
+ }
13099
+ }
13100
+ }
13101
+ return this._cachedIntervals;
13151
13102
  }
13152
- }
13153
- class NpsVisualizer extends VisualizerBase {
13154
- constructor(question, data, options, name) {
13155
- super(question, data, options, name || "nps");
13156
- this.precision = 2;
13157
- this._npsAdapter = new NpsAdapter(this);
13103
+ convertFromExternalData(externalCalculatedData) {
13104
+ return [externalCalculatedData];
13158
13105
  }
13159
- getCalculatedValuesCore() {
13160
- let result = {
13161
- detractors: 0,
13162
- passive: 0,
13163
- promoters: 0,
13164
- total: 0,
13165
- };
13166
- this.data.forEach((row) => {
13167
- const rowValue = row[this.question.name];
13168
- const scoreValue = parseInt(rowValue);
13169
- if (!Number.isNaN(scoreValue)) {
13170
- if (scoreValue <= NpsVisualizer.DetractorScore) {
13171
- result.detractors++;
13172
- }
13173
- else if (scoreValue >= NpsVisualizer.PromoterScore) {
13174
- result.promoters++;
13106
+ getSeriesValueIndexes() {
13107
+ const seriesValueIndexes = {};
13108
+ let valueIndex = 0;
13109
+ for (var i = 0; i < this.questionsY.length; ++i) {
13110
+ const questionValueType = this.getQuestionValueType(this.questionsY[i].question);
13111
+ if (questionValueType === "enum") {
13112
+ this.questionsY[i].getValues().reverse().forEach((value) => {
13113
+ seriesValueIndexes[this.questionsY[i].name + "_" + value] = valueIndex++;
13114
+ });
13115
+ }
13116
+ else {
13117
+ seriesValueIndexes[this.questionsY[i].name] = valueIndex++;
13118
+ }
13119
+ }
13120
+ return seriesValueIndexes;
13121
+ }
13122
+ updateStatisticsSeriesValue(statistics, dataRow, valueIndex, seriesValueIndexes) {
13123
+ for (let j = 0; j < this.questionsY.length; ++j) {
13124
+ if (dataRow[this.questionsY[j].name] !== undefined) {
13125
+ const questionValueType = this.getQuestionValueType(this.questionsY[j].question);
13126
+ if (questionValueType === "enum" || questionValueType === "date") {
13127
+ const seriesValueIndex = seriesValueIndexes[this.questionsY[j].name + "_" + dataRow[this.questionsY[j].name]];
13128
+ statistics[seriesValueIndex][valueIndex]++;
13175
13129
  }
13176
13130
  else {
13177
- result.passive++;
13131
+ const seriesValueIndex = seriesValueIndexes[this.questionsY[j].name];
13132
+ statistics[seriesValueIndex][valueIndex] += parseFloat(dataRow[this.questionsY[j].name]);
13178
13133
  }
13179
- result.total++;
13180
13134
  }
13181
- });
13182
- return result;
13135
+ }
13183
13136
  }
13184
- destroyContent(container) {
13185
- this._npsAdapter.destroy(container);
13186
- super.destroyContent(container);
13137
+ getCalculatedValuesCore() {
13138
+ const statistics = [];
13139
+ const series = this.getSeriesValues();
13140
+ if (series.length === 0) {
13141
+ series.push("");
13142
+ }
13143
+ const seriesValueIndexes = this.getSeriesValueIndexes();
13144
+ if (this.valueType === "enum") {
13145
+ const values = this.getValues();
13146
+ const valueIndexes = {};
13147
+ values.forEach((value, index) => {
13148
+ valueIndexes[value] = index;
13149
+ });
13150
+ for (var i = 0; i < series.length; ++i) {
13151
+ statistics.push(values.map(i => 0));
13152
+ }
13153
+ this.data.forEach(dataRow => {
13154
+ const answerData = dataRow[this.name];
13155
+ if (answerData !== undefined && valueIndexes[answerData] !== undefined) {
13156
+ const valueIndex = valueIndexes[answerData];
13157
+ if (this.questionsY.length === 0) {
13158
+ statistics[0][valueIndex]++;
13159
+ }
13160
+ else {
13161
+ this.updateStatisticsSeriesValue(statistics, dataRow, valueIndex, seriesValueIndexes);
13162
+ }
13163
+ }
13164
+ });
13165
+ }
13166
+ else {
13167
+ this.getContinuousValues();
13168
+ const intervals = this.intervals;
13169
+ for (var i = 0; i < series.length; ++i) {
13170
+ statistics.push(intervals.map(i => 0));
13171
+ }
13172
+ this._continuousData.forEach(dataValue => {
13173
+ for (let valueIndex = 0; valueIndex < intervals.length; ++valueIndex) {
13174
+ if (intervals[valueIndex].start <= dataValue.continuous && (dataValue.continuous < intervals[valueIndex].end || valueIndex == intervals.length - 1)) {
13175
+ if (this.questionsY.length === 0) {
13176
+ statistics[0][valueIndex]++;
13177
+ }
13178
+ else {
13179
+ this.updateStatisticsSeriesValue(statistics, dataValue.row, valueIndex, seriesValueIndexes);
13180
+ }
13181
+ break;
13182
+ }
13183
+ }
13184
+ });
13185
+ }
13186
+ return statistics;
13187
13187
  }
13188
- renderContentAsync(container) {
13189
- return __awaiter(this, void 0, void 0, function* () {
13190
- const npsNode = DocumentHelper.createElement("div");
13191
- container.appendChild(npsNode);
13192
- yield this._npsAdapter.create(npsNode);
13193
- container.innerHTML = "";
13194
- container.appendChild(npsNode);
13195
- return container;
13196
- });
13188
+ getValueType() {
13189
+ return this.valueType;
13197
13190
  }
13198
- destroy() {
13199
- this._npsAdapter.destroy(this.contentContainer);
13200
- super.destroy();
13191
+ isSupportSoftUpdateContent() {
13192
+ return false;
13193
+ }
13194
+ renderToolbar(container) {
13195
+ if (!this.haveCommercialLicense && this.isRoot) {
13196
+ const banner = createCommercialLicenseLink();
13197
+ container.appendChild(banner);
13198
+ }
13199
+ container.className += " sa-pivot__header";
13200
+ super.renderToolbar(container);
13201
13201
  }
13202
13202
  }
13203
- NpsVisualizer.DetractorScore = 6;
13204
- NpsVisualizer.PromoterScore = 9;
13205
- // VisualizationManager.registerVisualizer("rating", NpsVisualizer);
13203
+ PivotModel.IntervalsCount = 10;
13204
+ PivotModel.UseIntervalsFrom = 10;
13205
+ VisualizationManager.registerPivotVisualizer(PivotModel);
13206
13206
 
13207
13207
  export { AlternativeVisualizersWrapper as A, BooleanModel as B, DataProvider as D, HistogramModel as H, Matrix as M, NpsAdapter as N, PivotModel as P, RankingModel as R, SelectBase as S, Text as T, VisualizationComposite as V, WordCloud as W, __awaiter as _, NpsVisualizer as a, NpsVisualizerWidget as b, NumberModel as c, PostponeHelper as d, StatisticsTable as e, StatisticsTableAdapter as f, StatisticsTableBoolean as g, TextTableAdapter as h, VisualizationManager as i, VisualizationMatrixDropdown as j, VisualizationMatrixDynamic as k, VisualizationPanel as l, VisualizationPanelDynamic as m, VisualizerBase as n, VisualizerFactory as o, WordCloudAdapter as p, getBestIntervalMode as q, hideEmptyAnswersInData as r, intervalCalculators as s, textHelper as t, defaultStatisticsCalculator as u };
13208
13208
  //# sourceMappingURL=shared2.mjs.map