// Checks to make sure all events have been rendered and that the calendar
// has internal info on all the events.
function checkAllEvents() {
    expect(currentCalendar.getEvents().length).toEqual(3)
    let calendarWrapper = new CalendarWrapper(currentCalendar)
    expect(calendarWrapper.getEventEls().length).toEqual(3)
  }


() => {
  pushOptions({
    initialDate: '2014-06-24',
    initialView: 'dayGridMonth',
  })

  function buildEventsWithoutIds(): EventInput[] {
    return [
      { title: 'event zero', start: '2014-06-24', className: 'event-zero' },
      { title: 'event one', start: '2014-06-24', className: 'event-non-zero event-one' },
      { title: 'event two', start: '2014-06-24', className: 'event-non-zero event-two' },
    ]
  }

  function buildEventsWithIds() {
    let events = buildEventsWithoutIds()
    let i

    for (i = 0; i < events.length; i += 1) {
      events[i].id = i
    }

    return events
  }

  $.each({
    'when events without IDs': buildEventsWithoutIds,
    'when events with IDs': buildEventsWithIds,
  }, (desc, eventGenerator) => {
    describe(desc, () => {
      it('can remove all events if no args specified', (done) => {
        go(
          eventGenerator(),
          () => {
            currentCalendar.removeAllEvents()
          },
          () => {
            expect(currentCalendar.getEvents().length).toEqual(0)
            let calendarWrapper = new CalendarWrapper(currentCalendar)
            expect(calendarWrapper.getEventEls().length).toEqual(0)
          },
          done,
        )
      })

      it('can remove events individually', (done) => {
        go(
          eventGenerator(),
          () => {
            currentCalendar.getEvents().forEach((event) => {
              if ($.inArray('event-one', event.classNames) !== -1) {
                event.remove()
              }
            })
          },
          () => {
            expect(currentCalendar.getEvents().length).toEqual(2)
            let calendarWrapper = new CalendarWrapper(currentCalendar)
            expect(calendarWrapper.getEventEls().length).toEqual(2)
            expect($('.event-zero').length).toEqual(1)
            expect($('.event-two').length).toEqual(1)
          },
          done,
        )
      })
    })
  })

  it('can remove events with a numeric ID', (done) => {
    go(
      buildEventsWithIds(),
      () => {
        currentCalendar.getEventById(1 as any).remove()
      },
      () => {
        expect(currentCalendar.getEvents().length).toEqual(2)
        let calendarWrapper = new CalendarWrapper(currentCalendar)
        expect(calendarWrapper.getEventEls().length).toEqual(2)
        expect($('.event-zero').length).toEqual(1)
        expect($('.event-two').length).toEqual(1)
      },
      done,
    )
  })

  it('can remove events with a string ID', (done) => {
    go(
      buildEventsWithIds(),
      () => {
        currentCalendar.getEventById('1').remove()
      },
      () => {
        expect(currentCalendar.getEvents().length).toEqual(2)
        let calendarWrapper = new CalendarWrapper(currentCalendar)
        expect(calendarWrapper.getEventEls().length).toEqual(2)
        expect($('.event-zero').length).toEqual(1)
        expect($('.event-two').length).toEqual(1)
      },
      done,
    )
  })

  it('can remove an event with ID 0', (done) => { // for issue 2082
    go(
      buildEventsWithIds(),
      () => {
        currentCalendar.getEventById(0 as any).remove()
      },
      () => {
        expect(currentCalendar.getEvents().length).toEqual(2)
        let calendarWrapper = new CalendarWrapper(currentCalendar)
        expect(calendarWrapper.getEventEls().length).toEqual(2)
        expect($('.event-zero').length).toEqual(0)
        expect($('.event-non-zero').length).toEqual(2)
      },
      done,
    )
  })

  // Verifies the actions in removeFunc executed correctly by calling checkFunc.
  function go(events, removeFunc, checkFunc, doneFunc) {
    initCalendar({
      events,
    })

    checkAllEvents() // make sure all events initially rendered correctly
    removeFunc() // remove the events
    setTimeout(() => { // because the event rerender will be queued because we're a level deep
      checkFunc() // check correctness

      // move the calendar back out of view, then back in
      currentCalendar.next()
      currentCalendar.prev()

      // array event sources should maintain the same state
      // whereas "dynamic" event sources should refetch and reset the state
      if ($.isArray(events)) {
        checkFunc() // for issue 2187
      } else {
        checkAllEvents()
      }

      doneFunc()
    }, 0)
  }

  // Checks to make sure all events have been rendered and that the calendar
  // has internal info on all the events.
  function checkAllEvents() {
    expect(currentCalendar.getEvents().length).toEqual(3)
    let calendarWrapper = new CalendarWrapper(currentCalendar)
    expect(calendarWrapper.getEventEls().length).toEqual(3)
  }
}


(desc, eventGenerator) => {
    describe(desc, () => {
      it('can remove all events if no args specified', (done) => {
        go(
          eventGenerator(),
          () => {
            currentCalendar.removeAllEvents()
          },
          () => {
            expect(currentCalendar.getEvents().length).toEqual(0)
            let calendarWrapper = new CalendarWrapper(currentCalendar)
            expect(calendarWrapper.getEventEls().length).toEqual(0)
          },
          done,
        )
      })

      it('can remove events individually', (done) => {
        go(
          eventGenerator(),
          () => {
            currentCalendar.getEvents().forEach((event) => {
              if ($.inArray('event-one', event.classNames) !== -1) {
                event.remove()
              }
            })
          },
          () => {
            expect(currentCalendar.getEvents().length).toEqual(2)
            let calendarWrapper = new CalendarWrapper(currentCalendar)
            expect(calendarWrapper.getEventEls().length).toEqual(2)
            expect($('.event-zero').length).toEqual(1)
            expect($('.event-two').length).toEqual(1)
          },
          done,
        )
      })
    })
  }


() => {
      it('can remove all events if no args specified', (done) => {
        go(
          eventGenerator(),
          () => {
            currentCalendar.removeAllEvents()
          },
          () => {
            expect(currentCalendar.getEvents().length).toEqual(0)
            let calendarWrapper = new CalendarWrapper(currentCalendar)
            expect(calendarWrapper.getEventEls().length).toEqual(0)
          },
          done,
        )
      })

      it('can remove events individually', (done) => {
        go(
          eventGenerator(),
          () => {
            currentCalendar.getEvents().forEach((event) => {
              if ($.inArray('event-one', event.classNames) !== -1) {
                event.remove()
              }
            })
          },
          () => {
            expect(currentCalendar.getEvents().length).toEqual(2)
            let calendarWrapper = new CalendarWrapper(currentCalendar)
            expect(calendarWrapper.getEventEls().length).toEqual(2)
            expect($('.event-zero').length).toEqual(1)
            expect($('.event-two').length).toEqual(1)
          },
          done,
        )
      })
    }


(done) => {
        go(
          eventGenerator(),
          () => {
            currentCalendar.removeAllEvents()
          },
          () => {
            expect(currentCalendar.getEvents().length).toEqual(0)
            let calendarWrapper = new CalendarWrapper(currentCalendar)
            expect(calendarWrapper.getEventEls().length).toEqual(0)
          },
          done,
        )
      }


() => {
            currentCalendar.removeAllEvents()
          }


() => {
            expect(currentCalendar.getEvents().length).toEqual(0)
            let calendarWrapper = new CalendarWrapper(currentCalendar)
            expect(calendarWrapper.getEventEls().length).toEqual(0)
          }


(done) => {
        go(
          eventGenerator(),
          () => {
            currentCalendar.getEvents().forEach((event) => {
              if ($.inArray('event-one', event.classNames) !== -1) {
                event.remove()
              }
            })
          },
          () => {
            expect(currentCalendar.getEvents().length).toEqual(2)
            let calendarWrapper = new CalendarWrapper(currentCalendar)
            expect(calendarWrapper.getEventEls().length).toEqual(2)
            expect($('.event-zero').length).toEqual(1)
            expect($('.event-two').length).toEqual(1)
          },
          done,
        )
      }


() => {
            currentCalendar.getEvents().forEach((event) => {
              if ($.inArray('event-one', event.classNames) !== -1) {
                event.remove()
              }
            })
          }


(event) => {
              if ($.inArray('event-one', event.classNames) !== -1) {
                event.remove()
              }
            }


() => {
            expect(currentCalendar.getEvents().length).toEqual(2)
            let calendarWrapper = new CalendarWrapper(currentCalendar)
            expect(calendarWrapper.getEventEls().length).toEqual(2)
            expect($('.event-zero').length).toEqual(1)
            expect($('.event-two').length).toEqual(1)
          }


(done) => {
    go(
      buildEventsWithIds(),
      () => {
        currentCalendar.getEventById(1 as any).remove()
      },
      () => {
        expect(currentCalendar.getEvents().length).toEqual(2)
        let calendarWrapper = new CalendarWrapper(currentCalendar)
        expect(calendarWrapper.getEventEls().length).toEqual(2)
        expect($('.event-zero').length).toEqual(1)
        expect($('.event-two').length).toEqual(1)
      },
      done,
    )
  }


() => {
        currentCalendar.getEventById(1 as any).remove()
      }


() => {
        expect(currentCalendar.getEvents().length).toEqual(2)
        let calendarWrapper = new CalendarWrapper(currentCalendar)
        expect(calendarWrapper.getEventEls().length).toEqual(2)
        expect($('.event-zero').length).toEqual(1)
        expect($('.event-two').length).toEqual(1)
      }


(done) => {
    go(
      buildEventsWithIds(),
      () => {
        currentCalendar.getEventById('1').remove()
      },
      () => {
        expect(currentCalendar.getEvents().length).toEqual(2)
        let calendarWrapper = new CalendarWrapper(currentCalendar)
        expect(calendarWrapper.getEventEls().length).toEqual(2)
        expect($('.event-zero').length).toEqual(1)
        expect($('.event-two').length).toEqual(1)
      },
      done,
    )
  }


() => {
        currentCalendar.getEventById('1').remove()
      }


(done) => { // for issue 2082
    go(
      buildEventsWithIds(),
      () => {
        currentCalendar.getEventById(0 as any).remove()
      },
      () => {
        expect(currentCalendar.getEvents().length).toEqual(2)
        let calendarWrapper = new CalendarWrapper(currentCalendar)
        expect(calendarWrapper.getEventEls().length).toEqual(2)
        expect($('.event-zero').length).toEqual(0)
        expect($('.event-non-zero').length).toEqual(2)
      },
      done,
    )
  }


() => {
        currentCalendar.getEventById(0 as any).remove()
      }


() => {
        expect(currentCalendar.getEvents().length).toEqual(2)
        let calendarWrapper = new CalendarWrapper(currentCalendar)
        expect(calendarWrapper.getEventEls().length).toEqual(2)
        expect($('.event-zero').length).toEqual(0)
        expect($('.event-non-zero').length).toEqual(2)
      }


() => { // because the event rerender will be queued because we're a level deep
      checkFunc() // check correctness

      // move the calendar back out of view, then back in
      currentCalendar.next()
      currentCalendar.prev()

      // array event sources should maintain the same state
      // whereas "dynamic" event sources should refetch and reset the state
      if ($.isArray(events)) {
        checkFunc() // for issue 2187
      } else {
        checkAllEvents()
      }

      doneFunc()
    }


// Generated by typings
// Source: https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/fd5dc234aaa7c6ea536baa5596877637a3bad2bf/numeral/index.d.ts
interface NumeralJSLocale {
	delimiters: {
		thousands: string;
		decimal: string;
	};
	abbreviations: {
		thousand: string;
		million: string;
		billion: string;
		trillion: string;
	};
	ordinal(num: number): string;
	currency: {
		symbol: string;
	};
}


// http://numeraljs.com/#custom-formats
interface NumeralJsFormat {
	regexps: {
		format: RegExp,
		unformat: RegExp,
	},
	format: (value: any, format: string, roundingFunction: Function) => string,
	unformat: (value: string) => number
}


// http://numeraljs.com/#use-it
interface Numeral {
	(value?: any): Numeral;
	version: string;
	isNumeral: boolean;

	/**
	 * This function sets the current locale.  If no arguments are passed in,
	 * it will simply return the current global locale key.
	 */
	locale(key?: string): string;

