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