export class DebugViewlet extends viewlet.Viewlet {

	private toDispose: lifecycle.IDisposable[];
	private actions: actions.IAction[];
	private progressRunner: IProgressRunner;
	private viewletSettings: any;

	private $el: builder.Builder;
	private splitView: splitview.SplitView;
	private views: viewlet.IViewletView[];

	constructor(
		@ITelemetryService telemetryService: ITelemetryService,
		@IProgressService private progressService: IProgressService,
		@IDebugService private debugService: IDebugService,
		@IInstantiationService private instantiationService: IInstantiationService,
		@IWorkspaceContextService private contextService: IWorkspaceContextService,
		@IStorageService storageService: IStorageService
	) {
		super(debug.VIEWLET_ID, telemetryService);

		this.progressRunner = null;
		this.viewletSettings = this.getMemento(storageService, memento.Scope.WORKSPACE);
		this.views = [];
		this.toDispose = [];
		this.toDispose.push(this.debugService.addListener2(debug.ServiceEvents.STATE_CHANGED, () => {
			this.onDebugServiceStateChange();
		}));
	}

	// viewlet

	public create(parent: builder.Builder): TPromise<void> {
		super.create(parent);
		this.$el = parent.div().addClass('debug-viewlet');

		if (this.contextService.getWorkspace()) {
			const actionRunner = this.getActionRunner();
			this.views.push(this.instantiationService.createInstance(VariablesView, actionRunner, this.viewletSettings));
			this.views.push(this.instantiationService.createInstance(WatchExpressionsView, actionRunner, this.viewletSettings));
			this.views.push(this.instantiationService.createInstance(CallStackView, actionRunner, this.viewletSettings));
			this.views.push(this.instantiationService.createInstance(BreakpointsView, actionRunner, this.viewletSettings));

			this.splitView = new splitview.SplitView(this.$el.getHTMLElement());
			this.toDispose.push(this.splitView);
			this.views.forEach(v => this.splitView.addView(<any> v));
		} else {
			this.$el.append($([
				'
',
				'
', nls.localize('noWorkspace', "There is no currently opened folder."), '
',
				'
', nls.localize('pleaseRestartToDebug', "Open a folder in order to start debugging."), '
',
				'
'
			].join('')));
		}

		return TPromise.as(null);
	}

	public setVisible(visible: boolean): TPromise<void> {
		return super.setVisible(visible).then(() => {
			return Promise.join(this.views.map((view) => view.setVisible(visible)));
		});
	}

	public layout(dimension: builder.Dimension): void {
		if (this.splitView) {
			this.splitView.layout(dimension.height);
		}
	}

	public focus(): void {
		super.focus();

		if (this.views.length > 0) {
			(<VariablesView>this.views[0]).focusBody();
		}
	}

	public getActions(): actions.IAction[] {
		if (this.debugService.getState() === debug.State.Disabled) {
			return [];
		}

		if (!this.actions) {
			this.actions = [
				this.instantiationService.createInstance(debugactions.StartDebugAction, debugactions.StartDebugAction.ID, debugactions.StartDebugAction.LABEL),
				this.instantiationService.createInstance(debugactions.SelectConfigAction, debugactions.SelectConfigAction.ID, debugactions.SelectConfigAction.LABEL),
				this.instantiationService.createInstance(debugactions.ConfigureAction, debugactions.ConfigureAction.ID, debugactions.ConfigureAction.LABEL),
				this.instantiationService.createInstance(debugactions.ToggleReplAction, debugactions.ToggleReplAction.ID, debugactions.ToggleReplAction.LABEL)
			];

			this.actions.forEach(a => {
				this.toDispose.push(a);
			});
		}

		return this.actions;
	}

	public getActionItem(action: actions.IAction): actionbar.IActionItem {
		if (action.id === debugactions.SelectConfigAction.ID) {
			return this.instantiationService.createInstance(dbgactionitems.SelectConfigActionItem, action);
		}

		return null;
	}

	private onDebugServiceStateChange(): void {
		if (this.progressRunner) {
			this.progressRunner.done();
		}

		if (this.debugService.getState() === debug.State.Initializing) {
			this.progressRunner = this.progressService.show(true);
		} else {
			this.progressRunner = null;
		}
	}

	public dispose(): void {
		this.toDispose = lifecycle.disposeAll(this.toDispose);

		super.dispose();
	}

	public shutdown(): void {
		this.views.forEach(v => v.shutdown());
		super.shutdown();
	}
}


public renderHeader(container: HTMLElement): void {
		super.renderHeader(container);
		const titleDiv = $('div.title').appendTo(container);
		$('span').text(nls.localize('variables', "Variables")).appendTo(titleDiv);
	}


public renderBody(container: HTMLElement): void {
		dom.addClass(container, 'debug-variables');
		this.treeContainer = renderViewTree(container);

		this.tree = new treeimpl.Tree(this.treeContainer, {
			dataSource: new viewer.VariablesDataSource(this.debugService),
			renderer: this.instantiationService.createInstance(viewer.VariablesRenderer),
			accessibilityProvider: new viewer.VariablesAccessibilityProvider(),
			controller: new viewer.BaseDebugController(this.debugService, this.contextMenuService, new viewer.VariablesActionProvider(this.instantiationService))
		}, debugTreeOptions(nls.localize('variablesAriaTreeLabel', "Debug Variables")));

		const viewModel = this.debugService.getViewModel();

		this.tree.setInput(viewModel);

		const collapseAction = this.instantiationService.createInstance(viewlet.CollapseAction, this.tree, false, 'explorer-action collapse-explorer');
		this.toolBar.setActions(actionbarregistry.prepareActions([collapseAction]))();

		this.toDispose.push(viewModel.addListener2(debug.ViewModelEvents.FOCUSED_STACK_FRAME_UPDATED, () => this.onFocusedStackFrameUpdated()));
		this.toDispose.push(this.debugService.addListener2(debug.ServiceEvents.STATE_CHANGED, () => {
			collapseAction.enabled = this.debugService.getState() === debug.State.Running || this.debugService.getState() === debug.State.Stopped;
		}));
	}


private onFocusedStackFrameUpdated(): void {
		this.tree.refresh().then(() => {
			const stackFrame = this.debugService.getViewModel().getFocusedStackFrame();
			if (stackFrame) {
				return stackFrame.getScopes(this.debugService).then(scopes => {
					if (scopes.length > 0) {
						return this.tree.expand(scopes[0]);
					}
				});
			}
		}).done(null, errors.onUnexpectedError);
	}


public shutdown(): void {
		this.settings[VariablesView.MEMENTO] = (this.state === splitview.CollapsibleState.COLLAPSED);
		super.shutdown();
	}


public renderHeader(container: HTMLElement): void {
		super.renderHeader(container);
		const titleDiv = $('div.title').appendTo(container);
		$('span').text(nls.localize('watch', "Watch")).appendTo(titleDiv);
	}


public renderBody(container: HTMLElement): void {
		dom.addClass(container, 'debug-watch');
		this.treeContainer = renderViewTree(container);

		const actionProvider = new viewer.WatchExpressionsActionProvider(this.instantiationService);
		this.tree = new treeimpl.Tree(this.treeContainer, {
			dataSource: new viewer.WatchExpressionsDataSource(this.debugService),
			renderer: this.instantiationService.createInstance(viewer.WatchExpressionsRenderer, actionProvider, this.actionRunner),
			accessibilityProvider: new viewer.WatchExpressionsAccessibilityProvider(),
			controller: new viewer.WatchExpressionsController(this.debugService, this.contextMenuService, actionProvider)
		}, debugTreeOptions(nls.localize('watchAriaTreeLabel', "Debug Watch Expressions")));

		this.tree.setInput(this.debugService.getModel());

		const addWatchExpressionAction = this.instantiationService.createInstance(debugactions.AddWatchExpressionAction, debugactions.AddWatchExpressionAction.ID, debugactions.AddWatchExpressionAction.LABEL);
		const collapseAction = this.instantiationService.createInstance(viewlet.CollapseAction, this.tree, false, 'explorer-action collapse-explorer');
		const removeAllWatchExpressionsAction = this.instantiationService.createInstance(debugactions.RemoveAllWatchExpressionsAction, debugactions.RemoveAllWatchExpressionsAction.ID, debugactions.RemoveAllWatchExpressionsAction.LABEL);
		this.toolBar.setActions(actionbarregistry.prepareActions([addWatchExpressionAction, collapseAction, removeAllWatchExpressionsAction]))();

		this.toDispose.push(this.debugService.getModel().addListener2(debug.ModelEvents.WATCH_EXPRESSIONS_UPDATED, (we: model.Expression) => this.onWatchExpressionsUpdated(we)));
		this.toDispose.push(this.debugService.getViewModel().addListener2(debug.ViewModelEvents.SELECTED_EXPRESSION_UPDATED, (expression: debug.IExpression) => {
			if (!expression || !(expression instanceof model.Expression)) {
				return;
			}

			this.tree.refresh(expression, false).then(() => {
				this.tree.setHighlight(expression);

				const unbind = this.tree.addListener(events.EventType.HIGHLIGHT, (e: tree.IHighlightEvent) => {
					if (!e.highlight) {
						this.debugService.getViewModel().setSelectedExpression(null);
						this.tree.refresh(expression).done(null, errors.onUnexpectedError);
						unbind();
					}
				});
			}).done(null, errors.onUnexpectedError);
		}));
	}


private onWatchExpressionsUpdated(we: model.Expression): void {
		this.tree.refresh().done(() => {
			return we instanceof model.Expression ? this.tree.reveal(we): TPromise.as(true);
		}, errors.onUnexpectedError);
	}


public shutdown(): void {
		this.settings[WatchExpressionsView.MEMENTO] = (this.state === splitview.CollapsibleState.COLLAPSED);
		super.shutdown();
	}


public renderHeader(container: HTMLElement): void {
		super.renderHeader(container);
		const titleDiv = $('div.title').appendTo(container);
		$('span').text(nls.localize('callStack', "Call Stack")).appendTo(titleDiv);
	}