	/**
	 * Registers a language definition or a custom format definition.
	 *
	 * @param what Allowed values are: either 'format' or 'locale'
	 * @param key The key of the registerd type, e.g. 'de' for a german locale definition
	 * @param value The locale definition or the format definitiion
	 */
	register(what: RegisterType, key: string, value: NumeralJSLocale | NumeralJsFormat): NumeralJSLocale | NumeralJsFormat;

	zeroFormat(format: string): void;
	nullFormat(format: string): void;
	defaultFormat(format: string): void;
	clone(): Numeral;
	format(inputString?: string): string;
	formatCurrency(inputString?: string): string;
	unformat(inputString: string): number;
	value(): number;
	valueOf(): number;
	set (value: any): Numeral;
	add(value: any): Numeral;
	subtract(value: any): Numeral;
	multiply(value: any): Numeral;
	divide(value: any): Numeral;
	difference(value: any): number;
}


type RegisterType = 'format' | 'locale';


(sel) => {
			const inPosition = new Position(sel.positionLineNumber, sel.positionColumn);
			const outPosition = this._move(wordSeparators, model, inPosition, this._wordNavigationType);
			return this._moveTo(sel, outPosition, this._inSelectionMode);
		}


r => CursorState.fromModelSelection(r)


(sel) => {
			const deleteRange = this._delete(wordSeparators, model, sel, this._whitespaceHeuristics, this._wordNavigationType);
			return new ReplaceCommand(deleteRange, '');
		}


export abstract class MoveWordCommand extends EditorCommand {

	private readonly _inSelectionMode: boolean;
	private readonly _wordNavigationType: WordNavigationType;

	constructor(opts: MoveWordOptions) {
		super(opts);
		this._inSelectionMode = opts.inSelectionMode;
		this._wordNavigationType = opts.wordNavigationType;
	}

	public runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, args: any): void {
		if (!editor.hasModel()) {
			return;
		}
		const wordSeparators = getMapForWordSeparators(editor.getOption(EditorOption.wordSeparators));
		const model = editor.getModel();
		const selections = editor.getSelections();

		const result = selections.map((sel) => {
			const inPosition = new Position(sel.positionLineNumber, sel.positionColumn);
			const outPosition = this._move(wordSeparators, model, inPosition, this._wordNavigationType);
			return this._moveTo(sel, outPosition, this._inSelectionMode);
		});

		model.pushStackElement();
		editor._getViewModel().setCursorStates('moveWordCommand', CursorChangeReason.NotSet, result.map(r => CursorState.fromModelSelection(r)));
		if (result.length === 1) {
			const pos = new Position(result[0].positionLineNumber, result[0].positionColumn);
			editor.revealPosition(pos, ScrollType.Smooth);
		}
	}

	private _moveTo(from: Selection, to: Position, inSelectionMode: boolean): Selection {
		if (inSelectionMode) {
			// move just position
			return new Selection(
				from.selectionStartLineNumber,
				from.selectionStartColumn,
				to.lineNumber,
				to.column
			);
		} else {
			// move everything
			return new Selection(
				to.lineNumber,
				to.column,
				to.lineNumber,
				to.column
			);
		}
	}

	protected abstract _move(wordSeparators: WordCharacterClassifier, model: ITextModel, position: Position, wordNavigationType: WordNavigationType): Position;
}


export class WordLeftCommand extends MoveWordCommand {
	protected _move(wordSeparators: WordCharacterClassifier, model: ITextModel, position: Position, wordNavigationType: WordNavigationType): Position {
		return WordOperations.moveWordLeft(wordSeparators, model, position, wordNavigationType);
	}
}


export class WordRightCommand extends MoveWordCommand {
	protected _move(wordSeparators: WordCharacterClassifier, model: ITextModel, position: Position, wordNavigationType: WordNavigationType): Position {
		return WordOperations.moveWordRight(wordSeparators, model, position, wordNavigationType);
	}
}


export class CursorWordStartLeft extends WordLeftCommand {
	constructor() {
		super({
			inSelectionMode: false,
			wordNavigationType: WordNavigationType.WordStart,
			id: 'cursorWordStartLeft',
			precondition: undefined,
			kbOpts: {
				kbExpr: EditorContextKeys.textInputFocus,
				primary: KeyMod.CtrlCmd | KeyCode.LeftArrow,
				mac: { primary: KeyMod.Alt | KeyCode.LeftArrow },
				weight: KeybindingWeight.EditorContrib
			}
		});
	}
}


export class CursorWordEndLeft extends WordLeftCommand {
	constructor() {
		super({
			inSelectionMode: false,
			wordNavigationType: WordNavigationType.WordEnd,
			id: 'cursorWordEndLeft',
			precondition: undefined
		});
	}
}


export class CursorWordLeft extends WordLeftCommand {
	constructor() {
		super({
			inSelectionMode: false,
			wordNavigationType: WordNavigationType.WordStartFast,
			id: 'cursorWordLeft',
			precondition: undefined
		});
	}
}


export class CursorWordStartLeftSelect extends WordLeftCommand {
	constructor() {
		super({
			inSelectionMode: true,
			wordNavigationType: WordNavigationType.WordStart,
			id: 'cursorWordStartLeftSelect',
			precondition: undefined,
			kbOpts: {
				kbExpr: EditorContextKeys.textInputFocus,
				primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.LeftArrow,
				mac: { primary: KeyMod.Alt | KeyMod.Shift | KeyCode.LeftArrow },
				weight: KeybindingWeight.EditorContrib
			}
		});
	}
}


export class CursorWordEndLeftSelect extends WordLeftCommand {
	constructor() {
		super({
			inSelectionMode: true,
			wordNavigationType: WordNavigationType.WordEnd,
			id: 'cursorWordEndLeftSelect',
			precondition: undefined
		});
	}
}


export class CursorWordLeftSelect extends WordLeftCommand {
	constructor() {
		super({
			inSelectionMode: true,
			wordNavigationType: WordNavigationType.WordStartFast,
			id: 'cursorWordLeftSelect',
			precondition: undefined
		});
	}
}


// Accessibility navigation commands should only be enabled on windows since they are tuned to what NVDA expects
export class CursorWordAccessibilityLeft extends WordLeftCommand {
	constructor() {
		super({
			inSelectionMode: false,
			wordNavigationType: WordNavigationType.WordAccessibility,
			id: 'cursorWordAccessibilityLeft',
			precondition: undefined,
			kbOpts: {
				kbExpr: ContextKeyExpr.and(EditorContextKeys.textInputFocus, CONTEXT_ACCESSIBILITY_MODE_ENABLED),
				win: { primary: KeyMod.CtrlCmd | KeyCode.LeftArrow },
				weight: KeybindingWeight.EditorContrib + 1
			}
		});
	}

	protected _move(_: WordCharacterClassifier, model: ITextModel, position: Position, wordNavigationType: WordNavigationType): Position {
		return super._move(getMapForWordSeparators(EditorOptions.wordSeparators.defaultValue), model, position, wordNavigationType);
	}
}


export class CursorWordAccessibilityLeftSelect extends WordLeftCommand {
	constructor() {
		super({
			inSelectionMode: true,
			wordNavigationType: WordNavigationType.WordAccessibility,
			id: 'cursorWordAccessibilityLeftSelect',
			precondition: undefined,
			kbOpts: {
				kbExpr: ContextKeyExpr.and(EditorContextKeys.textInputFocus, CONTEXT_ACCESSIBILITY_MODE_ENABLED),
				win: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.LeftArrow },
				weight: KeybindingWeight.EditorContrib + 1
			}
		});
	}

	protected _move(_: WordCharacterClassifier, model: ITextModel, position: Position, wordNavigationType: WordNavigationType): Position {
		return super._move(getMapForWordSeparators(EditorOptions.wordSeparators.defaultValue), model, position, wordNavigationType);
	}
}


export class CursorWordStartRight extends WordRightCommand {
	constructor() {
		super({
			inSelectionMode: false,
			wordNavigationType: WordNavigationType.WordStart,
			id: 'cursorWordStartRight',
			precondition: undefined
		});
	}
}


export class CursorWordEndRight extends WordRightCommand {
	constructor() {
		super({
			inSelectionMode: false,
			wordNavigationType: WordNavigationType.WordEnd,
			id: 'cursorWordEndRight',
			precondition: undefined,
			kbOpts: {
				kbExpr: EditorContextKeys.textInputFocus,
				primary: KeyMod.CtrlCmd | KeyCode.RightArrow,
				mac: { primary: KeyMod.Alt | KeyCode.RightArrow },
				weight: KeybindingWeight.EditorContrib
			}
		});
	}
}


export class CursorWordRight extends WordRightCommand {
	constructor() {
		super({
			inSelectionMode: false,
			wordNavigationType: WordNavigationType.WordEnd,
			id: 'cursorWordRight',
			precondition: undefined
		});
	}
}


export class CursorWordStartRightSelect extends WordRightCommand {
	constructor() {
		super({
			inSelectionMode: true,
			wordNavigationType: WordNavigationType.WordStart,
			id: 'cursorWordStartRightSelect',
			precondition: undefined
		});
	}
}


export class CursorWordEndRightSelect extends WordRightCommand {
	constructor() {
		super({
			inSelectionMode: true,
			wordNavigationType: WordNavigationType.WordEnd,
			id: 'cursorWordEndRightSelect',
			precondition: undefined,
			kbOpts: {
				kbExpr: EditorContextKeys.textInputFocus,
				primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.RightArrow,
				mac: { primary: KeyMod.Alt | KeyMod.Shift | KeyCode.RightArrow },
				weight: KeybindingWeight.EditorContrib
			}
		});
	}
}


export class CursorWordRightSelect extends WordRightCommand {
	constructor() {
		super({
			inSelectionMode: true,
			wordNavigationType: WordNavigationType.WordEnd,
			id: 'cursorWordRightSelect',
			precondition: undefined
		});
	}
}


export class CursorWordAccessibilityRight extends WordRightCommand {
	constructor() {
		super({
			inSelectionMode: false,
			wordNavigationType: WordNavigationType.WordAccessibility,
			id: 'cursorWordAccessibilityRight',
			precondition: undefined,
			kbOpts: {
				kbExpr: ContextKeyExpr.and(EditorContextKeys.textInputFocus, CONTEXT_ACCESSIBILITY_MODE_ENABLED),
				win: { primary: KeyMod.CtrlCmd | KeyCode.RightArrow },
				weight: KeybindingWeight.EditorContrib + 1
			}
		});
	}

	protected _move(_: WordCharacterClassifier, model: ITextModel, position: Position, wordNavigationType: WordNavigationType): Position {
		return super._move(getMapForWordSeparators(EditorOptions.wordSeparators.defaultValue), model, position, wordNavigationType);
	}
}


export class CursorWordAccessibilityRightSelect extends WordRightCommand {
	constructor() {
		super({
			inSelectionMode: true,
			wordNavigationType: WordNavigationType.WordAccessibility,
			id: 'cursorWordAccessibilityRightSelect',
			precondition: undefined,
			kbOpts: {
				kbExpr: ContextKeyExpr.and(EditorContextKeys.textInputFocus, CONTEXT_ACCESSIBILITY_MODE_ENABLED),
				win: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.RightArrow },
				weight: KeybindingWeight.EditorContrib + 1
			}
		});
	}

	protected _move(_: WordCharacterClassifier, model: ITextModel, position: Position, wordNavigationType: WordNavigationType): Position {
		return super._move(getMapForWordSeparators(EditorOptions.wordSeparators.defaultValue), model, position, wordNavigationType);
	}
}


export abstract class DeleteWordCommand extends EditorCommand {
	private readonly _whitespaceHeuristics: boolean;
	private readonly _wordNavigationType: WordNavigationType;

	constructor(opts: DeleteWordOptions) {
		super(opts);
		this._whitespaceHeuristics = opts.whitespaceHeuristics;
		this._wordNavigationType = opts.wordNavigationType;
	}

	public runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, args: any): void {
		if (!editor.hasModel()) {
			return;
		}
		const wordSeparators = getMapForWordSeparators(editor.getOption(EditorOption.wordSeparators));
		const model = editor.getModel();
		const selections = editor.getSelections();

		const commands = selections.map((sel) => {
			const deleteRange = this._delete(wordSeparators, model, sel, this._whitespaceHeuristics, this._wordNavigationType);
			return new ReplaceCommand(deleteRange, '');
		});

		editor.pushUndoStop();
		editor.executeCommands(this.id, commands);
		editor.pushUndoStop();
	}

	protected abstract _delete(wordSeparators: WordCharacterClassifier, model: ITextModel, selection: Selection, whitespaceHeuristics: boolean, wordNavigationType: WordNavigationType): Range;
}


