public static fromArray(data: Array<any>): IMessage {
        switch (data[0]) {
            case AbortMessage.MSG_ABORT:
                return new AbortMessage(data[1], data[2]);
            case GoodbyeMessage.MSG_GOODBYE:
                return new GoodbyeMessage(data[1], data[2]);
            case UnregisteredMessage.MSG_UNREGISTERED:
                return new UnregisteredMessage(data[1]);
            case WelcomeMessage.MSG_WELCOME:
                return new WelcomeMessage(data[1], data[2]);
            case SubscribedMessage.MSG_SUBSCRIBED:
                return new SubscribedMessage(data[1], data[2]);
            case UnsubscribedMessage.MSG_UNSUBSCRIBED:
                return new UnsubscribedMessage(data[1]);
            case EventMessage.MSG_EVENT:
                return new EventMessage(data[1], data[2], data[3], data[4] || [], data[5] || {});
            case RegisteredMessage.MSG_REGISTERED:
                return new RegisteredMessage(data[1], data[2]);
            case InvocationMessage.MSG_INVOCATION:
                return new InvocationMessage(data[1], data[2], data[3], data[4] || [], data[5] || {});
            case ResultMessage.MSG_RESULT:
                return new ResultMessage(data[1], data[2], data[3], data[4]);
            case ChallengeMessage.MSG_CHALLENGE:
                return new ChallengeMessage(data[1], data[2]);
            case ErrorMessage.MSG_ERROR:
                return new ErrorMessage(data[1], data[2], data[3], data[4], data[5] || [], data[6] || {});
            case InterruptMessage.MSG_INTERRUPT:
                return new InterruptMessage(data[1], data[2]);
            // default:
            //     throw new MessageException("Unhandled message type: " . data[0]);
        }
    }


function compact(obj: any) {
  return Object.keys(obj).reduce(
    (acc, key) => {
      if (obj[key] !== undefined) {
        acc[key] = obj[key];
      }

      return acc;
    },
    {} as any,
  );
}


function observableQueryFields<TData, TVariables>(
  observable: ObservableQuery<TData>,
): ObservableQueryFields<TData, TVariables> {
  const fields = {
    variables: observable.variables,
    refetch: observable.refetch.bind(observable),
    fetchMore: observable.fetchMore.bind(observable),
    updateQuery: observable.updateQuery.bind(observable),
    startPolling: observable.startPolling.bind(observable),
    stopPolling: observable.stopPolling.bind(observable),
    subscribeToMore: observable.subscribeToMore.bind(observable),
  };
  // TODO: Need to cast this because we improved the type of `updateQuery` to be parametric
  // on variables, while the type in Apollo client just has object.
  // Consider removing this when that is properly typed
  return fields as ObservableQueryFields<TData, TVariables>;
}


(acc, key) => {
      if (obj[key] !== undefined) {
        acc[key] = obj[key];
      }

      return acc;
    }


() => {
    if (this.querySubscription) return;
    // store the initial renders worth of result
    let initial: QueryResult<TData, TVariables> | undefined = this.getQueryResult();
    this.querySubscription = this.queryObservable!.subscribe({
      next: ({ data }) => {
        // to prevent a quick second render from the subscriber
        // we compare to see if the original started finished (from cache) and is unchanged
        if (initial && initial.networkStatus === 7 && shallowEqual(initial.data, data)) {
          initial = undefined;
          return;
        }

        initial = undefined;
        this.updateCurrentData();
      },
      error: error => {
        this.resubscribeToQuery();
        // Quick fix for https://github.com/apollostack/react-apollo/issues/378
        if (!error.hasOwnProperty('graphQLErrors')) throw error;

        this.updateCurrentData();
      },
    });
  }


({ data }) => {
        // to prevent a quick second render from the subscriber
        // we compare to see if the original started finished (from cache) and is unchanged
        if (initial && initial.networkStatus === 7 && shallowEqual(initial.data, data)) {
          initial = undefined;
          return;
        }

        initial = undefined;
        this.updateCurrentData();
      }


error => {
        this.resubscribeToQuery();
        // Quick fix for https://github.com/apollostack/react-apollo/issues/378
        if (!error.hasOwnProperty('graphQLErrors')) throw error;

        this.updateCurrentData();
      }


() => {
    if (this.querySubscription) {
      this.querySubscription.unsubscribe();
      delete this.querySubscription;
    }
  }


() => {
    // force a rerender that goes through shouldComponentUpdate
    if (this.hasMounted) this.forceUpdate();
  }


(): QueryResult<TData, TVariables> => {
    let data = { data: Object.create(null) as TData } as any;
    // Attach bound methods
    Object.assign(data, observableQueryFields(this.queryObservable!));

    // When skipping a query (ie. we're not querying for data but still want
    // to render children), make sure the `data` is cleared out and
    // `loading` is set to `false` (since we aren't loading anything).
    if (this.props.skip) {
      data = {
        ...data,
        data: undefined,
        error: undefined,
        loading: false,
      };
    } else {
      // Fetch the current result (if any) from the store.
      const currentResult = this.queryObservable!.currentResult();
      const { loading, partial, networkStatus, errors } = currentResult;
      let { error } = currentResult;

      // Until a set naming convention for networkError and graphQLErrors is
      // decided upon, we map errors (graphQLErrors) to the error props.
      if (errors && errors.length > 0) {
        error = new ApolloError({ graphQLErrors: errors });
      }

      Object.assign(data, { loading, networkStatus, error });

      if (loading) {
        Object.assign(data.data, this.previousData, currentResult.data);
      } else if (error) {
        Object.assign(data, {
          data: (this.queryObservable!.getLastResult() || {}).data,
        });
      } else {
        const { fetchPolicy } = this.queryObservable!.options;
        const { partialRefetch } = this.props;
        if (
          partialRefetch &&
          Object.keys(currentResult.data).length === 0 &&
          partial &&
          fetchPolicy !== 'cache-only'
        ) {
          // When a `Query` component is mounted, and a mutation is executed
          // that returns the same ID as the mounted `Query`, but has less
          // fields in its result, Apollo Client's `QueryManager` returns the
          // data as an empty Object since a hit can't be found in the cache.
          // This can lead to application errors when the UI elements rendered by
          // the original `Query` component are expecting certain data values to
          // exist, and they're all of a sudden stripped away. To help avoid
          // this we'll attempt to refetch the `Query` data.
          Object.assign(data, { loading: true, networkStatus: NetworkStatus.loading });
          data.refetch();
          return data;
        }

        Object.assign(data.data, currentResult.data);
        this.previousData = currentResult.data;
      }
    }

    // Handle race condition where refetch is called on child mount or later
    // Normal execution model:
    // render(loading) -> mount -> start subscription -> get data -> render(with data)
    //
    // SSR with synchronous refetch:
    // render(with data) -> refetch -> mount -> start subscription
    //
    // SSR with asynchronous refetch:
    // render(with data) -> mount -> start subscription -> refetch
    //
    // If a subscription has not started, then the synchronous call to refetch
    // must be made at a time when an active network request is being made, so
    // we ensure that the network requests are deduped, to avoid an
    // inconsistent UI state that displays different data for the current query
    // alongside a refetched query.
    //
    // Once the Query component is mounted and the subscription is made, we
    // always hit the network with refetch, since the components data will be
    // updated and a network request is not currently active.
    if (!this.querySubscription) {
      const oldRefetch = (data as GraphqlQueryControls).refetch;

      (data as GraphqlQueryControls).refetch = args => {
        if (this.querySubscription) {
          return oldRefetch(args);
        } else {
          return new Promise((r, f) => {
            this.refetcherQueue = { resolve: r, reject: f, args };
          });
        }
      };
    }

    data.client = this.client;
    return data;
  }


args => {
        if (this.querySubscription) {
          return oldRefetch(args);
        } else {
          return new Promise((r, f) => {
            this.refetcherQueue = { resolve: r, reject: f, args };
          });
        }
      }


(r, f) => {
            this.refetcherQueue = { resolve: r, reject: f, args };
          }


export interface QueryResult<TData = any, TVariables = OperationVariables>
  extends ObservableQueryFields<TData, TVariables> {
  client: ApolloClient<any>;
  // we create an empty object to make checking for data
  // easier for consumers (i.e. instead of data && data.user
  // you can just check data.user) this also makes destructring
  // easier (i.e. { data: { user } })
  // however, this isn't realy possible with TypeScript that
  // I'm aware of. So intead we enforce checking for data
  // like so result.data!.user. This tells TS to use TData
  // XXX is there a better way to do this?
  data: TData | undefined;
  error?: ApolloError;
  loading: boolean;
  networkStatus: NetworkStatus;
}


export interface QueryProps<TData = any, TVariables = OperationVariables> {
  children: (result: QueryResult<TData, TVariables>) => React.ReactNode;
  fetchPolicy?: FetchPolicy;
  errorPolicy?: ErrorPolicy;
  notifyOnNetworkStatusChange?: boolean;
  pollInterval?: number;
  query: DocumentNode;
  variables?: TVariables;
  ssr?: boolean;
  displayName?: string;
  skip?: boolean;
  client?: ApolloClient<Object>;
  context?: Record<string, any>;
  partialRefetch?: boolean;
  onCompleted?: (data: TData | {}) => void;
  onError?: (error: ApolloError) => void;
}


export interface QueryContext {
  client?: ApolloClient<Object>;
  operations?: Map<string, { query: DocumentNode; variables: any }>;
}


export type ObservableQueryFields<TData, TVariables> = Pick<
  ObservableQuery<TData, TVariables>,
  'startPolling' | 'stopPolling' | 'subscribeToMore' | 'updateQuery' | 'refetch' | 'variables'
> & {
  fetchMore: (<K extends keyof TVariables>(
    fetchMoreOptions: FetchMoreQueryOptions<TVariables, K> & FetchMoreOptions<TData, TVariables>,
  ) => Promise<ApolloQueryResult<TData>>) &
    (<TData2, TVariables2, K extends keyof TVariables2>(
      fetchMoreOptions: { query: DocumentNode } & FetchMoreQueryOptions<TVariables2, K> &
        FetchMoreOptions<TData2, TVariables2>,
    ) => Promise<ApolloQueryResult<TData2>>);
};


// For server-side rendering (see getDataFromTree.ts)
fetchData(): Promise<ApolloQueryResult<any>> | boolean {
    if (this.props.skip) return false;

    // pull off react options
    const {
      children,
      ssr,
      displayName,
      skip,
      client,
      onCompleted,
      onError,
      partialRefetch,
      ...opts
    } = this.props;

    let { fetchPolicy } = opts;
    if (ssr === false) return false;
    if (fetchPolicy === 'network-only' || fetchPolicy === 'cache-and-network') {
      fetchPolicy = 'cache-first'; // ignore force fetch in SSR;
    }

    const observable = this.client.watchQuery({
      ...opts,
      fetchPolicy,
    });
    const result = this.queryObservable!.currentResult();

    return result.loading ? observable.result() : false;
  }


componentDidMount() {
    this.hasMounted = true;
    if (this.props.skip) return;

    this.startQuerySubscription();
    if (this.refetcherQueue) {
      const { args, resolve, reject } = this.refetcherQueue;
      this.queryObservable!.refetch(args)
        .then(resolve)
        .catch(reject);
    }
  }


componentWillReceiveProps(nextProps: QueryProps<TData, TVariables>, nextContext: QueryContext) {
    // the next render wants to skip
    if (nextProps.skip && !this.props.skip) {
      this.removeQuerySubscription();
      return;
    }

    const nextClient = getClient(nextProps, nextContext);

    if (shallowEqual(this.props, nextProps) && this.client === nextClient) {
      return;
    }

    if (this.client !== nextClient) {
      this.client = nextClient;
      this.removeQuerySubscription();
      this.queryObservable = null;
      this.previousData = {};
      this.updateQuery(nextProps);
    }

    if (this.props.query !== nextProps.query) {
      this.removeQuerySubscription();
    }

    this.updateQuery(nextProps);
    if (nextProps.skip) return;
    this.startQuerySubscription();
  }


