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