1/*
2 * Copyright (C) 2022 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 */
15
16import { BaseElement, element } from '../../base-ui/BaseElement';
17import { LitMainMenuGroup } from '../../base-ui/menu/LitMainMenuGroup';
18import { LitMainMenu, MenuGroup, MenuItem } from '../../base-ui/menu/LitMainMenu';
19import { LitMainMenuItem } from '../../base-ui/menu/LitMainMenuItem';
20import { SpStatisticsHttpUtil } from '../../statistics/util/SpStatisticsHttpUtil';
21import { EventDefinition, eventDefinitions } from '../enums/helpDocEnums';
22
23@element('sp-help')
24export class SpHelp extends BaseElement {
25  private appContent: HTMLElement | undefined | null;
26  private helpFile: HTMLElement | undefined | null;
27  private navbarContainer: HTMLElement | undefined | null;
28  private backToTop: HTMLElement | undefined | null;
29
30  get dark(): boolean {
31    return this.hasAttribute('dark');
32  }
33
34  set dark(dark: boolean) {
35    if (dark) {
36      this.setAttribute('dark', `${dark}`);
37    } else {
38      this.removeAttribute('dark');
39    }
40    this.helpFile!.innerHTML =
41      '<object type="text/html" data=' +
42      `/application/doc/quickstart_device_record.html?${dark} width="100%" height="100%"></object>`;
43    this.navbarInit('quickstart_device_record');
44  }
45
46  initElements(): void {
47    let parentElement = this.parentNode as HTMLElement;
48    parentElement.style.overflow = 'hidden';
49    this.appContent = this.shadowRoot?.querySelector('#app-content') as HTMLElement;
50    this.helpFile = this.shadowRoot?.querySelector('#help-file') as HTMLElement;
51    this.navbarContainer = this.shadowRoot?.querySelector('#navbar-container') as HTMLElement;
52    this.backToTop = this.shadowRoot?.querySelector('.back') as HTMLElement;
53    let mainMenu = this.shadowRoot?.querySelector('#main-menu') as LitMainMenu;
54    let header = mainMenu.shadowRoot?.querySelector('.header') as HTMLDivElement;
55    let color = mainMenu.shadowRoot?.querySelector('.customColor') as HTMLDivElement;
56    let version = mainMenu.shadowRoot?.querySelector('.version') as HTMLDivElement;
57    color.style.display = 'none';
58    header.style.display = 'none';
59    version.style.display = 'none';
60    this.setupMainMenu(mainMenu, this);
61    mainMenu.style.width = '330px';
62    let body = mainMenu.shadowRoot?.querySelector('.menu-body') as HTMLDivElement;
63    let groups = body.querySelectorAll<LitMainMenuGroup>('lit-main-menu-group');
64    groups.forEach((value) => {
65      let items = value.querySelectorAll<LitMainMenuItem>('lit-main-menu-item');
66      items.forEach((item) => {
67        item.style.width = '330px';
68      });
69      if (value.title === 'TraceStreamer') {
70        let items = value.querySelectorAll<LitMainMenuItem>('lit-main-menu-item');
71        items.forEach((i) => {
72          if (i.title !== 'TraceStreamer数据库说明') {
73            i.style.display = 'none';
74          }
75        });
76      }
77      if (value.title === 'SmartPerf') {
78        value.style.display = 'none';
79      }
80      if (value.title === 'TS栈') { 
81        value.style.display = 'none';
82      }
83      if (value.title === '内存') {
84        let items = value.querySelectorAll<LitMainMenuItem>('lit-main-menu-item');
85        items.forEach((i) => {
86          if (i.title === 'Js Memory抓取和展示说明') {
87            i.style.display = 'none';
88          }
89        });
90      }
91    });
92    let urlParams = new URL(window.location.href).searchParams;
93    if (urlParams && urlParams.get('action') && urlParams.get('action')!.length > 4) {
94      this.itemHelpClick(urlParams, this);
95    }
96  }
97
98  private itemHelpClick(urlParams: URLSearchParams, that: this): void {
99    if (urlParams.get('action')!.length > 4) {
100      let helpDocIndex = urlParams.get('action')!.substring(5);
101      let helpDocDetail = this.getEventDefinitionByIndex(Number(helpDocIndex));
102      that.helpFile!.innerHTML = `<object type="text/html" data='/application/doc/${helpDocDetail!.name}.html?${that.dark
103        }' width="100%" height="100%"></object>`;
104
105      this.navbarInit(helpDocDetail!.name);
106    }
107  }
108
109  private getEventDefinitionByIndex(index: number): EventDefinition | null {
110    for (let key in eventDefinitions) {
111      if (eventDefinitions[key].index === index) {
112        return eventDefinitions[key];
113      }
114    }
115    return null;
116  }
117
118  private setupMainMenu(mainMenu: LitMainMenu, that: this): void {
119    mainMenu.menus = [
120      {
121        collapsed: false,
122        title: 'QuickStart',
123        second: false,
124        icon: 'caret-down',
125        describe: '',
126        children: [
127          this.setupCaptureAndImportMenu(that),
128          this.setupMemoryMenu(that),
129          this.setupNativeMenu(that),
130          this.setupTsMenu(that),
131          this.setupAnalysisTemplateMenu(that),
132          this.setupFileMenu(that),
133          this.setupOtherMenu(that),
134        ],
135      },
136      {
137        collapsed: false,
138        title: 'TraceStreamer',
139        second: false,
140        icon: 'caret-down',
141        describe: '',
142        children: [
143          this.setupDatabaseMenu(that),
144          this.setupCompileMenu(that),
145          this.setupAnalysisMenu(that),
146          this.setupEventListMenu(that),
147          this.setupToolDescriptionMenu(that),
148          this.setupBinderMenu(that),
149          this.setupWakeUpMenu(that),
150        ],
151      },
152      {
153        collapsed: false,
154        title: 'SmartPerf',
155        second: false,
156        icon: 'caret-down',
157        describe: '',
158        children: [this.setupSmartPerfMenu(that)],
159      },
160    ];
161  }
162
163  private setupCaptureAndImportMenu(that: this): MenuGroup {
164    return {
165      collapsed: false,
166      title: '抓取和导入',
167      describe: '',
168      second: true,
169      icon: 'caret-down',
170      children: [
171        {
172          title: '设备端抓取trace说明',
173          icon: '',
174          clickHandler: function (item: MenuItem): void {
175            that.handleMemoryMenuItemClick(that, 'record', 'quickstart_device_record', '1');
176          },
177        },
178        {
179          title: 'web端抓取trace说明',
180          icon: '',
181          clickHandler: function (item: MenuItem): void {
182            that.handleMemoryMenuItemClick(that, 'online_record', 'quickstart_web_record', '2');
183          },
184        },
185        {
186          title: 'web端加载trace说明',
187          icon: '',
188          clickHandler: function (item: MenuItem): void {
189            that.handleMemoryMenuItemClick(that, 'load', 'quickstart_systemtrace', '3');
190          },
191        },
192      ],
193    };
194  }
195
196  private setupOtherMenu(that: this): MenuGroup {
197    return {
198      collapsed: false,
199      title: '其他',
200      describe: '',
201      icon: 'caret-down',
202      second: true,
203      children: this.setupOtherMenuItems(that),
204    };
205  }
206  private setupOtherMenuItems(that: this): MenuItem[] {
207    return [
208      this.createSubMenuItem('Sql分析和Metrics说明', 'sql', 'quickstart_sql_metrics', that, '17'),
209      this.createSubMenuItem('HiSystemEvent抓取和展示说明', 'hisys', 'quickstart_hisystemevent', that, '18'),
210      this.createSubMenuItem('sdk抓取和展示说明', 'sdk_record', 'quickstart_sdk', that, '19'),
211      this.createSubMenuItem('调用栈可视化和不同库函数调用占比说明', 'import_so', 'quickstart_Import_so', that, '20'),
212      this.createSubMenuItem('Hilog抓取和展示说明', 'hilog', 'quickstart_hilog', that, '21'),
213      this.createSubMenuItem('Ability Monitor抓取和展示说明', 'ability', 'quickstart_ability_monitor', that, '22'),
214      this.createSubMenuItem('Trace解析能力增强', 'trace_parsing', 'quickstart_parsing_ability', that, '23'),
215      this.createSubMenuItem('应用操作技巧', 'operation_skills', 'quickstart_Application_operation_skills', that, '24'),
216      this.createSubMenuItem('快捷键说明', 'keywords_shortcuts', 'quickstart_keywords_shortcuts', that, '25'),
217    ];
218  }
219
220  private createSubMenuItem(title: string, event: string, docName: string, that: this, index: string): MenuItem {
221    return {
222      title: title,
223      icon: '',
224      clickHandler: (item: MenuItem): void => {
225        that.handleMemoryMenuItemClick(that, event, docName, index);
226      },
227    };
228  }
229
230  private setupMemoryMenu(that: this): MenuGroup {
231    return {
232      collapsed: false,
233      title: '内存',
234      describe: '',
235      icon: 'caret-down',
236      second: true,
237      children: [
238        {
239          title: 'Js Memory抓取和展示说明',
240          icon: '',
241          clickHandler: function (item: MenuItem): void {
242            that.handleMemoryMenuItemClick(that, 'js_memory', 'quickstart_Js_memory', '4');
243          },
244        },
245        {
246          title: 'Native Memory抓取和展示说明',
247          icon: '',
248          clickHandler: function (item: MenuItem): void {
249            that.handleMemoryMenuItemClick(that, 'native', 'quickstart_native_memory', '5');
250          },
251        },
252        {
253          title: '页内存抓取和展示说明',
254          icon: '',
255          clickHandler: function (item: MenuItem): void {
256            that.handleMemoryMenuItemClick(that, 'virtual_memory', 'quickstart_page_fault', '6');
257          },
258        },
259        {
260          title: '系统内存抓取和展示说明',
261          icon: '',
262          clickHandler: function (item: MenuItem): void {
263            that.handleMemoryMenuItemClick(that, 'memory_template', 'quickstart_memory_template', '7');
264          },
265        },
266      ],
267    };
268  }
269
270  private handleMemoryMenuItemClick(that: this, event: string, docName: string, index?: string): void {
271    SpStatisticsHttpUtil.addOrdinaryVisitAction({
272      event: event,
273      action: 'help_doc',
274    });
275    that.helpFile!.innerHTML = `<object type="text/html" data='/application/doc/${docName}.html?${that.dark}' width="100%" height="100%"></object>`;
276    this.navbarInit(docName);
277    this.changeItemURL(index!);
278  }
279
280  private navbarInit(docName: string): void {
281    fetch(`/application/doc/${docName}.html`)
282      .then(response => response.text())
283      .then(htmlString => {
284        const parser = new DOMParser();
285        const doc = parser.parseFromString(htmlString, 'text/html');
286
287        const hTags = Array.from(doc.body.querySelectorAll('h1, h2, h3, h4, h5, h6')).map((header) => ({
288          id: header.id,
289          text: header.textContent!.trim()
290        }));
291        this.navbarContainer!.innerHTML = `<ul id="nav-links">${hTags.map(hTag => {
292          let backData: string = '';
293          if (hTag.id) {
294            backData = `<li class="tooltip"><a id="${hTag.id}" data-full-text="${hTag.text}">${hTag.text}</a><span class="tooltiptext" id="tooltip-${hTag.id}">${hTag.text}</span>
295          </li>`;
296          }
297          return backData;
298        }).join('')
299          }</ul>`;
300
301        let navLinks = this.navbarContainer!.querySelectorAll('#nav-links a');
302        navLinks.forEach((navLink) => {
303          navLink.addEventListener('click', (e) => {
304            let lis = this.navbarContainer!.querySelectorAll('#nav-links li');
305            lis.forEach(li => li.classList.remove('active'));
306            navLink.closest('li')!.classList.add('active');
307            let targetId = navLink.id;
308            e.preventDefault();
309            this.helpFile!.innerHTML = `<object type="text/html" data='/application/doc/${docName}.html?dark=${this.dark}&targetId=${targetId}' width="100%" height="100%"></object>`;
310          });
311        });
312
313        this.backToTop!.querySelector('#back-to-top')!.addEventListener('click', (e) => {
314          e.preventDefault();
315          navLinks.forEach((navLink) => {
316            navLink.closest('li')?.classList.remove('active');
317          });
318          this.helpFile!.innerHTML = `<object type="text/html" data='/application/doc/${docName}.html?dark=${this.dark}' width="100%" height="100%"></object>`;
319        });
320
321      })
322      .catch(error => {
323        console.error('Error fetching and modifying HTML:', error);
324      });
325  }
326
327  private changeItemURL(index: string): void {
328    let url = new URL(window.location.href);
329    let actionParam = url.searchParams.get('action');
330    let newActionValue = `help_${index}`;
331    if (actionParam) {
332      url.searchParams.set('action', newActionValue);
333      let newURL = url.href;
334      history.pushState({}, '', newURL);
335    } else {
336      history.pushState({}, '', window.location.origin + window.location.pathname);
337    }
338  }
339
340  private setupNativeMenu(that: this): MenuGroup {
341    return {
342      collapsed: false,
343      title: 'Native栈',
344      describe: '',
345      second: true,
346      icon: 'caret-down',
347      children: [
348        {
349          title: 'HiPerf的抓取和展示说明',
350          icon: '',
351          clickHandler: function (item: MenuItem): void {
352            that.handleMemoryMenuItemClick(that, 'perf', 'quickstart_hiperf', '8');
353          },
354        },
355      ],
356    };
357  }
358
359  private setupTsMenu(that: this): MenuGroup {
360    return {
361      collapsed: false,
362      title: 'TS栈',
363      describe: '',
364      second: true,
365      icon: 'caret-down',
366      children: [
367        {
368          title: 'Cpuprofiler抓取和展示说明',
369          icon: '',
370          clickHandler: function (item: MenuItem): void {
371            that.handleMemoryMenuItemClick(that, 'arkts', 'quickstart_arkts', '9');
372          },
373        },
374      ],
375    };
376  }
377
378  private setupAnalysisTemplateMenu(that: this): MenuGroup {
379    return {
380      collapsed: false,
381      title: '分析模板',
382      describe: '',
383      second: true,
384      icon: 'caret-down',
385      children: [
386        {
387          title: 'Frame timeline抓取和展示说明',
388          icon: '',
389          clickHandler: function (item: MenuItem): void {
390            that.handleMemoryMenuItemClick(that, 'frame_record', 'quickstart_Frametimeline', '10');
391          },
392        },
393        {
394          title: 'Animation的抓取和展示说明',
395          icon: '',
396          clickHandler: function (item: MenuItem): void {
397            that.handleMemoryMenuItemClick(that, 'animation', 'quickstart_animation', '11');
398          },
399        },
400        {
401          title: 'TaskPool抓取和展示说明',
402          icon: '',
403          clickHandler: function (item: MenuItem): void {
404            that.handleMemoryMenuItemClick(that, 'taskpool', 'quickstart_taskpool', '12');
405          },
406        },
407        {
408          title: 'App startup的抓取和展示说明',
409          icon: '',
410          clickHandler: function (item: MenuItem): void {
411            that.handleMemoryMenuItemClick(that, 'app_startup', 'quickstart_app_startup', '13');
412          },
413        },
414        {
415          title: 'Scheduling analysis抓取和展示说明',
416          icon: '',
417          clickHandler: function (item: MenuItem): void {
418            that.handleMemoryMenuItemClick(that, 'scheduling_record', 'quickstart_schedulinganalysis', '14');
419          },
420        },
421      ],
422    };
423  }
424
425  private setupFileMenu(that: this): MenuGroup {
426    return {
427      collapsed: false,
428      title: '文件',
429      describe: '',
430      second: true,
431      icon: 'caret-down',
432      children: [
433        {
434          title: 'FileSystem抓取和展示说明',
435          icon: '',
436          clickHandler: function (item: MenuItem): void {
437            that.handleMemoryMenuItemClick(that, 'file_system', 'quickstart_filesystem', '15');
438          },
439        },
440        {
441          title: 'Bio抓取和展示说明',
442          icon: '',
443          clickHandler: function (item: MenuItem): void {
444            that.handleMemoryMenuItemClick(that, 'bio', 'quickstart_bio', '16');
445          },
446        },
447      ],
448    };
449  }
450
451  private setupDatabaseMenu(that: this): MenuItem {
452    return {
453      title: 'TraceStreamer数据库说明',
454      icon: '',
455      clickHandler: function (item: MenuItem): void {
456        that.handleMemoryMenuItemClick(that, 'trace_streamer_explain', 'des_tables', '26');
457      },
458    };
459  }
460
461  private setupCompileMenu(that: this): MenuItem {
462    return {
463      title: '编译Trace_streamer',
464      icon: '',
465      clickHandler: function (item: MenuItem): void {
466        that.handleMemoryMenuItemClick(that, 'trace_streamer_compile', 'compile_trace_streamer');
467      },
468    };
469  }
470
471  private setupAnalysisMenu(that: this): MenuItem {
472    return {
473      title: 'TraceStreamer 解析数据状态表',
474      icon: '',
475      clickHandler: function (item: MenuItem): void {
476        that.handleMemoryMenuItemClick(that, 'trace_streamer_des', 'des_stat');
477      },
478    };
479  }
480
481  private setupSmartPerfMenu(that: this): MenuItem {
482    return {
483      title: 'SmartPerf 编译指导',
484      icon: '',
485      clickHandler: function (item: MenuItem): void {
486        that.handleMemoryMenuItemClick(that, 'smartperf_guide', 'quickstart_smartperflinux_compile_guide');
487      },
488    };
489  }
490
491  private setupEventListMenu(that: this): MenuItem {
492    return {
493      title: 'TraceStreamer支持解析事件列表',
494      icon: '',
495      clickHandler: function (item: MenuItem): void {
496        that.handleMemoryMenuItemClick(that, 'support_event', 'des_support_event');
497      },
498    };
499  }
500
501  private setupToolDescriptionMenu(that: this): MenuItem {
502    return {
503      title: 'trace_streamer工具说明',
504      icon: '',
505      clickHandler: function (item: MenuItem): void {
506        that.handleMemoryMenuItemClick(that, 'quickstart_trace_streamer', 'quickstart_trace_streamer');
507      },
508    };
509  }
510
511  private setupBinderMenu(that: this): MenuItem {
512    return {
513      title: 'binder事件上下文如何关联',
514      icon: '',
515      clickHandler: function (item: MenuItem): void {
516        that.handleMemoryMenuItemClick(that, 'binder', 'des_binder');
517      },
518    };
519  }
520
521  private setupWakeUpMenu(that: this): MenuItem {
522    return {
523      title: 'wakeup唤醒说明',
524      icon: '',
525      clickHandler: function (item: MenuItem): void {
526        that.handleMemoryMenuItemClick(that, 'wakeup', 'des_wakup');
527      },
528    };
529  }
530
531  initHtml(): string {
532    return `
533        <style>
534        .sp-help-vessel {
535            min-height: 100%;
536            display: grid;
537            grid-template-columns: 1fr;
538            grid-template-rows:1fr;
539            background-color: var(--dark-background5,#F6F6F6);
540        }
541        :host{
542            width: 100%;
543            display: block;
544            height: 100%;
545            background-color: var(--dark-background5,#F6F6F6);
546        }
547        .body{
548            width: 99%;
549            margin-left: 15px;
550            display: grid;
551            grid-template-columns: min-content  1fr;
552            border-radius: 16px 16px 16px 16px;
553        }
554
555        .content{
556          border-style: none none none solid;
557          border-width: 1px;
558          border-color: rgba(166,164,164,0.2);
559          border-radius: 0px 16px 16px 0px;
560          padding-left:15px;
561          display: flex;
562          overflow-y: hidden;
563          box-sizing: border-box;
564        }
565        #navbar-container { 
566          border-left: 5px solid #ecb829;
567        }
568        #navbar-container ul {  
569          list-style-type: none; 
570          width:100%;
571          margin: 0;  
572          padding: 0;
573        } 
574        #navbar-container ul li { 
575          position: relative;
576          width:100%;
577          height:30px;
578          line-height:30px; 
579          text-align: left;
580          padding: 0 10px;
581          box-sizing: border-box;
582          border: none; 
583          margin: 0;
584        } 
585        #navbar-container ul li a {  
586          width:100%;
587          height:100%;
588          color: black;
589          font-family: Helvetica;
590          font-size: 14px;
591          text-decoration: none;  
592          display: block; 
593          padding: 0;  
594          border: none;
595          white-space: nowrap; 
596          overflow: hidden;
597          text-overflow: ellipsis;
598        }  
599        #navbar-container ul li:hover,  
600        #navbar-container ul li:focus {  
601          color: #ecb829;
602          cursor: pointer;
603        } 
604        #navbar-container ul li:hover a,  
605        #navbar-container ul li:focus a {
606          color: #ecb829;
607        }
608        #navbar-container ul li .tooltiptext { 
609          position: absolute;   
610          bottom: 0;
611          left: 50%; 
612          transform: translateX(-50%);
613          visibility: hidden;  
614          width: 100%; 
615          background-color: #ecb829;  
616          color: #fff;
617          font-family: Helvetica;
618          font-size: 14px;  
619          text-align: center; 
620          border-radius: 6px;  
621          padding: 5px 5px; 
622          margin-left:5px; 
623          position: absolute;  
624          z-index: 1;  
625          opacity: 0;  
626          transition: opacity 0.3s;  
627          margin-bottom: 40px; 
628          &::after {  
629              content: '';  
630              position: absolute;  
631              bottom: -10px;
632              left: 50%; 
633              margin-left: -10px; 
634              width: 0;  
635              height: 0; 
636              border-style: solid;  
637              border-width: 10px 10px 0 10px; 
638              border-color: #ecb829 transparent transparent transparent; 
639            }  
640         } 
641         #navbar-container ul li.tooltip:hover .tooltiptext {  
642          visibility: visible;  
643          opacity: 1;  
644         }   
645         #navbar-container ul li.active, #navbar-container ul li.active a {  
646          color: #ecb829;  
647         }
648
649        </style>
650        <div class="sp-help-vessel">
651         <div class="body">
652            <lit-main-menu id="main-menu" class="menugroup" data=''></lit-main-menu>
653            <div id="app-content" class="content">
654               <div id="help-file" style="width:100%;overflow-y: hidden;"></div>
655                      <nav id="navbar-container" style="position:fixed;top:80px;left:79%;width:18%;"></nav>
656                      <div class="back" style="position:fixed;top:80px;left:98%;width:2%;">
657                          <lit-icon id="back-to-top" name="vertical-align-top" style="font-weight: bold;cursor: pointer;" size="20">
658                          </lit-icon>
659                      </div>
660               </div>
661            </div>
662         </div>
663        </div>
664        `;
665  }
666}
667