github.com/munnerz/test-infra@v0.0.0-20190108210205-ce3d181dc989/prow/cmd/deck/static/spyglass/lens.ts (about)

     1  import {Message, Response, isResponse, isTransitMessage} from './common';
     2  
     3  export interface Spyglass {
     4    /**
     5     * Replaces the lens display with a new server-rendered page.
     6     * The returned promise will be resolved once the page has been updated.
     7     *
     8     * @param data Some data to pass back to the server. JSON encoding is
     9     *             recommended, but not required.
    10     */
    11    updatePage(data: string): Promise<void>;
    12    /**
    13     * Requests that the server re-render the lens with the provided data, and
    14     * returns a promise that will resolve with that HTML as a string.
    15     *
    16     * This is equivalent to updatePage(), except that the displayed content is
    17     * not automatically changed.
    18     * @param data Some data to pass back to the server. JSON encoding is
    19     *             recommended, but not required.
    20     */
    21    requestPage(data: string): Promise<string>;
    22    /**
    23     * Sends a request to the server-side lens backend with the provided data, and
    24     * returns a promise that will resolve with the response as a string.
    25     *
    26     * @param data Some data to pass back to the server. JSON encoding is
    27     *             recommended, but not required.
    28     */
    29    request(data: string): Promise<string>;
    30    /**
    31     * Inform Spyglass that the lens content has updated. This should be called whenever
    32     * the visible content changes, so Spyglass can ensure that all content is visible.
    33     */
    34    contentUpdated(): void;
    35  }
    36  
    37  class SpyglassImpl implements Spyglass {
    38    private pendingRequests = new Map<number, (v: Response) => void>();
    39    private messageId = 0;
    40  
    41    constructor() {
    42      window.addEventListener('message', (e) => this.handleMessage(e));
    43    }
    44  
    45    async updatePage(data: string): Promise<void> {
    46      await this.postMessage({type: 'updatePage', data});
    47      this.contentUpdated();
    48    }
    49    async requestPage(data: string): Promise<string> {
    50      const result = await this.postMessage({type: 'requestPage', data});
    51      return result.data;
    52    }
    53    async request(data: string): Promise<string> {
    54      const result = await this.postMessage({type: 'request', data});
    55      return result.data;
    56    }
    57    contentUpdated(): void {
    58      // .then() to suppress complaints about unhandled promises (we just don't care here).
    59      this.postMessage({type: 'contentUpdated', height: document.body.offsetHeight}).then();
    60    }
    61  
    62    private postMessage(message: Message): Promise<Response> {
    63      return new Promise<Response>((resolve, reject) => {
    64        const id = ++this.messageId;
    65        this.pendingRequests.set(id, resolve);
    66        window.parent.postMessage({id, message}, document.location.origin);
    67      });
    68    }
    69  
    70    private handleMessage(e: MessageEvent) {
    71      if (e.origin !== document.location.origin) {
    72        console.warn(`Got MessageEvent from unexpected origin ${e.origin}; expected ${document.location.origin}`, e);
    73        return;
    74      }
    75      const data = e.data;
    76      if (isTransitMessage(data)) {
    77        if (isResponse(data.message)) {
    78          if (this.pendingRequests.has(data.id)) {
    79            this.pendingRequests.get(data.id)!(data.message);
    80            this.pendingRequests.delete(data.id);
    81          }
    82        }
    83      }
    84    }
    85  }
    86  
    87  const spyglass = new SpyglassImpl();
    88  
    89  window.addEventListener('load', () => {
    90    spyglass.contentUpdated();
    91  });
    92  
    93  (window as any).spyglass = spyglass;