public renderBody(container: HTMLElement): void {
		dom.addClass(container, 'debug-call-stack');
		this.renderMessageBox(container);
		this.treeContainer = renderViewTree(container);

		this.tree = new treeimpl.Tree(this.treeContainer, {
			dataSource: new viewer.CallStackDataSource(),
			renderer: this.instantiationService.createInstance(viewer.CallStackRenderer),
			accessibilityProvider: this.instantiationService.createInstance(viewer.CallstackAccessibilityProvider)
		}, debugTreeOptions(nls.localize('callStackAriaLabel', "Debug Call Stack")));

		const debugModel = this.debugService.getModel();

		this.tree.setInput(debugModel);

		this.toDispose.push(this.tree.addListener2('selection', (e: tree.ISelectionEvent) => {
			if (!e.selection.length) {
				return;
			}
			const element = e.selection[0];
			if (!(element instanceof model.StackFrame)) {
				return;
			}

			const stackFrame = <debug.IStackFrame> element;
			this.debugService.setFocusedStackFrameAndEvaluate(stackFrame);

			const isMouse = (e.payload.origin === 'mouse');
			let preserveFocus = isMouse;

			const originalEvent:KeyboardEvent|MouseEvent = e && e.payload && e.payload.originalEvent;
			if (originalEvent && isMouse && originalEvent.detail === 2) {
				preserveFocus = false;
				originalEvent.preventDefault();  // focus moves to editor, we need to prevent default
			}

			const sideBySide = (originalEvent && (originalEvent.ctrlKey || originalEvent.metaKey));
			this.debugService.openOrRevealEditor(stackFrame.source, stackFrame.lineNumber, preserveFocus, sideBySide).done(null, errors.onUnexpectedError);
		}));

		this.toDispose.push(debugModel.addListener2(debug.ModelEvents.CALLSTACK_UPDATED, () => {
			this.tree.refresh().done(null, errors.onUnexpectedError);
		}));
		this.toDispose.push(this.debugService.getViewModel().addListener2(debug.ViewModelEvents.FOCUSED_STACK_FRAME_UPDATED, () => {
			const focussedThread = this.debugService.getModel().getThreads()[this.debugService.getViewModel().getFocusedThreadId()];
			const previouslyHidden = this.messageBox.hidden;
			if (focussedThread && focussedThread.stoppedReason && focussedThread.stoppedReason !== 'step') {
				this.messageBox.textContent = nls.localize('debugStopped', "Paused on {0}.", focussedThread.stoppedReason);
				focussedThread.stoppedReason === 'exception' ? this.messageBox.classList.add('exception') : this.messageBox.classList.remove('exception');

				this.messageBox.hidden = false;
			} else {
				this.messageBox.hidden = true;
			}

			if (previouslyHidden !== this.messageBox.hidden) {
				this.layoutBody(this.fullSize);
			}
		}));

		this.toDispose.push(this.debugService.getViewModel().addListener2(debug.ViewModelEvents.FOCUSED_STACK_FRAME_UPDATED,() => {
			const focused = this.debugService.getViewModel().getFocusedStackFrame();
			if (focused) {
				const threads = this.debugService.getModel().getThreads();
				for (let ref in threads) {
					if (threads[ref].callStack.some(sf => sf === focused)) {
						this.tree.expand(threads[ref]);
					}
				}
				this.tree.setFocus(focused);
			}
		}));
	}


public layoutBody(size: number): void {
		this.fullSize = size;
		const sizeWithRespectToMessageBox = this.messageBox && !this.messageBox.hidden ? size - 27 : size;
		super.layoutBody(sizeWithRespectToMessageBox);
	}


private renderMessageBox(container: HTMLElement): void {
		this.messageBox = document.createElement('div');
		dom.addClass(this.messageBox, 'debug-message-box');
		this.messageBox.hidden = true;
		container.appendChild(this.messageBox);
	}


public shutdown(): void {
		this.settings[CallStackView.MEMENTO] = (this.state === splitview.CollapsibleState.COLLAPSED);
		super.shutdown();
	}


public renderHeader(container: HTMLElement): void {
		super.renderHeader(container);
		const titleDiv = $('div.title').appendTo(container);
		$('span').text(nls.localize('breakpoints', "Breakpoints")).appendTo(titleDiv);
	}


public renderBody(container: HTMLElement): void {
		dom.addClass(container, 'debug-breakpoints');
		this.treeContainer = renderViewTree(container);
		const actionProvider = new viewer.BreakpointsActionProvider(this.instantiationService);

		this.tree = new treeimpl.Tree(this.treeContainer, {
			dataSource: new viewer.BreakpointsDataSource(),
			renderer: this.instantiationService.createInstance(viewer.BreakpointsRenderer, actionProvider, this.actionRunner),
			accessibilityProvider: this.instantiationService.createInstance(viewer.BreakpointsAccessibilityProvider),
			controller: new viewer.BreakpointsController(this.debugService, this.contextMenuService, actionProvider),
			sorter: {
				compare(tree: tree.ITree, element: any, otherElement: any): number {
					const first = <debug.IBreakpoint> element;
					const second = <debug.IBreakpoint> otherElement;
					if (first instanceof model.ExceptionBreakpoint) {
						return -1;
					}
					if (second instanceof model.ExceptionBreakpoint) {
						return 1;
					}
					if (first instanceof model.FunctionBreakpoint) {
						return -1;
					}
					if(second instanceof model.FunctionBreakpoint) {
						return 1;
					}

					if (first.source.uri.toString() !== second.source.uri.toString()) {
						return first.source.uri.toString().localeCompare(second.source.uri.toString());
					}

					return first.desiredLineNumber - second.desiredLineNumber;
				}
			}
		}, debugTreeOptions(nls.localize('breakpointsAriaTreeLabel', "Debug Breakpoints")));

		const debugModel = this.debugService.getModel();

		this.tree.setInput(debugModel);

		this.toDispose.push(this.tree.addListener2('selection', (e: tree.ISelectionEvent) => {
			if (!e.selection.length) {
				return;
			}
			const element = e.selection[0];
			if (!(element instanceof model.Breakpoint)) {
				return;
			}

			const breakpoint = <debug.IBreakpoint> element;
			if (!breakpoint.source.inMemory) {
				const isMouse = (e.payload.origin === 'mouse');
				let preserveFocus = isMouse;

				const originalEvent:KeyboardEvent|MouseEvent = e && e.payload && e.payload.originalEvent;
				if (originalEvent && isMouse && originalEvent.detail === 2) {
					preserveFocus = false;
					originalEvent.preventDefault();  // focus moves to editor, we need to prevent default
				}

				const sideBySide = (originalEvent && (originalEvent.ctrlKey || originalEvent.metaKey));
				this.debugService.openOrRevealEditor(breakpoint.source, breakpoint.lineNumber, preserveFocus, sideBySide).done(null, errors.onUnexpectedError);
			}
		}));
	}


compare(tree: tree.ITree, element: any, otherElement: any): number {
					const first = <debug.IBreakpoint> element;
					const second = <debug.IBreakpoint> otherElement;
					if (first instanceof model.ExceptionBreakpoint) {
						return -1;
					}
					if (second instanceof model.ExceptionBreakpoint) {
						return 1;
					}
					if (first instanceof model.FunctionBreakpoint) {
						return -1;
					}
					if(second instanceof model.FunctionBreakpoint) {
						return 1;
					}

					if (first.source.uri.toString() !== second.source.uri.toString()) {
						return first.source.uri.toString().localeCompare(second.source.uri.toString());
					}

					return first.desiredLineNumber - second.desiredLineNumber;
				}


public getActions(): actions.IAction[] {
		return [
			// this.instantiationService.createInstance(debugactions.AddFunctionBreakpointAction, debugactions.AddFunctionBreakpointAction.ID, debugactions.AddFunctionBreakpointAction.LABEL),
			this.instantiationService.createInstance(debugactions.ReapplyBreakpointsAction, debugactions.ReapplyBreakpointsAction.ID, debugactions.ReapplyBreakpointsAction.LABEL),
			this.instantiationService.createInstance(debugactions.ToggleBreakpointsActivatedAction, debugactions.ToggleBreakpointsActivatedAction.ID, debugactions.ToggleBreakpointsActivatedAction.LABEL),
			this.instantiationService.createInstance(debugactions.RemoveAllBreakpointsAction, debugactions.RemoveAllBreakpointsAction.ID, debugactions.RemoveAllBreakpointsAction.LABEL)
		];
	}


private onBreakpointsChange(): void {
		const model = this.debugService.getModel();
		this.expandedBodySize = BreakpointsView.getExpandedBodySize(
			model.getBreakpoints().length + model.getExceptionBreakpoints().length + model.getFunctionBreakpoints().length);

		if (this.tree) {
			this.tree.refresh();
		}
	}


private static getExpandedBodySize(length: number): number {
		return Math.min(BreakpointsView.MAX_VISIBLE_FILES, length) * 22;
	}


public shutdown(): void {
		this.settings[BreakpointsView.MEMENTO] = (this.state === splitview.CollapsibleState.COLLAPSED);
		super.shutdown();
	}


// viewlet
public create(parent: builder.Builder): TPromise<void> {
		super.create(parent);
		this.$el = parent.div().addClass('debug-viewlet');

		if (this.contextService.getWorkspace()) {
			const actionRunner = this.getActionRunner();
			this.views.push(this.instantiationService.createInstance(VariablesView, actionRunner, this.viewletSettings));
			this.views.push(this.instantiationService.createInstance(WatchExpressionsView, actionRunner, this.viewletSettings));
			this.views.push(this.instantiationService.createInstance(CallStackView, actionRunner, this.viewletSettings));
			this.views.push(this.instantiationService.createInstance(BreakpointsView, actionRunner, this.viewletSettings));

			this.splitView = new splitview.SplitView(this.$el.getHTMLElement());
			this.toDispose.push(this.splitView);
			this.views.forEach(v => this.splitView.addView(<any> v));
		} else {
			this.$el.append($([
				'
',
				'
', nls.localize('noWorkspace', "There is no currently opened folder."), '
',
				'
', nls.localize('pleaseRestartToDebug', "Open a folder in order to start debugging."), '
',
				'
'
			].join('')));
		}

		return TPromise.as(null);
	}


public setVisible(visible: boolean): TPromise<void> {
		return super.setVisible(visible).then(() => {
			return Promise.join(this.views.map((view) => view.setVisible(visible)));
		});
	}


public layout(dimension: builder.Dimension): void {
		if (this.splitView) {
			this.splitView.layout(dimension.height);
		}
	}


public focus(): void {
		super.focus();

		if (this.views.length > 0) {
			(<VariablesView>this.views[0]).focusBody();
		}
	}


public getActions(): actions.IAction[] {
		if (this.debugService.getState() === debug.State.Disabled) {
			return [];
		}

		if (!this.actions) {
			this.actions = [
				this.instantiationService.createInstance(debugactions.StartDebugAction, debugactions.StartDebugAction.ID, debugactions.StartDebugAction.LABEL),
				this.instantiationService.createInstance(debugactions.SelectConfigAction, debugactions.SelectConfigAction.ID, debugactions.SelectConfigAction.LABEL),
				this.instantiationService.createInstance(debugactions.ConfigureAction, debugactions.ConfigureAction.ID, debugactions.ConfigureAction.LABEL),
				this.instantiationService.createInstance(debugactions.ToggleReplAction, debugactions.ToggleReplAction.ID, debugactions.ToggleReplAction.LABEL)
			];

			this.actions.forEach(a => {
				this.toDispose.push(a);
			});
		}

		return this.actions;
	}


public getActionItem(action: actions.IAction): actionbar.IActionItem {
		if (action.id === debugactions.SelectConfigAction.ID) {
			return this.instantiationService.createInstance(dbgactionitems.SelectConfigActionItem, action);
		}

		return null;
	}