componentWillUnmount() {
    this.removeQuerySubscription();
    this.hasMounted = false;
  }


componentDidUpdate() {
    const { onCompleted, onError } = this.props;
    if (onCompleted || onError) {
      const currentResult = this.queryObservable!.currentResult();
      const { loading, error, data } = currentResult;
      if (onCompleted && !loading && !error) {
        onCompleted(data);
      } else if (onError && !loading && error) {
        onError(error);
      }
    }
  }


render() {
    const { children } = this.props;
    const queryResult = this.getQueryResult();
    return children(queryResult);
  }


private extractOptsFromProps(props: QueryProps<TData, TVariables>) {
    const {
      variables,
      pollInterval,
      fetchPolicy,
      errorPolicy,
      notifyOnNetworkStatusChange,
      query,
      displayName = 'Query',
      context = {},
    } = props;

    this.operation = parser(query);

    invariant(
      this.operation.type === DocumentType.Query,
      `The <Query /> component requires a graphql query, but got a ${
        this.operation.type === DocumentType.Mutation ? 'mutation' : 'subscription'
      }.`,
    );

    return compact({
      variables,
      pollInterval,
      query,
      fetchPolicy,
      errorPolicy,
      notifyOnNetworkStatusChange,
      metadata: { reactComponent: { displayName } },
      context,
    });
  }


private initializeQueryObservable(props: QueryProps<TData, TVariables>) {
    const opts = this.extractOptsFromProps(props);
    // save for backwards compat of refetcherQueries without a recycler
    this.setOperations(opts);
    this.queryObservable = this.client.watchQuery(opts);
  }


private setOperations(props: QueryProps<TData, TVariables>) {
    if (this.context!.operations) {
      this.context!.operations!.set(this.operation!.name, {
        query: props.query,
        variables: props.variables,
      });
    }
  }


private updateQuery(props: QueryProps<TData, TVariables>) {
    // if we skipped initially, we may not have yet created the observable
    if (!this.queryObservable) {
      this.initializeQueryObservable(props);
    } else {
      this.setOperations(props);
    }

    this.queryObservable!.setOptions(this.extractOptsFromProps(props))
      // The error will be passed to the child container, so we don't
      // need to log it here. We could conceivably log something if
      // an option was set. OTOH we don't log errors w/ the original
      // query. See https://github.com/apollostack/react-apollo/issues/404
      .catch(() => null);
  }


private resubscribeToQuery() {
    this.removeQuerySubscription();

    const lastError = this.queryObservable!.getLastError();
    const lastResult = this.queryObservable!.getLastResult();
    // If lastError is set, the observable will immediately
    // send it, causing the stream to terminate on initialization.
    // We clear everything here and restore it afterward to
    // make sure the new subscription sticks.
    this.queryObservable!.resetLastResults();
    this.startQuerySubscription();
    Object.assign(this.queryObservable!, { lastError, lastResult });
  }


x => {
        return x.includes('a=rtpmap:');
      }


x => {
        return x.replace(/.* /, '').replace(/\/.*/, '');
      }


(v, i, a) => {
        return a.indexOf(v) === i;
      }


x => {
        return x !== 'rtx' && x !== 'red' && x !== 'ulpfec';
      }


version(): string {
    return this.browser.version;
  }


majorVersion(): number {
    return parseInt(this.version().split('.')[0]);
  }


name(): string {
    return this.browser.name;
  }


hasChromiumWebRTC(): boolean {
    for (const browser of this.chromeLike) {
      if (browser === this.browser.name) {
        return true;
      }
    }
    return false;
  }


hasWebKitWebRTC(): boolean {
    for (const browser of this.webkitBrowsers) {
      if (browser === this.browser.name) {
        return true;
      }
    }
    return false;
  }


hasFirefoxWebRTC(): boolean {
    return this.isFirefox();
  }


supportsCanvasCapturedStreamPlayback(): boolean {
    return !this.isIOSSafari() && !this.isIOSChrome() && !this.isIOSFirefox();
  }


requiresUnifiedPlan(): boolean {
    let shouldEnable =
      this.isFirefox() || (this.hasWebKitWebRTC() && this.isUnifiedPlanSupported());
    if (this.enableUnifiedPlanForChromiumBasedBrowsers) {
      shouldEnable = shouldEnable || this.hasChromiumWebRTC();
    }
    return shouldEnable;
  }


requiresResolutionAlignment(width: number, height: number): [number, number] {
    if (this.isAndroid() && this.isPixel3()) {
      return [Math.ceil(width / 64) * 64, Math.ceil(height / 64) * 64];
    }
    return [width, height];
  }


requiresCheckForSdpConnectionAttributes(): boolean {
    return !this.isIOSSafari() && !this.isIOSChrome() && !this.isIOSFirefox();
  }


requiresIceCandidateGatheringTimeoutWorkaround(): boolean {
    return this.hasChromiumWebRTC();
  }


requiresUnifiedPlanMunging(): boolean {
    let shouldRequire = this.hasWebKitWebRTC() && this.isUnifiedPlanSupported();
    if (this.enableUnifiedPlanForChromiumBasedBrowsers) {
      shouldRequire = shouldRequire || this.hasChromiumWebRTC();
    }
    return shouldRequire;
  }


requiresSortCodecPreferencesForSdpAnswer(): boolean {
    return this.isFirefox() && this.majorVersion() <= 68;
  }


requiresSimulcastMunging(): boolean {
    return this.isSafari();
  }


requiresBundlePolicy(): RTCBundlePolicy {
    return 'max-bundle';
  }


requiresPromiseBasedWebRTCGetStats(): boolean {
    return !this.hasChromiumWebRTC();
  }


requiresVideoElementWorkaround(): boolean {
    return this.isSafari();
  }


requiresNoExactMediaStreamConstraints(): boolean {
    return (
      this.isSamsungInternet() ||
      (this.isIOSSafari() && (this.version() === '12.0.0' || this.version() === '12.1.0'))
    );
  }


requiresGroupIdMediaStreamConstraints(): boolean {
    return this.isSamsungInternet();
  }


requiresContextRecreationForAudioWorklet(): boolean {
    if (!this.recreateAudioContextIfNeeded) {
      return false;
    }

    // Definitely not Chrome; no worries.
    if (!('chrome' in global)) {
      return false;
    }

    // Everything seems to work fine on platforms other than macOS.
    if (this.browser.os !== 'Mac OS') {
      return false;
    }

    // Electron or Chromium.
    // All other browsers are fine. But you'd have to be super weird --
    // faking global.chrome but not having a Chrome UA -- to get this far,
    // so we have some Istanbul directives on these.

    /* istanbul ignore else */
    if (this.isChrome() || this.isEdge()) {
      return true;
    }

    /* istanbul ignore next */
    return false;
  }


getDisplayMediaAudioCaptureSupport(): boolean {
    return this.isChrome() || this.isEdge();
  }


supportsSenderSideBandwidthEstimation(): boolean {
    return this.hasChromiumWebRTC() || this.isSafari();
  }


// TODO: Deprecated, needs to be removed
screenShareUnsupported(): boolean {
    console.warn('This function is no longer supported.');
    if (this.isSafari()) {
      return true;
    }
    return false;
  }


isSupported(): boolean {
    if (
      !this.browserSupport[this.browser.name] ||
      this.majorVersion() < this.browserSupport[this.browser.name]
    ) {
      return false;
    }
    if (this.browser.name === 'firefox' && this.isAndroid()) {
      return false;
    }
    return true;
  }


supportString(): string {
    if (this.isAndroid()) {
      return `${this.browserName['chrome']} ${this.browserSupport['chrome']}+, ${this.browserName['samsung']} ${this.browserSupport['samsung']}+`;
    }
    const s: string[] = [];
    for (const k in this.browserSupport) {
      s.push(`${this.browserName[k]} ${this.browserSupport[k]}+`);
    }
    return s.join(', ');
  }


async supportedVideoCodecs(): Promise<string[]> {
    const pc = new RTCPeerConnection();
    pc.addTransceiver('video', { direction: 'inactive', streams: [] });
    return (await pc.createOffer({ offerToReceiveVideo: true })).sdp
      .split('\r\n')
      .filter(x => {
        return x.includes('a=rtpmap:');
      })
      .map(x => {
        return x.replace(/.* /, '').replace(/\/.*/, '');
      })
      .filter((v, i, a) => {
        return a.indexOf(v) === i;
      })
      .filter(x => {
        return x !== 'rtx' && x !== 'red' && x !== 'ulpfec';
      });
  }


supportsSetSinkId(): boolean {
    return 'setSinkId' in HTMLAudioElement.prototype;
  }


// These helpers should be kept private to encourage
// feature detection instead of browser detection.
private isIOSSafari(): boolean {
    return this.browser.name === 'ios';
  }


private isSafari(): boolean {
    return this.browser.name === 'safari' || this.browser.name === 'ios';
  }


private isFirefox(): boolean {
    return this.browser.name === 'firefox';
  }


private isIOSFirefox(): boolean {
    return this.browser.name === 'fxios';
  }


private isIOSChrome(): boolean {
    return this.browser.name === 'crios';
  }


private isChrome(): boolean {
    return this.browser.name === 'chrome';
  }


private isEdge(): boolean {
    return this.browser.name === 'edge-chromium';
  }


private isSamsungInternet(): boolean {
    return this.browser.name === 'samsung';
  }


private isAndroid(): boolean {
    return /(android)/i.test(navigator.userAgent);
  }


private isPixel3(): boolean {
    return /( pixel 3)/i.test(navigator.userAgent);
  }


private isUnifiedPlanSupported(): boolean {
    return RTCRtpTransceiver.prototype.hasOwnProperty('currentDirection');
  }


