1/*
2 * Copyright (C) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15import {TabPaneApSummaryHtml} from './TabPaneApSummary.html.js';
16
17export class TabPaneApSummary extends HTMLElement {
18    static jsonData = null;
19    constructor() {
20        super();
21        this.apDataSource = [];
22        this.apData = [];
23        this.apTreeNodes = [];
24        this.expandedNodeList = new Set();
25        this.selectTreeDepth = 0;
26        fetch('/ap/config.json')
27            .then(response => response.json())
28            .then(data => {
29                TabPaneApSummary.jsonData = data;
30                this.expansionClickEvent = () => {
31                    let apItem;
32                    this.expandedNodeList.clear();
33                    if (((apItem = this.expansionUpIcon) === null || apItem === void 0 ? void 0 : apItem.name) === 'down') {
34                        this.selectTreeDepth = 0;
35                        this.expansionUpIcon.name = 'up';
36                        this.expansionDownIcon.name = 'down';
37                    } else {
38                        this.selectTreeDepth = TabPaneApSummary.jsonData.summaryTreeLevel.length;
39                        this.expansionUpIcon.name = 'down';
40                        this.expansionDownIcon.name = 'up';
41                    }
42                    this.refreshSelectDepth(this.apTreeNodes);
43                    this.refreshRowNodeTable(true);
44                };
45                this.attachShadow({mode: 'open'}).innerHTML = this.initHtml();
46                this.initElements();
47            });
48    }
49
50    set data(apDatas) {
51        this.apDataSource = [];
52        this.expandedNodeList.clear();
53        this.expansionUpIcon.name = 'up';
54        this.expansionDownIcon.name = 'down';
55        this.apSummaryTable.innerHTML = '';
56        this.apDataSource = apDatas;
57        this.apData = this.apDataSource;
58        if (this.apData.length !== 0) {
59            this.refreshRowNodeTable();
60        }
61    }
62
63    filterData(filter) {
64        const keysOfApBean = TabPaneApSummary.jsonData.filterKeys;
65        if (filter === '') {
66            this.apData = this.apDataSource;
67        } else {
68            this.apData = this.apDataSource.filter((item) => {
69                return keysOfApBean.some(field => {
70                    const value = item[field];
71                    return value !== undefined && value !== null && value.toString().toLowerCase().includes(filter);
72                });
73            });
74        }
75        this.expandedNodeList.clear();
76        this.expansionUpIcon.name = 'up';
77        this.expansionDownIcon.name = 'down';
78        this.apSummaryTable.innerHTML = '';
79        this.refreshRowNodeTable();
80    }
81
82    initElements() {
83        this.apSummaryTable = this.shadowRoot?.querySelector('#tab-summary');
84        this.expansionDiv = this.shadowRoot?.querySelector('.expansion-div');
85        this.expansionUpIcon = this.shadowRoot?.querySelector('.expansion-up-icon');
86        this.expansionDownIcon = this.shadowRoot?.querySelector('.expansion-down-icon');
87        let summaryTreeLevel = TabPaneApSummary.jsonData.summaryTreeLevel;
88        this.shadowRoot?.querySelectorAll('.head-label').forEach((summaryTreeHead) => {
89            summaryTreeHead.addEventListener('click', () => {
90                this.selectTreeDepth = summaryTreeLevel.indexOf(summaryTreeHead.textContent);
91                this.expandedNodeList.clear();
92                this.refreshSelectDepth(this.apTreeNodes);
93                this.refreshRowNodeTable(true);
94            });
95        });
96        this.apSummaryTable.onscroll = () => {
97            let apTreeTableEl = this.shadowRoot?.querySelector('.ap-tree-table');
98            if (apTreeTableEl) {
99                apTreeTableEl.scrollTop = (this.apSummaryTable?.scrollTop) || 0;
100            }
101        };
102    }
103
104    expansionAll() {
105        this.selectTreeDepth = TabPaneApSummary.jsonData.summaryTreeLevel.length - 1;
106        this.expandedNodeList.clear();
107        this.refreshSelectDepth(this.apTreeNodes);
108        this.refreshRowNodeTable(true);
109    }
110
111    initHtml() {
112        return TabPaneApSummaryHtml;
113    }
114
115    connectedCallback() {
116        new ResizeObserver(() => {
117            this.parentElement.style.overflow = 'hidden';
118            this.refreshRowNodeTable();
119        }).observe(this.parentElement);
120        this.expansionDiv?.addEventListener('click', this.expansionClickEvent);
121    }
122
123    disconnectedCallback() {
124        this.expansionDiv?.removeEventListener('click', this.expansionClickEvent);
125    }
126
127    refreshSelectDepth(apTreeNodes) {
128        apTreeNodes.forEach((item) => {
129            if (item.depth < this.selectTreeDepth) {
130                this.expandedNodeList.add(item.id);
131                if (item.children.length > 0) {
132                    this.refreshSelectDepth(item.children);
133                }
134            }
135        });
136    }
137
138    createTr(rowNode, className, proper) {
139        let elementTr = document.createElement('tr');
140        let elementTd = document.createElement('td');
141        elementTr.title = rowNode[proper];
142        elementTd.textContent = rowNode[proper];
143        elementTd.className = className;
144        elementTr.appendChild(elementTd);
145        return elementTr;
146    }
147
148    createRowNodeTableEL(rowNodeList, tableTreeEl, tableRightDivs) {
149        let unitPadding = 20;
150        let leftPadding = 5;
151        rowNodeList.forEach((rowNode) => {
152            let tableTreeRowEl = document.createElement('tr');
153            tableTreeRowEl.className = 'tree-row-tr';
154            tableTreeRowEl.title = `${rowNode.apName}`;
155            let leftSpacingEl = document.createElement('td');
156            leftSpacingEl.style.paddingLeft = `${rowNode.depth * unitPadding + leftPadding}px`;
157            tableTreeRowEl.appendChild(leftSpacingEl);
158            this.addToggleIconEl(rowNode, tableTreeRowEl);
159            let rowNodeTextEL = document.createElement('td');
160            rowNodeTextEL.textContent = `${rowNode.apName}`;
161            rowNodeTextEL.className = 'row-name-td';
162            tableTreeRowEl.appendChild(rowNodeTextEL);
163            tableTreeEl.appendChild(tableTreeRowEl);
164            let apTrs = TabPaneApSummary.jsonData.apTableTr;
165            for (let index = 0; index < apTrs.length; index++) {
166                let item = apTrs[index];
167                Object.keys(item).forEach((key) => {
168                    tableRightDivs[index].appendChild(this.createTr(rowNode, key, item[key]));
169                });
170            }
171            if (rowNode.children && this.expandedNodeList.has(rowNode.id)) {
172                this.createRowNodeTableEL(rowNode.children, tableTreeEl, tableRightDivs);
173            }
174        });
175    }
176
177    addToggleIconEl(rowNode, tableRowEl) {
178        let toggleIconEl = document.createElement('td');
179        let expandIcon = document.createElement('lit-icon');
180        expandIcon.classList.add('tree-icon');
181        if (rowNode.children && rowNode.children.length > 0) {
182            toggleIconEl.appendChild(expandIcon);
183            expandIcon.name = this.expandedNodeList.has(rowNode.id) ? 'minus-square' : 'plus-square';
184            toggleIconEl.classList.add('expand-icon');
185            toggleIconEl.addEventListener('click', () => {
186                let scrollTop = this.apSummaryTable?.scrollTop ?? 0;
187                this.changeNode(rowNode.id);
188                this.apSummaryTable.scrollTop = scrollTop;
189                let apTreeTableEl = this.shadowRoot?.querySelector('.ap-tree-table');
190                if (apTreeTableEl) {
191                    apTreeTableEl.scrollTop = scrollTop;
192                }
193            });
194        }
195        tableRowEl.appendChild(toggleIconEl);
196    }
197
198    changeNode(currentNode) {
199        if (this.expandedNodeList.has(currentNode)) {
200            this.expandedNodeList.delete(currentNode);
201        } else {
202            this.expandedNodeList.add(currentNode);
203        }
204        this.refreshRowNodeTable();
205    }
206
207    refreshRowNodeTable(useCacheRefresh = false) {
208        if (this.apSummaryTable === undefined) {
209            return;
210        }
211        this.apSummaryTable.innerHTML = '';
212        if (this.apSummaryTable && this.parentElement) {
213            this.apSummaryTable.style.height = `${this.parentElement.clientHeight - 30}px`;
214        }
215        if (!useCacheRefresh) {
216            this.apTreeNodes = this.buildTreeTblNodes(this.apData);
217        }
218        let tableFragmentEl = document.createDocumentFragment();
219        let tableTreeEl = document.createElement('div');
220        tableTreeEl.className = 'ap-tree-table';
221        let apTrs = TabPaneApSummary.jsonData.apTableTr;
222        let tableRightDivs = [];
223        for (let index = 0; index < apTrs.length; index++) {
224            tableRightDivs.push(document.createElement('div'));
225        }
226        if (this.parentElement) {
227            tableTreeEl.style.height = `${this.parentElement.clientHeight - 40}px`;
228        }
229        this.createRowNodeTableEL(this.apTreeNodes, tableTreeEl, tableRightDivs);
230        let emptyTr = document.createElement('tr');
231        emptyTr.className = 'tree-row-tr';
232        tableTreeEl === null || tableTreeEl === void 0 ? void 0 : tableTreeEl.appendChild(emptyTr);
233        tableFragmentEl.appendChild(tableTreeEl);
234        for (let index = 0; index < tableRightDivs.length; index++) {
235            let emptyItemTr = document.createElement('tr');
236            emptyItemTr.className = 'tree-row-tr';
237            tableRightDivs[index].appendChild(emptyItemTr);
238            tableFragmentEl.appendChild(tableRightDivs[index]);
239        }
240        this.apSummaryTable.appendChild(tableFragmentEl);
241    }
242
243    buildTreeTblNodes(apTreeNodes) {
244        let id = 0;
245        let root = {
246            id: id,
247            depth: 0,
248            children: [],
249            apName: 'All',
250            type: '',
251            isRoot: '',
252            apKind: '',
253            apAbcId: '',
254            apId: ''
255        };
256        apTreeNodes.forEach((item) => {
257            id++;
258            let moduleNode = root.children.find((node) => node.apName === item.moduleName);
259            let result = this.buildItem(moduleNode, id, item, root);
260            id = result.id;
261            let offsetNode = result.offsetNode;
262            if (offsetNode.depth === 2) {
263                let typeNode;
264                id++;
265                typeNode = {
266                    id: id, depth: 3, children: [], apName: item.type, type: '',
267                    isRoot: `${item.isRoot}`,
268                    apKind: item.apKind === '-' ? '-' : TabPaneApSummary.KindMap[item.apKind === undefined ?
269                        'UnknowId' : item.apKind],
270                    apAbcId: `${item.apAbcId}`,
271                    apId: `${item.apId}`
272                };
273                offsetNode.children.push(typeNode);
274            }
275        });
276        return root.children.sort((leftData, rightData) => {
277            return leftData.apName.localeCompare(rightData.apName);
278        });
279    }
280
281    buildItem(moduleNode, id, item, root) {
282        if (!moduleNode) {
283            id++;
284            moduleNode = {
285                id: id, depth: 0,
286                children: [],
287                apName: item.moduleName,
288                type: '',
289                isRoot: '-',
290                apKind: '-',
291                apAbcId: '-',
292                apId: '-'
293            };
294            root.children.push(moduleNode);
295        }
296        let funcNode = moduleNode.children.find((node) => node.apName === item.funcName);
297        if (!funcNode) {
298            id++;
299            funcNode = {
300                id: id, depth: 1,
301                children: [],
302                apName: item.funcName,
303                type: '',
304                isRoot: '-',
305                apKind: '-',
306                apAbcId: '-',
307                apId: '-'
308            };
309            moduleNode.children.push(funcNode);
310        }
311        if (item.offset === '-') {
312            return {id: id, offsetNode: funcNode};
313        }
314        let offsetNode = funcNode.children.find((node) => node.apName === item.offset);
315        if (!offsetNode) {
316            id++;
317            offsetNode = {
318                id: id, depth: 2,
319                children: [],
320                apName: item.offset,
321                type: '',
322                isRoot: '-',
323                apKind: '-',
324                apAbcId: '-',
325                apId: '-'
326            };
327            funcNode.children.push(offsetNode);
328        }
329        return {id, offsetNode};
330    }
331
332    static MakeApObj(moduleName, functionName, item) {
333        if (item === undefined) {
334            item = new TypeItem();
335        }
336        let abcId = this.abcFilePoolMap.get(String(item.abcId));
337        if (abcId === undefined) {
338            abcId = '-';
339        }
340        let transformedItem = {
341            moduleName: moduleName,
342            funcName: functionName,
343            offset: (item === null || item === void 0 ? void 0 : item.typeOffset) === undefined ? '-' : item.typeOffset,
344            type: (item === null || item === void 0 ? void 0 : item.typeName) === undefined ? '-' : item.typeName,
345            apKind: (item === null || item === void 0 ? void 0 : item.kind) === undefined ? '-' : item.kind,
346            apAbcId: (item === null || item === void 0 ? void 0 : item.abcId) === undefined ? '-' : abcId,
347            apId: (item === null || item === void 0 ? void 0 : item.id) === undefined ? '-' : item.id,
348            isRoot: (item === null || item === void 0 ? void 0 : item.isRoot) === undefined ? '-' : item.isRoot
349        };
350        return transformedItem;
351    }
352
353    static TransformJson(value) {
354        let apRes = JSON.parse(value);
355        let apData = JSON.parse(apRes.data);
356        let jsonData = apData.recordDetail;
357        let abcFilePool = apData.abcFilePool;
358        this.abcFilePoolMap = new Map(abcFilePool.map(item => {
359            let parts = item.abcFile.split(/\/|\\/);
360            let fileName = parts.length > 1 ? parts[parts.length - 1] : item.abcFile;
361            return [item.abcId, fileName];
362        }));
363        let result = [];
364        for (let moduleItem of jsonData) {
365            for (let functionItem of moduleItem.function) {
366                if (functionItem.type.length === 0) {
367                    result.push(TabPaneApSummary.MakeApObj(moduleItem.moduleName, functionItem.functionName));
368                }
369                for (let typeItem of functionItem.type) {
370                    if (Array.isArray(typeItem)) {
371                        for (let typesItem of typeItem) {
372                            if (Array.isArray(typesItem)) {
373                                for (let item of typesItem) {
374                                    result.push(TabPaneApSummary.MakeApObj(moduleItem.moduleName, functionItem.functionName, item));
375                                }
376                            } else {
377                                result.push(TabPaneApSummary.MakeApObj(moduleItem.moduleName, functionItem.functionName, typesItem));
378                            }
379                        }
380                    } else {
381                        result.push(TabPaneApSummary.MakeApObj(moduleItem.moduleName, functionItem.functionName, typeItem));
382                    }
383                }
384            }
385        }
386        return result;
387    }
388}
389
390TabPaneApSummary.KindMap = {
391    '0': 'ClassId',
392    '1': 'LiteralId',
393    '2': 'BuiltinsId',
394    '3': 'LegacyKind',
395    '4': 'MethodId',
396    '5': 'BuiltinFunctionId',
397    '6': 'RecordClassId',
398    '7': 'PrototypeId',
399    '8': 'ConstructorId',
400    '9': 'MegaStateKinds',
401    '10': 'TotalKinds',
402    '11': 'UnknowId',
403    '12': 'GlobalsId'
404};
405TabPaneApSummary.abcFilePoolMap = new Map();
406if (!customElements.get('tab-ap-summary')) {
407    customElements.define('tab-ap-summary', TabPaneApSummary);
408}
409export class TypeItem {
410    constructor() {
411        this.typeName = '-';
412        this.typeOffset = '-';
413        this.isRoot = '-';
414        this.kind = '-';
415        this.abcId = '-';
416        this.id = '-';
417    }
418}
419