private onDebugServiceStateChange(): void {
		if (this.progressRunner) {
			this.progressRunner.done();
		}

		if (this.debugService.getState() === debug.State.Initializing) {
			this.progressRunner = this.progressService.show(true);
		} else {
			this.progressRunner = null;
		}
	}


public dispose(): void {
		this.toDispose = lifecycle.disposeAll(this.toDispose);

		super.dispose();
	}


public shutdown(): void {
		this.views.forEach(v => v.shutdown());
		super.shutdown();
	}


() => {
    const snap = snapshot.render(
        <Groups>
            <Button design='accent'>Make payment</Button>
            <>
                <Button design='outline'>Download statement</Button>
                <Button design='outline'>
                    <Trash size='xs' /> Delete statement
                </Button>
            </>
        </Groups>
    );

    expect(snap).toMatchSnapshot();
}


() => {
    const snap = snapshot.render(
        <Groups>
            {null}
            <Button design='accent'>Make payment</Button>
            {false}
            <Button design='outline'>
                <Trash size='xs' /> Delete statement
            </Button>
        </Groups>
    );

    expect(snap).toMatchSnapshot();
}


() => {
    const snap = snapshot.render(
        <Groups>
            <Button design='accent'>Make payment</Button>
            <Groups>
                <Button design='outline'>Download statement</Button>
                <Button design='outline'>
                    <Trash size='xs' /> Delete statement
                </Button>
            </Groups>
        </Groups>
    );

    expect(snap).toMatchSnapshot();
}


() => {
    const designs: GroupsProps['design'][] = ['horizontal', 'vertical'];

    designs.forEach((design) => {
        const snap = snapshot.render(
            <Groups design={design}>
                <Button design='accent'>Make payment</Button>
                <Button design='outline'>Download statement</Button>
                <Button design='outline'>
                    <Trash size='xs' /> Delete statement
                </Button>
            </Groups>
        );

        expect(snap).toMatchSnapshot(design);
    });
}


(design) => {
        const snap = snapshot.render(
            <Groups design={design}>
                <Button design='accent'>Make payment</Button>
                <Button design='outline'>Download statement</Button>
                <Button design='outline'>
                    <Trash size='xs' /> Delete statement
                </Button>
            </Groups>
        );

        expect(snap).toMatchSnapshot(design);
    }


() => {
    const sizes: GroupsProps['size'][] = ['xs', 's', 'm', 'l', 'xl'];

    sizes.forEach((size) => {
        const snap = snapshot.render(
            <Groups size={size}>
                <Button design='accent'>Make payment</Button>
                <Button design='outline'>Download statement</Button>
                <Button design='outline'>
                    <Trash size='xs' /> Delete statement
                </Button>
            </Groups>
        );

        expect(snap).toMatchSnapshot(size);
    });
}


(size) => {
        const snap = snapshot.render(
            <Groups size={size}>
                <Button design='accent'>Make payment</Button>
                <Button design='outline'>Download statement</Button>
                <Button design='outline'>
                    <Trash size='xs' /> Delete statement
                </Button>
            </Groups>
        );

        expect(snap).toMatchSnapshot(size);
    }


() => {
    const snap = snapshot.render(
        <Groups height='full'>
            <Button design='accent'>Make payment</Button>
            <Groups>
                <Button design='outline'>Download statement</Button>
                <Button design='outline'>
                    <Trash size='xs' /> Delete statement
                </Button>
            </Groups>
        </Groups>
    );

    expect(snap).toMatchSnapshot();
}


() => {
    const snap = snapshot.render(
        <Groups alignItems='stretch'>
            <Button design='accent'>Make payment</Button>
            <Button design='outline'>Download statement</Button>
            <Button design='outline'>
                <Trash size='xs' /> Delete statement
            </Button>
        </Groups>
    );

    expect(snap).toMatchSnapshot();
}


() => {
    const snap = snapshot.render(
        <Groups justifyContent='center'>
            <Button design='accent'>Make payment</Button>
            <Button design='outline'>Download statement</Button>
            <Button design='outline'>
                <Trash size='xs' /> Delete statement
            </Button>
        </Groups>
    );

    expect(snap).toMatchSnapshot();
}


