UNPKG

survey-analytics

Version:

SurveyJS Dashboard is a UI component for visualizing and analyzing survey data. It interprets the form JSON schema to identify question types and renders collected responses using interactive charts and tables.

1,231 lines (1,220 loc) 551 kB
/*! * surveyjs - SurveyJS Dashboard library v2.5.2 * Copyright (c) 2015-2025 Devsoft Baltic OÜ - http://surveyjs.io/ * License: MIT (http://www.opensource.org/licenses/mit-license.php) */ import { D as DocumentHelper, l as localization, c as createImagesContainer, d as createLinksContainer, e as createCommercialLicenseLink, f as svgTemplate } from './shared.mjs'; export { a as setupLocale, s as surveyStrings } from './shared.mjs'; import { ItemValue, settings, hasLicense, Serializer, surveyLocalization, EventBase, Event } from 'survey-core'; import { TabulatorFull } from 'tabulator-tables'; class TableExtensions { constructor(table) { this.table = table; this.renderedExtensions = []; } render(targetNode, location, options) { var extensions = TableExtensions.extensions[location]; if (!!extensions) { extensions = this.sortExtensions(extensions); extensions.forEach((extension) => { if (!!extension.render && this.table.allowExtension(extension)) { var action = extension.render(this.table, options); if (!!action) { targetNode.appendChild(action); this.renderedExtensions.push(extension); } } }); } } destroy() { this.renderedExtensions.forEach((extension) => { if (!!extension.destroy) extension.destroy(); }); this.renderedExtensions = []; } static registerExtension(extension) { if (!this.extensions[extension.location]) this.extensions[extension.location] = []; this.extensions[extension.location].push(extension); } static removeExtension(extension) { if (!extension) { return; } const extensions = TableExtensions.extensions[extension.location]; const index = extensions.indexOf(extension); if (index >= 0) { extensions.splice(index, 1); } } static unregisterExtension(location, actionName) { if (!actionName) { return; } if (!!location) { const extension = TableExtensions.findExtension(location, actionName); TableExtensions.removeExtension(extension); } else { Object.keys(this.extensions).forEach((location) => TableExtensions.unregisterExtension(location, actionName)); } } static findExtension(location, actionName) { if (!this.extensions[location]) return null; var extension = this.extensions[location].filter(function (extension) { return extension.name == actionName; })[0]; return extension || null; } static getExtensions(location) { if (!this.extensions[location]) return []; return this.extensions[location].slice(0); } sortExtensions(extensions) { if (!Array.isArray(extensions)) return; return [] .concat(extensions.filter((extension) => extension.visibleIndex >= 0)) .sort((firstExtension, secondExtension) => { return firstExtension.visibleIndex - secondExtension.visibleIndex; }); } } TableExtensions.extensions = {}; TableExtensions.registerExtension({ location: "row", name: "details", visibleIndex: 0, render: (_table, options) => { const btn = DocumentHelper.createSvgButton("detail"); btn.title = localization.getString("showMinorColumns"); btn.className += " sa-table__row-extension"; btn.onclick = () => { options.row.toggleDetails(); }; return btn; }, }); TableExtensions.registerExtension({ location: "row", name: "select", visibleIndex: -1, render: function (_table, opt) { var row = opt.row; var checkbox = DocumentHelper.createElement("input", "sa-table__row-extension", { type: "checkbox", }); checkbox.checked = row.getIsSelected(); checkbox.onchange = function () { row.toggleSelect(); }; return checkbox; }, }); TableExtensions.registerExtension({ location: "header", name: "filter", visibleIndex: 0, render: function (table) { const input = DocumentHelper.createInput("sa-table__global-filter sa-table__header-extension", localization.getString("filterPlaceholder")); input.onchange = (event) => { table.applyFilter(event.target.value); }; return input; }, }); TableExtensions.registerExtension({ location: "header", name: "showcolumn", visibleIndex: 2, destroy: function () { this.onDestroy(); }, render: function (table) { const dropdown = DocumentHelper.createElement("select", "sa-table__show-column sa-table__header-extension"); function update() { var hiddenColumns = table.columns.filter((column) => !column.isVisible); if (hiddenColumns.length == 0) { dropdown.style.display = "none"; return; } dropdown.style.display = "inline-block"; dropdown.innerHTML = ""; var option = DocumentHelper.createElement("option", "", { text: localization.getString("showColumn"), disabled: true, selected: true, }); dropdown.appendChild(option); hiddenColumns.forEach((column) => { var text = column.displayName || column.name; if (!!text && text.length > 20) { text = text.substring(0, 20) + "..."; } var option = DocumentHelper.createElement("option", "", { text: text, title: column.displayName, value: column.name, }); dropdown.appendChild(option); }); } dropdown.onchange = (e) => { const val = e.target.value; e.stopPropagation(); if (!val) return; table.setColumnVisibility(val, true); }; update(); var onVisibilityChangedCallback = () => { update(); }; table.onColumnsVisibilityChanged.add(onVisibilityChangedCallback); this.onDestroy = () => { table.onColumnsVisibilityChanged.remove(onVisibilityChangedCallback); }; return dropdown; }, }); TableExtensions.registerExtension({ location: "header", name: "showentries", visibleIndex: 3, render: function (table) { if (table.options.paginationEnabled === false) { return DocumentHelper.createElement("div"); } function getEntriesDropdown(table) { const el = DocumentHelper.createElement("select"); var optionsValues = table.paginationSizeSelector || ["1", "5", "10", "25", "50", "75", "100"]; optionsValues.forEach(function (val) { var option = DocumentHelper.createElement("option", "", { value: val, text: val, }); el.appendChild(option); }); el.value = String(table.getPageSize()); el.onchange = () => { table.setPageSize(Number(el.value)); }; return el; } const selectorContainer = DocumentHelper.createElement("div", "sa-table__entries"); const spaceSpan = DocumentHelper.createElement("span", "sa-table__header-space"); const showSpan = DocumentHelper.createElement("span", "sa-table__entries-label sa-table__entries-label--right", { innerText: localization.getString("showLabel"), }); const entriesSpan = DocumentHelper.createElement("span", "sa-table__entries-label sa-table__entries-label--left", { innerText: localization.getString("entriesLabel"), }); selectorContainer.appendChild(spaceSpan); selectorContainer.appendChild(showSpan); selectorContainer.appendChild(getEntriesDropdown(table)); selectorContainer.appendChild(entriesSpan); return selectorContainer; }, }); TableExtensions.registerExtension({ location: "header", name: "removerows", visibleIndex: -1, render: function (table) { var btn = DocumentHelper.createElement("button", "sa-table__btn sa-table__btn--green sa-table__header-extension ", { innerText: localization.getString("removeRows") }); btn.onclick = function () { table.getCreatedRows().forEach(function (row) { if (row.getIsSelected()) { row.remove(); } }); }; return btn; }, }); TableExtensions.registerExtension({ location: "header", name: "changelocale", visibleIndex: 1, render: function (table) { var locales = table.getLocales(); if (table.options.disableLocaleSwitch || locales.length < 2) return null; const el = (DocumentHelper.createElement("select", "sa-table__header-extension", {})); var optionsValues = [localization.getString("changeLocale")].concat(locales); optionsValues.forEach(function (val) { var option = DocumentHelper.createElement("option", "", { value: val, text: localization.localeNames[val] || localization.getString(val) || val, }); el.appendChild(option); }); el.onchange = () => { table.locale = el.value; }; return el; }, }); var QuestionLocation; (function (QuestionLocation) { QuestionLocation[QuestionLocation["Column"] = 0] = "Column"; QuestionLocation[QuestionLocation["Row"] = 1] = "Row"; })(QuestionLocation || (QuestionLocation = {})); var ColumnDataType; (function (ColumnDataType) { ColumnDataType[ColumnDataType["Text"] = 0] = "Text"; ColumnDataType[ColumnDataType["FileLink"] = 1] = "FileLink"; ColumnDataType[ColumnDataType["Image"] = 2] = "Image"; })(ColumnDataType || (ColumnDataType = {})); TableExtensions.registerExtension({ location: "column", name: "drag", visibleIndex: 0, render: function (table, options) { const btn = DocumentHelper.createElement("button", "sa-table__svg-button sa-table__drag-button"); btn.appendChild(DocumentHelper.createSvgElement("drag")); btn.addEventListener("mousedown", () => { table.enableColumnReorder(); document.body.addEventListener("mouseup", () => { table.disableColumnReorder(); }, { once: true }); }); return btn; }, }); TableExtensions.registerExtension({ location: "column", name: "sort", visibleIndex: 1, render: function (table, options) { const descTitle = localization.getString("descOrder"); const ascTitle = localization.getString("ascOrder"); var btn = DocumentHelper.createSvgButton("sorting"); btn.title = ""; var direction = "asc"; btn.onclick = () => { if (direction == "asc") { btn.title = descTitle; direction = "desc"; } else { btn.title = ascTitle; direction = "asc"; } table.sortByColumn(options.columnName, direction); }; btn.ondrag = (e) => { e.stopPropagation(); }; return btn; }, }); TableExtensions.registerExtension({ location: "column", name: "hide", visibleIndex: 2, render: function (table, options) { var btn = DocumentHelper.createSvgButton("hide"); btn.title = localization.getString("hideColumn"); btn.onclick = () => { table.setColumnVisibility(options.columnName, false); }; return btn; }, }); TableExtensions.registerExtension({ location: "column", name: "movetodetails", visibleIndex: 3, render: function (table, options) { const button = DocumentHelper.createSvgButton("movetodetails"); button.title = localization.getString("moveToDetail"); button.onclick = (e) => { e.stopPropagation(); table.setColumnLocation(options.columnName, QuestionLocation.Row); }; return button; }, }); TableExtensions.registerExtension({ location: "column", name: "filter", visibleIndex: 4, render: function (table, options) { var el = DocumentHelper.createInput("sa-table__filter", localization.getString("filterPlaceholder")); el.onclick = (e) => e.stopPropagation(); el.onchange = (e) => { table.applyColumnFilter(options.columnName, el.value); }; return el; }, }); TableExtensions.registerExtension({ location: "column", name: "makepublic", visibleIndex: -1, render: function (table, options) { const button = DocumentHelper.createElement("button"); const makePrivateSvg = DocumentHelper.createSvgElement("makeprivate"); const makePublicSvg = DocumentHelper.createSvgElement("makepublic"); const column = table.getColumnByName(options.columnName); updateState(column); button.appendChild(makePrivateSvg); button.appendChild(makePublicSvg); button.onclick = (e) => { e.stopPropagation(); column.isPublic = !column.isPublic; updateState(column); table.onPermissionsChangedCallback && table.onPermissionsChangedCallback(table); }; function updateState(column) { if (column.isPublic) { button.className = "sa-table__svg-button"; button.title = localization.getString("makePrivateColumn"); makePrivateSvg.style.display = "none"; makePublicSvg.style.display = "block"; } else { button.className = "sa-table__svg-button sa-table__svg-button--active"; button.title = localization.getString("makePublicColumn"); makePrivateSvg.style.display = "block"; makePublicSvg.style.display = "none"; } } return button; }, }); class Details { constructor(table, row, targetNode) { this.table = table; this.row = row; this.targetNode = targetNode; this.location = "details"; this.createShowAsColumnButton = (columnName) => { const button = DocumentHelper.createElement("button", "sa-table__btn sa-table__btn--gray"); button.appendChild(document.createTextNode(localization.getString("showAsColumn"))); button.onclick = (e) => { e.stopPropagation(); this.table.setColumnLocation(columnName, QuestionLocation.Column); }; return button; }; this.detailsTable = DocumentHelper.createElement("table", "sa-table__detail-table"); this.table.onColumnsLocationChanged.add(() => { this.close(); }); } open() { this.detailsTable.innerHTML = ""; var rows = []; this.table.columns .filter((column) => column.location === QuestionLocation.Row && column) .forEach((column) => { var row = DocumentHelper.createElement("tr", "sa-table__detail"); var td1 = DocumentHelper.createElement("td"); td1.appendChild(document.createTextNode(column.displayName)); var td2 = DocumentHelper.createElement("td"); td2.textContent = this.row.getRowData()[column.name]; if (column.dataType === ColumnDataType.Image) { td2.innerHTML = "<image src='" + td2.textContent + "'/>"; } var td3 = DocumentHelper.createElement("td"); td3.appendChild(this.createShowAsColumnButton(column.name)); row.appendChild(td1); row.appendChild(td2); row.appendChild(td3); rows.push(row); }); var row = DocumentHelper.createElement("tr", "sa-table__detail"); var td = DocumentHelper.createElement("td", "", { colSpan: 3 }); var extensions = new TableExtensions(this.table); extensions.render(td, "details", { row: this.row }); if (td.children.length != 0) { row.appendChild(td); rows.push(row); } rows.forEach((row) => { this.detailsTable.appendChild(row); }); this.targetNode.appendChild(this.detailsTable); } close() { if (!!this.detailsTable.parentNode) { this.detailsTable.parentNode.removeChild(this.detailsTable); } } } class BaseColumn { constructor(question, table) { this.question = question; this.table = table; this.isVisible = true; this.isPublic = true; this.location = QuestionLocation.Column; this.dataType = this.getDataType(); } get name() { if (!this.nameValue) { this.name = this.getName(); } return this.nameValue; } set name(val) { this.nameValue = val; } get displayName() { if (!this.displayNameValue) { this.displayName = this.getDisplayName(); } return this.displayNameValue; } set displayName(val) { this.displayNameValue = val; } getDisplayName() { var _a; return this.table.useNamesAsTitles ? this.question.name : (((_a = this.question.locTitle) === null || _a === void 0 ? void 0 : _a.renderedHtml) || this.question.title || "").trim() || this.question.name; } getName() { return this.question.name; } getDataType() { return ColumnDataType.Text; } getDisplayValueCore(data) { return data[this.name]; } setupReadyChangedCallback(table, question) { if (!!question) { const onReadyChangedCallback = (sender, options) => { if (options.isReady) { table.refresh(!table.isInitTableDataProcessing); sender.onReadyChanged.remove(onReadyChangedCallback); } }; if (!question.isReady) { question.onReadyChanged.add(onReadyChangedCallback); } } } getDisplayValue(data, table, options) { let displayValue = this.getDisplayValueCore(data); const question = this.question; if (!!question) { if (options.useValuesAsLabels) { displayValue = question.value; } else { displayValue = question.displayValue; } } return displayValue; } formatDisplayValue(displayValue) { return typeof displayValue === "string" ? displayValue : JSON.stringify(displayValue) || ""; } getCellData(table, data) { const displayValue = this.getDisplayValue(data, table, table.options); this.setupReadyChangedCallback(table, this.question); return { question: this.question, displayValue: this.formatDisplayValue(displayValue) }; } toJSON() { return { name: this.name, displayName: this.displayName, dataType: this.dataType, isVisible: this.isVisible, isPublic: this.isPublic, location: this.location }; } fromJSON(data) { Object.keys(data).forEach(key => { this[key] = data[key]; }); } } class DefaultColumn extends BaseColumn { getDisplayValue(data, table, options) { return this.getDisplayValueCore(data); } } class CheckboxColumn extends BaseColumn { getDisplayValue(data, table, options) { const selectedItems = this.question.selectedItems; const res = []; selectedItems.forEach(item => { res.push(options.useValuesAsLabels ? item.value : item.textOrHtml); }); return res.join(table.itemsDelimiter); } } class SingleChoiceColumn extends BaseColumn { getDisplayValue(data, table, options) { const selectedItem = this.question.selectedItem; if (!selectedItem) return ""; return options.useValuesAsLabels ? selectedItem.value : selectedItem.textOrHtml; } } class SelectBaseColumn extends BaseColumn { getDisplayValue(data, table, options) { const value = options.useValuesAsLabels ? this.question.renderedValue : this.question.displayValue; if (Array.isArray(value)) { return value.join(table.itemsDelimiter); } else { return value; } } } class CommentColumn extends BaseColumn { getName() { return `${this.question.name}${settings.commentPrefix}`; } getDisplayName() { return this.question.commentText; } getDisplayValue(data, table, options) { return this.question.comment; } } class OtherColumn extends CommentColumn { getDisplayName() { return this.question.otherText; } } class MatrixColumn extends BaseColumn { constructor(question, row, table) { var _a; super(question, table); this.row = row; this.valueName = this.question.name; this.valuePath = (_a = this.row) === null || _a === void 0 ? void 0 : _a.value; } getName() { var _a; return this.question.name + "." + ((_a = this.row) === null || _a === void 0 ? void 0 : _a.value); } getDisplayName() { const table = this.table; const question = this.question; const row = this.row; return (table.useNamesAsTitles ? question.name : (question.title || "").trim() || question.name) + " - " + (table.useNamesAsTitles ? row === null || row === void 0 ? void 0 : row.value : row === null || row === void 0 ? void 0 : row.locText.textOrHtml); } getDisplayValue(data, table, options) { let displayValue = data[this.valueName]; if (this.valuePath && typeof displayValue === "object") { displayValue = displayValue[this.valuePath]; if (displayValue !== undefined) { if (Array.isArray(displayValue)) { const res = []; displayValue.forEach(itemValue => { const item = ItemValue.getItemByValue(this.question.columns, itemValue); if (!!item) { res.push(options.useValuesAsLabels ? item.value : item.locText.textOrHtml); } }); displayValue = res.join(table.itemsDelimiter); } else { const choiceValue = ItemValue.getItemByValue(this.question.columns, displayValue); if (!!choiceValue) { displayValue = options.useValuesAsLabels ? choiceValue.value : choiceValue.locText.textOrHtml; } } } } return displayValue; } } class ImageColumn extends BaseColumn { getDataType() { return ColumnDataType.Image; } } class FileColumn extends BaseColumn { getDataType() { return ColumnDataType.FileLink; } getDisplayValue(data, table, options) { let displayValue = this.getDisplayValueCore(data); if (Array.isArray(displayValue)) { displayValue = Table.showFilesAsImages ? createImagesContainer(displayValue).outerHTML : createLinksContainer(displayValue).outerHTML; } return displayValue; } } class MatrixDropdownColumn extends BaseColumn { constructor(question, row, col, table) { super(question, table); this.row = row; this.col = col; this.rowValue = this.row.value; this.colName = this.col.name; } getName() { return this.question.name + "." + this.row.value + "." + this.col.name; } getDisplayName() { const table = this.table; const question = this.question; return (this.table.useNamesAsTitles ? question.name : (question.title || "").trim() || question.name) + " - " + (table.useNamesAsTitles ? this.row.value : this.row.locText.textOrHtml) + " - " + (table.useNamesAsTitles ? this.col.name : this.col.locTitle.textOrHtml); } getDisplayValue(data, table, options) { let displayValue = data[this.question.name]; const question = this.question; if (this.rowValue && this.colName && typeof displayValue === "object") { let [rowId, colId] = [this.rowValue, this.colName]; displayValue = question.value; if (!options.useValuesAsLabels) { displayValue = question.displayValue; rowId = question.rows.filter(row => row.value === this.rowValue)[0].text; colId = question.getColumnByName(this.colName).title; } displayValue = (displayValue[rowId] && displayValue[rowId][colId]) || ""; } return displayValue; } } class CustomQuestionColumn extends BaseColumn { getCellData(table, data) { this.setupReadyChangedCallback(table, this.question.contentQuestion); return super.getCellData(table, data); } } class CompositeQuestionColumn extends BaseColumn { getCellData(table, data) { const questionList = []; this.question.contentPanel.addQuestionsToList(questionList); questionList.forEach((question) => { this.setupReadyChangedCallback(table, question); }); return super.getCellData(table, data); } } class DefaultColumnsBuilder { createColumn(question, table) { return new BaseColumn(question, table); } buildColumnsCore(question, table) { const columns = []; columns.push(this.createColumn(question, table)); return columns; } buildColumns(question, table) { const columns = this.buildColumnsCore(question, table); if (question.hasComment) { columns.push(new CommentColumn(question, table)); } if (question.hasOther && question["getStoreOthersAsComment"]()) { columns.push(new OtherColumn(question, table)); } return columns; } } class ColumnsBuilderFactory { constructor() { this.columnsBuilders = {}; this.defaultColumnsBuilder = new DefaultColumnsBuilder(); } registerBuilderColumn(type, columnsBuilder) { this.columnsBuilders[type] = columnsBuilder; } getColumnsBuilder(type) { return this.columnsBuilders[type] || this.defaultColumnsBuilder; } } ColumnsBuilderFactory.Instance = new ColumnsBuilderFactory(); class CheckboxColumnsBuilder extends DefaultColumnsBuilder { createColumn(question, table) { return new CheckboxColumn(question, table); } } ColumnsBuilderFactory.Instance.registerBuilderColumn("checkbox", new CheckboxColumnsBuilder()); class SingleChoiceColumnsBuilder extends DefaultColumnsBuilder { createColumn(question, table) { return new SingleChoiceColumn(question, table); } } ColumnsBuilderFactory.Instance.registerBuilderColumn("radiogroup", new SingleChoiceColumnsBuilder()); ColumnsBuilderFactory.Instance.registerBuilderColumn("dropdown", new SingleChoiceColumnsBuilder()); class MatrixColumnsBuilder extends DefaultColumnsBuilder { buildColumnsCore(questionBase, table) { const question = questionBase; const columns = []; question.rows.forEach(row => { columns.push(new MatrixColumn(question, row, table)); }); return columns; } } ColumnsBuilderFactory.Instance.registerBuilderColumn("matrix", new MatrixColumnsBuilder()); class ImageColumnsBuilder extends DefaultColumnsBuilder { createColumn(question, table) { return new ImageColumn(question, table); } } ColumnsBuilderFactory.Instance.registerBuilderColumn("signaturepad", new ImageColumnsBuilder()); class FileColumnsBuilder extends DefaultColumnsBuilder { createColumn(question, table) { return new FileColumn(question, table); } } ColumnsBuilderFactory.Instance.registerBuilderColumn("file", new FileColumnsBuilder()); class MatrixDropdownColumnBuilder extends DefaultColumnsBuilder { buildColumns(questionBase, table) { const question = questionBase; const columns = []; question.rows.forEach(row => { question.columns.forEach(col => { columns.push(new MatrixDropdownColumn(question, row, col, table)); }); }); return columns; } } ColumnsBuilderFactory.Instance.registerBuilderColumn("matrixdropdown", new MatrixDropdownColumnBuilder()); class CustomColumnsBuilder extends DefaultColumnsBuilder { createColumn(question, table) { return new CustomQuestionColumn(question, table); } } ColumnsBuilderFactory.Instance.registerBuilderColumn("custom", new CustomColumnsBuilder()); class CompositeColumnsBuilder extends DefaultColumnsBuilder { getDisplayName(question, table) { var _a; return table.useNamesAsTitles ? question.name : (((_a = question.locTitle) === null || _a === void 0 ? void 0 : _a.renderedHtml) || question.title || "").trim() || question.name; } buildColumnsCore(question, table) { if (CompositeColumnsBuilder.ShowAsSeparateColumns) { const innerQuestions = []; question.contentPanel.addQuestionsToList(innerQuestions); let columns = []; innerQuestions.forEach(innerQuestion => { const builder = ColumnsBuilderFactory.Instance.getColumnsBuilder(innerQuestion.getType()); const cols = builder.buildColumns(innerQuestion, table); cols.forEach(col => { col.name = question.name + "." + col.name; col.displayName = this.getDisplayName(question, table) + " - " + this.getDisplayName(innerQuestion, table); }); columns = columns.concat(cols); }); return columns; } return super.buildColumnsCore(question, table); } createColumn(question, table) { return new CompositeQuestionColumn(question, table); } } CompositeColumnsBuilder.ShowAsSeparateColumns = true; ColumnsBuilderFactory.Instance.registerBuilderColumn("composite", new CompositeColumnsBuilder()); // export type GetDataFn = (params: { filter?: any, limit?: number, offset?: number, callback?: (response: { data: Array<Object>, total: number, error?: any }) => void }) => Promise<{ data: Array<Object>, total: number, error?: any }> | void; class TableEvent extends EventBase { } class Table { constructor(_survey, data, _options = {}, _columnsData = []) { this._survey = _survey; this.data = data; this._options = _options; this._columnsData = _columnsData; this.haveCommercialLicense = false; this.currentPageSize = 5; this._rows = []; /** * Sets pagination selector content. */ this.paginationSizeSelector = [1, 5, 10, 25, 50, 100]; this.onColumnsVisibilityChanged = new TableEvent(); this.onColumnsLocationChanged = new TableEvent(); this.onRowRemoved = new TableEvent(); this.buildColumns = (survey) => { let columns = []; this._survey.getAllQuestions().forEach((question) => { if (!this.isNonValueQuestion(question)) { const builder = ColumnsBuilderFactory.Instance.getColumnsBuilder(question.getTemplate()); columns = columns.concat(builder.buildColumns(question, this)); } }); return columns; }; /** * Fires when table state changed. */ this.onStateChanged = new TableEvent(); if (!this._options) { this._options = {}; } this.initialize(); this.extensions = new TableExtensions(this); const f = hasLicense; this.haveCommercialLicense = (!!f && f(4)) || Table.haveCommercialLicense || (!!_options && (typeof _options.haveCommercialLicense !== "undefined" ? _options.haveCommercialLicense : false)); } initialize() { this.currentPageSize = this.options.pageSize || 5; this.currentPageNumber = 1; this._columns = this.buildColumns(this._survey); this.initTableData(this.data); localization.currentLocale = this._survey.locale; if (this._columnsData.length !== 0) { this.updateColumnsFromData(this._columnsData); } } getTableData() { return [].concat(this.tableData || []); } getData() { return this.data; } get survey() { return this._survey; } get options() { return this._options; } render(targetNode) { if (typeof targetNode === "string") { targetNode = document.getElementById(targetNode); } targetNode.innerHTML = ""; if (!this.haveCommercialLicense) { targetNode.appendChild(createCommercialLicenseLink()); } } enableColumnReorder() { this.isColumnReorderEnabled = true; } disableColumnReorder() { this.isColumnReorderEnabled = false; } getPageNumber() { return this.currentPageNumber; } setPageNumber(value) { this.currentPageNumber = value; } /** * Returns current page size. */ getPageSize() { return this.currentPageSize; } /** * Sets current page size. */ setPageSize(value) { this.currentPageSize = value; this.onStateChanged.fire(this, this.state); } getCreatedRows() { return [].concat(this._rows); } clearCreatedRows() { this._rows.forEach((row) => { row.destroy(); }); this._rows = []; } get useNamesAsTitles() { return this._options && this._options.useNamesAsTitles === true; } get itemsDelimiter() { return this._options && this._options.itemsDelimiter || ", "; } isNonValueQuestion(question) { return Serializer.isDescendantOf(question.getType(), "nonvalue"); } isColumnVisible(column) { if (column.location !== QuestionLocation.Column) return false; return column.isVisible; } get columns() { return [].concat(this._columns); } set columns(columns) { this._columns = columns; this.refresh(true); this.onStateChanged.fire(this, this.state); } get isInitTableDataProcessing() { return this.isInitTableDataProcessingValue; } initTableData(data) { if (!Array.isArray(data)) { this.tableData = undefined; return; } this.isInitTableDataProcessingValue = true; this.tableData = (data || []).map((item) => this.processLoadedDataItem(item)); this.isInitTableDataProcessingValue = false; } processLoadedDataItem(item) { var dataItem = {}; this._survey.data = item; this._columns.forEach((column) => { const opt = column.getCellData(this, item); if (typeof this._options.onGetQuestionValue === "function") { this._options.onGetQuestionValue(opt); } dataItem[column.name] = opt.displayValue; }); return dataItem; } moveColumn(from, to) { var deletedColumns = this._columns.splice(from, 1); this._columns.splice(to, 0, deletedColumns[0]); this.onStateChanged.fire(this, this.state); } setColumnLocation(columnName, location) { this.getColumnByName(columnName).location = location; this.onColumnsLocationChanged.fire(this, { columnName: columnName, location: location, }); this.onStateChanged.fire(this, this.state); } getColumnByName(columnName) { return this._columns.filter((column) => column.name === columnName)[0]; } setColumnVisibility(columnName, isVisible) { var column = this.getColumnByName(columnName); column.isVisible = isVisible; this.onColumnsVisibilityChanged.fire(this, { columnName: columnName, columnVisibility: isVisible, }); this.onStateChanged.fire(this, this.state); } setColumnWidth(columnName, width) { var column = this.getColumnByName(columnName); column.width = width; this.onStateChanged.fire(this, this.state); } removeRow(row) { var index = this._rows.indexOf(row); this._rows.splice(index, 1); this.onRowRemoved.fire(this, { row: row }); } /** * Returns current locale of the table. * If locales more than one, the language selection dropdown will be added in the toolbar */ get locale() { return localization.currentLocale; } /** * Sets locale for table. */ set locale(newLocale) { this._survey.locale = newLocale; localization.currentLocale = newLocale; this.columns.forEach(c => c.displayName = undefined); this.refresh(true); this.onStateChanged.fire(this, this.state); } getLocales() { return [].concat(this._survey.getUsedLocales()); } supportSoftRefresh() { return false; } softRefresh() { this.hardRefresh(); } hardRefresh() { this.initTableData(this.data); if (this.isRendered) { this.currentPageNumber = this.getPageNumber(); const targetNode = this.renderResult; this.destroy(); this.render(targetNode); this.setPageSize(this.currentPageSize); this.setPageNumber(this.currentPageNumber); } } refresh(hard = false) { if (hard || !this.supportSoftRefresh()) { this.hardRefresh(); } else { this.softRefresh(); } } destroy() { this.clearCreatedRows(); this.extensions.destroy(); this.renderResult.innerHTML = ""; this.renderResult = undefined; } get isRendered() { return !!this.renderResult; } /** * Table state getter. */ get state() { return { locale: localization.currentLocale, elements: JSON.parse(JSON.stringify(this._columns)), pageSize: this.currentPageSize, }; } /** * Table state setter. */ set state(newState) { if (!newState) return; if (typeof newState.elements !== "undefined") { this.updateColumnsFromData(newState.elements); } if (typeof newState.locale !== "undefined") { localization.currentLocale = newState.locale; this._survey.locale = newState.locale; this.initTableData(this.data); } if (typeof newState.pageSize !== "undefined") { this.currentPageSize = newState.pageSize; } this.refresh(true); } /** * Resets table state. */ resetState() { this._survey.locale = surveyLocalization.defaultLocale; this.initialize(); this.refresh(true); this.onStateChanged.fire(this, this.state); } updateColumnsFromData(columnsData) { const columns = this._columns; columns.forEach(column => { const el = columnsData.filter(el => column.name === el.name)[0]; if (!!el) { column.fromJSON(el); column.visibleIndex = columnsData.indexOf(el); } }); columnsData.forEach(el => { let column = columns.filter(column => column.name === el.name)[0]; if (!column) { column = new DefaultColumn(undefined, this); column.fromJSON(el); column.visibleIndex = columnsData.indexOf(el); columns.push(column); } }); this._columns = this._columns.sort((col1, col2) => col1.visibleIndex - col2.visibleIndex); } /** * Gets table permissions. */ get permissions() { return this._columns.map((column) => { return { name: column.name, isPublic: column.isPublic, }; }); } /** * Sets table permissions. */ set permissions(permissions) { const updatedElements = this._columns.map((column) => { permissions.forEach((permission) => { if (permission.name === column.name) column.isPublic = permission.isPublic; }); return column; }); this._columns = [].concat(updatedElements); this.onPermissionsChangedCallback && this.onPermissionsChangedCallback(this); } get allowSorting() { return this.options.allowSorting === undefined || this.options.allowSorting === true; } allowExtension(extension) { if (extension.location === "column" && extension.name === "sort") { return this.allowSorting; } return true; } } Table.showFilesAsImages = false; Table.haveCommercialLicense = false; class TableRow { constructor(table, extensionsContainer, detailsContainer) { this.table = table; this.extensionsContainer = extensionsContainer; this.detailsContainer = detailsContainer; this.detailedRowClass = "sa-table__detail-row"; this.isDetailsExpanded = false; this.onToggleDetails = new Event(); this.onColumnLocationChangedCallback = () => { this.closeDetails(); }; this.details = new Details(table, this, detailsContainer); this.extensions = new TableExtensions(table); table.onColumnsLocationChanged.add(this.onColumnLocationChangedCallback); } render() { this.extensions.render(this.extensionsContainer, "row", { row: this }); } openDetails() { this.details.open(); this.getElement().className += " " + this.detailedRowClass; this.onToggleDetails.fire(this, { isExpanded: true }); this.isDetailsExpanded = true; } closeDetails() { this.details.close(); this.getElement().classList.remove(this.detailedRowClass); this.onToggleDetails.fire(this, { isExpanded: false }); this.isDetailsExpanded = false; } toggleDetails() { if (this.isDetailsExpanded) { this.closeDetails(); } else this.openDetails(); } getIsSelected() { return this.isSelected; } toggleSelect() { this.isSelected = !this.isSelected; } remove() { this.table.removeRow(this); this.destroy(); } destroy() { this.table.onColumnsLocationChanged.remove(this.onColumnLocationChangedCallback); this.extensions.destroy(); } } const ARIAL_FONT = "AAEAAAAXAQAABABwRFNJRyQ9+ecABX+MAAAafEdERUZeI11yAAV1GAAAAKZHU1VC1fDdzAAFdcAAAAmqSlNURm0qaQYABX9sAAAAHkxUU0iAZfo8AAAceAAABo5PUy8yDN8yawAAAfgAAABWUENMVP17PkMABXTgAAAANlZETVhQkmr1AAAjCAAAEZRjbWFw50BqOgAA0cQAABdqY3Z0IJYq0nYAAPqgAAAGMGZwZ23MeVmaAADpMAAABm5nYXNwABgACQAFdNAAAAAQZ2x5Zg73j+wAARr8AAPnYmhkbXi+u8OXAAA0nAAAnShoZWFkzpgmkgAAAXwAAAA2aGhlYRIzEv8AAAG0AAAAJGhtdHgONFhAAAACUAAAGihrZXJuN2E5NgAFAmAAABVgbG9jYQ5haTIAAQDQAAAaLG1heHALRwyoAAAB2AAAACBuYW1lwPJlOwAFF8AAABsNcG9zdI/p134ABTLQAABB/3ByZXBS/sTpAADvoAAACv8AAQAAAAMAAObouupfDzz1CBsIAAAAAACi4ycqAAAAALnVtPb6r/1nEAAIDAAAAAkAAQABAAAAAAABAAAHPv5OAEMQAPqv/iYQAAABAAAAAAAAAAAAAAAAAAAGigABAAAGigEAAD8AdgAHAAIAEAAvAFYAAAQNCv8AAwACAAEDiAGQAAUAAAWaBTMAAAEbBZoFMwAAA9EAZgISCAUCCwYEAgICAgIEAAB6h4AAAAAAAAAIAAAAAE1vbm8AQAAg//wF0/5RATMHPgGyQAAB////AAAAAAYAAQAAAAAAAjkAAAI5AAACOQCwAtcAXgRzABUEcwBJBx0AdwVWAFgBhwBaAqoAfAKqAHwDHQBABKwAcgI5AKoCqgBBAjkAugI5AAAEcwBVBHMA3wRzADwEcwBWBHMAGgRzAFUEcwBNBHMAYQRzAFMEcwBVAjkAuQI5AKoErABwBKwAcgSsAHAEcwBaCB8AbwVW//0FVgCWBccAZgXHAJ4FVgCiBOMAqAY5AG0FxwCkAjkAvwQAADcFVgCWBHMAlgaqAJgFxwCcBjkAYwVWAJ4GOQBYBccAoQVWAFwE4wAwBccAoQVWAAkHjQAZBVYACQVWAAYE4wApAjkAiwI5AAACOQAnA8EANgRz/+ECqgBZBHMASgRzAIYEAABQBHMARgRzAEsCOQATBHMAQgRzAIcBxwCIAcf/ogQAAIgBxwCDBqoAhwRzAIcEcwBEBHMAhwRzAEgCqgCFBAAAPwI5ACQEcwCDBAAAGgXHAAYEAAAPBAAAIQQAACgCrAA5AhQAvAKsAC8ErABXBVb//QVW//0FxwBoBVYAogXHAJwGOQBjBccAoQRzAEoEcwBKBHMASgRzAEoEcwBKBHMASgQAAFAEcwBLBHMASwRzAEsEcwBLAjkAvQI5ACMCOf/lAjkACQRzAIcEcwBEBHMARARzAEQEcwBEBHMARARzAIMEcwCDBHMAgwRzAIMEcwBJAzMAgARzAGsEcwAbBHMAUQLNAG0ETAABBOMAmQXlAAMF5QADCAAA4QKqAN4CqgA9BGQATggAAAEGOQBTBbQAmgRkAE4EZABNBGQATQRz//0EnACgA/QAOAW0AHoGlgChBGQAAAIxAAAC9gAvAuwALQYlAH8HHQBEBOMAgQTjAJ4CqgDoBKwAcgRkAFQEcwAuBGQAMwTlABoEcwCGBHMAjAgAAO8FVv/9BVb//QY5AGMIAACBB40AUgRz//wIAAAAAqoAUwKqAEcBxwCAAccAbARkAE4D9AAvBAAAIQVWAAYBVv45BHP/5AKqAFwCqgBcBAAAFwQAABcEcwBJAjkAuQHHAGwCqgBHCAAAJQVW//0FVgCiBVb//QVWAKIFVgCiAjkAjQI5/+ACOQAEAjkAFQY5AGMGOQBjBjkAYwXHAKEFxwChBccAoQI5AMYCqgAZAqoABgKqAB0CqgAuAqoA5QKqAKICqgBrAqoAOgKqALcCqgAoBHMAAAHHAAMFVgBcBAAAPwTjACkEAAAoAhQAvAXH//0EcwBJBVYABgQAACEFVgCeBHMAhwSsAHIErAChAqoAawKqABkCqgAhBqwAawasAGsGrAAhBHMAAAY5AG0EcwBCAjkAsQVWAFwEAAA/BccAZgQAAFAFxwBmBAAAUARzAEYEa//hAqoB8QVW//0EcwBKBVb//QRzAEoFxwCeBOsARwXH//0FVgCiBHMASwVWAKIEcwBLBHMAlgHHAEIEcwCWAlUAiARzAJoCrACDBccAnARzAIcFxwCcBHMAhwY5AGMEcwBEBccAoQKqAIUFxwChAqoAPAVWAFwEAAA/BOMAMAI5ACQE4wAwAwAAIwXHAKEEcwCDBccAoQRzAIME4wApBAAAKATjACkEAAAoBGgApAY5AGAGYgBVBKAASAR0AEgDkQBiBPAARAMpAC4FMABIBGv/4QQAALAC6wBSCMAAMwgAAE8EAACZCAAATwQAAJkIAABPBAAAmAQAAJgH1QFqBcAAngSrAHIE1QCdBKwAcQTVAiIE1QEFBav/6QUAAckFqwJ+Bav/6QWrAn4Fq//pBasCfgWr/+kFq//pBav/6QWr/+kFq//pBasBwAWrAn4FqwHABasBwAWr/+kFq//pBav/6QWrAn4FqwHABasBwAWr/+kFq//pBav/6QWrAn4FqwHABasBwAWr/+kFq//pBav/6QWr/+kFq//pBav/6QWr/+kFq//pBav/6QWr/+kFq//pBav/6QWr/+kFq//pBav/6QWr/+kFqwLWBasAZgWr/+oF1f//BNUAkggAAAAH6wEwB+sBIAfrATAH6wEgBNUAsgTVAIAE1QAqCCsBmAhrAbgHVQAQBgAA9AYAAG8EQAA6BUAANwTAAD8EFQBABAAAJQYAAFUF4QC/A40AiQTV/9kBgACAAtUAhgcVAGEClgAPBNUAkgLWAIMC1gCDBNUAsgLWAHAFVv/9BHMASgXHAGYEAABQBccAZgQAAFAFVgCiBHMASwVWAKIEcwBLBVYAogRzAEsGOQBtBHMAQgY5AG0EcwBCBjkAbQRzAEIFxwCkBHMAhwXHAB8EcwAGAjn/zgI5/84COf/kAjn/5AI5//YCOf/1AjkAowHHAGYEAAA3Acf/ogVWAJYEAACIBAAAhgRzAJYBx//6BccAnARzAIcFyQClBHMAiwY5AGMEcwBEBjkAYwRzAEQFxwChAqoAawVWAFwEAAA/BOMAMAI5AAwFxwChBHMAgwXHAKEEcwCDBccAoQRzAIMFxwChBHMAgweNABkFxwAGBVYABgQAACEBxwCJBVb//QRzAEoIAAABBx0ARAY5AFME4wCBAjkAuQeNABkFxwAGB40AGQXHAAYHjQAZBccABgVWAAYEAAAhAccAigKq/+EEcwAbBM0AWgasAGsGrAAiBqwAIgasAEoCqgDiAqoAawKqAN4Cqv/qBVf//wZG/6cGtP+oAxL/qAYy/6cG2P+nBgX/pwHH/3gFVv/9BVYAlgVY//4FVgCiBOMAKQXHAKQCOQC/BVYAlgVYAAsGqgCYBccAnAUzAG0GOQBjBccApAVWAJ4E8gCUBOMAMAVWAAYFVgAJBq8AfwX7AGECOQAEBVYABgSgAEgDkQBiBHMAiwHHAGsEYACIBJoAjAQAABkDhwBIBHMAiwRzAFwBxwCJBAAAhgQAABgEnACgBAAAGgOVAFwEcwBEBI0AgwPbAFYEYACIBDMAEQW0AHoGPwBXAcf/yQRgAIgEcwBIBGAAiAY/AFcFVwCiBusAMgRVAKEFwABkBVYAXAI5AL8COQAEBAAANwh1AA0IFQCkBtUAMQSpAKEFFQAKBcAAoAVW//0FQACnBVYAlgRVAKEFawAABVYAogdjAAcE1QBOBcAAoQXAAKEEqQChBUAAEgaqAJgFxwCkBjkAYwXAAKAFVgCeBccAZgTjADAFFQAKBhUAUgVWAAkF6wCfBVUAVwdVAKEHgAChBlUAAAcVAKgFQAClBcAAVQgVAKQFxwAaBHMASgSVAFsEQACIAusAiASrAAAEcwBLBVr/+wOrADIEeACHBHgAhwOAAIYEqwAYBYAAjARrAIgEcwBEBFUAiARzAIcEAABQA6oAJgQAACEGlQBLBAAADwSVAIoEKwBFBmsAjQaVAI0FAAAoBcAAiwQrAIQEFQAwBgAAiQRVAB8EcwBLBHMAAALrAIkEFQBLBAAAPwHHAIgCOQAJAcf/ogdAABMGgACDBHMAAAOAAIYEAAAhBGsAiAPpAKEDSgCICAAAQQiVAKAFhQAtAqoBAQKqAB4CqgAxAqoAMQKqAQECqgB+AqoAfgKqAIwCqgCMAqoBAQKqABACqgEBAqoBIQMQAH0CqgCMAjMA0gKqAwsCqv8EAjkAuQSBAGkEVgAyAzEAGQQRAC0E0QCWAfkAmwMPAF8EygCbBLgAjAH5AJsEEwAoA7AAUAO0ADwEygCbBM8AUAH5AJsC0gA8BJgAWgQ8ABkEiABuBF8AcwOxABkD1AAKBGYAlgQTACgFjgBkBSQAKAPyAJsD8gCbA/IAmwHjAFoDVgBaBoYAmwH5/6wEEwAoBBMAKAO0/1cDtP9XBEgALQWOAGQFjgBkBY4AZAWOAGQEgQBpBIEAaQSBAGkEVgAyAzEAGQQRAC0E0QCWAksAAANKAAAEuACMAksAAAQTACgDsABQA7QAPATPAFAC0gA8BJgAWgSIAG4EXwBzA9QACgRmAJYEEwAoBY4AZAUkACgB+QCbBFYAMgOwAFAEXwBzBJsAPAAA/9wAAP8lAAD/3AAA/lECjQCrAo0AoALaAEMDTQB5Aaj/ugGcAEYB5QBGAZwARgGcAEYBrQBIAZwARgGxAEYBUQBGBDUBfAQ1AS4ENQC3BDUAgQQ1ASwENQC+BDUArwQ1AIEENQCaBDUA2wQ1AIUCjQDBBDUAswYAAQAGAAEAAkIANgYAAQAENQCeBDUAmAQ1AMsGAAEABgABAAYAAQAGAAEABgABAAGxAEYGAAEABgABAAYAAQAGAAEABgABAAYAAQAGAAEABgABAAUb/7oGAAEABgABAAYAAQAFtQA6BbUAOgH0/7oB9P+6BgABAAYAAQAGAAEABgABAASBADYENQA2BD3/ugQ9/7oD6QBKA+kASgZ/ABQHdgAUAyf/ugQe/7oGfwAUB3YAFAMn/7oEHv+6BRsAMgS1ACQGAAEABgABAAYAAQAGAAEABgABAAYAAQAGAAEABgABAAYAAQAGAAEABgABAAYAAQAGAAEABgABAAYAAQAGAAEAAc8AMAGxAEYBsQBGAbEAQAGxAEYGAAEABgABAAAA/9wAAP5RAAD/FgAA/xYAAP8WAAD/FgAA/xYAAP8WAAD/FgAA/xYAAP8WAAD/3AAA/xYAAP/cAAD/IAAA/9wEcwBKCAAAAAYAAQAGAAEABgABAAYAAQAGAAEABgABAAYAAQAGAAEABgABAAYAAQAGAAEABgABAAYAAQAGAAEABgABAAYAAQAGAAEABgABAAYAAQAGAAEABgABAAYAAQAGAAEABgABAAYAAQAGAAEABgABAAYAAQACjQB/Ao0AXQYAAQAE7gAVA00AeQGoAA4B1v/cAagAVgHWABADdQAyA3UAMgGoAC0B1gATBRsAMgS1ACQB9P+6AfT/ugGoAJMB1gATBbUAOgW1ADoB9P+6AfT/ugJCAAADAP/3BbUAOgW1ADoB9P+6AfT/ugW1ADoFtQA6AfT/ugH0/7oEgQA2BDUANgQ9/7oEPf+6BIEANgQ1ADYEPf+6BD3/ugSBADYENQA2BD3/ugQ9/7oCswBfArMAXwKzAF8CswBfA+kASgPpAEoD6QBKA+kASgaSAD4GkgA+BD//ugQ//7oGkgA+BpIAPgQ//7oEP/+6CMkAPgjJAD4Gxf+6BsX/ugjJAD4IyQA+BsX/ugbF/7oEp/+6BKf/ugSn/7oEp/+6BKf/ugSn/7oEp/+6BKf/ugRaACoDmgA2BDX/ugMn/7oEWgAqA5oANgQ1/7oDJ/+6Bk8AJwZPACcCJP+6Ahr/ugSnAEYEpwBGAiT/ugIa/7oEzwAtBM8ALQMn/7oDJ/+6BA0ARwQNAEcBqP+6Aaj/ugK0ACMCtAAjAyf/ugMn/7oENQBFBDUARQH0/7oB9P+6AkIANgMA//cDmv+6Ayf/ugN1ADIDdQAyBRsAMgS1ACQFGwAyBL