function Server() {
  const [isEditing, setIsEditing] = useState(false);
  const value = useTypedSelector((state) => state.server.value);
  const options = useTypedSelector((state) => state.server.options);

  const dispatch = useTypedDispatch();

  if (options.length <= 0) {
    return null;
  }

  if (options.length <= 1 && value?.variables === undefined) {
    return null;
  }

  if (!isEditing) {
    let url = "";
    if (value) {
      url = value.url.replace(/\/$/, "");
      if (value.variables) {
        Object.keys(value.variables).forEach((variable) => {
          url = url.replace(
            `{${variable}}`,
            value.variables?.[variable].default ?? ""
          );
        });
      }
    }
    return (
      <FloatingButton onClick={() => setIsEditing(true)} label="Edit">
        <pre
          style={{
            background: "var(--openapi-card-background-color)",
            paddingRight: "60px",
          }}
        >
          <code>{url}</code>
        </pre>
      </FloatingButton>
    );
  }


(state) => state.server.value


(state) => state.server.options


(variable) => {
          url = url.replace(
            `{${variable}}`,
            value.variables?.[variable].default ?? ""
          );
        }


(s) => s.url


(key) => {
          if (value.variables?.[key].enum !== undefined) {
            return (
              <FormItem label={key}>
                <FormSelect
                  options={value.variables[key].enum}
                  onChange={(e) => {
                    dispatch(setServerVariable({ key, value: e.target.value }));
                  }}
                />
              </FormItem>


export default class PlayerGG {

    public player: Spine;
    public hitbx: PIXI.Sprite;
    public lives: PIXI.Sprite[] = [];

    public livesSprite: PIXI.Container = new PIXI.Container()
    public livesNum: number = 3

    constructor(player: Spine) {
        this.player = player;
        window.app.stage.addChild(player);
        this.player.visible = false

        this.createHitbox();
        this.createLives();

        this.livesSprite.visible = false;
    }

    createHitbox() {
        let hitbx = new PIXI.Sprite(PIXI.Texture.EMPTY);

        hitbx.x = this.player.x - this.player.width / 2;
        hitbx.y = window.sceneHeight / 2;
        hitbx.width = this.player.width - 80;
        hitbx.height = this.player.height / 1.7;

        this.hitbx = hitbx;
        window.app.stage.addChild(hitbx);
    }

    createLives() {
        for (let i = 0; i < 3; i++) {
            let textur = PIXI.Texture.from('assets/live.png');
            let ress = new PIXI.Sprite(textur);

            ress.width = 64;
            ress.height = 64;
            ress.x = 5 + i * 72

            this.lives.push(ress);
            this.livesSprite.addChild(ress);

            window.app.stage.addChild(this.livesSprite);
        }
    }
}


createHitbox() {
        let hitbx = new PIXI.Sprite(PIXI.Texture.EMPTY);

        hitbx.x = this.player.x - this.player.width / 2;
        hitbx.y = window.sceneHeight / 2;
        hitbx.width = this.player.width - 80;
        hitbx.height = this.player.height / 1.7;

        this.hitbx = hitbx;
        window.app.stage.addChild(hitbx);
    }


createLives() {
        for (let i = 0; i < 3; i++) {
            let textur = PIXI.Texture.from('assets/live.png');
            let ress = new PIXI.Sprite(textur);

            ress.width = 64;
            ress.height = 64;
            ress.x = 5 + i * 72

            this.lives.push(ress);
            this.livesSprite.addChild(ress);

            window.app.stage.addChild(this.livesSprite);
        }
    }


export class NearAccountUtils implements IBlockchainAccountUtils {
    public getAccountDerivationPath(accountIndex: number): string {
        // all accounts are created / recovered using the first public-private key pair
        accountIndex = 0;
        return `${accountIndex}'`;
    }

    public getPrivateKeyFromDerived(derivedKey: HDKeyEd25519): string {
        const keyPair = nacl.sign.keyPair.fromSeed(derivedKey.key);
        return bs58Encode(Buffer.from(keyPair.secretKey));
    }

    public isValidChecksumAddress(address: string): boolean {
        return true;
    }

    public isValidAddress(address: string): boolean {
        return false;
    }

    public publicToAddress(publicKey: string): string {
        return publicKey;
    }

    public privateToPublic(privateKey: string): string {
        const keyPair = nacl.sign.keyPair.fromSecretKey(bs58Decode(privateKey));
        return 'ed25519:' + bs58Encode(Buffer.from(keyPair.publicKey));
    }

    public privateToAddress(privateKey: string): string {
        return this.privateToPublic(privateKey);
    }

    public getAccountFromPrivateKey(privateKey: string, index: number): IAccountState {
        const keyPair = nacl.sign.keyPair.fromSecretKey(bs58Decode(privateKey));
        const pk = bs58Encode(Buffer.from(keyPair.publicKey));
        const address = Buffer.from(bs58Decode(pk)).toString('hex');

        return {
            index: 0,
            type: AccountType.DEFAULT,
            selected: false,
            publicKey: 'ed25519:' + pk,
            address,
            blockchain: Blockchain.NEAR,
            tokens: generateTokensConfig(Blockchain.NEAR)
        };
    }

    public amountToStd(value: BigNumber | number | string, decimals: number): BigNumber {
        return new BigNumber(value).multipliedBy(new BigNumber(10).pow(decimals));
    }

    public amountFromStd(value: BigNumber | number | string, decimals: number): BigNumber {
        return new BigNumber(value).dividedBy(new BigNumber(10).pow(decimals));
    }

    public convertUnit(value: BigNumber, from: string, to: string): BigNumber {
        return convert(value, from, to, config);
    }

    public getLockupContract(accountId: string, chainId: ChainIdType): string {
        return sha256(Buffer.from(accountId)).substring(0, 40) + NEAR_LOCKUP_SUFFIX[chainId];
    }
}


public getAccountDerivationPath(accountIndex: number): string {
        // all accounts are created / recovered using the first public-private key pair
        accountIndex = 0;
        return `${accountIndex}'`;
    }


public getPrivateKeyFromDerived(derivedKey: HDKeyEd25519): string {
        const keyPair = nacl.sign.keyPair.fromSeed(derivedKey.key);
        return bs58Encode(Buffer.from(keyPair.secretKey));
    }


public isValidChecksumAddress(address: string): boolean {
        return true;
    }


public isValidAddress(address: string): boolean {
        return false;
    }


public publicToAddress(publicKey: string): string {
        return publicKey;
    }


public privateToPublic(privateKey: string): string {
        const keyPair = nacl.sign.keyPair.fromSecretKey(bs58Decode(privateKey));
        return 'ed25519:' + bs58Encode(Buffer.from(keyPair.publicKey));
    }


public privateToAddress(privateKey: string): string {
        return this.privateToPublic(privateKey);
    }


public getAccountFromPrivateKey(privateKey: string, index: number): IAccountState {
        const keyPair = nacl.sign.keyPair.fromSecretKey(bs58Decode(privateKey));
        const pk = bs58Encode(Buffer.from(keyPair.publicKey));
        const address = Buffer.from(bs58Decode(pk)).toString('hex');

        return {
            index: 0,
            type: AccountType.DEFAULT,
            selected: false,
            publicKey: 'ed25519:' + pk,
            address,
            blockchain: Blockchain.NEAR,
            tokens: generateTokensConfig(Blockchain.NEAR)
        };
    }


public amountToStd(value: BigNumber | number | string, decimals: number): BigNumber {
        return new BigNumber(value).multipliedBy(new BigNumber(10).pow(decimals));
    }


public amountFromStd(value: BigNumber | number | string, decimals: number): BigNumber {
        return new BigNumber(value).dividedBy(new BigNumber(10).pow(decimals));
    }


public convertUnit(value: BigNumber, from: string, to: string): BigNumber {
        return convert(value, from, to, config);
    }


public getLockupContract(accountId: string, chainId: ChainIdType): string {
        return sha256(Buffer.from(accountId)).substring(0, 40) + NEAR_LOCKUP_SUFFIX[chainId];
    }


(
  initialSchedule: Schedule
): ScheduleContextData => {
  const [schedule, setSchedule] = useState(initialSchedule);

  // TODO: keep track of states to support an undo button

  return {
    schedule,
    setSchedule,
  };
}


export class MidiSource {
    id: string;
    name: string;

    constructor(id: string, name: string) {
        this.id = id;
        this.name = name;
    }
}


<FilterNode extends Node>(
  dom: FilterNode,
  options: IGetNamedSelectorOptions
) => {
  const [query, filter] = getNamedSelector<FilterNode>(options);
  return selectAll(query, dom).filter(filter);
}


export default function fetchCount(cb: FetchCountCallback) {
  cb(0);
}


interface FetchCountCallback {
  (count: number): void;
}


(t) => {
  t.is(typeof Client, "function");
}


(t) => {
  t.notThrows(() => new Client("TRU5Z5MNWIEYP4F6DPH2T53IJWZIZ5GT1W"));
}


() => new Client("TRU5Z5MNWIEYP4F6DPH2T53IJWZIZ5GT1W")


(t) => {
  t.throws(() => new Client("y"));
}


() => new Client("y")


(t) => {
  const client = new Client("TRU5Z5MNWIEYP4F6DPH2T53IJWZIZ5GT1W");
  client.account("balance");
  t.pass();
}


(t) => {
  const client = new Client("TRU5Z5MNWIEYP4F6DPH2T53IJWZIZ5GT1W");
  client.account("balancemulti");
  t.pass();
}


(t) => {
  const client = new Client("TRU5Z5MNWIEYP4F6DPH2T53IJWZIZ5GT1W");
  client.account("tokentx");
  t.pass();
}


(t) => {
  const client = new Client("TRU5Z5MNWIEYP4F6DPH2T53IJWZIZ5GT1W");
  client.account("getminedblocks");
  t.pass();
}


(t) => {
  const client = new Client("TRU5Z5MNWIEYP4F6DPH2T53IJWZIZ5GT1W");
  client.account("txlist");
  t.pass();
}


(t) => {
  const client = new Client("TRU5Z5MNWIEYP4F6DPH2T53IJWZIZ5GT1W");
  client.account("txlistinternal");
  t.pass();
}


(t) => {
  const client = new Client("TRU5Z5MNWIEYP4F6DPH2T53IJWZIZ5GT1W");
  client.account("txlistinternaltxhash");
  t.pass();
}


(t) => {
  const client = new Client("TRU5Z5MNWIEYP4F6DPH2T53IJWZIZ5GT1W");
  t.throws(() => client.account("unknown"));
}


() => client.account("unknown")


(t) => {
  t.is(Client.version, pkg.version);
}


function roleFromPlaceholderArn(scope: Construct, arn: string): iam.IRole;


function roleFromPlaceholderArn(scope: Construct, arn: string | undefined): iam.IRole | undefined;


function roleFromPlaceholderArn(scope: Construct, arn: string | undefined): iam.IRole | undefined {
  if (!arn) { return undefined; }

  // Use placeholdered arn as construct ID.
  const id = arn;

  scope = hackyRoleScope(scope, arn);

  // https://github.com/aws/aws-cdk/issues/7255
  let existingRole = scope.node.tryFindChild(`ImmutableRole${id}`) as iam.IRole;
  if (existingRole) { return existingRole; }
  // For when #7255 is fixed.
  existingRole = scope.node.tryFindChild(id) as iam.IRole;
  if (existingRole) { return existingRole; }

  return iam.Role.fromRoleArn(scope, id, cfnExpressionFromManifestString(arn), { mutable: false });
}


/**
 * MASSIVE HACK
 *
 * We have a bug in the CDK where it's only going to consider Roles that are physically in a
 * different Stack object from the Pipeline "cross-account", and will add the appropriate
 * Bucket/Key policies.
 * https://github.com/aws/aws-cdk/pull/8280 will resolve this, but for now we fake it by hacking
 * up a Stack object to root the role in!
 *
 * Fortunatey, we can just 'new up' an unrooted Stack (unit tests do this all the time) and toss it
 * away. It will never be synthesized, but all the logic happens to work out!
 */
function hackyRoleScope(scope: Construct, arn: string): Construct {
  const parts = Arn.parse(cxapi.EnvironmentPlaceholders.replace(arn, {
    accountId: '', // Empty string on purpose, see below
    partition: '',
    region: '',
  }));
  return new Stack(undefined, undefined, {
    env: {
      // Empty string means ARN had a placeholder which means same account as pipeline stack
      account: parts.account || Stack.of(scope).account,
      // 'region' from an IAM ARN is always an empty string, so no point.
    },
  });
}


/**
 * Return a CloudFormation expression from a manifest string with placeholders
 */
function cfnExpressionFromManifestString(s: string) {
  // This implementation relies on the fact that the manifest placeholders are
  // '${AWS::Partition}' etc., and so are the same values as those that are
  // trivially substituable using a `Fn.sub`.
  return Fn.sub(s);
}


function isStackArtifact(a: cxapi.CloudArtifact): a is cxapi.CloudFormationStackArtifact {
  // instanceof is too risky, and we're at a too late stage to properly fix.
  // return a instanceof cxapi.CloudFormationStackArtifact;
  return a.constructor.name === 'CloudFormationStackArtifact';
}


s => s.id


/**
 * Action to deploy a CDK Stack
 *
 * Adds two CodePipeline Actions to the pipeline: one to create a ChangeSet
 * and one to execute it.
 *
 * You do not need to instantiate this action yourself -- it will automatically
 * be added by the pipeline when you add stack artifacts or entire stages.
 */
export class DeployCdkStackAction implements codepipeline.IAction {
  /**
   * Construct a DeployCdkStackAction from a Stack artifact
   */
  public static fromStackArtifact(scope: Construct, artifact: cxapi.CloudFormationStackArtifact, options: CdkStackActionFromArtifactOptions) {
    if (!artifact.assumeRoleArn) {
      throw new Error(`Stack '${artifact.stackName}' does not have deployment role information; use the 'DefaultStackSynthesizer' synthesizer, or set the '@aws-cdk/core:newStyleStackSynthesis' context key.`);
    }

    const actionRole = roleFromPlaceholderArn(scope, artifact.assumeRoleArn);
    const cloudFormationExecutionRole = roleFromPlaceholderArn(scope, artifact.cloudFormationExecutionRoleArn);

    const artRegion = artifact.environment.region;
    const region = artRegion === Stack.of(scope).region || artRegion === cxapi.UNKNOWN_REGION ? undefined : artRegion;

    // We need the path of the template relative to the root Cloud Assembly
    // It should be easier to get this, but for now it is what it is.
    const appAsmRoot = assemblyBuilderOf(appOf(scope)).outdir;
    const fullTemplatePath = path.join(artifact.assembly.directory, artifact.templateFile);
    const templatePath = path.relative(appAsmRoot, fullTemplatePath);

    return new DeployCdkStackAction({
      actionRole,
      cloudFormationExecutionRole,
      templatePath,
      region,
      stackArtifactId: artifact.id,
      dependencyStackArtifactIds: artifact.dependencies.filter(isStackArtifact).map(s => s.id),
      stackName: options.stackName ?? artifact.stackName,
      ...options,
    });
  }

  /**
   * The runorder for the prepare action
   */
  public readonly prepareRunOrder: number;

  /**
   * The runorder for the execute action
   */
  public readonly executeRunOrder: number;

  /**
   * Name of the deployed stack
   */
  public readonly stackName: string;

  /**
   * Artifact id of the artifact this action was based on
   */
  public readonly stackArtifactId?: string;

  /**
   * Artifact ids of the artifact this stack artifact depends on
   */
  public readonly dependencyStackArtifactIds: string[];

  private readonly prepareChangeSetAction: cpactions.CloudFormationCreateReplaceChangeSetAction;
  private readonly executeChangeSetAction: cpactions.CloudFormationExecuteChangeSetAction;

  constructor(props: DeployCdkStackActionProps) {
    if (props.output && !props.outputFileName) {
      throw new Error('If \'output\' is set, \'outputFileName\' is also required');
    }

    this.stackArtifactId = props.stackArtifactId;
    this.dependencyStackArtifactIds = props.dependencyStackArtifactIds ?? [];

    this.prepareRunOrder = props.prepareRunOrder ?? 1;
    this.executeRunOrder = props.executeRunOrder ?? this.prepareRunOrder + 1;
    this.stackName = props.stackName;
    const baseActionName = props.baseActionName ?? this.stackName;
    const changeSetName = props.changeSetName ?? 'PipelineChange';

    this.prepareChangeSetAction = new cpactions.CloudFormationCreateReplaceChangeSetAction({
      actionName: `${baseActionName}.Prepare`,
      changeSetName,
      runOrder: this.prepareRunOrder,
      stackName: this.stackName,
      templatePath: props.cloudAssemblyInput.atPath(props.templatePath),
      adminPermissions: false,
      role: props.actionRole,
      deploymentRole: props.cloudFormationExecutionRole,
      region: props.region,
      capabilities: [cfn.CloudFormationCapabilities.NAMED_IAM, cfn.CloudFormationCapabilities.AUTO_EXPAND],
    });
    this.executeChangeSetAction = new cpactions.CloudFormationExecuteChangeSetAction({
      actionName: `${baseActionName}.Deploy`,
      changeSetName,
      runOrder: this.executeRunOrder,
      stackName: this.stackName,
      role: props.actionRole,
      region: props.region,
      outputFileName: props.outputFileName,
      output: props.output,
    });
  }

  /**
   * Exists to implement IAction
   */
  public bind(scope: Construct, stage: codepipeline.IStage, options: codepipeline.ActionBindOptions):
  codepipeline.ActionConfig {
    stage.addAction(this.prepareChangeSetAction);

    return this.executeChangeSetAction.bind(scope, stage, options);
  }

  /**
   * Exists to implement IAction
   */
  public onStateChange(name: string, target?: events.IRuleTarget, options?: events.RuleProps): events.Rule {
    return this.executeChangeSetAction.onStateChange(name, target, options);
  }

  /**
   * Exists to implement IAction
   */
  public get actionProperties(): codepipeline.ActionProperties {
    return this.executeChangeSetAction.actionProperties;
  }
}


/**
 * Customization options for a DeployCdkStackAction
 */
export interface DeployCdkStackActionOptions {
  /**
   * Base name of the action
   *
   * @default stackName
   */
  readonly baseActionName?: string;

  /**
   * The CodePipeline artifact that holds the Cloud Assembly.
   */
  readonly cloudAssemblyInput: codepipeline.Artifact;

  /**
   * Run order for the Prepare action
   *
   * @default 1
   */
  readonly prepareRunOrder?: number;

  /**
   * Run order for the Execute action
   *
   * @default - prepareRunOrder + 1
   */
  readonly executeRunOrder?: number;

  /**
   * Artifact to write Stack Outputs to
   *
   * @default - No outputs
   */
  readonly output?: codepipeline.Artifact;

  /**
   * Filename in output to write Stack outputs to
   *
   * @default - Required when 'output' is set
   */
  readonly outputFileName?: string;

  /**
   * Name of the change set to create and deploy
   *
   * @default 'PipelineChange'
   */
  readonly changeSetName?: string;
}


/**
 * Properties for a DeployCdkStackAction
 */
export interface DeployCdkStackActionProps extends DeployCdkStackActionOptions {
  /**
   * Relative path of template in the input artifact
   */
  readonly templatePath: string;

  /**
   * Role for the action to assume
   *
   * This controls the account to deploy into
   */
  readonly actionRole: iam.IRole;

  /**
   * The name of the stack that should be created/updated
   */
  readonly stackName: string;

  /**
   * Role to execute CloudFormation under
   *
   * @default - Execute CloudFormation using the action role
   */
  readonly cloudFormationExecutionRole?: iam.IRole;

  /**
   * Region to deploy into
   *
   * @default - Same region as pipeline
   */
  readonly region?: string;

  /**
   * Artifact ID for the stack deployed here
   *
   * Used for pipeline order checking.
   *
   * @default - Order will not be checked
   */
  readonly stackArtifactId?: string;

  /**
   * Artifact ID for the stacks this stack depends on
   *
   * Used for pipeline order checking.
   *
   * @default - No dependencies
   */
  readonly dependencyStackArtifactIds?: string[];
}


/**
 * Options for the 'fromStackArtifact' operation
 */
export interface CdkStackActionFromArtifactOptions extends DeployCdkStackActionOptions {
  /**
   * The name of the stack that should be created/updated
   *
   * @default - Same as stack artifact
   */
  readonly stackName?: string;
}


/**
 * Options for CdkDeployAction.fromStackArtifact
 */
export interface FromStackArtifactOptions {
  /**
   * The CodePipeline artifact that holds the Cloud Assembly.
   */
  readonly cloudAssemblyInput: codepipeline.Artifact;

  /**
   * Run order for the 2 actions that will be created
   *
   * @default 1
   */
  readonly prepareRunOrder?: number;

  /**
   * Run order for the Execute action
   *
   * @default - prepareRunOrder + 1
   */
  readonly executeRunOrder?: number;

  /**
   * Artifact to write Stack Outputs to
   *
   * @default - No outputs
   */
  readonly output?: codepipeline.Artifact;

  /**
   * Filename in output to write Stack outputs to
   *
   * @default - Required when 'output' is set
   */
  readonly outputFileName?: string;
}


/**
   * Construct a DeployCdkStackAction from a Stack artifact
   */
public static fromStackArtifact(scope: Construct, artifact: cxapi.CloudFormationStackArtifact, options: CdkStackActionFromArtifactOptions) {
    if (!artifact.assumeRoleArn) {
      throw new Error(`Stack '${artifact.stackName}' does not have deployment role information; use the 'DefaultStackSynthesizer' synthesizer, or set the '@aws-cdk/core:newStyleStackSynthesis' context key.`);
    }

    const actionRole = roleFromPlaceholderArn(scope, artifact.assumeRoleArn);
    const cloudFormationExecutionRole = roleFromPlaceholderArn(scope, artifact.cloudFormationExecutionRoleArn);

    const artRegion = artifact.environment.region;
    const region = artRegion === Stack.of(scope).region || artRegion === cxapi.UNKNOWN_REGION ? undefined : artRegion;

    // We need the path of the template relative to the root Cloud Assembly
    // It should be easier to get this, but for now it is what it is.
    const appAsmRoot = assemblyBuilderOf(appOf(scope)).outdir;
    const fullTemplatePath = path.join(artifact.assembly.directory, artifact.templateFile);
    const templatePath = path.relative(appAsmRoot, fullTemplatePath);

    return new DeployCdkStackAction({
      actionRole,
      cloudFormationExecutionRole,
      templatePath,
      region,
      stackArtifactId: artifact.id,
      dependencyStackArtifactIds: artifact.dependencies.filter(isStackArtifact).map(s => s.id),
      stackName: options.stackName ?? artifact.stackName,
      ...options,
    });
  }


/**
   * Exists to implement IAction
   */
public bind(scope: Construct, stage: codepipeline.IStage, options: codepipeline.ActionBindOptions):
  codepipeline.ActionConfig {
    stage.addAction(this.prepareChangeSetAction);

    return this.executeChangeSetAction.bind(scope, stage, options);
  }


/**
   * Exists to implement IAction
   */
public onStateChange(name: string, target?: events.IRuleTarget, options?: events.RuleProps): events.Rule {
    return this.executeChangeSetAction.onStateChange(name, target, options);
  }


(data: any) => {
        this.groupheadings = data.GroupHeadings;
        this.subcategories = data.Subcategories;
      }


error => console.log('Categories load Error: ' + (<Error>error).message)


result => {
      this.isDupeQuestion = result as boolean;
      if (this.isDupeQuestion) {
        this.isCustomQuestionEmpty = false;
        return;
      }

      const salLevels: string[] = [];
      if (this.customSalL) { salLevels.push("L"); }
      if (this.customSalM) { salLevels.push("M"); }
      if (this.customSalH) { salLevels.push("H"); }
      if (this.customSalVH) { salLevels.push("VH"); }

      // push it to the API
      this.setBuilderSvc.addCustomQuestion(this.customQuestionText, this.selectedGHId, this.subcatText, salLevels).subscribe(() => {
        if (!!this.setBuilderSvc.activeRequirement) {
          this.setBuilderSvc.navRequirementDetail(this.setBuilderSvc.activeRequirement.RequirementID);
        } else {
          // navigate back to the questions list
          this.setBuilderSvc.navQuestionList();
        }
      });
    }


() => {
        if (!!this.setBuilderSvc.activeRequirement) {
          this.setBuilderSvc.navRequirementDetail(this.setBuilderSvc.activeRequirement.RequirementID);
        } else {
          // navigate back to the questions list
          this.setBuilderSvc.navQuestionList();
        }
      }


(result: any[]) => {
      this.searchPerformed = true;
      this.searchHits = result;
      this.searching = false;
      this.searchHits.forEach(q => {
        q.Sal = {};
        q.Sal['L'] = false;
        q.Sal['M'] = false;
        q.Sal['H'] = false;
        q.Sal['VH'] = false;
        q.SalLevels.forEach(s => {
          q.Sal[s] = true;
        });
      });
      this.selectedQuestionId = 0;
    }


q => {
        q.Sal = {};
        q.Sal['L'] = false;
        q.Sal['M'] = false;
        q.Sal['H'] = false;
        q.Sal['VH'] = false;
        q.SalLevels.forEach(s => {
          q.Sal[s] = true;
        });
      }


s => {
          q.Sal[s] = true;
        }


qid => {
      const qqq = this.searchHits.find(qq => qq.QuestionID === qid);
      if (this.missingSAL(qqq)) {
        return;
      }
      selectedQs.push(qqq);
    }


qq => qq.QuestionID === qid


() => {
          if (!!this.setBuilderSvc.activeRequirement) {
            this.setBuilderSvc.navRequirementDetail(this.setBuilderSvc.activeRequirement.RequirementID);
          } else {
            // navigate back to the questions list
            this.setBuilderSvc.navQuestionList();
          }
        }


x => x !== level


qid => qid === q.QuestionID


@Component({
  selector: 'app-add-question',
  templateUrl: './add-question.component.html',
  // tslint:disable-next-line:use-host-property-decorator
  host: { class: 'd-flex flex-column flex-11a w-100' }
})
export class AddQuestionComponent implements OnInit {

  customQuestionText: string = '';
  isDupeQuestion = false;
  isCustomQuestionEmpty = false;
  isCatOrSubcatEmpty = false;
  isSalSelectionEmpty = false;

  // custom add SAL values
  customSalL = false;
  customSalM = false;
  customSalH = false;
  customSalVH = false;

  groupheadings: CategoryEntry[];
  subcategories: CategoryEntry[];
  selectedGHId: number = 0;
  subcatText: string;

  searching = false;
  searchTerms: string = '';
  searchError = false;
  searchPerformed = false;
  searchHits: Question[] = null;
  selectedQuestionId: number;
  selectedQuestionIds: number[] = [];

  constructor(public setBuilderSvc: SetBuilderService) { }

  ngOnInit() {
    this.setBuilderSvc.getCategoriesSubcategoriesGroupHeadings().subscribe(
      (data: any) => {
        this.groupheadings = data.GroupHeadings;
        this.subcategories = data.Subcategories;
      },
      error => console.log('Categories load Error: ' + (<Error>error).message)
    );
  }

  /**
   * Creates a new question for the set.
   */
  addCustomQuestion() {
    // validate that they typed something
    this.customQuestionText = this.customQuestionText && this.customQuestionText.trim();

    this.isCustomQuestionEmpty = (this.customQuestionText.length === 0);
    this.isCatOrSubcatEmpty = this.selectedGHId.toString() === "0" || (!this.subcatText || this.subcatText.trim().length === 0);
    this.isSalSelectionEmpty = !this.customSalL && !this.customSalM && !this.customSalH && !this.customSalVH;
    this.isDupeQuestion = false;

    if (this.isCustomQuestionEmpty || this.isCatOrSubcatEmpty || this.isSalSelectionEmpty) {
      return;
    }

    // check to see if this question text already exists in the database.
    this.setBuilderSvc.existsQuestionText(this.customQuestionText).subscribe(result => {
      this.isDupeQuestion = result as boolean;
      if (this.isDupeQuestion) {
        this.isCustomQuestionEmpty = false;
        return;
      }

      const salLevels: string[] = [];
      if (this.customSalL) { salLevels.push("L"); }
      if (this.customSalM) { salLevels.push("M"); }
      if (this.customSalH) { salLevels.push("H"); }
      if (this.customSalVH) { salLevels.push("VH"); }

      // push it to the API
      this.setBuilderSvc.addCustomQuestion(this.customQuestionText, this.selectedGHId, this.subcatText, salLevels).subscribe(() => {
        if (!!this.setBuilderSvc.activeRequirement) {
          this.setBuilderSvc.navRequirementDetail(this.setBuilderSvc.activeRequirement.RequirementID);
        } else {
          // navigate back to the questions list
          this.setBuilderSvc.navQuestionList();
        }
      });
    });
  }

  /**
   * Search questions to find matches to the specified terms.
   */
  search() {
    this.searchError = false;
    this.searching = true;
    this.searchTerms = this.searchTerms.trim();

    if (this.searchTerms.length === 0) {
      this.searching = false;
      this.searchError = true;
      return;
    }

    this.setBuilderSvc.searchQuestions(this.searchTerms).subscribe((result: any[]) => {
      this.searchPerformed = true;
      this.searchHits = result;
      this.searching = false;
      this.searchHits.forEach(q => {
        q.Sal = {};
        q.Sal['L'] = false;
        q.Sal['M'] = false;
        q.Sal['H'] = false;
        q.Sal['VH'] = false;
        q.SalLevels.forEach(s => {
          q.Sal[s] = true;
        });
      });
      this.selectedQuestionId = 0;
    });
  }

  /**
   * Mark the existing question for selection.
   */
  markSelected(q: Question, e: any) {
    if (e.target.checked) {
      this.selectedQuestionIds.push(q.QuestionID);
    } else {
      this.selectedQuestionIds.splice(this.selectedQuestionIds.findIndex(function (x) {
        return x === q.QuestionID;
      }), 1);
    }
  }

  /**
   * xxx
   */
  isQuestionSelected(q) {
    return this.selectedQuestionIds.findIndex(function (x) {
      return x === q.QuestionID;
    }) >= 0;
  }

  /**
   * Add the question to the set.
   */
  addSelectedQuestions() {
    // make sure they selected a SAL
    const selectedQs = [];

    this.selectedQuestionIds.forEach(qid => {
      const qqq = this.searchHits.find(qq => qq.QuestionID === qid);
      if (this.missingSAL(qqq)) {
        return;
      }
      selectedQs.push(qqq);
    });

    this.setBuilderSvc.addExistingQuestions(selectedQs)
        .subscribe(() => {
          if (!!this.setBuilderSvc.activeRequirement) {
            this.setBuilderSvc.navRequirementDetail(this.setBuilderSvc.activeRequirement.RequirementID);
          } else {
            // navigate back to the questions list
            this.setBuilderSvc.navQuestionList();
          }
        });
  }

  /**
   *
   */
  hasSAL(q: Question, level: string): boolean {
    return (q.SalLevels.indexOf(level) >= 0);
  }

  /**
   * Includes/removes the level from the list of applicable SAL levels for the question.
   */
  toggleSAL(q: Question, level: string, e) {
    const checked = e.target.checked;
    const a = q.SalLevels.indexOf(level);

    if (checked) {
      if (a <= 0) {
        q.SalLevels.push(level);
      }
    } else if (a >= 0) {
      q.SalLevels = q.SalLevels.filter(x => x !== level);
    }
  }

  /**
   * Indicates if no SAL levels are currently selected for the question.
   */
  missingSAL(q: Question) {
    if (!q) {
      return false;
    }
    if ((!!this.selectedQuestionIds.find(qid => qid === q.QuestionID)) && q.SalLevels.length === 0) {
      return true;
    }
    return false;
  }
}


ngOnInit() {
    this.setBuilderSvc.getCategoriesSubcategoriesGroupHeadings().subscribe(
      (data: any) => {
        this.groupheadings = data.GroupHeadings;
        this.subcategories = data.Subcategories;
      },
      error => console.log('Categories load Error: ' + (<Error>error).message)
    );
  }


/**
   * Creates a new question for the set.
   */
addCustomQuestion() {
    // validate that they typed something
    this.customQuestionText = this.customQuestionText && this.customQuestionText.trim();

    this.isCustomQuestionEmpty = (this.customQuestionText.length === 0);
    this.isCatOrSubcatEmpty = this.selectedGHId.toString() === "0" || (!this.subcatText || this.subcatText.trim().length === 0);
    this.isSalSelectionEmpty = !this.customSalL && !this.customSalM && !this.customSalH && !this.customSalVH;
    this.isDupeQuestion = false;

    if (this.isCustomQuestionEmpty || this.isCatOrSubcatEmpty || this.isSalSelectionEmpty) {
      return;
    }

    // check to see if this question text already exists in the database.
    this.setBuilderSvc.existsQuestionText(this.customQuestionText).subscribe(result => {
      this.isDupeQuestion = result as boolean;
      if (this.isDupeQuestion) {
        this.isCustomQuestionEmpty = false;
        return;
      }

      const salLevels: string[] = [];
      if (this.customSalL) { salLevels.push("L"); }
      if (this.customSalM) { salLevels.push("M"); }
      if (this.customSalH) { salLevels.push("H"); }
      if (this.customSalVH) { salLevels.push("VH"); }

      // push it to the API
      this.setBuilderSvc.addCustomQuestion(this.customQuestionText, this.selectedGHId, this.subcatText, salLevels).subscribe(() => {
        if (!!this.setBuilderSvc.activeRequirement) {
          this.setBuilderSvc.navRequirementDetail(this.setBuilderSvc.activeRequirement.RequirementID);
        } else {
          // navigate back to the questions list
          this.setBuilderSvc.navQuestionList();
        }
      });
    });
  }


/**
   * Search questions to find matches to the specified terms.
   */
search() {
    this.searchError = false;
    this.searching = true;
    this.searchTerms = this.searchTerms.trim();

    if (this.searchTerms.length === 0) {
      this.searching = false;
      this.searchError = true;
      return;
    }

    this.setBuilderSvc.searchQuestions(this.searchTerms).subscribe((result: any[]) => {
      this.searchPerformed = true;
      this.searchHits = result;
      this.searching = false;
      this.searchHits.forEach(q => {
        q.Sal = {};
        q.Sal['L'] = false;
        q.Sal['M'] = false;
        q.Sal['H'] = false;
        q.Sal['VH'] = false;
        q.SalLevels.forEach(s => {
          q.Sal[s] = true;
        });
      });
      this.selectedQuestionId = 0;
    });
  }


/**
   * Mark the existing question for selection.
   */
markSelected(q: Question, e: any) {
    if (e.target.checked) {
      this.selectedQuestionIds.push(q.QuestionID);
    } else {
      this.selectedQuestionIds.splice(this.selectedQuestionIds.findIndex(function (x) {
        return x === q.QuestionID;
      }), 1);
    }
  }


/**
   * xxx
   */
isQuestionSelected(q) {
    return this.selectedQuestionIds.findIndex(function (x) {
      return x === q.QuestionID;
    }) >= 0;
  }


/**
   * Add the question to the set.
   */
addSelectedQuestions() {
    // make sure they selected a SAL
    const selectedQs = [];

    this.selectedQuestionIds.forEach(qid => {
      const qqq = this.searchHits.find(qq => qq.QuestionID === qid);
      if (this.missingSAL(qqq)) {
        return;
      }
      selectedQs.push(qqq);
    });

    this.setBuilderSvc.addExistingQuestions(selectedQs)
        .subscribe(() => {
          if (!!this.setBuilderSvc.activeRequirement) {
            this.setBuilderSvc.navRequirementDetail(this.setBuilderSvc.activeRequirement.RequirementID);
          } else {
            // navigate back to the questions list
            this.setBuilderSvc.navQuestionList();
          }
        });
  }


/**
   *
   */
hasSAL(q: Question, level: string): boolean {
    return (q.SalLevels.indexOf(level) >= 0);
  }


/**
   * Includes/removes the level from the list of applicable SAL levels for the question.
   */
toggleSAL(q: Question, level: string, e) {
    const checked = e.target.checked;
    const a = q.SalLevels.indexOf(level);

    if (checked) {
      if (a <= 0) {
        q.SalLevels.push(level);
      }
    } else if (a >= 0) {
      q.SalLevels = q.SalLevels.filter(x => x !== level);
    }
  }


/**
   * Indicates if no SAL levels are currently selected for the question.
   */
missingSAL(q: Question) {
    if (!q) {
      return false;
    }
    if ((!!this.selectedQuestionIds.find(qid => qid === q.QuestionID)) && q.SalLevels.length === 0) {
      return true;
    }
    return false;
  }


@Injectable({
  providedIn: 'root'
})
export class TagService {
  //Instance variables
  private tagsUrl = 'https://rapidscemeteryapi-dev.herokuapp.com/api/tag/';

  constructor(private http: HttpClient) { }

  /**
   * Method that will use the Angular http client
   * to retrieve data from the API.
   */
  getTags(): Observable<Tag[]> {
    return this.http.get<Tag[]>(this.tagsUrl+'getAllTags');
  }

  /**
   * Method that will use the Angular http client to add a tag
   * using the API.
   * @param tag
   */
  addItem(tag: object): Observable<HttpResponse<Tag>> {
    return this.http.post<Tag>(this.tagsUrl+'saveTag', tag, {observe: 'response'});
  }

  /**
   * Method that will use the Angular http client to update a tag
   * using the API.
   * @param tag
   */
  updateTag(tag: object): Observable<HttpResponse<Tag>> {
    return this.http.post<Tag>(this.tagsUrl+'saveTag', tag, {observe: 'response'});
  }

  /**
   * Method that will use the Angular http client to delete a tag
   * using the API.
   * @param tagIdObject
   */
  deleteTag(tagIdObject: object): Observable<boolean> {
    return this.http.post<boolean>(this.tagsUrl+'removeTagById', tagIdObject);
  }
}


/**
   * Method that will use the Angular http client
   * to retrieve data from the API.
   */
getTags(): Observable<Tag[]> {
    return this.http.get<Tag[]>(this.tagsUrl+'getAllTags');
  }


/**
   * Method that will use the Angular http client to add a tag
   * using the API.
   * @param tag
   */
addItem(tag: object): Observable<HttpResponse<Tag>> {
    return this.http.post<Tag>(this.tagsUrl+'saveTag', tag, {observe: 'response'});
  }


/**
   * Method that will use the Angular http client to update a tag
   * using the API.
   * @param tag
   */
updateTag(tag: object): Observable<HttpResponse<Tag>> {
    return this.http.post<Tag>(this.tagsUrl+'saveTag', tag, {observe: 'response'});
  }


/**
   * Method that will use the Angular http client to delete a tag
   * using the API.
   * @param tagIdObject
   */
deleteTag(tagIdObject: object): Observable<boolean> {
    return this.http.post<boolean>(this.tagsUrl+'removeTagById', tagIdObject);
  }


function getFirebaseApp() {
  if (firebaseApp) {
    return firebaseApp;
  }
  firebaseApp = firebase.initializeApp(config);
  return firebaseApp;
}


function getMessagesRef() {
  const firebaseAppInsance = getFirebaseApp();
  return firebaseAppInsance.database().ref(`${FIREBASE_PREFIX}/messages`);
}


async function sendMessage(message: RelayActionWithMessage) {
  const sanitizedPayload = JSON.parse(JSON.stringify(message));
  await getMessagesRef()
    .child(message.recipient)
    .push(sanitizedPayload);
}


async function readAndFeedMessages() {
  await Promise.all(messages.map(message => sendMessage({...message, recipient: HUB_ADDRESS})));
  getFirebaseApp().delete();
}


message => sendMessage({...message, recipient: HUB_ADDRESS})


() => {
        this.triggerOnScroll();
      }


() => {
        this.close();
      }


export default class Drawer extends DialogInterface {
  _scrollableEl: HTMLElement;

  position: string;

  constructor(options: DrawerOptions = {}) {
    super(options);

    this.position = options.position || "right";
    this.$(".drawer-dialog").classList.add(
      `drawer-dialog-position-${this.position}`
    );

    this._scrollableEl = this.$(".drawer-dialog");

    if (options.onScroll) {
      this._scrollableEl.addEventListener("scroll", () => {
        this.triggerOnScroll();
      });
    }
  }

  open(): void {
    super.open();

    this.element.classList.add("open");
  }

  close(): void {
    super.close();

    this.element.classList.remove("open");
  }

  getScrollableElement(): HTMLElement {
    return this._scrollableEl;
  }

  // TODO explore using an HTMLDialogElement here
  // https://developer.mozilla.org/en-US/docs/Web/API/HTMLDialogElement
  _constructElement(options: DrawerOptions): HTMLDivElement {
    const titleId = `drawer-title-${options.id}`;

    const drawer = createElement<HTMLDivElement>(`

            ${
              options.headerSymbol || ""
            }

          <button type="button" title="${this._getCloseButtonMessage(
            true
          )}" class="dialog-close modal-dialog-close">
            Close this dialog <svg aria-hidden="true" focusable="false" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><path d="M217.5 256l137.2-137.2c4.7-4.7 4.7-12.3 0-17l-8.5-8.5c-4.7-4.7-12.3-4.7-17 0L192 230.5 54.8 93.4c-4.7-4.7-12.3-4.7-17 0l-8.5 8.5c-4.7 4.7-4.7 12.3 0 17L166.5 256 29.4 393.2c-4.7 4.7-4.7 12.3 0 17l8.5 8.5c4.7 4.7 12.3 4.7 17 0L192 281.5l137.2 137.2c4.7 4.7 12.3 4.7 17 0l8.5-8.5c4.7-4.7 4.7-12.3 0-17L217.5 256z"></path></svg>
          </button>

`);

    (drawer.querySelector(".dialog-close") as HTMLDivElement).addEventListener(
      "click",
      () => {
        this.close();
      }
    );

    return drawer;
  }
}


interface DrawerOptions extends DialogInterfaceOptions {
  position?: string;
  contentMessage?: string;
}


open(): void {
    super.open();

    this.element.classList.add("open");
  }


close(): void {
    super.close();

    this.element.classList.remove("open");
  }


getScrollableElement(): HTMLElement {
    return this._scrollableEl;
  }


// TODO explore using an HTMLDialogElement here
// https://developer.mozilla.org/en-US/docs/Web/API/HTMLDialogElement
_constructElement(options: DrawerOptions): HTMLDivElement {
    const titleId = `drawer-title-${options.id}`;

    const drawer = createElement<HTMLDivElement>(`

            ${
              options.headerSymbol || ""
            }

          <button type="button" title="${this._getCloseButtonMessage(
            true
          )}" class="dialog-close modal-dialog-close">
            Close this dialog <svg aria-hidden="true" focusable="false" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><path d="M217.5 256l137.2-137.2c4.7-4.7 4.7-12.3 0-17l-8.5-8.5c-4.7-4.7-12.3-4.7-17 0L192 230.5 54.8 93.4c-4.7-4.7-12.3-4.7-17 0l-8.5 8.5c-4.7 4.7-4.7 12.3 0 17L166.5 256 29.4 393.2c-4.7 4.7-4.7 12.3 0 17l8.5 8.5c4.7 4.7 12.3 4.7 17 0L192 281.5l137.2 137.2c4.7 4.7 12.3 4.7 17 0l8.5-8.5c4.7-4.7 4.7-12.3 0-17L217.5 256z"></path></svg>
          </button>

`);

    (drawer.querySelector(".dialog-close") as HTMLDivElement).addEventListener(
      "click",
      () => {
        this.close();
      }
    );

    return drawer;
  }


() => {
  let component: TargetScaffoldComponent;
  let fixture: ComponentFixture<TargetScaffoldComponent>;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ TargetScaffoldComponent ]
    })
    .compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(TargetScaffoldComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });
}