() => {
    const props = { className: 'className', id: 'Counter', title: 'title', onClick: () => 'Counter' };
    const component = (
        <Groups {...props}>
            <Button design='accent'>Make payment</Button>
            <Button design='outline'>Download statement</Button>
            <Button design='outline'>
                <Trash size='xs' /> Delete statement
            </Button>
        </Groups>


() => 'Counter'


() => {
    const snap = snapshot.render(
        <Groups clean>
            <Button>{'A'}</Button>

            {true}
            {false}
            {null}
            {undefined}

            {
                <>
                    <Button>{'B'}</Button>
                    {true}
                    <Button>{'C'}</Button>
                    {null}
                </>
            }


() => {
	const { tableData } = useContext<Props | any>(SoccerContext);
	const classes = useStyles();
	const sortedTableDataByWins: Array<SoccerStanding> = tableData
		.slice()
		.sort((a: SoccerStanding, b: SoccerStanding) => b.won - a.won);

	return (

			<TableHead>
				<TableRow>
					<TableCell>Team</TableCell>
					<TableCell>Won</TableCell>
					<TableCell>Lost</TableCell>
					<TableCell>Draw</TableCell>
					<TableCell>Points</TableCell>
				</TableRow>
			</TableHead>
			<TableBody>
				{sortedTableDataByWins.map((soccerStanding) => {
					const { won, draw, team, points, position, lost }: SoccerStanding =
						soccerStanding;
					return (
						<TableRow key={position}>
							<TableCell className={classes.center}>
								{team?.crestUrl && (

								)}
								{team?.name && (
									{team.name}


(a: SoccerStanding, b: SoccerStanding) => b.won - a.won


(soccerStanding) => {
					const { won, draw, team, points, position, lost }: SoccerStanding =
						soccerStanding;
					return (
						<TableRow key={position}>
							<TableCell className={classes.center}>
								{team?.crestUrl && (
									<img
										className={classes.img}
										src={team.crestUrl}
										alt={team?.name}
									/>


type Props = {
	tableData: Array<SoccerStanding>;
};


() => {
  return (

        <GithubSource name="ButtonGroupDocs" />

Button groups

          Group a series of buttons together on a single line with the button
          group.

        <ButtonGroup aria-label="Basic example">
          <Button variant="secondary">Left</Button>
          <Button variant="secondary">Middle</Button>
          <Button variant="secondary">Right</Button>
        </ButtonGroup>

Button toolbar

        <ButtonToolbar aria-label="Toolbar with button groups">
          <ButtonGroup className="me-2" aria-label="First group">
            <Button>1</Button> <Button>2</Button> <Button>3</Button>{" "}
            <Button>4</Button>
          </ButtonGroup>
          <ButtonGroup className="me-2" aria-label="Second group">
            <Button>5</Button> <Button>6</Button> <Button>7</Button>
          </ButtonGroup>
          <ButtonGroup aria-label="Third group">
            <Button>8</Button>
          </ButtonGroup>
        </ButtonToolbar>

          Feel free to mix input groups with button groups in your toolbars.
          Similar to the example above, you’ll likely need some utilities though
          to space things properly.

        <ButtonToolbar className="mb-3" aria-label="Toolbar with Button groups">
          <ButtonGroup className="me-2" aria-label="First group">
            <Button variant="secondary">1</Button>{" "}
            <Button variant="secondary">2</Button>{" "}
            <Button variant="secondary">3</Button>{" "}
            <Button variant="secondary">4</Button>
          </ButtonGroup>
          <InputGroup>
            <InputGroup.Text id="btnGroupAddon">@</InputGroup.Text>
            <FormControl
              type="text"
              placeholder="Input group example"
              aria-label="Input group example"
              aria-describedby="btnGroupAddon"
            />
          </InputGroup>
        </ButtonToolbar>

        <ButtonToolbar
          className="justify-content-between"
          aria-label="Toolbar with Button groups"
        >
          <ButtonGroup aria-label="First group">
            <Button variant="secondary">1</Button>{" "}
            <Button variant="secondary">2</Button>{" "}
            <Button variant="secondary">3</Button>{" "}
            <Button variant="secondary">4</Button>
          </ButtonGroup>
          <InputGroup>
            <InputGroup.Text id="btnGroupAddon2">@</InputGroup.Text>
            <FormControl
              type="text"
              placeholder="Input group example"
              aria-label="Input group example"
              aria-describedby="btnGroupAddon2"
            />
          </InputGroup>
        </ButtonToolbar>

Sizing

        <ButtonGroup size="lg" className="mb-2">
          <Button>Left</Button>
          <Button>Middle</Button>
          <Button>Right</Button>
        </ButtonGroup>

        <ButtonGroup className="mb-2">
          <Button>Left</Button>
          <Button>Middle</Button>
          <Button>Right</Button>
        </ButtonGroup>

        <ButtonGroup size="sm">
          <Button>Left</Button>
          <Button>Middle</Button>
          <Button>Right</Button>
        </ButtonGroup>

Nesting

        <ButtonGroup>
          <Button>1</Button>
          <Button>2</Button>

          <DropdownButton
            as={ButtonGroup}
            title="Dropdown"
            id="bg-nested-dropdown"
          >
            <Dropdown.Item eventKey="1">Dropdown link</Dropdown.Item>
            <Dropdown.Item eventKey="2">Dropdown link</Dropdown.Item>
          </DropdownButton>
        </ButtonGroup>

Vertical variation

        <ButtonGroup vertical>
          <Button>Button</Button>
          <Button>Button</Button>

          <DropdownButton
            as={ButtonGroup}
            title="Dropdown"
            id="bg-vertical-dropdown-1"
          >
            <Dropdown.Item eventKey="1">Dropdown link</Dropdown.Item>
            <Dropdown.Item eventKey="2">Dropdown link</Dropdown.Item>
          </DropdownButton>

          <Button>Button</Button>
          <Button>Button</Button>

          <DropdownButton
            as={ButtonGroup}
            title="Dropdown"
            id="bg-vertical-dropdown-2"
          >
            <Dropdown.Item eventKey="1">Dropdown link</Dropdown.Item>
            <Dropdown.Item eventKey="2">Dropdown link</Dropdown.Item>
          </DropdownButton>

          <DropdownButton
            as={ButtonGroup}
            title="Dropdown"
            id="bg-vertical-dropdown-3"
          >
            <Dropdown.Item eventKey="1">Dropdown link</Dropdown.Item>
            <Dropdown.Item eventKey="2">Dropdown link</Dropdown.Item>
          </DropdownButton>
        </ButtonGroup>

      <ButtonGroupApi />


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

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [RecentActivityComponent],
      imports: [StoreModule.forRoot(reducers)],
      schemas: [NO_ERRORS_SCHEMA],
    })
      .compileComponents();
  }));

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

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


() => {
    TestBed.configureTestingModule({
      declarations: [RecentActivityComponent],
      imports: [StoreModule.forRoot(reducers)],
      schemas: [NO_ERRORS_SCHEMA],
    })
      .compileComponents();
  }


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


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

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

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

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


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


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


/**
 * Enum to used for Meridians to determine the
 * direction they should face and what type
 * of meridian they are
 */
export enum Orientation {
  HORIZONTAL,
  VERTICAL,
}


async () => {
  await server.start();
}


async () => {
  await server.shutdown();
}


() => {
  let manager: KernelManager;
  let kernel: Kernel.IModel;

  beforeAll(async () => {
    jest.setTimeout(20000);
    kernel = await KernelAPI.startNew();
  });

  beforeEach(() => {
    manager = new KernelManager({ standby: 'never' });
    return manager.ready;
  });

  afterEach(() => {
    manager.dispose();
  });

  afterAll(async () => {
    const models = await KernelAPI.listRunning();
    await Promise.all(models.map(m => KernelAPI.shutdownKernel(m.id)));
  });

  describe('KernelManager', () => {
    describe('#constructor()', () => {
      it('should take the options as an argument', async () => {
        manager.dispose();
        manager = new KernelManager({
          serverSettings: makeSettings(),
          standby: 'never'
        });
        expect(manager instanceof KernelManager).toBe(true);
        await manager.ready;
      });
    });

    describe('#serverSettings', () => {
      it('should get the server settings', async () => {
        manager.dispose();
        const serverSettings = makeSettings();
        const standby = 'never';
        const token = serverSettings.token;
        manager = new KernelManager({ serverSettings, standby });
        await manager.ready;
        expect(manager.serverSettings.token).toBe(token);
      });
    });

    describe('#running()', () => {
      it('should get the running sessions', async () => {
        await manager.refreshRunning();
        expect(toArray(manager.running()).length).toBeGreaterThan(0);
      });
    });
    describe('#runningChanged', () => {
      it('should be emitted in refreshRunning when the running kernels changed', async () => {
        let called = false;
        manager.runningChanged.connect((sender, args) => {
          expect(sender).toBe(manager);
          expect(toArray(args).length).toBeGreaterThan(0);
          called = true;
        });
        await KernelAPI.startNew();
        await manager.refreshRunning();
        expect(called).toBe(true);
      });

      it('should be emitted when a kernel is shut down', async () => {
        const kernel = await manager.startNew();
        await kernel.info;
        let called = false;
        manager.runningChanged.connect(() => {
          called = true;
        });
        await manager.shutdown(kernel.id);
        await manager.refreshRunning();
        expect(called).toBe(true);
      });
    });

    describe('#isReady', () => {
      it('should test whether the manager is ready', async () => {
        manager.dispose();
        manager = new KernelManager({ standby: 'never' });
        expect(manager.isReady).toBe(false);
        await manager.ready;
        expect(manager.isReady).toBe(true);
      });
    });

    describe('#ready', () => {
      it('should resolve when the manager is ready', () => {
        return manager.ready;
      });
    });

    describe('#refreshRunning()', () => {
      it('should update the running kernels', async () => {
        await manager.refreshRunning();
        expect(toArray(manager.running()).length).toBeGreaterThan(0);
      });

      it('should update the running kernels when one is shut down', async () => {
        const old = toArray(manager.running()).length;
        await KernelAPI.startNew();
        await manager.refreshRunning();
        expect(toArray(manager.running()).length).toBeGreaterThan(old);
      });
    });

    describe('#startNew()', () => {
      it('should start a new kernel', () => {
        return manager.startNew();
      });

      it('should emit a runningChanged signal', async () => {
        let called = false;
        manager.runningChanged.connect(() => {
          called = true;
        });
        const kernel = await manager.startNew();
        await kernel.info;
        expect(called).toBe(true);
      });
    });

    describe('#findById()', () => {
      it('should find an existing kernel by id', async () => {
        const id = kernel.id;
        const model = await manager.findById(id);
        expect(model!.id).toBe(id);
      });
    });

    describe('#connectTo()', () => {
      it('should connect to an existing kernel', () => {
        const id = kernel.id;
        const newConnection = manager.connectTo({ model: kernel });
        expect(newConnection.model.id).toBe(id);
      });
    });

    describe('shutdown()', () => {
      it('should shut down a kernel by id', async () => {
        const kernel = await manager.startNew();
        await kernel.info;
        await manager.shutdown(kernel.id);
        expect(kernel.isDisposed).toBe(true);
      });

      it('should emit a runningChanged signal', async () => {
        const kernel = await manager.startNew();
        const emission = testEmission(manager.runningChanged, {
          test: () => {
            expect(kernel.isDisposed).toBe(false);
          }
        });
        await kernel.info;
        await manager.shutdown(kernel.id);
        await emission;
      });

      it('should dispose of all relevant kernel connections', async () => {
        const kernel0 = await manager.startNew();
        const kernel1 = manager.connectTo({ model: kernel0.model });
        await kernel0.info;
        await kernel1.info;
        await manager.refreshRunning();
        await kernel0.shutdown();
        expect(kernel0.status).toBe('dead');
        expect(kernel0.isDisposed).toBe(true);

        // Wait for the round trip to the server to update the connections.
        await sleep(100);
        expect(kernel1.status).toBe('dead');
        expect(kernel1.isDisposed).toBe(true);
      });
    });
  });
}


async () => {
    jest.setTimeout(20000);
    kernel = await KernelAPI.startNew();
  }


() => {
    manager = new KernelManager({ standby: 'never' });
    return manager.ready;
  }


() => {
    manager.dispose();
  }


async () => {
    const models = await KernelAPI.listRunning();
    await Promise.all(models.map(m => KernelAPI.shutdownKernel(m.id)));
  }


m => KernelAPI.shutdownKernel(m.id)


() => {
    describe('#constructor()', () => {
      it('should take the options as an argument', async () => {
        manager.dispose();
        manager = new KernelManager({
          serverSettings: makeSettings(),
          standby: 'never'
        });
        expect(manager instanceof KernelManager).toBe(true);
        await manager.ready;
      });
    });

    describe('#serverSettings', () => {
      it('should get the server settings', async () => {
        manager.dispose();
        const serverSettings = makeSettings();
        const standby = 'never';
        const token = serverSettings.token;
        manager = new KernelManager({ serverSettings, standby });
        await manager.ready;
        expect(manager.serverSettings.token).toBe(token);
      });
    });

    describe('#running()', () => {
      it('should get the running sessions', async () => {
        await manager.refreshRunning();
        expect(toArray(manager.running()).length).toBeGreaterThan(0);
      });
    });
    describe('#runningChanged', () => {
      it('should be emitted in refreshRunning when the running kernels changed', async () => {
        let called = false;
        manager.runningChanged.connect((sender, args) => {
          expect(sender).toBe(manager);
          expect(toArray(args).length).toBeGreaterThan(0);
          called = true;
        });
        await KernelAPI.startNew();
        await manager.refreshRunning();
        expect(called).toBe(true);
      });

      it('should be emitted when a kernel is shut down', async () => {
        const kernel = await manager.startNew();
        await kernel.info;
        let called = false;
        manager.runningChanged.connect(() => {
          called = true;
        });
        await manager.shutdown(kernel.id);
        await manager.refreshRunning();
        expect(called).toBe(true);
      });
    });

    describe('#isReady', () => {
      it('should test whether the manager is ready', async () => {
        manager.dispose();
        manager = new KernelManager({ standby: 'never' });
        expect(manager.isReady).toBe(false);
        await manager.ready;
        expect(manager.isReady).toBe(true);
      });
    });

    describe('#ready', () => {
      it('should resolve when the manager is ready', () => {
        return manager.ready;
      });
    });

    describe('#refreshRunning()', () => {
      it('should update the running kernels', async () => {
        await manager.refreshRunning();
        expect(toArray(manager.running()).length).toBeGreaterThan(0);
      });

      it('should update the running kernels when one is shut down', async () => {
        const old = toArray(manager.running()).length;
        await KernelAPI.startNew();
        await manager.refreshRunning();
        expect(toArray(manager.running()).length).toBeGreaterThan(old);
      });
    });

    describe('#startNew()', () => {
      it('should start a new kernel', () => {
        return manager.startNew();
      });

      it('should emit a runningChanged signal', async () => {
        let called = false;
        manager.runningChanged.connect(() => {
          called = true;
        });
        const kernel = await manager.startNew();
        await kernel.info;
        expect(called).toBe(true);
      });
    });

    describe('#findById()', () => {
      it('should find an existing kernel by id', async () => {
        const id = kernel.id;
        const model = await manager.findById(id);
        expect(model!.id).toBe(id);
      });
    });

    describe('#connectTo()', () => {
      it('should connect to an existing kernel', () => {
        const id = kernel.id;
        const newConnection = manager.connectTo({ model: kernel });
        expect(newConnection.model.id).toBe(id);
      });
    });

    describe('shutdown()', () => {
      it('should shut down a kernel by id', async () => {
        const kernel = await manager.startNew();
        await kernel.info;
        await manager.shutdown(kernel.id);
        expect(kernel.isDisposed).toBe(true);
      });

      it('should emit a runningChanged signal', async () => {
        const kernel = await manager.startNew();
        const emission = testEmission(manager.runningChanged, {
          test: () => {
            expect(kernel.isDisposed).toBe(false);
          }
        });
        await kernel.info;
        await manager.shutdown(kernel.id);
        await emission;
      });

      it('should dispose of all relevant kernel connections', async () => {
        const kernel0 = await manager.startNew();
        const kernel1 = manager.connectTo({ model: kernel0.model });
        await kernel0.info;
        await kernel1.info;
        await manager.refreshRunning();
        await kernel0.shutdown();
        expect(kernel0.status).toBe('dead');
        expect(kernel0.isDisposed).toBe(true);

        // Wait for the round trip to the server to update the connections.
        await sleep(100);
        expect(kernel1.status).toBe('dead');
        expect(kernel1.isDisposed).toBe(true);
      });
    });
  }


() => {
      it('should take the options as an argument', async () => {
        manager.dispose();
        manager = new KernelManager({
          serverSettings: makeSettings(),
          standby: 'never'
        });
        expect(manager instanceof KernelManager).toBe(true);
        await manager.ready;
      });
    }


async () => {
        manager.dispose();
        manager = new KernelManager({
          serverSettings: makeSettings(),
          standby: 'never'
        });
        expect(manager instanceof KernelManager).toBe(true);
        await manager.ready;
      }


() => {
      it('should get the server settings', async () => {
        manager.dispose();
        const serverSettings = makeSettings();
        const standby = 'never';
        const token = serverSettings.token;
        manager = new KernelManager({ serverSettings, standby });
        await manager.ready;
        expect(manager.serverSettings.token).toBe(token);
      });
    }


async () => {
        manager.dispose();
        const serverSettings = makeSettings();
        const standby = 'never';
        const token = serverSettings.token;
        manager = new KernelManager({ serverSettings, standby });
        await manager.ready;
        expect(manager.serverSettings.token).toBe(token);
      }


() => {
      it('should get the running sessions', async () => {
        await manager.refreshRunning();
        expect(toArray(manager.running()).length).toBeGreaterThan(0);
      });
    }


async () => {
        await manager.refreshRunning();
        expect(toArray(manager.running()).length).toBeGreaterThan(0);
      }


() => {
      it('should be emitted in refreshRunning when the running kernels changed', async () => {
        let called = false;
        manager.runningChanged.connect((sender, args) => {
          expect(sender).toBe(manager);
          expect(toArray(args).length).toBeGreaterThan(0);
          called = true;
        });
        await KernelAPI.startNew();
        await manager.refreshRunning();
        expect(called).toBe(true);
      });

      it('should be emitted when a kernel is shut down', async () => {
        const kernel = await manager.startNew();
        await kernel.info;
        let called = false;
        manager.runningChanged.connect(() => {
          called = true;
        });
        await manager.shutdown(kernel.id);
        await manager.refreshRunning();
        expect(called).toBe(true);
      });
    }


async () => {
        let called = false;
        manager.runningChanged.connect((sender, args) => {
          expect(sender).toBe(manager);
          expect(toArray(args).length).toBeGreaterThan(0);
          called = true;
        });
        await KernelAPI.startNew();
        await manager.refreshRunning();
        expect(called).toBe(true);
      }


(sender, args) => {
          expect(sender).toBe(manager);
          expect(toArray(args).length).toBeGreaterThan(0);
          called = true;
        }


async () => {
        const kernel = await manager.startNew();
        await kernel.info;
        let called = false;
        manager.runningChanged.connect(() => {
          called = true;
        });
        await manager.shutdown(kernel.id);
        await manager.refreshRunning();
        expect(called).toBe(true);
      }


() => {
          called = true;
        }


() => {
      it('should test whether the manager is ready', async () => {
        manager.dispose();
        manager = new KernelManager({ standby: 'never' });
        expect(manager.isReady).toBe(false);
        await manager.ready;
        expect(manager.isReady).toBe(true);
      });
    }


async () => {
        manager.dispose();
        manager = new KernelManager({ standby: 'never' });
        expect(manager.isReady).toBe(false);
        await manager.ready;
        expect(manager.isReady).toBe(true);
      }


() => {
      it('should resolve when the manager is ready', () => {
        return manager.ready;
      });
    }


() => {
        return manager.ready;
      }


() => {
      it('should update the running kernels', async () => {
        await manager.refreshRunning();
        expect(toArray(manager.running()).length).toBeGreaterThan(0);
      });

      it('should update the running kernels when one is shut down', async () => {
        const old = toArray(manager.running()).length;
        await KernelAPI.startNew();
        await manager.refreshRunning();
        expect(toArray(manager.running()).length).toBeGreaterThan(old);
      });
    }


async () => {
        const old = toArray(manager.running()).length;
        await KernelAPI.startNew();
        await manager.refreshRunning();
        expect(toArray(manager.running()).length).toBeGreaterThan(old);
      }


() => {
      it('should start a new kernel', () => {
        return manager.startNew();
      });

      it('should emit a runningChanged signal', async () => {
        let called = false;
        manager.runningChanged.connect(() => {
          called = true;
        });
        const kernel = await manager.startNew();
        await kernel.info;
        expect(called).toBe(true);
      });
    }


() => {
        return manager.startNew();
      }


async () => {
        let called = false;
        manager.runningChanged.connect(() => {
          called = true;
        });
        const kernel = await manager.startNew();
        await kernel.info;
        expect(called).toBe(true);
      }


() => {
      it('should find an existing kernel by id', async () => {
        const id = kernel.id;
        const model = await manager.findById(id);
        expect(model!.id).toBe(id);
      });
    }


async () => {
        const id = kernel.id;
        const model = await manager.findById(id);
        expect(model!.id).toBe(id);
      }


() => {
      it('should connect to an existing kernel', () => {
        const id = kernel.id;
        const newConnection = manager.connectTo({ model: kernel });
        expect(newConnection.model.id).toBe(id);
      });
    }


() => {
        const id = kernel.id;
        const newConnection = manager.connectTo({ model: kernel });
        expect(newConnection.model.id).toBe(id);
      }


() => {
      it('should shut down a kernel by id', async () => {
        const kernel = await manager.startNew();
        await kernel.info;
        await manager.shutdown(kernel.id);
        expect(kernel.isDisposed).toBe(true);
      });

      it('should emit a runningChanged signal', async () => {
        const kernel = await manager.startNew();
        const emission = testEmission(manager.runningChanged, {
          test: () => {
            expect(kernel.isDisposed).toBe(false);
          }
        });
        await kernel.info;
        await manager.shutdown(kernel.id);
        await emission;
      });

      it('should dispose of all relevant kernel connections', async () => {
        const kernel0 = await manager.startNew();
        const kernel1 = manager.connectTo({ model: kernel0.model });
        await kernel0.info;
        await kernel1.info;
        await manager.refreshRunning();
        await kernel0.shutdown();
        expect(kernel0.status).toBe('dead');
        expect(kernel0.isDisposed).toBe(true);

        // Wait for the round trip to the server to update the connections.
        await sleep(100);
        expect(kernel1.status).toBe('dead');
        expect(kernel1.isDisposed).toBe(true);
      });
    }


async () => {
        const kernel = await manager.startNew();
        await kernel.info;
        await manager.shutdown(kernel.id);
        expect(kernel.isDisposed).toBe(true);
      }


async () => {
        const kernel = await manager.startNew();
        const emission = testEmission(manager.runningChanged, {
          test: () => {
            expect(kernel.isDisposed).toBe(false);
          }
        });
        await kernel.info;
        await manager.shutdown(kernel.id);
        await emission;
      }


() => {
            expect(kernel.isDisposed).toBe(false);
          }


async () => {
        const kernel0 = await manager.startNew();
        const kernel1 = manager.connectTo({ model: kernel0.model });
        await kernel0.info;
        await kernel1.info;
        await manager.refreshRunning();
        await kernel0.shutdown();
        expect(kernel0.status).toBe('dead');
        expect(kernel0.isDisposed).toBe(true);

        // Wait for the round trip to the server to update the connections.
        await sleep(100);
        expect(kernel1.status).toBe('dead');
        expect(kernel1.isDisposed).toBe(true);
      }


(done: any) => {
  rollup.rollup(config)
    .then((bundle: any) => {
      const result = bundle.generate({
        format: 'iife'
      });
      const path = join(Config.TMP_DIR, 'bundle.js');
      writeFile(path, result.code, (error: any) => {
        if (error) {
          console.error(error);
          process.exit(0);
        }
        done();
      });
    })
    .catch((error: any) => {
      console.error(error);
      process.exit(0);
    });
}


(bundle: any) => {
      const result = bundle.generate({
        format: 'iife'
      });
      const path = join(Config.TMP_DIR, 'bundle.js');
      writeFile(path, result.code, (error: any) => {
        if (error) {
          console.error(error);
          process.exit(0);
        }
        done();
      });
    }


(error: any) => {
        if (error) {
          console.error(error);
          process.exit(0);
        }
        done();
      }


(error: any) => {
      console.error(error);
      process.exit(0);
    }


export default function Modal (props: ModalProps): React.ReactElement {
  const show = props.show
  const title = props.title || 'Oops!...'

  useEffect(() => {
    if (show) {
      document.body.style.overflow = 'hidden'
    } else {
      document.body.style.overflow = 'unset'
    }
  }, [show])

  return (
  <div className={`modal-background ${show ? 'modal-visible' : ''}`}


() => {
    if (show) {
      document.body.style.overflow = 'hidden'
    } else {
      document.body.style.overflow = 'unset'
    }
  }


(buttonDescription, index) => {
            return (
              <button key={index} className={`button-${buttonDescription.buttonType}`}


export interface ButtonDescription {
  buttonType: 'danger' | 'primary' | 'success' | 'disabled'
  buttonText: string
  onClick: MouseEventHandler
}


interface ModalProps {
  buttons: ButtonDescription[]
  show: boolean
  title?: string
  children: React.ReactNode
}


(
    fastify: FastifyInstance,
    opts: ServerOptions,
    next: CallableFunction,
  ) => {
    const {
      app,
      redis,
      maxProfiles,
      twitch,
    } = opts;
    const { urlPrefix } = app;
    fastify.register(db, {
      ...opts.db,
      maxProfiles,
    });

    if (opts.cloudflare.enable) {
      fastify.register(cloudflare, opts.cloudflare);
    }

    fastify.register(sas, opts.sas);
    fastify.register(playerConfig, { maxProfiles });
    fastify.register(twitchConfigValidator, twitch);
    fastify.register(viewer, { ttl: redis.ttl });
    fastify.register(statusRoutes);
    fastify.register(configRoutes.get, { urlPrefix });
    fastify.register(configRoutes.post, { urlPrefix });
    fastify.register(viewerRoutes.get, { urlPrefix });
    next();
  }


interface ServerOptions {
  app: {
    urlPrefix: string;
  };
  db: {
    uri: string;
  };
  redis: {
    ttl: number;
  };
  twitch: TwitchPluginOptions;
  sas: SasOptions;
  maxProfiles: number;
  cloudflare: {
    enable: boolean;
    token: string;
    zoneId: string;
    productionDomain: string;
    viewerRoute: string;
  };
}


_type => SongCreateWithoutSongInMovieInput


_type => SongCreateOrConnectWithoutSongInMovieInput


_type => SongWhereUniqueInput


@TypeGraphQL.InputType({
  isAbstract: true
})
export class SongCreateNestedOneWithoutSongInMovieInput {
  @TypeGraphQL.Field(_type => SongCreateWithoutSongInMovieInput, {
    nullable: true
  })
  create?: SongCreateWithoutSongInMovieInput | undefined;

  @TypeGraphQL.Field(_type => SongCreateOrConnectWithoutSongInMovieInput, {
    nullable: true
  })
  connectOrCreate?: SongCreateOrConnectWithoutSongInMovieInput | undefined;

  @TypeGraphQL.Field(_type => SongWhereUniqueInput, {
    nullable: true
  })
  connect?: SongWhereUniqueInput | undefined;
}


export default class UnlockableRowInfoProvider extends InfoProvider {
    private readonly getter;
    private contentHandler?;
    constructor(getter: () => number);
    setContent(handler?: ContentHandler | InfoProvider | Translation): this;
    get(context: InfoProviderContext): ArrayOr<TranslationGenerator<import("../../../language/dictionary/UiTranslation").default, []> | InfoProvider>;
    getClass(): string[];
    initComponent(context: InfoProviderContext): {
        component: import("../../../ui/component/Component").default<HTMLElement>;
        fullInit(): void;
    };
    protected onRefresh(): void;
}


declare type ContentHandler = (context: InfoProviderContext, provider: UnlockableRowInfoProvider) => ArrayOr<TranslationGenerator | InfoProvider>;


setContent(handler?: ContentHandler | InfoProvider | Translation): this;


get(context: InfoProviderContext): ArrayOr<TranslationGenerator<import("../../../language/dictionary/UiTranslation").default, []> | InfoProvider>;


getClass(): string[];


initComponent(context: InfoProviderContext): {
        component: import("../../../ui/component/Component").default<HTMLElement>;
        fullInit(): void;
    };


protected onRefresh(): void;


@Component({
  selector: 'sbb-icon-sa-sn',
  // tslint:disable:max-line-length
  template: `
    <svg
      focusable="false"
      [attr.class]="'sbb-svg-icon ' + svgClass"
      xmlns="http://www.w3.org/2000/svg"
      viewBox="0 0 26 16"
    >
      <path
        fill-rule="evenodd"
        d="M3.307 14.267c-.35-.529-.64-1-.87-1.407-.232-.411-.528-1.02-.894-1.833C1.183 10.215 1 9.207 1 8.004c0-1.167.196-2.207.59-3.123.393-.913.967-1.954 1.717-3.12h1.274c-.412.72-.727 1.304-.947 1.75a25.028 25.028 0 00-.78 1.831c-.303.776-.455 1.669-.455 2.68 0 1.035.164 1.97.499 2.805.333.836.59 1.442.771 1.814.183.373.486.913.91 1.624H3.307v.002zm6.782-4.478L8.18 7.64l-.855-1.028a3.595 3.595 0 01-.693-2.162c0-1.095.404-2.127 1.21-3.1l.2-.216L8.14 1l4.84 5.462-.005-2.067 3.742-3.393.887.953-3.324 3.015.002 1.59 4.101-3.758.915.979-4.127 3.753 1.616.104L20.1 4.647l.908.937-3.725 3.393-2.2-.143 4.145 4.674L17.52 15l-4.284-4.835-.093-.343-.106.325L7.724 15l-1.527-1.652 3.892-3.56zM8.214 3.474c-.061.145-.104.286-.128.425a2.487 2.487 0 00-.035.406c0 .664.246 1.278.741 1.844l.51.576 1.834 2.095 1.086-.972-3.856-4.38-.134-.355-.018.361zM23.04 1.76c.69 1.07 1.248 2.081 1.673 3.032.423.95.636 2.02.636 3.212 0 .59-.06 1.173-.18 1.75a8.222 8.222 0 01-.52 1.632 15.05 15.05 0 01-.627 1.291c-.193.35-.522.878-.982 1.59H21.77c.425-.735.74-1.315.945-1.741.206-.426.463-1.035.772-1.823.309-.787.466-1.694.466-2.716 0-1.023-.162-1.947-.483-2.77-.321-.824-.581-1.432-.78-1.823-.202-.39-.507-.936-.92-1.634h1.272z"
      />
    </svg>
  `,
  // tslint:enable:max-line-length
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class IconSaSnComponent extends IconBase {
  constructor() {
    super({ width: '26px', height: '16px', ratio: 1.625 });
  }
}


@NgModule({
  imports: [CommonModule],
  declarations: [IconSaSnComponent],
  exports: [IconSaSnComponent]
})
export class IconSaSnModule {}


async function fetchMyCAPTCHA() {
    const res = await fetch('/api/captcha', {
      method: 'POST',
      credentials: 'include',
      headers: {
        Accept: 'application/json',
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ page: 'login' })
    });

    if (res.status === 200) {
      const resp = await res.json();
      setCaptcha({ svg: resp.svg, encryptedExpr: resp.encryptedExpr, hasCaptcha: resp.hasCaptcha });
    } else {
      const msg = await res.text();
      setErrorMsg(msg);
    }
  }


async function onClick(e, endpoint) {
    e.preventDefault();

    const res = await fetch(endpoint, {
      method: 'POST',
      credentials: 'include',
      headers: {
        Accept: 'application/json',
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ ...formData, _csrf: props._csrf, encryptedExpr: captcha.encryptedExpr }),
    });

    if (res.status === 200) {
      localStorage.setItem('login_timestamp', new Date().toString());
      const token = await res.json();
      if (token.user.role === 'pending') {
        router.push('/pending');
      } else {
        router.push('/dashboard');
      }
      window.location.reload();
    } else {
      const msg = await res.text();
      setErrorMsg(msg);
      fetchMyCAPTCHA();
    }
  }


(props: any): any => {
  const [errorMsg, setErrorMsg] = useState('');
  const [formData, setFormData] = useState({
    email: '',
    password: '',
    rememberMe: false,
    captcha: ''
  });

  async function fetchMyCAPTCHA() {
    const res = await fetch('/api/captcha', {
      method: 'POST',
      credentials: 'include',
      headers: {
        Accept: 'application/json',
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ page: 'login' })
    });

    if (res.status === 200) {
      const resp = await res.json();
      setCaptcha({ svg: resp.svg, encryptedExpr: resp.encryptedExpr, hasCaptcha: resp.hasCaptcha });
    } else {
      const msg = await res.text();
      setErrorMsg(msg);
    }
  }

  const [captcha, setCaptcha] = useState({ svg: '', encryptedExpr: '', hasCaptcha: false });
  const router = useRouter();

  async function onClick(e, endpoint) {
    e.preventDefault();

    const res = await fetch(endpoint, {
      method: 'POST',
      credentials: 'include',
      headers: {
        Accept: 'application/json',
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ ...formData, _csrf: props._csrf, encryptedExpr: captcha.encryptedExpr }),
    });

    if (res.status === 200) {
      localStorage.setItem('login_timestamp', new Date().toString());
      const token = await res.json();
      if (token.user.role === 'pending') {
        router.push('/pending');
      } else {
        router.push('/dashboard');
      }
      window.location.reload();
    } else {
      const msg = await res.text();
      setErrorMsg(msg);
      fetchMyCAPTCHA();
    }
  }

  useEffect(() => {
    fetchMyCAPTCHA();
  }, []);

  return (
    <ThemeProvider theme={theme}>
      <Container component="main" maxWidth="sm">

          <Typography
            className={props.classes.input}
            component="h1"
            variant="h5"
          >
            Sign In
          </Typography>
          {errorMsg &&
{errorMsg}
}
          <form method="post" className="login-form">
            <input name="stype" type="hidden" defaultValue="login" />
            <TextField
              variant="outlined"
              margin="normal"
              required
              fullWidth
              id="email"
              color="primary"
              label="Email Address"
              name="email"
              autoComplete="email"
              autoFocus
              onChange={(e)


() => {
    fetchMyCAPTCHA();
  }


(props) => {
  const classes = useStyles();
  return <Login {...props} classes={classes} />;
}


() => ({ cart: {}, displayCart: [] } as State)


(ci : Product) => {
          if (ci.id === product.id) {
            isAdded = true
            return { id: ci.id, qty: ci.qty + 1 }
          }
          return { id: ci.id, qty: ci.qty }
        }


ci => ci.id !== id


ci => {
        const requiredProduct = productData.filter(p => p.id === ci.id)
        // if (requiredProduct[0].stock >= ci.qty)
        return {
          id: ci.id,
          name: requiredProduct[0].name,
          price: requiredProduct[0].price,
          qty: ci.qty,
          color: requiredProduct[0].color,
          currency: requiredProduct[0].currency,
          instock: requiredProduct[0].stock >= ci.qty,
        }
      }


p => p.id === ci.id


// interface Cart {
//     cid: string
//     products: Array<{id: number, qty: number}>
// }
interface State{
  cart: Cart | {}
  displayCart: DisplayCart [] | []
}


loadCartInstance() {
      const cs = localStorage.getItem('cart')
      if (!cs)
        this.cart = {}
      else
        this.cart = JSON.parse(cs)
    }


addToCart(product: Product) {
      const cs = localStorage.getItem('cart')
      let isAdded = false

      if (!cs) {
        this.cart = {
          cid: uuid4(),
          products: [product],
        }
      }
      else {
        const cartLocalStorage = JSON.parse(cs)
        cartLocalStorage.products = cartLocalStorage.products.map((ci : Product) => {
          if (ci.id === product.id) {
            isAdded = true
            return { id: ci.id, qty: ci.qty + 1 }
          }
          return { id: ci.id, qty: ci.qty }
        })
        if (!isAdded)
          cartLocalStorage.products.push({ id: product.id, qty: product.qty })

        this.cart = cartLocalStorage
      }
      localStorage.setItem('cart', JSON.stringify(this.cart))
    }


removeFromCart(id: number) {
      (this.cart as Cart).products = (this.cart as Cart).products.filter(ci => ci.id !== id)
      this.displayCartLoad()
      localStorage.setItem('cart')
    }


displayCartLoad() {
      this.displayCart = (this.cart as Cart).products.map(ci => {
        const requiredProduct = productData.filter(p => p.id === ci.id)
        // if (requiredProduct[0].stock >= ci.qty)
        return {
          id: ci.id,
          name: requiredProduct[0].name,
          price: requiredProduct[0].price,
          qty: ci.qty,
          color: requiredProduct[0].color,
          currency: requiredProduct[0].currency,
          instock: requiredProduct[0].stock >= ci.qty,
        }
      })
    }


export default function Features(): JSX.Element {
    const [flow, setFlow] = React.useState([]);
    return (
        <div className='d-flex flex-row'>
            <RMap className='example-map' initial={{center: fromLonLat([2.364, 48.82]), zoom: 11}}


(p) => `<li className="m-0">${p}</li>`


useCallback(


export class IsListOperator extends Operator implements IOperator {
  /**
   * Loop while the condition is true.
   */
  public call(engine: Engine, args: Array<any>): Object {
    if (engine.eval(args[0]) instanceof Array) {
      return true;
    } else {
      return false;
    }
  }
}


/**
   * Loop while the condition is true.
   */
public call(engine: Engine, args: Array<any>): Object {
    if (engine.eval(args[0]) instanceof Array) {
      return true;
    } else {
      return false;
    }
  }


function defineCustomPatternSpec(variant, customPattern) {
                        it(variant, () => {
                            let time = 1

                            function extraContextValidator(
                                text,
                                offset,
                                tokens,
                                groups
                            ) {
                                let result = isFunction(customPattern)
                                    ? customPattern(text, offset)
                                    : customPattern.exec(text, offset)
                                if (result !== null) {
                                    if (time === 1) {
                                        expect(tokens).to.be.empty
                                        time++
                                    } else if (time === 2) {
                                        expect(tokens).to.have.lengthOf(2)
                                        expect(
                                            groups.whitespace
                                        ).to.have.lengthOf(2)
                                        time++
                                    } else {
                                        throw Error(
                                            "Issue with Custom Token pattern context"
                                        )
                                    }
                                }

                                return result
                            }

                            let A = createToken({
                                name: "A",
                                pattern: "A"
                            })
                            let B = createToken({
                                name: "B",
                                pattern: <any>extraContextValidator
                            })
                            let WS = createToken({
                                name: "WS",
                                pattern: {
                                    exec: (text, offset) =>
                                        /^\s+/.exec(text.substring(offset))
                                },
                                group: "whitespace",
                                line_breaks: true
                            })

                            let lexerDef: any = [WS, A, B]
                            let myLexer = new Lexer(lexerDef, lexerConfig)
                            let lexResult = myLexer.tokenize("B A\n B ")
                            expect(lexResult.tokens).to.have.length(3)
                            expect(tokenMatcher(lexResult.tokens[0], B)).to.be
                                .true
                            expect(tokenMatcher(lexResult.tokens[1], A)).to.be
                                .true
                            expect(tokenMatcher(lexResult.tokens[2], B)).to.be
                                .true

                            let lastToken = lexResult.tokens[2]
                            expect(lastToken.startOffset).to.equal(5)

                            if (testStart) {
                                expect(lastToken.startLine).to.equal(2)
                                expect(lastToken.startColumn).to.equal(2)
                            }

                            if (testFull) {
                                expect(lastToken.endLine).to.equal(2)
                                expect(lastToken.endColumn).to.equal(2)
                                expect(lastToken.endOffset).to.equal(5)
                            }
                        })
                    }


function extraContextValidator(
                                text,
                                offset,
                                tokens,
                                groups
                            ) {
                                let result = isFunction(customPattern)
                                    ? customPattern(text, offset)
                                    : customPattern.exec(text, offset)
                                if (result !== null) {
                                    if (time === 1) {
                                        expect(tokens).to.be.empty
                                        time++
                                    } else if (time === 2) {
                                        expect(tokens).to.have.lengthOf(2)
                                        expect(
                                            groups.whitespace
                                        ).to.have.lengthOf(2)
                                        time++
                                    } else {
                                        throw Error(
                                            "Issue with Custom Token pattern context"
                                        )
                                    }
                                }

                                return result
                            }


function wrapWithCustom(baseExtendToken) {
    return function() {
        let newToken = baseExtendToken.apply(null, arguments)

        let pattern = newToken.PATTERN
        if (
            isRegExp(pattern) &&
            !/\\n|\\r|\\s/g.test(pattern.source) &&
            pattern !== Lexer.NA
        ) {
            newToken.PATTERN = function(text, offset) {
                // can't use sticky here because tests on node.js version 4 won't pass.
                let withStart = addStartOfInput(pattern)
                let execResult = withStart.exec(text.substring(offset))
                return execResult
            }
        }
        return newToken
    }
}


() => {
            it("can create a token from a string with priority to the First Token Type with the longest match #1", () => {
                // this can match either IdentifierTok or BambaTok but should match BambaTok has its pattern is defined before IdentifierTok
                let input = "bamba"
                let result = testLexer.tokenize(input)
                expect(tokenMatcher(result.tokens[0], BambaTok)).to.be.true
                expect(result.tokens[0].image).to.equal("bamba")
                expect(result.tokens[0].startOffset).to.equal(0)
            })

            it("can create a token from a string with priority to the First Token Type with the longest match #2", () => {
                let input = "bambaMIA"
                let result = testLexer.tokenize(input)
                expect(tokenMatcher(result.tokens[0], IdentifierTok)).to.be.true
                expect(result.tokens[0].image).to.equal("bambaMIA")
                expect(result.tokens[0].startOffset).to.equal(0)
            })

            it("can create a token from a string with priority to the First Token Type with the longest match - negative", () => {
                const IntegerTok = createToken({
                    name: "IntegerTok",
                    pattern: /[1-9]\d*/
                })
                const IdentTok = createToken({
                    name: "IdentifierTok",
                    pattern: /[A-Za-z]+/
                })
                // a bit contrived to test all code branches, the BambaTok is not actually prefix of Identifier tok due to the "_"
                const BambaTok = createToken({
                    name: "BambaTok",
                    pattern: /_bamba/
                })
                BambaTok.LONGER_ALT = IdentTok

                const myLexer = new Lexer([BambaTok, IntegerTok, IdentTok], {
                    positionTracking: "onlyOffset"
                })
                let input = "_bamba123"
                let result = myLexer.tokenize(input)

                expect(tokenMatcher(result.tokens[0], BambaTok)).to.be.true
                expect(result.tokens[0].image).to.equal("_bamba")
                expect(tokenMatcher(result.tokens[1], IntegerTok)).to.be.true
                expect(result.tokens[1].image).to.equal("123")
            })

            it("can create a token from a string", () => {
                let input = "6666543221231"
                let result = testLexer.tokenize(input)
                expect(tokenMatcher(result.tokens[0], IntegerTok)).to.be.true
                expect(result.tokens[0].image).to.equal("6666543221231")
                expect(result.tokens[0].startOffset).to.equal(0)
            })
        }


() => {
                // this can match either IdentifierTok or BambaTok but should match BambaTok has its pattern is defined before IdentifierTok
                let input = "bamba"
                let result = testLexer.tokenize(input)
                expect(tokenMatcher(result.tokens[0], BambaTok)).to.be.true
                expect(result.tokens[0].image).to.equal("bamba")
                expect(result.tokens[0].startOffset).to.equal(0)
            }


() => {
                let input = "bambaMIA"
                let result = testLexer.tokenize(input)
                expect(tokenMatcher(result.tokens[0], IdentifierTok)).to.be.true
                expect(result.tokens[0].image).to.equal("bambaMIA")
                expect(result.tokens[0].startOffset).to.equal(0)
            }


() => {
                const IntegerTok = createToken({
                    name: "IntegerTok",
                    pattern: /[1-9]\d*/
                })
                const IdentTok = createToken({
                    name: "IdentifierTok",
                    pattern: /[A-Za-z]+/
                })
                // a bit contrived to test all code branches, the BambaTok is not actually prefix of Identifier tok due to the "_"
                const BambaTok = createToken({
                    name: "BambaTok",
                    pattern: /_bamba/
                })
                BambaTok.LONGER_ALT = IdentTok

                const myLexer = new Lexer([BambaTok, IntegerTok, IdentTok], {
                    positionTracking: "onlyOffset"
                })
                let input = "_bamba123"
                let result = myLexer.tokenize(input)

                expect(tokenMatcher(result.tokens[0], BambaTok)).to.be.true
                expect(result.tokens[0].image).to.equal("_bamba")
                expect(tokenMatcher(result.tokens[1], IntegerTok)).to.be.true
                expect(result.tokens[1].image).to.equal("123")
            }


() => {
                let input = "6666543221231"
                let result = testLexer.tokenize(input)
                expect(tokenMatcher(result.tokens[0], IntegerTok)).to.be.true
                expect(result.tokens[0].image).to.equal("6666543221231")
                expect(result.tokens[0].startOffset).to.equal(0)
            }


() => {
                    let result = findMissingPatterns([
                        BambaTok,
                        IntegerTok,
                        IdentifierTok
                    ])
                    expect(result.errors).to.be.empty
                    expect(result.valid).to.deep.equal([
                        BambaTok,
                        IntegerTok,
                        IdentifierTok
                    ])
                }


() => {
                    let tokenClasses = [ValidNaPattern, MissingPattern]
                    let result = findMissingPatterns(tokenClasses)
                    expect(result.errors.length).to.equal(1)
                    expect(result.errors[0].tokenTypes).to.deep.equal([
                        MissingPattern
                    ])
                    expect(result.errors[0].type).to.equal(
                        LexerDefinitionErrorType.MISSING_PATTERN
                    )
                    expect(result.errors[0].message).to.contain(
                        "MissingPattern"
                    )
                    expect(result.valid).to.deep.equal([ValidNaPattern])
                }


() => {
                    let result = findInvalidPatterns([
                        BambaTok,
                        IntegerTok,
                        IdentifierTok,
                        ValidNaPattern
                    ])
                    expect(result.errors).to.be.empty
                    expect(result.valid).to.deep.equal([
                        BambaTok,
                        IntegerTok,
                        IdentifierTok,
                        ValidNaPattern
                    ])
                }


() => {
                    let tokenClasses = [ValidNaPattern, InvalidPattern]
                    let result = findInvalidPatterns(tokenClasses)
                    expect(result.errors.length).to.equal(1)
                    expect(result.errors[0].tokenTypes).to.deep.equal([
                        InvalidPattern
                    ])
                    expect(result.errors[0].type).to.equal(
                        LexerDefinitionErrorType.INVALID_PATTERN
                    )
                    expect(result.errors[0].message).to.contain(
                        "InvalidPattern"
                    )
                    expect(result.valid).to.deep.equal([ValidNaPattern])
                }


() => {
                    let errors = findUnsupportedFlags([
                        BambaTok,
                        IntegerTok,
                        IdentifierTok,
                        CaseInsensitivePattern
                    ])
                    expect(errors).to.be.empty
                }


() => {
                    let tokenClasses = [ValidNaPattern, MultiLinePattern]
                    let errors = findUnsupportedFlags(tokenClasses)
                    expect(errors.length).to.equal(1)
                    expect(errors[0].tokenTypes).to.deep.equal([
                        MultiLinePattern
                    ])
                    expect(errors[0].type).to.equal(
                        LexerDefinitionErrorType.UNSUPPORTED_FLAGS_FOUND
                    )
                    expect(errors[0].message).to.contain("MultiLinePattern")
                }


() => {
                    let tokenClasses = [ValidNaPattern, GlobalPattern]
                    let errors = findUnsupportedFlags(tokenClasses)
                    expect(errors.length).to.equal(1)
                    expect(errors[0].tokenTypes).to.deep.equal([GlobalPattern])
                    expect(errors[0].type).to.equal(
                        LexerDefinitionErrorType.UNSUPPORTED_FLAGS_FOUND
                    )
                    expect(errors[0].message).to.contain("GlobalPattern")
                }


() => {
                    let errors = findDuplicatePatterns([
                        MultiLinePattern,
                        IntegerValid
                    ])
                    expect(errors).to.be.empty
                }


() => {
                    let errors = findDuplicatePatterns([
                        ValidNaPattern,
                        ValidNaPattern2
                    ])
                    expect(errors).to.be.empty
                }


() => {
                    let InvalidToken = createToken({
                        name: "InvalidToken",
                        pattern: /BAMBA$/
                    })
                    let tokenClasses = [ValidNaPattern, InvalidToken]
                    let errors = findEndOfInputAnchor(tokenClasses)
                    expect(errors.length).to.equal(1)
                    expect(errors[0].tokenTypes).to.deep.equal([InvalidToken])
                    expect(errors[0].type).to.equal(
                        LexerDefinitionErrorType.EOI_ANCHOR_FOUND
                    )
                    expect(errors[0].message).to.contain("InvalidToken")
                }


() => {
                    let errors = findEndOfInputAnchor([
                        IntegerTok,
                        IntegerValid
                    ])
                    expect(errors).to.be.empty
                }


() => {
                    let InvalidToken = createToken({
                        name: "InvalidToken",
                        pattern: /^BAMBA/
                    })
                    let tokenClasses = [ValidNaPattern, InvalidToken]
                    let errors = findStartOfInputAnchor(tokenClasses)
                    expect(errors.length).to.equal(1)
                    expect(errors[0].tokenTypes).to.deep.equal([InvalidToken])
                    expect(errors[0].type).to.equal(
                        LexerDefinitionErrorType.SOI_ANCHOR_FOUND
                    )
                    expect(errors[0].message).to.contain("InvalidToken")
                }


() => {
                    const ClassKeyword = createToken({
                        name: "ClassKeyword",
                        pattern: /class/
                    })

                    const Identifier = createToken({
                        name: "Identifier",
                        pattern: /\w+/
                    })

                    let tokenClasses = [Identifier, ClassKeyword]
                    let errors = findUnreachablePatterns(tokenClasses)
                    expect(errors.length).to.equal(1)
                    expect(errors[0].tokenTypes).to.deep.equal([
                        Identifier,
                        ClassKeyword
                    ])
                    expect(errors[0].type).to.equal(
                        LexerDefinitionErrorType.UNREACHABLE_PATTERN
                    )
                    expect(errors[0].message).to.contain("can never be matched")
                }


() => {
                    let negationPattern = createToken({
                        name: "negationPattern",
                        pattern: /[^\\]/
                    })
                    let errors = findStartOfInputAnchor([negationPattern])
                    expect(errors).to.be.empty
                }


() => {
                    let errors = findStartOfInputAnchor([
                        IntegerTok,
                        IntegerValid
                    ])
                    expect(errors).to.be.empty
                }


() => {
                    let tokenClasses = [DecimalInvalid, IntegerValid]
                    let errors = findDuplicatePatterns(tokenClasses)
                    expect(errors.length).to.equal(1)
                    expect(errors[0].tokenTypes).to.deep.equal([
                        DecimalInvalid,
                        IntegerValid
                    ])
                    expect(errors[0].type).to.equal(
                        LexerDefinitionErrorType.DUPLICATE_PATTERNS_FOUND
                    )
                    expect(errors[0].message).to.contain("IntegerValid")
                    expect(errors[0].message).to.contain("DecimalInvalid")
                }


() => {
                    // should use \d+ as * allows zero repetitions
                    const emptyMatch = createToken({
                        name: "emptyMatch",
                        pattern: /\d*/
                    })

                    let tokenClasses = [emptyMatch]
                    let errors = findEmptyMatchRegExps(tokenClasses)
                    expect(errors.length).to.equal(1)
                    expect(errors[0].tokenTypes).to.deep.equal([emptyMatch])
                    expect(errors[0].type).to.equal(
                        LexerDefinitionErrorType.EMPTY_MATCH_PATTERN
                    )
                    expect(errors[0].message).to.contain("emptyMatch")
                    expect(errors[0].message).to.contain(
                        "must not match an empty string"
                    )
                }


() => {
                    let errors = findInvalidGroupType([
                        IntegerTok,
                        Skipped,
                        Special
                    ])
                    //noinspection BadExpressionStatementJS
                    expect(errors).to.be.empty
                }


() => {
                    let tokenClasses = [InvalidGroupNumber]
                    let errors = findInvalidGroupType(tokenClasses)
                    expect(errors.length).to.equal(1)
                    expect(errors[0].tokenTypes).to.deep.equal([
                        InvalidGroupNumber
                    ])
                    expect(errors[0].type).to.equal(
                        LexerDefinitionErrorType.INVALID_GROUP_TYPE_FOUND
                    )
                    expect(errors[0].message).to.contain("InvalidGroupNumber")
                }


() => {
            it("can transform a pattern to one with startOfInput mark ('^') #1 (NO OP)", () => {
                let orgSource = (<any>BambaTok.PATTERN).source
                let transPattern = addStartOfInput(<any>BambaTok.PATTERN)
                expect(transPattern.source).to.equal("^(?:" + orgSource + ")")
                expect(/^\^/.test(transPattern.source)).to.equal(true)
            })

            it("can transform a pattern to one with startOfInput mark ('^') #2", () => {
                let orgSource = PatternNoStart.PATTERN.source
                let transPattern = addStartOfInput(PatternNoStart.PATTERN)
                expect(transPattern.source).to.equal("^(?:" + orgSource + ")")
                expect(/^\^/.test(transPattern.source)).to.equal(true)
            })

            if (!skipValidationChecks) {
                it("can transform/analyze an array of Token Typees into matched/ignored/patternToClass", () => {
                    let tokenClasses = [
                        Keyword,
                        If,
                        Else,
                        Return,
                        Integer,
                        Punctuation,
                        LParen,
                        RParen,
                        Whitespace,
                        NewLine
                    ]
                    let analyzeResult = analyzeTokenTypes(tokenClasses, {
                        useSticky: false
                    })

                    let allPatterns = map(
                        analyzeResult.patternIdxToConfig,
                        currConfig => currConfig.pattern
                    )

                    expect(allPatterns.length).to.equal(8)
                    let allPatternsString = map(allPatterns, pattern => {
                        return isString(pattern) ? pattern : pattern.source
                    })
                    setEquality(allPatternsString, [
                        "^(?:(\\t| ))",
                        "^(?:(\\n|\\r|\\r\\n))",
                        "^(?:[1-9]\\d*)",
                        "(",
                        ")",
                        "^(?:if)",
                        "^(?:else)",
                        "^(?:return)"
                    ])

                    let patternIdxToClass = map(
                        analyzeResult.patternIdxToConfig,
                        currConfig => currConfig.tokenType
                    )
                    expect(keys(patternIdxToClass).length).to.equal(8)
                    expect(patternIdxToClass[0]).to.equal(If)
                    expect(patternIdxToClass[1]).to.equal(Else)
                    expect(patternIdxToClass[2]).to.equal(Return)
                    expect(patternIdxToClass[3]).to.equal(Integer)
                    expect(patternIdxToClass[4]).to.equal(LParen)
                    expect(patternIdxToClass[5]).to.equal(RParen)
                    expect(patternIdxToClass[6]).to.equal(Whitespace)
                    expect(patternIdxToClass[7]).to.equal(NewLine)
                })
            }

            if (!skipValidationChecks && ORG_SUPPORT_STICKY) {
                it("can transform/analyze an array of Token Typees into matched/ignored/patternToClass - sticky", () => {
                    let tokenClasses = [
                        Keyword,
                        If,
                        Else,
                        Return,
                        Integer,
                        Punctuation,
                        LParen,
                        RParen,
                        Whitespace,
                        NewLine
                    ]
                    // on newer node.js this will run with the 2nd argument as true.
                    let analyzeResult = analyzeTokenTypes(tokenClasses, {
                        useSticky: true
                    })
                    let allPatterns = map(
                        analyzeResult.patternIdxToConfig,
                        currConfig => currConfig.pattern
                    )
                    expect(allPatterns.length).to.equal(8)
                    let allPatternsString = map(allPatterns, pattern => {
                        return isString(pattern) ? pattern : pattern.source
                    })
                    setEquality(allPatternsString, [
                        "(\\t| )",
                        "(\\n|\\r|\\r\\n)",
                        "(",
                        ")",
                        "[1-9]\\d*",
                        "if",
                        "else",
                        "return"
                    ])

                    forEach(allPatterns, currPattern => {
                        if (isRegExp(currPattern)) {
                            expect(currPattern.sticky).to.be.true
                        }
                    })
                    let patternIdxToClass = map(
                        analyzeResult.patternIdxToConfig,
                        currConfig => currConfig.tokenType
                    )
                    expect(keys(patternIdxToClass).length).to.equal(8)
                    expect(patternIdxToClass[0]).to.equal(If)
                    expect(patternIdxToClass[1]).to.equal(Else)
                    expect(patternIdxToClass[2]).to.equal(Return)
                    expect(patternIdxToClass[3]).to.equal(Integer)
                    expect(patternIdxToClass[4]).to.equal(LParen)
                    expect(patternIdxToClass[5]).to.equal(RParen)
                    expect(patternIdxToClass[6]).to.equal(Whitespace)
                    expect(patternIdxToClass[7]).to.equal(NewLine)
                })
            }

            it("can count the number of line terminators in a string", () => {
                let ltCounter = new Lexer([
                    createToken({
                        name: "lt",
                        pattern: /\s+/,
                        line_breaks: true
                    }),
                    createToken({
                        name: "num",
                        pattern: /\d+/
                    })
                ])
                let lastToken = last(ltCounter.tokenize("1\r\n1\r1").tokens)
                expect(lastToken.startLine).to.equal(3)

                let lastToken2 = last(
                    ltCounter.tokenize("\r\r\r1234\r\n1").tokens
                )
                expect(lastToken2.startLine).to.equal(5)
                expect(lastToken2.startColumn).to.equal(1)

                let lastToken3 = last(ltCounter.tokenize("2\r3\n\r4\n5").tokens)
                expect(lastToken3.startLine).to.equal(5)
            })

            it("can count the number of line terminators in a string - string literal patterns", () => {
                let ltCounter = new Lexer([
                    createToken({
                        name: "lt",
                        pattern: "\n",
                        line_breaks: true
                    }),
                    createToken({
                        name: "num",
                        pattern: /\d+/
                    })
                ])
                let lastToken = last(ltCounter.tokenize("1\n1\n1").tokens)
                expect(lastToken.startLine).to.equal(3)
            })

            it("Supports custom Line Terminators", () => {
                let WS = createToken({
                    name: "WS",
                    pattern: /\u2028/,
                    line_breaks: true,
                    group: Lexer.SKIPPED
                })
                let ifElseLexer = new Lexer([WS, If, Else], {
                    lineTerminatorsPattern: /\u2028/g
                })

                let input = "if\u2028elseif"

                let lexResult = ifElseLexer.tokenize(input)
                let tokens: any = lexResult.tokens
                expect(tokens[0].image).to.equal("if")
                expect(tokens[0].startLine).to.equal(1)
                expect(tokens[0].startColumn).to.equal(1)
                expect(tokens[1].image).to.equal("else")
                expect(tokens[1].startLine).to.equal(2)
                expect(tokens[1].startColumn).to.equal(1)
                expect(tokens[2].image).to.equal("if")
                expect(tokens[2].startLine).to.equal(2)
                expect(tokens[2].startColumn).to.equal(5)
            })
        }


() => {
                let orgSource = (<any>BambaTok.PATTERN).source
                let transPattern = addStartOfInput(<any>BambaTok.PATTERN)
                expect(transPattern.source).to.equal("^(?:" + orgSource + ")")
                expect(/^\^/.test(transPattern.source)).to.equal(true)
            }


() => {
                let orgSource = PatternNoStart.PATTERN.source
                let transPattern = addStartOfInput(PatternNoStart.PATTERN)
                expect(transPattern.source).to.equal("^(?:" + orgSource + ")")
                expect(/^\^/.test(transPattern.source)).to.equal(true)
            }