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