export class Service {
        constructor(private utility: LevelUp.Common.Utility) {
        }
        emojis(){
            this.utility.messageExtension(null, 'emojis');
        }

        environmentDetails() {
            let entity = this.utility.is2016OrGreater ? 'organizations' : 'OrganizationSet';
            this.utility.fetch(entity).then((c) => {
                let settings = {};
                let settingsArray = [];
                if (c.length > 0) {
                    settings = c[0];
                }
                for (let s in settings) {
                    if (s.indexOf('@') == -1 && s.indexOf('_') == -1) {
                        settingsArray.push({ name: s, value: settings[s] });
                    }
                }
                this.utility.messageExtension(settingsArray, 'Settings');
            }).catch((err) => {
                console.log(err);
            });
        }

        myRoles() {
            let resultsArray = [{ cells: ['Name', 'Role Id'] }];
            let attributes = 'RoleId,Name';
            let entity = 'RoleSet';
            let filter = Xrm.Page.context.getUserRoles().map(x => `RoleId eq (guid'${x}')`).join(' or ');
            if (this.utility.is2016OrGreater) {
                entity = 'roles';
                attributes = attributes.toLocaleLowerCase();
                filter = Xrm.Page.context.getUserRoles().map(x => `roleid eq ${x}`).join(' or ');
            }
            this.utility.fetch(entity, attributes, filter)
                .then((results) => {
                    results.forEach(r => {
                        resultsArray.push({ cells: Object.keys(r).sort().filter(x => !x.startsWith('@') && !x.startsWith('_')).map(key => r[key]) });
                    });
                    this.utility.messageExtension(resultsArray, 'myRoles');
                }).catch((err) => {
                    console.log(err);
                });
        }

        allUserRoles() {
            let resultsArray: any[] = [{ cells: ['Business Unit', 'Role', 'User', 'AD Name'] }];
            this.utility.fetch('systemusers', null, null, null, `
            <fetch>
                <entity name="systemuser" >
                    <attribute name="domainname" />
                    <attribute name="businessunitid" />
                    <attribute name="fullname" />
                    <filter>
                        <condition entityname="role" attribute="parentroleid" operator="null" />
                    </filter>
                    <link-entity name="systemuserroles" from="systemuserid" to="systemuserid" link-type="outer" alias="systemuserroles" >
                        <attribute name="roleid" />
                        <attribute name="systemuserid" />
                        <link-entity name="role" from="roleid" to="roleid" link-type="outer" alias="role" >
                            <attribute name="name" />
                            <order attribute="name" />
                        </link-entity>
                    </link-entity>
                </entity>
            </fetch>`).then(entities => {
                console.log(entities);
                let cells = entities.forEach(attributes => {
                    let roleId = attributes['systemuserroles.roleid'] || 'No Role',
                        roleName = attributes['role.name'] || 'No Role',
                        userId = attributes['systemuserid'],
                        userName = attributes['fullname'];

                    resultsArray.push({
                        bu: attributes['_businessunitid_value@OData.Community.Display.V1.FormattedValue'],
                        role: {
                            id: roleId,
                            name: roleName,
                            url: `${this.utility.clientUrlForParams}etn=role&id=${roleId}&newWindow=true&pagetype=entityrecord`
                        },
                        user: {
                            id: userId,
                            name: userName,
                            url: `${this.utility.clientUrlForParams}etn=systemuser&id=${userId}&newWindow=true&pagetype=entityrecord`
                        },
                        adname: attributes['domainname']
                    });
                });
                this.utility.messageExtension(resultsArray, 'allUserRoles');
            });
        }
        lightUpNavigation(){
            this.toggleNavigation(true);
        }

        classicNavigation(){
            this.toggleNavigation(false);
        }

        private toggleNavigation(isNewNavigation){
            if(this.utility.version.startsWith('9.1')){
                let organizationSettings = Xrm.Page.context.organizationSettings;
                if(organizationSettings) {
                    Xrm.WebApi.updateRecord('organization', organizationSettings.organizationId, {
                        clientfeatureset:
                        `<clientfeatures>
                            <clientfeature xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-Instance\">
                                <name>FCB.ShellRefresh</name>
                                <value>${isNewNavigation}</value>
                                <location>Organization</location>
                            </clientfeature>
                        </clientfeatures>`
                    }).then(s=>{
                        if(Xrm.Internal.isUci || Xrm.Internal.isUci()){
                            alert(`New navigation has been ${isNewNavigation ? 'enabled' : 'disabled'}. The page will now reload.`);
                            location.reload();
                        }
                        else{
                            alert(`New navigation has been ${isNewNavigation ? 'enabled' : 'disabled'}.`);
                        }
                    });
                }
            }
            else{
                alert('New navigation is available only on orgs that are >= 9.1');
            }
        }
    }


emojis(){
            this.utility.messageExtension(null, 'emojis');
        }


environmentDetails() {
            let entity = this.utility.is2016OrGreater ? 'organizations' : 'OrganizationSet';
            this.utility.fetch(entity).then((c) => {
                let settings = {};
                let settingsArray = [];
                if (c.length > 0) {
                    settings = c[0];
                }
                for (let s in settings) {
                    if (s.indexOf('@') == -1 && s.indexOf('_') == -1) {
                        settingsArray.push({ name: s, value: settings[s] });
                    }
                }
                this.utility.messageExtension(settingsArray, 'Settings');
            }).catch((err) => {
                console.log(err);
            });
        }


myRoles() {
            let resultsArray = [{ cells: ['Name', 'Role Id'] }];
            let attributes = 'RoleId,Name';
            let entity = 'RoleSet';
            let filter = Xrm.Page.context.getUserRoles().map(x => `RoleId eq (guid'${x}')`).join(' or ');
            if (this.utility.is2016OrGreater) {
                entity = 'roles';
                attributes = attributes.toLocaleLowerCase();
                filter = Xrm.Page.context.getUserRoles().map(x => `roleid eq ${x}`).join(' or ');
            }
            this.utility.fetch(entity, attributes, filter)
                .then((results) => {
                    results.forEach(r => {
                        resultsArray.push({ cells: Object.keys(r).sort().filter(x => !x.startsWith('@') && !x.startsWith('_')).map(key => r[key]) });
                    });
                    this.utility.messageExtension(resultsArray, 'myRoles');
                }).catch((err) => {
                    console.log(err);
                });
        }


allUserRoles() {
            let resultsArray: any[] = [{ cells: ['Business Unit', 'Role', 'User', 'AD Name'] }];
            this.utility.fetch('systemusers', null, null, null, `
            <fetch>
                <entity name="systemuser" >
                    <attribute name="domainname" />
                    <attribute name="businessunitid" />
                    <attribute name="fullname" />
                    <filter>
                        <condition entityname="role" attribute="parentroleid" operator="null" />
                    </filter>
                    <link-entity name="systemuserroles" from="systemuserid" to="systemuserid" link-type="outer" alias="systemuserroles" >
                        <attribute name="roleid" />
                        <attribute name="systemuserid" />
                        <link-entity name="role" from="roleid" to="roleid" link-type="outer" alias="role" >
                            <attribute name="name" />
                            <order attribute="name" />
                        </link-entity>
                    </link-entity>
                </entity>
            </fetch>`).then(entities => {
                console.log(entities);
                let cells = entities.forEach(attributes => {
                    let roleId = attributes['systemuserroles.roleid'] || 'No Role',
                        roleName = attributes['role.name'] || 'No Role',
                        userId = attributes['systemuserid'],
                        userName = attributes['fullname'];

                    resultsArray.push({
                        bu: attributes['_businessunitid_value@OData.Community.Display.V1.FormattedValue'],
                        role: {
                            id: roleId,
                            name: roleName,
                            url: `${this.utility.clientUrlForParams}etn=role&id=${roleId}&newWindow=true&pagetype=entityrecord`
                        },
                        user: {
                            id: userId,
                            name: userName,
                            url: `${this.utility.clientUrlForParams}etn=systemuser&id=${userId}&newWindow=true&pagetype=entityrecord`
                        },
                        adname: attributes['domainname']
                    });
                });
                this.utility.messageExtension(resultsArray, 'allUserRoles');
            });
        }


lightUpNavigation(){
            this.toggleNavigation(true);
        }


classicNavigation(){
            this.toggleNavigation(false);
        }


private toggleNavigation(isNewNavigation){
            if(this.utility.version.startsWith('9.1')){
                let organizationSettings = Xrm.Page.context.organizationSettings;
                if(organizationSettings) {
                    Xrm.WebApi.updateRecord('organization', organizationSettings.organizationId, {
                        clientfeatureset:
                        `<clientfeatures>
                            <clientfeature xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-Instance\">
                                <name>FCB.ShellRefresh</name>
                                <value>${isNewNavigation}</value>
                                <location>Organization</location>
                            </clientfeature>
                        </clientfeatures>`
                    }).then(s=>{
                        if(Xrm.Internal.isUci || Xrm.Internal.isUci()){
                            alert(`New navigation has been ${isNewNavigation ? 'enabled' : 'disabled'}. The page will now reload.`);
                            location.reload();
                        }
                        else{
                            alert(`New navigation has been ${isNewNavigation ? 'enabled' : 'disabled'}.`);
                        }
                    });
                }
            }
            else{
                alert('New navigation is available only on orgs that are >= 9.1');
            }
        }