() => {
    TestBed.configureTestingModule({
      declarations: [ TargetScaffoldComponent ]
    })
    .compileComponents();
  }


() => {
    fixture = TestBed.createComponent(TargetScaffoldComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  }


export default function mathCeil(n:number){
  return Math.ceil(n)
}


measure => measure.metric === key


metric => metric.key === key


historyItem => {
      return this.datePipe.transform(historyItem.date, 'dd-MMM-yy');
    }


historyItem => {
      return parseInt(historyItem.value, 10);
    }


facet => facet.property === 'assignees'


value1 => {
      if (value1.val) {
        issueAssignees.assignees.push(value1.val ? Utils.getNameFromEmail(value1.val) : 'Unassigned');
        issueAssignees.allIssues.push(value1.count);

        const fixedIssue = assigneesFacet.values.find(value2 => value2.val === value1.val);
        issueAssignees.fixedIssues.push(fixedIssue ? fixedIssue.count : 0);
      }
    }


value2 => value2.val === value1.val


@Injectable({
  providedIn: DashboardModule
})
export class SonarQubeHelper {

  constructor(private sharedService: SharedService,
              private datePipe: DatePipe) { }

  parseComponentMeasures(componentMeasures: any) {
    const projectMeasures = new ProjectMeasures();

    const alertStatusMeasure = this.getMeasure(componentMeasures.component, 'alert_status');
    projectMeasures.passed = alertStatusMeasure.value === 'OK';

    projectMeasures.measures.push(
      this.parseProjectMeasure(componentMeasures, 'bugs', 'reliability_rating', faBug)
    );
    projectMeasures.measures.push(
      this.parseProjectMeasure(componentMeasures, 'vulnerabilities', 'security_rating', faUnlockAlt)
    );
    projectMeasures.measures.push(
      this.parseProjectMeasure(componentMeasures, 'code_smells', 'sqale_rating', faRadiationAlt)
    );
    projectMeasures.measures.push(
      this.parseProjectMeasure(componentMeasures, 'duplicated_lines_density', 'duplicated_lines_density', faClone)
    );

    return projectMeasures;
  }

  private parseProjectMeasure(
    componentMeasures: any,
    measureKey: string,
    ratingMeasureKey: string,
    icon: IconDefinition) {
    const measure = this.getMeasure(componentMeasures.component, measureKey);
    const metric = this.getMetric(componentMeasures, measureKey);

    const ratingMeasure = this.getMeasure(componentMeasures.component, ratingMeasureKey);
    const rating = ratingMeasureKey === 'duplicated_lines_density' ?
      this.getDuplicationsRating(ratingMeasure.value) : this.getRating(ratingMeasure.value);

    return new Measure(metric.name, measure.value, rating, icon);
  }

  getMeasure(component: any, key: string) {
    return component.measures.find(measure => measure.metric === key);
  }

  getMetric(componentMeasures: any, key: string) {
    return componentMeasures.metrics.find(metric => metric.key === key);
  }

  getRating(value: string) {
    const rating = parseInt(value, 10);
    switch (rating) {
      case 1: return 'a';
      case 2: return 'b';
      case 3: return 'c';
      case 4: return 'd';
      case 5: return 'e';
      default: return '';
    }
  }

  getDuplicationsRating(value: string): string {
    const duplicatedLinesDensity = parseFloat(value);
    if (duplicatedLinesDensity > 20) {
      return 'e';
    } else if (duplicatedLinesDensity > 10) {
      return 'd';
    } else if (duplicatedLinesDensity > 5) {
      return 'c';
    } else if (duplicatedLinesDensity >= 3) {
      return 'b';
    }
    return 'a';
  }

  parseComponentMeasuresHistory(componentMeasuresHistory: any) {
    const projectTrend = new ProjectTrends();

    projectTrend.dates = componentMeasuresHistory.measures[0].history.map(historyItem => {
      return this.datePipe.transform(historyItem.date, 'dd-MMM-yy');
    });

    projectTrend.trends.push(
      this.parseProjectMeasureHistory(componentMeasuresHistory, 'bugs', faBug)
    );
    projectTrend.trends.push(
      this.parseProjectMeasureHistory(componentMeasuresHistory, 'vulnerabilities', faUnlockAlt)
    );
    projectTrend.trends.push(
      this.parseProjectMeasureHistory(componentMeasuresHistory, 'code_smells', faRadiationAlt)
    );
    projectTrend.trends.push(
      this.parseProjectMeasureHistory(componentMeasuresHistory, 'duplicated_lines_density', faClone)
    );

    return projectTrend;
  }

  private parseProjectMeasureHistory(
    componentMeasuresHistory: any,
    metricKey: string,
    icon: IconDefinition) {
    const trend = new Trend();

    trend.name = this.sharedService.getMetric(metricKey);

    const measure = this.getMeasure(componentMeasuresHistory, metricKey);
    trend.values = measure.history.map(historyItem => {
      return parseInt(historyItem.value, 10);
    });

    return trend;
  }

  parseIssueAssignees(issueSearchResults: any) {
    const issueAssignees = new IssueAssignees();

    const allAssigneesFacet = issueSearchResults[0].facets.find(facet => facet.property === 'assignees');
    const assigneesFacet = issueSearchResults[1].facets.find(facet => facet.property === 'assignees');

    allAssigneesFacet.values.forEach(value1 => {
      if (value1.val) {
        issueAssignees.assignees.push(value1.val ? Utils.getNameFromEmail(value1.val) : 'Unassigned');
        issueAssignees.allIssues.push(value1.count);

        const fixedIssue = assigneesFacet.values.find(value2 => value2.val === value1.val);
        issueAssignees.fixedIssues.push(fixedIssue ? fixedIssue.count : 0);
      }
    });

    return issueAssignees;
  }

}


parseComponentMeasures(componentMeasures: any) {
    const projectMeasures = new ProjectMeasures();

    const alertStatusMeasure = this.getMeasure(componentMeasures.component, 'alert_status');
    projectMeasures.passed = alertStatusMeasure.value === 'OK';

    projectMeasures.measures.push(
      this.parseProjectMeasure(componentMeasures, 'bugs', 'reliability_rating', faBug)
    );
    projectMeasures.measures.push(
      this.parseProjectMeasure(componentMeasures, 'vulnerabilities', 'security_rating', faUnlockAlt)
    );
    projectMeasures.measures.push(
      this.parseProjectMeasure(componentMeasures, 'code_smells', 'sqale_rating', faRadiationAlt)
    );
    projectMeasures.measures.push(
      this.parseProjectMeasure(componentMeasures, 'duplicated_lines_density', 'duplicated_lines_density', faClone)
    );

    return projectMeasures;
  }


private parseProjectMeasure(
    componentMeasures: any,
    measureKey: string,
    ratingMeasureKey: string,
    icon: IconDefinition) {
    const measure = this.getMeasure(componentMeasures.component, measureKey);
    const metric = this.getMetric(componentMeasures, measureKey);

    const ratingMeasure = this.getMeasure(componentMeasures.component, ratingMeasureKey);
    const rating = ratingMeasureKey === 'duplicated_lines_density' ?
      this.getDuplicationsRating(ratingMeasure.value) : this.getRating(ratingMeasure.value);

    return new Measure(metric.name, measure.value, rating, icon);
  }


getMeasure(component: any, key: string) {
    return component.measures.find(measure => measure.metric === key);
  }


getMetric(componentMeasures: any, key: string) {
    return componentMeasures.metrics.find(metric => metric.key === key);
  }


getRating(value: string) {
    const rating = parseInt(value, 10);
    switch (rating) {
      case 1: return 'a';
      case 2: return 'b';
      case 3: return 'c';
      case 4: return 'd';
      case 5: return 'e';
      default: return '';
    }
  }


getDuplicationsRating(value: string): string {
    const duplicatedLinesDensity = parseFloat(value);
    if (duplicatedLinesDensity > 20) {
      return 'e';
    } else if (duplicatedLinesDensity > 10) {
      return 'd';
    } else if (duplicatedLinesDensity > 5) {
      return 'c';
    } else if (duplicatedLinesDensity >= 3) {
      return 'b';
    }
    return 'a';
  }


parseComponentMeasuresHistory(componentMeasuresHistory: any) {
    const projectTrend = new ProjectTrends();

    projectTrend.dates = componentMeasuresHistory.measures[0].history.map(historyItem => {
      return this.datePipe.transform(historyItem.date, 'dd-MMM-yy');
    });

    projectTrend.trends.push(
      this.parseProjectMeasureHistory(componentMeasuresHistory, 'bugs', faBug)
    );
    projectTrend.trends.push(
      this.parseProjectMeasureHistory(componentMeasuresHistory, 'vulnerabilities', faUnlockAlt)
    );
    projectTrend.trends.push(
      this.parseProjectMeasureHistory(componentMeasuresHistory, 'code_smells', faRadiationAlt)
    );
    projectTrend.trends.push(
      this.parseProjectMeasureHistory(componentMeasuresHistory, 'duplicated_lines_density', faClone)
    );

    return projectTrend;
  }


private parseProjectMeasureHistory(
    componentMeasuresHistory: any,
    metricKey: string,
    icon: IconDefinition) {
    const trend = new Trend();

    trend.name = this.sharedService.getMetric(metricKey);

    const measure = this.getMeasure(componentMeasuresHistory, metricKey);
    trend.values = measure.history.map(historyItem => {
      return parseInt(historyItem.value, 10);
    });

    return trend;
  }


parseIssueAssignees(issueSearchResults: any) {
    const issueAssignees = new IssueAssignees();

    const allAssigneesFacet = issueSearchResults[0].facets.find(facet => facet.property === 'assignees');
    const assigneesFacet = issueSearchResults[1].facets.find(facet => facet.property === 'assignees');

    allAssigneesFacet.values.forEach(value1 => {
      if (value1.val) {
        issueAssignees.assignees.push(value1.val ? Utils.getNameFromEmail(value1.val) : 'Unassigned');
        issueAssignees.allIssues.push(value1.count);

        const fixedIssue = assigneesFacet.values.find(value2 => value2.val === value1.val);
        issueAssignees.fixedIssues.push(fixedIssue ? fixedIssue.count : 0);
      }
    });

    return issueAssignees;
  }


() => {
  type Context = {
    name: string;
    age: number;
  };
  type Event = { type: 'START' } | { type: 'STOP' } | { type: 'TOGGLE' };

  const exampleMachine = createMachine<Context, Event>({
    context: {
      name: 'John Doe',
      age: 21,
    },
    initial: 'stopped',
    states: {
      stopped: {
        on: { START: 'started', TOGGLE: 'started' },
      },
      started: {
        on: { STOP: 'stopped', TOGGLE: 'stopped' },
      },
    },
  });

  const {
    Provider: ExampleProvider,
    useInterpreter: useExampleInterpreter,
    useActor: useExampleActor,
    useSelector: useExampleSelector,
    useSend: useExampleSend,
  } = createReactContextHelpers('ExampleMachine', () => {
    return useInterpret(exampleMachine);
  });

  test('children callback function', () => {
    render(
      <ExampleProvider>
        {({ interpreter }) => {
          return
initial state: {interpreter.initialState.value}
;
        }}
      </ExampleProvider>,
    );

    expect(screen.getByText(/initial state/i)).toHaveTextContent('initial state: stopped');
  }


() => {
    return useInterpret(exampleMachine);
  }


() => {
    render(
      <ExampleProvider>
        {({ interpreter }) => {
          return
initial state: {interpreter.initialState.value}
;
        }}


async () => {
    const App: React.FC = () => {
      const [state, send] = useActor(useExampleInterpreter());
      return (
        <div>
          <button onClick={() => send('TOGGLE')}


() => {
      const [state, send] = useActor(useExampleInterpreter());
      return (
        <div>
          <button onClick={()


async () => {
    const App: React.FC = () => {
      const [state, send] = useExampleActor();
      return (
        <div>
          <button onClick={() => send('TOGGLE')}


() => {
      const [state, send] = useExampleActor();
      return (
        <div>
          <button onClick={()


async () => {
    const App: React.FC = () => {
      const send = useExampleSend();
      const [state] = useActor(useExampleInterpreter());
      return (
        <div>
          <button onClick={() => send('TOGGLE')}


() => {
      const send = useExampleSend();
      const [state] = useActor(useExampleInterpreter());
      return (
        <div>
          <button onClick={()


async () => {
    const App: React.FC = () => {
      const name = useExampleSelector(React.useCallback(state => state.context.name, []));
      return (

          name: {name}


() => {
      const name = useExampleSelector(React.useCallback(state => state.context.name, []));
      return (

          name: {name}


state => state.context.name


() => {
    const { Provider: ExampleProvider } = createReactContextHelpers(
      'ExampleMachine',
      ({ age }: { age: number }) => {
        return useInterpret(exampleMachine, { context: { age } });
      },
    );

    test('with provider props', () => {
      render(
        <ExampleProvider age={33}>
          {({ interpreter }) =>
age: {interpreter.initialState.context.age}
}
        </ExampleProvider>,
      );

      expect(screen.getByText(/age/i)).toHaveTextContent('age: 33');
    });
  }


({ age }: { age: number }) => {
        return useInterpret(exampleMachine, { context: { age } });
      }


() => {
      render(
        <ExampleProvider age={33}>
          {({ interpreter }) =>
age: {interpreter.initialState.context.age}
}
        </ExampleProvider>,
      );

      expect(screen.getByText(/age/i)).toHaveTextContent('age: 33');
    }


() => {
    const { Provider: ExampleProvider, ReactContext: ExampleContext } = createReactContextHelpers(
      'ExampleMachine',
      ({ age }: { age: number }) => {
        return useInterpret(exampleMachine, { context: { age } });
      },
    );

    test('raw context', () => {
      const App = () => {
        const interpreter = React.useContext(ExampleContext);
        return
age: {interpreter.initialState.context.age}
;
      };

      render(
        <ExampleProvider age={33}>
          <App />
        </ExampleProvider>,
      );

      expect(screen.getByText(/age/i)).toHaveTextContent('age: 33');
    });
  }


() => {
      const App = () => {
        const interpreter = React.useContext(ExampleContext);
        return
age: {interpreter.initialState.context.age}
;
      };

      render(
        <ExampleProvider age={33}>
          <App />
        </ExampleProvider>,
      );

      expect(screen.getByText(/age/i)).toHaveTextContent('age: 33');
    }


() => {
        const interpreter = React.useContext(ExampleContext);
        return
age: {interpreter.initialState.context.age}
;
      }


type Context = {
    name: string;
    age: number;
  };


type Event = { type: 'START' } | { type: 'STOP' } | { type: 'TOGGLE' };


return <p>


function MyApp({ Component, pageProps }: AppProps) {
  return (
    <ApolloProvider client= {client}>
      <Component {...pageProps} />
    </ApolloProvider>
  )
}


async (
    selector: string
): Promise<IWebhooksSchema> => {
    const doc: IWebhooksSchema | null = await WebhooksModel.findOne({ selector }, {}).lean().exec();

    if (doc === null) {
        return Promise.reject();
    }

    return Promise.resolve(doc);
}


async (
    req: IRequest
): Promise<IWebhookPayload[]> => {
    const hrstart: [number, number] = process.hrtime();

    const role: string = req.user.role;

    if (req.user.rolenbr < viewSecurity) {
        utils.logInfo(
            `$connections (connections): not allowed - email: ${req.user.email} - role: ${role} - org: ${req.user.org}`,
            req.transid,
            process.hrtime(hrstart)
        );

        return Promise.resolve([]);
    }

    const body: IQuery = { ...req.body };

    body.org = req.user.org;
    body.email = req.user.email;

    const filter: any = parseFilter(body);

    const rows: IWebhooksSchema[] | [] = await WebhooksModel.find(filter, {}).sort({ selector: 1 }).exec();

    utils.logInfo(
        `$webhooks (webhooks) - email: ${body.email} - org: ${body.org}`,
        req.transid,
        process.hrtime(hrstart)
    );

    const payload: IWebhookPayload[] = [];

    rows.forEach((row: IWebhooksSchema, i: number) => {
        const id: string = row._id.toString();

        if (row.idtype && row.idtype === EIdType.personal && body.email !== row.owner) {
            row.webhook = '/* redacted */';
        }

        /**
         * create a lock icon
         * - open is user and personal
         * - closed is personal but not the user
         * - no lock is functional
         */
        const lock: string | undefined =
            !row.idtype || (row.idtype && row.idtype !== EIdType.personal)
                ? undefined
                : body.email && body.email === row.owner
                ? 'fas fa-lock-open'
                : 'fas fa-lock';

        payload[i] = {
            createdby: row.owner || EIdType.functional,
            createddate: row.annotations.createddate.toISOString(),
            description: row.description,
            id,
            idtype: row.idtype || EIdType.functional,
            lock,
            name: row.name,
            org: row.project.org,
            owner: row.owner,
            selector: row.selector,
            viewicon: `${FaIcons.viewicon}`,
            webhook: row.webhook,
            webhookid: row._id.toString(),
        };

        if (req.user.rolenbr >= editSecurity) {
            if (
                row.idtype !== EIdType.personal ||
                req.user.rolenbr >= managerSecurity ||
                (row.idtype === EIdType.personal && body.email === row.owner)
            ) {
                payload[i].deleteicon = `${FaIcons.deleteicon}`;
            }

            if (row.idtype !== EIdType.personal || (row.idtype === EIdType.personal && body.email === row.owner)) {
                payload[i].editicon = `${FaIcons.editicon}`;
            }
        }
    });

    return Promise.resolve(payload);
}


(row: IWebhooksSchema, i: number) => {
        const id: string = row._id.toString();

        if (row.idtype && row.idtype === EIdType.personal && body.email !== row.owner) {
            row.webhook = '/* redacted */';
        }

        /**
         * create a lock icon
         * - open is user and personal
         * - closed is personal but not the user
         * - no lock is functional
         */
        const lock: string | undefined =
            !row.idtype || (row.idtype && row.idtype !== EIdType.personal)
                ? undefined
                : body.email && body.email === row.owner
                ? 'fas fa-lock-open'
                : 'fas fa-lock';

        payload[i] = {
            createdby: row.owner || EIdType.functional,
            createddate: row.annotations.createddate.toISOString(),
            description: row.description,
            id,
            idtype: row.idtype || EIdType.functional,
            lock,
            name: row.name,
            org: row.project.org,
            owner: row.owner,
            selector: row.selector,
            viewicon: `${FaIcons.viewicon}`,
            webhook: row.webhook,
            webhookid: row._id.toString(),
        };

        if (req.user.rolenbr >= editSecurity) {
            if (
                row.idtype !== EIdType.personal ||
                req.user.rolenbr >= managerSecurity ||
                (row.idtype === EIdType.personal && body.email === row.owner)
            ) {
                payload[i].deleteicon = `${FaIcons.deleteicon}`;
            }

            if (row.idtype !== EIdType.personal || (row.idtype === EIdType.personal && body.email === row.owner)) {
                payload[i].editicon = `${FaIcons.editicon}`;
            }
        }
    }


/// [[BottomPanel]] React component that shows the iModel name and path.
export function InfoBottomPanel(props: InfoBottomPanelProps) {
  const { name, filename } = props;
  const pathLabel = React.useMemo(() => i18n("InfoBottomPanel", "PathFormat", { filename }), [filename]);
  // Note: This is just a sample app. Don't construct a filename that results in an XSS injection due to the
  // use of dangerouslySetInnerHTML below. An app designed for end users would never display a path like this.
  return (
    <BottomPanel {...props} className="info-bottom-panel">
      <HeaderTitle label={name} iconSpec="icon-info-hollow" />
      {/* eslint-disable-next-line jam3/no-sanitizer-with-danger */}
      <div dangerouslySetInnerHTML={{ __html: pathLabel }}


() => i18n("InfoBottomPanel", "PathFormat", { filename })


/// Properties for the [[InfoBottomPanel]] React component.
interface InfoBottomPanelProps extends BottomPanelProps {
  /// The name of the iModel.
  name: string;
  /// The path for the iModel.
  filename: string;
}


(ingredient, index) => (
              <IngredientRowComponent
                key={index}


render(h): VNode {
    return (

          {
            this.ingredients.map((ingredient, index) => (
              <IngredientRowComponent
                key={index}
                ingredient={ingredient}
                removeIngredient={this.removeIngredient}
              />
            )


function getRandomIntInclusive(min: number, max: number): number {
    return Math.floor(Math.random() * (max - min + 1)) + min;
  }


/**
   * Basic node visitor. Inherit from this if you want a more sophisticated visitor, for instance all
   * renderers extends this class.
   */
export class NodeVisitor {
    visitNode(node: Node, state: State) {
      // ...
    }

    visitShape(node: Shape, state: State) {
      this.visitNode(node, state);
    }

    visitGroup(node: Group, state: State) {
      this.visitNode(node, state);
      var children = node.getChildren();
      for (var i = 0; i < children.length; i++) {
        children[i].visit(this, state);
      }
    }

    visitStage(node: Stage, state: State) {
      this.visitGroup(node, state);
    }

    visitRenderable(node: Renderable, state: State) {
      this.visitNode(node, state);
    }
  }


/**
   * Nodes that cache transformation state. These are used to thread state when traversing
   * the scene graph. Since they keep track of rendering state, they might as well become
   * scene graph nodes.
   */
export class State {
    constructor() {

    }
  }


export class PreRenderState extends State {
    private static _dirtyStack: PreRenderState [] = [];

    matrix: Matrix = Matrix.createIdentity();
    depth: number = 0;

    constructor() {
      super();
    }

    transform(transform: Transform): PreRenderState {
      var state = this.clone();
      state.matrix.preMultiply(transform.getMatrix());
      return state;
    }

    static allocate(): PreRenderState {
      var dirtyStack = PreRenderState._dirtyStack;
      var state = null;
      if (dirtyStack.length) {
        state = dirtyStack.pop();
      }
      return state;
    }

    public clone(): PreRenderState {
      var state = PreRenderState.allocate();
      if (!state) {
        state = new PreRenderState();
      }
      state.set(this);
      return state;
    }

    set (state: PreRenderState) {
      this.matrix.set(state.matrix);
    }

    free() {
      PreRenderState._dirtyStack.push(this);
    }
  }


/**
   * Helper visitor that checks and resets the dirty bit and calculates stack levels. If the root
   * node is dirty, then we have to repaint the entire node tree.
   */
export class PreRenderVisitor extends NodeVisitor {
    public isDirty = true;
    private _dirtyRegion: DirtyRegion;

    start(node: Group, dirtyRegion: DirtyRegion) {
      this._dirtyRegion = dirtyRegion;
      var state = new PreRenderState();
      state.matrix.setIdentity();
      node.visit(this, state);
      state.free();
    }

    visitGroup(node: Group, state: PreRenderState) {
      var children = node.getChildren();
      this.visitNode(node, state);
      for (var i = 0; i < children.length; i++) {
        var child = children[i];
        var childState = state.transform(child.getTransform());
        child.visit(this, childState);
        childState.free();
      }
    }

    visitNode(node: Node, state: PreRenderState) {
      if (node.hasFlags(NodeFlags.Dirty)) {
        this.isDirty = true;
      }
      node.toggleFlags(NodeFlags.Dirty, false);
      node.depth = state.depth++;
    }
  }


/**
   * Debugging visitor.
   */
export class TracingNodeVisitor extends NodeVisitor {
    constructor(public writer: IndentingWriter) {
      super();
    }

    visitNode(node: Node, state: State) {
      // ...
    }

    visitShape(node: Shape, state: State) {
      this.writer.writeLn(node.toString());
      this.visitNode(node, state);
    }

    visitGroup(node: Group, state: State) {
      this.visitNode(node, state);

      var children = node.getChildren();
      this.writer.enter(node.toString() + " " + children.length);
      for (var i = 0; i < children.length; i++) {
        children[i].visit(this, state);
      }
      this.writer.outdent();
    }

    visitStage(node: Stage, state: State) {
      this.visitGroup(node, state);
    }
  }


/**
   * Nodes that contain other nodes.
   */
export class Group extends Node {
    protected _children: Node [];

    constructor() {
      super();
      this._type = NodeType.Group;
      this._children = [];
    }

    public getChildren(clone: boolean = false): Node [] {
      if (clone) {
        return this._children.slice(0);
      }
      return this._children;
    }

    public childAt(index: number): Node {
      release || assert(index >= 0 && index < this._children.length);
      return this._children[index];
    }

    public get child(): Node {
      release || assert(this._children.length === 1);
      return this._children[0];
    }

    public get groupChild(): Group {
      release || assert(this._children.length === 1);
      release || assert(this._children[0] instanceof Group);
      return <Group>this._children[0];
    }

    /**
     * Adds a node and remove's it from its previous location if it has a parent and propagates
     * flags accordingly.
     */
    public addChild(node: Node) {
      release || assert(node);
      release || assert(!node.isAncestor(this));
      if (node._parent) {
        node._parent.removeChildAt(node._index);
      }
      node._parent = this;
      node._index = this._children.length;
      this._children.push(node);
      this._propagateFlagsUp(NodeFlags.UpOnAddedOrRemoved);
      node._propagateFlagsDown(NodeFlags.DownOnAddedOrRemoved);
      node._markCurrentBoundsAsDirtyRegion();
    }

    /**
     * Removes a child at the given index and propagates flags accordingly.
     */
    public removeChildAt(index: number) {
      release || assert(index >= 0 && index < this._children.length);
      var node = this._children[index];
      release || assert(index === node._index);
      node._markCurrentBoundsAsDirtyRegion();
      this._children.splice(index, 1);
      node._index = -1;
      node._parent = null;
      this._propagateFlagsUp(NodeFlags.UpOnAddedOrRemoved);
      node._propagateFlagsDown(NodeFlags.DownOnAddedOrRemoved);
    }

    public clearChildren() {
      for (var i = 0; i < this._children.length; i++) {
        var child = this._children[i];
        child._markCurrentBoundsAsDirtyRegion();
        if (child) {
          child._index = -1;
          child._parent = null;
          child._propagateFlagsDown(NodeFlags.DownOnAddedOrRemoved);
        }
      }
      this._children.length = 0;
      this._propagateFlagsUp(NodeFlags.UpOnAddedOrRemoved);
    }

    /**
     * Override and propagate flags to all children.
     */
    _propagateFlagsDown(flags: NodeFlags) {
      if (this.hasFlags(flags)) {
        return;
      }
      this.setFlags(flags);
      var children = this._children;
      for (var i = 0; i < children.length; i++) {
        children[i]._propagateFlagsDown(flags);
      }
    }

    /**
     * Takes the inion of all child bounds and caches the bounds locally.
     */
    public getBounds(clone: boolean = false): Rectangle {
      var bounds = this._bounds || (this._bounds = Rectangle.createEmpty());
      if (this.hasFlags(NodeFlags.InvalidBounds)) {
        bounds.setEmpty();
        var children = this._children;
        var childBounds = Rectangle.allocate();
        for (var i = 0; i < children.length; i++) {
          var child = children[i];
          childBounds.set(child.getBounds());
          child.getTransformMatrix().transformRectangleAABB(childBounds);
          bounds.union(childBounds);
        }
        childBounds.free();
        this.removeFlags(NodeFlags.InvalidBounds);
      }
      if (clone) {
        return bounds.clone();
      }
      return bounds;
    }
  }