() => {
        for (const languageCode of resources.containedLanguageCodes()) {
            const create = resources.createAttribute({
                messageId: 'test',
                id: 'login',
                pattern: 'Welcome to this test, please login.',
                languageCode: languageCode,
            });
            if (create.isErr) {
                fail(create.error);
            }
        }

        const deletion = resources.deleteAttributeForAllResources({ messageId: 'test', id: 'login' });
        expect(deletion.isOk).toBeTruthy();
    }


() => {
        for (const languageCode of resources.containedLanguageCodes().slice(1)) {
            const create = resources.createAttribute({
                messageId: 'test',
                id: 'login',
                pattern: 'Welcome to this test, please login.',
                languageCode: languageCode,
            });
            if (create.isErr) {
                fail(create.error);
            }
        }
        const deletion = resources.deleteAttributeForAllResources({ messageId: 'test', id: 'login' });
        expect(deletion.isErr).toBeTruthy();
    }


export default function Note({ note }: NoteProps) {
  const { slug, date, title, tags } = note
  return (

          <Link href={`/til/${slug}`} className="text-gray-900 dark:text-gray-100">
            {title}
          </Link>

          <time dateTime={date}>{formatDate(date)}</time>

        {tags.map((tag) => (

{tag}

        ))}

  )
}