/* Handles insights short links `/i/{id}` */
export function InsightRouter(): JSX.Element {
    const { error } = useValues(insightRouterLogic)
    return (
        <>
            {error ? (
                <NotFound object="insight" />
            ) : (
                <>
                    <Skeleton active paragraph={{ rows: 0 }} />
                    <Card>
                        <Row gutter={16}>
                            <Col md={18}>
                                <Skeleton active />
                            </Col>
                            <Col md={6} style={{ textAlign: 'center' }}


(id: string) => ({ id })


({ actions }) => ({
        loadInsight: async ({ id }) => {
            const response = await api.get(`api/insight/?short_id=${id}`)
            if (response.results.length) {
                const item = response.results[0] as DashboardItemType
                router.actions.push(
                    combineUrl('/insights', item.filters, {
                        fromItem: item.id,
                        fromItemName: item.name,
                        fromDashboard: item.dashboard,
                        id: item.short_id,
                    }).url
                )
            } else {
                actions.setError()
            }
        },
    })


async ({ id }) => {
            const response = await api.get(`api/insight/?short_id=${id}`)
            if (response.results.length) {
                const item = response.results[0] as DashboardItemType
                router.actions.push(
                    combineUrl('/insights', item.filters, {
                        fromItem: item.id,
                        fromItemName: item.name,
                        fromDashboard: item.dashboard,
                        id: item.short_id,
                    }).url
                )
            } else {
                actions.setError()
            }
        }


({ actions }) => ({
        '/i/:id': ({ id }: { id: string }) => {
            actions.loadInsight(id)
        },
    })


({ id }: { id: string }) => {
            actions.loadInsight(id)
        }


error ? (
                <NotFound object="insight" />
            ) : (
                <>
                    <Skeleton active paragraph={{ rows: 0 }


(itemCount?: number) => {
  const rval = new MemoryCache();
  if (itemCount) {
    for (let i = 0; i < itemCount; i++) {
      rval.put(`key${i}`, `value${i}`);
    }
  }
  return rval;
}


(delaySeconds = 1010): Promise<void> => {
  return new Promise<void>(resolve => {
    setTimeout(() => resolve(), delaySeconds);
  });
}


resolve => {
    setTimeout(() => resolve(), delaySeconds);
  }


() => {
  describe('keys', () => {
    it('can return keys', () => {
      const result = newInstance(15).keys();
      expect(result.length).toBe(15);
      expect(result.includes('key6')).toBeTruthy();
    });
  });

  describe('clear', () => {
    it('can clear the cache', () => {
      const cache = newInstance(15);
      cache.clear();
      expect(cache.keys().length).toBe(0);
      expect(cache.containsKey('key6')).toBeFalsy();
    });
  });

  describe('size', () => {
    it('can return the size of the cache', () => {
      const size = newInstance(12).size();
      expect(size).toBe(12);
    });

    it('accurately reflects expired item removal', async () => {
      const cache = newInstance(12);
      cache.put('newItem', 'itemValue', 1);
      expect(cache.size()).toBe(13);
      await delay();
      expect(cache.size()).toBe(12);
    });
  });

  describe('containsKey', () => {
    it('can indicate if a key currently exists', () => {
      const cache = newInstance(25);
      expect(cache.containsKey('key6')).toBeTruthy();
      expect(cache.containsKey('key26')).toBeFalsy();
    });
  });

  describe('put', () => {
    it('can cache a value', () => {
      const cache = newInstance();
      const previous = cache.put('foo', 'bar');
      expect(previous).toBeFalsy();
      expect(cache.containsKey('foo')).toBeTruthy();
      expect(cache.get('foo')).toBe('bar');
    });

    it('can cache a value and return the previous value if it exists', () => {
      const cache = newInstance();
      cache.put('foo', 'oldValue');
      const previous = cache.put('foo', 'bar');
      expect(previous).toBe('oldValue');
      expect(cache.containsKey('foo')).toBeTruthy();
      expect(cache.get('foo')).toBe('bar');
    });
  });

  describe('get', () => {
    it('can return a cached value', () => {
      const cache = newInstance(8);
      expect(cache.get('key3')).toBe('value3');
    });

    it('will not return an expired value', async () => {
      const cache = newInstance();
      cache.put('foo', 'oldValue', 1);
      await delay();
      expect(cache.get('foo')).toBeFalsy();
    });
  });

  describe('remove', () => {
    it('can remove and return a cached value', () => {
      const cache = newInstance(8);
      const result = cache.remove('key2');
      expect(result).toBe('value2');
      expect(cache.size()).toBe(7);
    });
  });
}


() => {
    it('can return keys', () => {
      const result = newInstance(15).keys();
      expect(result.length).toBe(15);
      expect(result.includes('key6')).toBeTruthy();
    });
  }


() => {
      const result = newInstance(15).keys();
      expect(result.length).toBe(15);
      expect(result.includes('key6')).toBeTruthy();
    }


() => {
    it('can clear the cache', () => {
      const cache = newInstance(15);
      cache.clear();
      expect(cache.keys().length).toBe(0);
      expect(cache.containsKey('key6')).toBeFalsy();
    });
  }


() => {
      const cache = newInstance(15);
      cache.clear();
      expect(cache.keys().length).toBe(0);
      expect(cache.containsKey('key6')).toBeFalsy();
    }


() => {
    it('can return the size of the cache', () => {
      const size = newInstance(12).size();
      expect(size).toBe(12);
    });

    it('accurately reflects expired item removal', async () => {
      const cache = newInstance(12);
      cache.put('newItem', 'itemValue', 1);
      expect(cache.size()).toBe(13);
      await delay();
      expect(cache.size()).toBe(12);
    });
  }


() => {
      const size = newInstance(12).size();
      expect(size).toBe(12);
    }


async () => {
      const cache = newInstance(12);
      cache.put('newItem', 'itemValue', 1);
      expect(cache.size()).toBe(13);
      await delay();
      expect(cache.size()).toBe(12);
    }


() => {
    it('can indicate if a key currently exists', () => {
      const cache = newInstance(25);
      expect(cache.containsKey('key6')).toBeTruthy();
      expect(cache.containsKey('key26')).toBeFalsy();
    });
  }


() => {
      const cache = newInstance(25);
      expect(cache.containsKey('key6')).toBeTruthy();
      expect(cache.containsKey('key26')).toBeFalsy();
    }


() => {
    it('can cache a value', () => {
      const cache = newInstance();
      const previous = cache.put('foo', 'bar');
      expect(previous).toBeFalsy();
      expect(cache.containsKey('foo')).toBeTruthy();
      expect(cache.get('foo')).toBe('bar');
    });

    it('can cache a value and return the previous value if it exists', () => {
      const cache = newInstance();
      cache.put('foo', 'oldValue');
      const previous = cache.put('foo', 'bar');
      expect(previous).toBe('oldValue');
      expect(cache.containsKey('foo')).toBeTruthy();
      expect(cache.get('foo')).toBe('bar');
    });
  }


() => {
      const cache = newInstance();
      const previous = cache.put('foo', 'bar');
      expect(previous).toBeFalsy();
      expect(cache.containsKey('foo')).toBeTruthy();
      expect(cache.get('foo')).toBe('bar');
    }


() => {
      const cache = newInstance();
      cache.put('foo', 'oldValue');
      const previous = cache.put('foo', 'bar');
      expect(previous).toBe('oldValue');
      expect(cache.containsKey('foo')).toBeTruthy();
      expect(cache.get('foo')).toBe('bar');
    }


() => {
    it('can return a cached value', () => {
      const cache = newInstance(8);
      expect(cache.get('key3')).toBe('value3');
    });

    it('will not return an expired value', async () => {
      const cache = newInstance();
      cache.put('foo', 'oldValue', 1);
      await delay();
      expect(cache.get('foo')).toBeFalsy();
    });
  }


() => {
      const cache = newInstance(8);
      expect(cache.get('key3')).toBe('value3');
    }


async () => {
      const cache = newInstance();
      cache.put('foo', 'oldValue', 1);
      await delay();
      expect(cache.get('foo')).toBeFalsy();
    }


() => {
    it('can remove and return a cached value', () => {
      const cache = newInstance(8);
      const result = cache.remove('key2');
      expect(result).toBe('value2');
      expect(cache.size()).toBe(7);
    });
  }


() => {
      const cache = newInstance(8);
      const result = cache.remove('key2');
      expect(result).toBe('value2');
      expect(cache.size()).toBe(7);
    }


export type Character = {
  id: string
  name?: string
  status: 'Alive' | 'Dead' | 'unknown'
  species?: string
  type?: string
  gender?: string
  origin?: Location
  location?: Location
  image?: string
  episode?: Episode[]
}


export type Episode = {
  id: string
  name: string
  air_date: string
  episode: string
  characters: Character[]
}


export type Episodes = {
  info: Info
  results: Episode[]
}


export type Info = {
  count: number
  pages: number
  next: number
  prev: number
}


export type Location = {
  id?: number
  name?: string
  type?: string
  dimension?: string
  residents?: Character[]
}


export type Locations = {
  info: Info
  results: Location[]
}


function getTableHeight() {
      let panelHeight = ctrl.height;

      if (pageCount > 1) {
        panelHeight -= 26;
      }

      return panelHeight - 31 + 'px';
    }


function appendCards(cardElem: JQuery) {
      ctrl.renderer.setTable(data);
      cardElem.empty();
      cardElem.html(ctrl.renderer.render(ctrl.pageIndex));
    }


// function switchPage(e: any) {
//   const el = $(e.currentTarget);
//   ctrl.pageIndex = parseInt(el.text(), 10) - 1;
//   renderPanel();
// }
function appendPaginationControls(footerElem: JQuery) {
      footerElem.empty();

      const pageSize = panel.pageSize || 100;
      pageCount = Math.ceil(data.rows.length / pageSize);
      if (pageCount === 1) {
        return;
      }

      const startPage = Math.max(ctrl.pageIndex - 3, 0);
      const endPage = Math.min(pageCount, startPage + 9);

      const paginationList = $('

');

      for (let i = startPage; i < endPage; i++) {
        const activeClass = i === ctrl.pageIndex ? 'active' : '';
        const pageLinkElem = $(
          '
' + (i + 1) + '
'
        );
        paginationList.append(pageLinkElem);
      }

      footerElem.append(paginationList);
    }


function renderPanel() {
      const rootElem = elem.find('.app-card-panel-scroll');
      const cardElem = elem.find('.app-cards');
      const footerElem = elem.find('.app-card-panel-footer');

      elem.css({ 'font-size': panel.fontSize });

      appendCards(cardElem);
      appendPaginationControls(footerElem);

      rootElem.css({ 'max-height': getTableHeight() });
    }


function iFrameModal(e: JQueryEventObject) {
      e.preventDefault();
      e.stopPropagation();

      const el = $(e.currentTarget);

      const url = el.prop('href') || 'href??';

      const template = `<i-frame-modal dismiss="dismiss()" url="model.url"></i-frame-modal>`;

      appEvents.emit('show-modal', {
        templateHtml: template,
        modalClass: 'modal--iframe',
        model: {
          url: url,
        },
      });
    }


(variable: any) => {
      if (variable.name === 'timeRange') {
        for (let i = 0, l = this.panel.timeRanges.length; i < l; i++) {
          const element = this.panel.timeRanges[i];
          if (variable.current.value === element.option) {
            this.panel.timeRange = element;
            break;
          }
        }
      }
    }


(anno: any) => {
          this.loading = false;
          this.dataRaw = anno;
          this.pageIndex = 0;
          this.render();
          return { data: this.dataRaw }; // Not used
        }


(variable: any) => {
      if (variable.name === 'timeRange') {
        this.variableSrv.setOptionAsCurrent(variable, {
          text: timeRange.option,
          value: timeRange.option,
          selected: true,
        });
        this.variableSrv.updateUrlParamsWithCurrentVariables();
        this.timeSrv.refreshDashboard();
      }
    }


() => {
      drilldownTooltip.detach();
    }


e => {
      const target = $(e.target);
      let linkTT;

      if (target.data('linkTooltipMouseover') !== undefined) {
        linkTT = target;
      } else {
        linkTT = target.parents('[data-link-tooltip-mouseover]');
      }

      if (linkTT.length < 1) {
        drilldownTooltip.detach();
        return;
      }

      const originalTitle = linkTT.data('originalTitle');

      if (originalTitle !== undefined && originalTitle !== '') {
        drilldownTooltip.text(originalTitle);
        drilldownTooltip.place_tt(e.pageX - 10, e.pageY + 20);
      }
    }


(renderData: any) => {
      data = renderData || data;
      if (data) {
        renderPanel();
      }
      ctrl.renderingCompleted();
    }


setCardMode(mode: string) {
    this.cardMode = mode ? 'mode-' + mode : 'mode-card';
  }


onInitEditMode() {
    this.addEditorTab('Options', tablePanelEditor, 2);
    this.addEditorTab('Column Styles', columnOptionsTab, 3);
  }


onInitPanelActions(actions: any[]) {
    actions.push({ text: 'Export CSV', click: 'ctrl.exportCsv()' });
  }


issueQueries(datasource: any) {
    this.pageIndex = 0;

    if (this.panel.transform === 'annotations') {
      return this.annotationsSrv
        .getAnnotations({
          dashboard: this.dashboard,
          panel: this.panel,
          range: this.range,
        })
        .then((anno: any) => {
          this.loading = false;
          this.dataRaw = anno;
          this.pageIndex = 0;
          this.render();
          return { data: this.dataRaw }; // Not used
        });
    }

    return super.issueQueries(datasource);
  }


onDataError(err: any) {
    this.dataRaw = [];
    this.render();
  }


onDataReceived(dataList: any) {
    this.dataRaw = dataList;
    this.pageIndex = 0;

    // automatically correct transform mode based on data
    if (this.dataRaw && this.dataRaw.length) {
      if (isTableData(this.dataRaw[0])) {
        this.panel.transform = 'table';
      } else {
        if (this.dataRaw[0].type === 'docs') {
          this.panel.transform = 'json';
        } else {
          if (this.panel.transform === 'table' || this.panel.transform === 'json') {
            this.panel.transform = 'timeseries_to_rows';
          }
        }
      }
    }

    this.render();
  }


onFilter() {
    this.render();
  }


render() {
    this.table = transformDataToTable(this.dataRaw, this.panel);
    this.table.sort(this.panel.sort);

    this.renderer = new AppCardRenderer(
      this.panel,
      this.table,
      this.dashboard.isTimezoneUtc(),
      this.$sanitize,
      this.templateSrv,
      config.theme.type,
      this.panel.appNameFilter,
      ''
    );

    return super.render(this.table);
  }


toggleColumnSort(col: any, colIndex: any) {
    // remove sort flag from current column
    if (this.table.columns[this.panel.sort.col]) {
      this.table.columns[this.panel.sort.col].sort = false;
    }

    if (this.panel.sort.col === colIndex) {
      if (this.panel.sort.desc) {
        this.panel.sort.desc = false;
      } else {
        this.panel.sort.col = null;
      }
    } else {
      this.panel.sort.col = colIndex;
      this.panel.sort.desc = true;
    }
    this.render();

    return false;
  }


setTimeRange(timeRange: any, index: any) {
    // update panel
    this.panel.timeRange = timeRange;

    // update time variable
    // this.timeSrv.setTime({
    //   from: timeRange.value,
    //   to: 'now'
    // });

    // update custom time variable
    this.variableSrv.variables.forEach((variable: any) => {
      if (variable.name === 'timeRange') {
        this.variableSrv.setOptionAsCurrent(variable, {
          text: timeRange.option,
          value: timeRange.option,
          selected: true,
        });
        this.variableSrv.updateUrlParamsWithCurrentVariables();
        this.timeSrv.refreshDashboard();
      }
    });
  }


exportCsv() {
    const scope = this.$scope.$new(true);
    scope.tableData = this.renderer.render_values();
    scope.panel = 'table';
    this.publishAppEvent(CoreEvents.showModal, {
      templateHtml: '<export-data-modal panel="panel" data="tableData"></export-data-modal>',
      scope,
      modalClass: 'modal--narrow',
    });
  }


link(scope: any, elem: JQuery, attrs: any, ctrl: AppCardPanelCtrl) {
    let data: any;
    const panel = ctrl.panel;
    let pageCount = 0;

    function getTableHeight() {
      let panelHeight = ctrl.height;

      if (pageCount > 1) {
        panelHeight -= 26;
      }

      return panelHeight - 31 + 'px';
    }

    function appendCards(cardElem: JQuery) {
      ctrl.renderer.setTable(data);
      cardElem.empty();
      cardElem.html(ctrl.renderer.render(ctrl.pageIndex));
    }

    // function switchPage(e: any) {
    //   const el = $(e.currentTarget);
    //   ctrl.pageIndex = parseInt(el.text(), 10) - 1;
    //   renderPanel();
    // }

    function appendPaginationControls(footerElem: JQuery) {
      footerElem.empty();

      const pageSize = panel.pageSize || 100;
      pageCount = Math.ceil(data.rows.length / pageSize);
      if (pageCount === 1) {
        return;
      }

      const startPage = Math.max(ctrl.pageIndex - 3, 0);
      const endPage = Math.min(pageCount, startPage + 9);

      const paginationList = $('

');

      for (let i = startPage; i < endPage; i++) {
        const activeClass = i === ctrl.pageIndex ? 'active' : '';
        const pageLinkElem = $(
          '
' + (i + 1) + '
'
        );
        paginationList.append(pageLinkElem);
      }

      footerElem.append(paginationList);
    }

    function renderPanel() {
      const rootElem = elem.find('.app-card-panel-scroll');
      const cardElem = elem.find('.app-cards');
      const footerElem = elem.find('.app-card-panel-footer');

      elem.css({ 'font-size': panel.fontSize });

      appendCards(cardElem);
      appendPaginationControls(footerElem);

      rootElem.css({ 'max-height': getTableHeight() });
    }

    function iFrameModal(e: JQueryEventObject) {
      e.preventDefault();
      e.stopPropagation();

      const el = $(e.currentTarget);

      const url = el.prop('href') || 'href??';

      const template = `<i-frame-modal dismiss="dismiss()" url="model.url"></i-frame-modal>`;

      appEvents.emit('show-modal', {
        templateHtml: template,
        modalClass: 'modal--iframe',
        model: {
          url: url,
        },
      });
    }

    // wire up iframe modal
    elem.on('click', '[data-iframe]', iFrameModal);

    // hook up link tooltips
    elem.tooltip({
      selector: '[data-link-tooltip]',
    });

    // wire up mouseover tooltips
    const drilldownTooltip = $('
hello
"');

    elem.mouseleave(() => {
      drilldownTooltip.detach();
    });

    elem.mousemove(e => {
      const target = $(e.target);
      let linkTT;

      if (target.data('linkTooltipMouseover') !== undefined) {
        linkTT = target;
      } else {
        linkTT = target.parents('[data-link-tooltip-mouseover]');
      }

      if (linkTT.length < 1) {
        drilldownTooltip.detach();
        return;
      }

      const originalTitle = linkTT.data('originalTitle');

      if (originalTitle !== undefined && originalTitle !== '') {
        drilldownTooltip.text(originalTitle);
        drilldownTooltip.place_tt(e.pageX - 10, e.pageY + 20);
      }
    });

    // function addFilterClicked(e: any) {
    //   const filterData = $(e.currentTarget).data();
    //   const options = {
    //     datasource: panel.datasource,
    //     key: data.columns[filterData.column].text,
    //     value: data.rows[filterData.row][filterData.column],
    //     operator: filterData.operator,
    //   };

    //   ctrl.variableSrv.setAdhocFilter(options);
    // }

    // elem.on('click', '.table-panel-page-link', switchPage);
    // elem.on('click', '.table-panel-filter-link', addFilterClicked);

    // const unbindDestroy = scope.$on('$destroy', () => {
    //   elem.off('click', '.table-panel-page-link');
    //   elem.off('click', '.table-panel-filter-link');
    //   unbindDestroy();
    // });

    ctrl.events.on('render', (renderData: any) => {
      data = renderData || data;
      if (data) {
        renderPanel();
      }
      ctrl.renderingCompleted();
    });
  }


({ className, ...props }, ref) => {
    const classNames = useClassNameMapper();
    return (
      <Image
        ref={ref}
        {


classNames(className, 'figure-img')


r => {
                if (!this._errorFlag) {
                    this._loadResponse(r);
                }

            }


e => {
                this._errorFlag = true;
                this._setReadyState(this.DONE, e);
            }


() => r.content.toString()


() => this.responseText


(handler) => handler !== toDetach


(handler) => {
            handler(...args);
        }


export class FormData {
    private _data: Map<string, any>;

    constructor() {
        this._data = new Map<string, any>();
    }

    append(name: string, value: any) {
        this._data.set(name, value);
    }

    toString(): string {
        let arr = new Array<string>();

        this._data.forEach(function (value, name, map) {
            arr.push(`${encodeURIComponent(name)}=${encodeURIComponent(value)}`);
        });

        return arr.join("&");
    }
}


public open(method: string, url: string, async?: boolean, user?: string, password?: string) {
        if (types.isString(method) && types.isString(url)) {
            this._options = { url: url, method: method };
            this._options.headers = {};

            if (types.isString(user)) {
                this._options.headers["user"] = user;
            }

            if (types.isString(password)) {
                this._options.headers["password"] = password;
            }

            this._setReadyState(this.OPENED);
        }
    }


public abort() {
        this._errorFlag = true;

        this._response = null;
        this._responseTextReader = null;
        this._headers = null;
        this._status = null;

        if (this._readyState === this.UNSENT || this._readyState === this.OPENED || this._readyState === this.DONE) {
            this._readyState = this.UNSENT;
        } else {
            this._setReadyState(this.DONE);
        }
    }


public send(data?: any) {
        this._errorFlag = false;
        this._response = null;
        this._responseTextReader = null;
        this._headers = null;
        this._status = null;

        if (types.isDefined(this._options)) {
            if (types.isString(data) && this._options.method !== 'GET') {
                //The Android Java HTTP lib throws an exception if we provide a
                //a request body for GET requests, so we avoid doing that.
                //Browser implementations silently ignore it as well.
                this._options.content = data;
            } else if (data instanceof FormData) {
                this._options.content = (<FormData>data).toString();
            }

            http.request(this._options).then(r => {
                if (!this._errorFlag) {
                    this._loadResponse(r);
                }

            }).catch(e => {
                this._errorFlag = true;
                this._setReadyState(this.DONE, e);
            });
        }
    }


private _loadResponse(r) {
        this._status = r.statusCode;
        this._response = r.content.raw;

        this._headers = r.headers;
        this._setReadyState(this.HEADERS_RECEIVED);

        this._setReadyState(this.LOADING);

        this._setResponseType();

        this._responseTextReader = () => r.content.toString();
        this._addToStringOnResponse();

        if (this.responseType === XMLHttpRequestResponseType.json) {
            this._response = JSON.parse(this.responseText);
        }

        this._setReadyState(this.DONE);
    }


private _addToStringOnResponse() {
        // Add toString() method to ease debugging and
        // make Angular2 response.text() method work properly.
        if (types.isObject(this.response)) {
            Object.defineProperty(this._response, "toString", {
                configurable: true,
                enumerable: false,
                writable: true,
                value: () => this.responseText
            });
        }
    }


private _setResponseType() {
        const header = this.getResponseHeader('Content-Type');
        const contentType = header && header.toLowerCase();

        if (contentType) {
            if (contentType.indexOf('application/json') >= 0 || contentType.indexOf('+json') >= 0) {
                this.responseType = XMLHttpRequestResponseType.json;
            } else if (contentType.indexOf('text/plain') >= 0) {
                this.responseType = XMLHttpRequestResponseType.text;
            }
        } else {
            this.responseType = XMLHttpRequestResponseType.text;
        }
    }


public addEventListener(eventName: string, handler: Function) {
        if (eventName !== 'load' && eventName !== 'error') {
            throw new Error('Event not supported: ' + eventName);
        }

        let handlers = this._listeners.get(eventName) || [];
        handlers.push(handler);
        this._listeners.set(eventName, handlers);
    }


public removeEventListener(eventName: string, toDetach: Function) {
        let handlers = this._listeners.get(eventName) || [];
        handlers = handlers.filter((handler) => handler !== toDetach);
        this._listeners.set(eventName, handlers);
    }


private emitEvent(eventName: string, ...args: Array<any>) {
        let handlers = this._listeners.get(eventName) || [];
        handlers.forEach((handler) => {
            handler(...args);
        });
    }


public setRequestHeader(header: string, value: string) {
        if (types.isDefined(this._options) && types.isString(header) && types.isString(value)) {
            this._options.headers[header] = value;
        }
    }


public getAllResponseHeaders(): string {
        if (this._readyState < 2 || this._errorFlag) {
            return "";
        }

        let result = "";

        for (let i in this._headers) {
            // Cookie headers are excluded
            if (i !== "set-cookie" && i !== "set-cookie2") {
                result += i + ": " + this._headers[i] + "\r\n";
            }
        }
        return result.substr(0, result.length - 2);
    }


public getResponseHeader(header: string): string {
        if (types.isString(header) && this._readyState > 1
            && this._headers
            && !this._errorFlag
        ) {
            header = header.toLowerCase();
            for (let i in this._headers) {
                if (i.toLowerCase() === header) {
                    return this._headers[i];
                }
            }
        }

        return null;
    }


public overrideMimeType(mime: string) {
        //
    }


private _setReadyState(value: number, error?: any) {
        if (this._readyState !== value) {
            this._readyState = value;

            if (types.isFunction(this.onreadystatechange)) {
                this.onreadystatechange();
            }
        }

        if (this._readyState === this.DONE) {
            if (this._errorFlag) {
                if (types.isFunction(this.onerror)) {
                    this.onerror(error);
                }
                this.emitEvent('error', error);
            } else {
                if (types.isFunction(this.onload)) {
                    this.onload();
                }
                this.emitEvent('load');
            }
        }
    }


append(name: string, value: any) {
        this._data.set(name, value);
    }


toString(): string {
        let arr = new Array<string>();

        this._data.forEach(function (value, name, map) {
            arr.push(`${encodeURIComponent(name)}=${encodeURIComponent(value)}`);
        });

        return arr.join("&");
    }


() => {
  cy.get(tid('new-trade')).click();
}


() => {
    cypressVisitWithWeb3();
    WalletConnection.connect();
    WalletConnection.isConnected();
    Tab.instant();
    instantForm();
  }


() => {
    it('without proxy', () => {
      const from = 'ETH';
      const to = 'DAI';
      const willPay = '0.357';
      const willReceive = '100';
      const price = '280 ETH/DAI';

      const trade = new Trade();
      trade.buy(to).amount(willReceive);

      trade.expectToPay(`${willPay}`);

      const finalization = trade.execute();

      const summary = finalization.shouldCreateProxy().shouldCommitATrade(willPay, from, willReceive, to);

      summary.expectProxyBeingCreated();
      summary.expectBought(willReceive, to);
      summary.expectSold(willPay, from);
      summary.expectPriceOf(
        price,
      ); /* TODO .then(() => {
        verifySendTxs([{
          from: ACCOUNT_3_PUBLIC,
          to: INSTANT_PROXY_CREATE_AND_EXECUTE_ADDRESS,
          value: toHex('374999999999999999'),
        }]);
      });*/
    });

    it('with proxy', () => {
      const to = 'DAI';
      const from = 'ETH';
      const willReceive = '100';

      const trade = new Trade();
      trade.buy(to).amount(willReceive);

      trade.execute();

      nextTrade();

      const willReceiveMore = '500';
      const willPay = '1.806';
      const price = '276.78 ETH/DAI';

      const secondTrade = new Trade();
      secondTrade.buy(to).amount(willReceiveMore);

      secondTrade.expectToPay(willPay);

      const finalization = trade.execute();

      const summary = finalization.shouldNotCreateProxy().shouldCommitATrade(willPay, from, willReceiveMore, to);

      summary.expectProxyNotBeingCreated();
      summary.expectBought(willReceiveMore, to);
      summary.expectSold(willPay, from);
      summary.expectPriceOf(price);
    });
  }


() => {
      const from = 'ETH';
      const to = 'DAI';
      const willPay = '0.357';
      const willReceive = '100';
      const price = '280 ETH/DAI';

      const trade = new Trade();
      trade.buy(to).amount(willReceive);

      trade.expectToPay(`${willPay}`);

      const finalization = trade.execute();

      const summary = finalization.shouldCreateProxy().shouldCommitATrade(willPay, from, willReceive, to);

      summary.expectProxyBeingCreated();
      summary.expectBought(willReceive, to);
      summary.expectSold(willPay, from);
      summary.expectPriceOf(
        price,
      ); /* TODO .then(() => {
        verifySendTxs([{
          from: ACCOUNT_3_PUBLIC,
          to: INSTANT_PROXY_CREATE_AND_EXECUTE_ADDRESS,
          value: toHex('374999999999999999'),
        }]);
      });*/
    }


() => {
      const to = 'DAI';
      const from = 'ETH';
      const willReceive = '100';

      const trade = new Trade();
      trade.buy(to).amount(willReceive);

      trade.execute();

      nextTrade();

      const willReceiveMore = '500';
      const willPay = '1.806';
      const price = '276.78 ETH/DAI';

      const secondTrade = new Trade();
      secondTrade.buy(to).amount(willReceiveMore);

      secondTrade.expectToPay(willPay);

      const finalization = trade.execute();

      const summary = finalization.shouldNotCreateProxy().shouldCommitATrade(willPay, from, willReceiveMore, to);

      summary.expectProxyNotBeingCreated();
      summary.expectBought(willReceiveMore, to);
      summary.expectSold(willPay, from);
      summary.expectPriceOf(price);
    }


() => {
    it('without proxy and allowance', () => {
      const from = 'DAI';
      const to = 'ETH';
      const willPay = '37.02';
      const willReceive = '0.123';
      const price = '301 ETH/DAI';

      Trade.swapTokens();

      const trade = new Trade();
      trade.buy(to).amount(willReceive);

      trade.expectToPay(`${willPay}`);

      const finalization = trade.execute();

      finalization.shouldCreateProxy().expectSuccess();
      finalization.shouldSetAllowanceFor(from).expectSuccess();

      const summary = finalization.shouldCommitATrade(willPay, from, willReceive, to);

      summary.expectBought(willReceive, to);
      summary.expectSold(willPay, from);
      summary.expectPriceOf(price);
    });

    it('with proxy and no allowance', () => {
      const from = 'ETH';
      const to = 'DAI';
      const willPay = '1';

      const trade = new Trade();
      trade.buy(to);
      trade.sell(from).amount(willPay);
      trade.execute();

      nextTrade();

      Trade.swapTokens();

      const nextFrom = 'DAI';
      const nextTo = 'ETH';
      const nextWillPay = '37.02';
      const nextWillReceive = '0.123';
      const price = '301 ETH/DAI';

      const secondTrade = new Trade();
      secondTrade.sell(nextFrom);
      secondTrade.buy(nextTo).amount(nextWillReceive);

      secondTrade.expectToReceive(`${nextWillReceive}`);

      const nextFinalization = secondTrade.execute();

      nextFinalization.shouldNotCreateProxy().shouldSetAllowanceFor(nextFrom).expectSuccess();

      const finalSummary = nextFinalization.shouldCommitATrade(nextWillPay, nextFrom, nextWillReceive, nextTo);

      finalSummary.expectProxyNotBeingCreated();
      finalSummary.expectBought(nextWillReceive, nextTo);
      finalSummary.expectSold(nextWillPay, nextFrom);
      finalSummary.expectPriceOf(price);
    });

    it('with proxy and allowance', () => {
      const from = 'DAI';
      const to = 'ETH';
      const willPay = '37.02';
      const willReceive = '0.123';
      const price = '301 ETH/DAI';

      Trade.swapTokens();

      const trade = new Trade();
      trade.sell(from);
      trade.buy(to).amount(willReceive);
      trade.execute();

      nextTrade();

      const newTrade = new Trade();
      trade.sell(from);
      trade.buy(to).amount(willReceive);

      const finalization = newTrade.execute();

      const summary = finalization
        .shouldNotCreateProxy()
        .shouldNotSetAllowance()
        .shouldCommitATrade(willPay, from, willReceive, to);

      summary.expectBought(willReceive, to);
      summary.expectSold(willPay, from);
      summary.expectPriceOf(price);
    });
  }


() => {
      const from = 'DAI';
      const to = 'ETH';
      const willPay = '37.02';
      const willReceive = '0.123';
      const price = '301 ETH/DAI';

      Trade.swapTokens();

      const trade = new Trade();
      trade.buy(to).amount(willReceive);

      trade.expectToPay(`${willPay}`);

      const finalization = trade.execute();

      finalization.shouldCreateProxy().expectSuccess();
      finalization.shouldSetAllowanceFor(from).expectSuccess();

      const summary = finalization.shouldCommitATrade(willPay, from, willReceive, to);

      summary.expectBought(willReceive, to);
      summary.expectSold(willPay, from);
      summary.expectPriceOf(price);
    }


() => {
      const from = 'ETH';
      const to = 'DAI';
      const willPay = '1';

      const trade = new Trade();
      trade.buy(to);
      trade.sell(from).amount(willPay);
      trade.execute();

      nextTrade();

      Trade.swapTokens();

      const nextFrom = 'DAI';
      const nextTo = 'ETH';
      const nextWillPay = '37.02';
      const nextWillReceive = '0.123';
      const price = '301 ETH/DAI';

      const secondTrade = new Trade();
      secondTrade.sell(nextFrom);
      secondTrade.buy(nextTo).amount(nextWillReceive);

      secondTrade.expectToReceive(`${nextWillReceive}`);

      const nextFinalization = secondTrade.execute();

      nextFinalization.shouldNotCreateProxy().shouldSetAllowanceFor(nextFrom).expectSuccess();

      const finalSummary = nextFinalization.shouldCommitATrade(nextWillPay, nextFrom, nextWillReceive, nextTo);

      finalSummary.expectProxyNotBeingCreated();
      finalSummary.expectBought(nextWillReceive, nextTo);
      finalSummary.expectSold(nextWillPay, nextFrom);
      finalSummary.expectPriceOf(price);
    }


() => {
      const from = 'DAI';
      const to = 'ETH';
      const willPay = '37.02';
      const willReceive = '0.123';
      const price = '301 ETH/DAI';

      Trade.swapTokens();

      const trade = new Trade();
      trade.sell(from);
      trade.buy(to).amount(willReceive);
      trade.execute();

      nextTrade();

      const newTrade = new Trade();
      trade.sell(from);
      trade.buy(to).amount(willReceive);

      const finalization = newTrade.execute();

      const summary = finalization
        .shouldNotCreateProxy()
        .shouldNotSetAllowance()
        .shouldCommitATrade(willPay, from, willReceive, to);

      summary.expectBought(willReceive, to);
      summary.expectSold(willPay, from);
      summary.expectPriceOf(price);
    }


() => {
    it('without proxy and allowance', () => {
      const from = 'DAI';
      const to = 'WETH';
      const willPay = '150.50';
      const willReceive = '0.5';
      const price = '301 WETH/DAI';

      Trade.swapTokens();

      const trade = new Trade();
      trade.sell(from);
      trade.buy(to).amount(willReceive);

      trade.expectToPay(`${willPay}`);

      const finalization = trade.execute();

      finalization.shouldCreateProxy().expectSuccess();
      finalization.shouldSetAllowanceFor(from).expectSuccess();

      const summary = finalization.shouldCommitATrade(willPay, from, willReceive, to);

      summary.expectBought(willReceive, to);
      summary.expectSold(willPay, from);
      summary.expectPriceOf(price);
    });

    it('with proxy and no allowance', () => {
      const from = 'ETH';
      const to = 'DAI';
      const willPay = '1';

      const trade = new Trade();
      trade.buy(to);
      trade.sell(from).amount(willPay);
      trade.execute();

      nextTrade();

      Trade.swapTokens();

      const nextFrom = 'DAI';
      const nextTo = 'WETH';
      const nextWillPay = '150.50';
      const nextWillReceive = '0.5';
      const price = '301 WETH/DAI';

      const secondTrade = new Trade();
      secondTrade.sell(nextFrom);
      secondTrade.buy(nextTo).amount(nextWillReceive);

      secondTrade.expectToReceive(`${nextWillReceive}`);

      const nextFinalization = secondTrade.execute();

      nextFinalization.shouldNotCreateProxy().shouldSetAllowanceFor(nextFrom).expectSuccess();

      const finalSummary = nextFinalization.shouldCommitATrade(nextWillPay, nextFrom, nextWillReceive, nextTo);

      finalSummary.expectProxyNotBeingCreated();
      finalSummary.expectBought(nextWillReceive, nextTo);
      finalSummary.expectSold(nextWillPay, nextFrom);
      finalSummary.expectPriceOf(price);
    });

    it('with proxy and allowance', () => {
      const from = 'DAI';
      const to = 'WETH';
      const willReceive = '0.3';
      const price = '301 WETH/DAI';

      Trade.swapTokens();

      const trade = new Trade();
      trade.sell(from);
      trade.buy(to).amount(willReceive);
      trade.execute();

      nextTrade();

      const nextWillReceive = '0.1';
      const nextWillPay = '30.10';

      const newTrade = new Trade();
      newTrade.sell(from);
      newTrade.buy(to).amount(nextWillReceive);

      const finalization = newTrade.execute();

      const summary = finalization
        .shouldNotCreateProxy()
        .shouldNotSetAllowance()
        .shouldCommitATrade(nextWillPay, from, nextWillReceive, to);

      summary.expectBought(nextWillReceive, to);
      summary.expectSold(nextWillPay, from);
      summary.expectPriceOf(price);
    });
  }


() => {
      const from = 'DAI';
      const to = 'WETH';
      const willPay = '150.50';
      const willReceive = '0.5';
      const price = '301 WETH/DAI';

      Trade.swapTokens();

      const trade = new Trade();
      trade.sell(from);
      trade.buy(to).amount(willReceive);

      trade.expectToPay(`${willPay}`);

      const finalization = trade.execute();

      finalization.shouldCreateProxy().expectSuccess();
      finalization.shouldSetAllowanceFor(from).expectSuccess();

      const summary = finalization.shouldCommitATrade(willPay, from, willReceive, to);

      summary.expectBought(willReceive, to);
      summary.expectSold(willPay, from);
      summary.expectPriceOf(price);
    }


() => {
      const from = 'ETH';
      const to = 'DAI';
      const willPay = '1';

      const trade = new Trade();
      trade.buy(to);
      trade.sell(from).amount(willPay);
      trade.execute();

      nextTrade();

      Trade.swapTokens();

      const nextFrom = 'DAI';
      const nextTo = 'WETH';
      const nextWillPay = '150.50';
      const nextWillReceive = '0.5';
      const price = '301 WETH/DAI';

      const secondTrade = new Trade();
      secondTrade.sell(nextFrom);
      secondTrade.buy(nextTo).amount(nextWillReceive);

      secondTrade.expectToReceive(`${nextWillReceive}`);

      const nextFinalization = secondTrade.execute();

      nextFinalization.shouldNotCreateProxy().shouldSetAllowanceFor(nextFrom).expectSuccess();

      const finalSummary = nextFinalization.shouldCommitATrade(nextWillPay, nextFrom, nextWillReceive, nextTo);

      finalSummary.expectProxyNotBeingCreated();
      finalSummary.expectBought(nextWillReceive, nextTo);
      finalSummary.expectSold(nextWillPay, nextFrom);
      finalSummary.expectPriceOf(price);
    }


() => {
      const from = 'DAI';
      const to = 'WETH';
      const willReceive = '0.3';
      const price = '301 WETH/DAI';

      Trade.swapTokens();

      const trade = new Trade();
      trade.sell(from);
      trade.buy(to).amount(willReceive);
      trade.execute();

      nextTrade();

      const nextWillReceive = '0.1';
      const nextWillPay = '30.10';

      const newTrade = new Trade();
      newTrade.sell(from);
      newTrade.buy(to).amount(nextWillReceive);

      const finalization = newTrade.execute();

      const summary = finalization
        .shouldNotCreateProxy()
        .shouldNotSetAllowance()
        .shouldCommitATrade(nextWillPay, from, nextWillReceive, to);

      summary.expectBought(nextWillReceive, to);
      summary.expectSold(nextWillPay, from);
      summary.expectPriceOf(price);
    }


/**
 * Multiplies two two-dimensional affine matrices
 * https://jsperf.com/array-vs-object-affine-matrices/
 * @param m1 Left-side matrix array
 * @param m2 Right-side matrix array
 */
export declare function multiplyAffine2d(m1: number[], m2: number[]): number[];


/**
 * QR decomposition using the Gram–Schmidt process.
 * Decomposes a css matrix to simple transforms - translate, rotate and scale.
 * @param matrix The css matrix array to decompose.
 */
export function decompose2DTransformMatrix(matrix: number[])
    : TransformFunctionsInfo;


({ children }: ThemeNameConsumerProps) => {
  return children(useThemeName());
}


interface ThemeNameConsumerProps {
  children(name: string): ReactElement;
}


(props: SubMenuProps) => {
  const {
    style,
    className,

    title,
    eventKey,

    disabled,
    internalPopupClose,

    children,

    // Icons
    itemIcon,
    expandIcon,

    // Popup
    popupClassName,
    popupOffset,

    // Events
    onClick,
    onMouseEnter,
    onMouseLeave,
    onTitleClick,
    onTitleMouseEnter,
    onTitleMouseLeave,

    ...restProps
  } = props;

  const domDataId = useMenuId(eventKey);

  const {
    prefixCls,
    mode,
    openKeys,

    // Disabled
    disabled: contextDisabled,
    overflowDisabled,

    // ActiveKey
    activeKey,

    // SelectKey
    selectedKeys,

    // Icon
    itemIcon: contextItemIcon,
    expandIcon: contextExpandIcon,

    // Events
    onItemClick,
    onOpenChange,

    onActive,
  } = React.useContext(MenuContext);

  const { isSubPathKey } = React.useContext(PathUserContext);
  const connectedPath = useFullPath();

  const subMenuPrefixCls = `${prefixCls}-submenu`;
  const mergedDisabled = contextDisabled || disabled;
  const elementRef = React.useRef<HTMLDivElement>();
  const popupRef = React.useRef<HTMLUListElement>();

  // ================================ Icon ================================
  const mergedItemIcon = itemIcon || contextItemIcon;
  const mergedExpandIcon = expandIcon || contextExpandIcon;

  // ================================ Open ================================
  const originOpen = openKeys.includes(eventKey);
  const open = !overflowDisabled && originOpen;

  // =============================== Select ===============================
  const childrenSelected = isSubPathKey(selectedKeys, eventKey);

  // =============================== Active ===============================
  const { active, ...activeProps } = useActive(
    eventKey,
    mergedDisabled,
    onTitleMouseEnter,
    onTitleMouseLeave,
  );

  // Fallback of active check to avoid hover on menu title or disabled item
  const [childrenActive, setChildrenActive] = React.useState(false);

  const triggerChildrenActive = (newActive: boolean) => {
    if (!mergedDisabled) {
      setChildrenActive(newActive);
    }
  };

  const onInternalMouseEnter: React.MouseEventHandler<HTMLLIElement> = domEvent => {
    triggerChildrenActive(true);

    onMouseEnter?.({
      key: eventKey,
      domEvent,
    });
  };

  const onInternalMouseLeave: React.MouseEventHandler<HTMLLIElement> = domEvent => {
    triggerChildrenActive(false);

    onMouseLeave?.({
      key: eventKey,
      domEvent,
    });
  };

  const mergedActive = React.useMemo(() => {
    if (active) {
      return active;
    }

    if (mode !== 'inline') {
      return childrenActive || isSubPathKey([activeKey], eventKey);
    }

    return false;
  }, [mode, active, activeKey, childrenActive, eventKey, isSubPathKey]);

  // ========================== DirectionStyle ==========================
  const directionStyle = useDirectionStyle(connectedPath.length);

  // =============================== Events ===============================
  // >>>> Title click
  const onInternalTitleClick: React.MouseEventHandler<HTMLElement> = e => {
    // Skip if disabled
    if (mergedDisabled) {
      return;
    }

    onTitleClick?.({
      key: eventKey,
      domEvent: e,
    });

    // Trigger open by click when mode is `inline`
    if (mode === 'inline') {
      onOpenChange(eventKey, !originOpen);
    }
  };

  // >>>> Context for children click
  const onMergedItemClick = useMemoCallback((info: MenuInfo) => {
    onClick?.(warnItemProp(info));
    onItemClick(info);
  });

  // >>>>> Visible change
  const onPopupVisibleChange = (newVisible: boolean) => {
    if (mode !== 'inline') {
      onOpenChange(eventKey, newVisible);
    }
  };

  /**
   * Used for accessibility. Helper will focus element without key board.
   * We should manually trigger an active
   */
  const onInternalFocus: React.FocusEventHandler<HTMLDivElement> = () => {
    onActive(eventKey);
  };

  // =============================== Render ===============================
  const popupId = domDataId && `${domDataId}-popup`;

  // >>>>> Title
  let titleNode: React.ReactElement = (
    <div
      role="menuitem"
      style={directionStyle}
      className={`${subMenuPrefixCls}-title`}


(newActive: boolean) => {
    if (!mergedDisabled) {
      setChildrenActive(newActive);
    }
  }


domEvent => {
    triggerChildrenActive(true);

    onMouseEnter?.({
      key: eventKey,
      domEvent,
    });
  }


domEvent => {
    triggerChildrenActive(false);

    onMouseLeave?.({
      key: eventKey,
      domEvent,
    });
  }


() => {
    if (active) {
      return active;
    }

    if (mode !== 'inline') {
      return childrenActive || isSubPathKey([activeKey], eventKey);
    }

    return false;
  }


e => {
    // Skip if disabled
    if (mergedDisabled) {
      return;
    }

    onTitleClick?.({
      key: eventKey,
      domEvent: e,
    });

    // Trigger open by click when mode is `inline`
    if (mode === 'inline') {
      onOpenChange(eventKey, !originOpen);
    }
  }


(info: MenuInfo) => {
    onClick?.(warnItemProp(info));
    onItemClick(info);
  }


(newVisible: boolean) => {
    if (mode !== 'inline') {
      onOpenChange(eventKey, newVisible);
    }
  }


() => {
    onActive(eventKey);
  }


export interface SubMenuProps {
  style?: React.CSSProperties;
  className?: string;

  title?: React.ReactNode;
  children?: React.ReactNode;

  disabled?: boolean;
  /** @private Used for rest popup. Do not use in your prod */
  internalPopupClose?: boolean;

  /** @private Internal filled key. Do not set it directly */
  eventKey?: string;

  // >>>>> Icon
  itemIcon?: RenderIconType;
  expandIcon?: RenderIconType;

  // >>>>> Active
  onMouseEnter?: MenuHoverEventHandler;
  onMouseLeave?: MenuHoverEventHandler;

  // >>>>> Popup
  popupClassName?: string;
  popupOffset?: number[];

  // >>>>> Events
  onClick?: MenuClickEventHandler;
  onTitleClick?: (info: MenuTitleInfo) => void;
  onTitleMouseEnter?: MenuHoverEventHandler;
  onTitleMouseLeave?: MenuHoverEventHandler;

  // >>>>>>>>>>>>>>>>>>>>> Next  Round <<<<<<<<<<<<<<<<<<<<<<<
  // onDestroy?: DestroyEventHandler;
}


classNames(
          subMenuPrefixCls,
          `${subMenuPrefixCls


/**
 *
Associates a wireless device with a thing.

 */
export class AssociateWirelessDeviceWithThingCommand extends $Command<
  AssociateWirelessDeviceWithThingCommandInput,
  AssociateWirelessDeviceWithThingCommandOutput,
  IoTWirelessClientResolvedConfig
> {
  // Start section: command_properties
  // End section: command_properties

  constructor(readonly input: AssociateWirelessDeviceWithThingCommandInput) {
    // Start section: command_constructor
    super();
    // End section: command_constructor
  }

  /**
   * @internal
   */
  resolveMiddleware(
    clientStack: MiddlewareStack<ServiceInputTypes, ServiceOutputTypes>,
    configuration: IoTWirelessClientResolvedConfig,
    options?: __HttpHandlerOptions
  ): Handler<AssociateWirelessDeviceWithThingCommandInput, AssociateWirelessDeviceWithThingCommandOutput> {
    this.middlewareStack.use(getSerdePlugin(configuration, this.serialize, this.deserialize));

    const stack = clientStack.concat(this.middlewareStack);

    const { logger } = configuration;
    const clientName = "IoTWirelessClient";
    const commandName = "AssociateWirelessDeviceWithThingCommand";
    const handlerExecutionContext: HandlerExecutionContext = {
      logger,
      clientName,
      commandName,
      inputFilterSensitiveLog: AssociateWirelessDeviceWithThingRequest.filterSensitiveLog,
      outputFilterSensitiveLog: AssociateWirelessDeviceWithThingResponse.filterSensitiveLog,
    };
    const { requestHandler } = configuration;
    return stack.resolve(
      (request: FinalizeHandlerArguments<any>) =>
        requestHandler.handle(request.request as __HttpRequest, options || {}),
      handlerExecutionContext
    );
  }

  private serialize(
    input: AssociateWirelessDeviceWithThingCommandInput,
    context: __SerdeContext
  ): Promise<__HttpRequest> {
    return serializeAws_restJson1AssociateWirelessDeviceWithThingCommand(input, context);
  }

  private deserialize(
    output: __HttpResponse,
    context: __SerdeContext
  ): Promise<AssociateWirelessDeviceWithThingCommandOutput> {
    return deserializeAws_restJson1AssociateWirelessDeviceWithThingCommand(output, context);
  }

  // Start section: command_body_extra
  // End section: command_body_extra
}


export type AssociateWirelessDeviceWithThingCommandInput = AssociateWirelessDeviceWithThingRequest;


export type AssociateWirelessDeviceWithThingCommandOutput = AssociateWirelessDeviceWithThingResponse & __MetadataBearer;


/**
   * @internal
   */
resolveMiddleware(
    clientStack: MiddlewareStack<ServiceInputTypes, ServiceOutputTypes>,
    configuration: IoTWirelessClientResolvedConfig,
    options?: __HttpHandlerOptions
  ): Handler<AssociateWirelessDeviceWithThingCommandInput, AssociateWirelessDeviceWithThingCommandOutput> {
    this.middlewareStack.use(getSerdePlugin(configuration, this.serialize, this.deserialize));

    const stack = clientStack.concat(this.middlewareStack);

    const { logger } = configuration;
    const clientName = "IoTWirelessClient";
    const commandName = "AssociateWirelessDeviceWithThingCommand";
    const handlerExecutionContext: HandlerExecutionContext = {
      logger,
      clientName,
      commandName,
      inputFilterSensitiveLog: AssociateWirelessDeviceWithThingRequest.filterSensitiveLog,
      outputFilterSensitiveLog: AssociateWirelessDeviceWithThingResponse.filterSensitiveLog,
    };
    const { requestHandler } = configuration;
    return stack.resolve(
      (request: FinalizeHandlerArguments<any>) =>
        requestHandler.handle(request.request as __HttpRequest, options || {}),
      handlerExecutionContext
    );
  }


private serialize(
    input: AssociateWirelessDeviceWithThingCommandInput,
    context: __SerdeContext
  ): Promise<__HttpRequest> {
    return serializeAws_restJson1AssociateWirelessDeviceWithThingCommand(input, context);
  }


private deserialize(
    output: __HttpResponse,
    context: __SerdeContext
  ): Promise<AssociateWirelessDeviceWithThingCommandOutput> {
    return deserializeAws_restJson1AssociateWirelessDeviceWithThingCommand(output, context);
  }


() => (
  <Layout>
    <SEO />
    <AnimatedFlex


({ style, children }) => (
  <div className={styles.col}


interface ColProps extends React.HTMLProps<HTMLDivElement> {
}


export interface OptionTypeBase {
  [key: string]: any;
}


export interface GroupType<OptionType extends OptionTypeBase> {
  options: OptionsType<OptionType>;
  [key: string]: any;
}


export interface PropsWithInnerRef {
  /** The inner reference. */
  innerRef: React.Ref<any>;
}


export interface PropsWithStyles {
  /*
    Get the styles of a particular part of the select. Pass in the name of the
    property as the first argument, and the current props as the second argument.
    See the `styles` object for the properties available.
  */
  getStyles: (name: string, props: any) => {};
  theme: Theme;
}


export interface CommonProps<OptionType extends OptionTypeBase> {
  clearValue: () => void;
  className?: string;
  cx: (state: ClassNamesState | undefined, className: string | undefined) => string;
  /*
    Get the styles of a particular part of the select. Pass in the name of the
    property as the first argument, and the current props as the second argument.
    See the `styles` object for the properties available.
  */
  getStyles: (name: string, props: any) => {};
  getValue: () => ValueType<OptionType>;
  hasValue: boolean;
  isMulti: boolean;
  options: OptionsType<OptionType>;
  selectOption: (option: OptionType) => void;
  selectProps: SelectProps<OptionType>;
  setValue: (value: ValueType<OptionType>, action: ActionTypes) => void;
}


export interface ActionMeta<OptionType extends OptionTypeBase> {
  action: ActionTypes;
  name?: string;
  option?: OptionType;
  removedValue?: OptionType;
}


export interface InputActionMeta {
  action: InputActionTypes;
}


export interface ThemeSpacing {
  baseUnit: number;
  controlHeight: number;
  menuGutter: number;
}


export interface Theme {
  borderRadius: number;
  colors: { [key: string]: string };
  spacing: ThemeSpacing;
}


export type OptionsType<OptionType extends OptionTypeBase> = ReadonlyArray<OptionType>;


export type GroupedOptionsType<OptionType extends OptionTypeBase> = ReadonlyArray<GroupType<OptionType>>;


export type ValueType<OptionType extends OptionTypeBase> = OptionType | OptionsType<OptionType> | null | undefined;


export type FocusEventHandler = (event: React.FocusEvent<HTMLElement>) => void;


export type MouseEventHandler = (event: React.MouseEvent<HTMLElement>) => void;


export type KeyboardEventHandler = (event: React.KeyboardEvent<HTMLElement>) => void;


export type InnerRef = React.Ref<any>;


export type ClassNameList = string[];


export type ClassNamesState = { [key: string]: boolean } | undefined;


export type ActionTypes =
  | 'select-option'
  | 'deselect-option'
  | 'remove-value'
  | 'pop-value'
  | 'set-value'
  | 'clear'
  | 'create-option';


export type InputActionTypes =
  | 'set-value'
  | 'input-change'
  | 'input-blur'
  | 'menu-close';


export type MenuPlacement = 'auto' | 'bottom' | 'top';


export type MenuPosition = 'absolute' | 'fixed';


export type FocusDirection =
  | 'up'
  | 'down'
  | 'pageup'
  | 'pagedown'
  | 'first'
  | 'last';


export type OptionProps = PropsWithInnerRef & {
  data: any,
  id: number,
  index: number,
  isDisabled: boolean,
  isFocused: boolean,
  isSelected: boolean,
  label: string,
  onClick: MouseEventHandler,
  onMouseOver: MouseEventHandler,
  value: any,
};


(success) => {
            return success.body.result;
        }


export class IsProfileAlreadyInUseHandler implements ApiRequestHandler<IsProfileAlreadyInUseRequest, ProfileExistsResponse> {
    private readonly GET_PROFILE_ALREADY_IN_USE_ENDPOINT = '/get';

    constructor(private apiService: ApiService,
                private profileAlreadyInUseConfig: ProfileServiceConfig) {
    }

    handle(request: IsProfileAlreadyInUseRequest): Observable<ProfileExistsResponse> {
        const apiRequest: Request = new Request.Builder()
            .withType(HttpRequestType.GET)
            .withPath(this.profileAlreadyInUseConfig.profileApiPath +
                this.GET_PROFILE_ALREADY_IN_USE_ENDPOINT + '/' + request.type + '/' + request.key)
            .withApiToken(true)
            .withSessionToken(true)
            .build();

        return this.apiService.fetch<{ result: ProfileExistsResponse }>(apiRequest).map((success) => {
            return success.body.result;
        });
    }

}


handle(request: IsProfileAlreadyInUseRequest): Observable<ProfileExistsResponse> {
        const apiRequest: Request = new Request.Builder()
            .withType(HttpRequestType.GET)
            .withPath(this.profileAlreadyInUseConfig.profileApiPath +
                this.GET_PROFILE_ALREADY_IN_USE_ENDPOINT + '/' + request.type + '/' + request.key)
            .withApiToken(true)
            .withSessionToken(true)
            .build();

        return this.apiService.fetch<{ result: ProfileExistsResponse }>(apiRequest).map((success) => {
            return success.body.result;
        });
    }


() => {
                listener.status(address).subscribe(
                    (transactionStatusError) => {
                        const deadlineLocalDateTime = transactionStatusError.deadline.toLocalDateTime(profile.epochAdjustment);
                        const text =
                            '\nHash: ' +
                            transactionStatusError.hash +
                            '\n' +
                            'Error code: ' +
                            transactionStatusError.code +
                            '\n' +
                            'Deadline: ' +
                            deadlineLocalDateTime.toLocalDate().toString() +
                            ' ' +
                            deadlineLocalDateTime.toLocalTime().toString();
                        console.log(text);
                    },
                    (err) => {
                        console.log(FormatterService.error(err));
                        listener.close();
                    },
                );
            }


(transactionStatusError) => {
                        const deadlineLocalDateTime = transactionStatusError.deadline.toLocalDateTime(profile.epochAdjustment);
                        const text =
                            '\nHash: ' +
                            transactionStatusError.hash +
                            '\n' +
                            'Error code: ' +
                            transactionStatusError.code +
                            '\n' +
                            'Deadline: ' +
                            deadlineLocalDateTime.toLocalDate().toString() +
                            ' ' +
                            deadlineLocalDateTime.toLocalTime().toString();
                        console.log(text);
                    }


(err) => {
                        console.log(FormatterService.error(err));
                        listener.close();
                    }


(err) => {
                this.spinner.stop();
                console.log(FormatterService.error(err));
            }


@command({
    description: 'Monitor transaction status error',
})
export default class extends MonitorAddressCommand {
    constructor() {
        super();
    }

    @metadata
    async execute(options: MonitorAddressOptions) {
        const profile = this.getProfile(options);
        const address = await new AddressResolver().resolve(options, profile);

        console.log(FormatterService.title('Monitoring ') + `${address.pretty()} using ${profile.url}`);
        const listener = profile.repositoryFactory.createListener();
        listener.open().then(
            () => {
                listener.status(address).subscribe(
                    (transactionStatusError) => {
                        const deadlineLocalDateTime = transactionStatusError.deadline.toLocalDateTime(profile.epochAdjustment);
                        const text =
                            '\nHash: ' +
                            transactionStatusError.hash +
                            '\n' +
                            'Error code: ' +
                            transactionStatusError.code +
                            '\n' +
                            'Deadline: ' +
                            deadlineLocalDateTime.toLocalDate().toString() +
                            ' ' +
                            deadlineLocalDateTime.toLocalTime().toString();
                        console.log(text);
                    },
                    (err) => {
                        console.log(FormatterService.error(err));
                        listener.close();
                    },
                );
            },
            (err) => {
                this.spinner.stop();
                console.log(FormatterService.error(err));
            },
        );
    }
}


@metadata
    async execute(options: MonitorAddressOptions) {
        const profile = this.getProfile(options);
        const address = await new AddressResolver().resolve(options, profile);

        console.log(FormatterService.title('Monitoring ') + `${address.pretty()} using ${profile.url}`);
        const listener = profile.repositoryFactory.createListener();
        listener.open().then(
            () => {
                listener.status(address).subscribe(
                    (transactionStatusError) => {
                        const deadlineLocalDateTime = transactionStatusError.deadline.toLocalDateTime(profile.epochAdjustment);
                        const text =
                            '\nHash: ' +
                            transactionStatusError.hash +
                            '\n' +
                            'Error code: ' +
                            transactionStatusError.code +
                            '\n' +
                            'Deadline: ' +
                            deadlineLocalDateTime.toLocalDate().toString() +
                            ' ' +
                            deadlineLocalDateTime.toLocalTime().toString();
                        console.log(text);
                    },
                    (err) => {
                        console.log(FormatterService.error(err));
                        listener.close();
                    },
                );
            },
            (err) => {
                this.spinner.stop();
                console.log(FormatterService.error(err));
            },
        );
    }


() => {
  let page: AppPage;

  beforeEach(() => {
    page = new AppPage();
  });

  it('should display welcome message', () => {
    page.navigateTo();
    expect(page.getTitleText()).toEqual('incognito-log-viewer app is running!');
  });

  afterEach(async () => {
    // Assert that there are no errors emitted from the browser
    const logs = await browser.manage().logs().get(logging.Type.BROWSER);
    expect(logs).not.toContain(jasmine.objectContaining({
      level: logging.Level.SEVERE,
    } as logging.Entry));
  });
}


() => {
    page.navigateTo();
    expect(page.getTitleText()).toEqual('incognito-log-viewer app is running!');
  }


(request, response) => {
  response.json({message: "Hello World"});
}


(request, response) => {
  const body = request.body;
  response.json({message: `ECHO: ${body.message}`});
}


<
  State extends NavigationState,
  ScreenOptions extends {},
  EventMap extends EventMapBase
>(
  children: React.ReactNode
) => {
  const configs = React.Children.toArray(children).reduce<
    RouteConfig<ParamListBase, string, State, ScreenOptions, EventMap>[]
  >((acc, child) => {
    if (React.isValidElement(child)) {
      if (child.type === Screen) {
        // We can only extract the config from `Screen` elements
        // If something else was rendered, it's probably a bug
        acc.push(
          child.props as RouteConfig<
            ParamListBase,
            string,
            State,
            ScreenOptions,
            EventMap
          >
        );
        return acc;
      }

      if (child.type === React.Fragment) {
        // When we encounter a fragment, we need to dive into its children to extract the configs
        // This is handy to conditionally define a group of screens
        acc.push(
          ...getRouteConfigsFromChildren<State, ScreenOptions, EventMap>(
            child.props.children
          )
        );
        return acc;
      }
    }

    throw new Error(
      `A navigator can only contain 'Screen' components as its direct children (found ${
        React.isValidElement(child)
          ? `'${
              typeof child.type === 'string' ? child.type : child.type?.name
            }'${
              child.props?.name ? ` for the screen '${child.props.name}'` : ''
            }`
          : typeof child === 'object'
          ? JSON.stringify(child)
          : `'${String(child)}'`
      }). To render this component in the navigator, pass it in the 'component' prop to 'Screen'.`
    );
  }, []);

  if (process.env.NODE_ENV !== 'production') {
    configs.forEach((config) => {
      const { name, children, component, getComponent } = config;

      if (typeof name !== 'string' || !name) {
        throw new Error(
          `Got an invalid name (${JSON.stringify(
            name
          )}) for the screen. It must be a non-empty string.`
        );
      }

      if (
        children != null ||
        component !== undefined ||
        getComponent !== undefined
      ) {
        if (children != null && component !== undefined) {
          throw new Error(
            `Got both 'component' and 'children' props for the screen '${name}'. You must pass only one of them.`
          );
        }

        if (children != null && getComponent !== undefined) {
          throw new Error(
            `Got both 'getComponent' and 'children' props for the screen '${name}'. You must pass only one of them.`
          );
        }

        if (component !== undefined && getComponent !== undefined) {
          throw new Error(
            `Got both 'component' and 'getComponent' props for the screen '${name}'. You must pass only one of them.`
          );
        }

        if (children != null && typeof children !== 'function') {
          throw new Error(
            `Got an invalid value for 'children' prop for the screen '${name}'. It must be a function returning a React Element.`
          );
        }

        if (component !== undefined && !isValidElementType(component)) {
          throw new Error(
            `Got an invalid value for 'component' prop for the screen '${name}'. It must be a valid React Component.`
          );
        }

        if (getComponent !== undefined && typeof getComponent !== 'function') {
          throw new Error(
            `Got an invalid value for 'getComponent' prop for the screen '${name}'. It must be a function returning a React Component.`
          );
        }

        if (typeof component === 'function' && component.name === 'component') {
          // Inline anonymous functions passed in the `component` prop will have the name of the prop
          // It's relatively safe to assume that it's not a component since it should also have PascalCase name
          // We won't catch all scenarios here, but this should catch a good chunk of incorrect use.
          console.warn(
            `Looks like you're passing an inline function for 'component' prop for the screen '${name}' (e.g. component={() => <SomeComponent />}). Passing an inline function will cause the component state to be lost on re-render and cause perf issues since it's re-created every render. You can pass the function as children to 'Screen' instead to achieve the desired behaviour.`
          );
        }
      } else {
        throw new Error(
          `Couldn't find a 'component', 'getComponent' or 'children' prop for the screen '${name}'. This can happen if you passed 'undefined'. You likely forgot to export your component from the file it's defined in, or mixed up default import and named import when importing.`
        );
      }
    });
  }

  return configs;
}


(acc, child) => {
    if (React.isValidElement(child)) {
      if (child.type === Screen) {
        // We can only extract the config from `Screen` elements
        // If something else was rendered, it's probably a bug
        acc.push(
          child.props as RouteConfig<
            ParamListBase,
            string,
            State,
            ScreenOptions,
            EventMap
          >
        );
        return acc;
      }

      if (child.type === React.Fragment) {
        // When we encounter a fragment, we need to dive into its children to extract the configs
        // This is handy to conditionally define a group of screens
        acc.push(
          ...getRouteConfigsFromChildren<State, ScreenOptions, EventMap>(
            child.props.children
          )
        );
        return acc;
      }
    }

    throw new Error(
      `A navigator can only contain 'Screen' components as its direct children (found ${
        React.isValidElement(child)
          ? `'${
              typeof child.type === 'string' ? child.type : child.type?.name
            }'${
              child.props?.name ? ` for the screen '${child.props.name}'` : ''
            }`
          : typeof child === 'object'
          ? JSON.stringify(child)
          : `'${String(child)}'`
      }). To render this component in the navigator, pass it in the 'component' prop to 'Screen'.`
    );
  }


(config) => {
      const { name, children, component, getComponent } = config;

      if (typeof name !== 'string' || !name) {
        throw new Error(
          `Got an invalid name (${JSON.stringify(
            name
          )}) for the screen. It must be a non-empty string.`
        );
      }

      if (
        children != null ||
        component !== undefined ||
        getComponent !== undefined
      ) {
        if (children != null && component !== undefined) {
          throw new Error(
            `Got both 'component' and 'children' props for the screen '${name}'. You must pass only one of them.`
          );
        }

        if (children != null && getComponent !== undefined) {
          throw new Error(
            `Got both 'getComponent' and 'children' props for the screen '${name}'. You must pass only one of them.`
          );
        }

        if (component !== undefined && getComponent !== undefined) {
          throw new Error(
            `Got both 'component' and 'getComponent' props for the screen '${name}'. You must pass only one of them.`
          );
        }

        if (children != null && typeof children !== 'function') {
          throw new Error(
            `Got an invalid value for 'children' prop for the screen '${name}'. It must be a function returning a React Element.`
          );
        }

        if (component !== undefined && !isValidElementType(component)) {
          throw new Error(
            `Got an invalid value for 'component' prop for the screen '${name}'. It must be a valid React Component.`
          );
        }

        if (getComponent !== undefined && typeof getComponent !== 'function') {
          throw new Error(
            `Got an invalid value for 'getComponent' prop for the screen '${name}'. It must be a function returning a React Component.`
          );
        }

        if (typeof component === 'function' && component.name === 'component') {
          // Inline anonymous functions passed in the `component` prop will have the name of the prop
          // It's relatively safe to assume that it's not a component since it should also have PascalCase name
          // We won't catch all scenarios here, but this should catch a good chunk of incorrect use.
          console.warn(
            `Looks like you're passing an inline function for 'component' prop for the screen '${name}' (e.g. component={() => <SomeComponent />}). Passing an inline function will cause the component state to be lost on re-render and cause perf issues since it's re-created every render. You can pass the function as children to 'Screen' instead to achieve the desired behaviour.`
          );
        }
      } else {
        throw new Error(
          `Couldn't find a 'component', 'getComponent' or 'children' prop for the screen '${name}'. This can happen if you passed 'undefined'. You likely forgot to export your component from the file it's defined in, or mixed up default import and named import when importing.`
        );
      }
    }


(acc, config) => {
    if (config.name in acc) {
      throw new Error(
        `A navigator cannot contain multiple 'Screen' components with the same name (found duplicate screen named '${config.name}')`
      );
    }

    acc[config.name] = config;
    return acc;
  }


(config) => config.name


(acc, curr) => {
      const { initialParams } = screens[curr];
      const initialParamsFromParams =
        route?.params?.state == null &&
        route?.params?.initial !== false &&
        route?.params?.screen === curr
          ? route.params.params
          : undefined;

      acc[curr] =
        initialParams !== undefined || initialParamsFromParams !== undefined
          ? {
              ...initialParams,
              ...initialParamsFromParams,
            }
          : undefined;

      return acc;
    }


(acc, curr) =>
      Object.assign(acc, {
        [curr]: screens[curr].getId,
      })


(state) => state.type === undefined || state.type === router.type


(state) =>
      state !== undefined && state.stale === false && isStateValid(state)


() => {
    // If the current state isn't initialized on first render, we initialize it
    // We also need to re-initialize it if the state passed from parent was changed (maybe due to reset)
    // Otherwise assume that the state was provided as initial state
    // So we need to rehydrate it to make it usable
    if (
      (currentState === undefined || !isStateValid(currentState)) &&
      route?.params?.state == null
    ) {
      return [
        router.getInitialState({
          routeNames,
          routeParamList,
          routeGetIdList,
        }),
        true,
      ];
    } else {
      return [
        router.getRehydratedState(
          route?.params?.state ?? (currentState as PartialState<State>),
          {
            routeNames,
            routeParamList,
            routeGetIdList,
          }
        ),
        false,
      ];
    }
    // We explicitly don't include routeNames/routeParamList in the dep list
    // below. We want to avoid forcing a new state to be calculated in cases
    // where routeConfigs change without affecting routeNames/routeParamList.
    // Instead, we handle changes to these in the nextState code below. Note
    // that some changes to routeConfigs are explicitly ignored, such as changes
    // to initialParams
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }


() => {
    previousNestedParamsRef.current = route?.params;
  }


() => {
    if (shouldUpdate) {
      // If the state needs to be updated, we'll schedule an update
      setState(nextState);
    }
  }


() => {
    setKey(navigatorKey);

    if (!getIsInitial()) {
      // If it's not initial render, we need to update the state
      // This will make sure that our container gets notifier of state changes due to new mounts
      // This is necessary for proper screen tracking, URL updates etc.
      setState(nextState);
    }

    return () => {
      // We need to clean up state for this navigator on unmount
      // We do it in a timeout because we need to detect if another navigator mounted in the meantime
      // For example, if another navigator has started rendering, we should skip cleanup
      // Otherwise, our cleanup step will cleanup state for the other navigator and re-initialize it
     if (getCurrentState() !== undefined && getKey() === navigatorKey) {
        setState(undefined);
      }
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }


() => {
      // We need to clean up state for this navigator on unmount
      // We do it in a timeout because we need to detect if another navigator mounted in the meantime
      // For example, if another navigator has started rendering, we should skip cleanup
      // Otherwise, our cleanup step will cleanup state for the other navigator and re-initialize it
     if (getCurrentState() !== undefined && getKey() === navigatorKey) {
        setState(undefined);
      }
    }


(): State => {
    const currentState = getCurrentState();

    return isStateInitialized(currentState)
      ? (currentState as State)
      : (initializedStateRef.current as State);
  }


(e) => {
    let routeNames = [];

    let route: Route<string> | undefined;

    if (e.target) {
      route = state.routes.find((route) => route.key === e.target);

      if (route?.name) {
        routeNames.push(route.name);
      }
    } else {
      route = state.routes[state.index];
      routeNames.push(
        ...Object.keys(screens).filter((name) => route?.name === name)
      );
    }

    if (route == null) {
      return;
    }

    const navigation = descriptors[route.key].navigation;

    const listeners = ([] as (((e: any) => void) | undefined)[])
      .concat(
        ...routeNames.map((name) => {
          const { listeners } = screens[name];
          const map =
            typeof listeners === 'function'
              ? listeners({ route: route as any, navigation })
              : listeners;

          return map
            ? Object.keys(map)
                .filter((type) => type === e.type)
                .map((type) => map?.[type])
            : undefined;
        })
      )
      .filter((cb, i, self) => cb && self.lastIndexOf(cb) === i);

    listeners.forEach((listener) => listener?.(e));
  }


(route) => route.key === e.target


(name) => route?.name === name


(name) => {
          const { listeners } = screens[name];
          const map =
            typeof listeners === 'function'
              ? listeners({ route: route as any, navigation })
              : listeners;

          return map
            ? Object.keys(map)
                .filter((type) => type === e.type)
                .map((type) => map?.[type])
            : undefined;
        }


(type) => type === e.type


(type) => map?.[type]


(cb, i, self) => cb && self.lastIndexOf(cb) === i


(listener) => listener?.(e)


() => {
    emitter.emit({ type: 'state', data: { state } });
  }


type NavigatorRoute<State extends NavigationState> = {
  key: string;
  params?: NavigatorScreenParams<ParamListBase, State>;
};


(activeRect: ClientRect, targetRect: ClientRect) => {
      let distance = -1;
      // ClientRect values can be floats that differ by very small fractions of a decimal.
      // If the difference between top and bottom are within a pixel then we should treat
      // them as equivalent by using Math.floor. For instance 5.2222 and 5.222221 should be equivalent,
      // but without Math.Floor they will be handled incorrectly.
      let targetRectTop = Math.floor(targetRect.top);
      let activeRectBottom = Math.floor(activeRect.bottom);

      if (targetRectTop < activeRectBottom) {
        return 999999999;
      }

      if ((targetTop === -1 && targetRectTop >= activeRectBottom) ||
        (targetRectTop === targetTop)) {
        targetTop = targetRectTop;
        if (leftAlignment >= targetRect.left && leftAlignment <= (targetRect.left + targetRect.width)) {
          distance = 0;
        } else {
          distance = Math.abs((targetRect.left + (targetRect.width / 2)) - leftAlignment);
        }
      }

      return distance;
    }


(activeRect: ClientRect, targetRect: ClientRect) => {
      let distance = -1;
      // ClientRect values can be floats that differ by very small fractions of a decimal.
      // If the difference between top and bottom are within a pixel then we should treat
      // them as equivalent by using Math.floor. For instance 5.2222 and 5.222221 should be equivalent,
      // but without Math.Floor they will be handled incorrectly.
      let targetRectBottom = Math.floor(targetRect.bottom);
      let targetRectTop = Math.floor(targetRect.top);
      let activeRectTop = Math.floor(activeRect.top);

      if (targetRectBottom > activeRectTop) {
        return 999999999;
      }

      if ((targetTop === -1 && targetRectBottom <= activeRectTop) ||
        (targetRectTop === targetTop)) {
        targetTop = targetRectTop;
        if (leftAlignment >= targetRect.left && leftAlignment <= (targetRect.left + targetRect.width)) {
          distance = 0;
        } else {
          distance = Math.abs((targetRect.left + (targetRect.width / 2)) - leftAlignment);
        }
      }

      return distance;
    }


(activeRect: ClientRect, targetRect: ClientRect) => {
      let distance = -1;

      if (
        targetRect.bottom > activeRect.top &&
        targetRect.right <= activeRect.right &&
        this.props.direction !== FocusZoneDirection.vertical
      ) {

        distance = activeRect.right - targetRect.right;
      }

      return distance;
    }


(activeRect: ClientRect, targetRect: ClientRect) => {
      let distance = -1;

      if (
        targetRect.top < activeRect.bottom &&
        targetRect.left >= activeRect.left &&
        this.props.direction !== FocusZoneDirection.vertical
      ) {

        distance = targetRect.left - activeRect.left;
      }

      return distance;
    }


interface IPoint {
  left: number;
  top: number;
}


public componentDidMount() {
    _allInstances[this._id] = this;
    if (this._root) {
      const windowElement = this._root.ownerDocument.defaultView;

      let parentElement = getParent(this._root);

      while (
        parentElement &&
        parentElement !== document.body &&
        parentElement.nodeType === 1
      ) {
        if (isElementFocusZone(parentElement)) {
          this._isInnerZone = true;
          break;
        }
        parentElement = getParent(parentElement);
      }

      if (!this._isInnerZone) {
        this._events.on(windowElement, 'keydown', this._onKeyDownCapture, true);
      }

      // Assign initial tab indexes so that we can set initial focus as appropriate.
      this._updateTabIndexes();

      if (this.props.defaultActiveElement) {
        this._activeElement = getDocument()!.querySelector(this.props.defaultActiveElement) as HTMLElement;
        this.focus();
      }
    }
  }


public componentWillUnmount() {
    delete _allInstances[this._id];
  }


public render() {
    let { rootProps, ariaDescribedBy, ariaLabelledBy, className } = this.props;
    let divProps = getNativeProps(this.props, divProperties);

    const Tag = this.props.elementType || 'div';

    return (
      <Tag
        role='presentation'
        {
        ...divProps
        }
        {
        ...rootProps
        }
        className={ css('ms-FocusZone', className) }
        ref={ this._resolveRef('_root') }
        data-focuszone-id={ this._id }
        aria-labelledby={ ariaLabelledBy }
        aria-describedby={ ariaDescribedBy }
        onKeyDown={ this._onKeyDown }
        onFocus={ this._onFocus }
        onMouseDownCapture={ this._onMouseDown }
      >
        { this.props.children }
      </Tag>
    );
  }


css('ms-FocusZone', className)


/**
   * Sets focus to the first tabbable item in the zone.
   * @returns True if focus could be set to an active element, false if no operation was taken.
   */
public focus(forceIntoFirstElement: boolean = false): boolean {
    if (this._root) {
      if (!forceIntoFirstElement && this._root.getAttribute(IS_FOCUSABLE_ATTRIBUTE) === 'true' && this._isInnerZone) {
        const ownerZoneElement = this._getOwnerZone(this._root);

        if (ownerZoneElement !== this._root) {
          const ownerZone = _allInstances[ownerZoneElement.getAttribute(FOCUSZONE_ID_ATTRIBUTE) as string];

          return !!ownerZone && ownerZone.focusElement(this._root);
        }

        return false;
      } else if (this._activeElement && elementContains(this._root, this._activeElement)
        && isElementTabbable(this._activeElement)) {
        this._activeElement.focus();
        return true;
      } else {
        const firstChild = this._root.firstChild as HTMLElement;

        return this.focusElement(getNextElement(this._root, firstChild, true) as HTMLElement);
      }
    }
    return false;
  }


/**
   * Sets focus to a specific child element within the zone. This can be used in conjunction with
   * onBeforeFocus to created delayed focus scenarios (like animate the scroll position to the correct
   * location and then focus.)
   * @param {HTMLElement} element The child element within the zone to focus.
   * @returns True if focus could be set to an active element, false if no operation was taken.
   */
public focusElement(element: HTMLElement): boolean {
    let { onBeforeFocus } = this.props;

    if (onBeforeFocus && !onBeforeFocus(element)) {
      return false;
    }

    if (element) {
      this._setActiveElement(element);

      return true;
    }

    return false;
  }


@autobind
  private _onFocus(ev: React.FocusEvent<HTMLElement>) {
    let { onActiveElementChanged } = this.props;

    if (this._isImmediateDescendantOfZone(ev.target as HTMLElement)) {
      this._activeElement = ev.target as HTMLElement;
      this._setFocusAlignment(this._activeElement);
    } else {
      let parentElement = ev.target as HTMLElement;

      while (parentElement && parentElement !== this._root) {
        if (isElementTabbable(parentElement) && this._isImmediateDescendantOfZone(parentElement)) {
          this._activeElement = parentElement;
          break;
        }
        parentElement = getParent(parentElement) as HTMLElement;
      }
    }
    if (onActiveElementChanged) {
      onActiveElementChanged(this._activeElement as HTMLElement, ev);
    }
  }


/**
   * Handle global tab presses so that we can patch tabindexes on the fly.
   */
private _onKeyDownCapture(ev: KeyboardEvent) {
    if (ev.which === KeyCodes.tab) {
      this._updateTabIndexes();
    }
  }