export class DeleteWordLeftCommand extends DeleteWordCommand {
	protected _delete(wordSeparators: WordCharacterClassifier, model: ITextModel, selection: Selection, whitespaceHeuristics: boolean, wordNavigationType: WordNavigationType): Range {
		let r = WordOperations.deleteWordLeft(wordSeparators, model, selection, whitespaceHeuristics, wordNavigationType);
		if (r) {
			return r;
		}
		return new Range(1, 1, 1, 1);
	}
}


export class DeleteWordRightCommand extends DeleteWordCommand {
	protected _delete(wordSeparators: WordCharacterClassifier, model: ITextModel, selection: Selection, whitespaceHeuristics: boolean, wordNavigationType: WordNavigationType): Range {
		let r = WordOperations.deleteWordRight(wordSeparators, model, selection, whitespaceHeuristics, wordNavigationType);
		if (r) {
			return r;
		}
		const lineCount = model.getLineCount();
		const maxColumn = model.getLineMaxColumn(lineCount);
		return new Range(lineCount, maxColumn, lineCount, maxColumn);
	}
}


export class DeleteWordStartLeft extends DeleteWordLeftCommand {
	constructor() {
		super({
			whitespaceHeuristics: false,
			wordNavigationType: WordNavigationType.WordStart,
			id: 'deleteWordStartLeft',
			precondition: EditorContextKeys.writable
		});
	}
}


export class DeleteWordEndLeft extends DeleteWordLeftCommand {
	constructor() {
		super({
			whitespaceHeuristics: false,
			wordNavigationType: WordNavigationType.WordEnd,
			id: 'deleteWordEndLeft',
			precondition: EditorContextKeys.writable
		});
	}
}


export class DeleteWordLeft extends DeleteWordLeftCommand {
	constructor() {
		super({
			whitespaceHeuristics: true,
			wordNavigationType: WordNavigationType.WordStart,
			id: 'deleteWordLeft',
			precondition: EditorContextKeys.writable,
			kbOpts: {
				kbExpr: EditorContextKeys.textInputFocus,
				primary: KeyMod.CtrlCmd | KeyCode.Backspace,
				mac: { primary: KeyMod.Alt | KeyCode.Backspace },
				weight: KeybindingWeight.EditorContrib
			}
		});
	}
}


export class DeleteWordStartRight extends DeleteWordRightCommand {
	constructor() {
		super({
			whitespaceHeuristics: false,
			wordNavigationType: WordNavigationType.WordStart,
			id: 'deleteWordStartRight',
			precondition: EditorContextKeys.writable
		});
	}
}


export class DeleteWordEndRight extends DeleteWordRightCommand {
	constructor() {
		super({
			whitespaceHeuristics: false,
			wordNavigationType: WordNavigationType.WordEnd,
			id: 'deleteWordEndRight',
			precondition: EditorContextKeys.writable
		});
	}
}


export class DeleteWordRight extends DeleteWordRightCommand {
	constructor() {
		super({
			whitespaceHeuristics: true,
			wordNavigationType: WordNavigationType.WordEnd,
			id: 'deleteWordRight',
			precondition: EditorContextKeys.writable,
			kbOpts: {
				kbExpr: EditorContextKeys.textInputFocus,
				primary: KeyMod.CtrlCmd | KeyCode.Delete,
				mac: { primary: KeyMod.Alt | KeyCode.Delete },
				weight: KeybindingWeight.EditorContrib
			}
		});
	}
}


export interface MoveWordOptions extends ICommandOptions {
	inSelectionMode: boolean;
	wordNavigationType: WordNavigationType;
}


export interface DeleteWordOptions extends ICommandOptions {
	whitespaceHeuristics: boolean;
	wordNavigationType: WordNavigationType;
}


public runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, args: any): void {
		if (!editor.hasModel()) {
			return;
		}
		const wordSeparators = getMapForWordSeparators(editor.getOption(EditorOption.wordSeparators));
		const model = editor.getModel();
		const selections = editor.getSelections();

		const result = selections.map((sel) => {
			const inPosition = new Position(sel.positionLineNumber, sel.positionColumn);
			const outPosition = this._move(wordSeparators, model, inPosition, this._wordNavigationType);
			return this._moveTo(sel, outPosition, this._inSelectionMode);
		});

		model.pushStackElement();
		editor._getViewModel().setCursorStates('moveWordCommand', CursorChangeReason.NotSet, result.map(r => CursorState.fromModelSelection(r)));
		if (result.length === 1) {
			const pos = new Position(result[0].positionLineNumber, result[0].positionColumn);
			editor.revealPosition(pos, ScrollType.Smooth);
		}
	}


private _moveTo(from: Selection, to: Position, inSelectionMode: boolean): Selection {
		if (inSelectionMode) {
			// move just position
			return new Selection(
				from.selectionStartLineNumber,
				from.selectionStartColumn,
				to.lineNumber,
				to.column
			);
		} else {
			// move everything
			return new Selection(
				to.lineNumber,
				to.column,
				to.lineNumber,
				to.column
			);
		}
	}


protected abstract _move(wordSeparators: WordCharacterClassifier, model: ITextModel, position: Position, wordNavigationType: WordNavigationType): Position;


protected _move(wordSeparators: WordCharacterClassifier, model: ITextModel, position: Position, wordNavigationType: WordNavigationType): Position {
		return WordOperations.moveWordLeft(wordSeparators, model, position, wordNavigationType);
	}


protected _move(wordSeparators: WordCharacterClassifier, model: ITextModel, position: Position, wordNavigationType: WordNavigationType): Position {
		return WordOperations.moveWordRight(wordSeparators, model, position, wordNavigationType);
	}


protected _move(_: WordCharacterClassifier, model: ITextModel, position: Position, wordNavigationType: WordNavigationType): Position {
		return super._move(getMapForWordSeparators(EditorOptions.wordSeparators.defaultValue), model, position, wordNavigationType);
	}


public runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, args: any): void {
		if (!editor.hasModel()) {
			return;
		}
		const wordSeparators = getMapForWordSeparators(editor.getOption(EditorOption.wordSeparators));
		const model = editor.getModel();
		const selections = editor.getSelections();

		const commands = selections.map((sel) => {
			const deleteRange = this._delete(wordSeparators, model, sel, this._whitespaceHeuristics, this._wordNavigationType);
			return new ReplaceCommand(deleteRange, '');
		});

		editor.pushUndoStop();
		editor.executeCommands(this.id, commands);
		editor.pushUndoStop();
	}


protected abstract _delete(wordSeparators: WordCharacterClassifier, model: ITextModel, selection: Selection, whitespaceHeuristics: boolean, wordNavigationType: WordNavigationType): Range;


protected _delete(wordSeparators: WordCharacterClassifier, model: ITextModel, selection: Selection, whitespaceHeuristics: boolean, wordNavigationType: WordNavigationType): Range {
		let r = WordOperations.deleteWordLeft(wordSeparators, model, selection, whitespaceHeuristics, wordNavigationType);
		if (r) {
			return r;
		}
		return new Range(1, 1, 1, 1);
	}


protected _delete(wordSeparators: WordCharacterClassifier, model: ITextModel, selection: Selection, whitespaceHeuristics: boolean, wordNavigationType: WordNavigationType): Range {
		let r = WordOperations.deleteWordRight(wordSeparators, model, selection, whitespaceHeuristics, wordNavigationType);
		if (r) {
			return r;
		}
		const lineCount = model.getLineCount();
		const maxColumn = model.getLineMaxColumn(lineCount);
		return new Range(lineCount, maxColumn, lineCount, maxColumn);
	}


export default async function () {
  // setup
  const argv = getGlobalVariable('argv');
  if (argv.noglobal) {
    return;
  }

  fs.writeFileSync('.npmrc', 'registry = http://localhost:4873', 'utf8');

  await silentNpm(
    'install',
    '-g',
    '@angular-devkit/schematics-cli',
    '--registry=http://localhost:4873',
  );
  await exec(process.platform.startsWith('win') ? 'where' : 'which', 'schematics');

  const startCwd = process.cwd();
  const schematicPath = path.join(startCwd, 'test-schematic');

  try {
    // create schematic
    await exec('schematics', 'schematic', '--name', 'test-schematic');

    process.chdir(schematicPath);

    await silentNpm('install');
    await silentNpm('test');
  } finally {
    // restore path
    process.chdir(startCwd);
    await rimraf(schematicPath);
  }
}


export function adt<T>(tag: T, value?: undefined): ADT<T>;


export function adt<T, V>(tag: T, value: V): ADT<T, V>;


export function adt<T extends ADT<unknown, unknown>>(tag: T['tag'], value: T['value']): ADT<T['tag'], T['value']> {
  return { tag, value };
}


export function adtCurried<T extends ADT<unknown, unknown>>(tag: T['tag']): ((value: T['value']) => ADT<T['tag'], T['value']>) {
  return value => adt(tag, value);
}


value => adt(tag, value)


/**
 * "ADT" stands for "Algebraic Data Type".
 *
 * ```
 * type Color
 *   = ADT<'red'>
 *   | ADT<'blue'>
 *   | ADT<'green'>
 *   | ADT<'rgb', [number, number, number]>;
 *
 * const red: Color = { tag: 'red', value: undefined };
 * const rgb: Color = { tag: 'rgb', value: [123, 255, 7] };
 * ```
 */
export interface ADT<Tag, Value = undefined> {
  readonly tag: Tag;
  readonly value: Value;
}


export interface BodyWithErrors {
  permissions?: string[];
  database?: string[];
  notFound?: string[];
  conflict?: string[];
}


export enum ClientHttpMethod {
  Get = 'GET',
  Post = 'POST',
  Put = 'PUT',
  Patch = 'PATCH',
  Delete = 'DELETE',
  Options = 'OPTIONS'
}


export type Id = string;


export type Defined<T> = Exclude<T, undefined>;


export type IfElse<T, Extends, Yes, No> = T extends Extends ? Yes : No;


export type IfDefined<T, IsDefined, IsUndefined = undefined> = IfElse<T, undefined, IsUndefined, IsDefined>;


export type Comparison = -1 | 0 | 1;


() => I18nService


export class ToolbarConfigModel {

  constructor( @Inject( forwardRef( () => I18nService)) public i18n: I18nService ) {}

  public font = {
    family: { show: true, tooltipText: this.i18n.getLocale().Editor.fontFamilyText },
    size: { show: true, tooltipText: this.i18n.getLocale().Editor.fontSizeText },
    bold: { show: true, tooltipText: this.i18n.getLocale().Editor.boldText },
    italic: { show: true, tooltipText: this.i18n.getLocale().Editor.italicText },
    underline: { show: true, tooltipText: this.i18n.getLocale().Editor.underlineText },
    color: { show: true, tooltipText: this.i18n.getLocale().Editor.colorText },
    highlight: { show: true, tooltipText: this.i18n.getLocale().Editor.highlightText },
  };
  public alignment = {
    left: { show: true, tooltipText: this.i18n.getLocale().Editor.alignLeftText },
    center: { show: true, tooltipText: this.i18n.getLocale().Editor.alignCenterText },
    right: { show: true, tooltipText: this.i18n.getLocale().Editor.alignRightText },
    justify: { show: true, tooltipText: this.i18n.getLocale().Editor.justifyText }
  };
  public lists = {
    ordered: { show: true, tooltipText: this.i18n.getLocale().Editor.listOrdered },
    unordered: { show: true, tooltipText: this.i18n.getLocale().Editor.listUnordered }
  };
  public others = {
    link: { show: true, tooltipText: this.i18n.getLocale().Editor.linkText },
    imageLink: { show: true, tooltipText: this.i18n.getLocale().Editor.imageText },
    quote: { show: true, tooltipText: this.i18n.getLocale().Editor.quoteText }
  };

}


(emitter: EventMultiplexer<InstallExtensionOnServerEvent>, server) => { emitter.add(Event.map(server.extensionManagementService.onInstallExtension, e => ({ ...e, server }))); return emitter; }


