UNPKG

devextreme

Version:

HTML5 JavaScript Component Suite for Responsive Web Development

1,076 lines (1,069 loc) • 51.3 kB
/** * DevExtreme (ui/pivot_grid/ui.pivot_grid.js) * Version: 18.1.3 * Build date: Tue May 15 2018 * * Copyright (c) 2012 - 2018 Developer Express Inc. ALL RIGHTS RESERVED * Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/ */ "use strict"; var $ = require("../../core/renderer"), windowUtils = require("../../core/utils/window"), window = windowUtils.getWindow(), eventsEngine = require("../../events/core/events_engine"), registerComponent = require("../../core/component_registrator"), getPublicElement = require("../../core/utils/dom").getPublicElement, stringUtils = require("../../core/utils/string"), commonUtils = require("../../core/utils/common"), each = require("../../core/utils/iterator").each, isDefined = require("../../core/utils/type").isDefined, extend = require("../../core/utils/extend").extend, clickEvent = require("../../events/click"), messageLocalization = require("../../localization/message"), Widget = require("../widget/ui.widget"), eventUtils = require("../../events/utils"), gridCoreUtils = require("../grid_core/ui.grid_core.utils"), pivotGridUtils = require("./ui.pivot_grid.utils"), pivotGridDataController = require("./ui.pivot_grid.data_controller"), PivotGridDataSource = require("./data_source"), dataAreaNamespace = require("./ui.pivot_grid.data_area"), headersArea = require("./ui.pivot_grid.headers_area"), fieldsArea = require("./ui.pivot_grid.fields_area"), PivotGridFieldChooser = require("./ui.pivot_grid.field_chooser"), PivotGridFieldChooserBase = require("./ui.pivot_grid.field_chooser_base"), ExportMixin = require("./ui.pivot_grid.export").ExportMixin, chartIntegrationMixin = require("./ui.pivot_grid.chart_integration"), Popup = require("../popup"), ContextMenu = require("../context_menu"), deferredUtils = require("../../core/utils/deferred"), when = deferredUtils.when, Deferred = deferredUtils.Deferred, DATA_AREA_CELL_CLASS = "dx-area-data-cell", ROW_AREA_CELL_CLASS = "dx-area-row-cell", COLUMN_AREA_CELL_CLASS = "dx-area-column-cell", DESCRIPTION_AREA_CELL_CLASS = "dx-area-description-cell", BORDERS_CLASS = "dx-pivotgrid-border", PIVOTGRID_CLASS = "dx-pivotgrid", ROW_LINES_CLASS = "dx-row-lines", BOTTOM_ROW_CLASS = "dx-bottom-row", BOTTOM_BORDER_CLASS = "dx-bottom-border", FIELDS_CONTAINER_CLASS = "dx-pivotgrid-fields-container", FIELDS_CLASS = "dx-area-fields", FIELD_CHOOSER_POPUP_CLASS = "dx-fieldchooser-popup", INCOMPRESSIBLE_FIELDS_CLASS = "dx-incompressible-fields", OVERFLOW_HIDDEN_CLASS = "dx-overflow-hidden", TR = "<tr>", TD = "<td>", DIV = "<div>", TEST_HEIGHT = 66666; function getArraySum(array) { var sum = 0; each(array, function(_, value) { sum += value || 0 }); return sum } function adjustSizeArray(sizeArray, space) { var delta = space / sizeArray.length; for (var i = 0; i < sizeArray.length; i++) { sizeArray[i] -= delta } } function unsubscribeScrollEvents(area) { area.off("scroll").off("stop") } function subscribeToScrollEvent(area, handler) { unsubscribeScrollEvents(area); area.on("scroll", handler).on("stop", handler) } var scrollBarInfoCache = {}; function getScrollBarInfo(useNativeScrolling) { if (scrollBarInfoCache[useNativeScrolling]) { return scrollBarInfoCache[useNativeScrolling] } var scrollBarUseNative, scrollBarWidth = 0, options = {}; var container = $(DIV).css({ position: "absolute", visibility: "hidden", top: -1e3, left: -1e3, width: 100, height: 100 }).appendTo("body"); var content = $("<p>").css({ width: "100%", height: 200 }).appendTo(container); if ("auto" !== useNativeScrolling) { options.useNative = !!useNativeScrolling; options.useSimulatedScrollbar = !useNativeScrolling } container.dxScrollable(options); scrollBarUseNative = container.dxScrollable("instance").option("useNative"); scrollBarWidth = scrollBarUseNative ? container.width() - content.width() : 0; container.remove(); scrollBarInfoCache[useNativeScrolling] = { scrollBarWidth: scrollBarWidth, scrollBarUseNative: scrollBarUseNative }; return scrollBarInfoCache[useNativeScrolling] } function getCommonBorderWidth(elements, direction) { var borderStyleNames = "width" === direction ? ["borderLeftWidth", "borderRightWidth"] : ["borderTopWidth", "borderBottomWidth"], width = 0; each(elements, function(_, elem) { var computedStyle = window.getComputedStyle(elem.get(0)); borderStyleNames.forEach(function(borderStyleName) { width += parseFloat(computedStyle[borderStyleName]) || 0 }) }); return width } function clickedOnFieldsArea($targetElement) { return $targetElement.closest("." + FIELDS_CLASS).length || $targetElement.find("." + FIELDS_CLASS).length } var PivotGrid = Widget.inherit({ _getDefaultOptions: function() { return extend(this.callBase(), { scrolling: { timeout: 300, renderingThreshold: 150, minTimeout: 10, mode: "standard", useNative: "auto", removeInvisiblePages: true }, encodeHtml: true, dataSource: null, activeStateEnabled: false, fieldChooser: { minWidth: 250, minHeight: 250, enabled: true, allowSearch: false, layout: 0, title: messageLocalization.format("dxPivotGrid-fieldChooserTitle"), width: 600, height: 600, applyChangesMode: "instantly" }, onContextMenuPreparing: null, allowSorting: false, allowSortingBySummary: false, allowFiltering: false, allowExpandAll: false, wordWrapEnabled: true, fieldPanel: { showColumnFields: true, showFilterFields: true, showDataFields: true, showRowFields: true, allowFieldDragging: true, visible: false, texts: { columnFieldArea: messageLocalization.format("dxPivotGrid-columnFieldArea"), rowFieldArea: messageLocalization.format("dxPivotGrid-rowFieldArea"), filterFieldArea: messageLocalization.format("dxPivotGrid-filterFieldArea"), dataFieldArea: messageLocalization.format("dxPivotGrid-dataFieldArea") } }, dataFieldArea: "column", "export": { enabled: false, fileName: "PivotGrid", proxyUrl: void 0, ignoreExcelErrors: true }, showRowTotals: true, showRowGrandTotals: true, showColumnTotals: true, showColumnGrandTotals: true, hideEmptySummaryCells: true, showTotalsPrior: "none", rowHeaderLayout: "standard", loadPanel: { enabled: true, text: messageLocalization.format("Loading"), width: 200, height: 70, showIndicator: true, indicatorSrc: "", showPane: true }, texts: { grandTotal: messageLocalization.format("dxPivotGrid-grandTotal"), total: messageLocalization.getFormatter("dxPivotGrid-total"), noData: messageLocalization.format("dxDataGrid-noDataText"), showFieldChooser: messageLocalization.format("dxPivotGrid-showFieldChooser"), expandAll: messageLocalization.format("dxPivotGrid-expandAll"), collapseAll: messageLocalization.format("dxPivotGrid-collapseAll"), sortColumnBySummary: messageLocalization.getFormatter("dxPivotGrid-sortColumnBySummary"), sortRowBySummary: messageLocalization.getFormatter("dxPivotGrid-sortRowBySummary"), removeAllSorting: messageLocalization.format("dxPivotGrid-removeAllSorting"), exportToExcel: messageLocalization.format("dxDataGrid-exportToExcel"), dataNotAvailable: messageLocalization.format("dxPivotGrid-dataNotAvailable") }, onCellClick: null, onCellPrepared: null, showBorders: false, stateStoring: { enabled: false, storageKey: null, type: "localStorage", customLoad: null, customSave: null, savingTimeout: 2e3 }, onExpandValueChanging: null, renderCellCountLimit: 2e4, onExporting: null, onExported: null, onFileSaving: null, headerFilter: { width: 252, height: 325, allowSearch: false, texts: { emptyValue: messageLocalization.format("dxDataGrid-headerFilterEmptyValue"), ok: messageLocalization.format("dxDataGrid-headerFilterOK"), cancel: messageLocalization.format("dxDataGrid-headerFilterCancel") } } }) }, _getDataControllerOptions: function() { var that = this; return { component: that, dataSource: that.option("dataSource"), texts: that.option("texts"), showRowTotals: that.option("showRowTotals"), showRowGrandTotals: that.option("showRowGrandTotals"), showColumnTotals: that.option("showColumnTotals"), showTotalsPrior: that.option("showTotalsPrior"), showColumnGrandTotals: that.option("showColumnGrandTotals"), dataFieldArea: that.option("dataFieldArea"), rowHeaderLayout: that.option("rowHeaderLayout"), hideEmptySummaryCells: that.option("hideEmptySummaryCells"), onFieldsPrepared: function(fields) { each(fields, function(index, field) { each(["allowSorting", "allowSortingBySummary", "allowFiltering", "allowExpandAll"], function(_, optionName) { if (void 0 === field[optionName]) { pivotGridUtils.setFieldProperty(field, optionName, that.option(optionName)) } }) }) } } }, _initDataController: function() { var that = this; that._dataController && that._dataController.dispose(); that._dataController = new pivotGridDataController.DataController(that._getDataControllerOptions()); if (windowUtils.hasWindow()) { that._dataController.changed.add(function() { that._render() }) } that._dataController.scrollChanged.add(function(options) { that._scrollLeft = options.left; that._scrollTop = options.top }); that._dataController.loadingChanged.add(function(isLoading) { that._updateLoading() }); that._dataController.progressChanged.add(that._updateLoading.bind(that)); that._dataController.dataSourceChanged.add(function() { that._trigger("onChanged") }); var expandValueChanging = that.option("onExpandValueChanging"); if (expandValueChanging) { that._dataController.expandValueChanging.add(function(e) { expandValueChanging(e) }) } }, _init: function() { var that = this; that.callBase(); that._initDataController(); that._scrollLeft = that._scrollTop = null; that._initActions() }, _initActions: function() { var that = this; that._actions = { onChanged: that._createActionByOption("onChanged"), onContextMenuPreparing: that._createActionByOption("onContextMenuPreparing"), onCellClick: that._createActionByOption("onCellClick"), onExporting: that._createActionByOption("onExporting"), onExported: that._createActionByOption("onExported"), onFileSaving: that._createActionByOption("onFileSaving"), onCellPrepared: that._createActionByOption("onCellPrepared") } }, _trigger: function(eventName, eventArg) { this._actions[eventName](eventArg) }, _optionValuesEqual: function(name, oldValue, newValue) { if ("dataSource" === name && newValue instanceof PivotGridDataSource && oldValue instanceof PivotGridDataSource) { return newValue === oldValue } return this.callBase.apply(this, arguments) }, _optionChanged: function(args) { var that = this; switch (args.name) { case "dataSource": case "allowSorting": case "allowFiltering": case "allowExpandAll": case "allowSortingBySummary": case "scrolling": case "stateStoring": that._initDataController(); that._fieldChooserPopup.hide(); that._renderFieldChooser(); that._invalidate(); break; case "texts": case "showTotalsPrior": case "showRowTotals": case "showRowGrandTotals": case "showColumnTotals": case "showColumnGrandTotals": case "hideEmptySummaryCells": case "dataFieldArea": that._dataController.updateViewOptions(that._getDataControllerOptions()); break; case "useNativeScrolling": case "encodeHtml": case "renderCellCountLimit": break; case "rtlEnabled": that.callBase(args); that._renderFieldChooser(); that._renderContextMenu(); windowUtils.hasWindow() && that._renderLoadPanel(that._dataArea.groupElement(), that.$element()); that._invalidate(); break; case "export": that._renderDescriptionArea(); break; case "onExpandValueChanging": break; case "onCellClick": case "onContextMenuPreparing": case "onExporting": case "onExported": case "onFileSaving": case "onCellPrepared": that._actions[args.name] = that._createActionByOption(args.name); break; case "fieldChooser": that._renderFieldChooser(); that._renderDescriptionArea(); break; case "loadPanel": if (windowUtils.hasWindow()) { that._renderLoadPanel(that._dataArea.groupElement(), that.$element()); that._invalidate() } break; case "fieldPanel": that._renderDescriptionArea(); that._invalidate(); break; case "headerFilter": that._renderFieldChooser(); that._invalidate(); break; case "showBorders": that._tableElement().toggleClass(BORDERS_CLASS, !!args.value); that.updateDimensions(); break; case "wordWrapEnabled": that._tableElement().toggleClass("dx-word-wrap", !!args.value); that.updateDimensions(); break; case "rowHeaderLayout": that._tableElement().find("." + ROW_AREA_CELL_CLASS).toggleClass("dx-area-tree-view", "tree" === args.value); that._dataController.updateViewOptions(that._getDataControllerOptions()); break; case "height": case "width": that._hasHeight = null; that.callBase(args); that.resize(); break; default: that.callBase(args) } }, _updateScrollPosition: function(columnsArea, rowsArea, dataArea) { var scrollTop, scrollLeft, that = this, scrolled = that._scrollTop || that._scrollLeft; if (rowsArea && !rowsArea.hasScroll() && that._hasHeight) { that._scrollTop = null } if (columnsArea && !columnsArea.hasScroll()) { that._scrollLeft = null } if (null !== that._scrollTop || null !== that._scrollLeft || scrolled || that.option("rtlEnabled")) { scrollTop = that._scrollTop || 0; scrollLeft = that._scrollLeft || 0; dataArea.scrollTo({ x: scrollLeft, y: scrollTop }); columnsArea.scrollTo(scrollLeft); rowsArea.scrollTo(scrollTop); that._dataController.updateWindowScrollPosition(that._scrollTop) } }, _subscribeToEvents: function(columnsArea, rowsArea, dataArea) { var that = this, scrollHandler = function(e) { var scrollOffset = e.scrollOffset, leftOffset = isDefined(scrollOffset.left) ? scrollOffset.left : that._scrollLeft, topOffset = isDefined(scrollOffset.top) && that._hasHeight ? scrollOffset.top : that._scrollTop; if ((that._scrollLeft || 0) !== (leftOffset || 0) || (that._scrollTop || 0) !== (topOffset || 0)) { that._scrollLeft = leftOffset; that._scrollTop = topOffset; that._updateScrollPosition(columnsArea, rowsArea, dataArea); if ("virtual" === that.option("scrolling.mode")) { that._dataController.setViewportPosition(that._scrollLeft, that._scrollTop) } } }; each([columnsArea, rowsArea, dataArea], function(_, area) { subscribeToScrollEvent(area, scrollHandler) }); !that._hasHeight && that._dataController.subscribeToWindowScrollEvents(dataArea.groupElement()) }, _clean: commonUtils.noop, _needDelayResizing: function(cellsInfo) { var cellsCount = cellsInfo.length * (cellsInfo.length ? cellsInfo[0].length : 0); return cellsCount > this.option("renderCellCountLimit") }, _renderFieldChooser: function() { var that = this, container = that._pivotGridContainer, fieldChooserOptions = that.option("fieldChooser") || {}, toolbarItems = "onDemand" === fieldChooserOptions.applyChangesMode ? [{ toolbar: "bottom", location: "after", widget: "dxButton", options: { text: messageLocalization.format("OK"), onClick: function(e) { that._fieldChooserPopup.$content().dxPivotGridFieldChooser("applyChanges"); that._fieldChooserPopup.hide() } } }, { toolbar: "bottom", location: "after", widget: "dxButton", options: { text: messageLocalization.format("Cancel"), onClick: function(e) { that._fieldChooserPopup.hide() } } }] : [], fieldChooserComponentOptions = { layout: fieldChooserOptions.layout, texts: fieldChooserOptions.texts || {}, dataSource: that.getDataSource(), allowSearch: fieldChooserOptions.allowSearch, width: void 0, height: void 0, headerFilter: that.option("headerFilter"), encodeHtml: that.option("encodeHtml"), applyChangesMode: fieldChooserOptions.applyChangesMode }, popupOptions = { shading: false, title: fieldChooserOptions.title, width: fieldChooserOptions.width, height: fieldChooserOptions.height, showCloseButton: true, resizeEnabled: true, minWidth: fieldChooserOptions.minWidth, minHeight: fieldChooserOptions.minHeight, toolbarItems: toolbarItems, onResize: function(e) { e.component.$content().dxPivotGridFieldChooser("updateDimensions") }, onShown: function(e) { that._createComponent(e.component.content(), PivotGridFieldChooser, fieldChooserComponentOptions) }, onHidden: function(e) { var fieldChooser = e.component.$content().dxPivotGridFieldChooser("instance"); fieldChooser.resetTreeView(); fieldChooser.cancelChanges() } }; if (that._fieldChooserPopup) { that._fieldChooserPopup.option(popupOptions); that._fieldChooserPopup.$content().dxPivotGridFieldChooser(fieldChooserComponentOptions) } else { that._fieldChooserPopup = that._createComponent($(DIV).addClass(FIELD_CHOOSER_POPUP_CLASS).appendTo(container), Popup, popupOptions) } }, _renderContextMenu: function() { var that = this, $container = that._pivotGridContainer; if (that._contextMenu) { that._contextMenu.$element().remove() } that._contextMenu = that._createComponent($(DIV).appendTo($container), ContextMenu, { onPositioning: function(actionArgs) { var targetElement, args, items, event = actionArgs.event; actionArgs.cancel = true; if (!event) { return } targetElement = event.target.cellIndex >= 0 ? event.target : $(event.target).closest("td").get(0); if (!targetElement) { return } args = that._createEventArgs(targetElement, event); items = that._getContextMenuItems(args); if (items) { actionArgs.component.option("items", items); actionArgs.cancel = false; return } }, onItemClick: function(params) { params.itemData.onItemClick && params.itemData.onItemClick(params) }, cssClass: PIVOTGRID_CLASS, target: that.$element() }) }, _getContextMenuItems: function(e) { var that = this, items = [], texts = that.option("texts"); if ("row" === e.area || "column" === e.area) { var areaFields = e[e.area + "Fields"], oppositeAreaFields = e["column" === e.area ? "rowFields" : "columnFields"], field = e.cell.path && areaFields[e.cell.path.length - 1], dataSource = that.getDataSource(); if (field && field.allowExpandAll && e.cell.path.length < e[e.area + "Fields"].length) { items.push({ beginGroup: true, icon: "none", text: texts.expandAll, onItemClick: function() { dataSource.expandAll(field.index) } }); items.push({ text: texts.collapseAll, icon: "none", onItemClick: function() { dataSource.collapseAll(field.index) } }) } if (e.cell.isLast) { var sortingBySummaryItemCount = 0; each(oppositeAreaFields, function(index, field) { if (!field.allowSortingBySummary) { return } each(e.dataFields, function(dataIndex, dataField) { if (isDefined(e.cell.dataIndex) && e.cell.dataIndex !== dataIndex) { return } var showDataFieldCaption = !isDefined(e.cell.dataIndex) && e.dataFields.length > 1, textFormat = "column" === e.area ? texts.sortColumnBySummary : texts.sortRowBySummary, checked = pivotGridUtils.findField(e.dataFields, field.sortBySummaryField) === dataIndex && (e.cell.path || []).join("/") === (field.sortBySummaryPath || []).join("/"), text = stringUtils.format(textFormat, showDataFieldCaption ? field.caption + " - " + dataField.caption : field.caption); items.push({ beginGroup: 0 === sortingBySummaryItemCount, icon: checked ? "desc" === field.sortOrder ? "sortdowntext" : "sortuptext" : "none", text: text, onItemClick: function() { dataSource.field(field.index, { sortBySummaryField: dataField.caption || dataField.dataField, sortBySummaryPath: e.cell.path, sortOrder: "desc" === field.sortOrder ? "asc" : "desc" }); dataSource.load() } }); sortingBySummaryItemCount++ }) }); each(oppositeAreaFields, function(index, field) { if (!field.allowSortingBySummary || !isDefined(field.sortBySummaryField)) { return } items.push({ beginGroup: 0 === sortingBySummaryItemCount, icon: "none", text: texts.removeAllSorting, onItemClick: function() { each(oppositeAreaFields, function(index, field) { dataSource.field(field.index, { sortBySummaryField: void 0, sortBySummaryPath: void 0, sortOrder: void 0 }) }); dataSource.load() } }); return false }) } } if (that.option("fieldChooser.enabled")) { items.push({ beginGroup: true, icon: "columnchooser", text: texts.showFieldChooser, onItemClick: function() { that._fieldChooserPopup.show() } }) } if (that.option("export.enabled")) { items.push({ beginGroup: true, icon: "exportxlsx", text: texts.exportToExcel, onItemClick: function() { that.exportToExcel() } }) } e.items = items; that._trigger("onContextMenuPreparing", e); items = e.items; if (items && items.length) { return items } }, _createEventArgs: function(targetElement, dxEvent) { var that = this, dataSource = that.getDataSource(), args = { rowFields: dataSource.getAreaFields("row"), columnFields: dataSource.getAreaFields("column"), dataFields: dataSource.getAreaFields("data"), event: dxEvent }; if (clickedOnFieldsArea($(targetElement))) { return extend(that._createFieldArgs(targetElement), args) } else { return extend(that._createCellArgs(targetElement), args) } }, _createFieldArgs: function(targetElement) { var field = $(targetElement).children().data("field"), args = { field: field }; return isDefined(field) ? args : {} }, _createCellArgs: function(cellElement) { var $cellElement = $(cellElement), columnIndex = cellElement.cellIndex, rowIndex = cellElement.parentElement.rowIndex, $table = $cellElement.closest("table"), data = $table.data("data"), cell = data && data[rowIndex] && data[rowIndex][columnIndex], args = { area: $table.data("area"), rowIndex: rowIndex, columnIndex: columnIndex, cellElement: getPublicElement($cellElement), cell: cell }; return args }, _handleCellClick: function(e) { var that = this, args = that._createEventArgs(e.currentTarget, e), cell = args.cell; if (!cell || !args.area && (args.rowIndex || args.columnIndex)) { return } that._trigger("onCellClick", args); cell && !args.cancel && isDefined(cell.expanded) && setTimeout(function() { that._dataController[cell.expanded ? "collapseHeaderItem" : "expandHeaderItem"](args.area, cell.path) }) }, _getNoDataText: function() { return this.option("texts.noData") }, _renderNoDataText: gridCoreUtils.renderNoDataText, _renderLoadPanel: gridCoreUtils.renderLoadPanel, _updateLoading: function(progress) { var loadPanelVisible, that = this, isLoading = that._dataController.isLoading(); if (!that._loadPanel) { return } loadPanelVisible = that._loadPanel.option("visible"); if (!loadPanelVisible) { that._startLoadingTime = new Date } if (isLoading) { if (progress) { if (new Date - that._startLoadingTime >= 1e3) { that._loadPanel.option("message", Math.floor(100 * progress) + "%") } } else { that._loadPanel.option("message", that.option("loadPanel.text")) } } clearTimeout(that._hideLoadingTimeoutID); if (loadPanelVisible && !isLoading) { that._hideLoadingTimeoutID = setTimeout(function() { that._loadPanel.option("visible", false); that.$element().removeClass(OVERFLOW_HIDDEN_CLASS) }) } else { that._loadPanel.option("visible", isLoading); that.$element().toggleClass(OVERFLOW_HIDDEN_CLASS, !isLoading) } }, _renderDescriptionArea: function() { var that = this, $element = that.$element(), $descriptionCell = $element.find("." + DESCRIPTION_AREA_CELL_CLASS), $toolbarContainer = $(DIV).addClass("dx-pivotgrid-toolbar"), fieldPanel = that.option("fieldPanel"), $filterHeader = $element.find(".dx-filter-header"), $columnHeader = $element.find(".dx-column-header"); var $targetContainer; if (fieldPanel.visible && fieldPanel.showFilterFields) { $targetContainer = $filterHeader } else { if (fieldPanel.visible && (fieldPanel.showDataFields || fieldPanel.showColumnFields)) { $targetContainer = $columnHeader } else { $targetContainer = $descriptionCell } } $columnHeader.toggleClass(BOTTOM_BORDER_CLASS, !!(fieldPanel.visible && (fieldPanel.showDataFields || fieldPanel.showColumnFields))); $filterHeader.toggleClass(BOTTOM_BORDER_CLASS, !!(fieldPanel.visible && fieldPanel.showFilterFields)); $descriptionCell.toggleClass("dx-pivotgrid-background", fieldPanel.visible && (fieldPanel.showDataFields || fieldPanel.showColumnFields || fieldPanel.showRowFields)); that.$element().find(".dx-pivotgrid-toolbar").remove(); $toolbarContainer.prependTo($targetContainer); if (that.option("fieldChooser.enabled")) { that._createComponent($(DIV).appendTo($toolbarContainer).addClass("dx-pivotgrid-field-chooser-button"), "dxButton", { icon: "columnchooser", hint: that.option("texts.showFieldChooser"), onClick: function() { that.getFieldChooserPopup().show() } }) } if (that.option("export.enabled")) { that._createComponent($(DIV).appendTo($toolbarContainer).addClass("dx-pivotgrid-export-button"), "dxButton", { icon: "exportxlsx", hint: that.option("texts.exportToExcel"), onClick: function() { that.exportToExcel() } }) } }, _detectHasContainerHeight: function() { var testElement, that = this, element = that.$element(); if (isDefined(that._hasHeight) || element.is(":hidden")) { return } that._pivotGridContainer.addClass("dx-hidden"); testElement = $(DIV).height(TEST_HEIGHT); element.append(testElement); that._hasHeight = element.height() !== TEST_HEIGHT; that._pivotGridContainer.removeClass("dx-hidden"); testElement.remove() }, _renderHeaders: function(rowHeaderContainer, columnHeaderContainer, filterHeaderContainer, dataHeaderContainer) { var that = this, dataSource = that.getDataSource(), FieldsArea = fieldsArea.FieldsArea; that._rowFields = that._rowFields || new FieldsArea(that, "row"); that._rowFields.render(rowHeaderContainer, dataSource.getAreaFields("row")); that._columnFields = that._columnFields || new FieldsArea(that, "column"); that._columnFields.render(columnHeaderContainer, dataSource.getAreaFields("column")); that._filterFields = that._filterFields || new FieldsArea(that, "filter"); that._filterFields.render(filterHeaderContainer, dataSource.getAreaFields("filter")); that._dataFields = that._dataFields || new FieldsArea(that, "data"); that._dataFields.render(dataHeaderContainer, dataSource.getAreaFields("data")); that.$element().dxPivotGridFieldChooserBase("instance").renderSortable() }, _createTableElement: function() { var that = this; var $table = $("<table>").css({ width: "100%" }).toggleClass(BORDERS_CLASS, !!that.option("showBorders")).toggleClass("dx-word-wrap", !!that.option("wordWrapEnabled")); eventsEngine.on($table, eventUtils.addNamespace(clickEvent.name, "dxPivotGrid"), "td", that._handleCellClick.bind(that)); return $table }, _renderDataArea: function(dataAreaElement) { var that = this, dataArea = that._dataArea || new dataAreaNamespace.DataArea(that); that._dataArea = dataArea; dataArea.render(dataAreaElement, that._dataController.getCellsInfo()); return dataArea }, _renderRowsArea: function(rowsAreaElement) { var that = this, rowsArea = that._rowsArea || new headersArea.VerticalHeadersArea(that); that._rowsArea = rowsArea; rowsArea.render(rowsAreaElement, that._dataController.getRowsInfo()); return rowsArea }, _renderColumnsArea: function(columnsAreaElement) { var that = this, columnsArea = that._columnsArea || new headersArea.HorizontalHeadersArea(that); that._columnsArea = columnsArea; columnsArea.render(columnsAreaElement, that._dataController.getColumnsInfo()); return columnsArea }, _initMarkup: function() { var that = this; that.callBase.apply(this, arguments); that.$element().addClass(PIVOTGRID_CLASS) }, _renderContentImpl: function() { var columnsAreaElement, rowsAreaElement, dataAreaElement, tableElement, dataArea, rowsArea, columnsArea, rowHeaderContainer, columnHeaderContainer, filterHeaderContainer, dataHeaderContainer, that = this, isFirstDrawing = !that._pivotGridContainer; tableElement = !isFirstDrawing && that._tableElement(); if (!tableElement) { that.$element().addClass(ROW_LINES_CLASS).addClass(FIELDS_CONTAINER_CLASS); that._pivotGridContainer = $(DIV).addClass("dx-pivotgrid-container"); that._renderFieldChooser(); that._renderContextMenu(); columnsAreaElement = $(TD).addClass(COLUMN_AREA_CELL_CLASS); rowsAreaElement = $(TD).addClass(ROW_AREA_CELL_CLASS); dataAreaElement = $(TD).addClass(DATA_AREA_CELL_CLASS); tableElement = that._createTableElement(); dataHeaderContainer = $(TD).addClass("dx-data-header"); filterHeaderContainer = $("<td>").attr("colspan", "2").addClass("dx-filter-header"); columnHeaderContainer = $(TD).addClass("dx-column-header"); rowHeaderContainer = $(TD).addClass(DESCRIPTION_AREA_CELL_CLASS); $(TR).append(filterHeaderContainer).appendTo(tableElement); $(TR).append(dataHeaderContainer).append(columnHeaderContainer).appendTo(tableElement); $(TR).append(rowHeaderContainer).append(columnsAreaElement).appendTo(tableElement); $(TR).addClass(BOTTOM_ROW_CLASS).append(rowsAreaElement).append(dataAreaElement).appendTo(tableElement); that._pivotGridContainer.append(tableElement); that.$element().append(that._pivotGridContainer); if ("tree" === that.option("rowHeaderLayout")) { rowsAreaElement.addClass("dx-area-tree-view") } } that.$element().addClass(OVERFLOW_HIDDEN_CLASS); that._createComponent(that.$element(), PivotGridFieldChooserBase, { dataSource: that.getDataSource(), encodeHtml: that.option("encodeHtml"), allowFieldDragging: that.option("fieldPanel.allowFieldDragging"), headerFilter: that.option("headerFilter") }); dataArea = that._renderDataArea(dataAreaElement); rowsArea = that._renderRowsArea(rowsAreaElement); columnsArea = that._renderColumnsArea(columnsAreaElement); dataArea.tableElement().prepend(columnsArea.headElement()); if (isFirstDrawing) { that._renderLoadPanel(dataArea.groupElement().parent(), that.$element()); that._renderDescriptionArea(); rowsArea.processScroll(); columnsArea.processScroll() }[dataArea, rowsArea, columnsArea].forEach(function(area) { unsubscribeScrollEvents(area) }); that._renderHeaders(rowHeaderContainer, columnHeaderContainer, filterHeaderContainer, dataHeaderContainer); that._update(isFirstDrawing) }, _update: function(isFirstDrawing) { var updateHandler, that = this; updateHandler = function() { that.updateDimensions().done(function() { that._subscribeToEvents(that._columnsArea, that._rowsArea, that._dataArea) }) }; if (that._needDelayResizing(that._dataArea.getData()) && isFirstDrawing) { setTimeout(updateHandler) } else { updateHandler() } }, _fireContentReadyAction: function() { if (!this._dataController.isLoading()) { this.callBase() } }, getScrollPath: function(area) { var that = this; if ("column" === area) { return that._columnsArea.getScrollPath(that._scrollLeft) } else { return that._rowsArea.getScrollPath(that._scrollTop) } }, getDataSource: function() { return this._dataController.getDataSource() }, getFieldChooserPopup: function() { return this._fieldChooserPopup }, hasScroll: function(area) { var that = this; return "column" === area ? that._columnsArea.hasScroll() : that._rowsArea.hasScroll() }, _dimensionChanged: function() { this.updateDimensions() }, _visibilityChanged: function(visible) { if (visible) { this.updateDimensions() } }, _dispose: function() { var that = this; clearTimeout(that._hideLoadingTimeoutID); that.callBase.apply(that, arguments); if (that._dataController) { that._dataController.dispose() } }, _tableElement: function() { return this.$element().find("table").first() }, addWidgetPrefix: function(className) { return "dx-pivotgrid-" + className }, resize: function() { this.updateDimensions() }, isReady: function() { return this.callBase() && !this._dataController.isLoading() }, updateDimensions: function() { var groupWidth, groupHeight, dataAreaHeights, rowsAreaHeights, resultHeights, resultWidths, rowsAreaColumnWidths, bordersWidth, hasRowsScroll, hasColumnsScroll, elementWidth, columnsAreaHeight, descriptionCellHeight, columnsAreaRowHeights, rowHeights, columnsAreaRowCount, needSynchronizeFieldPanel, that = this, tableElement = that._tableElement(), rowsArea = that._rowsArea, columnsArea = that._columnsArea, dataArea = that._dataArea, totalWidth = 0, totalHeight = 0, rowsAreaWidth = 0, scrollBarInfo = getScrollBarInfo(that.option("scrolling.useNative")), scrollBarWidth = scrollBarInfo.scrollBarWidth, dataAreaCell = tableElement.find("." + DATA_AREA_CELL_CLASS), rowAreaCell = tableElement.find("." + ROW_AREA_CELL_CLASS), columnAreaCell = tableElement.find("." + COLUMN_AREA_CELL_CLASS), descriptionCell = tableElement.find("." + DESCRIPTION_AREA_CELL_CLASS), filterHeaderCell = tableElement.find(".dx-filter-header"), rowFieldsHeader = that._rowFields, d = new Deferred; if (!windowUtils.hasWindow()) { return } needSynchronizeFieldPanel = rowFieldsHeader.isVisible() && "tree" !== that.option("rowHeaderLayout"), that._detectHasContainerHeight(); if (!dataArea.headElement().length) { dataArea.tableElement().prepend(columnsArea.headElement()) } if (needSynchronizeFieldPanel) { rowsArea.updateColspans(rowFieldsHeader.getColumnsCount()); rowsArea.tableElement().prepend(rowFieldsHeader.headElement()) } tableElement.addClass(INCOMPRESSIBLE_FIELDS_CLASS); dataArea.reset(); rowsArea.reset(); columnsArea.reset(); rowFieldsHeader.reset(); commonUtils.deferUpdate(function() { resultWidths = dataArea.getColumnsWidth(); rowHeights = rowsArea.getRowsHeight(); rowsAreaHeights = needSynchronizeFieldPanel ? rowHeights.slice(1) : rowHeights; dataAreaHeights = dataArea.getRowsHeight(); descriptionCellHeight = descriptionCell.outerHeight() + (needSynchronizeFieldPanel ? rowHeights[0] : 0); columnsAreaRowCount = that._dataController.getColumnsInfo().length; resultHeights = pivotGridUtils.mergeArraysByMaxValue(rowsAreaHeights, dataAreaHeights.slice(columnsAreaRowCount)); columnsAreaRowHeights = dataAreaHeights.slice(0, columnsAreaRowCount); columnsAreaHeight = getArraySum(columnsAreaRowHeights); rowsAreaColumnWidths = rowsArea.getColumnsWidth(); if (that._hasHeight) { bordersWidth = getCommonBorderWidth([columnAreaCell, dataAreaCell, tableElement, tableElement.find(".dx-column-header"), filterHeaderCell], "height"); groupHeight = that.$element().height() - filterHeaderCell.height() - tableElement.find(".dx-data-header").height() - (Math.max(dataArea.headElement().height(), columnAreaCell.height(), descriptionCellHeight) + bordersWidth) } totalWidth = dataArea.tableElement().width(); totalHeight = getArraySum(resultHeights); rowsAreaWidth = getArraySum(rowsAreaColumnWidths); elementWidth = that.$element().width(); bordersWidth = getCommonBorderWidth([rowAreaCell, dataAreaCell, tableElement], "width"); groupWidth = elementWidth - rowsAreaWidth - bordersWidth; groupWidth = groupWidth > 0 ? groupWidth : totalWidth; hasRowsScroll = that._hasHeight && totalHeight - groupHeight >= 1; hasColumnsScroll = totalWidth - groupWidth >= 1; if (!hasRowsScroll) { groupHeight = totalHeight + (hasColumnsScroll ? scrollBarWidth : 0) } commonUtils.deferRender(function() { columnsArea.tableElement().append(dataArea.headElement()); rowFieldsHeader.tableElement().append(rowsArea.headElement()); if (!hasColumnsScroll && hasRowsScroll && scrollBarWidth) { adjustSizeArray(resultWidths, scrollBarWidth); totalWidth -= scrollBarWidth } if (descriptionCellHeight > columnsAreaHeight) { adjustSizeArray(columnsAreaRowHeights, columnsAreaHeight - descriptionCellHeight); columnsArea.setRowsHeight(columnsAreaRowHeights) } tableElement.removeClass(INCOMPRESSIBLE_FIELDS_CLASS); columnsArea.groupWidth(groupWidth); columnsArea.processScrollBarSpacing(hasRowsScroll ? scrollBarWidth : 0); columnsArea.setColumnsWidth(resultWidths); rowsArea.groupHeight(that._hasHeight ? groupHeight : "auto"); rowsArea.processScrollBarSpacing(hasColumnsScroll ? scrollBarWidth : 0); rowsArea.setColumnsWidth(rowsAreaColumnWidths); rowsArea.setRowsHeight(resultHeights); dataArea.setColumnsWidth(resultWidths); dataArea.setRowsHeight(resultHeights); dataArea.groupWidth(groupWidth); dataArea.groupHeight(that._hasHeight ? groupHeight : "auto"); needSynchronizeFieldPanel && rowFieldsHeader.setColumnsWidth(rowsAreaColumnWidths); dataAreaCell.toggleClass(BOTTOM_BORDER_CLASS, !hasRowsScroll); rowAreaCell.toggleClass(BOTTOM_BORDER_CLASS, !hasRowsScroll); if (!that._hasHeight && elementWidth !== that.$element().width()) { var diff = elementWidth - that.$element().width(); if (!hasColumnsScroll) { adjustSizeArray(resultWidths, diff); columnsArea.setColumnsWidth(resultWidths); dataArea.setColumnsWidth(resultWidths) } dataArea.groupWidth(groupWidth - diff); columnsArea.groupWidth(groupWidth - diff) } if ("virtual" === that.option("scrolling.mode") && !that._dataController.isEmpty()) { var virtualContentParams = that._dataController.calculateVirtualContentParams({ contentWidth: totalWidth, contentHeight: totalHeight, rowCount: resultHeights.length, columnCount: resultWidths.length, viewportWidth: groupWidth, viewportHeight: that._hasHeight ? groupHeight : $(window).outerHeight() }); dataArea.setVirtualContentParams({ top: virtualContentParams.contentTop, left: virtualContentParams.contentLeft, width: virtualContentParams.width, height: virtualContentParams.height }); rowsArea.setVirtualContentParams({ top: virtualContentParams.contentTop, width: row