Types have now been added, go to 'Update Columns' to configure and group take-off
`;\n// Exports\nexport default code;","import { json } from 'aurelia-fetch-client';\r\nimport { HttpResponseMessage } from 'aurelia-http-client';\r\nimport { autoinject, bindable } from 'aurelia-framework';\r\nimport { EventAggregator, Subscription } from 'aurelia-event-aggregator';\r\nimport { ApiClient } from 'app/services/api-client';\r\nimport { RequestMethod } from 'app/services/RequestMethod';\r\nimport { AzFuncsClient } from 'app/services/AzFuncsClient';\r\nimport { AppContextService } from 'app/services/app-context-service';\r\nimport { I18N } from 'aurelia-i18n';\r\nimport { Router } from 'aurelia-router';\r\nimport { QuantityFilterDto } from 'app/project/quantity-views/quantity-filters';\r\nimport { ModalService } from 'app/services/modal-service';\r\nimport { QuantityFilterColumns } from '../quantity-filter-columns/quantity-filter-columns';\r\nimport { ColInfo, PivotDto, ColInf2 } from './ColInfo';\r\nimport { ContextMenuService } from 'app/services/context-menu-service';\r\nimport { PivotGridSelectionService } from 'app/services/pivot-grid-selection-service';\r\nimport { OrgTokenService } from 'app/services/org-token-service';\r\nimport { ObjectIDsIF } from 'app/project/ifc-fun/openbim-container';\r\nimport { DetailedParameterDefinitionDto } from './DetailedParameterDefinitionDto';\r\nimport { RequestErrorHandling } from 'app/services/RequestErrorHandling';\r\n\r\n@autoinject\r\nexport class PivotGridComponent {\r\n\tprivate quantityFilterId: string = \"\";\r\n\tprivate quantityFilterName: string = \"\";\r\n\tprivate subscriptions: Subscription[] = [];\r\n\r\n\t@bindable QF: QuantityFilterDto | undefined;\r\n\t@bindable shouldRoundNumbers: boolean | undefined;\r\n\t@bindable useFunction: boolean | undefined;\r\n\t@bindable showIds: boolean | undefined;\r\n\tprivate headerDTO: ColInfo[] = [];\r\n\t//private headers_: string[] = [];\r\n\tprivate headers: ColInf2[] = [];\r\n\tprivate entityLabelsPerRow: number[][] = []; // currently only holds relAggr main-entityLabels, not sub/children entityLabels.\r\n\tprivate revitElmIdsPerRow: number[][] = []\r\n\tprivate filterHasSources: boolean = false;\r\n\tprivate quantityColumnOpen: boolean = false;\r\n\tprivate childrenCount: number = -1;\r\n\tprivate entityLabelToAjourInstIdMap: Map; // beware, will only hold MAIN entityLabel for relAggr currently, not SUB/children entityLabels.\r\n\r\n\tconstructor(\r\n\t\tpublic apiClient: ApiClient,\r\n\t\tpublic azf_Client: AzFuncsClient,\r\n\t\tpublic appContext: AppContextService, private i18n: I18N, private router: Router,\r\n\t\tprivate events: EventAggregator, private modal: ModalService, private contextMenu: ContextMenuService,\r\n\t\tprivate selectionService: PivotGridSelectionService,\r\n\t\tprivate orgTokenService: OrgTokenService\r\n\t) {\r\n\t\tconsole.log('ctor A');\r\n\t\tthis.entityLabelToAjourInstIdMap = new Map();\r\n\t}\r\n\r\n\tapi(): RequestMethod {\r\n\t\treturn this.apiClient.project(this.appContext.projectId);\r\n\t}\r\n\r\n\tbind(): void {\r\n\t\tthis.quantityColumnOpen = false;\r\n\t\tthis.subscriptions = [\r\n\t\t\tthis.events.subscribe('pivot-grid-component:update-grid', () => {\r\n\t\t\t\tthis.initialiseGrid();\r\n\t\t\t}),\r\n\t\t\tthis.events.subscribe('base-viewer-combined:select', async (response: { event: Event, objectIds: number[] }) => { await this.highlightMatchingRows(response.objectIds) }),\r\n\t\t]\r\n\t}\r\n\r\n async highlightMatchingRows(selectedObjectIds: number[]):Promise {\r\n\r\n\t\t//objectIds.forEach((entityLabel: number) => {});\r\n\r\n\t\tlet workHarder_entityLabels: number[] = [];\r\n\r\n\t\tlet found_rowNumbersToHighlight: number[] = [];\r\n\r\n\t\tfor (let selected_entityLabel /*:number*/ of selectedObjectIds) { // for each selected IfcEntityLabel (StepId) .. \r\n\t\t\tlet found:boolean = false; // Look through all row-entityLabels to see if any of the rows contains contains that selected-entityLabel:\r\n\t\t\tfor (let rowNr:number = 0; rowNr < this.entityLabelsPerRow.length; ++rowNr) {\r\n\t\t\t\tlet entityLabelsInRow = this.entityLabelsPerRow[rowNr];\r\n\t\t\t\t//if (entityLabelsInRow.filter((entityLabel_inRow: number) => entityLabel_inRow == selected_entityLabel).length > 0) { //JavaScript arrays and objects are compared by reference and not by value, which is why we do not use include.\r\n\t\t\t\tif (entityLabelsInRow.includes(selected_entityLabel)) { // JG: I believe we can still use 'includes()', because we are looking up a primitive value(number) in an array.\r\n\t\t\t\t\tfound_rowNumbersToHighlight.push(rowNr);\t\t\t\r\n\t\t\t\t\tfound = true;\r\n\t\t\t\t} \r\n\t\t\t}\r\n\t\t\tif (!found) {\r\n\t\t\t\tconsole.warn('NB, sel entityLabel %d not found in any of the row entityLabels', selected_entityLabel);\r\n\t\t\t\tworkHarder_entityLabels.push(selected_entityLabel);\r\n\t\t\t} // NB, in RelAggr mainPart->subParts, partsCount>0, this does not work right?\r\n\t\t\t/*\r\n\t\t\tHmm, maybe we could even use this as a trigger!\r\n\t\t\tSo, all entityLabels we find nothing for, we put into a 'workHarder' list..\r\n\t\t\t*/\r\n\t\t\t// for partsCount>0, maybe we should look in a different data map? Where is the other map, used when we go in the opposite direction?\r\n\t\t}\t\t\r\n\r\n\t\tif (workHarder_entityLabels.length>0) {\r\n\t\t\tawait this.thenWorkHarder(workHarder_entityLabels, found_rowNumbersToHighlight);\r\n\t\t}\r\n\r\n\t\tthis.selectionService.selectFromViewer(found_rowNumbersToHighlight);\r\n\t}\r\n\r\n\tasync thenWorkHarder(workHarder_entityLabels: number[], found_rowNumbersToHighlight: number[]):Promise {\r\n\t\tconsole.warn('here, you should work harder:', workHarder_entityLabels);\r\n\t\t// Can we fetch them all at once, or should we process them one at a time?\r\n\r\n\t\t//let _entityLabelsPerRow = await this.goFetchChildren(workHarder_entityLabels); //entityLabelsPerRow);\r\n\t\t//console.log('%c thenWorkHarder-goFetchChildren()-result: #%d (_entityLabelsPerRow[]/objectIDs)', 'color: red', _entityLabelsPerRow.length, _entityLabelsPerRow);\r\n\r\n\r\n\t\tconst map_entityLabels2instIDs:MyFancyMap = await this.getFromServer_GetAjourInstIDs_ForSub_EntityLabels(workHarder_entityLabels);\r\n\t\t// now that we know both main and sub part, we can look up the main entityLabel in our this.entityLabelsPerRow.\r\n\r\n\t\tlet rowsToInclude:number[] = [];\r\n\r\n\t\t// maybe it is better to iterate through Object.Values.\r\n\t\tvar keys:number[] = Object.keys(map_entityLabels2instIDs);\r\n\t\tfor (const sub_entityLabel of keys) { //map_entityLabels2instIDs) {\r\n\t\t\tvar dto:MainSubPartsDTO = map_entityLabels2instIDs[sub_entityLabel];\r\n\t\t\tvar main_entityLabel2 = dto.main_EntityLabel;\r\n\r\n\t\t\tlet found:boolean = false;\r\n\t\t\tfor (let rowIx = 0; rowIx < this.entityLabelsPerRow.length;++rowIx) {\r\n\t\t\t\tif (this.entityLabelsPerRow[rowIx].includes(main_entityLabel2)) {\r\n\t\t\t\t\trowsToInclude.push(rowIx); found = true;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tif (!found) {\r\n\t\t\t\tconsole.warn('never found mainEntityLabel', main_entityLabel2, 'for dto:', dto);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tconsole.log('will extend found_rowNumbersToHighlight with #', rowsToInclude.length, rowsToInclude);\r\n\t\tfound_rowNumbersToHighlight.push(...rowsToInclude);\t\r\n\r\n\t\t// todo - now that we know the instIDs for our sub entitylabels (relAggr),\r\n\t\t// we must somehow map them back to entityLabels.\r\n\t\t// Presumably we somewhere have a map of which instIDs go in which rows?\r\n\t\t//objectIDs.objectIds = _entityLabelsPerRow;\r\n\t\t//found_rowNumbersToHighlight.push(..._entityLabelsPerRow);\t\r\n\t}\r\n\r\n\tunbind(): void {\r\n\t\tthis.subscriptions.forEach(subscription => subscription.dispose());\r\n\t}\r\n\r\n\tsetupResizing() {\r\n\t\t//Code inspiration from: https://htmldom.dev/resize-columns-of-a-table/\r\n\t\t// Query the table\r\n\t\tconst table = document.getElementById('resizable');\r\n\r\n\t\t// Query all headers\r\n\t\tconst cols: NodeListOf | undefined = table?.querySelectorAll('th');\r\n\r\n\t\t// Loop over them\r\n\t\tcols?.forEach(col => {\r\n\t\t\tif (col == cols[cols.length - 1]) { //We do not want to set a resizer on last column.\r\n\t\t\t\treturn;\r\n\t\t\t};\r\n\r\n\t\t\t// Create a resizer element\r\n\t\t\tconst resizer = document.createElement('div');\r\n\t\t\tresizer.classList.add('resizer');\r\n\r\n\t\t\t// Set the height\r\n\t\t\tresizer.style.height = `${table?.offsetHeight}px`;\r\n\r\n\t\t\t// Add a resizer element to the column\r\n\t\t\tcol.appendChild(resizer);\r\n\r\n\t\t\t// Will be implemented in the next section\r\n\t\t\tthis.createResizableColumn(col, resizer);\r\n\t\t});\r\n\t}\r\n\r\n\tcreateResizableColumn(col: HTMLTableCellElement, resizer: HTMLDivElement) {\r\n\t\t// Track the current position of mouse\r\n\t\tlet x = 0;\r\n\t\tlet w = 0;\r\n\r\n\t\tconst mouseDownHandler = function (e: any) {\r\n\t\t\t// Get the current mouse position\r\n\t\t\tx = e.clientX;\r\n\r\n\t\t\t// Calculate the current width of column\r\n\t\t\tconst styles = window.getComputedStyle(col);\r\n\t\t\tw = parseInt(styles.width, 10);\r\n\r\n\t\t\t// Attach listeners for document's events\r\n\t\t\tdocument.addEventListener('mousemove', mouseMoveHandler);\r\n\t\t\tdocument.addEventListener('mouseup', mouseUpHandler);\r\n\t\t};\r\n\r\n\t\tconst mouseMoveHandler = function (e: any) {\r\n\t\t\t// Determine how far the mouse has been moved\r\n\t\t\tconst dx = e.clientX - x;\r\n\r\n\t\t\t// Update the width of column\r\n\t\t\tlet newWidth: number = w + dx;\r\n\t\t\tcol.style.minWidth = `${newWidth}px`; //Both has to be set.\r\n\t\t\tcol.style.width = `${newWidth}px`; //Both has to be set.\r\n\t\t};\r\n\r\n\t\t// When user releases the mouse, remove the existing event listeners\r\n\t\tconst mouseUpHandler = function () {\r\n\t\t\tdocument.removeEventListener('mousemove', mouseMoveHandler);\r\n\t\t\tdocument.removeEventListener('mouseup', mouseUpHandler);\r\n\t\t};\r\n\r\n\t\tresizer.addEventListener('mousedown', mouseDownHandler);\r\n\t}\r\n\r\n\tasync initialiseGrid() {\r\n\t\tthis.runAnim(async () => {\r\n\t\t\tthis.selectionService.gridData = [];\r\n\t\t\tlet pivotData: PivotDto | undefined = await this.getPivotData();\r\n\r\n\t\t\t// console.log(\"This is the grid data we have to work with \", pivotData);\r\n\t\t\t//for (let xx of pivotData) { console.log(xx); }\r\n\r\n\t\t\tif (pivotData != undefined) {\r\n\t\t\t\t//if (pivotData.length > 0) {\r\n\t\t\t\t//\tthis.headers_ = pivotData[0];\r\n\t\t\t\t//\tpivotData.shift(); // first row, which we put into headers instead.\r\n\t\t\t\t//\tconsole.log(\"HEADERS \" , this.headers_);\r\n\t\t\t\t//}\r\n\r\n\t\t\t\tthis.headers = this.headerDTO.map(\r\n\t\t\t\t\t(c: ColInfo): ColInf2 => {\r\n\t\t\t\t\t\tlet unit = c.unit.unitShort;\r\n\t\t\t\t\t\tlet unit_s = unit ? `(${unit})` : '';\r\n\t\t\t\t\t\treturn {\r\n\t\t\t\t\t\t\tunit: unit_s, //c.unit.unitShort, \r\n\t\t\t\t\t\t\tname: c.headerLabel // c.properColumnName\r\n\t\t\t\t\t\t};\r\n\t\t\t\t\t});\r\n\t\t\t\tthis.selectionService.gridData = pivotData.rounded;\r\n\t\t\t}\r\n\t\t});\r\n\t\t//console.log('headers2:', this.headers);\r\n\t}\r\n\r\n\tasync getPivotData(): Promise {\r\n\t\tlet dto: PivotIF = await this.getFromServer();\r\n\t\tif (!dto) { console.warn('getPivotData got empty PivotIF.'); return; }\r\n\r\n\t\tthis.filterHasSources = dto.hasSources; //console.log(\"DTO.has-Sources \" , dto.has-Sources);\r\n\t\tthis.entityLabelsPerRow = dto.entityLabelsPerRow; //console.log(\"working? \" , this.entityLabelsPerRow);//getPivotData\r\n\r\n\t\tif (!dto.revitElmIdsPerRow) {\r\n\t\t\tconsole.warn(\"PivotIF.revitElmIdsPerRow was empty:\", dto.revitElmIdsPerRow);//getPivotData\t\r\n\t\t} else {\r\n\t\t\tthis.revitElmIdsPerRow = dto.revitElmIdsPerRow;\r\n\t\t\t//console.log(\"revitElmIdsPerRow? \" , this.entityLabelsPerRow);//getPivotData\t\r\n\t\t}\r\n\r\n\t\tif (this.showIds) { this.decorateIfcIDs(dto); }\r\n\r\n\t\tthis.headerDTO = dto.pivot.cols; //console.log('headerDTO:', this.headerDTO);\r\n\t\treturn dto.pivot; //.precise; //grid;\r\n\t}\r\n\r\n\tdecorateIfcIDs(dto: PivotIF) {\r\n\t\tconst unit1: DetailedParameterDefinitionDto = { unitShort: '' }\r\n\t\tconst label: ColInfo = { headerLabel: 'XP-ID', properColumnName: '1', unit: unit1 };\r\n\t\tconst revit: ColInfo = { headerLabel: 'RevitId', properColumnName: '2', unit: unit1 };\r\n\t\tdto.pivot.cols.push(label);\r\n\t\tdto.pivot.cols.push(revit);\r\n\t\tconst len = dto.pivot.rounded.length;\r\n\t\tfor (let i = 0; i < len; ++i) {\r\n\t\t\tdto.pivot.rounded[i].push(dto.entityLabelsPerRow[i]);\r\n\t\t\tdto.pivot.rounded[i].push(dto.revitElmIdsPerRow[i]);\r\n\t\t}\r\n\t}\r\n\r\n\tasync getFromServer(): Promise {\r\n\t\tconsole.log('%cgetFromServer()->useFunction=', 'color: darkseagreen', this.useFunction);\r\n\t\tif (this.useFunction) {\r\n\t\t\treturn await this.getFromServer_func();\r\n\t\t} else {\r\n\t\t\tlet pivotIF: PivotIF = await this.getFromServer_AspnetAction();\r\n\t\t\tconsole.log('%cinst ids', 'color: darkseagreen', pivotIF.pivot.instIDs);\r\n\t\t\tthis.childrenCount = await this.getFromServer_GetProjectInstancesChildrenCount(pivotIF.pivot.instIDs.flat());\r\n\t\t\tconsole.log('%cgetFromServer_GetChildrenCountForInstances=%d', 'color: darkseagreen', this.childrenCount);\r\n\t\t\t// if any kids were found instances \r\n\t\t\tif (this.childrenCount > 0) {\r\n\t\t\t\t// build map between instances and entitylabels\r\n\t\t\t\tthis.buildEntityLabelToAjourInstIdMap(pivotIF.pivot.instIDs.flat(), pivotIF.entityLabelsPerRow.flat());\r\n\t\t\t\tconsole.table(this.entityLabelToAjourInstIdMap);\r\n\t\t\t}\r\n\t\t\treturn pivotIF;\r\n\t\t}\r\n\t}\r\n\r\n\tprivate buildEntityLabelToAjourInstIdMap(instIds: string[], entityLabels: number[]): void {\r\n\t\tif (instIds.length === entityLabels.length) {\r\n\t\t\tfor (let i = 0; i < instIds.length; i++) {\r\n\t\t\t\tthis.entityLabelToAjourInstIdMap.set(entityLabels[i], instIds[i]);\r\n\t\t\t}\r\n\t\t\tconsole.log('buildEntityLabelToAjourInstIdMap', this.entityLabelToAjourInstIdMap)\r\n\t\t} else {\r\n\t\t\tconsole.warn('buildEntityLabelToAjourInstIdMap, both array whould be of same size', instIds.length, entityLabels.length);\r\n\t\t}\r\n\t}\r\n\r\n\tasync getFromServer_AspnetAction(): Promise {\r\n\t\tconsole.log('getFromServer_AspnetAction');\r\n\t\tlet path = `QuantityFilter/RenderPivotGrid/${this.quantityFilterId}/${this.shouldRoundNumbers}`;\r\n\t\tlet dto: PivotIF = await this.api().get(path).jsonResult();\r\n\t\tconsole.log('B we got back:', dto);\r\n\t\treturn dto;\r\n\t}\r\n\r\n\tasync getFromServer_GetProjectInstancesChildren(instanceIds: string[]): Promise {\r\n\t\tlet response: PrjInstanceIFCPartDto[] = await this.api().postJson(\"QuantityFilter/GetProjectInstancesChildren\", instanceIds).jsonResult();\r\n\t\tconsole.log('%cgetFromServer_GetProjectInstancesChildren got back:', 'color: turquoise', response);\r\n\t\tif (response !== undefined) {\r\n\t\t\treturn response;\r\n\t\t}\r\n\t\treturn [];\r\n\t}\r\n\r\n\tasync getFromServer_GetProjectInstancesChildrenCount(instanceIds: string[]): Promise {\r\n\t\tlet response: PrjInstancesCountDto = await this.api().postJson(\"QuantityFilter/GetProjectInstancesChildrenCount\", instanceIds).jsonResult();\r\n\t\tconsole.log('%cgetFromServer_GetChildrenCountForInstances got back:', 'color: turquoise', response);\r\n\t\tif (response !== undefined) {\r\n\t\t\treturn response.childrenCount;\r\n\t\t}\r\n\t\treturn -1;\r\n\t}\r\n\r\n\tasync getFromServer_GetAjourInstIDs_ForSub_EntityLabels(entityLabels: number[]): Promise {\r\n\t\t//PrjInstanceIFCPartDto[]\r\n\t\t// It will be some kind of map..?\r\n\t\t// long->MainSubPartsDTO, C:\\dev\\aoc\\AO.Commands\\Project\\IProjectInstanceService.cs\r\n\r\n\t\t//MyFancyMap\r\n\t\tlet map_entityLabels2instIDs: MyFancyMap = await this.api().postJson(\"QuantityFilter/GetAjourInstIDs_ForSub_EntityLabels\", entityLabels).jsonResult();\r\n\t\tconsole.log('%cgetFromServer_GetAjourInstIDs_ForSub_EntityLabels got back:', 'color: turquoise', map_entityLabels2instIDs);\r\n\t\tif (map_entityLabels2instIDs !== undefined) {\r\n\t\t\treturn map_entityLabels2instIDs;\r\n\t\t}\r\n\t\treturn {}; //[];\r\n\t}\r\n\r\n\r\n\r\n\r\n\r\n\tasync getFromServer_func(): Promise {\r\n\t\t// \"PrjId\": Meget maerkeligt - hvis jeg laver en af json keys om til streng,\r\n\t\t// ka azure-functions godt modtage det??\r\n\t\tlet args = {\r\n\t\t\t\"PrjId\": this.appContext.projectId,\r\n\t\t\tUserId: this.appContext.currentUser?.id,\r\n\t\t\tFilterId: this.quantityFilterId,\r\n\t\t\tRound: this.shouldRoundNumbers\r\n\t\t};\r\n\t\t// https://blog.bredvid.no/patterns-for-securing-your-azure-functions-2fef634f4020\r\n\t\tconsole.log('getFromServer_func, inputArgs:', args);\r\n\t\tlet path = '/api/QuantityFunc'; //?code=_1k2Wka7rVYnJB7zgQNjWcSzqVhnRMWHYLYpYI-lKrECAzFuOTxh0w=='; // todo, vi skal ha fundet en bedre maade.\r\n\t\tlet api = this.azf_Client.custom();\r\n\r\n\t\t(api.request.headers)[\"x-functions-key\"] = \"_1k2Wka7rVYnJB7zgQNjWcSzqVhnRMWHYLYpYI-lKrECAzFuOTxh0w==\";\r\n\r\n\t\tlet dto: PivotIF = await api.postJson(path, args).jsonResult();\r\n\t\tconsole.log('A PivotIF dto we got back:', dto);\r\n\t\treturn dto;\r\n\t\t/*\r\n\t\tFixed by settings CORS on Azure Function, OR defining a cors host policy in local settings (only works for local.)\r\n\t\t Cross-Origin Request Blocked: \r\n\t\t The Same Origin Policy disallows reading the remote resource \r\n\t\t at http://localhost:7155/api/QuantityFunc.\r\n\t\t (Reason: CORS request did not succeed). \r\n\t\t Status code: (null). */\r\n\t}\r\n\r\n\r\n\tasync showContextMenu(event: PointerEvent): Promise {\r\n\t\tlet entityLabelsOfSelected: number[] = this.getEntityLabelsFromSelected();\r\n\t\tlet rios: number[] = this.getRevitIDsFromSelected(); //revitIDsOfSelected\r\n\t\tconsole.log(\"entityLabels used selected rows\", entityLabelsOfSelected);\r\n\t\tlet actions: any[] = [\r\n\t\t\t{ viewKey: \"button\", setup: { textKey: \"Highlight selected\", actionKey: \"highlight\", service: { highlight: () => { this.sendEventToViewer(entityLabelsOfSelected, \"highlight\", rios) } } } },\r\n\t\t\t{ viewKey: \"button\", setup: { textKey: \"Hide selected\", actionKey: \"hide\", service: { hide: () => { this.sendEventToViewer(entityLabelsOfSelected, \"hide\", rios) } } } },\r\n\t\t\t{ viewKey: \"button\", setup: { textKey: \"Hide others\", actionKey: \"hideOthers\", service: { hideOthers: () => { this.sendEventToViewer(entityLabelsOfSelected, \"hideOthers\", rios) } } } },\r\n\t\t\t{ viewKey: \"button\", setup: { textKey: \"Make selected translucent\", actionKey: \"translucent\", service: { translucent: () => { this.sendEventToViewer(entityLabelsOfSelected, \"translucent\", rios) } } } },\r\n\t\t\t{ viewKey: \"button\", setup: { textKey: \"Isolate selected\", actionKey: \"isolate\", service: { isolate: () => { this.sendEventToViewer(entityLabelsOfSelected, \"isolate\", rios) } } } },\r\n\t\t\t{ viewKey: \"button\", setup: { textKey: \"Look at selected\", actionKey: \"lookAt\", service: { lookAt: () => { this.sendEventToViewer(entityLabelsOfSelected, \"lookAt\", rios) } } } }\r\n\t\t];\r\n\r\n\t\tthis.contextMenu.open(event, actions);\r\n\t}\r\n\r\n\tprivate getEntityLabelsFromSelected(): number[] {\r\n\t\tlet entityLabelsOfSelected: number[] = [];\r\n\t\tfor (let i = 0; i < this.selectionService.gridData.length; i++) {\r\n\t\t\tif ((this.selectionService.gridData[i]).isSelected) { //isSelected is a hack for handling selection.\r\n\t\t\t\tentityLabelsOfSelected.push(...this.entityLabelsPerRow[i]);//getEntityLabelsFromSelected\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn entityLabelsOfSelected;\r\n\t}\r\n\r\n\tprivate getRevitIDsFromSelected(): number[] {\r\n\t\tlet revitIDsOfSelected: number[] = [];\r\n\t\tfor (let i = 0; i < this.selectionService.gridData.length; i++) {\r\n\t\t\tif ((this.selectionService.gridData[i]).isSelected) { //isSelected is a hack for handling selection.\r\n\t\t\t\trevitIDsOfSelected.push(...this.revitElmIdsPerRow[i]);//getRevitIDsFromSelected\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn revitIDsOfSelected;\r\n\t}\r\n\r\n\tasync sendEventToViewer(entityLabelsPerRow: number[], eventToSend: string, revitIDsOfSelected: number[]): Promise {\r\n\t\tconsole.log('%csendEventToViewer: #%d %s', 'color: red', entityLabelsPerRow.length, eventToSend);\r\n\t\tif (entityLabelsPerRow.length == 0) { console.trace('sendEventToViewer called with empty entityLabelsPerRow'); }\r\n\r\n\t\tconst eventNAMEToSend = `viewer:${eventToSend}`;\r\n\t\tif (PivotGridComponent.showLog) { console.log('PivotGridComponent.sendEventToViewer, eventName:', eventNAMEToSend); }\r\n\t\tlet objectIDs: ObjectIDsIF = {\r\n\t\t\tobjectIds: entityLabelsPerRow,\r\n\t\t\trevitObjectIds: revitIDsOfSelected,\r\n\t\t};\r\n\t\tif (this.childrenCount > 0) {\r\n\t\t\tlet _entityLabelsPerRow = await this.goFetchChildren(entityLabelsPerRow);//sendEventToViewer\r\n\t\t\tconsole.log('%csendEventToViewer-goFetchChildren()-result: #%d (_entityLabelsPerRow[]/objectIDs)', 'color: red', _entityLabelsPerRow.length);\r\n\t\t\tobjectIDs.objectIds = _entityLabelsPerRow;\r\n\t\t}\r\n\t\tthis.events.publish(eventNAMEToSend, objectIDs);\r\n\t\t//this.events.publish(eventNAMEToSend, entityLabelsPerRow); //revitIDsOfSelected\r\n\t}\r\n\r\n\thasInstances(index: number): boolean {\r\n\t\treturn this.entityLabelsPerRow[index]?.length > 0;//hasInstances\r\n\t}\r\n\r\n\tinf(cell: string, index: number): any {\r\n\t\tif (!this) { console.warn('no this.'); return '-'; }\r\n\r\n\t\tif (!this.revitElmIdsPerRow) { console.warn('no revitElmIdsPerRow.'); return '/'; }\r\n\r\n\t\tlet value1: any = this.revitElmIdsPerRow[index];\r\n\t\tlet value2: any = this.entityLabelsPerRow[index];\r\n\t\tif (!value1 || value1.length == 0) { value1 = '(R)'; }\r\n\t\tif (!value2) { value2 = '(I)'; }\r\n\t\treturn `R:${value1}, I:${value2}`;\r\n\t}\r\n\r\n\tpublic static showLog: boolean = false;\r\n\r\n\tasync goFetchChildren(entityLabels: number[]): Promise {\r\n\t\t// can caller survive empty result return? \r\n\t\t/* \r\n\t\tgoFetchChildren works by \r\n\t\tfirst looking up the InstId for each MAIN_entityLabel in the [entityLabelToAjourInstIdMap].\r\n\r\n\t\tIt appears we are actually UPDATING the entityLabels input array IN SITU; I assume that is OK/doesn't eschew any index-offsets (relative to parallel tables).\r\n\t\tI think it is OK, because it's 'just' an event-arg-list-if-IDS-to-highlight.\r\n\t\t*/\r\n\r\n\t\t// let _entityLabels: number[] = [];\r\n\t\tlet instanceId2EntityLabelMap: Map = new Map();\r\n\t\tconsole.log('%cgoFetchChildren(# %d) - (entityLabels[])', 'color: cyan', entityLabels.length);\r\n\t\tif (entityLabels.length == 0) { console.warn('goFetchChildren - bailing out as entityLabels has zero elms'); return []; }\r\n\r\n\t\tlet ajourInstIds: string[] = [];\r\n\t\tfor (let i = 0; i < entityLabels.length; i++) {\r\n\t\t\tlet entityLabel: number = entityLabels[i];\r\n\t\t\tif (this.entityLabelToAjourInstIdMap.has(entityLabel)) {\r\n\t\t\t\tlet ajourInstId: string | undefined = this.entityLabelToAjourInstIdMap.get(entityLabel);\r\n\t\t\t\tconsole.log('%cthis.entityLabelToAjourInstIdMap.get(entityLabel:%d)->result=%s', 'color: cyan', entityLabel, ajourInstId);\r\n\t\t\t\tif (ajourInstId !== undefined) {\r\n\t\t\t\t\tajourInstIds.push(ajourInstId);\r\n\t\t\t\t\t// map between project instance id and entity label: We need it later to exchange an entity label with the an collection of entity label children\r\n\t\t\t\t\tinstanceId2EntityLabelMap.set(ajourInstId, entityLabel);\r\n\t\t\t\t} else {\r\n\t\t\t\t\tconsole.warn('empty ajourInstId for entityLabel in entityLabelToAjourInstIdMap:', entityLabel);\r\n\t\t\t\t}\r\n\t\t\t} else {\r\n\t\t\t\tconsole.warn('entityLabel not in entityLabelToAjourInstIdMap:', entityLabel, this.entityLabelToAjourInstIdMap);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tif (ajourInstIds.length == 0) { console.warn('goFetchChildren - bailing out as ajourInstIds has zero elms'); return []; }\r\n\r\n\t\t// go get me some data\r\n\t\tlet projectInstancesIfcPartsDto = await this.getFromServer_GetProjectInstancesChildren(ajourInstIds);\r\n\t\tconsole.log('%cgoFetchChildren-getFromServer_GetProjectInstancesChildren->result = #%d', 'color: cyan', projectInstancesIfcPartsDto.length);\r\n\r\n\t\tif (projectInstancesIfcPartsDto.length > 0) {\r\n\t\t\tprojectInstancesIfcPartsDto.forEach( partsDTO => {\r\n\t\t\t\t// what the origin entity label\r\n\t\t\t\tlet originEntityLabel = instanceId2EntityLabelMap.get(partsDTO.prjInstanceId);\r\n\t\t\t\tif (originEntityLabel !== undefined) {\r\n\t\t\t\t\t// find inde of origin entity label\r\n\t\t\t\t\tconst index = entityLabels.indexOf(originEntityLabel);\r\n\t\t\t\t\tif (index !== -1) {\r\n\t\t\t\t\t\t// remove the origin entity label from collection of entity labels\r\n\t\t\t\t\t\tentityLabels.splice(index, 1);\r\n\t\t\t\t\t\t// add found entity label children to collection of entity labels\r\n\t\t\t\t\t\tentityLabels = entityLabels.concat(partsDTO.entityLabels); // possibly this would be better as push(...x).\r\n\t\t\t\t\t}\r\n\t\t\t\t} else {\r\n\t\t\t\t\tconsole.warn(\"Origin entity label wasn't found for project instance id:\", partsDTO.prjInstanceId);\r\n\t\t\t\t}\r\n\t\t\t});\r\n\r\n\t\t}\r\n\t\treturn entityLabels;\r\n\t}\r\n\r\n\tasync selectAndView(index: number, event: PointerEvent): Promise {\r\n\t\tconsole.log('%cselectAndView-index: %d', 'color: green', index);\r\n\r\n\t\tif (PivotGridComponent.showLog) { console.log('PivotGridComponent.selectAndView'); }\r\n\t\tthis.selectionService.handleSelect(index, event);\r\n\r\n\t\tlet rios: number[] = this.getRevitIDsFromSelected(); //revitIDsOfSelected\r\n\r\n\t\tlet entityLabelsOfSelected: number[] = this.getEntityLabelsFromSelected();\r\n\t\tconsole.log('%cselectAndView-getEntityLabelsFromSelected(): #%d', 'color: green', entityLabelsOfSelected.length);\r\n\t\t// look after... \r\n\t\tthis.sendEventToViewer(entityLabelsOfSelected, 'highlight', rios); // ie viewer:highlight\r\n\t}\r\n\r\n\tasync QFChanged(qf: QuantityFilterDto) {\r\n\t\tthis.QF = qf;\r\n\t\tif (this.QF) {\r\n\t\t\tthis.quantityFilterId = qf.id;\r\n\t\t\tthis.quantityFilterName = qf.name;\r\n\t\t\t//console.log(\"SELECTED \" , this.QF);\r\n\t\t\tawait this.initialiseGrid();\r\n\t\t} else {\r\n\t\t\tthis.selectionService.gridData = [];\r\n\t\t\tthis.selectionService.lastSelectedRow = -1;\r\n\t\t\tthis.headers = [];\r\n\t\t\tthis.filterHasSources = false;\r\n\r\n\t\t}\r\n\t}\r\n\r\n\t// shouldRoundNumbers_Changed - we cannot rename it, as it maps magically?\r\n\tasync shouldRoundNumbersChanged(): Promise { if (this.QF) { await this.initialiseGrid(); } }\r\n\r\n\tasync useFunctionChanged(): Promise {\r\n\t\t//console.log('useFunctionChanged', this.useFunction);\r\n\t\tif (this.QF) { await this.initialiseGrid(); }\r\n\t}\r\n\r\n\tasync showIdsChanged(): Promise {\r\n\t\tconsole.log('showIdsChanged', this.showIds);\r\n\t\tif (this.QF) { await this.initialiseGrid(); }\r\n\t}\r\n\r\n\tpublic async hasOrgSelected(): Promise {\r\n\t\tlet ots = this.orgTokenService;\r\n\t\tlet hasOrgSelected: boolean = ots.hasOrgSelected();\r\n\t\tconsole.log('hasOrgSelected C?', hasOrgSelected, ots.orgId());\r\n\t\tif (!hasOrgSelected) {\r\n\t\t\tawait this.modal.OpenMessage('Information', 'Please select a current organisation');\r\n\t\t\treturn false;\r\n\t\t}\r\n\t\treturn true;\r\n\t}\r\n\r\n\tasync exportToExcel(): Promise {\r\n\t\tconsole.log('exportToExcel A C:/dev/aoc/AO.Client/ClientApp/src/app/components/pivot-grid-component/pivot-grid-component.ts');\r\n\t\tif (!await this.hasOrgSelected()) { return; }\r\n\r\n\t\t// todo, it should probably be the RAW result?\r\n\t\tthis.api().get(`QuantityFilter/RenderQuantityPivot/${this.quantityFilterId}`)\r\n\t\t\t.handleError(this.i18n.tr('Error_Title_Generic'), this.i18n.tr('Error_Message_Generic'))\r\n\t\t\t.downloadFile(`${this.quantityFilterName}.xlsx`);\r\n\t}\r\n\r\n\tasync back(): Promise {\r\n\t\tconsole.log('back A');\r\n\t\tlet path = `QuantityFilter/GetQuantityViewIdByFilterId?quantityFilterId=${this.quantityFilterId}`;\r\n\t\tif (!this.quantityFilterId) { return; }\r\n\t\tlet QV_Ids: string | undefined = await this.api().get(path).jsonResult();\r\n\t\tif (QV_Ids) {\r\n\t\t\tthis.router.navigateToRoute('project/quantity-filters', { projectId: this.appContext.getSelectedProject()?.id, QV_Id: QV_Ids }, { replace: true });\r\n\t\t}\r\n\t}\r\n\r\n\tasync updateColumns(): Promise {\r\n\t\tif (this.quantityColumnOpen == false) {\r\n\t\t\tthis.quantityColumnOpen = true;\r\n\t\t\tawait this.modal.OpenModal(QuantityFilterColumns, this.QF).whenClosed(result => {\r\n\t\t\t\tif (result.wasCancelled != true) {\r\n\t\t\t\t\tthis.initialiseGrid();\r\n\t\t\t\t}\r\n\t\t\t\tthis.quantityColumnOpen = false;\r\n\t\t\t});\r\n\t\t}\r\n\t}\r\n\r\n\r\n\tisNumber(\r\n\t\tcell: string | null\r\n\t\t// columnIndex: number // JG: Jeg ved ikke hvad dette er, men det er ikke andre steder i koden..\r\n\t): boolean {\r\n\t\tif (cell == null) { return false; }\r\n\t\t// if (this.shouldBeRightAligned(columnIndex)) { return false; }\r\n\t\t//console.log(\"Get column index \" , columnIndex);\r\n\r\n\t\t// console.log(\"headers \" , this.headers); // JG: Det er egentlig en masse log, vi ikke behøver pt.\r\n\r\n\t\tlet isFloat = /^\\d+\\.\\d+$/.test(cell); //IsFloat // JG: hvis vi ønsker at gøre dette, ku vi ikke bare lave en parseFloat istedet, som nok har bedre performance? (ville skulle tænkes igennem, om det gør det samme.)\r\n\t\treturn isFloat ? isFloat : /^\\d+$/.test(cell); //IsInt\r\n\t}\r\n\r\n\t// shouldBeRightAligned(columnIndex: number) {\r\n\t// \tif (this.headers[columnIndex].name.toLocaleLowerCase().includes('num')) { return true; }\r\n\t// \tif (this.headers[columnIndex].name.toLocaleLowerCase().includes('ajourcount')) { return true; }\r\n\t// \treturn false;\r\n\t// }\r\n\r\n\t// getIntegrals(cell: string): string {\r\n\t// \tif (!cell.includes(\".\")) { return cell }\r\n\t// \tlet integralValue: string = cell.substring(0, cell.indexOf(\".\"));\r\n\t// \treturn integralValue;\r\n\t// }\r\n\r\n\t// getDecimals(cell: string): string {\r\n\t// \tif (!cell.includes(\".\")) { return \"0\" }\r\n\t// \tlet decimalValue: string = cell.slice(cell.indexOf('.') + 1);\r\n\t// \treturn decimalValue;\r\n\t// }\r\n\r\n\tasync runAnim(download: () => Promise) {\r\n\t\ttry {\r\n\t\t\tthis.animate(true);\r\n\t\t\t//console.log('before rendering the grid');\r\n\t\t\tawait download();\r\n\t\t} finally {\r\n\t\t\t//console.log('after rendering grid');\r\n\t\t\tthis.animate(false);\r\n\t\t\tthis.setupResizing();\r\n\t\t}\r\n\t}\r\n\r\n\tpublic anim: boolean = false; //true;\r\n\r\n\tanimate(on: boolean): void {\r\n\t\tthis.anim = on;\r\n\t}\r\n}\r\n\r\n\r\ninterface PivotIF {\r\n\tpivot: PivotDto,\r\n\tentityLabelsPerRow: number[][],\r\n\trevitElmIdsPerRow: number[][],\r\n\thasSources: boolean\r\n}\r\n\r\ninterface PrjInstancesCountDto {\r\n\tchildrenCount: number\r\n}\r\n\r\ninterface PrjInstanceIFCPartDto {\r\n\tprjInstanceId: string;\r\n\tentityLabels: number[];\r\n\trevitElmIDs: string[];\r\n}\r\n\r\ninterface MainSubPartsDTO { // C:\\dev\\aoc\\AO.Commands\\Project\\IProjectInstanceService.cs\r\n\tsub_EntityLabel:number;\r\n\tmain_EntityLabel:number;\r\n\tprjInstance_Id:string;\r\n}\r\n\r\ntype MyFancyMap = {\t[key: number]: MainSubPartsDTO; };\r\n"],"names":["___CSS_LOADER_EXPORT___","push","module","id","PivotGridComponent","constructor","apiClient","azf_Client","appContext","i18n","router","events","modal","contextMenu","selectionService","orgTokenService","quantityFilterId","quantityFilterName","subscriptions","headerDTO","headers","entityLabelsPerRow","revitElmIdsPerRow","filterHasSources","quantityColumnOpen","childrenCount","anim","console","log","this","entityLabelToAjourInstIdMap","Map","api","project","projectId","bind","subscribe","initialiseGrid","async","response","highlightMatchingRows","objectIds","selectedObjectIds","workHarder_entityLabels","found_rowNumbersToHighlight","selected_entityLabel","found","rowNr","length","includes","warn","thenWorkHarder","selectFromViewer","map_entityLabels2instIDs","getFromServer_GetAjourInstIDs_ForSub_EntityLabels","rowsToInclude","keys","Object","sub_entityLabel","dto","main_entityLabel2","main_EntityLabel","rowIx","unbind","forEach","subscription","dispose","setupResizing","table","document","getElementById","cols","querySelectorAll","col","resizer","createElement","classList","add","style","height","offsetHeight","appendChild","createResizableColumn","x","w","mouseMoveHandler","e","dx","clientX","newWidth","minWidth","width","mouseUpHandler","removeEventListener","addEventListener","styles","window","getComputedStyle","parseInt","runAnim","gridData","pivotData","getPivotData","undefined","map","c","unit","unitShort","name","headerLabel","rounded","getFromServer","hasSources","showIds","decorateIfcIDs","pivot","unit1","label","properColumnName","revit","len","i","useFunction","getFromServer_func","pivotIF","getFromServer_AspnetAction","instIDs","getFromServer_GetProjectInstancesChildrenCount","flat","buildEntityLabelToAjourInstIdMap","instIds","entityLabels","set","path","shouldRoundNumbers","get","jsonResult","getFromServer_GetProjectInstancesChildren","instanceIds","postJson","args","UserId","currentUser","FilterId","Round","custom","request","showContextMenu","event","entityLabelsOfSelected","getEntityLabelsFromSelected","rios","getRevitIDsFromSelected","actions","viewKey","setup","textKey","actionKey","service","highlight","sendEventToViewer","hide","hideOthers","translucent","isolate","lookAt","open","isSelected","revitIDsOfSelected","eventToSend","trace","eventNAMEToSend","showLog","objectIDs","revitObjectIds","_entityLabelsPerRow","goFetchChildren","publish","hasInstances","index","inf","cell","value1","value2","instanceId2EntityLabelMap","ajourInstIds","entityLabel","has","ajourInstId","projectInstancesIfcPartsDto","partsDTO","originEntityLabel","prjInstanceId","indexOf","splice","concat","selectAndView","handleSelect","QFChanged","qf","QF","lastSelectedRow","shouldRoundNumbersChanged","useFunctionChanged","showIdsChanged","hasOrgSelected","ots","orgId","OpenMessage","exportToExcel","handleError","tr","downloadFile","back","QV_Ids","navigateToRoute","getSelectedProject","QV_Id","replace","updateColumns","OpenModal","whenClosed","result","wasCancelled","isNumber","test","download","animate","on","bindable","autoinject","I18N","EventAggregator"],"sourceRoot":""}