e => ({ ...e, server })


(emitter: EventMultiplexer<readonly InstallExtensionResult[]>, server) => { emitter.add(server.extensionManagementService.onDidInstallExtensions); return emitter; }


(emitter: EventMultiplexer<UninstallExtensionOnServerEvent>, server) => { emitter.add(Event.map(server.extensionManagementService.onUninstallExtension, e => ({ ...e, server }))); return emitter; }


(emitter: EventMultiplexer<DidUninstallExtensionOnServerEvent>, server) => { emitter.add(Event.map(server.extensionManagementService.onDidUninstallExtension, e => ({ ...e, server }))); return emitter; }


({ extensionManagementService }) => extensionManagementService.getInstalled(type, donotIgnoreInvalidExtensions)


s => s !== server


i => !i.isBuiltin && areSameExtensions(i.identifier, extension.identifier)


i => !this.extensionManifestPropertiesService.prefersExecuteOnUI(i.manifest)
				&& i.manifest.extensionDependencies && i.manifest.extensionDependencies.some(id => areSameExtensions({ id }, extension.identifier))


id => areSameExtensions({ id }, extension.identifier)


server => server !== this.extensionManagementServerService.webExtensionManagementServer


({ extensionManagementService }) => extensionManagementService.unzip(zipLocation)


([extensionIdentifier]) => extensionIdentifier


server => this.installVSIX(vsix, server, options)


server => server.extensionManagementService.installFromGallery(gallery, installOptions)


([local]) => local


extension => this.installFromGallery(extension, installOptions)


id => ({ id })


async getInstalled(type?: ExtensionType, donotIgnoreInvalidExtensions?: boolean): Promise<ILocalExtension[]> {
		const result = await Promise.all(this.servers.map(({ extensionManagementService }) => extensionManagementService.getInstalled(type, donotIgnoreInvalidExtensions)));
		return flatten(result);
	}


async uninstall(extension: ILocalExtension, options?: UninstallOptions): Promise<void> {
		const server = this.getServer(extension);
		if (!server) {
			return Promise.reject(`Invalid location ${extension.location.toString()}`);
		}
		if (this.servers.length > 1) {
			if (isLanguagePackExtension(extension.manifest)) {
				return this.uninstallEverywhere(extension);
			}
			return this.uninstallInServer(extension, server, options);
		}
		return server.extensionManagementService.uninstall(extension);
	}


private async uninstallEverywhere(extension: ILocalExtension): Promise<void> {
		const server = this.getServer(extension);
		if (!server) {
			return Promise.reject(`Invalid location ${extension.location.toString()}`);
		}
		const promise = server.extensionManagementService.uninstall(extension);
		const otherServers: IExtensionManagementServer[] = this.servers.filter(s => s !== server);
		if (otherServers.length) {
			for (const otherServer of otherServers) {
				const installed = await otherServer.extensionManagementService.getInstalled();
				extension = installed.filter(i => !i.isBuiltin && areSameExtensions(i.identifier, extension.identifier))[0];
				if (extension) {
					await otherServer.extensionManagementService.uninstall(extension);
				}
			}
		}
		return promise;
	}


private async uninstallInServer(extension: ILocalExtension, server: IExtensionManagementServer, options?: UninstallOptions): Promise<void> {
		if (server === this.extensionManagementServerService.localExtensionManagementServer) {
			const installedExtensions = await this.extensionManagementServerService.remoteExtensionManagementServer!.extensionManagementService.getInstalled(ExtensionType.User);
			const dependentNonUIExtensions = installedExtensions.filter(i => !this.extensionManifestPropertiesService.prefersExecuteOnUI(i.manifest)
				&& i.manifest.extensionDependencies && i.manifest.extensionDependencies.some(id => areSameExtensions({ id }, extension.identifier)));
			if (dependentNonUIExtensions.length) {
				return Promise.reject(new Error(this.getDependentsErrorMessage(extension, dependentNonUIExtensions)));
			}
		}
		return server.extensionManagementService.uninstall(extension, options);
	}


private getDependentsErrorMessage(extension: ILocalExtension, dependents: ILocalExtension[]): string {
		if (dependents.length === 1) {
			return localize('singleDependentError', "Cannot uninstall extension '{0}'. Extension '{1}' depends on this.",
				extension.manifest.displayName || extension.manifest.name, dependents[0].manifest.displayName || dependents[0].manifest.name);
		}
		if (dependents.length === 2) {
			return localize('twoDependentsError', "Cannot uninstall extension '{0}'. Extensions '{1}' and '{2}' depend on this.",
				extension.manifest.displayName || extension.manifest.name, dependents[0].manifest.displayName || dependents[0].manifest.name, dependents[1].manifest.displayName || dependents[1].manifest.name);
		}
		return localize('multipleDependentsError', "Cannot uninstall extension '{0}'. Extensions '{1}', '{2}' and others depend on this.",
			extension.manifest.displayName || extension.manifest.name, dependents[0].manifest.displayName || dependents[0].manifest.name, dependents[1].manifest.displayName || dependents[1].manifest.name);

	}


async reinstallFromGallery(extension: ILocalExtension): Promise<void> {
		const server = this.getServer(extension);
		if (server) {
			await this.checkForWorkspaceTrust(extension.manifest);
			return server.extensionManagementService.reinstallFromGallery(extension);
		}
		return Promise.reject(`Invalid location ${extension.location.toString()}`);
	}


updateMetadata(extension: ILocalExtension, metadata: IGalleryMetadata): Promise<ILocalExtension> {
		const server = this.getServer(extension);
		if (server) {
			return server.extensionManagementService.updateMetadata(extension, metadata);
		}
		return Promise.reject(`Invalid location ${extension.location.toString()}`);
	}


updateExtensionScope(extension: ILocalExtension, isMachineScoped: boolean): Promise<ILocalExtension> {
		const server = this.getServer(extension);
		if (server) {
			return server.extensionManagementService.updateExtensionScope(extension, isMachineScoped);
		}
		return Promise.reject(`Invalid location ${extension.location.toString()}`);
	}


zip(extension: ILocalExtension): Promise<URI> {
		const server = this.getServer(extension);
		if (server) {
			return server.extensionManagementService.zip(extension);
		}
		return Promise.reject(`Invalid location ${extension.location.toString()}`);
	}


unzip(zipLocation: URI): Promise<IExtensionIdentifier> {
		return Promises.settled(this.servers
			// Filter out web server
			.filter(server => server !== this.extensionManagementServerService.webExtensionManagementServer)
			.map(({ extensionManagementService }) => extensionManagementService.unzip(zipLocation))).then(([extensionIdentifier]) => extensionIdentifier);
	}


async install(vsix: URI, options?: InstallVSIXOptions): Promise<ILocalExtension> {
		if (this.extensionManagementServerService.localExtensionManagementServer && this.extensionManagementServerService.remoteExtensionManagementServer) {
			const manifest = await this.getManifest(vsix);
			if (isLanguagePackExtension(manifest)) {
				// Install on both servers
				const [local] = await Promises.settled([this.extensionManagementServerService.localExtensionManagementServer, this.extensionManagementServerService.remoteExtensionManagementServer].map(server => this.installVSIX(vsix, server, options)));
				return local;
			}
			if (this.extensionManifestPropertiesService.prefersExecuteOnUI(manifest)) {
				// Install only on local server
				return this.installVSIX(vsix, this.extensionManagementServerService.localExtensionManagementServer, options);
			}
			// Install only on remote server
			return this.installVSIX(vsix, this.extensionManagementServerService.remoteExtensionManagementServer, options);
		}
		if (this.extensionManagementServerService.localExtensionManagementServer) {
			return this.installVSIX(vsix, this.extensionManagementServerService.localExtensionManagementServer, options);
		}
		if (this.extensionManagementServerService.remoteExtensionManagementServer) {
			return this.installVSIX(vsix, this.extensionManagementServerService.remoteExtensionManagementServer, options);
		}
		return Promise.reject('No Servers to Install');
	}


async installWebExtension(location: URI): Promise<ILocalExtension> {
		if (!this.extensionManagementServerService.webExtensionManagementServer) {
			throw new Error('Web extension management server is not found');
		}
		return this.extensionManagementServerService.webExtensionManagementServer.extensionManagementService.install(location);
	}


protected async installVSIX(vsix: URI, server: IExtensionManagementServer, options: InstallVSIXOptions | undefined): Promise<ILocalExtension> {
		const manifest = await this.getManifest(vsix);
		if (manifest) {
			await this.checkForWorkspaceTrust(manifest);
			return server.extensionManagementService.install(vsix, options);
		}
		return Promise.reject('Unable to get the extension manifest.');
	}


getManifest(vsix: URI): Promise<IExtensionManifest> {
		if (vsix.scheme === Schemas.file && this.extensionManagementServerService.localExtensionManagementServer) {
			return this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.getManifest(vsix);
		}
		if (vsix.scheme === Schemas.vscodeRemote && this.extensionManagementServerService.remoteExtensionManagementServer) {
			return this.extensionManagementServerService.remoteExtensionManagementServer.extensionManagementService.getManifest(vsix);
		}
		return Promise.reject('No Servers');
	}


async canInstall(gallery: IGalleryExtension): Promise<boolean> {
		if (this.extensionManagementServerService.localExtensionManagementServer
			&& await this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.canInstall(gallery)) {
			return true;
		}
		const manifest = await this.extensionGalleryService.getManifest(gallery, CancellationToken.None);
		if (!manifest) {
			return false;
		}
		if (this.extensionManagementServerService.remoteExtensionManagementServer
			&& await this.extensionManagementServerService.remoteExtensionManagementServer.extensionManagementService.canInstall(gallery)
			&& this.extensionManifestPropertiesService.canExecuteOnWorkspace(manifest)) {
			return true;
		}
		if (this.extensionManagementServerService.webExtensionManagementServer
			&& await this.extensionManagementServerService.webExtensionManagementServer.extensionManagementService.canInstall(gallery)
			&& this.extensionManifestPropertiesService.canExecuteOnWeb(manifest)) {
			return true;
		}
		return false;
	}


async updateFromGallery(gallery: IGalleryExtension, extension: ILocalExtension, installOptions?: InstallOptions): Promise<ILocalExtension> {
		const server = this.getServer(extension);
		if (!server) {
			return Promise.reject(`Invalid location ${extension.location.toString()}`);
		}

		const servers: IExtensionManagementServer[] = [];

		// Update Language pack on local and remote servers
		if (isLanguagePackExtension(extension.manifest)) {
			servers.push(...this.servers.filter(server => server !== this.extensionManagementServerService.webExtensionManagementServer));
		} else {
			servers.push(server);
		}

		return Promises.settled(servers.map(server => server.extensionManagementService.installFromGallery(gallery, installOptions))).then(([local]) => local);
	}


async installExtensions(extensions: IGalleryExtension[], installOptions?: InstallOptions): Promise<ILocalExtension[]> {
		if (!installOptions) {
			const isMachineScoped = await this.hasToFlagExtensionsMachineScoped(extensions);
			installOptions = { isMachineScoped, isBuiltin: false };
		}
		return Promises.settled(extensions.map(extension => this.installFromGallery(extension, installOptions)));
	}


async installFromGallery(gallery: IGalleryExtension, installOptions?: InstallOptions): Promise<ILocalExtension> {

		const manifest = await this.extensionGalleryService.getManifest(gallery, CancellationToken.None);
		if (!manifest) {
			return Promise.reject(localize('Manifest is not found', "Installing Extension {0} failed: Manifest is not found.", gallery.displayName || gallery.name));
		}

		const servers: IExtensionManagementServer[] = [];

		// Install Language pack on local and remote servers
		if (isLanguagePackExtension(manifest)) {
			servers.push(...this.servers.filter(server => server !== this.extensionManagementServerService.webExtensionManagementServer));
		} else {
			const server = this.getExtensionManagementServerToInstall(manifest);
			if (server) {
				servers.push(server);
			}
		}

		if (servers.length) {
			if (!installOptions) {
				const isMachineScoped = await this.hasToFlagExtensionsMachineScoped([gallery]);
				installOptions = { isMachineScoped, isBuiltin: false };
			}
			if (!installOptions.isMachineScoped && this.isExtensionsSyncEnabled()) {
				if (this.extensionManagementServerService.localExtensionManagementServer && !servers.includes(this.extensionManagementServerService.localExtensionManagementServer) && (await this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.canInstall(gallery))) {
					servers.push(this.extensionManagementServerService.localExtensionManagementServer);
				}
			}
			await this.checkForWorkspaceTrust(manifest);
			if (!installOptions.donotIncludePackAndDependencies) {
				await this.checkInstallingExtensionPackOnWeb(gallery, manifest);
			}
			return Promises.settled(servers.map(server => server.extensionManagementService.installFromGallery(gallery, installOptions))).then(([local]) => local);
		}

		const error = new Error(localize('cannot be installed', "Cannot install the '{0}' extension because it is not available in this setup.", gallery.displayName || gallery.name));
		error.name = INSTALL_ERROR_NOT_SUPPORTED;
		return Promise.reject(error);
	}


