text: // Copyright 2016 The Go Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style
  // license that can be found in the LICENSE file.
  
  /**
   * An enum of types of actions that might be requested
   * by the app.
   */
  enum Action {
    TOGGLE_SIDEBAR,  // Toggle the sidebar.
    NAVIGATE_ABOUT,  // Go to the about page.
  }
  
  const TITLE = 'Go Heap Viewer';
  
  /**
   * A type of event that signals to the AppElement controller
   * that something shoud be done. For the most part, the structure
   * of the app will be that elements' state will mostly be controlled
   * by parent elements. Elements will issue actions that the AppElement
   * will handle, and the app will be re-rendered down the DOM
   * hierarchy.
   */
  class ActionEvent extends Event {
    static readonly EVENT_TYPE = 'action-event'
    constructor(public readonly action: Action) { super(ActionEvent.EVENT_TYPE); }
  }
  
  /**
   * A hamburger menu element. Triggers a TOGGLE_SIDE action to toggle the
   * sidebar.
   */
  export class HamburgerElement extends HTMLElement {
    static readonly NAME = 'heap-hamburger';
  
    createdCallback() {
      this.appendChild(document.createTextNode('☰'));
      this.onclick =
          () => { this.dispatchEvent(new ActionEvent(Action.TOGGLE_SIDEBAR)) };
    }
  }
  document.registerElement(HamburgerElement.NAME, HamburgerElement);
  
  /**
   * A heading for the page with a hamburger menu and a title.
   */
  export class HeadingElement extends HTMLElement {
    static readonly NAME = 'heap-heading';
  
    createdCallback() {
      this.style.display = 'block';
      this.style.backgroundColor = '#2196F3';
      this.style.webkitUserSelect = 'none';
      this.style.cursor = 'default';
      this.style.color = '#FFFFFF';
      this.style.padding = '10px';
  
      const div = document.createElement('div');
      div.style.margin = '0px';
      div.style.fontSize = '2em';
      div.appendChild(document.createElement(HamburgerElement.NAME));
      div.appendChild(document.createTextNode(' ' + TITLE));
      this.appendChild(div);
    }
  }
  document.registerElement(HeadingElement.NAME, HeadingElement);
  
  /**
   * A sidebar that has navigation for the app.
   */
  export class SidebarElement extends HTMLElement {
    static readonly NAME = 'heap-sidebar';
  
    createdCallback() {
      this.style.display = 'none';
      this.style.backgroundColor = '#9E9E9E';
      this.style.width = '15em';
  
      const aboutButton = document.createElement('button');
      aboutButton.innerText = 'about';
      aboutButton.onclick =
          () => { this.dispatchEvent(new ActionEvent(Action.NAVIGATE_ABOUT)) };
      this.appendChild(aboutButton);
    }
  
    toggle() {
      this.style.display = this.style.display === 'none' ? 'block' : 'none';
    }
  }
  document.registerElement(SidebarElement.NAME, SidebarElement);
  
  /**
   * A Container for the main content in the app.
   * TODO(matloob): Implement main content.
   */
  export class MainContentElement extends HTMLElement {
    static readonly NAME = 'heap-container';
  
    attachedCallback() {
      this.style.backgroundColor = '#E0E0E0';
      this.style.height = '100%';
      this.style.flex = '1';
    }
  }
  document.registerElement(MainContentElement.NAME, MainContentElement);
  
  /**
   * A container and controller for the whole app.
   * Contains the heading, side drawer and main panel.
   */
  class AppElement extends HTMLElement {
    static readonly NAME = 'heap-app';
    private sidebar: SidebarElement;
    private mainContent: MainContentElement;
  
    attachedCallback() {
      document.title = TITLE;
  
      this.addEventListener(
          ActionEvent.EVENT_TYPE, e => this.handleAction(e as ActionEvent),
          /* capture */ true);
  
      this.render();
    }
  
    render() {
      this.style.display = 'block';
      this.style.height = '100vh';
      this.style.width = '100vw';
      this.appendChild(document.createElement(HeadingElement.NAME));
  
      const bodyDiv = document.createElement('div');
      bodyDiv.style.height = '100%';
      bodyDiv.style.display = 'flex';
      this.sidebar =
          document.createElement(SidebarElement.NAME) as SidebarElement;
      bodyDiv.appendChild(this.sidebar);
      this.mainContent =
          document.createElement(MainContentElement.NAME) as MainContentElement;
      bodyDiv.appendChild(this.mainContent);
      this.appendChild(bodyDiv);
  
      this.renderRoute();
    }
  
    renderRoute() {
      this.mainContent.innerHTML = ''
      switch (window.location.pathname) {
        case '/about':
          this.mainContent.appendChild(
              document.createElement(AboutPageElement.NAME));
          break;
      }
    }
  
    handleAction(event: ActionEvent) {
      switch (event.action) {
        case Action.TOGGLE_SIDEBAR:
          this.sidebar.toggle();
          break;
        case Action.NAVIGATE_ABOUT:
          window.history.pushState({}, '', '/about');
          this.renderRoute();
          break;
      }
    }
  }
  document.registerElement(AppElement.NAME, AppElement);
  
  /**
   * An about page.
   */
  class AboutPageElement extends HTMLElement {
    static readonly NAME = 'heap-about';
  
    createdCallback() { this.textContent = TITLE; }
  }
  document.registerElement(AboutPageElement.NAME, AboutPageElement);
  
  /**
   * Resets body's margin and padding, and sets font.
   */
  function clearStyle(document: Document) {
    const styleElement = document.createElement('style') as HTMLStyleElement;
    document.head.appendChild(styleElement);
    const styleSheet = styleElement.sheet as CSSStyleSheet;
    styleSheet.insertRule(
        '* {font-family: Roboto,Helvetica; box-sizing: border-box}', 0);
    styleSheet.insertRule('body {margin: 0px; padding:0px}', 0);
  }
  
  export function main() {
    clearStyle(document);
    document.body.appendChild(document.createElement(AppElement.NAME));
  }
  