@progress/telerik-angular-native-report-viewer
Version:
Progress® Telerik® Native Report Viewer for Angular
1 lines • 190 kB
Source Map (JSON)
{"version":3,"file":"progress-telerik-angular-native-report-viewer.mjs","sources":["../../src/lib/localization/messages.ts","../../src/lib/reporting-angular-viewer.service.ts","../../src/lib/parameters/parameters-section.component.ts","../../src/lib/toolbar-tools/pager.component.ts","../../src/lib/directives/after-filter-changed.directive.ts","../../src/lib/search/search-window.component.ts","../../src/lib/aiprompt/aiprompt-window.component.ts","../../src/lib/aiprompt/aiprompt-window.component.html","../../src/lib/models/zoom-level.interface.ts","../../src/lib/toolbar-tools/zoom.component.ts","../../src/lib/reporting-angular-viewer.component.ts","../../src/lib/reporting-angular-viewer.module.ts","../../src/index.ts","../../src/progress-telerik-angular-native-report-viewer.ts"],"sourcesContent":["import { LocalizationMessageType } from '@progress/telerik-common-report-viewer';\n\nexport const messages: LocalizationMessageType = {\n ReportViewer_SendEmailDialogToLabel: 'To:',\n ReportViewer_SendEmailDialogTitle: 'Send Email',\n ReportViewer_SearchDialogCaptionText: 'Find',\n ReportViewer_SearchDialogMatchWholeWordTitle: 'Match Whole Word',\n ReportViewer_SearchDialogUseRegexTitle: 'Use Regex',\n ReportViewer_SearchDialogMatchCaseTitle: 'Match Case',\n ReportViewer_SearchDialogNavigateUpTitle: 'Navigate Up',\n ReportViewer_SearchDialogNavigateDownTitle: 'Navigate Down',\n ReportViewer_SearchDialogPageText: 'page',\n ReportViewer_SearchDialogComboBoxPlaceholderText: 'Search...',\n ReportViewer_SendEmailDialogClose: 'Close',\n ReportViewer_SearchDialogTitle: 'Search in report',\n ReportViewer_SearchDialogClearTitle: 'Clear',\n ReportViewer_SearchDialogNoResultsLabel: 'No results',\n ReportViewer_ToolbarNavigateBackwardTitle: 'Navigate Backward',\n ReportViewer_ToolbarNavigateForwardTitle: 'Navigate Forward',\n ReportViewer_ToolbarSearchTitle: 'Toggle Search',\n ReportViewer_ToolbarAiPromptTitle: 'Toggle AI Prompt',\n ReportViewer_ToolbarExportTitle: 'Export',\n ReportViewer_ToolbarToggleDocumentMapTitle: 'Toggle Document Map',\n ReportViewer_ToolbarFirstPageTitle: 'First Page',\n ReportViewer_ToolbarLastPageTitle: 'Last Page',\n ReportViewer_ToolbarNextPageTitle: 'Next Page',\n ReportViewer_ToolbarPreviousPageTitle: 'Previous Page',\n ReportViewer_ToolbarToggleParametersAreaTitle: 'Toggle Parameters Area',\n ReportViewer_ToolbarPrintPreviewTitle: 'Print Preview',\n ReportViewer_ToolbarPrintReportTitle: 'Print',\n ReportViewer_ToolbarInteractiveViewTitle: 'Interactive View',\n ReportViewer_ToolbarRefreshTitle: 'Refresh',\n ReportViewer_ToolbarStopRenderingTitle: 'Stop Rendering',\n ReportViewer_ToolbarZoomInTitle: 'Zoom In',\n ReportViewer_ToolbarZoomOutTitle: 'Zoom Out',\n ReportViewer_ToolbarZoomComboBoxPlaceholderText: 'Choose zoom level',\n ReportViewer_NoPageToDisplay: 'No page to display.',\n ReportViewer_LoadingReport: 'Loading report...',\n ReportViewer_LoadingReportPagesInProgress: '{0} pages loaded so far...',\n ReportViewer_LoadedReportPagesComplete: 'Done. Total {0} pages loaded.',\n ReportViewer_RenderingCancelled: 'Report processing was canceled.',\n ReportViewer_PreparingPrint: 'Preparing document to print. Please wait...',\n ReportViewer_PreparingDownload: 'Preparing document to download. Please wait...',\n ReportViewer_MissingOrInvalidParameter: 'Error creating report document. Missing or invalid parameter value. Please input valid data for the following parameters:',\n ReportViewer_ErrorSendingDocument: 'Error sending document.',\n ReportViewer_UnableToGetReportParameters: 'Unable to get report parameters.',\n ReportViewer_InvalidParameter: 'Invalid parameter',\n ReportViewer_ErrorCreatingReportDocument: 'Error creating report document.',\n ReportViewer_ErrorServiceUrl: \"Cannot access the Reporting REST service. (serviceUrl = '{0}'). Make sure the service address is correct and enable CORS if needed. (https://enable-cors.org)\",\n ReportViewer_NoReport: 'No report.',\n ReportViewer_AutoRunDisabled: 'Please validate the report parameter values and press Preview to generate the report.',\n ReportViewer_AiPromptDialogConsentAccept: 'Consent',\n ReportViewer_AiPromptDialogConsentReject: 'Cancel',\n ReportViewer_AiPromptDialogTextAreaPlaceholder: \"Enter your prompt\",\n ReportViewer_AiPromptDialogNoPredefinedAndCustomPromptsPlaceholder: \"Custom prompts are disabled and there are no predefined prompts configured. Please allow custom prompts or add predefined prompts to use the AI feature.\",\n ReportViewer_AiPromptDialogNoCustomPromptsPlaceholder: \"Custom prompts are disabled, please select one of the predefined suggestions below\"\n};\n","import { EventEmitter, Injectable } from '@angular/core';\nimport {\n ContentArea,\n DocumentInfo,\n ReportController,\n ReportControllerOptions,\n ReportSourceOptions,\n ScaleMode,\n ServiceType,\n ViewMode,\n ServiceClient,\n SearchManager,\n ServiceClientOptions,\n ServiceClientLoginInfo,\n ParameterInfo,\n reportSourcesAreEqual\n} from '@progress/telerik-common-report-viewer';\nimport { messages } from './localization/messages';\nimport { HistoryItem } from './models/history-item.interface';\n\n\nconst zoomStep = 0.25;\nconst minScale = 0.25;\nconst maxScale = 4;\n\n@Injectable()\nexport class ReportingAngularViewerService {\n public options: any = {\n contentTabIndex: 1000,\n authenticationToken: '',\n reportSource: {\n parameters: {},\n report: null,\n },\n scale: 1,\n scaleMode: 2,\n serviceType: 0,\n serviceUrl: null,\n viewMode: 0,\n keepClientAlive: true,\n messages: messages,\n pageMode: 0,\n printMode: 0,\n reportServer: null,\n };\n\n public documentInfo: DocumentInfo = {\n documentReady: false,\n documentMapAvailable: false,\n containsFrozenContent: false,\n pageCount: 1,\n documentMapNodes: [],\n bookmarkNodes: [],\n renderingExtensions: [],\n };\n\n public onZoomChange: EventEmitter<number> = new EventEmitter<number>();\n\n public parameters: ParameterInfo[] = [];\n public isSearchWindowOpen: boolean = false;\n public isAiPromptWindowOpen: boolean = false;\n public currentHistoryIndex = -1;\n public history: any[] = [];\n public currentPageIndex: number = 0;\n public renderingInProgress: boolean = false;\n public isBackwardsNavigationInProgress: boolean = false;\n public isParametersSectionAvailable: boolean = false;\n\n public reportWidth: number;\n public windowWidth: number = 310;\n public windowHeight: number = 390;\n public totalPages: number = 1;\n public skip: number = 0;\n\n public controller!: ReportController;\n public serviceClient!: ServiceClient;\n public searchManager!: SearchManager;\n public contentArea!: ContentArea;\n\n public getServiceClientOptions(): ServiceClientOptions {\n if (this.options.serviceType === ServiceType.ReportServer && this.options.reportServer !== null) {\n let serverOptions = this.options.reportServer;\n let serviceUrl = serverOptions.url + '/api/reports';\n let tokenUrl = serverOptions.url + '/Token';\n let loginInfo = new ServiceClientLoginInfo(tokenUrl, serverOptions.username, serverOptions.password);\n\n return new ServiceClientOptions(serviceUrl, loginInfo);\n }\n\n return new ServiceClientOptions(this.options.serviceUrl);\n }\n\n public getControllerOptions(): ReportControllerOptions {\n return new ReportControllerOptions(\n this.options.keepClientAlive,\n this.options.authenticationToken,\n this.options.reportSource,\n this.options.printMode,\n this.options.pageMode,\n this.options.viewMode,\n this.options.scaleMode,\n this.options.scale,\n this.options.messages\n );\n }\n\n public setOptions(options: any) {\n this.options = options;\n this.controller.setOptions(this.getControllerOptions());\n this.controller.refreshReport(true, '');\n }\n\n public applyHistory(reportSource: ReportSourceOptions, \n page: number, \n reportDocumentId: string, \n scale: number, \n scaleMode: number, \n viewMode: number) {\n this.controller.setReportSource(reportSource);\n this.controller.setReportDocumentId(reportDocumentId);\n\n if (this.controller.getScale() !== scale) {\n this.setScale(scale);\n }\n\n if (this.controller.getScaleMode() !== scaleMode) {\n this.setScaleMode(scaleMode);\n }\n\n if (this.controller.getViewMode() !== viewMode) {\n this.setViewMode(viewMode);\n }\n\n this.controller.refreshReport(false /* ignoreCache */, reportDocumentId, true /* alwaysRespectAutoRun */);\n this.controller.navigateToPage(page, undefined);\n }\n\n public applyParameter(id: string, value: any) {\n this.isSearchWindowOpen = false;\n this.isAiPromptWindowOpen = false;\n this.controller.setParameter(id, value);\n this.controller.previewReport();\n }\n\n public setScale(scale: number) {\n this.options.scale = scale;\n this.controller.setScale(scale);\n this.onZoomChange.emit(scale);\n }\n\n public setScaleMode(scaleMode: ScaleMode) {\n this.options.scaleMode = scaleMode;\n this.controller.setScaleMode(scaleMode);\n }\n\n public setViewMode(mode?: ViewMode): void {\n if (mode) {\n this.options.viewMode = mode;\n } else {\n this.options.viewMode ? this.options.viewMode = 0 : this.options.viewMode = 1;\n }\n\n this.controller.setViewMode(this.options.viewMode);\n this.controller.refreshReportCore(false, this.controller.getReportDocumentId());\n }\n\n public zoomIn(): void {\n if (this.options.scale < maxScale) {\n this.options.scale += zoomStep;\n this.setScale(this.options.scale);\n }\n }\n\n public zoomOut(): void {\n if (this.options.scale > minScale) {\n this.options.scale -= zoomStep;\n this.setScale(this.options.scale);\n }\n }\n\n public navigateBackwards(): void {\n this.renderingInProgress = true;\n this.isSearchWindowOpen = false;\n this.isAiPromptWindowOpen = false;\n this.isBackwardsNavigationInProgress = true;\n\n this.currentHistoryIndex--;\n\n if (this.history.length >= 0) {\n const currentHistoryItem = this.history[this.currentHistoryIndex];\n this.applyHistory(currentHistoryItem.reportSource, currentHistoryItem.page, currentHistoryItem.documentId, currentHistoryItem.scale, currentHistoryItem.scaleMode, currentHistoryItem.viewMode);\n }\n }\n\n public navigateForward(): void {\n this.renderingInProgress = true;\n this.isSearchWindowOpen = false;\n this.isAiPromptWindowOpen = false;\n\n this.currentHistoryIndex++;\n\n if (this.currentHistoryIndex < this.history.length) {\n const currentHistoryItem = this.history[this.currentHistoryIndex];\n this.applyHistory(currentHistoryItem.reportSource, currentHistoryItem.page, currentHistoryItem.documentId, currentHistoryItem.scale, currentHistoryItem.scaleMode, currentHistoryItem.viewMode);\n }\n }\n\n public addHistoryItem(temp: boolean = false): HistoryItem {\n this.removeTempHistoryItems();\n\n const historyItem: HistoryItem = {\n reportSource: { report: this.controller.getReportSource()?.report, parameters: this.options.reportSource.parameters },\n documentId: this.controller.getReportDocumentId(),\n page: this.getPageNumber(),\n scale: this.controller.getScale(),\n scaleMode: this.controller.getScaleMode(),\n viewMode: this.controller.getViewMode(),\n temp: temp\n };\n\n const controllerReportSource = this.controller.getReportSource();\n if (!this.history.some(item => reportSourcesAreEqual({ firstReportSource: item.reportSource, secondReportSource: controllerReportSource }))) {\n this.currentHistoryIndex++;\n\n let deleteCount: number = this.isBackwardsNavigationInProgress ? 0 : this.history.length - this.currentHistoryIndex;\n this.history.splice(this.currentHistoryIndex, deleteCount, historyItem);\n }\n\n this.isBackwardsNavigationInProgress = false\n\n return historyItem;\n }\n\n public removeTempHistoryItems(): void {\n var lastIndex = this.history.length - 1;\n\n while (lastIndex >= 0 && this.history[lastIndex].temp) {\n this.history.splice(lastIndex, 1);\n\n if (this.currentHistoryIndex >= lastIndex) {\n this.currentHistoryIndex--;\n }\n lastIndex--;\n }\n }\n\n public clearReport(): void {\n this.isSearchWindowOpen = this.isAiPromptWindowOpen = this.renderingInProgress = false;\n this.isSearchWindowOpen = this.renderingInProgress = false;\n this.currentPageIndex = 0;\n this.totalPages = 0;\n this.documentInfo = new DocumentInfo();\n }\n\n public getPageNumber(): number {\n return this.currentPageIndex + 1;\n }\n}\n","import { AfterViewInit, Component, Input, ViewChild, ChangeDetectorRef } from '@angular/core';\nimport { ReportingAngularViewerService } from '../reporting-angular-viewer.service';\nimport { DatePickerComponent } from '@progress/kendo-angular-dateinputs';\nimport { debounceTime } from 'rxjs';\nimport { ParameterInfo, ParameterValue } from '@progress/telerik-common-report-viewer';\n\nconst SystemBooleanType = 'System.Boolean';\nconst SystemDateTimeType = 'System.DateTime';\n\n@Component({\n selector: 'parameters-section',\n template: `\n <div *ngIf=\"parameter.isVisible\" class=\"k-card trv-parameter-container\">\n <div class=\"k-card-header trv-parameter-header\">\n <div class=\"k-card-title trv-parameter-title\" [title]=\"parameter.text\">{{parameter.text}}</div>\n </div>\n <div class=\"k-card-body trv-parameter-value\">\n <div *ngIf=\"isInvalidInput(parameter)\" class=\"k-notification k-notification-error trv-parameter-error\">\n <div class=\"k-notification-wrap\">\n <span class=\"k-icon k-i-warning\"></span>\n <span class=\"k-notification-content trv-parameter-error-message\">{{' ' + service.options.messages.ReportViewer_InvalidParameter}}</span>\n </div>\n </div>\n <div class=\"trv-parameter-editor-available-values\">\n <kendo-multiselect\n *ngIf=\"parameter.multivalue && parameter.availableValues\"\n [data]=\"parameter.availableValues\"\n [valuePrimitive]=\"true\"\n textField=\"name\"\n valueField=\"value\"\n [value]=\"parameter.value\"\n (valueChange)=\"onValueChange(parameter, $event)\"\n [disabled]=\"loading\"\n [kendoDropDownFilter]=\"{caseSensitive: false, operator: 'contains'}\"\n >\n </kendo-multiselect>\n <kendo-combobox\n *ngIf=\"!parameter.multivalue && parameter.availableValues\"\n [data]=\"parameter.availableValues\"\n [valuePrimitive]=\"true\"\n textField=\"name\"\n valueField=\"value\"\n [value]=\"parameter.value\"\n (valueChange)=\"onValueChange(parameter, $event)\"\n [disabled]=\"loading\"\n >\n </kendo-combobox>\n <div *ngIf=\"parameter.availableValues\" class=\"k-actions k-actions-end trv-parameter-actions\">\n <button kendoButton *ngIf=\"parameter.multivalue\" fillMode=\"outline\" class=\"trv-select-all\" (click)=\"onSelectAllClick(parameter)\">Select all</button>\n </div>\n\n <kendo-datepicker *ngIf=\"parameter.type === 'System.DateTime' && !parameter.availableValues\"\n [value]=\"datepickerValue\"\n [disabled]=\"loading\"\n >\n </kendo-datepicker>\n\n <kendo-numerictextbox *ngIf=\"(parameter.type === 'System.Int64' || parameter.type ==='System.Double') && !parameter.availableValues\"\n [value]=\"parameter.value\"\n (valueChange)=\"onValueChange(parameter, $event)\"\n [disabled]=\"loading\"\n >\n </kendo-numerictextbox>\n\n <kendo-textbox *ngIf=\"parameter.type === 'System.String' && !parameter.availableValues\"\n [value]=\"parameter.value\"\n (valueChange)=\"onValueChange(parameter, $event)\"\n [disabled]=\"loading\"\n >\n </kendo-textbox>\n\n <input *ngIf=\"parameter.type === 'System.Boolean' && !parameter.availableValues\"\n type=\"checkbox\"\n kendoCheckBox\n [id]=\"parameter.id\"\n [checked]=\"(parameter.value && parameter.value !== 'False') || parameter.value === 'True'\"\n (change)=\"onValueChange(parameter, $event)\"\n [disabled]=\"loading\"\n />\n </div>\n </div>\n </div>\n `\n})\nexport class ParametersSectionComponent implements AfterViewInit {\n @ViewChild(DatePickerComponent) public datePicker: DatePickerComponent;\n\n @Input() public loading = true;\n @Input() public parameter: ParameterInfo = {\n name: '',\n type: '',\n text: '',\n multivalue: false,\n allowNull: false, \n allowBlank: false,\n isVisible: false, \n autoRefresh: false,\n hasChildParameters: false,\n childParameters: [],\n availableValues: [],\n value: '',\n id: '',\n label: ''\n };\n\n public datepickerValue: Date = new Date();\n isInvalidInput = ParametersSectionComponent.isInvalidInput\n\n public static isInvalidInput(parameter: ParameterInfo): boolean {\n if (parameter.allowNull) {\n return false;\n }\n\n if (parameter.multivalue) {\n return parameter.value == null || parameter.value.length === 0;\n }\n\n return parameter.value == null;\n }\n\n public constructor(public service: ReportingAngularViewerService, private cdr: ChangeDetectorRef) {}\n\n public ngAfterViewInit(): void {\n this.datePicker?.valueChange.asObservable().pipe(debounceTime(this.parameter.autoRefresh ? 2000 : 0)).subscribe(value => {\n this.onValueChange(this.parameter, adjustTimezone(value));\n });\n \n const reportDateParam: ParameterInfo = <ParameterInfo>this.service.parameters.find(p => p.type === SystemDateTimeType && p.id === this.parameter.id);\n this.datepickerValue = new Date(<string>reportDateParam?.value);\n }\n\n public onClearSelectionClick(param: ParameterInfo): void {\n param.value = '';\n }\n\n public onSelectAllClick(parameter: ParameterInfo): void {\n parameter.value = parameter.availableValues.map((val: ParameterValue) => val.value);\n\n this.service.options.reportSource.parameters = JSON.parse(JSON.stringify(this.service.options.reportSource.parameters));\n this.service.options.reportSource.parameters[parameter.id] = parameter.value;\n\n if (!parameter.autoRefresh || !this.service.controller.autoRunEnabled) return;\n\n this.service.applyParameter(parameter.id, parameter.value);\n }\n\n public onValueChange(parameter: ParameterInfo, value: any): void {\n //TODO: should be removed once the common viewer accepts lower case true/false values\n if (parameter.type === SystemBooleanType) {\n value = value.target.checked ? 'True' : 'False';\n }\n\n parameter.value = value;\n this.service.options.reportSource.parameters = JSON.parse(JSON.stringify(this.service.options.reportSource.parameters));\n\n // Can't check for all falsy values because 0 is falsy, won't work with integer parameters\n if (value === undefined || value === null || value.length === 0){\n parameter.value = value = null;\n\n if(!parameter.allowNull) return;\n } \n\n if (parameter.childParameters?.length > 0) {\n parameter.childParameters.forEach((param: ParameterInfo) => {\n delete this.service.options.reportSource.parameters[param.toString()];\n });\n\n this.service.options.reportSource.parameters[parameter.id] = parameter.value;\n\n if (!parameter.autoRefresh || !this.service.controller.autoRunEnabled) return;\n\n this.service.controller.setParameters(this.service.options.reportSource.parameters);\n this.service.controller.refreshReport(true, '');\n } else {\n this.service.options.reportSource.parameters[parameter.id] = value;\n\n if (!parameter.autoRefresh || !this.service.controller.autoRunEnabled) return;\n\n this.service.applyParameter(parameter.id, value);\n\n this.cdr.detectChanges();\n }\n }\n}\n\n// TODO: This method is taken from HTML viewer\nexport function adjustTimezone(date: Date) {\n let result = new Date(\n Date.UTC(\n date.getFullYear(),\n date.getMonth(),\n date.getDate(),\n date.getHours(),\n date.getMinutes(),\n date.getSeconds(),\n date.getMilliseconds()\n )\n );\n return result;\n}\n","import {\n Component,\n Input,\n TemplateRef,\n ViewChild,\n ElementRef,\n forwardRef,\n ViewChildren,\n QueryList,\n} from '@angular/core';\nimport { ToolBarToolComponent } from '@progress/kendo-angular-toolbar';\nimport { ReportingAngularViewerService } from '../reporting-angular-viewer.service';\nimport { PageChangeEvent, PagerComponent } from '@progress/kendo-angular-pager';\nimport {\n SVGIcon,\n caretAltLeftIcon,\n caretAltRightIcon,\n caretAltToLeftIcon,\n caretAltToRightIcon\n} from '@progress/kendo-svg-icons';\nimport { isDocumentAvailable } from '@progress/kendo-angular-common';\n\n@Component({\n providers: [{ provide: ToolBarToolComponent, useExisting: forwardRef(() => CustomPagerToolComponent) }],\n selector: 'custom-pager-tool',\n template: `\n <ng-template #toolbarTemplate>\n <span #toolbarElement>\n <kendo-datapager #pager\n fillMode=\"flat\"\n [class]=\"{'k-disabled' : this.disabled}\"\n [tabindex]=\"tabindex\"\n [pageSize]=\"1\"\n [skip]=\"service.currentPageIndex\"\n [total]=\"service.totalPages\"\n [pageSizeValues]=\"false\"\n [info]=\"false\"\n [previousNext]=\"true\"\n type=\"input\"\n (pageChange)=\"onPageChange($event)\"\n >\n </kendo-datapager>\n </span>\n </ng-template>\n <ng-template #popupTemplate>\n <button\n #navButton\n kendoButton\n class=\"toolbar-nav-btn\"\n icon=\"arrow-end-left\"\n [svgIcon]=\"caretAltToLeftIcon\"\n [tabindex]=\"tabindex\"\n [disabled]=\"service.totalPages <= 1 || service.currentPageIndex === 0\"\n [title]=\"getLocaleMessage('FirstPage')\"\n (click)=\"overflowNavButton(0)\"\n fillMode=\"flat\"> {{ getLocaleMessage('FirstPage') }}\n </button>\n <button\n #navButton\n kendoButton\n class=\"toolbar-nav-btn\"\n icon=\"arrow-60-left\"\n [svgIcon]=\"caretAltLeftIcon\"\n [tabindex]=\"tabindex\"\n [disabled]=\"service.totalPages <= 1 || service.currentPageIndex === 0\"\n [title]=\"getLocaleMessage('PreviousPage')\"\n (click)=\"overflowNavButton(service.currentPageIndex - 1)\"\n fillMode=\"flat\"> {{ getLocaleMessage('PreviousPage') }}\n </button>\n <button\n #navButton\n kendoButton\n class=\"toolbar-nav-btn\"\n icon=\"arrow-60-right\"\n [svgIcon]=\"caretAltRightIcon\"\n [tabindex]=\"tabindex\"\n [disabled]=\"service.totalPages <= 1 || service.currentPageIndex === service.totalPages - 1\"\n [title]=\"getLocaleMessage('NextPage')\"\n (click)=\"overflowNavButton(service.currentPageIndex + 1)\"\n fillMode=\"flat\"> {{ getLocaleMessage('NextPage') }}\n </button>\n <button\n #navButton\n kendoButton\n class=\"toolbar-nav-btn\"\n icon=\"arrow-end-right\"\n [svgIcon]=\"caretAltToRightIcon\"\n [tabindex]=\"tabindex\"\n [disabled]=\"service.totalPages <= 1 || service.currentPageIndex === service.totalPages - 1\"\n [title]=\"getLocaleMessage('LastPage')\"\n (click)=\"overflowNavButton(service.totalPages - 1)\"\n fillMode=\"flat\"> {{ getLocaleMessage('LastPage') }}\n </button>\n </ng-template>\n `,\n styles: [`\n .toolbar-nav-btn {\n width: 100%;\n justify-content: start;\n }\n `]\n})\nexport class CustomPagerToolComponent extends ToolBarToolComponent {\n @Input() public totalPages: number = 1;\n @Input() public disabled: boolean = false;\n\n @ViewChild('toolbarTemplate', { static: true }) public override toolbarTemplate!: TemplateRef<unknown>;\n @ViewChild('popupTemplate', { static: true }) public override popupTemplate!: TemplateRef<unknown>;\n @ViewChild('toolbarElement') public toolbarElement?: ElementRef;\n @ViewChild('popupElement') public popupElement?: ElementRef;\n @ViewChild('pager', { read: PagerComponent, static: false }) public pager?: PagerComponent;\n @ViewChildren('navButton') public navButtons!: QueryList<ElementRef>\n\n public tabindex: number = -1;\n public prevKey: number = 38\n public nextKey: number = 40;\n public focusedIndex: any = -1;\n public caretAltToLeftIcon: SVGIcon = caretAltToLeftIcon;\n public caretAltLeftIcon: SVGIcon = caretAltLeftIcon;\n public caretAltRightIcon: SVGIcon = caretAltRightIcon;\n public caretAltToRightIcon: SVGIcon = caretAltToRightIcon;\n\n constructor(public service: ReportingAngularViewerService) {\n super();\n }\n\n public override canFocus(): boolean {\n return true;\n }\n\n public override focus(ev: any): void {\n this.tabindex = 0;\n\n if (this.overflows) {\n if (!isDocumentAvailable()) return;\n\n if (ev) {\n this.focusedIndex = this.getIndexOfFocused(this.prevKey, this.nextKey, this.buttonElements)(ev);\n this.focusButton(this.focusedIndex, ev);\n }\n } else {\n this.pager?.['element'].nativeElement.focus();\n }\n }\n\n public override handleKey(ev: any): boolean {\n if (this.overflows && isDocumentAvailable()) {\n\n const peekAtIndex = this.makePeeker(this.buttonElements);\n const isUnmodified = this.areEqual(this.focusedIndex);\n\n this.focusedIndex = this.seekFocusedIndex(this.prevKey, this.nextKey, peekAtIndex)(this.focusedIndex, ev);\n this.focusButton(this.focusedIndex, ev);\n\n return !isUnmodified(this.focusedIndex);\n }\n this.tabindex = -1;\n\n return false;\n }\n\n public overflowNavButton(pageIndex: number) {\n this.service.currentPageIndex = pageIndex;\n this.service.controller?.navigateToPage(this.service.getPageNumber(), undefined);\n }\n\n public getLocaleMessage(message: string): string {\n return this.service.options.messages[`ReportViewer_Toolbar${message}Title`];\n }\n\n public onPageChange(e: PageChangeEvent): void {\n this.service.currentPageIndex = e.skip;\n this.service.controller?.navigateToPage(this.service.getPageNumber(), undefined);\n }\n\n private get buttonElements(): HTMLElement[] {\n return [...this.navButtons.toArray().filter(el => !el.nativeElement.classList.contains('k-disabled'))].map(el => el.nativeElement);\n }\n\n private focusButton(index: number, ev: Partial<Event>): void {\n // Guard against focusing twice on mousedown.\n if (!ev.type || ev.type === 'focus' || ev.type === 'keydown') {\n this.buttonElements[index]?.focus();\n }\n }\n\n private getIndexOfFocused = (prevKeyCode: number, nextKeyCode: number, collection: HTMLElement[]): any => (ev: any): any => {\n switch (ev.type) {\n case 'keydown':\n if (ev.keyCode === prevKeyCode) {\n return collection.length - 1;\n }\n \n if (ev.keyCode === nextKeyCode) {\n return 0;\n }\n \n break;\n \n case 'click':\n return collection.findIndex(be => be === ev.target || be.contains(ev.target as Node));\n \n case 'focus':\n return 0;\n \n default:\n return 0;\n }\n };\n\n private seekFocusedIndex = (prevKeyCode: number, nextKeyCode: number, seeker: (i: number) => boolean) => (\n startIndex: number,\n ev: any\n ): number => {\n switch (ev.keyCode) {\n case prevKeyCode:\n return seeker(startIndex - 1) ? startIndex - 1 : startIndex;\n case nextKeyCode:\n return seeker(startIndex + 1) ? startIndex + 1 : startIndex;\n default:\n return startIndex;\n }\n };\n\n private isPresent: Function = (value: any): boolean => value !== null && value !== undefined;\n private makePeeker = (collection: any[]) => (index: number): boolean => this.isPresent(collection[index]);\n private areEqual = (first: number) => (second: number): boolean => first === second;\n}","import { Directive, Input, HostListener, OnDestroy, Output, EventEmitter } from '@angular/core';\nimport { Subject, Subscription } from 'rxjs';\nimport { debounceTime } from 'rxjs/operators';\n\n@Directive({\n selector: '[afterFilterChanged]'\n})\nexport class afterFilterChangedDirective implements OnDestroy {\n @Output()\n public afterFilterChanged: EventEmitter<number> = new EventEmitter<number>();\n\n @Input()\n public filterChangeDelay = 600;\n\n private stream: Subject<number> = new Subject<number>();\n private subscription: Subscription;\n\n constructor() {\n this.subscription = this.stream\n .pipe(debounceTime(this.filterChangeDelay))\n .subscribe((value: number) => this.afterFilterChanged.next(value));\n }\n\n ngOnDestroy(): void {\n this.subscription.unsubscribe();\n }\n\n @HostListener('filterChange', [ '$event' ])\n public onFilterChange(value: number): void {\n this.stream.next(value);\n }\n}\n","import { AfterViewInit, Component, Input, NgZone, OnDestroy, OnInit, ViewChild, ViewEncapsulation } from '@angular/core';\nimport { ReportingAngularViewerService } from '../reporting-angular-viewer.service';\nimport { SearchInfo, SearchResult } from '@progress/telerik-common-report-viewer';\nimport { ListBoxComponent, ListBoxSelectionEvent } from '@progress/kendo-angular-listbox';\nimport {\n SVGIcon,\n chevronDownIcon,\n chevronUpIcon,\n convertLowercaseIcon,\n regularExpressionIcon,\n wholeWordIcon\n} from '@progress/kendo-svg-icons';\nimport { ComboBoxComponent } from '@progress/kendo-angular-dropdowns';\nimport { Subscription, take } from 'rxjs';\n\n@Component({\n selector: 'search-window',\n template: `\n <kendo-window\n (close)=\"toggle(false)\"\n class=\"trv-search trv-search-dialog\"\n [height]=\"390\"\n [(top)]=\"windowTop\"\n [(left)]=\"windowLeft\"\n (widthChange)=\"onWidthChange($event)\"\n (heightChange)=\"onHeightChange($event)\"\n [width]=\"windowWidth\"\n [height]=\"windowHeight\"\n [minHeight]=\"390\"\n [minWidth]=\"330\"\n >\n <kendo-window-titlebar>\n <span class=\"k-window-title\"> {{ getLocaleMessage('Title') }} </span>\n <button kendoWindowMinimizeAction></button>\n <button kendoWindowRestoreAction></button>\n <button kendoWindowCloseAction></button>\n </kendo-window-titlebar>\n <div class=\"trv-search-window trv-window k-window-content\">\n <div class=\"search-dialog-wrapper\">\n <kendo-combobox\n #comboBox\n [data]=\"searchTerms\"\n [value]=\"searchText\"\n [allowCustom]=\"true\"\n [filterable]=\"true\"\n [placeholder]=\"getLocaleMessage('ComboBoxPlaceholderText')\"\n (afterFilterChanged)=\"handleFilter($event)\"\n (click)=\"open($event, comboBox)\"\n (close)=\"onClose()\"\n (blur)=\"onBlur()\"\n (valueChange)=\"search($event)\"\n (open)=\"$event.preventDefault()\"\n ></kendo-combobox>\n\n <div class=\"k-hstack k-gap-2 k-ml-2\">\n <button \n kendoButton\n fillMode=\"flat\"\n icon=\"convert-lowercase\"\n [svgIcon]=\"convertToLowerCaseIcon\"\n [title]=\"getLocaleMessage('MatchCaseTitle')\"\n [selected]=\"searchInfo.matchCase\"\n (click)=\"onSearchFilter('matchCase')\">\n </button>\n <button\n kendoButton\n fillMode=\"flat\"\n icon=\"whole-word\"\n [svgIcon]=\"wholeWordIcon\"\n [title]=\"getLocaleMessage('MatchWholeWordTitle')\"\n [selected]=\"searchInfo.matchWholeWord\"\n (click)=\"onSearchFilter('matchWholeWord')\">\n </button>\n <button\n kendoButton\n fillMode=\"flat\"\n icon=\"regular-expression\"\n [svgIcon]=\"regularExpressionIcon\"\n [title]=\"getLocaleMessage('UseRegexTitle')\"\n [selected]=\"searchInfo.useRegularExpressions\"\n (click)=\"onSearchFilter('useRegularExpressions')\">\n </button>\n </div>\n </div>\n\n <div class=\"trv-search-dialog-results k-hstack k-gap-3 k-align-items-center k-mt-3 k-mb-2\">\n <span *ngIf=\"searchResults.length > 0\" class=\"trv-search-dialog-results-label\">\n Result {{ listBox.selectedIndex + 1}} of {{searchResults.length }}\n </span>\n <span *ngIf=\"searchResults.length === 0\" class=\"trv-search-dialog-results-label\">\n {{ getLocaleMessage('NoResultsLabel') }}\n </span>\n <kendo-buttongroup>\n <button\n kendoButton\n fillMode=\"flat\"\n icon=\"chevron-up\"\n [svgIcon]=\"chevronUpIcon\"\n [title]=\"getLocaleMessage('NavigateUpTitle')\"\n (click)=\"onNavigateButtons('up')\"\n [disabled]=\"selectedIndex <= 0\">\n </button>\n <button\n kendoButton\n fillMode=\"flat\"\n icon=\"chevron-down\"\n [svgIcon]=\"chevronDownIcon\"\n [title]=\"getLocaleMessage('NavigateDownTitle')\"\n (click)=\"onNavigateButtons('down');\"\n [disabled]=\"selectedIndex + 1 >= searchResults.length\">\n </button>\n </kendo-buttongroup>\n </div>\n\n <kendo-listbox\n #listBox\n [style.width.%]=\"100\"\n [data]=\"searchResults\"\n [toolbar]=\"false\"\n (selectionChange)=\"onSelectionChange($event)\"\n >\n <ng-template kendoListBoxItemTemplate let-dataItem>\n <div\n [attr.data-uid]=\"dataItem.id\"\n class=\"trv-search-dialog-results-row k-listview-item k-selected\"\n role=\"option\"\n >\n <span class=\"k-search-dialog-result-desc\">{{dataItem.description}}</span>\n <span class=\"trv-search-dialog-results-pageSpan\">page {{dataItem.page}}</span>\n </div>\n </ng-template>\n </kendo-listbox>\n </div>\n </kendo-window>\n `,\n styles: [`\n .k-window-content .k-listbox {\n overflow: hidden;\n position: relative;\n flex-grow: 1;\n height: auto;\n }\n\n .k-window-content .k-list {\n max-height: 100%;\n }\n\n .trv-search {\n max-height: 700px;\n }\n\n .k-listview-item .k-search-dialog-result-desc {\n flex: 1;\n margin-right: 1em;\n }\n\n .k-listview-item.trv-search-dialog-results-row {\n width: 100%;\n display: flex;\n justify-content: space-between;\n }\n `],\n encapsulation: ViewEncapsulation.None\n})\nexport class SearchComponent implements OnInit, AfterViewInit, OnDestroy {\n @Input() searchTerms: string[];\n\n @ViewChild('listBox') public listbox!: ListBoxComponent;\n @ViewChild('comboBox') public comboBox!: ComboBoxComponent;\n\n public chevronUpIcon: SVGIcon = chevronUpIcon;\n public wholeWordIcon: SVGIcon = wholeWordIcon;\n public chevronDownIcon: SVGIcon = chevronDownIcon;\n public convertToLowerCaseIcon: SVGIcon = convertLowercaseIcon;\n public regularExpressionIcon: SVGIcon = regularExpressionIcon;\n\n public windowTop: number = 30;\n public windowLeft!: number;\n public windowWidth!: number;\n public windowHeight!: number;\n\n public searchResults: SearchResult[] = [];\n public isComboboxOpen: boolean = false;\n public searchText: string = '';\n public opened = true;\n public selectedIndex: number = 0;\n\n public searchInfo: SearchInfo = {\n searchToken: '',\n matchCase: false,\n matchWholeWord: false,\n useRegularExpressions: false\n }\n\n private subs: Subscription = new Subscription();\n\n public constructor(\n private ngZone: NgZone,\n public service: ReportingAngularViewerService) {}\n\n public ngOnInit(): void {\n const reportElemWidth = this.service.reportWidth;\n\n this.windowHeight = this.service.windowHeight;\n this.windowWidth = this.service.windowWidth;\n this.windowLeft = reportElemWidth - (this.windowWidth + 40);\n }\n\n public ngAfterViewInit(): void {\n this.service.searchManager?.on('searchComplete', (results: SearchResult[]) => this.onSearchComplete(results));\n\n this.ngZone.onStable.pipe(take(1)).subscribe(_ => this.comboBox?.focus());\n\n const combobox = this.comboBox?.wrapper?.nativeElement;\n this.subs.add(combobox.addEventListener('keydown', (event: any) => this.onArrowEvent(event), true));\n }\n\n public ngOnDestroy(): void {\n this.subs.unsubscribe();\n }\n\n public toggle(isOpened: boolean): void {\n this.service.isSearchWindowOpen = isOpened;\n this.searchResults = [];\n this.service.searchManager.closeSearch();\n }\n\n public handleFilter(value: any) {\n if(value === \"\") {\n return;\n }\n\n this.searchText = value;\n this.search(value, true);\n }\n\n public onWidthChange(width: number): void {\n this.service.windowWidth = width;\n }\n\n public onHeightChange(height: number): void {\n this.service.windowHeight = height;\n }\n\n public onBlur() {\n this.searchText = this.comboBox.text = this.comboBox?.searchbar.value;\n }\n\n public search(text: string, isTextFilter: boolean = false): void {\n if (typeof text !== 'string' && !isTextFilter) return;\n\n if (text && this.searchTerms.indexOf(text) === -1) {\n this.searchTerms.push(text);\n }\n\n if (this.searchInfo.searchToken !== text) {\n this.selectedIndex = 0;\n }\n\n this.comboBox.value = text;\n this.comboBox.searchBarChange(text);\n this.searchInfo.searchToken = text;\n\n this.service.searchManager?.search(this.searchInfo);\n\n this.ngZone.onStable.pipe(take(1)).subscribe(_ => this.comboBox?.focus());\n }\n\n public onSearchFilter(filterName: 'matchCase' | 'matchWholeWord' | 'useRegularExpressions'): void {\n this.selectedIndex = 0;\n this.searchInfo[filterName] = !this.searchInfo[filterName];\n\n this.search(this.searchInfo.searchToken);\n }\n\n public onSelectionChange(item: ListBoxSelectionEvent): void {\n const searchResultItem: SearchResult = this.searchResults[item.index];\n this.service.searchManager?.highlightSearchItem(searchResultItem);\n }\n\n public open(event: any, comboBox: ComboBoxComponent): void {\n if (event.target.closest('button') && !this.isComboboxOpen) {\n this.isComboboxOpen = true;\n comboBox.toggle(true);\n }\n }\n\n public onClose(): void {\n setTimeout(() => this.isComboboxOpen = false )\n }\n\n public onNavigateButtons(direction: 'up' | 'down', sourceTarget: 'comboboxNav' | 'listboxNav' = 'listboxNav'): void {\n const topReached = direction === 'up' && this.selectedIndex <= 0;\n const bottomReached = direction === 'down' && this.selectedIndex >= this.searchResults.length - 1;\n\n if (topReached || bottomReached) {\n return;\n }\n\n this.selectedIndex = this.selectedIndex + (direction === 'up' ? -1 : 1);\n\n this.listbox?.selectionService.select(this.selectedIndex);\n }\n\n public getLocaleMessage(message: string): string {\n return this.service.options.messages[`ReportViewer_SearchDialog${message}`];\n }\n\n private onArrowEvent(event: any): void {\n event.stopImmediatePropagation();\n const searchResultItems = this.searchResults.length;\n\n if (searchResultItems > 0) {\n const isArrowUp = event.key === 'ArrowUp';\n const isArrowDown = event.key === 'ArrowDown'\n\n if (isArrowUp || isArrowDown) {\n event.preventDefault();\n\n const dir: 'up' | 'down' = isArrowUp ? 'up' : 'down';\n this.onNavigateButtons(dir, 'comboboxNav');\n }\n\n const currentListboxItem = this.listbox.listboxItems.toArray()[this.selectedIndex].nativeElement;\n currentListboxItem?.focus();\n\n this.ngZone.onStable.pipe(take(1)).subscribe(() => this.comboBox?.focus());\n }\n }\n\n private onSearchComplete(results: SearchResult[]): void {\n let totalCount = results.length;\n\n if (totalCount > 250) {\n results = results.slice(0, 250);\n }\n\n this.searchResults = results;\n\n if (results) {\n this.listbox?.selectItem(0);\n }\n \n if (totalCount > 0) {\n this.service.searchManager?.navigateToPage(results[0]);\n }\n }\n}\n","import { Component, ElementRef, Input, OnInit, Renderer2, ViewEncapsulation } from '@angular/core';\nimport { ReportingAngularViewerService } from '../reporting-angular-viewer.service';\nimport { PromptOutput, PromptRequestEvent } from \"@progress/kendo-angular-conversational-ui\";\nimport { SVGIcon, xIcon } from '@progress/kendo-svg-icons';\nimport { AiInfo } from '@progress/telerik-common-report-viewer/dist/Types/AiInfoType';\n\n@Component({\n selector: 'aiprompt-window',\n templateUrl: './aiprompt-window.component.html',\n styleUrls: ['./aiprompt-window.component.scss'],\n encapsulation: ViewEncapsulation.None\n})\nexport class AiPromptComponent implements OnInit {\n @Input() promptOutputs: Array<PromptOutput>;\n\n public windowTop: number = 80;\n public windowLeft!: number;\n public windowWidth!: number;\n\n public xIcon: SVGIcon = xIcon;\n public activeView: number = 0;\n public showConsent: boolean = false;\n public aiInfo: AiInfo;\n public predefinedPrompts: string[];\n public consentAcceptBtnText: string = \"\";\n public consentRejectBtnText: string = \"\";\n private idCounter = 0;\n\n public constructor(private renderer: Renderer2, private el: ElementRef, private service: ReportingAngularViewerService) {}\n \n async ngOnInit(): Promise<void> {\n const reportElemWidth = this.service.reportWidth;\n this.windowWidth = 500;\n this.windowLeft = reportElemWidth - (this.windowWidth + 20);\n\n this.aiInfo = await this.service.controller.createAIThread();\n if (this.aiInfo.predefinedPrompts && this.aiInfo.predefinedPrompts.length > 0) {\n this.predefinedPrompts = this.aiInfo.predefinedPrompts;\n }\n\n if (this.aiInfo.requireConsent) {\n this.showConsent = this.aiInfo.requireConsent && this.service.controller.loadFromSessionStorage(\"trvAiConsent\") !== \"true\";\n this.consentAcceptBtnText = this.getLocaleMessage('ConsentAccept');;\n this.consentRejectBtnText = this.getLocaleMessage('ConsentReject');;\n }\n\n this.onActiveViewChange(this.activeView);\n }\n\n public toggle(isOpened: boolean): void {\n this.service.isAiPromptWindowOpen = isOpened;\n }\n\n public onWidthChange(width: number): void {\n this.service.windowWidth = width;\n }\n\n public onHeightChange(height: number): void {\n this.service.windowHeight = height;\n }\n\n public onPromptRequest(event: PromptRequestEvent): void {\n const generateButtonEl = this.el.nativeElement.querySelector('.k-prompt-footer .k-actions .k-button');\n generateButtonEl && this.renderer.addClass(generateButtonEl, 'k-disabled');\n this.service.controller.getAIResponse(event.prompt)\n .then(response => {\n this.promptOutputs.unshift(this.createPromptOutputFromResponse(response, event));\n generateButtonEl && this.renderer.removeClass(generateButtonEl, 'k-disabled');\n this.activeView = 1;\n }).catch(error => {\n this.promptOutputs.unshift(this.createPromptOutputFromResponse(error?._responseJSON, event));\n generateButtonEl && this.renderer.removeClass(generateButtonEl, 'k-disabled');\n this.activeView = 1;\n });\n }\n\n public onActiveViewChange(viewId: number): void {\n // The Kendo event is triggered before the new AI prompt view is rendered so the textarea doesn't exist\n // Wrap the call in setTimeout in order to have the textarea element present before setting the disabled class\n if (viewId === 0 && !this.aiInfo.allowCustomPrompts) {\n setTimeout(() => {\n let promptTextAreaPlaceholder = this.getLocaleMessage('TextAreaPlaceholder');\n if (!(this.predefinedPrompts && this.predefinedPrompts.length > 0)) {\n const aiPromptGenerateButton = this.el.nativeElement.querySelector('.k-prompt-footer .k-actions');\n aiPromptGenerateBut