getExtensionManagementServerToInstall(manifest: IExtensionManifest): IExtensionManagementServer | null {

		// Only local server
		if (this.servers.length === 1 && this.extensionManagementServerService.localExtensionManagementServer) {
			return this.extensionManagementServerService.localExtensionManagementServer;
		}

		const extensionKind = this.extensionManifestPropertiesService.getExtensionKind(manifest);
		for (const kind of extensionKind) {
			if (kind === 'ui' && this.extensionManagementServerService.localExtensionManagementServer) {
				return this.extensionManagementServerService.localExtensionManagementServer;
			}
			if (kind === 'workspace' && this.extensionManagementServerService.remoteExtensionManagementServer) {
				return this.extensionManagementServerService.remoteExtensionManagementServer;
			}
			if (kind === 'web' && this.extensionManagementServerService.webExtensionManagementServer) {
				return this.extensionManagementServerService.webExtensionManagementServer;
			}
		}

		// Local server can accept any extension. So return local server if not compatible server found.
		return this.extensionManagementServerService.localExtensionManagementServer;
	}


private isExtensionsSyncEnabled(): boolean {
		return this.userDataAutoSyncEnablementService.isEnabled() && this.userDataSyncResourceEnablementService.isResourceEnabled(SyncResource.Extensions);
	}


private async hasToFlagExtensionsMachineScoped(extensions: IGalleryExtension[]): Promise<boolean> {
		if (this.isExtensionsSyncEnabled()) {
			const result = await this.dialogService.show(
				Severity.Info,
				extensions.length === 1 ? localize('install extension', "Install Extension") : localize('install extensions', "Install Extensions"),
				[
					localize('install', "Install"),
					localize('install and do no sync', "Install (Do not sync)"),
					localize('cancel', "Cancel"),
				],
				{
					cancelId: 2,
					detail: extensions.length === 1
						? localize('install single extension', "Would you like to install and synchronize '{0}' extension across your devices?", extensions[0].displayName)
						: localize('install multiple extensions', "Would you like to install and synchronize extensions across your devices?")
				}
			);
			switch (result.choice) {
				case 0:
					return false;
				case 1:
					return true;
			}
			throw canceled();
		}
		return false;
	}


getExtensionsReport(): Promise<IReportedExtension[]> {
		if (this.extensionManagementServerService.localExtensionManagementServer) {
			return this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.getExtensionsReport();
		}
		if (this.extensionManagementServerService.remoteExtensionManagementServer) {
			return this.extensionManagementServerService.remoteExtensionManagementServer.extensionManagementService.getExtensionsReport();
		}
		return Promise.resolve([]);
	}


private getServer(extension: ILocalExtension): IExtensionManagementServer | null {
		return this.extensionManagementServerService.getExtensionManagementServer(extension);
	}


protected async checkForWorkspaceTrust(manifest: IExtensionManifest): Promise<void> {
		if (this.extensionManifestPropertiesService.getExtensionUntrustedWorkspaceSupportType(manifest) === false) {
			const trustState = await this.workspaceTrustRequestService.requestWorkspaceTrust({
				message: localize('extensionInstallWorkspaceTrustMessage', "Enabling this extension requires a trusted workspace."),
				buttons: [
					{ label: localize('extensionInstallWorkspaceTrustButton', "Trust Workspace & Install"), type: 'ContinueWithTrust' },
					{ label: localize('extensionInstallWorkspaceTrustContinueButton', "Install"), type: 'ContinueWithoutTrust' },
					{ label: localize('extensionInstallWorkspaceTrustManageButton', "Learn More"), type: 'Manage' }
				]
			});

			if (trustState === undefined) {
				throw canceled();
			}
		}
	}


private async checkInstallingExtensionPackOnWeb(extension: IGalleryExtension, manifest: IExtensionManifest): Promise<void> {
		if (!manifest.extensionPack?.length) {
			return;
		}

		if (this.servers.length !== 1 || this.servers[0] !== this.extensionManagementServerService.webExtensionManagementServer) {
			return;
		}

		const nonWebExtensions = [];
		const extensions = await this.extensionGalleryService.getExtensions(manifest.extensionPack.map(id => ({ id })), CancellationToken.None);
		for (const extension of extensions) {
			if (!(await this.servers[0].extensionManagementService.canInstall(extension))) {
				nonWebExtensions.push(extension);
			}
		}

		if (nonWebExtensions.length) {
			const productName = localize('VS Code for Web', "{0} for the Web", this.productService.nameLong);
			if (nonWebExtensions.length === extensions.length) {
				throw new ExtensionManagementError('Not supported in Web', INSTALL_ERROR_NOT_SUPPORTED);
			}
			const { choice } = await this.dialogService.show(Severity.Info, localize('non web extensions', "'{0}' contains extensions which are not available in {1}. Would you like to install it anyways?", extension.displayName || extension.identifier.id, productName),
				[localize('install', "Install"), localize('cancel', "Cancel")], { cancelId: 2 });
			if (choice !== 0) {
				throw canceled();
			}
		}
	}


registerParticipant() { throw new Error('Not Supported'); }


getTargetPlatform(): Promise<TargetPlatform> { throw new Error('Not Supported'); }


export function parseRequest(req: IncomingMessage) {
    console.log('HTTP ' + req.url);
    const { pathname, query } = parse(req.url || '/', true);
    const { fontSize, images, widths, heights, theme, md } = (query || {});

    if (Array.isArray(fontSize)) {
        throw new Error('Expected a single fontSize');
    }
    if (Array.isArray(theme)) {
        throw new Error('Expected a single theme');
    }

    const arr = (pathname || '/').slice(1).split('.');
    let extension = '';
    let text = '';
    if (arr.length === 0) {
        text = '';
    } else if (arr.length === 1) {
        text = arr[0];
    } else {
        extension = arr.pop() as string;
        text = arr.join('.');
    }

    const parsedRequest: ParsedRequest = {
        fileType: extension === 'jpeg' ? extension : 'png',
        text: decodeURIComponent(text),
        theme: theme === 'dark' ? 'dark' : 'light',
        md: md === '1' || md === 'true',
        fontSize: fontSize || '96px',
        images: getArray(images),
        widths: getArray(widths),
        heights: getArray(heights),
    };
    parsedRequest.images = getDefaultImages(parsedRequest.images, parsedRequest.theme);
    return parsedRequest;
}


function getArray(stringOrArray: string[] | string | undefined): string[] {
    if (typeof stringOrArray === 'undefined') {
        return [];
    } else if (Array.isArray(stringOrArray)) {
        return stringOrArray;
    } else {
        return [stringOrArray];
    }
}


function getDefaultImages(images: string[], theme: Theme): string[] {
    const defaultImage = theme === 'light'
        ? 'https://manage.e-resident.gov.ee/company/image/profile_logo/202109/6rYn1fAPNQ_1632924814.png'
        : 'https://manage.e-resident.gov.ee/company/image/profile_logo/202109/6rYn1fAPNQ_1632924814.png';

    if (!images || !images[0]) {
        return [defaultImage];
    }
    if (!images[0].startsWith('https://assets.vercel.com/') && !images[0].startsWith('https://assets.zeit.co/')) {
        images[0] = defaultImage;
    }
    return images;
}


/**
 * Model's translation(position) control for {@link ARWallControl}
 * @category Controls-AR
 */
class ARWallTranslateControl implements ARControl {
  public readonly position = new THREE.Vector3();
  public readonly wallPosition = new THREE.Vector3();
  public readonly hitRotation = new THREE.Quaternion();
  // Global Y guaranteed rotation matrix
  public readonly wallRotation = new THREE.Quaternion();
  private _arrowIndicator: ArrowIndicator;

  // Options

  // Internal states
  private _dragPlane = new THREE.Plane();
  private _enabled = true;
  private _active = false;
  private _initialPos = new THREE.Vector2();

  /**
   * Whether this control is enabled or not
   * @readonly
   */
  public get enabled() { return this._enabled; }

  /**
   * Create new instance of ARTranslateControl
   * @param {ARWallTranslateControlOption} [options={}] Options
   */
  constructor(options: Partial<ARWallTranslateControlOption> = {}) {
    this._arrowIndicator = new ArrowIndicator(options.arrow);
  }

  public initWallTransform({ hitPosition, hitRotation, modelPosition, wallRotation }: {
    hitPosition: THREE.Vector3,
    hitRotation: THREE.Quaternion,
    modelPosition: THREE.Vector3,
    wallRotation: THREE.Quaternion,
  }) {
    this.position.copy(modelPosition);
    this.hitRotation.copy(hitRotation);
    this.wallPosition.copy(hitPosition);
    this.wallRotation.copy(wallRotation);

    const wallNormal = new THREE.Vector3(0, 1, 0).applyQuaternion(wallRotation);

    this._dragPlane.set(wallNormal, -wallNormal.dot(modelPosition));
  }

  public init({ view3d }: XRRenderContext) {
    view3d.scene.add(this._arrowIndicator.object);
  }

  public destroy({ view3d }: XRContext) {
    view3d.scene.remove(this._arrowIndicator.object);
  }

  /**
   * Enable this control
   */
  public enable() {
    this._enabled = true;
  }

  /**
   * Disable this control
   */
  public disable() {
    this._enabled = false;
    this.deactivate();
  }

  public activate({ model }: XRRenderContext, gesture: TOUCH.GESTURE) {
    if (!this._enabled) return;

    this._active = true;

    // Update arrows
    const arrowIndicator = this._arrowIndicator;
    const modelBbox = model.initialBbox;
    modelBbox.min.multiply(model.scene.scale);
    modelBbox.max.multiply(model.scene.scale);
    modelBbox.translate(model.scene.position);

    arrowIndicator.show();
    arrowIndicator.updatePosition(modelBbox.getCenter(new THREE.Vector3()));
    arrowIndicator.updateScale(model.size / 16);

    const arrowPlaneRotation = model.scene.quaternion.clone();

    arrowIndicator.updateRotation(arrowPlaneRotation);
    arrowIndicator.updateOffset(new THREE.Vector3().subVectors(modelBbox.max, modelBbox.min).multiplyScalar(0.5));
  }

  public deactivate() {
    this._active = false;
    this._arrowIndicator.hide();
  }

  public setInitialPos(coords: THREE.Vector2[]) {
    this._initialPos.copy(coords[0]);
  }