(tag) => (
          <div
            key={tag}


interface NoteProps {
  note: PostFrontMatter
}


formatDate(date)


() => {
  let component: LoginPageComponent;
  let fixture: ComponentFixture<LoginPageComponent>;
  let mockStore: MockStore;
  let mockAnySmallBreakpoint: MemoizedSelector<AppState, boolean>;
  let auth: AngularFireAuth;
  let mockRouter: Router;
  let mockSnackbar: MatSnackBar;

  beforeEach(waitForAsync(() => {
    TestBed.configureTestingModule({
      declarations: [ LoginPageComponent ],
      imports: [
        ReactiveFormsModule,
        RouterTestingModule.withRoutes([]),
        MatSnackBarModule,
        // AngularFireModule.initializeApp(environment.firebaseConfig),
      ],
      providers: [
        provideMockStore({ initialState }),
        { provide: FIREBASE_OPTIONS, useValue: environment.firebaseConfig },
      ]
    })
    .compileComponents();
  }));

  beforeEach(() => {
    mockStore = TestBed.inject(MockStore);
    mockAnySmallBreakpoint = mockStore.overrideSelector(appStateSelectAnySmall, false);

    auth = TestBed.inject(AngularFireAuth);
    mockRouter = TestBed.inject(Router);
    mockSnackbar = TestBed.inject(MatSnackBar);

    fixture = TestBed.createComponent(LoginPageComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

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

  describe('onLogin', () => {
    it ('should work when login form is valid', done => {
      component.loginForm.get('email').setValue('user@test.com');
      component.loginForm.get('password').setValue('password');

      const mockCreds = {
        user: {
          displayName: 'test'
        }
      } as UserCredential;
      const loginSpy = spyOn(auth, 'signInWithEmailAndPassword').and.resolveTo(mockCreds);
      const navigateSpy = spyOn(mockRouter, 'navigate').and.resolveTo();
      const openSpy = spyOn(mockSnackbar, 'open');

      component.onLogin().then(() => {
        expect(loginSpy).toHaveBeenCalledWith('user@test.com', 'password');
        expect(navigateSpy).toHaveBeenCalledWith(['/local']);
        expect(openSpy).toHaveBeenCalledWith('Welcome test', 'CLOSE');
        done();
      });
    });
  });

  describe('onGoogleLogin', () => {
    it('should login properly', done => {
      const mockCreds = {
        user: {
          displayName: 'test'
        }
      } as UserCredential;
      const signInSpy = spyOn(auth, 'signInWithPopup').and.resolveTo(mockCreds);
      const navigateSpy = spyOn(mockRouter, 'navigate').and.resolveTo();
      const openSpy = spyOn(mockSnackbar, 'open');

      component.onGoogleLogin().then(() => {
        expect(signInSpy).toHaveBeenCalled();
        expect(navigateSpy).toHaveBeenCalledWith(['/local']);
        expect(openSpy).toHaveBeenCalledWith('Welcome test', 'CLOSE');
        done();
      });
    });
  });
}


() => {
    TestBed.configureTestingModule({
      declarations: [ LoginPageComponent ],
      imports: [
        ReactiveFormsModule,
        RouterTestingModule.withRoutes([]),
        MatSnackBarModule,
        // AngularFireModule.initializeApp(environment.firebaseConfig),
      ],
      providers: [
        provideMockStore({ initialState }),
        { provide: FIREBASE_OPTIONS, useValue: environment.firebaseConfig },
      ]
    })
    .compileComponents();
  }


() => {
    mockStore = TestBed.inject(MockStore);
    mockAnySmallBreakpoint = mockStore.overrideSelector(appStateSelectAnySmall, false);

    auth = TestBed.inject(AngularFireAuth);
    mockRouter = TestBed.inject(Router);
    mockSnackbar = TestBed.inject(MatSnackBar);

    fixture = TestBed.createComponent(LoginPageComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  }


() => {
    it ('should work when login form is valid', done => {
      component.loginForm.get('email').setValue('user@test.com');
      component.loginForm.get('password').setValue('password');

      const mockCreds = {
        user: {
          displayName: 'test'
        }
      } as UserCredential;
      const loginSpy = spyOn(auth, 'signInWithEmailAndPassword').and.resolveTo(mockCreds);
      const navigateSpy = spyOn(mockRouter, 'navigate').and.resolveTo();
      const openSpy = spyOn(mockSnackbar, 'open');

      component.onLogin().then(() => {
        expect(loginSpy).toHaveBeenCalledWith('user@test.com', 'password');
        expect(navigateSpy).toHaveBeenCalledWith(['/local']);
        expect(openSpy).toHaveBeenCalledWith('Welcome test', 'CLOSE');
        done();
      });
    });
  }


done => {
      component.loginForm.get('email').setValue('user@test.com');
      component.loginForm.get('password').setValue('password');

      const mockCreds = {
        user: {
          displayName: 'test'
        }
      } as UserCredential;
      const loginSpy = spyOn(auth, 'signInWithEmailAndPassword').and.resolveTo(mockCreds);
      const navigateSpy = spyOn(mockRouter, 'navigate').and.resolveTo();
      const openSpy = spyOn(mockSnackbar, 'open');

      component.onLogin().then(() => {
        expect(loginSpy).toHaveBeenCalledWith('user@test.com', 'password');
        expect(navigateSpy).toHaveBeenCalledWith(['/local']);
        expect(openSpy).toHaveBeenCalledWith('Welcome test', 'CLOSE');
        done();
      });
    }


() => {
        expect(loginSpy).toHaveBeenCalledWith('user@test.com', 'password');
        expect(navigateSpy).toHaveBeenCalledWith(['/local']);
        expect(openSpy).toHaveBeenCalledWith('Welcome test', 'CLOSE');
        done();
      }


() => {
    it('should login properly', done => {
      const mockCreds = {
        user: {
          displayName: 'test'
        }
      } as UserCredential;
      const signInSpy = spyOn(auth, 'signInWithPopup').and.resolveTo(mockCreds);
      const navigateSpy = spyOn(mockRouter, 'navigate').and.resolveTo();
      const openSpy = spyOn(mockSnackbar, 'open');

      component.onGoogleLogin().then(() => {
        expect(signInSpy).toHaveBeenCalled();
        expect(navigateSpy).toHaveBeenCalledWith(['/local']);
        expect(openSpy).toHaveBeenCalledWith('Welcome test', 'CLOSE');
        done();
      });
    });
  }


done => {
      const mockCreds = {
        user: {
          displayName: 'test'
        }
      } as UserCredential;
      const signInSpy = spyOn(auth, 'signInWithPopup').and.resolveTo(mockCreds);
      const navigateSpy = spyOn(mockRouter, 'navigate').and.resolveTo();
      const openSpy = spyOn(mockSnackbar, 'open');

      component.onGoogleLogin().then(() => {
        expect(signInSpy).toHaveBeenCalled();
        expect(navigateSpy).toHaveBeenCalledWith(['/local']);
        expect(openSpy).toHaveBeenCalledWith('Welcome test', 'CLOSE');
        done();
      });
    }


() => {
        expect(signInSpy).toHaveBeenCalled();
        expect(navigateSpy).toHaveBeenCalledWith(['/local']);
        expect(openSpy).toHaveBeenCalledWith('Welcome test', 'CLOSE');
        done();
      }


function IconPlusCSizeAll(props: React.SVGProps<SVGSVGElement>) {
  return (
    <svg viewBox="0 0 24 24" {...props}>
      <path d="M11.25 7.5C11.25 7.08579 11.5858 6.75 12 6.75C12.4142 6.75 12.75 7.08579 12.75 7.5V11.25H16.5C16.9142 11.25 17.25 11.5858 17.25 12C17.25 12.4142 16.9142 12.75 16.5 12.75H12.75V16.5C12.75 16.9142 12.4142 17.25 12 17.25C11.5858 17.25 11.25 16.9142 11.25 16.5V12.75H7.5C7.08579 12.75 6.75 12.4142 6.75 12C6.75 11.5858 7.08579 11.25 7.5 11.25H11.25V7.5Z"/>
      <path fillRule="evenodd" clipRule="evenodd" d="M21.75 12C21.75 17.3848 17.3848 21.75 12 21.75C6.61522 21.75 2.25 17.3848 2.25 12C2.25 6.61522 6.61522 2.25 12 2.25C17.3848 2.25 21.75 6.61522 21.75 12ZM20.25 12C20.25 16.5563 16.5563 20.25 12 20.25C7.44365 20.25 3.75 16.5563 3.75 12C3.75 7.44365 7.44365 3.75 12 3.75C16.5563 3.75 20.25 7.44365 20.25 12Z"/>
    </svg>
  );
}


() => {
    this.setState(prevState => ({
      isPopoverOpen: !prevState.isPopoverOpen,
    }));
  }


prevState => ({
      isPopoverOpen: !prevState.isPopoverOpen,
    })


() => {
    this.setState({
      isPopoverOpen: false,
    });
  }


() => {
    this.props.toggleEnabledProvider();
    this.closePopover();
  }


() => {
    this.props.toggleExcludedProvider();
    this.closePopover();
  }


export class ProviderItemBadge extends PureComponent<ProviderItemBadgeProps, OwnState> {
  public readonly state = {
    isPopoverOpen: false,
  };

  public render() {
    const { deleteProvider, field, kqlQuery, isEnabled, isExcluded, providerId, val } = this.props;

    const badge = (
      <ProviderBadge
        deleteProvider={deleteProvider}
        field={field}
        kqlQuery={kqlQuery}
        isEnabled={isEnabled}
        isExcluded={isExcluded}
        providerId={providerId}
        togglePopover={this.togglePopover}
        val={val}
      />
    );

    return (
      <ProviderItemActions
        button={badge}
        closePopover={this.closePopover}
        deleteProvider={deleteProvider}
        field={field}
        kqlQuery={kqlQuery}
        isEnabled={isEnabled}
        isExcluded={isExcluded}
        isOpen={this.state.isPopoverOpen}
        providerId={providerId}
        toggleEnabledProvider={this.toggleEnabledProvider}
        toggleExcludedProvider={this.toggleExcludedProvider}
        value={val}
      />
    );
  }

  private togglePopover = () => {
    this.setState(prevState => ({
      isPopoverOpen: !prevState.isPopoverOpen,
    }));
  };

  private closePopover = () => {
    this.setState({
      isPopoverOpen: false,
    });
  };

  private toggleEnabledProvider = () => {
    this.props.toggleEnabledProvider();
    this.closePopover();
  };

  private toggleExcludedProvider = () => {
    this.props.toggleExcludedProvider();
    this.closePopover();
  };
}


interface ProviderItemBadgeProps {
  deleteProvider: () => void;
  field: string;
  kqlQuery: string;
  isEnabled: boolean;
  isExcluded: boolean;
  providerId: string;
  toggleEnabledProvider: () => void;
  toggleExcludedProvider: () => void;
  val: string | number;
}


interface OwnState {
  isPopoverOpen: boolean;
}


public render() {
    const { deleteProvider, field, kqlQuery, isEnabled, isExcluded, providerId, val } = this.props;

    const badge = (
      <ProviderBadge
        deleteProvider={deleteProvider}
        field={field}
        kqlQuery={kqlQuery}
        isEnabled={isEnabled}
        isExcluded={isExcluded}
        providerId={providerId}
        togglePopover={this.togglePopover}
        val={val}
      />
    );

    return (
      <ProviderItemActions
        button={badge}
        closePopover={this.closePopover}
        deleteProvider={deleteProvider}
        field={field}
        kqlQuery={kqlQuery}
        isEnabled={isEnabled}
        isExcluded={isExcluded}
        isOpen={this.state.isPopoverOpen}
        providerId={providerId}
        toggleEnabledProvider={this.toggleEnabledProvider}
        toggleExcludedProvider={this.toggleExcludedProvider}
        value={val}
      />
    );
  }


function tryEpochToMoment(timestamp: any) {
  if (timestamp && _.isNumber(timestamp)) {
    const epoch = Number(timestamp);
    return dateTime(epoch);
  } else {
    return timestamp;
  }
}


export function eventEditor() {
  return {
    restrict: 'E',
    controller: EventEditorCtrl,
    bindToController: true,
    controllerAs: 'ctrl',
    templateUrl: 'public/app/features/annotations/partials/event_editor.html',
    scope: {
      panelCtrl: '=',
      event: '=',
      close: '&',
    },
  };
}


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


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


export class EventEditorCtrl {
  panelCtrl: MetricsPanelCtrl;
  event: AnnotationEvent;
  timeRange: { from: number; to: number };
  form: any;
  close: any;
  timeFormated: string;

  /** @ngInject */
  constructor(private annotationsSrv: AnnotationsSrv) {}

  $onInit() {
    this.event.panelId = this.panelCtrl.panel.id;
    this.event.dashboardId = this.panelCtrl.dashboard.id;

    // Annotations query returns time as Unix timestamp in milliseconds
    this.event.time = tryEpochToMoment(this.event.time);
    if (this.event.isRegion) {
      this.event.timeEnd = tryEpochToMoment(this.event.timeEnd);
    }

    this.timeFormated = this.panelCtrl.dashboard.formatDate(this.event.time!);
  }

  save() {
    if (!this.form.$valid) {
      return;
    }

    const saveModel = _.cloneDeep(this.event);
    saveModel.time = saveModel.time!.valueOf();
    saveModel.timeEnd = 0;

    if (saveModel.isRegion) {
      saveModel.timeEnd = this.event.timeEnd!.valueOf();

      if (saveModel.timeEnd < saveModel.time) {
        console.log('invalid time');
        return;
      }
    }

    if (saveModel.id) {
      this.annotationsSrv
        .updateAnnotationEvent(saveModel)
        .then(() => {
          this.panelCtrl.refresh();
          this.close();
        })
        .catch(() => {
          this.panelCtrl.refresh();
          this.close();
        });
    } else {
      this.annotationsSrv
        .saveAnnotationEvent(saveModel)
        .then(() => {
          this.panelCtrl.refresh();
          this.close();
        })
        .catch(() => {
          this.panelCtrl.refresh();
          this.close();
        });
    }
  }

  delete() {
    return this.annotationsSrv
      .deleteAnnotationEvent(this.event)
      .then(() => {
        this.panelCtrl.refresh();
        this.close();
      })
      .catch(() => {
        this.panelCtrl.refresh();
        this.close();
      });
  }
}


$onInit() {
    this.event.panelId = this.panelCtrl.panel.id;
    this.event.dashboardId = this.panelCtrl.dashboard.id;

    // Annotations query returns time as Unix timestamp in milliseconds
    this.event.time = tryEpochToMoment(this.event.time);
    if (this.event.isRegion) {
      this.event.timeEnd = tryEpochToMoment(this.event.timeEnd);
    }

    this.timeFormated = this.panelCtrl.dashboard.formatDate(this.event.time!);
  }


save() {
    if (!this.form.$valid) {
      return;
    }

    const saveModel = _.cloneDeep(this.event);
    saveModel.time = saveModel.time!.valueOf();
    saveModel.timeEnd = 0;

    if (saveModel.isRegion) {
      saveModel.timeEnd = this.event.timeEnd!.valueOf();

      if (saveModel.timeEnd < saveModel.time) {
        console.log('invalid time');
        return;
      }
    }

    if (saveModel.id) {
      this.annotationsSrv
        .updateAnnotationEvent(saveModel)
        .then(() => {
          this.panelCtrl.refresh();
          this.close();
        })
        .catch(() => {
          this.panelCtrl.refresh();
          this.close();
        });
    } else {
      this.annotationsSrv
        .saveAnnotationEvent(saveModel)
        .then(() => {
          this.panelCtrl.refresh();
          this.close();
        })
        .catch(() => {
          this.panelCtrl.refresh();
          this.close();
        });
    }
  }


delete() {
    return this.annotationsSrv
      .deleteAnnotationEvent(this.event)
      .then(() => {
        this.panelCtrl.refresh();
        this.close();
      })
      .catch(() => {
        this.panelCtrl.refresh();
        this.close();
      });
  }


(path: string): string[] => {
    if (fs.existsSync(path)) {
        const data = fs.readFileSync(config.fileName, { encoding: 'utf8', flag: 'r' })
        return data.split(os.EOL)
    } else {
        return []
    }
}


(path: string, data: string): void => {
    fs.writeFileSync(config.fileName, data, { encoding: 'utf8', flag: 'w' })
}


(line: string): LineProcessResult => {
    const trimmedLine = line.trim()
    if (trimmedLine.length == 0) {
        return { lineType: 'blank' }
    }

    if (trimmedLine.startsWith('#')) {
        return { lineType: 'comment' }
    }

    const sep = trimmedLine.indexOf(config.keyValueSeparator)
    if (sep > 0) {
        const lineKey = trimmedLine.substring(0, sep).trim()
        const lineValue = trimmedLine.substring(sep + 1).trim()
        if (lineKey.length > 0) {
            return ({ lineType: 'property', property: { key: lineKey, value: lineValue } })
        }
    }

    return ({ lineType: 'error' })
}


(lines: string[]): LineRecord[] => {
    const parsedProperties: LineRecord[] = []

    let i = 1
    lines.forEach(line => {
        const lpr = parseLine(line)
        parsedProperties.push({
            lineNumber: i,
            result: lpr
        })
        i++
    })
    return parsedProperties
}


line => {
        const lpr = parseLine(line)
        parsedProperties.push({
            lineNumber: i,
            result: lpr
        })
        i++
    }


(lines: string[], propertiesMap: Map<string, string>): string[] => {
    const pendingMap = new Map<string, string>()
    propertiesMap.forEach((value: string, key: string) => {
        pendingMap.set(key, value)
    })

    const processedLines = []
    for (let i = 0; i < lines.length; i++) {
        const line = lines[i]
        const lpr = parseLine(line)
        switch (lpr.lineType) {
            case 'blank':
            case 'comment':
                processedLines.push(line)
                break;
            case 'property': {
                if (lpr.property) {
                    const property = lpr.property
                    const updatedValue = pendingMap.get(property.key)
                    processedLines.push(`${property.key}${config.keyValueSeparator}${updatedValue}`)
                    pendingMap.delete(property.key)
                }
                break;
            }
            default:
                break;
        }
    }

    // Add missing properties
    pendingMap.forEach((value: string, key: string) => {
        processedLines.push(`${key}${config.keyValueSeparator}${value}`)
    })

    return processedLines
}


(value: string, key: string) => {
        pendingMap.set(key, value)
    }


(value: string, key: string) => {
        processedLines.push(`${key}${config.keyValueSeparator}${value}`)
    }


() => {
        const map = new Map<string, string>()

        // Build defaults map
        CONFIG_DEFAULTS.forEach(cd => {
            map.set(cd.key, cd.value)
        })

        // Read config file
        const configFilePath = path.join(process.cwd(), config.fileName)
        const lines = readFile(configFilePath)
        const lineRecords = parseProperties(lines)
        lineRecords.forEach(lr => {
            if ((lr.result.lineType === 'property') && (lr.result.property)) {
                const property = lr.result.property
                map.set(property.key, property.value)
            }
        })
        this.configMap = map
    }


cd => {
            map.set(cd.key, cd.value)
        }


lr => {
            if ((lr.result.lineType === 'property') && (lr.result.property)) {
                const property = lr.result.property
                map.set(property.key, property.value)
            }
        }


class AppProperties {
    private static instance: AppProperties
    private configMap = new Map<string, string>()

    // eslint-disable-next-line @typescript-eslint/no-empty-function
    private constructor() {
    }

    public init() {
        this.loadConfig()
    }

    public static getInstance(): AppProperties {
        if (!AppProperties.instance) {
            AppProperties.instance = new AppProperties()
        }
        return AppProperties.instance
    }

    private loadConfig = () => {
        const map = new Map<string, string>()

        // Build defaults map
        CONFIG_DEFAULTS.forEach(cd => {
            map.set(cd.key, cd.value)
        })

        // Read config file
        const configFilePath = path.join(process.cwd(), config.fileName)
        const lines = readFile(configFilePath)
        const lineRecords = parseProperties(lines)
        lineRecords.forEach(lr => {
            if ((lr.result.lineType === 'property') && (lr.result.property)) {
                const property = lr.result.property
                map.set(property.key, property.value)
            }
        })
        this.configMap = map
    }

    get(key: string): string {
        const value = this.configMap.get(key)
        return (value || '')
    }

    set(key: string, value: string): void {
        this.configMap.set(key, (value || ''))
    }

    save(): void {
        const configFilePath = path.join(process.cwd(), config.fileName)
        const lines = readFile(configFilePath)
        const processedLines = updateProperties(lines, this.configMap)
        const updatedFile = processedLines.join(os.EOL)
        writeFile(configFilePath, updatedFile)
        console.debug('Configuration saved to ', configFilePath)
    }
}


type ConfigurationProperty = {
    key: string,
    value: string
}


type LineProcessResult = {
    lineType: 'blank' | 'comment' | 'property' | 'error',
    property?: ConfigurationProperty
}


type LineRecord = {
    lineNumber: number,
    result: LineProcessResult
}


public init() {
        this.loadConfig()
    }


public static getInstance(): AppProperties {
        if (!AppProperties.instance) {
            AppProperties.instance = new AppProperties()
        }
        return AppProperties.instance
    }


get(key: string): string {
        const value = this.configMap.get(key)
        return (value || '')
    }


set(key: string, value: string): void {
        this.configMap.set(key, (value || ''))
    }


save(): void {
        const configFilePath = path.join(process.cwd(), config.fileName)
        const lines = readFile(configFilePath)
        const processedLines = updateProperties(lines, this.configMap)
        const updatedFile = processedLines.join(os.EOL)
        writeFile(configFilePath, updatedFile)
        console.debug('Configuration saved to ', configFilePath)
    }


function renderItem( { item: transaction }: { item: TransactionDetails } ): ReactElement {
    return (
      <TransactionsFlatListItem
        transaction={transaction}
        onPress={()


( item: TransactionDetails ) => item.txid


( {
  transactions,
  onTransactionSelected,
}: Props ) => {

  function renderItem( { item: transaction }: { item: TransactionDetails } ): ReactElement {
    return (
      <TransactionsFlatListItem
        transaction={transaction}
        onPress={()


export type Props = {
  transactions: TransactionDetails[];
  onTransactionSelected: ( transaction: TransactionDetails ) => void;
};


// Simulate user input: change input value, then dispatch change event
function userInput(value: string): void {
                inputElement.changeValue(value)
                changeEvents.next({
                    preventDefault: noop,
                    target: inputReference.current,
                })
            }


/**
     * Creates a mock input element for emails. Only checks for '@'. Acts as
     * if `novalidate` is true.
     *
     * Implements the `HTMLInputElement` properties needed by `useInputValidation`,
     * along with a `changeValue` method to simulate input and change the internal value
     */
function createEmailInputElement(): Pick<
        HTMLInputElement,
        'checkValidity' | 'validationMessage' | 'setCustomValidity' | 'value'
    > & { changeValue: (newValue: string) => void } {
        const internalStrings = {
            value: '',
            validationMessage: '',
        }

        return {
            get value() {
                return internalStrings.value
            },
            get validationMessage() {
                return internalStrings.validationMessage
            },
            checkValidity() {
                // Check if custom validity was set
                if (internalStrings.validationMessage.length > 0) {
                    return false
                }
                // Built-in rules
                if (!internalStrings.value.includes('@')) {
                    internalStrings.validationMessage = "Email must include '@'"
                    return false
                }
                // Clear built-in messages
                internalStrings.validationMessage = ''
                return true
            },
            setCustomValidity(error) {
                internalStrings.validationMessage = error
            },
            changeValue(newValue) {
                internalStrings.value = newValue
            },
        }
    }


/**
     * Shared test validators
     */
function isDotCo(email: string): string | undefined {
        if (email.endsWith('.co')) {
            return undefined
        }

        return "Email must end with '.co'"
    }


function isEmailUnique(email: string): Observable<string | undefined> {
            return of(email === 'test@sg.co' ? 'Email is taken' : undefined).pipe(delay(200))
        }


() => {
        clock = sinon.useFakeTimers()
    }


() => {
        clock.restore()
    }


() => {
        subscriptions = new Subscription()

        setupValidationPipelineTest = (
            validationOptions
        ): ((inputScript: (string | number)[]) => InputValidationState[]) => {
            const inputElement = createEmailInputElement()
            const inputReference = { current: inputElement }

            const inputValidationStates: InputValidationState[] = []

            const validationPipeline = createValidationPipeline(
                validationOptions,
                inputReference,
                // We want to test the values that this callback is called with,
                // not the emissions of the returned observable. Therefore, we will
                // push these values to an array whose values we will assert.
                inputValidationStates.push.bind(inputValidationStates)
            )

            // Creating this type instead of a generic util because TS doesn't support higher-kinded types
            type ObservableEmission<T> = T extends Observable<infer V> ? V : never
            const changeEvents = new Subject<ObservableEmission<Parameters<typeof validationPipeline>[0]>>()

            // We don't care about this observable.
            // Here, we simply set up the pipeline:
            // change event -> validation pipeline -> validation results
            //                      |
            //                      v
            //                 validation states (this is what we're testing)
            subscriptions.add(validationPipeline(changeEvents).subscribe(noop))

            // Simulate user input: change input value, then dispatch change event
            function userInput(value: string): void {
                inputElement.changeValue(value)
                changeEvents.next({
                    preventDefault: noop,
                    target: inputReference.current,
                })
            }

            // "Scripting" user interaction: strings are new input values, numbers are delays before next input in ms.
            return function executeUserInputScript(inputScript) {
                for (const input of inputScript) {
                    if (typeof input === 'string') {
                        userInput(input)
                    } else {
                        clock.tick(input)
                    }
                }
                // Wait for debounceTime after final input
                clock.tick(500)

                return inputValidationStates
            }
        }
    }


(
            validationOptions
        ): ((inputScript: (string | number)[]) => InputValidationState[]) => {
            const inputElement = createEmailInputElement()
            const inputReference = { current: inputElement }

            const inputValidationStates: InputValidationState[] = []

            const validationPipeline = createValidationPipeline(
                validationOptions,
                inputReference,
                // We want to test the values that this callback is called with,
                // not the emissions of the returned observable. Therefore, we will
                // push these values to an array whose values we will assert.
                inputValidationStates.push.bind(inputValidationStates)
            )

            // Creating this type instead of a generic util because TS doesn't support higher-kinded types
            type ObservableEmission<T> = T extends Observable<infer V> ? V : never
            const changeEvents = new Subject<ObservableEmission<Parameters<typeof validationPipeline>[0]>>()

            // We don't care about this observable.
            // Here, we simply set up the pipeline:
            // change event -> validation pipeline -> validation results
            //                      |
            //                      v
            //                 validation states (this is what we're testing)
            subscriptions.add(validationPipeline(changeEvents).subscribe(noop))

            // Simulate user input: change input value, then dispatch change event
            function userInput(value: string): void {
                inputElement.changeValue(value)
                changeEvents.next({
                    preventDefault: noop,
                    target: inputReference.current,
                })
            }

            // "Scripting" user interaction: strings are new input values, numbers are delays before next input in ms.
            return function executeUserInputScript(inputScript) {
                for (const input of inputScript) {
                    if (typeof input === 'string') {
                        userInput(input)
                    } else {
                        clock.tick(input)
                    }
                }
                // Wait for debounceTime after final input
                clock.tick(500)

                return inputValidationStates
            }
        }


() => {
        subscriptions.unsubscribe()
    }


() => {
        const executeUserInputScript = setupValidationPipelineTest({
            synchronousValidators: [isDotCo],
        })

        const inputs: (string | number)[] = [
            'source',
            'sourcegraph',
            // Advance less than `VALIDATION_DEBOUNCE_TIME` to ensure value isn't validated in this case
            min([VALIDATION_DEBOUNCE_TIME - 100, 200]) ?? 200,
            'sourcegraph@',
            VALIDATION_DEBOUNCE_TIME,
            'sourcegraph@sg.co',
        ]

        const expectedStates: InputValidationState[] = [
            {
                kind: 'LOADING',
                value: 'source',
            },
            {
                kind: 'LOADING',
                value: 'sourcegraph',
            },
            {
                kind: 'LOADING',
                value: 'sourcegraph@',
            },
            {
                kind: 'INVALID',
                value: 'sourcegraph@',
                reason: "Email must end with '.co'",
            },
            {
                kind: 'LOADING',
                value: 'sourcegraph@sg.co',
            },
            {
                kind: 'VALID',
                value: 'sourcegraph@sg.co',
            },
        ]

        expect(executeUserInputScript(inputs)).toStrictEqual(expectedStates)
    }


() => {
        const executeUserInputScript = setupValidationPipelineTest({
            synchronousValidators: [isDotCo],
            initialValue: 'so',
        })

        const inputs: (string | number)[] = [
            VALIDATION_DEBOUNCE_TIME,
            'source',
            'sourcegraph',
            // Advance less than `VALIDATION_DEBOUNCE_TIME` to ensure value isn't validated in this case
            min([VALIDATION_DEBOUNCE_TIME - 100, 200]) ?? 200,
            'sourcegraph@',
            VALIDATION_DEBOUNCE_TIME,
            'sourcegraph@sg.co',
        ]

        const expectedStates: InputValidationState[] = [
            {
                kind: 'LOADING',
                value: 'so',
            },
            {
                kind: 'INVALID',
                reason: "Email must include '@'",
                value: 'so',
            },
            {
                kind: 'LOADING',
                value: 'source',
            },
            {
                kind: 'LOADING',
                value: 'sourcegraph',
            },
            {
                kind: 'LOADING',
                value: 'sourcegraph@',
            },
            {
                kind: 'INVALID',
                value: 'sourcegraph@',
                reason: "Email must end with '.co'",
            },
            {
                kind: 'LOADING',
                value: 'sourcegraph@sg.co',
            },
            {
                kind: 'VALID',
                value: 'sourcegraph@sg.co',
            },
        ]

        expect(executeUserInputScript(inputs)).toStrictEqual(expectedStates)
    }


() => {
        function isEmailUnique(email: string): Observable<string | undefined> {
            return of(email === 'test@sg.co' ? 'Email is taken' : undefined).pipe(delay(200))
        }

        const executeUserInputScript = setupValidationPipelineTest({
            synchronousValidators: [isDotCo],
            asynchronousValidators: [isEmailUnique],
        })

        const inputs: (string | number)[] = [
            'test',
            VALIDATION_DEBOUNCE_TIME,
            'test@sg.c',
            'test@sg.co',
            // Advance 200 more ms due to delay from `isEmailUnique`
            VALIDATION_DEBOUNCE_TIME + 200,
            '@sg.co',
            // Advance less than `VALIDATION_DEBOUNCE_TIME` to ensure value isn't validated in this case
            min([VALIDATION_DEBOUNCE_TIME - 100, 200]) ?? 200,
            'sourcegraph@sg.co',
            // Advance 200 more ms due to delay from `isEmailUnique`
            200,
        ]

        const expectedStates: InputValidationState[] = [
            {
                kind: 'LOADING',
                value: 'test',
            },
            {
                kind: 'INVALID',
                reason: "Email must include '@'",
                value: 'test',
            },
            {
                kind: 'LOADING',
                value: 'test@sg.c',
            },
            {
                kind: 'LOADING',
                value: 'test@sg.co',
            },
            {
                kind: 'INVALID',
                value: 'test@sg.co',
                reason: 'Email is taken',
            },
            {
                kind: 'LOADING',
                value: '@sg.co',
            },
            {
                kind: 'LOADING',
                value: 'sourcegraph@sg.co',
            },
            {
                kind: 'VALID',
                value: 'sourcegraph@sg.co',
            },
        ]

        expect(executeUserInputScript(inputs)).toStrictEqual(expectedStates)
    }


() => {
        const { result } = renderHook(() =>
            useInputValidation({
                synchronousValidators: [isDotCo],
            })
        )

        act(() => {
            const [, nextEmailFieldChange, emailInputReference] = result.current
            const inputElement = createEmailInputElement()
            emailInputReference.current = (inputElement as unknown) as HTMLInputElement

            inputElement.changeValue('test-string')
            nextEmailFieldChange({
                target: emailInputReference.current,
                preventDefault: noop,
            } as React.ChangeEvent<HTMLInputElement>)
        })

        expect(result.current[0]).toStrictEqual({ value: 'test-string', kind: 'LOADING' })

        act(() => {
            const overrideEmailState = result.current[3]
            overrideEmailState({ value: 'test@sg.co', validate: false })
        })

        expect(result.current[0]).toStrictEqual({ value: 'test@sg.co', kind: 'NOT_VALIDATED' })

        act(() => {
            const overrideEmailState = result.current[3]
            overrideEmailState({ value: '' })
        })

        expect(result.current[0]).toStrictEqual({ value: '', kind: 'NOT_VALIDATED' })
    }


() =>
            useInputValidation({
                synchronousValidators: [isDotCo],
            })


() => {
            const [, nextEmailFieldChange, emailInputReference] = result.current
            const inputElement = createEmailInputElement()
            emailInputReference.current = (inputElement as unknown) as HTMLInputElement

            inputElement.changeValue('test-string')
            nextEmailFieldChange({
                target: emailInputReference.current,
                preventDefault: noop,
            } as React.ChangeEvent<HTMLInputElement>)
        }


() => {
            const overrideEmailState = result.current[3]
            overrideEmailState({ value: 'test@sg.co', validate: false })
        }


() => {
            const overrideEmailState = result.current[3]
            overrideEmailState({ value: '' })
        }


// Creating this type instead of a generic util because TS doesn't support higher-kinded types
type ObservableEmission<T> = T extends Observable<infer V> ? V : never


checkValidity() {
                // Check if custom validity was set
                if (internalStrings.validationMessage.length > 0) {
                    return false
                }
                // Built-in rules
                if (!internalStrings.value.includes('@')) {
                    internalStrings.validationMessage = "Email must include '@'"
                    return false
                }
                // Clear built-in messages
                internalStrings.validationMessage = ''
                return true
            }


setCustomValidity(error) {
                internalStrings.validationMessage = error
            }


changeValue(newValue) {
                internalStrings.value = newValue
            }


interface DrawerSlots {
    /* Main Content Slots */
    default: VNode[];

    /* Title Slots */
    title: VNode[];

    [key: string]: VNode[]
}


/** Drawer Component */
interface IDrawer extends ElementUIComponent {
    /* Equivalent to `Dialog`'s append to body attribute, when applying nested drawer, make sure this one is set to true */
    appendToBody: boolean

    /* Hook method called before close drawer, the first parameter is a function which should determine if the drawer should be closed */
    beforeClose: (done: hide) => void

    /** Whether the Drawer can be closed by pressing ESC */
    closeOnPressEscape: boolean

    /** Custom class names for Dialog */
    customClass: string

    /* Determine whether the wrapped children should be destroyed, if true, children's destroyed life cycle method will be called all local state will be destroyed */
    destroyOnClose: boolean

    /* Equivalent to `Dialog`'s modal attribute, determines whether the dark shadowing background should show */
    modal: boolean

    /* Equivalent to `Dialog`'s modal-append-to-body attribute, determines whether the shadowing background should be inserted direct to DocumentBody element */
    modalAppendToBody: boolean

    /* Attributes that controls the drawer's direction of display*/
    position: Direction

    /* Whether the close button should be rendered to control the drawer's visible state */
    showClose: boolean

    /* The size of the drawer component, supporting number with unit of pixel, string by percentage e.g. 30% */
    size: number | string

    /* The Drawer's title, also can be replaced by named slot `title` */
    title: string

    /* Whether the drawer component should show, also can be decorated by `.sync` */
    visible: boolean

    /* Flag attribute whi */
    wrapperClosable: boolean

    $slots: DrawerSlots
}


declare enum Direction {
    LTR = 'ltr', // left to right
    RTL = 'rtl', // right to left
    TTB = 'ttb', // top to bottom
    BTT = 'btt' // bottom to top
}


type hide = (shouldCancel: boolean) => void


export interface OptionDescriptor {
    /** Value of the option */
    value: string;
    /** Display label for the option */
    label: React.ReactNode;
    /** Whether the option is disabled or not */
    disabled?: boolean;
    /** Whether the option is active or not */
    active?: boolean;
    /** Unique identifier for the option */
    id?: string;
    /** Media to display to the left of the option content */
    media?: React.ReactElement<IconProps | ThumbnailProps | AvatarProps>;
}


export interface SectionDescriptor {
    /** Collection of options within the section */
    options: OptionDescriptor[];
    /** Section title */
    title?: string;
}


export interface BaseButton {
    /** A unique identifier for the button */
    id?: string;
    /** A destination to link to, rendered in the href attribute of a link */
    url?: string;
    /** Forces url to open in a new tab */
    external?: boolean;
    /** Tells the browser to download the url instead of opening it. Provides a hint for the downloaded filename if it is a string value */
    download?: string | boolean;
    /** Allows the button to submit a form */
    submit?: boolean;
    /** Disables the button, disallowing merchant interaction */
    disabled?: boolean;
    /** Replaces button text with a spinner while a background action is being performed */
    loading?: boolean;
    /** Sets the button in a pressed state */
    pressed?: boolean;
    /** Visually hidden text for screen readers */
    accessibilityLabel?: string;
    /** A valid WAI-ARIA role to define the semantic value of this element */
    role?: string;
    /** Id of the element the button controls */
    ariaControls?: string;
    /** Tells screen reader the controlled element is expanded */
    ariaExpanded?: boolean;
    /** Indicates the ID of the element that describes the button */
    ariaDescribedBy?: string;
    /** Callback when clicked */
    onClick?(): void;
    /** Callback when button becomes focussed */
    onFocus?(): void;
    /** Callback when focus leaves button */
    onBlur?(): void;
    /** Callback when a keypress event is registered on the button */
    onKeyPress?(event: React.KeyboardEvent<HTMLButtonElement>): void;
    /** Callback when a keyup event is registered on the button */
    onKeyUp?(event: React.KeyboardEvent<HTMLButtonElement>): void;
    /** Callback when a keydown event is registered on the button */
    onKeyDown?(event: React.KeyboardEvent<HTMLButtonElement>): void;
    /** Callback when mouse enter */
    onMouseEnter?(): void;
    /** Callback when element is touched */
    onTouchStart?(): void;
}


export interface Action {
    /** A unique identifier for the action */
    id?: string;
    /** Content the action displays */
    content?: string;
    /** Visually hidden text for screen readers */
    accessibilityLabel?: string;
    /** A destination to link to, rendered in the action */
    url?: string;
    /** Forces url to open in a new tab */
    external?: boolean;
    /** Callback when an action takes place */
    onAction?(): void;
    /** Callback when mouse enter */
    onMouseEnter?(): void;
    /** Callback when element is touched */
    onTouchStart?(): void;
}


export interface LinkAction {
    /** A unique identifier for the action */
    id?: string;
    /** Content the action displays */
    content?: string;
    /** Visually hidden text for screen readers */
    accessibilityLabel?: string;
    /** A destination to link to */
    url: string;
}


export interface BadgeAction {
    badge?: {
        status: 'new';
        content: string;
    };
}


export interface BaseCallbackAction {
    /** A unique identifier for the action */
    id?: string;
    /** Content the action displays */
    content?: string;
    /** Visually hidden text for screen readers */
    accessibilityLabel?: string;
    /** Callback when an action takes place */
    onAction(): void;
}


export interface CallbackAction extends BaseCallbackAction {
}


export interface DisableableAction extends Action {
    /** Whether or not the action is disabled */
    disabled?: boolean;
}


export interface DestructableAction extends Action {
    /** Destructive action */
    destructive?: boolean;
}


export interface IconableAction extends Action {
    /** Source of the icon */
    icon?: IconSource;
}


export interface LoadableAction extends Action {
    /** Should a spinner be displayed */
    loading?: boolean;
}


export interface OutlineableAction extends Action {
    /** Should action be displayed as an outlined button */
    outline?: boolean;
}


export interface PlainAction extends Action {
    /** Should action be displayed as a plain link */
    plain?: boolean;
}


export interface ActionListItemDescriptor extends IconableAction, DisableableAction, BadgeAction, DestructableAction {
    /** Visually hidden text for screen readers */
    accessibilityLabel?: string;
    /** Additional hint text to display with item */
    helpText?: string;
    /** Image source */
    image?: string;
    /** Prefix source */
    prefix?: React.ReactNode;
    /** Suffix source */
    suffix?: React.ReactNode;
    /**  Add an ellipsis suffix to action content */
    ellipsis?: boolean;
    /** Whether the action is active or not */
    active?: boolean;
    /** Defines a role for the action */
    role?: string;
}


export interface ActionListSection {
    /** Section title */
    title?: string;
    /** Collection of action items for the list */
    items: readonly ActionListItemDescriptor[];
}


export interface ComplexAction extends Action, DisableableAction, DestructableAction, IconableAction, OutlineableAction, LoadableAction, PlainAction {
}


export interface MenuActionDescriptor extends ComplexAction {
    /** Zero-indexed numerical position. Overrides the action's order in the menu */
    index?: number;
}


export interface MenuGroupDescriptor extends BadgeAction {
    /** Menu group title */
    title: string;
    /** List of actions */
    actions: ActionListItemDescriptor[];
    /** Icon to display */
    icon?: IconableAction['icon'];
    /** Action details */
    details?: React.ReactNode;
    /** Zero-indexed numerical position. Overrides the group's order in the menu. */
    index?: number;
    /** Callback when any action takes place */
    onActionAnyItem?: ActionListItemDescriptor['onAction'];
}


export interface ConnectedDisclosure {
    /** Visually hidden label for the connected disclosure button.
     * @default 'Related actions'
     */
    accessibilityLabel?: string;
    /** Whether or not the disclosure is disabled */
    disabled?: boolean;
    /** List of actions */
    actions: ActionListItemDescriptor[];
}


export interface CheckboxHandles {
    focus(): void;
}


export declare enum Key {
    Backspace = 8,
    Tab = 9,
    Enter = 13,
    Shift = 16,
    Ctrl = 17,
    Alt = 18,
    Pause = 19,
    CapsLock = 20,
    Escape = 27,
    Space = 32,
    PageUp = 33,
    PageDown = 34,
    End = 35,
    Home = 36,
    LeftArrow = 37,
    UpArrow = 38,
    RightArrow = 39,
    DownArrow = 40,
    Insert = 45,
    Delete = 46,
    Key0 = 48,
    Key1 = 49,
    Key2 = 50,
    Key3 = 51,
    Key4 = 52,
    Key5 = 53,
    Key6 = 54,
    Key7 = 55,
    Key8 = 56,
    Key9 = 57,
    KeyA = 65,
    KeyB = 66,
    KeyC = 67,
    KeyD = 68,
    KeyE = 69,
    KeyF = 70,
    KeyG = 71,
    KeyH = 72,
    KeyI = 73,
    KeyJ = 74,
    KeyK = 75,
    KeyL = 76,
    KeyM = 77,
    KeyN = 78,
    KeyO = 79,
    KeyP = 80,
    KeyQ = 81,
    KeyR = 82,
    KeyS = 83,
    KeyT = 84,
    KeyU = 85,
    KeyV = 86,
    KeyW = 87,
    KeyX = 88,
    KeyY = 89,
    KeyZ = 90,
    LeftMeta = 91,
    RightMeta = 92,
    Select = 93,
    Numpad0 = 96,
    Numpad1 = 97,
    Numpad2 = 98,
    Numpad3 = 99,
    Numpad4 = 100,
    Numpad5 = 101,
    Numpad6 = 102,
    Numpad7 = 103,
    Numpad8 = 104,
    Numpad9 = 105,
    Multiply = 106,
    Add = 107,
    Subtract = 109,
    Decimal = 110,
    Divide = 111,
    F1 = 112,
    F2 = 113,
    F3 = 114,
    F4 = 115,
    F5 = 116,
    F6 = 117,
    F7 = 118,
    F8 = 119,
    F9 = 120,
    F10 = 121,
    F11 = 122,
    F12 = 123,
    NumLock = 144,
    ScrollLock = 145,
    Semicolon = 186,
    Equals = 187,
    Comma = 188,
    Dash = 189,
    Period = 190,
    ForwardSlash = 191,
    GraveAccent = 192,
    OpenBracket = 219,
    BackSlash = 220,
    CloseBracket = 221,
    SingleQuote = 222
}


export declare type Descriptor = SectionDescriptor | OptionDescriptor;


export declare type IconSource = React.FunctionComponent<React.SVGProps<SVGSVGElement>> | 'placeholder' | string;


export declare type HeadingTagName = 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'p';


export declare type Error = string | React.ReactElement | (string | React.ReactElement)[];


export declare type NonEmptyArray<T> = [T, ...T[]];


export declare type ArrayElement<T> = T extends (infer U)[] ? U : never;


@Component({
  selector: 'basic',
  templateUrl: './basic.component.html',
//   styleUrls: ['./home.component.css']
})
export class BasicComponent {
    @Input() framework: String;
    welcomeMessage: string = 'Hello from ';

    code1 = ` import { Component } from '@angular/core';

 @Component({
   selector: 'single-component-selector',
   template: '
Hello from Angular SFC
',
   styles: ['h1 {font-weight: bold;}']
 })
 export class SingleComponent {
   constructor() { }
 }
    `

    code2 = ` import { Component } from '@angular/core';

 @Component({
   selector: 'basic-component-selector',
   templateUrl: './basic.component.html', //templateUrl instead of template
   styleUrls: ['./basic.component.css']  //styleUrls instead of styles
 })
 export class BasicComponent {
   constructor() { }
 }
    `

    code3 = `

Angular component

   <basic-component-selector></basic-component-selector>

       `

    code4 = ` /* ... */
 import { BasicComponent } from './basic-component/basic.component'

 @NgModule({
   declatations: [
     /* ... */
     BasicComponent,
    ],
    /* ... */
 })
 export class AppModule { }
`

}


export async function createRoom(userId: string, roomName: string) {
  const user = await Users.findOne({ where: { userId } })
  if(!user) return

  const alphabet =
    '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
  const nanoid = customAlphabet(alphabet, 7)
  const roomId = nanoid()

  await Rooms.create({
    roomName: roomName,
    roomId: roomId,
    createdByUserId: userId,
  })

  return roomId
}


export async function checkIfRoomExists(roomId: string) {
  const room = await Rooms.findOne({ where: { roomId: roomId } })

  return room ? true : false
}


declare class Nedb {
    constructor(pathOrOptions?: string | Nedb.DataStoreOptions);

    persistence: Nedb.Persistence;

    /**
     * Load the database from the datafile, and trigger the execution of buffered commands if any
     */
    loadDatabase(cb?: (err: Error) => void): void;

    /**
     * Get an array of all the data in the database
     */
    getAllData(): Array<any>;

    /**
     * Reset all currently defined indexes
     */
    resetIndexes(newData: any): void;

    /**
     * Ensure an index is kept for this field. Same parameters as lib/indexes
     * For now this function is synchronous, we need to test how much time it takes
     * We use an async API for consistency with the rest of the code
     * @param {String} options.fieldName
     * @param {Boolean} options.unique
     * @param {Boolean} options.sparse
     * @param {Function} cb Optional callback, signature: err
     */
    ensureIndex(options: Nedb.EnsureIndexOptions, cb?: (err: Error) => void): void;

    /**
     * Remove an index
     * @param {String} fieldName
     * @param {Function} cb Optional callback, signature: err
     */
    removeIndex(fieldName: string, cb?: (err: Error) => void): void;

    /**
     * Add one or several document(s) to all indexes
     */
    addToIndexes<T>(doc: T | T[]): void;

    /**
     * Remove one or several document(s) from all indexes
     */
    removeFromIndexes<T>(doc: T | T[]): void;

    /**
     * Update one or several documents in all indexes
     * To update multiple documents, oldDoc must be an array of { oldDoc, newDoc } pairs
     * If one update violates a constraint, all changes are rolled back
     */
    updateIndexes<T>(oldDoc: T, newDoc: T): void;
    updateIndexes<T>(updates: Array<{oldDoc: T; newDoc: T}>): void;

    /**
     * Return the list of candidates for a given query
     * Crude implementation for now, we return the candidates given by the first usable index if any
     * We try the following query types, in this order: basic match, $in match, comparison match
     * One way to make it better would be to enable the use of multiple indexes if the first usable index
     * returns too much data. I may do it in the future.
     *
     * TODO: needs to be moved to the Cursor module
     */
    getCandidates(query: any): void;

    /**
     * Insert a new document
     * @param {Function} cb Optional callback, signature: err, insertedDoc
     */
    insert<T>(newDoc: T, cb?: (err: Error, document: T) => void): void;

    /**
     * Count all documents matching the query
     * @param {any} query MongoDB-style query
     */
    count(query: any, callback: (err: Error, n: number) => void): void;
    count(query: any): Nedb.CursorCount;

    /**
     * Find all documents matching the query
     * If no callback is passed, we return the cursor so that user can limit, skip and finally exec
     * @param {any} query MongoDB-style query
     * @param {any} projection MongoDB-style projection
     */
    find<T>(query: any, projection: T, callback: (err: Error, documents: Array<T>) => void): void;
    find<T>(query: any, projection?: T): Nedb.Cursor<T>;

    /**
     * Find all documents matching the query
     * If no callback is passed, we return the cursor so that user can limit, skip and finally exec
     * * @param {any} query MongoDB-style query
     */
    find<T>(query: any, callback: (err: Error, documents: Array<T>) => void): void;

    /**
     * Find one document matching the query
     * @param {any} query MongoDB-style query
     * @param {any} projection MongoDB-style projection
     */
    findOne<T>(query: any, projection: T, callback: (err: Error, document: T) => void): void;

    /**
     * Find one document matching the query
     * @param {any} query MongoDB-style query
     */
    findOne<T>(query: any, callback: (err: Error, document: T) => void): void;

    /**
     * Update all docs matching query v1.7.4 and prior signature.
     * For now, very naive implementation (recalculating the whole database)
     * @param {any} query
     * @param {any} updateQuery
     * @param {Object} options Optional options
     *                 options.multi If true, can update multiple documents (defaults to false)
     *                 options.upsert If true, document is inserted if the query doesn't match anything
     * @param {Function} cb Optional callback, signature: err,
     *                                                    numReplaced,
     *                                                    upsert (set to true if the update was in fact an upsert)
     *
     * @api private Use Datastore.update which has the same signature
     */
    update(query: any, updateQuery: any, options?: Nedb.UpdateOptions, cb?: (err: Error, numberOfUpdated: number, upsert: boolean) => void): void;

    /**
     * Update all docs matching query v1.8 signature.
     * For now, very naive implementation (recalculating the whole database)
     * @param {any} query
     * @param {any} updateQuery
     * @param {Object} options Optional options
     *                 options.multi If true, can update multiple documents (defaults to false)
     *                 options.upsert If true, document is inserted if the query doesn't match anything
     * @param {Function} cb Optional callback, signature: err,
     *                                                    numAffected,
     *                                                    affectedDocuments (when returnUpdatedDocs is set to true), obj or array
     *                                                    upsert (set to true if the update was in fact an upsert)
     *
     * @api private Use Datastore.update which has the same signature
     */
    update<T>(query: any, updateQuery: any, options?: Nedb.UpdateOptions, cb?: (err: Error, numberOfUpdated: number, affectedDocuments: any, upsert: boolean) => void): void;

    /**
     * Remove all docs matching the query
     * For now very naive implementation (similar to update)
     * @param {Object} query
     * @param {Object} options Optional options
     *                 options.multi If true, can update multiple documents (defaults to false)
     * @param {Function} cb Optional callback, signature: err, numRemoved
     *
     * @api private Use Datastore.remove which has the same signature
     */
    remove(query: any, options: Nedb.RemoveOptions, cb?: (err: Error, n: number) => void): void;
    remove(query: any, cb?: (err: Error, n: number) => void): void;
}


interface Cursor<T> {
        sort(query: any): Cursor<T>;
        skip(n: number): Cursor<T>;
        limit(n: number): Cursor<T>;
        projection(query: any): Cursor<T>;
        exec(callback: (err: Error, documents: Array<T>) => void): void;
    }


interface CursorCount {
        exec(callback: (err: Error, count: number) => void): void;
    }


interface DataStoreOptions {
        filename?: string // Optional, datastore will be in-memory only if not provided
        inMemoryOnly?: boolean // Optional, default to false
        nodeWebkitAppName?: boolean // Optional, specify the name of your NW app if you want options.filename to be relative to the directory where
        autoload?: boolean // Optional, defaults to false
        // Optional, if autoload is used this will be called after the load database with the error object as parameter. If you don't pass it the error will be thrown
        onload?: (error: Error) => any
        // (optional): hook you can use to transform data after it was serialized and before it is written to disk.
        // Can be used for example to encrypt data before writing database to disk.
        // This function takes a string as parameter (one line of an NeDB data file) and outputs the transformed string, which must absolutely not contain a \n character (or data will be lost)
        afterSerialization?: (line: string) => string;
        // (optional): reverse of afterSerialization.
        // Make sure to include both and not just one or you risk data loss.
        // For the same reason, make sure both functions are inverses of one another.
        // Some failsafe mechanisms are in place to prevent data loss if you misuse the serialization hooks:
        // NeDB checks that never one is declared without the other, and checks that they are reverse of one another by testing on random strings of various lengths.
        // In addition, if too much data is detected as corrupt,
        // NeDB will refuse to start as it could mean you're not using the deserialization hook corresponding to the serialization hook used before (see below)
        beforeDeserialization?: (line: string) => string;
        // (optional): between 0 and 1, defaults to 10%. NeDB will refuse to start if more than this percentage of the datafile is corrupt.
        // 0 means you don't tolerate any corruption, 1 means you don't care
        corruptAlertThreshold?: number;
    }


/**
     * multi (defaults to false) which allows the modification of several documents if set to true
     * upsert (defaults to false) if you want to insert a new document corresponding to the update rules if your query doesn't match anything
     */
interface UpdateOptions {
        multi?: boolean;
        upsert?: boolean;
        returnUpdatedDocs?: boolean
    }


/**
     * options only one option for now: multi which allows the removal of multiple documents if set to true. Default is false
     */
interface RemoveOptions {
        multi?: boolean
    }


interface EnsureIndexOptions {
        fieldName: string;
        unique?: boolean;
        sparse?: boolean;
    }


interface Persistence {
        compactDatafile(): void;
        setAutocompactionInterval(interval: number): void;
        stopAutocompaction(): void;
    }


/**
     * Load the database from the datafile, and trigger the execution of buffered commands if any
     */
loadDatabase(cb?: (err: Error) => void): void;


/**
     * Get an array of all the data in the database
     */
getAllData(): Array<any>;


/**
     * Reset all currently defined indexes
     */
resetIndexes(newData: any): void;


/**
     * Ensure an index is kept for this field. Same parameters as lib/indexes
     * For now this function is synchronous, we need to test how much time it takes
     * We use an async API for consistency with the rest of the code
     * @param {String} options.fieldName
     * @param {Boolean} options.unique
     * @param {Boolean} options.sparse
     * @param {Function} cb Optional callback, signature: err
     */
ensureIndex(options: Nedb.EnsureIndexOptions, cb?: (err: Error) => void): void;


/**
     * Remove an index
     * @param {String} fieldName
     * @param {Function} cb Optional callback, signature: err
     */
removeIndex(fieldName: string, cb?: (err: Error) => void): void;


/**
     * Add one or several document(s) to all indexes
     */
addToIndexes<T>(doc: T | T[]): void;


/**
     * Remove one or several document(s) from all indexes
     */
removeFromIndexes<T>(doc: T | T[]): void;


/**
     * Update one or several documents in all indexes
     * To update multiple documents, oldDoc must be an array of { oldDoc, newDoc } pairs
     * If one update violates a constraint, all changes are rolled back
     */
updateIndexes<T>(oldDoc: T, newDoc: T): void;


updateIndexes<T>(updates: Array<{oldDoc: T; newDoc: T}>): void;


/**
     * Return the list of candidates for a given query
     * Crude implementation for now, we return the candidates given by the first usable index if any
     * We try the following query types, in this order: basic match, $in match, comparison match
     * One way to make it better would be to enable the use of multiple indexes if the first usable index
     * returns too much data. I may do it in the future.
     *
     * TODO: needs to be moved to the Cursor module
     */
getCandidates(query: any): void;


/**
     * Insert a new document
     * @param {Function} cb Optional callback, signature: err, insertedDoc
     */
insert<T>(newDoc: T, cb?: (err: Error, document: T) => void): void;


/**
     * Count all documents matching the query
     * @param {any} query MongoDB-style query
     */
count(query: any, callback: (err: Error, n: number) => void): void;


count(query: any): Nedb.CursorCount;


/**
     * Find all documents matching the query
     * If no callback is passed, we return the cursor so that user can limit, skip and finally exec
     * @param {any} query MongoDB-style query
     * @param {any} projection MongoDB-style projection
     */
find<T>(query: any, projection: T, callback: (err: Error, documents: Array<T>) => void): void;


find<T>(query: any, projection?: T): Nedb.Cursor<T>;


/**
     * Find all documents matching the query
     * If no callback is passed, we return the cursor so that user can limit, skip and finally exec
     * * @param {any} query MongoDB-style query
     */
find<T>(query: any, callback: (err: Error, documents: Array<T>) => void): void;


/**
     * Find one document matching the query
     * @param {any} query MongoDB-style query
     * @param {any} projection MongoDB-style projection
     */
findOne<T>(query: any, projection: T, callback: (err: Error, document: T) => void): void;


/**
     * Find one document matching the query
     * @param {any} query MongoDB-style query
     */
findOne<T>(query: any, callback: (err: Error, document: T) => void): void;


/**
     * Update all docs matching query v1.7.4 and prior signature.
     * For now, very naive implementation (recalculating the whole database)
     * @param {any} query
     * @param {any} updateQuery
     * @param {Object} options Optional options
     *                 options.multi If true, can update multiple documents (defaults to false)
     *                 options.upsert If true, document is inserted if the query doesn't match anything
     * @param {Function} cb Optional callback, signature: err,
     *                                                    numReplaced,
     *                                                    upsert (set to true if the update was in fact an upsert)
     *
     * @api private Use Datastore.update which has the same signature
     */
update(query: any, updateQuery: any, options?: Nedb.UpdateOptions, cb?: (err: Error, numberOfUpdated: number, upsert: boolean) => void): void;


/**
     * Update all docs matching query v1.8 signature.
     * For now, very naive implementation (recalculating the whole database)
     * @param {any} query
     * @param {any} updateQuery
     * @param {Object} options Optional options
     *                 options.multi If true, can update multiple documents (defaults to false)
     *                 options.upsert If true, document is inserted if the query doesn't match anything
     * @param {Function} cb Optional callback, signature: err,
     *                                                    numAffected,
     *                                                    affectedDocuments (when returnUpdatedDocs is set to true), obj or array
     *                                                    upsert (set to true if the update was in fact an upsert)
     *
     * @api private Use Datastore.update which has the same signature
     */
update<T>(query: any, updateQuery: any, options?: Nedb.UpdateOptions, cb?: (err: Error, numberOfUpdated: number, affectedDocuments: any, upsert: boolean) => void): void;


/**
     * Remove all docs matching the query
     * For now very naive implementation (similar to update)
     * @param {Object} query
     * @param {Object} options Optional options
     *                 options.multi If true, can update multiple documents (defaults to false)
     * @param {Function} cb Optional callback, signature: err, numRemoved
     *
     * @api private Use Datastore.remove which has the same signature
     */
remove(query: any, options: Nedb.RemoveOptions, cb?: (err: Error, n: number) => void): void;


remove(query: any, cb?: (err: Error, n: number) => void): void;


(resolve, reject) => {
      const poll = () => {
        if (this.isResultAvailable()) {
          resolve(this.getResult());
          this._pollingPromise = null;
        } else if (counter++ > limit) {
          reject('Timed out');
          this._pollingPromise = null;
        } else {
          requestAnimationFrame(poll);
        }
      };

      requestAnimationFrame(poll);
    }


() => {
        if (this.isResultAvailable()) {
          resolve(this.getResult());
          this._pollingPromise = null;
        } else if (counter++ > limit) {
          reject('Timed out');
          this._pollingPromise = null;
        } else {
          requestAnimationFrame(poll);
        }
      }


/**
 * Asynchronous queries for different kinds of information
 */
export default class Query extends WebGLResource<QueryProps> {
  get [Symbol.toStringTag](): string { return 'Query'; }

  // Returns true if Query is supported by the WebGL implementation
  // Can also check whether timestamp queries are available.
  static isSupported(gl: WebGLRenderingContext, options: string[] = []): boolean {
    const webgl2 = isWebGL2(gl);

    // Initial value
    const webglDevice = WebGLDevice.attach(gl);
    const hasTimerQuery = webglDevice.features.has('timer-query-webgl');
    let supported = webgl2 || hasTimerQuery;

    for (const key of options) {
      switch (key) {
        case 'queries':
          supported = supported && webgl2;
          break;
        case 'timers':
          supported = supported && hasTimerQuery;
          break;
        default:
          assert(false);
      }
    }

    return supported;
  }

  target = null;
  _queryPending = false;
  _pollingPromise = null;

  // Create a query class
  constructor(gl: WebGLRenderingContext, opts: QueryProps = {}) {
    super(WebGLDevice.attach(gl), opts, {} as any);
    Object.seal(this);
  }

  // Shortcut for timer query (dependent on extension in both WebGL1 and 2)
  // Measures GPU time delta between this call and a matching `end` call in the
  // GPU instruction stream.
  beginTimeElapsedQuery(): this {
    return this.begin(GL_TIME_ELAPSED_EXT);
  }

  // Shortcut for occlusion queries
  beginOcclusionQuery(options?: {conservative?: boolean}): this {
    return this.begin(options?.conservative ? GL_ANY_SAMPLES_PASSED_CONSERVATIVE : GL_ANY_SAMPLES_PASSED);
  }

  // Shortcut for transformFeedbackQuery
  beginTransformFeedbackQuery(): this {
    return this.begin(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN);
  }

  // Due to OpenGL API limitations, after calling `begin()` on one Query
  // instance, `end()` must be called on that same instance before
  // calling `begin()` on another query. While there can be multiple
  // outstanding queries representing disjoint `begin()`/`end()` intervals.
  // It is not possible to interleave or overlap `begin` and `end` calls.
  begin(target: number): this {
    // Don't start a new query if one is already active.
    if (this._queryPending) {
      return this;
    }

    this.target = target;
    this.gl2.beginQuery(this.target, this.handle);

    return this;
  }

  // ends the current query
  end(): this {
    // Can't end a new query if the last one hasn't been resolved.
    if (this._queryPending) {
      return this;
    }

    if (this.target) {
      this.gl2.endQuery(this.target);
      this.target = null;
      this._queryPending = true;
    }
    return this;
  }

  // Returns true if the query result is available
  isResultAvailable(): boolean {
    if (!this._queryPending) {
      return false;
    }

    const resultAvailable = this.gl2.getQueryParameter(this.handle, GL_QUERY_RESULT_AVAILABLE);
    if (resultAvailable) {
      this._queryPending = false;
    }
    return resultAvailable;
  }

  // Timing query is disjoint, i.e. results are invalid
  isTimerDisjoint(): boolean {
    return this.gl2.getParameter(GL_GPU_DISJOINT_EXT);
  }

  // Returns query result.
  getResult(): any {
    return this.gl2.getQueryParameter(this.handle, GL_QUERY_RESULT);
  }

  // Returns the query result, converted to milliseconds to match JavaScript conventions.
  getTimerMilliseconds() {
    return this.getResult() / 1e6;
  }

  // Polls the query
  createPoll(limit: number = Number.POSITIVE_INFINITY): Promise<any> {
    if (this._pollingPromise) {
      return this._pollingPromise;
    }

    let counter = 0;

    this._pollingPromise = new Promise((resolve, reject) => {
      const poll = () => {
        if (this.isResultAvailable()) {
          resolve(this.getResult());
          this._pollingPromise = null;
        } else if (counter++ > limit) {
          reject('Timed out');
          this._pollingPromise = null;
        } else {
          requestAnimationFrame(poll);
        }
      };

      requestAnimationFrame(poll);
    });

    return this._pollingPromise;
  }

  _createHandle() {
    return Query.isSupported(this.gl) ? this.gl2.createQuery() : null;
  }

  _deleteHandle() {
    this.gl2.deleteQuery(this.handle);
  }
}


export type QueryProps = ResourceProps & {
};


// Returns true if Query is supported by the WebGL implementation
// Can also check whether timestamp queries are available.
static isSupported(gl: WebGLRenderingContext, options: string[] = []): boolean {
    const webgl2 = isWebGL2(gl);

    // Initial value
    const webglDevice = WebGLDevice.attach(gl);
    const hasTimerQuery = webglDevice.features.has('timer-query-webgl');
    let supported = webgl2 || hasTimerQuery;

    for (const key of options) {
      switch (key) {
        case 'queries':
          supported = supported && webgl2;
          break;
        case 'timers':
          supported = supported && hasTimerQuery;
          break;
        default:
          assert(false);
      }
    }

    return supported;
  }


// Shortcut for timer query (dependent on extension in both WebGL1 and 2)
// Measures GPU time delta between this call and a matching `end` call in the
// GPU instruction stream.
beginTimeElapsedQuery(): this {
    return this.begin(GL_TIME_ELAPSED_EXT);
  }


// Shortcut for occlusion queries
beginOcclusionQuery(options?: {conservative?: boolean}): this {
    return this.begin(options?.conservative ? GL_ANY_SAMPLES_PASSED_CONSERVATIVE : GL_ANY_SAMPLES_PASSED);
  }


// Shortcut for transformFeedbackQuery
beginTransformFeedbackQuery(): this {
    return this.begin(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN);
  }


// Due to OpenGL API limitations, after calling `begin()` on one Query
// instance, `end()` must be called on that same instance before
// calling `begin()` on another query. While there can be multiple
// outstanding queries representing disjoint `begin()`/`end()` intervals.
// It is not possible to interleave or overlap `begin` and `end` calls.
begin(target: number): this {
    // Don't start a new query if one is already active.
    if (this._queryPending) {
      return this;
    }

    this.target = target;
    this.gl2.beginQuery(this.target, this.handle);

    return this;
  }


// ends the current query
end(): this {
    // Can't end a new query if the last one hasn't been resolved.
    if (this._queryPending) {
      return this;
    }

    if (this.target) {
      this.gl2.endQuery(this.target);
      this.target = null;
      this._queryPending = true;
    }
    return this;
  }


// Returns true if the query result is available
isResultAvailable(): boolean {
    if (!this._queryPending) {
      return false;
    }

    const resultAvailable = this.gl2.getQueryParameter(this.handle, GL_QUERY_RESULT_AVAILABLE);
    if (resultAvailable) {
      this._queryPending = false;
    }
    return resultAvailable;
  }


// Timing query is disjoint, i.e. results are invalid
isTimerDisjoint(): boolean {
    return this.gl2.getParameter(GL_GPU_DISJOINT_EXT);
  }


// Returns query result.
getResult(): any {
    return this.gl2.getQueryParameter(this.handle, GL_QUERY_RESULT);
  }


// Returns the query result, converted to milliseconds to match JavaScript conventions.
getTimerMilliseconds() {
    return this.getResult() / 1e6;
  }


// Polls the query
createPoll(limit: number = Number.POSITIVE_INFINITY): Promise<any> {
    if (this._pollingPromise) {
      return this._pollingPromise;
    }

    let counter = 0;

    this._pollingPromise = new Promise((resolve, reject) => {
      const poll = () => {
        if (this.isResultAvailable()) {
          resolve(this.getResult());
          this._pollingPromise = null;
        } else if (counter++ > limit) {
          reject('Timed out');
          this._pollingPromise = null;
        } else {
          requestAnimationFrame(poll);
        }
      };

      requestAnimationFrame(poll);
    });

    return this._pollingPromise;
  }


_createHandle() {
    return Query.isSupported(this.gl) ? this.gl2.createQuery() : null;
  }


_deleteHandle() {
    this.gl2.deleteQuery(this.handle);
  }


of => MempoolModel


returns => Float


({ childComplexity, args }) => 1


returns => Int


returns => PaginatedUnconfirmedTransactionResponse


({ childComplexity, args }) => args.limit * childComplexity


@ArgsType()
class UnconfirmedTransactionsArgs extends PaginationArgs {

    @Field({ nullable: true })
    cursor: UnconfirmedTransactionCursor

}


@Service()
@Resolver(of => MempoolModel)
export class MempoolResolver {
    @FieldResolver(returns => Float, { nullable: false, complexity: ({ childComplexity, args }) => 1 })
    async totalFees(@Root() mempoolModel: MempoolModel): Promise<number> {
        return mempoolModel.coin.mempool.unconfirmedMempool.totalFeesSat / 1e8
    }

    @FieldResolver(returns => Int, { nullable: false, complexity: ({ childComplexity, args }) => 1 })
    async txCount(@Root() mempoolModel: MempoolModel): Promise<number> {
        return mempoolModel.coin.mempool.unconfirmedMempool.txs.size
    }

    @FieldResolver(returns => PaginatedUnconfirmedTransactionResponse, { nullable: false, complexity: ({ childComplexity, args }) => args.limit * childComplexity })
    async transactions(@Root() mempoolModel: MempoolModel, @Args() { cursor, limit }: UnconfirmedTransactionsArgs): Promise<PaginatedUnconfirmedTransactionResponse> {
        let it
        if (cursor) {
            it = mempoolModel.coin.mempool.unconfirmedMempool.txids.upperBound({ timestamp: cursor.timestamp.getTime(), txid: cursor.txid })
        } else {
            it = mempoolModel.coin.mempool.unconfirmedMempool.txids.iterator()
            it.next()
        }
        let item: { txid: string, timestamp: number } = it.data()
        const res: UnconfirmedTransaction[] = []
        let hasMore = false
        while (item !== null) {
            if (res.length === limit) {
                hasMore = true
                break
            }
            res.push(<UnconfirmedTransaction>{ coin: mempoolModel.coin, timestamp: new Date(item.timestamp), txid: item.txid })
            item = it.next()
        }
        return { items: res, hasMore: hasMore }
    }
}


@FieldResolver(returns => Float, { nullable: false, complexity: ({ childComplexity, args }) => 1 })
    async totalFees(@Root() mempoolModel: MempoolModel): Promise<number> {
        return mempoolModel.coin.mempool.unconfirmedMempool.totalFeesSat / 1e8
    }


@FieldResolver(returns => Int, { nullable: false, complexity: ({ childComplexity, args }) => 1 })
    async txCount(@Root() mempoolModel: MempoolModel): Promise<number> {
        return mempoolModel.coin.mempool.unconfirmedMempool.txs.size
    }


@FieldResolver(returns => PaginatedUnconfirmedTransactionResponse, { nullable: false, complexity: ({ childComplexity, args }) => args.limit * childComplexity })
    async transactions(@Root() mempoolModel: MempoolModel, @Args() { cursor, limit }: UnconfirmedTransactionsArgs): Promise<PaginatedUnconfirmedTransactionResponse> {
        let it
        if (cursor) {
            it = mempoolModel.coin.mempool.unconfirmedMempool.txids.upperBound({ timestamp: cursor.timestamp.getTime(), txid: cursor.txid })
        } else {
            it = mempoolModel.coin.mempool.unconfirmedMempool.txids.iterator()
            it.next()
        }
        let item: { txid: string, timestamp: number } = it.data()
        const res: UnconfirmedTransaction[] = []
        let hasMore = false
        while (item !== null) {
            if (res.length === limit) {
                hasMore = true
                break
            }
            res.push(<UnconfirmedTransaction>{ coin: mempoolModel.coin, timestamp: new Date(item.timestamp), txid: item.txid })
            item = it.next()
        }
        return { items: res, hasMore: hasMore }
    }


export class BrightnessProperty extends Property<number> {
  constructor(device: Device, name: string,
    // eslint-disable-next-line no-unused-vars
    private onChange: (value: number) => void) {
    super(device, name, {
      type: 'number',
      '@type': 'BrightnessProperty',
      title: 'brightness',
      description: 'The brightness of the dimmer',
    });
  }

  async setValue(value: number): Promise<number> {
    const newVal = await super.setValue(value);
    this.onChange(newVal);
    return newVal;
  }
}


async setValue(value: number): Promise<number> {
    const newVal = await super.setValue(value);
    this.onChange(newVal);
    return newVal;
  }


props =>
    props.theme.light
      ? `
   background-color: #00b8dc;
   background-image: linear-gradient(
    -86deg,
    #1db6dc 0%,
    #1db6dc 20%,
    #25c5ed 46%,
    #0fa2c6 75%,
    #0fa2c6 100%
  );`
      : 'background-color: #0d1f2d;'


class AccountBar extends React.Component<AccountBarProps, AccountBarState> {
  constructor(props) {
    super(props)
    this.state = {
      sendOpen: false,
      tooltipSendOpen: false,
      tooltipReceiveOpen: false,
      receiveOpen: false,
    }

    this.toggleSend = this.toggleSend.bind(this)
    this.toggleReceive = this.toggleReceive.bind(this)
    this.toggleSendTooltip = this.toggleSendTooltip.bind(this)
    this.toggleReceiveTooltip = this.toggleReceiveTooltip.bind(this)
  }

  toggleSend() {
    this.setState({ sendOpen: !this.state.sendOpen })
  }

  toggleReceive() {
    this.setState({ receiveOpen: !this.state.receiveOpen })
  }

  toggleSendTooltip() {
    this.setState({
      tooltipSendOpen: !this.state.tooltipSendOpen,
    })
  }

  toggleReceiveTooltip() {
    this.setState({
      tooltipReceiveOpen: !this.state.tooltipReceiveOpen,
    })
  }

  render() {
    const formatter = new Intl.NumberFormat(
      VergeCacheStore.get('locale', 'en-US'),
      {
        style: 'currency',
        currency: VergeCacheStore.get('currency', 'USD'),
        minimumFractionDigits: 2,
      },
    )

    const formatterPrice = new Intl.NumberFormat(
      VergeCacheStore.get('locale', 'en-US'),
      {
        style: 'currency',
        currency: VergeCacheStore.get('currency', 'USD'),
        minimumFractionDigits: 5,
      },
    )

    const XVGformatter = new Intl.NumberFormat(
      VergeCacheStore.get('locale', 'en-US'),
      {
        style: 'decimal',
        minimumFractionDigits: 2,
        maximumFractionDigits: 3,
      },
    )

    return (
      <AccountBarContainer className="container-fluid">
        <SendPanel open={this.state.sendOpen} toggle={this.toggleSend} />
        <ReceivePanel
          open={this.state.receiveOpen}
          toggle={this.toggleReceive}
        />
        <div className="row">
          <div
            className="col-md-3"
            style={{
              textAlign: 'left',
              fontWeight: 'bold',
              paddingTop: '10px',
              paddingLeft: '5%',
            }}


interface AccountBarProps {
  SettingsStore?: SettingsStore
  AccountInformationStore?: AccountInformationStore
  CoinStatsStore?: CoinStatsStore
}


interface AccountBarState {
  sendOpen: boolean
  tooltipSendOpen: boolean
  tooltipReceiveOpen: boolean
  receiveOpen: boolean
}


toggleSend() {
    this.setState({ sendOpen: !this.state.sendOpen })
  }


toggleReceive() {
    this.setState({ receiveOpen: !this.state.receiveOpen })
  }


toggleSendTooltip() {
    this.setState({
      tooltipSendOpen: !this.state.tooltipSendOpen,
    })
  }


toggleReceiveTooltip() {
    this.setState({
      tooltipReceiveOpen: !this.state.tooltipReceiveOpen,
    })
  }


render() {
    const formatter = new Intl.NumberFormat(
      VergeCacheStore.get('locale', 'en-US'),
      {
        style: 'currency',
        currency: VergeCacheStore.get('currency', 'USD'),
        minimumFractionDigits: 2,
      },
    )

    const formatterPrice = new Intl.NumberFormat(
      VergeCacheStore.get('locale', 'en-US'),
      {
        style: 'currency',
        currency: VergeCacheStore.get('currency', 'USD'),
        minimumFractionDigits: 5,
      },
    )

    const XVGformatter = new Intl.NumberFormat(
      VergeCacheStore.get('locale', 'en-US'),
      {
        style: 'decimal',
        minimumFractionDigits: 2,
        maximumFractionDigits: 3,
      },
    )

    return (
      <AccountBarContainer className="container-fluid">
        <SendPanel open={this.state.sendOpen} toggle={this.toggleSend} />
        <ReceivePanel
          open={this.state.receiveOpen}
          toggle={this.toggleReceive}
        />
        <div className="row">
          <div
            className="col-md-3"
            style={{
              textAlign: 'left',
              fontWeight: 'bold',
              paddingTop: '10px',
              paddingLeft: '5%',
            }}


function createConfigFile(fileName: string, content: any) {
	jest.unmock(`${__dirname}/${fileName}`);
	jest.mock(`${__dirname}/${fileName}`, () => content, {virtual: true});
}


(config, options) => ({
	...config,
	module: {
		rules: [
			{
				test: '.node',
				user: 'node-loader'
			}
		]
	}
})


() => content


() => {
	it('Should return original config if no custom configuration object has been provided', () => {
		CustomWebpackBuilder.buildWebpackConfig(__dirname as Path, null, baseWebpackConfig, {});
	})
	it('Should load webpack.config.js if no path specified', () => {
		createConfigFile(defaultWebpackConfigPath, customWebpackConfig);
		CustomWebpackBuilder.buildWebpackConfig(__dirname as Path, {}, baseWebpackConfig, {});
		expect(WebpackConfigMerger.merge).toHaveBeenCalledWith(baseWebpackConfig, customWebpackConfig, undefined, undefined);
	});

	it('Should load the file specified in configuration', () => {
		const fileName = 'extra-webpack.config.js';
		createConfigFile(fileName, customWebpackConfig);
		CustomWebpackBuilder.buildWebpackConfig(__dirname as Path, {path: fileName}, baseWebpackConfig, {});
		expect(WebpackConfigMerger.merge).toHaveBeenCalledWith(baseWebpackConfig, customWebpackConfig, undefined, undefined);
	});

	it('Should pass on merge strategies', () => {
		createConfigFile(defaultWebpackConfigPath, customWebpackConfig);
		const mergeStrategies: MergeStrategies = {'blah': 'prepend'};
		CustomWebpackBuilder.buildWebpackConfig(__dirname as Path, {mergeStrategies}, baseWebpackConfig, {});
		expect(WebpackConfigMerger.merge).toHaveBeenCalledWith(baseWebpackConfig, customWebpackConfig, mergeStrategies, undefined);
	});

	it('Should pass on replaceDuplicatePlugins flag', () => {
		createConfigFile(defaultWebpackConfigPath, customWebpackConfig);
		CustomWebpackBuilder.buildWebpackConfig(__dirname as Path, {replaceDuplicatePlugins: true}, baseWebpackConfig, {});
		expect(WebpackConfigMerger.merge).toHaveBeenCalledWith(baseWebpackConfig, customWebpackConfig, undefined, true);
	});

	it('Should pass build options to the webpack config function', () => {
		const spy = jest.fn((config, options) => config);
		createConfigFile(defaultWebpackConfigPath, spy);
		CustomWebpackBuilder.buildWebpackConfig(__dirname as Path, {}, baseWebpackConfig, buildOptions);
		expect(spy).toHaveBeenCalledWith(baseWebpackConfig, buildOptions);
	});

	it('Should apply custom function on configuration', () => {
		createConfigFile(defaultWebpackConfigPath, customWebpackFunction);
		const mergedConfig = CustomWebpackBuilder.buildWebpackConfig(__dirname as Path, {}, baseWebpackConfig, {});
		expect(mergedConfig).toEqual(customWebpackFunctionObj);
	});
}


() => {
		CustomWebpackBuilder.buildWebpackConfig(__dirname as Path, null, baseWebpackConfig, {});
	}


() => {
		createConfigFile(defaultWebpackConfigPath, customWebpackConfig);
		CustomWebpackBuilder.buildWebpackConfig(__dirname as Path, {}, baseWebpackConfig, {});
		expect(WebpackConfigMerger.merge).toHaveBeenCalledWith(baseWebpackConfig, customWebpackConfig, undefined, undefined);
	}


() => {
		const fileName = 'extra-webpack.config.js';
		createConfigFile(fileName, customWebpackConfig);
		CustomWebpackBuilder.buildWebpackConfig(__dirname as Path, {path: fileName}, baseWebpackConfig, {});
		expect(WebpackConfigMerger.merge).toHaveBeenCalledWith(baseWebpackConfig, customWebpackConfig, undefined, undefined);
	}


() => {
		createConfigFile(defaultWebpackConfigPath, customWebpackConfig);
		const mergeStrategies: MergeStrategies = {'blah': 'prepend'};
		CustomWebpackBuilder.buildWebpackConfig(__dirname as Path, {mergeStrategies}, baseWebpackConfig, {});
		expect(WebpackConfigMerger.merge).toHaveBeenCalledWith(baseWebpackConfig, customWebpackConfig, mergeStrategies, undefined);
	}


() => {
		createConfigFile(defaultWebpackConfigPath, customWebpackConfig);
		CustomWebpackBuilder.buildWebpackConfig(__dirname as Path, {replaceDuplicatePlugins: true}, baseWebpackConfig, {});
		expect(WebpackConfigMerger.merge).toHaveBeenCalledWith(baseWebpackConfig, customWebpackConfig, undefined, true);
	}


() => {
		const spy = jest.fn((config, options) => config);
		createConfigFile(defaultWebpackConfigPath, spy);
		CustomWebpackBuilder.buildWebpackConfig(__dirname as Path, {}, baseWebpackConfig, buildOptions);
		expect(spy).toHaveBeenCalledWith(baseWebpackConfig, buildOptions);
	}


(config, options) => config


() => {
		createConfigFile(defaultWebpackConfigPath, customWebpackFunction);
		const mergedConfig = CustomWebpackBuilder.buildWebpackConfig(__dirname as Path, {}, baseWebpackConfig, {});
		expect(mergedConfig).toEqual(customWebpackFunctionObj);
	}


(args) => (
  <MessageDetailsBody {...args} />
)


class SlideHorizontalWithIconTransitionController extends AbstractTransitionController<SlideHorizontalWithIconController>
{
	/**
	 * @public
	 * @method setupTransitionInTimeline
	 * @description overwrite this method in the parent class
	 * */
	protected setupTransitionInTimeline(): void
	{
		this.transitionInTimeline.from(this.element, 0.5, {
			autoAlpha: 0,
			display: 'none',
			ease: Linear.easeNone
		});
	}
}


/**
	 * @public
	 * @method setupTransitionInTimeline
	 * @description overwrite this method in the parent class
	 * */
protected setupTransitionInTimeline(): void
	{
		this.transitionInTimeline.from(this.element, 0.5, {
			autoAlpha: 0,
			display: 'none',
			ease: Linear.easeNone
		});
	}


export function cloneDiagnosticSettings(
        diagSettings: DiagnosticSettings): DiagnosticSettings {

    // Create a shallow copy of the existing object.
    return Object.assign({}, diagSettings);
}


export function getBooleanDiagnosticSettings() {
    return [
        'strictListInference',
        'strictDictionaryInference',
        'strictParameterNoneValue'
    ];
}


export function getDiagLevelSettings() {
    return [
        'reportTypeshedErrors',
        'reportMissingImports',
        'reportMissingTypeStubs',
        'reportImportCycles',
        'reportUnusedImport',
        'reportUnusedClass',
        'reportUnusedFunction',
        'reportUnusedVariable',
        'reportOptionalSubscript',
        'reportOptionalMemberAccess',
        'reportOptionalCall',
        'reportOptionalIterable',
        'reportOptionalContextManager',
        'reportOptionalOperand',
        'reportUntypedFunctionDecorator',
        'reportUntypedClassDecorator',
        'reportUntypedBaseClass',
        'reportUntypedNamedTuple',
        'reportPrivateUsage',
        'reportConstantRedefinition',
        'reportIncompatibleMethodOverride',
        'reportInvalidStringEscapeSequence',
        'reportUnknownParameterType',
        'reportUnknownVariableType',
        'reportUnknownMemberType',
        'reportCallInDefaultInitializer'
    ];
}


export function getStrictDiagnosticSettings(): DiagnosticSettings {
    const diagSettings: DiagnosticSettings = {
        strictListInference: true,
        strictDictionaryInference: true,
        strictParameterNoneValue: true,
        reportTypeshedErrors: 'error',
        reportMissingImports: 'error',
        reportMissingTypeStubs: 'error',
        reportImportCycles: 'error',
        reportUnusedImport: 'error',
        reportUnusedClass: 'error',
        reportUnusedFunction: 'error',
        reportUnusedVariable: 'error',
        reportOptionalSubscript: 'error',
        reportOptionalMemberAccess: 'error',
        reportOptionalCall: 'error',
        reportOptionalIterable: 'error',
        reportOptionalContextManager: 'error',
        reportOptionalOperand: 'error',
        reportUntypedFunctionDecorator: 'error',
        reportUntypedClassDecorator: 'error',
        reportUntypedBaseClass: 'error',
        reportUntypedNamedTuple: 'error',
        reportPrivateUsage: 'error',
        reportConstantRedefinition: 'error',
        reportIncompatibleMethodOverride: 'error',
        reportInvalidStringEscapeSequence: 'error',
        reportUnknownParameterType: 'error',
        reportUnknownVariableType: 'error',
        reportUnknownMemberType: 'error',
        reportCallInDefaultInitializer: 'none'
    };

    return diagSettings;
}


export function getDefaultDiagnosticSettings(): DiagnosticSettings {
    const diagSettings: DiagnosticSettings = {
        strictListInference: false,
        strictDictionaryInference: false,
        strictParameterNoneValue: false,
        reportTypeshedErrors: 'none',
        reportMissingImports: 'error',
        reportMissingTypeStubs: 'none',
        reportImportCycles: 'none',
        reportUnusedImport: 'none',
        reportUnusedClass: 'none',
        reportUnusedFunction: 'none',
        reportUnusedVariable: 'none',
        reportOptionalSubscript: 'none',
        reportOptionalMemberAccess: 'none',
        reportOptionalCall: 'none',
        reportOptionalIterable: 'none',
        reportOptionalContextManager: 'none',
        reportOptionalOperand: 'none',
        reportUntypedFunctionDecorator: 'none',
        reportUntypedClassDecorator: 'none',
        reportUntypedBaseClass: 'none',
        reportUntypedNamedTuple: 'none',
        reportPrivateUsage: 'none',
        reportConstantRedefinition: 'none',
        reportIncompatibleMethodOverride: 'none',
        reportInvalidStringEscapeSequence: 'warning',
        reportUnknownParameterType: 'none',
        reportUnknownVariableType: 'none',
        reportUnknownMemberType: 'none',
        reportCallInDefaultInitializer: 'none'
    };

    return diagSettings;
}


env => {
            let envRoot = ensureTrailingDirectorySeparator(
                normalizePath(combinePaths(this.projectRoot, env.root)));
            return filePath.startsWith(envRoot);
        }


(fileSpec, index) => {
                    if (typeof fileSpec !== 'string') {
                        console.log(`Index ${ index } of "include" array should be a string.`);
                    } else if (isAbsolute(fileSpec)) {
                        console.log(`Ignoring path "${ fileSpec }" in "include" array because it is not relative.`);
                    } else {
                        this.include.push(getFileSpec(this.projectRoot, fileSpec));
                    }
                }


(fileSpec, index) => {
                    if (typeof fileSpec !== 'string') {
                        console.log(`Index ${ index } of "exclude" array should be a string.`);
                    } else if (isAbsolute(fileSpec)) {
                        console.log(`Ignoring path "${ fileSpec }" in "exclude" array because it is not relative.`);
                    } else {
                        this.exclude.push(getFileSpec(this.projectRoot, fileSpec));
                    }
                }


(fileSpec, index) => {
                    if (typeof fileSpec !== 'string') {
                        console.log(`Index ${ index } of "ignore" array should be a string.`);
                    } else if (isAbsolute(fileSpec)) {
                        console.log(`Ignoring path "${ fileSpec }" in "ignore" array because it is not relative.`);
                    } else {
                        this.ignore.push(getFileSpec(this.projectRoot, fileSpec));
                    }
                }


(fileSpec, index) => {
                    if (typeof fileSpec !== 'string') {
                        console.log(`Index ${ index } of "strict" array should be a string.`);
                    } else if (isAbsolute(fileSpec)) {
                        console.log(`Ignoring path "${ fileSpec }" in "strict" array because it is not relative.`);
                    } else {
                        this.strict.push(getFileSpec(this.projectRoot, fileSpec));
                    }
                }


(env, index) => {
                    let execEnv = this._initExecutionEnvironmentFromJson(env, index, console);
                    if (execEnv) {
                        this.executionEnvironments.push(execEnv);
                    }
                }


(path, pathIndex) => {
                        if (typeof path !== 'string') {
                            console.log(`Config executionEnvironments index ${ index }:` +
                                ` extraPaths field ${ pathIndex } must be a string.`);
                        } else {
                            newExecEnv.extraPaths.push(normalizePath(combinePaths(this.projectRoot, path)));
                        }
                    }


export class ExecutionEnvironment {
    // Default to "." which indicates every file in the project.
    constructor(root: string, defaultPythonVersion?: PythonVersion, defaultPythonPlatform?: string) {
        this.root = root;
        this.pythonVersion = defaultPythonVersion || LatestStablePythonVersion;
        this.pythonPlatform = defaultPythonPlatform;
    }

    // Root directory for execution - absolute or relative to the
    // project root.
    root: string;

    // Always default to the latest stable version of the language.
    pythonVersion: PythonVersion;

    // Default to no platform.
    pythonPlatform?: string;

    // Default to no extra paths.
    extraPaths: string[] = [];

    // Name of virtual environment to use.
    venv?: string;
}