  public process({ view3d, model, frame, referenceSpace, xrCam }: XRRenderContext, { hitResults }: XRInputs) {
    if (!hitResults || hitResults.length !== 1 || !this._active) return;

    const dragPlane = this._dragPlane;

    const modelRoot = model.scene;
    const modelZOffset = -model.initialBbox.min.z * modelRoot.scale.z;

    const camPos = new THREE.Vector3().setFromMatrixPosition(xrCam.matrixWorld);
    const hitResult = hitResults[0];
    const hitPose = hitResult.results[0] && hitResult.results[0].getPose(referenceSpace);

    const isWallHit = hitPose && hitPose.transform.matrix[5] < 0.25;

    if (!hitPose || !isWallHit) {
      // Use previous drag plane if no hit plane is found
      const targetRayPose = frame.getPose(hitResult.inputSource.targetRaySpace, view3d.renderer.threeRenderer.xr.getReferenceSpace());
      const fingerDir = new THREE.Vector3().copy(targetRayPose.transform.position).sub(camPos).normalize();

      const fingerRay = new THREE.Ray(camPos, fingerDir);
      const intersection = fingerRay.intersectPlane(dragPlane, new THREE.Vector3());

      if (intersection) {
        this.wallPosition.copy(intersection.clone().sub(dragPlane.normal.clone().multiplyScalar(modelZOffset)));
        this.position.copy(intersection);
      }
      return;
    }

    const hitMatrix = new THREE.Matrix4().fromArray(hitPose.transform.matrix);
    const hitOrientation = new THREE.Quaternion().copy(hitPose.transform.orientation);
    const hitPosition = new THREE.Vector3().setFromMatrixPosition(hitMatrix);
    const worldYAxis = new THREE.Vector3(0, 1, 0);
    /*
     * ^ wallU
     * |
     * ⨀---> wallV
     * wallNormal
     */
    const wallNormal = new THREE.Vector3(0, 1, 0).applyQuaternion(hitOrientation).normalize();
    const wallU = new THREE.Vector3().crossVectors(worldYAxis, wallNormal);
    const wallV = wallNormal.clone().applyAxisAngle(wallU, -Math.PI / 2);

    // Reconstruct wall matrix with prev Y(normal) direction as Z axis
    const wallMatrix = new THREE.Matrix4().makeBasis(wallU, wallV, wallNormal);
    const modelPosition = hitPosition.clone().add(wallNormal.clone().multiplyScalar(modelZOffset));

    // Update position
    this.position.copy(modelPosition);
    this.wallPosition.copy(hitPosition);

    // Update rotation if it differs more than 10deg
    const prevWallNormal = new THREE.Vector3(0, 1, 0).applyQuaternion(this.hitRotation).normalize();
    if (Math.acos(Math.abs(prevWallNormal.dot(wallNormal))) >= Math.PI / 18) {
      const prevWallRotation = this.wallRotation.clone();
      const wallRotation = new THREE.Quaternion().setFromRotationMatrix(wallMatrix);
      const rotationDiff = prevWallRotation.inverse().premultiply(wallRotation);

      modelRoot.quaternion.premultiply(rotationDiff);
      this.wallRotation.copy(wallRotation);
      this.hitRotation.copy(hitOrientation);

      this._arrowIndicator.updateRotation(modelRoot.quaternion);

      // Update drag plane
      dragPlane.set(wallNormal, -modelPosition.dot(wallNormal));
    }
  }

  public update({ model }: XRRenderContext, delta: number) {
    model.scene.position.copy(this.position);
    this._arrowIndicator.updatePosition(this.position);
    model.scene.updateMatrix();
  }
}


/**
 * Options for {@link ARTranslateControl}
 * @category Controls-AR
 * @interface
 * @property {ArrowIndicatorOption} arrow Options for {@link ArrowIndicator}
 */
export interface ARWallTranslateControlOption {
  arrow: Partial<ArrowIndicatorOption>;
}


public initWallTransform({ hitPosition, hitRotation, modelPosition, wallRotation }: {
    hitPosition: THREE.Vector3,
    hitRotation: THREE.Quaternion,
    modelPosition: THREE.Vector3,
    wallRotation: THREE.Quaternion,
  }) {
    this.position.copy(modelPosition);
    this.hitRotation.copy(hitRotation);
    this.wallPosition.copy(hitPosition);
    this.wallRotation.copy(wallRotation);

    const wallNormal = new THREE.Vector3(0, 1, 0).applyQuaternion(wallRotation);

    this._dragPlane.set(wallNormal, -wallNormal.dot(modelPosition));
  }


public init({ view3d }: XRRenderContext) {
    view3d.scene.add(this._arrowIndicator.object);
  }


public destroy({ view3d }: XRContext) {
    view3d.scene.remove(this._arrowIndicator.object);
  }


/**
   * Enable this control
   */
public enable() {
    this._enabled = true;
  }


/**
   * Disable this control
   */
public disable() {
    this._enabled = false;
    this.deactivate();
  }


public activate({ model }: XRRenderContext, gesture: TOUCH.GESTURE) {
    if (!this._enabled) return;

    this._active = true;

    // Update arrows
    const arrowIndicator = this._arrowIndicator;
    const modelBbox = model.initialBbox;
    modelBbox.min.multiply(model.scene.scale);
    modelBbox.max.multiply(model.scene.scale);
    modelBbox.translate(model.scene.position);

    arrowIndicator.show();
    arrowIndicator.updatePosition(modelBbox.getCenter(new THREE.Vector3()));
    arrowIndicator.updateScale(model.size / 16);

    const arrowPlaneRotation = model.scene.quaternion.clone();

    arrowIndicator.updateRotation(arrowPlaneRotation);
    arrowIndicator.updateOffset(new THREE.Vector3().subVectors(modelBbox.max, modelBbox.min).multiplyScalar(0.5));
  }


public deactivate() {
    this._active = false;
    this._arrowIndicator.hide();
  }


public setInitialPos(coords: THREE.Vector2[]) {
    this._initialPos.copy(coords[0]);
  }


public process({ view3d, model, frame, referenceSpace, xrCam }: XRRenderContext, { hitResults }: XRInputs) {
    if (!hitResults || hitResults.length !== 1 || !this._active) return;

    const dragPlane = this._dragPlane;

    const modelRoot = model.scene;
    const modelZOffset = -model.initialBbox.min.z * modelRoot.scale.z;

    const camPos = new THREE.Vector3().setFromMatrixPosition(xrCam.matrixWorld);
    const hitResult = hitResults[0];
    const hitPose = hitResult.results[0] && hitResult.results[0].getPose(referenceSpace);

    const isWallHit = hitPose && hitPose.transform.matrix[5] < 0.25;

    if (!hitPose || !isWallHit) {
      // Use previous drag plane if no hit plane is found
      const targetRayPose = frame.getPose(hitResult.inputSource.targetRaySpace, view3d.renderer.threeRenderer.xr.getReferenceSpace());
      const fingerDir = new THREE.Vector3().copy(targetRayPose.transform.position).sub(camPos).normalize();

      const fingerRay = new THREE.Ray(camPos, fingerDir);
      const intersection = fingerRay.intersectPlane(dragPlane, new THREE.Vector3());

      if (intersection) {
        this.wallPosition.copy(intersection.clone().sub(dragPlane.normal.clone().multiplyScalar(modelZOffset)));
        this.position.copy(intersection);
      }
      return;
    }

    const hitMatrix = new THREE.Matrix4().fromArray(hitPose.transform.matrix);
    const hitOrientation = new THREE.Quaternion().copy(hitPose.transform.orientation);
    const hitPosition = new THREE.Vector3().setFromMatrixPosition(hitMatrix);
    const worldYAxis = new THREE.Vector3(0, 1, 0);
    /*
     * ^ wallU
     * |
     * ⨀---> wallV
     * wallNormal
     */
    const wallNormal = new THREE.Vector3(0, 1, 0).applyQuaternion(hitOrientation).normalize();
    const wallU = new THREE.Vector3().crossVectors(worldYAxis, wallNormal);
    const wallV = wallNormal.clone().applyAxisAngle(wallU, -Math.PI / 2);

    // Reconstruct wall matrix with prev Y(normal) direction as Z axis
    const wallMatrix = new THREE.Matrix4().makeBasis(wallU, wallV, wallNormal);
    const modelPosition = hitPosition.clone().add(wallNormal.clone().multiplyScalar(modelZOffset));

    // Update position
    this.position.copy(modelPosition);
    this.wallPosition.copy(hitPosition);

    // Update rotation if it differs more than 10deg
    const prevWallNormal = new THREE.Vector3(0, 1, 0).applyQuaternion(this.hitRotation).normalize();
    if (Math.acos(Math.abs(prevWallNormal.dot(wallNormal))) >= Math.PI / 18) {
      const prevWallRotation = this.wallRotation.clone();
      const wallRotation = new THREE.Quaternion().setFromRotationMatrix(wallMatrix);
      const rotationDiff = prevWallRotation.inverse().premultiply(wallRotation);

      modelRoot.quaternion.premultiply(rotationDiff);
      this.wallRotation.copy(wallRotation);
      this.hitRotation.copy(hitOrientation);

      this._arrowIndicator.updateRotation(modelRoot.quaternion);

      // Update drag plane
      dragPlane.set(wallNormal, -modelPosition.dot(wallNormal));
    }
  }


public update({ model }: XRRenderContext, delta: number) {
    model.scene.position.copy(this.position);
    this._arrowIndicator.updatePosition(this.position);
    model.scene.updateMatrix();
  }


() => {
  before(() => {
    cy.Login();
    cy.visit('/');
    cy.createProject(testName);
    cy.createNAD(testName);
    cy.createDataVolume(pvcTemplate.pvcName, pvcTemplate.pvcNS);
  });

  after(() => {
    [urlTemplate, registryTemplate, pvcTemplate, pxeTemplate].forEach((data) => {
      cy.deleteResource(K8S_KIND.Template, data.name, data.namespace);
    });

    cy.deleteResource(K8S_KIND.NAD, NAD_NAME, testName);
    cy.deleteTestProject(testName);
  });

  [urlTemplate, registryTemplate, pvcTemplate, pxeTemplate].forEach((data) => {
    it(`${data.description}`, () => {
      cy.visitVMTemplatesList();
      virtualization.templates.createTemplateFromWizard(data);
      const vmt: Template = { name: data.name };
      const vmData: VirtualMachineData = {
        name: `vm-from-${data.name}`,
        template: vmt,
        sourceAvailable: true,
      };
      cy.visitVMsList();
      vm.create(vmData);
      vm.delete();
    });
  });
}


() => {
    cy.Login();
    cy.visit('/');
    cy.createProject(testName);
    cy.createNAD(testName);
    cy.createDataVolume(pvcTemplate.pvcName, pvcTemplate.pvcNS);
  }


() => {
    [urlTemplate, registryTemplate, pvcTemplate, pxeTemplate].forEach((data) => {
      cy.deleteResource(K8S_KIND.Template, data.name, data.namespace);
    });

    cy.deleteResource(K8S_KIND.NAD, NAD_NAME, testName);
    cy.deleteTestProject(testName);
  }


(data) => {
      cy.deleteResource(K8S_KIND.Template, data.name, data.namespace);
    }


(data) => {
    it(`${data.description}`, () => {
      cy.visitVMTemplatesList();
      virtualization.templates.createTemplateFromWizard(data);
      const vmt: Template = { name: data.name };
      const vmData: VirtualMachineData = {
        name: `vm-from-${data.name}`,
        template: vmt,
        sourceAvailable: true,
      };
      cy.visitVMsList();
      vm.create(vmData);
      vm.delete();
    });
  }


() => {
      cy.visitVMTemplatesList();
      virtualization.templates.createTemplateFromWizard(data);
      const vmt: Template = { name: data.name };
      const vmData: VirtualMachineData = {
        name: `vm-from-${data.name}`,
        template: vmt,
        sourceAvailable: true,
      };
      cy.visitVMsList();
      vm.create(vmData);
      vm.delete();
    }


m => {
      this.userName = m['userName'];
    }


@Component({
  templateUrl: './live-video.component.html',
  styleUrls: ['./live-video.component.css']
})
export class PatientLiveVideoComponent implements OnInit {
  public form: any;
  public userName:string;
  public role=0;
  public isMeet= false;

  // setup your signature endpoint here: https://github.com/zoom/websdk-sample-signature-node.js

  constructor(public fb: FormBuilder, public router:Router,public activatedRoute:ActivatedRoute) {
    this.activatedRoute.queryParams.subscribe(m => {
      this.userName = m['userName'];
    });
  }

  ngOnInit() {
   this.isMeet=true;
  }

}


ngOnInit() {
   this.isMeet=true;
  }


@Component({
  selector: 'home',
  templateUrl: `./homeComponent.html`,

})
export class HomeComponent {

}


(props: FrameProps) => {
  const { label, value, onClick, error, errorMsg } = props;
  const listClass = classnames({
    'list-default': true,
    'list-error': error,
  })
  const lableClass = classnames({
    'material-design-lable': true,
    'material-design-lable-focus': value
  })
  const listClick =(e) => {
    if(onClick) onClick(e)
  }
  return (
    <View className="list-item-com">
      <View className={listClass} onClick={listClick}>
        <Text className={lableClass}>{label}</Text>
        {value&&<Text className="list-value">{value}</Text>}
        <View className="down-icon">
          <AtIcon prefixClass='iconfont' value='down' size='16' color='#BEC1D9'/>
        </View>
      </View>
      {
        error&&<View className="error-msg">
          <Icon size='12' type='warn' color='#FF7800' />
          <Text className="error-des">{errorMsg}</Text>
        </View>
      }
    </View>


(e) => {
    if(onClick) onClick(e)
  }


type FrameProps = {
  label?: string;
  value?: string;
  error?: boolean;
  errorMsg?: string;
  onClick?: (any)=>void;
}


interface PhoneNumberInfo {

    /**
     * Internal identifier of a phone number
     */
    id?: string;

    /**
     * Brief information on a phone number country
     */
    country?: CountryInfo;

    /**
     * Information on the extension, to which the phone number is assigned. Returned only for the request of Account phone number list
     */
    extension?: ExtensionInfo;

    /**
     * Custom user name of a phone number, if any. Supported for numbers assigned to Auto-Receptionist, with usage type 'CompanyNumber'
     */
    label?: string;

    /**
     * Location (City, State). Filled for local US numbers
     */
    location?: string;

    /**
     * Payment type. 'External' is returned for forwarded numbers which are not terminated in the RingCentral phone system
     */
    paymentType?: "External" | "TollFree" | "Local";

    /**
     * Phone number
     */
    phoneNumber?: string;

    /**
     * Status of a phone number. If the value is 'Normal', the phone number is ready to be used. Otherwise it is an external number not yet ported to RingCentral
     */
    status?: string;

    /**
     * Phone number type
     */
    type?: "VoiceFax" | "FaxOnly" | "VoiceOnly";

    /**
     * Usage type of the phone number
     */
    usageType?: "MainCompanyNumber" | "AdditionalCompanyNumber" | "CompanyNumber" | "DirectNumber" | "CompanyFaxNumber" | "ForwardedNumber" | "ForwardedCompanyNumber" | "ContactCenterNumber";
}


() => {
    beforeEach(() => {
      cy.setup();
      cy.login();
    });
    it("Can create, update, delete a policy successfully, and turn on failing policies webhook", () => {
      cy.visit("/policies/manage");
      cy.get(".manage-policies-page__description")
        .should("contain", /add policies/i)
        .and("contain", /manage automations/i); // Ensure page load

      // Add a policy
      cy.findByText(/add a policy/i).click();
      cy.findByText(/create your own policy/i).click();

      cy.get(".ace_scroller")
        .click({ force: true })
        .type(
          "{selectall}SELECT 1 FROM users WHERE username = 'backup' LIMIT 1;"
        );

      cy.findByRole("button", { name: /save policy/i }).click();

      // save modal
      cy.get(".policy-form__policy-save-modal-name")
        .click()
        .type("Does the device have a user named 'backup'?");

      cy.get(".policy-form__policy-save-modal-description")
        .click()
        .type("Returns yes or no for having a user named 'backup'");

      cy.get(".policy-form__policy-save-modal-resolution")
        .click()
        .type("Create a user named 'backup'");

      cy.findByRole("button", { name: /^Save$/ }).click();

      // Confirm that policy was added successfully
      cy.findByText(/policy created/i).should("exist");

      cy.visit("/policies/manage");
      cy.get(".manage-policies-page__description").should(
        "contain",
        /add policies/i
      ); // Ensure page load

      // Add a default policy
      cy.findByText(/add a policy/i).click();

      cy.findByText(/gatekeeper enabled/i).click();
      cy.findByRole("button", { name: /save policy/i }).click();
      cy.findByRole("button", { name: /^Save$/ }).click();

      // Confirm that policy was added successfully
      cy.findByText(/policy created/i).should("exist");

      cy.visit("/policies/manage");

      // Click on link in table and confirm that policies filter block diplays as expected on manage hosts page
      cy.getAttached(".failing_host_count__cell")
        .first()
        .within(() => {
          cy.findByRole("button", { name: /0 hosts/i }).click();
        });

      // confirm policy functionality on manage host page
      cy.get(".manage-hosts__policies-filter-block").within(() => {
        cy.findByText(/user named 'backup'/i).should("exist");
        cy.findByText(/no/i).should("exist").click();
        cy.findByText(/yes/i).should("exist");
        cy.get('img[alt="Remove policy filter"]').click();
        cy.findByText(/user named 'backup'/i).should("not.exist");
      });

      // Click on policies tab to return to manage policies page
      cy.get(".site-nav-container").within(() => {
        cy.findByText(/policies/i).click();
      });

      // Update policy
      cy.findByText(/gatekeeper enabled/i).click();

      cy.get(".ace_scroller")
        .click({ force: true })
        .type(
          "{selectall}SELECT 1 FROM gatekeeper WHERE assessments_enabled = 1;"
        );

      cy.findByRole("button", { name: /^Save$/ }).click();

      // Confirm that policy was added successfully
      cy.findByText(/policy updated/i).should("exist");

      // Delete policy
      cy.visit("/policies/manage");

      cy.getAttached("tbody").within(() => {
        cy.get("tr")
          .first()
          .within(() => {
            cy.get(".fleet-checkbox__input").check({ force: true });
          });
      });
      cy.findByRole("button", { name: /delete/i }).click();
      cy.get(".remove-policies-modal").within(() => {
        cy.findByRole("button", { name: /cancel/i }).should("exist");
        cy.findByRole("button", { name: /delete/i }).click();
      });
      cy.findByText(/removed policy/i).should("exist");
      cy.findByText(/backup/i).should("not.exist");

      // Create failing policies webhook
      cy.findByRole("button", { name: /manage automations/i }).click();
      cy.getAttached(".manage-automations-modal").within(() => {
        cy.get(".fleet-checkbox__input").check({ force: true });
      });
      cy.get("#webhook-url").click().type("www.foo.com/bar");
      cy.findByRole("button", { name: /^Save$/ }).click();

      // Confirm that failing policies webhook was added successfully
      cy.findByText(/updated policy automations/i).should("exist");
      cy.findByRole("button", { name: /manage automations/i }).click();
      cy.getAttached(".manage-automations-modal").within(() => {
        cy.get(".fleet-checkbox__input").should("be.checked");
      });
    });
  }


() => {
      cy.setup();
      cy.login();
    }


() => {
      cy.visit("/policies/manage");
      cy.get(".manage-policies-page__description")
        .should("contain", /add policies/i)
        .and("contain", /manage automations/i); // Ensure page load

      // Add a policy
      cy.findByText(/add a policy/i).click();
      cy.findByText(/create your own policy/i).click();

      cy.get(".ace_scroller")
        .click({ force: true })
        .type(
          "{selectall}SELECT 1 FROM users WHERE username = 'backup' LIMIT 1;"
        );

      cy.findByRole("button", { name: /save policy/i }).click();

      // save modal
      cy.get(".policy-form__policy-save-modal-name")
        .click()
        .type("Does the device have a user named 'backup'?");

      cy.get(".policy-form__policy-save-modal-description")
        .click()
        .type("Returns yes or no for having a user named 'backup'");

      cy.get(".policy-form__policy-save-modal-resolution")
        .click()
        .type("Create a user named 'backup'");

      cy.findByRole("button", { name: /^Save$/ }).click();

      // Confirm that policy was added successfully
      cy.findByText(/policy created/i).should("exist");

      cy.visit("/policies/manage");
      cy.get(".manage-policies-page__description").should(
        "contain",
        /add policies/i
      ); // Ensure page load

      // Add a default policy
      cy.findByText(/add a policy/i).click();

      cy.findByText(/gatekeeper enabled/i).click();
      cy.findByRole("button", { name: /save policy/i }).click();
      cy.findByRole("button", { name: /^Save$/ }).click();

      // Confirm that policy was added successfully
      cy.findByText(/policy created/i).should("exist");

      cy.visit("/policies/manage");

      // Click on link in table and confirm that policies filter block diplays as expected on manage hosts page
      cy.getAttached(".failing_host_count__cell")
        .first()
        .within(() => {
          cy.findByRole("button", { name: /0 hosts/i }).click();
        });

      // confirm policy functionality on manage host page
      cy.get(".manage-hosts__policies-filter-block").within(() => {
        cy.findByText(/user named 'backup'/i).should("exist");
        cy.findByText(/no/i).should("exist").click();
        cy.findByText(/yes/i).should("exist");
        cy.get('img[alt="Remove policy filter"]').click();
        cy.findByText(/user named 'backup'/i).should("not.exist");
      });

      // Click on policies tab to return to manage policies page
      cy.get(".site-nav-container").within(() => {
        cy.findByText(/policies/i).click();
      });

      // Update policy
      cy.findByText(/gatekeeper enabled/i).click();

      cy.get(".ace_scroller")
        .click({ force: true })
        .type(
          "{selectall}SELECT 1 FROM gatekeeper WHERE assessments_enabled = 1;"
        );

      cy.findByRole("button", { name: /^Save$/ }).click();

      // Confirm that policy was added successfully
      cy.findByText(/policy updated/i).should("exist");

      // Delete policy
      cy.visit("/policies/manage");

      cy.getAttached("tbody").within(() => {
        cy.get("tr")
          .first()
          .within(() => {
            cy.get(".fleet-checkbox__input").check({ force: true });
          });
      });
      cy.findByRole("button", { name: /delete/i }).click();
      cy.get(".remove-policies-modal").within(() => {
        cy.findByRole("button", { name: /cancel/i }).should("exist");
        cy.findByRole("button", { name: /delete/i }).click();
      });
      cy.findByText(/removed policy/i).should("exist");
      cy.findByText(/backup/i).should("not.exist");

      // Create failing policies webhook
      cy.findByRole("button", { name: /manage automations/i }).click();
      cy.getAttached(".manage-automations-modal").within(() => {
        cy.get(".fleet-checkbox__input").check({ force: true });
      });
      cy.get("#webhook-url").click().type("www.foo.com/bar");
      cy.findByRole("button", { name: /^Save$/ }).click();

      // Confirm that failing policies webhook was added successfully
      cy.findByText(/updated policy automations/i).should("exist");
      cy.findByRole("button", { name: /manage automations/i }).click();
      cy.getAttached(".manage-automations-modal").within(() => {
        cy.get(".fleet-checkbox__input").should("be.checked");
      });
    }


() => {
          cy.findByRole("button", { name: /0 hosts/i }).click();
        }


() => {
        cy.findByText(/user named 'backup'/i).should("exist");
        cy.findByText(/no/i).should("exist").click();
        cy.findByText(/yes/i).should("exist");
        cy.get('img[alt="Remove policy filter"]').click();
        cy.findByText(/user named 'backup'/i).should("not.exist");
      }


() => {
        cy.findByText(/policies/i).click();
      }


() => {
        cy.get("tr")
          .first()
          .within(() => {
            cy.get(".fleet-checkbox__input").check({ force: true });
          });
      }


() => {
            cy.get(".fleet-checkbox__input").check({ force: true });
          }


() => {
        cy.findByRole("button", { name: /cancel/i }).should("exist");
        cy.findByRole("button", { name: /delete/i }).click();
      }


() => {
        cy.get(".fleet-checkbox__input").check({ force: true });
      }


() => {
        cy.get(".fleet-checkbox__input").should("be.checked");
      }


class ChartBuildQueryRegistry extends Registry<BuildQuery> {
  constructor() {
    super({ name: 'ChartBuildQuery', overwritePolicy: OverwritePolicy.WARN });
  }
}


// Ideally this would be <T extends QueryFormData>
type BuildQuery = (formData: any) => QueryContext;


() => {
    let page: ExplorePage;

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

    it('should display message saying app works', () => {
	page.navigateTo();
	// expect(page.getParagraphText()).toEqual('app works!');
    });
}


() => {
	page = new ExplorePage();
    }


() => {
	page.navigateTo();
	// expect(page.getParagraphText()).toEqual('app works!');
    }


async (graph, module) => getDependantTasksForModule({
        garden,
        graph,
        log,
        module,
        fromWatch: false,
        hotReloadServiceNames,
        force: opts.force,
        forceBuild: opts["force-build"],
      })


async (graph, module) => {
        const tasks: BaseTask[] = await getDependantTasksForModule({
          garden, graph, log, module, hotReloadServiceNames, force: true, forceBuild: opts["force-build"],
          fromWatch: true, includeDependants: true,
        })

        const hotReloadServices = await graph.getServices(hotReloadServiceNames)
        const hotReloadTasks = hotReloadServices
          .filter(service => service.module.name === module.name || service.sourceModule.name === module.name)
          .map(service => new HotReloadTask({ garden, graph, log, service, force: true }))

        tasks.push(...hotReloadTasks)

        return tasks
      }


service => service.module.name === module.name || service.sourceModule.name === module.name


service => new HotReloadTask({ garden, graph, log, service, force: true })


export class DeployCommand extends Command<Args, Opts> {
  name = "deploy"
  help = "Deploy service(s) to your environment."

  description = dedent`
    Deploys all or specified services, taking into account service dependency order.
    Also builds modules and dependencies if needed.

    Optionally stays running and automatically re-builds and re-deploys services if their module source
    (or their dependencies' sources) change.

    Examples:

        garden deploy                      # deploy all modules in the project
        garden deploy my-service           # only deploy my-service
        garden deploy service-a,service-b  # only deploy service-a and service-b
        garden deploy --force              # force re-deploy of modules, even if they're already deployed
        garden deploy --watch              # watch for changes to code
        garden deploy --hot=my-service     # deploys all services, with hot reloading enabled for my-service
        garden deploy --hot=*              # deploys all compatible services with hot reloading enabled
        garden deploy --env stage          # deploy your services to an environment called stage
  `

  arguments = deployArgs
  options = deployOpts

  private server: GardenServer

  async prepare({ headerLog, footerLog, opts }: PrepareParams<Args, Opts>) {
    printHeader(headerLog, "Deploy", "rocket")

    if (!!opts.watch || !!opts["hot-reload"]) {
      this.server = await startServer(footerLog)
    }
  }

  async action({ garden, log, footerLog, args, opts }: CommandParams<Args, Opts>): Promise<CommandResult<TaskResults>> {
    if (this.server) {
      this.server.setGarden(garden)
    }

    const initGraph = await garden.getConfigGraph()
    const services = await initGraph.getServices(args.services)

    if (services.length === 0) {
      log.error({ msg: "No services found. Aborting." })
      return { result: {} }
    }

    const hotReloadServiceNames = await getHotReloadServiceNames(opts["hot-reload"], initGraph)
    let watch: boolean

    if (hotReloadServiceNames.length > 0) {
      await initGraph.getServices(hotReloadServiceNames) // validate the existence of these services
      const errMsg = await validateHotReloadServiceNames(hotReloadServiceNames, initGraph)
      if (errMsg) {
        log.error({ msg: errMsg })
        return { result: {} }
      }
      watch = true
    } else {
      watch = opts.watch
    }

    // TODO: make this a task
    const actions = await garden.getActionHelper()
    await actions.prepareEnvironment({ log })

    const results = await processServices({
      garden,
      graph: initGraph,
      log,
      footerLog,
      services,
      watch,
      handler: async (graph, module) => getDependantTasksForModule({
        garden,
        graph,
        log,
        module,
        fromWatch: false,
        hotReloadServiceNames,
        force: opts.force,
        forceBuild: opts["force-build"],
      }),
      changeHandler: async (graph, module) => {
        const tasks: BaseTask[] = await getDependantTasksForModule({
          garden, graph, log, module, hotReloadServiceNames, force: true, forceBuild: opts["force-build"],
          fromWatch: true, includeDependants: true,
        })

        const hotReloadServices = await graph.getServices(hotReloadServiceNames)
        const hotReloadTasks = hotReloadServices
          .filter(service => service.module.name === module.name || service.sourceModule.name === module.name)
          .map(service => new HotReloadTask({ garden, graph, log, service, force: true }))

        tasks.push(...hotReloadTasks)

        return tasks
      },
    })

    return handleTaskResults(footerLog, "deploy", results)
  }
}


type Args = typeof deployArgs


type Opts = typeof deployOpts


async prepare({ headerLog, footerLog, opts }: PrepareParams<Args, Opts>) {
    printHeader(headerLog, "Deploy", "rocket")

    if (!!opts.watch || !!opts["hot-reload"]) {
      this.server = await startServer(footerLog)
    }
  }


async action({ garden, log, footerLog, args, opts }: CommandParams<Args, Opts>): Promise<CommandResult<TaskResults>> {
    if (this.server) {
      this.server.setGarden(garden)
    }

    const initGraph = await garden.getConfigGraph()
    const services = await initGraph.getServices(args.services)

    if (services.length === 0) {
      log.error({ msg: "No services found. Aborting." })
      return { result: {} }
    }

    const hotReloadServiceNames = await getHotReloadServiceNames(opts["hot-reload"], initGraph)
    let watch: boolean

    if (hotReloadServiceNames.length > 0) {
      await initGraph.getServices(hotReloadServiceNames) // validate the existence of these services
      const errMsg = await validateHotReloadServiceNames(hotReloadServiceNames, initGraph)
      if (errMsg) {
        log.error({ msg: errMsg })
        return { result: {} }
      }
      watch = true
    } else {
      watch = opts.watch
    }

    // TODO: make this a task
    const actions = await garden.getActionHelper()
    await actions.prepareEnvironment({ log })

    const results = await processServices({
      garden,
      graph: initGraph,
      log,
      footerLog,
      services,
      watch,
      handler: async (graph, module) => getDependantTasksForModule({
        garden,
        graph,
        log,
        module,
        fromWatch: false,
        hotReloadServiceNames,
        force: opts.force,
        forceBuild: opts["force-build"],
      }),
      changeHandler: async (graph, module) => {
        const tasks: BaseTask[] = await getDependantTasksForModule({
          garden, graph, log, module, hotReloadServiceNames, force: true, forceBuild: opts["force-build"],
          fromWatch: true, includeDependants: true,
        })

        const hotReloadServices = await graph.getServices(hotReloadServiceNames)
        const hotReloadTasks = hotReloadServices
          .filter(service => service.module.name === module.name || service.sourceModule.name === module.name)
          .map(service => new HotReloadTask({ garden, graph, log, service, force: true }))

        tasks.push(...hotReloadTasks)

        return tasks
      },
    })

    return handleTaskResults(footerLog, "deploy", results)
  }


export class ThreadNode {
  name: string;
  threadlevel?: string;
  totalTimeConsumption?: number;
  blocked: number;
  running: number;
  waiting: number;
  icon?: DtIconType;
  children?: ThreadNode[];
  isShowMore?: boolean;
}


export class ThreadFlatNode {
  name: string;
  threadlevel?: string;
  totalTimeConsumption?: number;
  blocked: number;
  running: number;
  waiting: number;
  icon?: DtIconType;
  level: number;
  expandable: boolean;
  isShowMore?: boolean;
}


export interface FillOperationParams {
    /** The mouse event for the fill operation. */
    event: MouseEvent;
    /** The values that have been processed by the fill operation. */
    values: any[];
    /** The RowNode of the current cell being changed. */
    rowNode: RowNode;
    /** The Column of the current cell being changed. */
    column: Column;
    /** The values that were present before processing started. */
    initialValues: any[];
    /** The index of the current processed value. */
    currentIndex: number;
    /** The value of the cell being currently processed by the Fill Operation. */
    currentCellValue: any;
    /** The direction of the Fill Operation. */
    direction: 'up' | 'down' | 'left' | 'right';
    api: GridApi;
    columnApi: ColumnApi;
    /** The context as provided on `gridOptions.context` */
    context: any;
}


export interface GetDataPath {
    (data: any): string[];
}


export interface IsServerSideGroup {
    (dataItem: any): boolean;
}


export interface IsApplyServerSideTransaction {
    (params: IsApplyServerSideTransactionParams): boolean;
}


export interface IsApplyServerSideTransactionParams {
    /** The transaction getting applied. */
    transaction: ServerSideTransaction;
    /** The parent RowNode, if transaction is applied to a group. */
    parentNode: RowNode;
    //** Store info, if any, as passed via the success() callback when loading data. */
    storeInfo: any;
}


export interface GetServerSideGroupKey {
    (dataItem: any): string;
}


export interface IsRowMaster {
    (dataItem: any): boolean;
}


export interface IsRowSelectable {
    (node: RowNode): boolean;
}


export interface RowClassRules {
    [cssClassName: string]: (((params: RowClassParams) => boolean) | string);
}


export interface RowStyle { [cssProperty: string]: string | number; }


export interface RowClassParams {
    /** The data associated with this row from rowData */
    data: any;
    /** The RowNode associated with this row */
    node: RowNode;
    /** The index of the row */
    rowIndex: number;
    /** If using AngularJs, is the row's child scope, otherwise null */
    $scope: any;
    api: GridApi;
    columnApi: ColumnApi;
    /** The context as provided on `gridOptions.context` */
    context: any;
}


export interface RowHeightParams {
    data: any;
    node: RowNode;
    api: GridApi;
    /** The context as provided on `gridOptions.context` */
    context: any;
}


export interface SendToClipboardParams {
    data: string;
}


export interface GetContextMenuItemsParams {
    /** Names of the items that would be provided by default. */
    defaultItems: string[] | undefined;
    /** The column, if a cell was clicked, otherwise null. */
    column: Column | null;
    /** The row node, if a cell was clicked, otherwise null. */
    node: RowNode | null;
    /** The value, if a cell was clicked, otherwise null.  */
    value: any;
    api: GridApi;
    columnApi: ColumnApi;
    /** The context as provided on `gridOptions.context` */
    context: any;
}


export interface GetContextMenuItems {
    (params: GetContextMenuItemsParams): (string | MenuItemDef)[];
}


export interface GetChartToolbarItemsParams {
    defaultItems?: ChartMenuOptions[];
    api: GridApi;
    columnApi: ColumnApi;
}


export interface GetChartToolbarItems {
    (params: GetChartToolbarItemsParams): ChartMenuOptions[];
}


export interface MenuItemDef {
    /** Name of the menu item */
    name: string;
    /** It the item should be enabled / disabled */
    disabled?: boolean;
    /** Shortcut (just display text, saying the shortcut here does nothing) */
    shortcut?: string;
    /** Function that gets executed when item is chosen */
    action?: () => void;
    /** Set to true to provide a check beside the option */
    checked?: boolean;
    /** The icon to display, either a DOM element or HTML string */
    icon?: HTMLElement | string;
    /** If this item is a sub menu, contains a list of menu item definitions */
    subMenu?: (MenuItemDef | string)[] | IComponent<any>;
    /** CSS classes to apply to the menu item */
    cssClasses?: string[];
    /** Tooltip for the menu item */
    tooltip?: string;
}


export interface GetMainMenuItemsParams {
    /** The column that was clicked */
    column: Column;
    /** List of the items that would be displayed by default */
    defaultItems: string[];
    api: GridApi;
    columnApi: ColumnApi;
    /** The context as provided on `gridOptions.context` */
    context: any;
}


export interface GetMainMenuItems {
    (params: GetMainMenuItemsParams): (string | MenuItemDef)[];
}


export interface GetRowNodeIdFunc {
    (data: any): string;
}


export interface ProcessRowParams {
    eRow: HTMLElement;
    ePinnedLeftRow: HTMLElement;
    ePinnedRightRow: HTMLElement;
    rowIndex: number;
    node: RowNode;
    api: GridApi;
    columnApi: ColumnApi;
    addRenderedRowListener: (eventType: string, listener: Function) => void;
    /** The context as provided on `gridOptions.context` */
    context: any;
}


export interface NavigateToNextHeaderParams {
    /** The key for the arrow key pressed,
     *  left = 'ArrowLeft', up = 'ArrowUp', right = 'ArrowRight', down = 'ArrowDown' */
    key: string;
    /** The header that currently has focus */
    previousHeaderPosition: HeaderPosition | null;
    /** The header the grid would normally pick as the next header for this navigation */
    nextHeaderPosition: HeaderPosition | null;
    /** The number of header rows present in the grid */
    headerRowCount: number;
    event: KeyboardEvent;
    api: GridApi;
    columnApi: ColumnApi;
}


export interface TabToNextHeaderParams {
    /** True if the Shift key is also down */
    backwards: boolean;
    /** The header that currently has focus */
    previousHeaderPosition: HeaderPosition | null;
    /** The header the grid would normally pick as the next header for this navigation */
    nextHeaderPosition: HeaderPosition | null;
    /** The number of header rows present in the grid */
    headerRowCount: number;
    api: GridApi;
    columnApi: ColumnApi;
}