private reportModifiedSetting(props: { key: string, query: string, searchResults: ISearchResult[] | null, rawResults: ISearchResult[] | null, showConfiguredOnly: boolean, isReset: boolean, settingsTarget: SettingsTarget }): void {
		this.pendingSettingUpdate = null;

		let groupId: string | undefined = undefined;
		let nlpIndex: number | undefined = undefined;
		let displayIndex: number | undefined = undefined;
		if (props.searchResults) {
			const remoteResult = props.searchResults[SearchResultIdx.Remote];
			const localResult = props.searchResults[SearchResultIdx.Local];

			const localIndex = localResult!.filterMatches.findIndex(m => m.setting.key === props.key);
			groupId = localIndex >= 0 ?
				'local' :
				'remote';

			displayIndex = localIndex >= 0 ?
				localIndex :
				remoteResult && (remoteResult.filterMatches.findIndex(m => m.setting.key === props.key) + localResult.filterMatches.length);

			if (this.searchResultModel) {
				const rawResults = this.searchResultModel.getRawResults();
				if (rawResults[SearchResultIdx.Remote]) {
					const _nlpIndex = rawResults[SearchResultIdx.Remote].filterMatches.findIndex(m => m.setting.key === props.key);
					nlpIndex = _nlpIndex >= 0 ? _nlpIndex : undefined;
				}
			}
		}

		const reportedTarget = props.settingsTarget === ConfigurationTarget.USER_LOCAL ? 'user' :
			props.settingsTarget === ConfigurationTarget.USER_REMOTE ? 'user_remote' :
				props.settingsTarget === ConfigurationTarget.WORKSPACE ? 'workspace' :
					'folder';

		const data = {
			key: props.key,
			query: props.query,
			groupId,
			nlpIndex,
			displayIndex,
			showConfiguredOnly: props.showConfiguredOnly,
			isReset: props.isReset,
			target: reportedTarget
		};

		/* __GDPR__
			"settingsEditor.settingModified" : {
				"key" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
				"query" : { "classification": "CustomerContent", "purpose": "FeatureInsight" },
				"groupId" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
				"nlpIndex" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
				"displayIndex" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
				"showConfiguredOnly" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
				"isReset" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
				"target" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
			}
		*/
		this.telemetryService.publicLog('settingsEditor.settingModified', data);
	}


private render(token: CancellationToken): Promise<any> {
		if (this.input) {
			return this.input.resolve()
				.then((model: IEditorModel | null) => {
					if (token.isCancellationRequested || !(model instanceof Settings2EditorModel)) {
						return undefined;
					}

					this._register(model.onDidChangeGroups(() => {
						this.updatedConfigSchemaDelayer.trigger(() => {
							this.onConfigUpdate(undefined, undefined, true);
						});
					}));
					this.defaultSettingsEditorModel = model;
					return this.onConfigUpdate(undefined, true);
				});
		}
		return Promise.resolve(null);
	}


private onSearchModeToggled(): void {
		this.rootElement.classList.remove('no-toc-search');
		if (this.configurationService.getValue('workbench.settings.settingsSearchTocBehavior') === 'hide') {
			this.rootElement.classList.toggle('no-toc-search', !!this.searchResultModel);
		}
	}


private scheduleRefresh(element: HTMLElement, key = ''): void {
		if (key && this.scheduledRefreshes.has(key)) {
			return;
		}

		if (!key) {
			this.scheduledRefreshes.forEach(r => r.dispose());
			this.scheduledRefreshes.clear();
		}

		const scheduledRefreshTracker = DOM.trackFocus(element);
		this.scheduledRefreshes.set(key, scheduledRefreshTracker);
		scheduledRefreshTracker.onDidBlur(() => {
			scheduledRefreshTracker.dispose();
			this.scheduledRefreshes.delete(key);
			this.onConfigUpdate([key]);
		});
	}


private async onConfigUpdate(keys?: string[], forceRefresh = false, schemaChange = false): Promise<void> {
		if (keys && this.settingsTreeModel) {
			return this.updateElementsByKey(keys);
		}

		const groups = this.defaultSettingsEditorModel.settingsGroups.slice(1); // Without commonlyUsed
		const dividedGroups = collections.groupBy(groups, g => g.extensionInfo ? 'extension' : 'core');
		const settingsResult = resolveSettingsTree(tocData, dividedGroups.core, this.logService);
		const resolvedSettingsRoot = settingsResult.tree;

		// Warn for settings not included in layout
		if (settingsResult.leftoverSettings.size && !this.hasWarnedMissingSettings) {
			const settingKeyList: string[] = [];
			settingsResult.leftoverSettings.forEach(s => {
				settingKeyList.push(s.key);
			});

			this.logService.warn(`SettingsEditor2: Settings not included in settingsLayout.ts: ${settingKeyList.join(', ')}`);
			this.hasWarnedMissingSettings = true;
		}

		const commonlyUsed = resolveSettingsTree(commonlyUsedData, dividedGroups.core, this.logService);
		resolvedSettingsRoot.children!.unshift(commonlyUsed.tree);

		resolvedSettingsRoot.children!.push(resolveExtensionsSettings(dividedGroups.extension || []));

		if (this.searchResultModel) {
			this.searchResultModel.updateChildren();
		}

		if (this.settingsTreeModel) {
			this.settingsTreeModel.update(resolvedSettingsRoot);

			if (schemaChange && !!this.searchResultModel) {
				// If an extension's settings were just loaded and a search is active, retrigger the search so it shows up
				return await this.onSearchInputChanged();
			}

			this.refreshTOCTree();
			this.renderTree(undefined, forceRefresh);
		} else {
			this.settingsTreeModel = this.instantiationService.createInstance(SettingsTreeModel, this.viewState);
			this.settingsTreeModel.update(resolvedSettingsRoot);
			this.tocTreeModel.settingsTreeRoot = this.settingsTreeModel.root as SettingsTreeGroupElement;

			const cachedState = this.restoreCachedState();
			if (cachedState && cachedState.searchQuery) {
				await this.onSearchInputChanged();
			} else {
				this.refreshTOCTree();
				this.refreshTree();
				this.tocTree.collapseAll();
			}
		}
	}


private updateElementsByKey(keys: string[]): void {
		if (keys.length) {
			if (this.searchResultModel) {
				keys.forEach(key => this.searchResultModel!.updateElementsByName(key));
			}

			if (this.settingsTreeModel) {
				keys.forEach(key => this.settingsTreeModel.updateElementsByName(key));
			}

			keys.forEach(key => this.renderTree(key));
		} else {
			return this.renderTree();
		}
	}


private getActiveControlInSettingsTree(): HTMLElement | null {
		return (document.activeElement && DOM.isAncestor(document.activeElement, this.settingsTree.getHTMLElement())) ?
			<HTMLElement>document.activeElement :
			null;
	}


private renderTree(key?: string, force = false): void {
		if (!force && key && this.scheduledRefreshes.has(key)) {
			this.updateModifiedLabelForKey(key);
			return;
		}

		// If the context view is focused, delay rendering settings
		if (this.contextViewFocused()) {
			const element = document.querySelector('.context-view');
			if (element) {
				this.scheduleRefresh(element as HTMLElement, key);
			}
			return;
		}

		// If a setting control is currently focused, schedule a refresh for later
		const activeElement = this.getActiveControlInSettingsTree();
		const focusedSetting = activeElement && this.settingRenderers.getSettingDOMElementForDOMElement(activeElement);
		if (focusedSetting && !force) {
			// If a single setting is being refreshed, it's ok to refresh now if that is not the focused setting
			if (key) {
				const focusedKey = focusedSetting.getAttribute(AbstractSettingRenderer.SETTING_KEY_ATTR);
				if (focusedKey === key &&
					// update `list`s live, as they have a separate "submit edit" step built in before this
					(focusedSetting.parentElement && !focusedSetting.parentElement.classList.contains('setting-item-list'))
				) {

					this.updateModifiedLabelForKey(key);
					this.scheduleRefresh(focusedSetting, key);
					return;
				}
			} else {
				this.scheduleRefresh(focusedSetting);
				return;
			}
		}

		this.renderResultCountMessages();

		if (key) {
			const elements = this.currentSettingsModel.getElementsByName(key);
			if (elements && elements.length) {
				// TODO https://github.com/microsoft/vscode/issues/57360
				this.refreshTree();
			} else {
				// Refresh requested for a key that we don't know about
				return;
			}
		} else {
			this.refreshTree();
		}

		return;
	}


private contextViewFocused(): boolean {
		return !!DOM.findParentWithClass(<HTMLElement>document.activeElement, 'context-view');
	}


private refreshTree(): void {
		if (this.isVisible()) {
			this.settingsTree.setChildren(null, createGroupIterator(this.currentSettingsModel.root));
		}
	}


private refreshTOCTree(): void {
		if (this.isVisible()) {
			this.tocTreeModel.update();
			this.tocTree.setChildren(null, createTOCIterator(this.tocTreeModel, this.tocTree));
		}
	}


private updateModifiedLabelForKey(key: string): void {
		const dataElements = this.currentSettingsModel.getElementsByName(key);
		const isModified = dataElements && dataElements[0] && dataElements[0].isConfigured; // all elements are either configured or not
		const elements = this.settingRenderers.getDOMElementsForSettingKey(this.settingsTree.getHTMLElement(), key);
		if (elements && elements[0]) {
			elements[0].classList.toggle('is-configured', !!isModified);
		}
	}


private async onSearchInputChanged(): Promise<void> {
		const query = this.searchWidget.getValue().trim();
		this.delayedFilterLogging.cancel();
		await this.triggerSearch(query.replace(/›/g, ' '));

		if (query && this.searchResultModel) {
			this.delayedFilterLogging.trigger(() => this.reportFilteringUsed(query, this.searchResultModel!.getUniqueResults()));
		}
	}


private parseSettingFromJSON(query: string): string | null {
		const match = query.match(/"([a-zA-Z.]+)": /);
		return match && match[1];
	}


private triggerSearch(query: string): Promise<void> {
		this.viewState.tagFilters = new Set<string>();
		this.viewState.extensionFilters = new Set<string>();
		this.viewState.featureFilters = new Set<string>();
		if (query) {
			const parsedQuery = parseQuery(query);
			query = parsedQuery.query;
			parsedQuery.tags.forEach(tag => this.viewState.tagFilters!.add(tag));
			parsedQuery.extensionFilters.forEach(extensionId => this.viewState.extensionFilters!.add(extensionId));
			parsedQuery.featureFilters!.forEach(feature => this.viewState.featureFilters!.add(feature));
		}

		if (query && query !== '@') {
			query = this.parseSettingFromJSON(query) || query;
			return this.triggerFilterPreferences(query);
		} else {
			if (this.viewState.tagFilters.size || this.viewState.extensionFilters.size || this.viewState.featureFilters.size) {
				this.searchResultModel = this.createFilterModel();
			} else {
				this.searchResultModel = null;
			}

			this.localSearchDelayer.cancel();
			this.remoteSearchThrottle.cancel();
			if (this.searchInProgress) {
				this.searchInProgress.cancel();
				this.searchInProgress.dispose();
				this.searchInProgress = null;
			}

			this.tocTree.setFocus([]);
			this.viewState.filterToCategory = undefined;
			this.tocTreeModel.currentSearchModel = this.searchResultModel;
			this.onSearchModeToggled();

			if (this.searchResultModel) {
				// Added a filter model
				this.tocTree.setSelection([]);
				this.tocTree.expandAll();
				this.refreshTOCTree();
				this.renderResultCountMessages();
				this.refreshTree();
			} else {
				// Leaving search mode
				this.tocTree.collapseAll();
				this.refreshTOCTree();
				this.renderResultCountMessages();
				this.refreshTree();
			}
		}

		return Promise.resolve();
	}


/**
	 * Return a fake SearchResultModel which can hold a flat list of all settings, to be filtered (@modified etc)
	 */
private createFilterModel(): SearchResultModel {
		const filterModel = this.instantiationService.createInstance(SearchResultModel, this.viewState);

		const fullResult: ISearchResult = {
			filterMatches: []
		};
		for (const g of this.defaultSettingsEditorModel.settingsGroups.slice(1)) {
			for (const sect of g.sections) {
				for (const setting of sect.settings) {
					fullResult.filterMatches.push({ setting, matches: [], score: 0 });
				}
			}
		}

		filterModel.setResult(0, fullResult);

		return filterModel;
	}


private reportFilteringUsed(query: string, results: ISearchResult[]): void {
		const nlpResult = results[SearchResultIdx.Remote];
		const nlpMetadata = nlpResult && nlpResult.metadata;

		const durations = {
			nlpResult: nlpMetadata && nlpMetadata.duration
		};

		// Count unique results
		const counts: { nlpResult?: number, filterResult?: number } = {};
		const filterResult = results[SearchResultIdx.Local];
		if (filterResult) {
			counts['filterResult'] = filterResult.filterMatches.length;
		}

		if (nlpResult) {
			counts['nlpResult'] = nlpResult.filterMatches.length;
		}

		const requestCount = nlpMetadata && nlpMetadata.requestCount;

		const data = {
			query,
			durations,
			counts,
			requestCount
		};

		/* __GDPR__
			"settingsEditor.filter" : {
				"query": { "classification": "CustomerContent", "purpose": "FeatureInsight" },
				"durations.nlpResult" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
				"counts.nlpResult" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
				"counts.filterResult" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
				"requestCount" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }
			}
		*/
		this.telemetryService.publicLog('settingsEditor.filter', data);
	}


private triggerFilterPreferences(query: string): Promise<void> {
		if (this.searchInProgress) {
			this.searchInProgress.cancel();
			this.searchInProgress = null;
		}

		// Trigger the local search. If it didn't find an exact match, trigger the remote search.
		const searchInProgress = this.searchInProgress = new CancellationTokenSource();
		return this.localSearchDelayer.trigger(() => {
			if (searchInProgress && !searchInProgress.token.isCancellationRequested) {
				return this.localFilterPreferences(query).then(result => {
					if (result && !result.exactMatch) {
						this.remoteSearchThrottle.trigger(() => {
							return searchInProgress && !searchInProgress.token.isCancellationRequested ?
								this.remoteSearchPreferences(query, this.searchInProgress!.token) :
								Promise.resolve();
						});
					}
				});
			} else {
				return Promise.resolve();
			}
		});
	}


private localFilterPreferences(query: string, token?: CancellationToken): Promise<ISearchResult | null> {
		const localSearchProvider = this.preferencesSearchService.getLocalSearchProvider(query);
		return this.filterOrSearchPreferences(query, SearchResultIdx.Local, localSearchProvider, token);
	}


private remoteSearchPreferences(query: string, token?: CancellationToken): Promise<void> {
		const remoteSearchProvider = this.preferencesSearchService.getRemoteSearchProvider(query);
		const newExtSearchProvider = this.preferencesSearchService.getRemoteSearchProvider(query, true);

		return Promise.all([
			this.filterOrSearchPreferences(query, SearchResultIdx.Remote, remoteSearchProvider, token),
			this.filterOrSearchPreferences(query, SearchResultIdx.NewExtensions, newExtSearchProvider, token)
		]).then(() => { });
	}


private filterOrSearchPreferences(query: string, type: SearchResultIdx, searchProvider?: ISearchProvider, token?: CancellationToken): Promise<ISearchResult | null> {
		return this._filterOrSearchPreferencesModel(query, this.defaultSettingsEditorModel, searchProvider, token).then(result => {
			if (token && token.isCancellationRequested) {
				// Handle cancellation like this because cancellation is lost inside the search provider due to async/await
				return null;
			}

			if (!this.searchResultModel) {
				this.searchResultModel = this.instantiationService.createInstance(SearchResultModel, this.viewState);
				this.searchResultModel.setResult(type, result);
				this.tocTreeModel.currentSearchModel = this.searchResultModel;
				this.onSearchModeToggled();
			} else {
				this.searchResultModel.setResult(type, result);
				this.tocTreeModel.update();
			}

			if (type === SearchResultIdx.Local) {
				this.tocTree.setFocus([]);
				this.viewState.filterToCategory = undefined;
				this.tocTree.expandAll();
			}

			this.refreshTOCTree();
			this.renderTree(undefined, true);
			return result;
		});
	}


private renderResultCountMessages() {
		if (!this.currentSettingsModel) {
			return;
		}

		this.clearFilterLinkContainer.style.display = this.viewState.tagFilters && this.viewState.tagFilters.size > 0
			? 'initial'
			: 'none';

		if (!this.searchResultModel) {
			if (this.countElement.style.display !== 'none') {
				this.searchResultLabel = null;
				this.countElement.style.display = 'none';
				this.layout(this.dimension);
			}

			this.rootElement.classList.remove('no-results');
			return;
		}

		if (this.tocTreeModel && this.tocTreeModel.settingsTreeRoot) {
			const count = this.tocTreeModel.settingsTreeRoot.count;
			let resultString: string;
			switch (count) {
				case 0: resultString = localize('noResults', "No Settings Found"); break;
				case 1: resultString = localize('oneResult', "1 Setting Found"); break;
				default: resultString = localize('moreThanOneResult', "{0} Settings Found", count);
			}

			this.searchResultLabel = resultString;
			this.updateInputAriaLabel();
			this.countElement.innerText = resultString;
			aria.status(resultString);

			if (this.countElement.style.display !== 'block') {
				this.countElement.style.display = 'block';
				this.layout(this.dimension);
			}
			this.rootElement.classList.toggle('no-results', count === 0);
		}
	}


private _filterOrSearchPreferencesModel(filter: string, model: ISettingsEditorModel, provider?: ISearchProvider, token?: CancellationToken): Promise<ISearchResult | null> {
		const searchP = provider ? provider.searchModel(model, token) : Promise.resolve(null);
		return searchP
			.then<ISearchResult, ISearchResult | null>(undefined, err => {
				if (isPromiseCanceledError(err)) {
					return Promise.reject(err);
				} else {
					/* __GDPR__
						"settingsEditor.searchError" : {
							"message": { "classification": "CallstackOrException", "purpose": "FeatureInsight" }
						}
					*/
					const message = getErrorMessage(err).trim();
					if (message && message !== 'Error') {
						// "Error" = any generic network error
						this.telemetryService.publicLogError('settingsEditor.searchError', { message });
						this.logService.info('Setting search error: ' + message);
					}
					return null;
				}
			});
	}


private layoutTrees(dimension: DOM.Dimension): void {
		const listHeight = dimension.height - (72 + 11 /* header height + editor padding */);
		const settingsTreeHeight = listHeight - 14;
		this.settingsTreeContainer.style.height = `${settingsTreeHeight}px`;
		this.settingsTree.layout(settingsTreeHeight, dimension.width);

		const tocTreeHeight = settingsTreeHeight - 1;
		this.tocTreeContainer.style.height = `${tocTreeHeight}px`;
		this.tocTree.layout(tocTreeHeight);
	}


protected saveState(): void {
		if (this.isVisible()) {
			const searchQuery = this.searchWidget.getValue().trim();
			const target = this.settingsTargetsWidget.settingsTarget as SettingsTarget;
			if (this.group && this.input) {
				this.editorMemento.saveEditorState(this.group, this.input, { searchQuery, target });
			}
		}

		super.saveState();
	}


private updateLastSyncedTime(): void {
		const last = this.userDataSyncService.lastSyncTime;
		let label: string;
		if (typeof last === 'number') {
			const d = fromNow(last, true);
			label = localize('lastSyncedLabel', "Last synced: {0}", d);
		} else {
			label = '';
		}

		this.lastSyncedLabel.textContent = label;
		this._onDidChangeLastSyncedLabel.fire(label);
	}


private update(): void {
		if (this.userDataSyncService.status === SyncStatus.Uninitialized) {
			return;
		}

		if (this.userDataAutoSyncEnablementService.isEnabled() || this.userDataSyncService.status !== SyncStatus.Idle) {
			DOM.show(this.lastSyncedLabel);
			DOM.hide(this.turnOnSyncButton.element);
		} else {
			DOM.hide(this.lastSyncedLabel);
			DOM.show(this.turnOnSyncButton.element);
		}
	}


function getDeviceType() : string {
  const ua = navigator.userAgent;
  if (/(tablet|ipad|playbook|silk)|(android(?!.*mobi))/i.test(ua)) {
    return 'tablet';
  }
  if (
    /Mobile|iP(hone|od)|Android|BlackBerry|IEMobile|Kindle|Silk-Accelerated|(hpw|web)OS|Opera M(obi|ini)/.test(
      ua
    )
  ) {
    return 'phone';
  }
  return 'desktop';
}


function getDeviceOS() : object {
    let regex;
    let regexv;
    let match;
    let matches;
    let version;
    const agent = header.join(' ');
    for (const d of os) {
      regex = new RegExp(d.value, 'i');
      match = regex.test(agent);
      if (match) {
          regexv = new RegExp(d.version + '[- \\/:;]?\s?([\\d._]+)', 'i');
          matches = agent.match(regexv);
          version = '';
          if (matches && matches[1]) {
            version = matches[1];
          } else {
              version = '0';
          }
          return {
              name: d.name,
              version
          };
      }
    }
    return { name: 'unknown', version: 0 };
}


@Injectable()
export class StateManager {
  constructor() {
  }

  private prefixKey(key: string): string {
    return `vc-state.${key}`;
  }

  get(key: string): string {
    return localStorage.getItem(this.prefixKey(key));
  }

  getObject<T>(key: string): T {
    return JSON.parse(this.get(key)) as T;
  }

  put(key: string, value: any): void {
    localStorage.setItem(this.prefixKey(key), value);
  }

  putObject(key: string, obj: any): void {
    this.put(key, JSON.stringify(obj));
  }

  remove(key: string): any {
    const value = localStorage.getItem(this.prefixKey(key));
    localStorage.removeItem(this.prefixKey(key));
    return value;
  }

  hasKey(key: string): boolean {
    return localStorage.getItem(this.prefixKey(key)) != null;
  }
}


private prefixKey(key: string): string {
    return `vc-state.${key}`;
  }


get(key: string): string {
    return localStorage.getItem(this.prefixKey(key));
  }


getObject<T>(key: string): T {
    return JSON.parse(this.get(key)) as T;
  }


put(key: string, value: any): void {
    localStorage.setItem(this.prefixKey(key), value);
  }


putObject(key: string, obj: any): void {
    this.put(key, JSON.stringify(obj));
  }


remove(key: string): any {
    const value = localStorage.getItem(this.prefixKey(key));
    localStorage.removeItem(this.prefixKey(key));
    return value;
  }


hasKey(key: string): boolean {
    return localStorage.getItem(this.prefixKey(key)) != null;
  }


@Component({
    selector: 'awscognito-angular2-app',
    templateUrl: './secureHome.html'
    // styleUrls: ['/assets/css/sb-admin.css']
})
export class SecureHomeComponent implements OnInit, LoggedInCallback {

    constructor(public router: Router, public userService: UserLoginService) {
        this.userService.isAuthenticated(this);
        console.log("SecureHomeComponent: constructor");
    }

    ngOnInit() {

    }

    isLoggedIn(message: string, isLoggedIn: boolean) {
        if (!isLoggedIn) {
            this.router.navigate(['/home/login']);
        }
    }
}


ngOnInit() {

    }


isLoggedIn(message: string, isLoggedIn: boolean) {
        if (!isLoggedIn) {
            this.router.navigate(['/home/login']);
        }
    }


(item: any) => {
    this.fire('EvtChange', item)
    this.update(true)
  }


(item: string) => {
    if (item==="always") {
      (this.css = this.css + `.o-card {
        display: flex;
        flex-direction: column;
        box-shadow: 0 1px 2px -2px #00000029, 0 3px 6px #0000001f, 0 5px 12px 4px #00000017;
        transition: all .3s;
        z-index: 1;
      }`)
      return;
    }
    const hovercss = () => {
      switch(this.props.size){
        case 'large':
          (this.css = this.css + `.o-card:hover {
            display: flex;
            flex-direction: column;
            box-shadow: 0 1px 2px -2px #00000029, 0 3px 6px #0000001f, 0 5px 12px 4px #00000017;
            transition: all .3s;
            z-index: 1;
          }`)
          break;
        case 'medium':
          (this.css = this.css + `.o-card-medium:hover {
            display: flex;
            flex-direction: column;
            box-shadow: 0 1px 2px -2px #00000029, 0 3px 6px #0000001f, 0 5px 12px 4px #00000017;
            transition: all .3s;
            z-index: 1;
          }`)
          break;
        case 'small':
          (this.css = this.css + `.o-card-small:hover {
            display: flex;
            flex-direction: column;
            box-shadow: 0 1px 2px -2px #00000029, 0 3px 6px #0000001f, 0 5px 12px 4px #00000017;
            transition: all .3s;
            z-index: 1;
          }`)
          break;
        default:
          (this.css = this.css + `.o-card:hover {
            display: flex;
            flex-direction: column;
            box-shadow: 0 1px 2px -2px #00000029, 0 3px 6px #0000001f, 0 5px 12px 4px #00000017;
            transition: all .3s;
            z-index: 1;
          }`)
      }
    }
    item && hovercss()
  }


() => {
      switch(this.props.size){
        case 'large':
          (this.css = this.css + `.o-card:hover {
            display: flex;
            flex-direction: column;
            box-shadow: 0 1px 2px -2px #00000029, 0 3px 6px #0000001f, 0 5px 12px 4px #00000017;
            transition: all .3s;
            z-index: 1;
          }`)
          break;
        case 'medium':
          (this.css = this.css + `.o-card-medium:hover {
            display: flex;
            flex-direction: column;
            box-shadow: 0 1px 2px -2px #00000029, 0 3px 6px #0000001f, 0 5px 12px 4px #00000017;
            transition: all .3s;
            z-index: 1;
          }`)
          break;
        case 'small':
          (this.css = this.css + `.o-card-small:hover {
            display: flex;
            flex-direction: column;
            box-shadow: 0 1px 2px -2px #00000029, 0 3px 6px #0000001f, 0 5px 12px 4px #00000017;
            transition: all .3s;
            z-index: 1;
          }`)
          break;
        default:
          (this.css = this.css + `.o-card:hover {
            display: flex;
            flex-direction: column;
            box-shadow: 0 1px 2px -2px #00000029, 0 3px 6px #0000001f, 0 5px 12px 4px #00000017;
            transition: all .3s;
            z-index: 1;
          }`)
      }
    }


(item) => {
                  this._iconTag = 'o-icon-' + item.icon
                  return <button onClick={_ => { this.clickHandler(item) }} className="item">
                    <this._iconTag class="icon" />
                  </button>
                }


_ => { this.clickHandler(item) }


@tag(tagName)
export default class Card extends WeElement<Props> {

  css = css.default ? css.default : css

  static defaultProps = {
    title: '',
    hoverable: '',
    extra: ''
  }

  static propTypes = {
    title: String,
    hoverable: String,
    extra: String,
    actions: Array,
    size: String
  }

  clickHandler = (item: any) => {
    this.fire('EvtChange', item)
    this.update(true)
  }

  handleMousemove = (item: string) => {
    if (item==="always") {
      (this.css = this.css + `.o-card {
        display: flex;
        flex-direction: column;
        box-shadow: 0 1px 2px -2px #00000029, 0 3px 6px #0000001f, 0 5px 12px 4px #00000017;
        transition: all .3s;
        z-index: 1;
      }`)
      return;
    }
    const hovercss = () => {
      switch(this.props.size){
        case 'large':
          (this.css = this.css + `.o-card:hover {
            display: flex;
            flex-direction: column;
            box-shadow: 0 1px 2px -2px #00000029, 0 3px 6px #0000001f, 0 5px 12px 4px #00000017;
            transition: all .3s;
            z-index: 1;
          }`)
          break;
        case 'medium':
          (this.css = this.css + `.o-card-medium:hover {
            display: flex;
            flex-direction: column;
            box-shadow: 0 1px 2px -2px #00000029, 0 3px 6px #0000001f, 0 5px 12px 4px #00000017;
            transition: all .3s;
            z-index: 1;
          }`)
          break;
        case 'small':
          (this.css = this.css + `.o-card-small:hover {
            display: flex;
            flex-direction: column;
            box-shadow: 0 1px 2px -2px #00000029, 0 3px 6px #0000001f, 0 5px 12px 4px #00000017;
            transition: all .3s;
            z-index: 1;
          }`)
          break;
        default:
          (this.css = this.css + `.o-card:hover {
            display: flex;
            flex-direction: column;
            box-shadow: 0 1px 2px -2px #00000029, 0 3px 6px #0000001f, 0 5px 12px 4px #00000017;
            transition: all .3s;
            z-index: 1;
          }`)
      }
    }
    item && hovercss()
  }

  _iconTag: any

  render(props: Props) {
    return (
      <h.f>

          <slot name="cover">

{props.title}

                  <slot name="extra"></slot>

          </slot>

            <slot></slot>

          {props.actions ? (

                {props.actions.map((item) => {
                  this._iconTag = 'o-icon-' + item.icon
                  return <button onClick={_ => { this.clickHandler(item) }} className="item">
                    <this._iconTag class="icon" />
                  </button>
                })}

          ) : null}

      </h.f>
    )
  }
}


interface IntrinsicElements {
      [tagName]: Omi.Props & Attrs
    }


export type Attrs = {
  title?: string,
  hoverable?: 'always' | 'true' | 'false',
  extra?: string,
  actions?: any[],
  size?: 'large' | 'medium' | 'small',
}


export type Props = OverwriteProps<Attrs, { title?: string,
  hoverable?: 'always' | 'true' | 'false',
  extra?: string,
  actions?: any[],
  size?: 'large' | 'medium' | 'small', }>


render(props: Props) {
    return (
      <h.f>

          <slot name="cover">

{props.title}

                  <slot name="extra"></slot>

          </slot>

            <slot></slot>

          {props.actions ? (

                {props.actions.map((item) => {
                  this._iconTag = 'o-icon-' + item.icon
                  return <button onClick={_ => { this.clickHandler(item) }} className="item">
                    <this._iconTag class="icon" />
                  </button>
                })}

          ) : null}

      </h.f>
    )
  }


@NgModule({
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule]
})
export class TeacherRoutingModule { }


@Injectable()
export class VotingService {
  constructor(
    private readonly usersService: UsersService,
    @InjectRepository(Voting, POSTGRES_CONNECTION)
    private readonly votingRepo: Repository<Voting>,
  ) {}

  async getVotingList(channelId: string): Promise<Voting[]> {
    return this.votingRepo.find({
      where: { broadcaster: { id: channelId } },
      order: { createdAt: 'DESC' },
    });
  }

  async getVoting(votingId: number): Promise<Voting> {
    const voting = await this.votingRepo.findOne({
      where: { id: votingId },
    });

    if (!voting) throw new NotFoundException();

    return voting;
  }

  // TODO: add voting limit that users can create
  async createVoting(
    userId: string,
    { channelId, ...data }: CreateVotingDto,
  ): Promise<Voting> {
    const hasAccess = await this.canCreateVoting(userId, channelId);

    if (!hasAccess) throw new ForbiddenException();

    // TODO: auto generate title?

    const savedVoting = await this.votingRepo.save({
      ...data,
      broadcaster: { id: channelId },
    } as Voting);

    delete savedVoting.broadcaster;

    return savedVoting;
  }

  async updateVoting(
    userId: string,
    votingId: number,
    data: UpdateVotingDto,
  ): Promise<Voting> {
    const [user, voting] = await Promise.all([
      this.usersService.findOne(userId, { relations: ['credentials'] }),
      this.votingRepo.findOne(votingId, {
        relations: ['broadcaster', 'broadcaster.credentials'],
      }),
    ]);
    const hasAccess = await this.canUpdateOrDeleteVoting(user, voting);

    if (!hasAccess) throw new ForbiddenException();

    return this.votingRepo.save({ ...voting, ...data } as Voting);
  }

  async deleteVoting(userId: string, votingId: number): Promise<void> {
    const [user, voting] = await Promise.all([
      this.usersService.findOne(userId, { relations: ['credentials'] }),
      this.votingRepo.findOne(votingId, {
        relations: ['broadcaster', 'broadcaster.credentials'],
      }),
    ]);
    const hasAccess = await this.canUpdateOrDeleteVoting(user, voting);

    if (!hasAccess) throw new ForbiddenException();

    await this.votingRepo.delete(votingId);
  }

  private async canCreateVoting(
    userId: string,
    channelId: string,
  ): Promise<boolean> {
    if (userId === channelId) {
      const user = await this.usersService.findOne(userId, {
        relations: ['credentials'],
      });

      return !!user;
    }

    const [user, channel] = await Promise.all([
      this.usersService.findOne(userId, { relations: ['credentials'] }),
      this.usersService.findOne(channelId, { relations: ['credentials'] }),
    ]);

    if (!user || !channel) return false;

    const isEditor = await this.usersService.isEditor(channel, user);

    if (!isEditor) {
      throw new ForbiddenException(HoneyError.VotingCreateNoPermission);
    }

    return isEditor;
  }

  private async canUpdateOrDeleteVoting(
    user: User,
    voting: Voting,
  ): Promise<boolean> {
    if (!voting || !user) return false;

    const isOwner = voting.broadcaster.id === user.id;

    if (isOwner) return true;

    const isEditor = await this.usersService.isEditor(voting.broadcaster, user);

    if (!isEditor) {
      throw new ForbiddenException(HoneyError.VotingCreateNoPermission);
    }

    return isEditor;
  }
}


async getVotingList(channelId: string): Promise<Voting[]> {
    return this.votingRepo.find({
      where: { broadcaster: { id: channelId } },
      order: { createdAt: 'DESC' },
    });
  }


async getVoting(votingId: number): Promise<Voting> {
    const voting = await this.votingRepo.findOne({
      where: { id: votingId },
    });

    if (!voting) throw new NotFoundException();

    return voting;
  }


// TODO: add voting limit that users can create
async createVoting(
    userId: string,
    { channelId, ...data }: CreateVotingDto,
  ): Promise<Voting> {
    const hasAccess = await this.canCreateVoting(userId, channelId);

    if (!hasAccess) throw new ForbiddenException();

    // TODO: auto generate title?

    const savedVoting = await this.votingRepo.save({
      ...data,
      broadcaster: { id: channelId },
    } as Voting);

    delete savedVoting.broadcaster;

    return savedVoting;
  }


async updateVoting(
    userId: string,
    votingId: number,
    data: UpdateVotingDto,
  ): Promise<Voting> {
    const [user, voting] = await Promise.all([
      this.usersService.findOne(userId, { relations: ['credentials'] }),
      this.votingRepo.findOne(votingId, {
        relations: ['broadcaster', 'broadcaster.credentials'],
      }),
    ]);
    const hasAccess = await this.canUpdateOrDeleteVoting(user, voting);

    if (!hasAccess) throw new ForbiddenException();

    return this.votingRepo.save({ ...voting, ...data } as Voting);
  }


async deleteVoting(userId: string, votingId: number): Promise<void> {
    const [user, voting] = await Promise.all([
      this.usersService.findOne(userId, { relations: ['credentials'] }),
      this.votingRepo.findOne(votingId, {
        relations: ['broadcaster', 'broadcaster.credentials'],
      }),
    ]);
    const hasAccess = await this.canUpdateOrDeleteVoting(user, voting);

    if (!hasAccess) throw new ForbiddenException();

    await this.votingRepo.delete(votingId);
  }


private async canCreateVoting(
    userId: string,
    channelId: string,
  ): Promise<boolean> {
    if (userId === channelId) {
      const user = await this.usersService.findOne(userId, {
        relations: ['credentials'],
      });

      return !!user;
    }

    const [user, channel] = await Promise.all([
      this.usersService.findOne(userId, { relations: ['credentials'] }),
      this.usersService.findOne(channelId, { relations: ['credentials'] }),
    ]);

    if (!user || !channel) return false;

    const isEditor = await this.usersService.isEditor(channel, user);

    if (!isEditor) {
      throw new ForbiddenException(HoneyError.VotingCreateNoPermission);
    }

    return isEditor;
  }


private async canUpdateOrDeleteVoting(
    user: User,
    voting: Voting,
  ): Promise<boolean> {
    if (!voting || !user) return false;

    const isOwner = voting.broadcaster.id === user.id;

    if (isOwner) return true;

    const isEditor = await this.usersService.isEditor(voting.broadcaster, user);

    if (!isEditor) {
      throw new ForbiddenException(HoneyError.VotingCreateNoPermission);
    }

    return isEditor;
  }


({ user }: { user: IUser }) => {
  const [title, setTitle] = useState('')
  const [description, setDescription] = useState('')
  const [dueDate, setDueDate] = useState('')
  const [createTodo] = useMutation(CREATE_TODO)
  const { loading, data } = useQuery(GET_TODOS, {
    variables: { limit: 100 },
  })

  const handleCreate = () => {
    createTodo({
      variables: {
        title,
        description,
        dueDate: addTimezoneOffset(dueDate),
      },
      refetchQueries: [
        {
          query: GET_TODOS,
          variables: { limit: 100 },
        },
      ],
    }).then(() => {
      setTitle('')
      setDescription('')
      setDueDate('')
    })
  }

  const handleTitleUpdate = (e: React.ChangeEvent<HTMLInputElement>) => {
    setTitle(e.target.value)
  }

  const handleDescriptionUpdate = (e: React.ChangeEvent<HTMLInputElement>) => {
    setDescription(e.target.value)
  }

  const handleDueDateUpdate = (e: React.ChangeEvent<HTMLInputElement>) => {
    addTimezoneOffset(e.target.value)
    setDueDate(e.target.value)
  }

  if (loading) return
Loading...

  return (
    <>

          <UserStats
            name={user.name}
            createdDate={String(user.createdDate)}
            todoCount={data.todos.totalCount}
          />

          <TodoList payload={data.todos.payload} />

          <Form title={'Create'} onSubmit={handleCreate}>


() => {
    createTodo({
      variables: {
        title,
        description,
        dueDate: addTimezoneOffset(dueDate),
      },
      refetchQueries: [
        {
          query: GET_TODOS,
          variables: { limit: 100 },
        },
      ],
    }).then(() => {
      setTitle('')
      setDescription('')
      setDueDate('')
    })
  }


() => {
      setTitle('')
      setDescription('')
      setDueDate('')
    }


(e: React.ChangeEvent<HTMLInputElement>) => {
    setTitle(e.target.value)
  }


(e: React.ChangeEvent<HTMLInputElement>) => {
    setDescription(e.target.value)
  }


(e: React.ChangeEvent<HTMLInputElement>) => {
    addTimezoneOffset(e.target.value)
    setDueDate(e.target.value)
  }


async (context: any) => {
  try {
    const { data } = await context.apolloClient.query({ query: GET_USER })
    return data || {}
  } catch (err) {
    const { res } = context
    if (res) {
      res.writeHead(302, { Location: '/' })
      res.end()
    } else {
      Router.replace('/')
    }
  }
}


String(user


(Component: ComponentType<any>) => flow(withComponent(Component), withId('Bold'), asMark, withKey('b'), withButton('format_bold'))


(Component: ComponentType<any>) => flow(withComponent(Component), withId('Italic'), asMark, withKey('i'), withButton('format_italic'))


(Component: ComponentType<any>) => flow(withComponent(Component), withId('Underline'), asMark, withKey('u'), withButton('format_underline'))


(Component: ComponentType<any>) => flow(withComponent(Component), withId('Link'), asInline, withKey('k'), withButton('link'))


(items: any[]) => <P extends object> (Component:ComponentType<P>) => (
  (props:P) => (
    <Component items={items}


<P extends object> (Component:ComponentType<P>) => (
  (props:P) => (
    <Component items={items}


(props:P) => (
    <Component items={items}


function ClubItemTable({props} : {props: ClubItemTableData}){
    return(
        <S.Table>
            <S.TableHead>

                    <S.TableNameWrapper>
                        <ToggleItem></ToggleItem>
                        {props.user_name}
                    </S.TableNameWrapper>
                    <S.TableName>신청물품 (1개)</S.TableName>
                    수량
                    가격
                    상태

            </S.TableHead>
            <S.TableBody>
                {
                    props.data.map((i,index)=>{
                        return(<ClubItem key={index} props={i}></ClubItem>)
                    })
                }
            </S.TableBody>
        </S.Table>
    )
}


(i,index)=>{
                        return(<ClubItem key={index} props={i}></ClubItem>)
                    }


export class AddressRefactor1616615743863 implements MigrationInterface {

    public async up(queryRunner: QueryRunner): Promise<void> {
        await queryRunner.query(
            `ALTER TABLE "address" RENAME COLUMN "address" TO "title"`,
        );
    }

    public async down(queryRunner: QueryRunner): Promise<void> {
        await queryRunner.query(
            `ALTER TABLE "address" RENAME COLUMN "title" TO "address"`,
        );
    }

}


public async up(queryRunner: QueryRunner): Promise<void> {
        await queryRunner.query(
            `ALTER TABLE "address" RENAME COLUMN "address" TO "title"`,
        );
    }


public async down(queryRunner: QueryRunner): Promise<void> {
        await queryRunner.query(
            `ALTER TABLE "address" RENAME COLUMN "title" TO "address"`,
        );
    }


(data: FiasObject): data is AddressObject => {
    return (
      data && data.hasOwnProperty('name') && data.hasOwnProperty('abbreviation')
    );
  }


(data: FiasObject): data is Stead => {
    return (
      data &&
      (data.hasOwnProperty('number') &&
        data.hasOwnProperty('liveStatus') &&
        !data.hasOwnProperty('estateStatus'))
    );
  }


(data: FiasObject): data is House => {
    return (
      data &&
      data.hasOwnProperty('structureStatus') &&
      (data.hasOwnProperty('number') || data.hasOwnProperty('structureNumber'))
    );
  }


export class FiasData {
  public static isAddressObject = (data: FiasObject): data is AddressObject => {
    return (
      data && data.hasOwnProperty('name') && data.hasOwnProperty('abbreviation')
    );
  };

  public static isStead = (data: FiasObject): data is Stead => {
    return (
      data &&
      (data.hasOwnProperty('number') &&
        data.hasOwnProperty('liveStatus') &&
        !data.hasOwnProperty('estateStatus'))
    );
  };

  public static isHouse = (data: FiasObject): data is House => {
    return (
      data &&
      data.hasOwnProperty('structureStatus') &&
      (data.hasOwnProperty('number') || data.hasOwnProperty('structureNumber'))
    );
  };

  constructor(public data: FiasObject) {}

  public get name(): string {
    const { data } = this;
    if (FiasData.isAddressObject(data)) {
      return data.name;
    } else if (FiasData.isStead(data)) {
      return data.number;
    } else if (FiasData.isHouse(data)) {
      return data.number || data.structureNumber || data.buildingNumber || '';
    }
    return '';
  }

  public get abbreviation(): string {
    return FiasData.isAddressObject(this.data) ? this.data.abbreviation : '';
  }

  public get code(): string {
    return FiasData.isAddressObject(this.data) ? this.data.code : '';
  }

  public get number(): string {
    return (FiasData.isStead(this.data) || FiasData.isHouse(this.data)) &&
      this.data.number
      ? this.data.number
      : '';
  }

  public get structureNumber(): string {
    return FiasData.isHouse(this.data) && this.data.structureNumber
      ? this.data.structureNumber
      : '';
  }

  public get buildingNumber(): string {
    return FiasData.isHouse(this.data) && this.data.buildingNumber
      ? this.data.buildingNumber
      : '';
  }

  public get fiasId(): FiasId {
    return (this.data && this.data.fiasId) || '';
  }

  public get postalCode(): string {
    return (this.data && this.data.postalCode) || '';
  }

  public get estateStatus(): EstateStatuses | undefined {
    if (FiasData.isHouse(this.data)) {
      return this.data.estateStatus;
    }
  }

  public get structureStatus(): StructureStatuses | undefined {
    if (FiasData.isHouse(this.data)) {
      return this.data.structureStatus;
    }
  }
}


@injectable()
export class PalidorConfig implements IConfig {
    @inject(IContext) public context: IContext;

    @inject(IFlowManager) public flowManager: IFlowManager;

    @inject(IEventDispatcher) public dispatcher: IEventDispatcher;
    public configure(): void {
        this.mapPalidor();

        this.dispatcher.dispatchEvent(new FlowEvent(FlowEvent.SHOW_INTRO_VIEW));
    }
    private mapPalidor(): void {
        this.context.injector
            .bind(FlowService)
            .to(FlowService)
            .inSingletonScope();

        this.flowManager.map(FlowEvent.SHOW_GAME_VIEW).toView(GameView);
        this.flowManager.map(FlowEvent.SHOW_HOME_VIEW).toView(HomeView);
        this.flowManager.map(FlowEvent.SHOW_INTRO_VIEW).toView(IntroView);

        this.flowManager.map(FlowEvent.SHOW_GAME_OVER_POPUP).toFloatingView(GameOverPopup);
        this.flowManager.map(FlowEvent.SHOW_YOU_WIN_POPUP).toFloatingView(YouWinPopup);
        this.flowManager.map(FlowEvent.SHOW_PAUSE_POPUP).toFloatingView(PausePopup);
    }
}


public configure(): void {
        this.mapPalidor();

        this.dispatcher.dispatchEvent(new FlowEvent(FlowEvent.SHOW_INTRO_VIEW));
    }


private mapPalidor(): void {
        this.context.injector
            .bind(FlowService)
            .to(FlowService)
            .inSingletonScope();

        this.flowManager.map(FlowEvent.SHOW_GAME_VIEW).toView(GameView);
        this.flowManager.map(FlowEvent.SHOW_HOME_VIEW).toView(HomeView);
        this.flowManager.map(FlowEvent.SHOW_INTRO_VIEW).toView(IntroView);

        this.flowManager.map(FlowEvent.SHOW_GAME_OVER_POPUP).toFloatingView(GameOverPopup);
        this.flowManager.map(FlowEvent.SHOW_YOU_WIN_POPUP).toFloatingView(YouWinPopup);
        this.flowManager.map(FlowEvent.SHOW_PAUSE_POPUP).toFloatingView(PausePopup);
    }


export interface IBehaviorConfig<TConfig> {
  id: string;

  type: string;

  /**
   * The behavior will be executed every `runInterval` seconds.
   */
  runInterval: number;

  config: TConfig;
}


function command(commandId: string, options: ScmCommandOptions = {}): Function {
	return (_target: any, key: string, descriptor: any) => {
		if (!(typeof descriptor.value === 'function')) {
			throw new Error('not supported');
		}

		Commands.push({ commandId, key, method: descriptor.value, options });
	};
}


// const ImageMimetypes = [
// 	'image/png',
// 	'image/gif',
// 	'image/jpeg',
// 	'image/webp',
// 	'image/tiff',
// 	'image/bmp'
// ];
async function categorizeResourceByResolution(resources: Resource[]): Promise<{ merge: Resource[]; resolved: Resource[]; unresolved: Resource[]; deletionConflicts: Resource[] }> {
	const selection = resources.filter(s => s instanceof Resource) as Resource[];
	const merge = selection.filter(s => s.resourceGroupType === ResourceGroupType.Merge);
	const isBothAddedOrModified = (s: Resource) => s.type === Status.BOTH_MODIFIED || s.type === Status.BOTH_ADDED;
	const isAnyDeleted = (s: Resource) => s.type === Status.DELETED_BY_THEM || s.type === Status.DELETED_BY_US;
	const possibleUnresolved = merge.filter(isBothAddedOrModified);
	const promises = possibleUnresolved.map(s => grep(s.resourceUri.fsPath, /^<{7}|^={7}|^>{7}/));
	const unresolvedBothModified = await Promise.all<boolean>(promises);
	const resolved = possibleUnresolved.filter((_s, i) => !unresolvedBothModified[i]);
	const deletionConflicts = merge.filter(s => isAnyDeleted(s));
	const unresolved = [
		...merge.filter(s => !isBothAddedOrModified(s) && !isAnyDeleted(s)),
		...possibleUnresolved.filter((_s, i) => unresolvedBothModified[i])
	];

	return { merge, resolved, unresolved, deletionConflicts };
}


function createCheckoutItems(repository: Repository): CheckoutItem[] {
	const config = workspace.getConfiguration('git');
	const checkoutTypeConfig = config.get<string | string[]>('checkoutType');
	let checkoutTypes: string[];

	if (checkoutTypeConfig === 'all' || !checkoutTypeConfig || checkoutTypeConfig.length === 0) {
		checkoutTypes = ['local', 'remote', 'tags'];
	} else if (typeof checkoutTypeConfig === 'string') {
		checkoutTypes = [checkoutTypeConfig];
	} else {
		checkoutTypes = checkoutTypeConfig;
	}

	const processors = checkoutTypes.map(getCheckoutProcessor)
		.filter(p => !!p) as CheckoutProcessor[];

	for (const ref of repository.refs) {
		for (const processor of processors) {
			processor.onRef(ref);
		}
	}

	return processors.reduce<CheckoutItem[]>((r, p) => r.concat(...p.items), []);
}


function getCheckoutProcessor(type: string): CheckoutProcessor | undefined {
	switch (type) {
		case 'local':
			return new CheckoutProcessor(RefType.Head, CheckoutItem);
		case 'remote':
			return new CheckoutProcessor(RefType.RemoteHead, CheckoutRemoteHeadItem);
		case 'tags':
			return new CheckoutProcessor(RefType.Tag, CheckoutTagItem);
	}

	return undefined;
}


function sanitizeRemoteName(name: string) {
	name = name.trim();
	return name && name.replace(/^\.|\/\.|\.\.|~|\^|:|\/$|\.lock$|\.lock\/|\\|\*|\s|^\s*$|\.$|\[|\]$/g, '-');
}


(_target: any, key: string, descriptor: any) => {
		if (!(typeof descriptor.value === 'function')) {
			throw new Error('not supported');
		}

		Commands.push({ commandId, key, method: descriptor.value, options });
	}


s => s instanceof Resource


s => s.resourceGroupType === ResourceGroupType.Merge


(s: Resource) => s.type === Status.BOTH_MODIFIED || s.type === Status.BOTH_ADDED


(s: Resource) => s.type === Status.DELETED_BY_THEM || s.type === Status.DELETED_BY_US


s => grep(s.resourceUri.fsPath, /^<{7}|^={7}|^>{7}/)


(_s, i) => !unresolvedBothModified[i]


s => isAnyDeleted(s)


s => !isBothAddedOrModified(s) && !isAnyDeleted(s)


(_s, i) => unresolvedBothModified[i]


p => !!p


(r, p) => r.concat(...p.items)


r => new this.ctor(r)


({ commandId, key, method, options }) => {
			const command = this.createCommand(commandId, key, method, options);

			if (options.diff) {
				return commands.registerDiffInformationCommand(commandId, command);
			} else {
				return commands.registerCommand(commandId, command);
			}
		}


(logLevel: LogLevel) => {
			let description: string | undefined;
			const defaultDescription = localize('default', "Default");
			const currentDescription = localize('current', "Current");

			if (logLevel === this.outputChannelLogger.defaultLogLevel && logLevel === this.outputChannelLogger.currentLogLevel) {
				description = `${defaultDescription} & ${currentDescription} `;
			} else if (logLevel === this.outputChannelLogger.defaultLogLevel) {
				description = defaultDescription;
			} else if (logLevel === this.outputChannelLogger.currentLogLevel) {
				description = currentDescription;
			}

			return {
				label: LogLevel[logLevel],
				logLevel,
				description
			};
		}


provider => localize('clonefrom', "Clone from {0}", provider.name)


(progress, token) => this.git.clone(url!, { parentPath: parentPath!, progress, recursive: options.recursive }, token)


folder => ({ label: folder.name, description: folder.uri.fsPath, folder })


w => w.uri.toString() === uri.toString()


r => r.type !== Status.DELETED && r.type !== Status.INDEX_DELETED


r => r.resourceUri


async (repository, resources) => {
				for (const resource of resources) {
					await this._stageDeletionConflict(repository, resource);
				}
			}


s => s.resourceGroupType === ResourceGroupType.WorkingTree


s => s.resourceGroupType === ResourceGroupType.Untracked


async (repository, resources) => repository.add(resources)


r => r.resourceUri.toString() === uriString


r => r.type !== Status.UNTRACKED && r.type !== Status.IGNORED


r => r.type === Status.UNTRACKED || r.type === Status.IGNORED


e => e.document.uri.toString() === uri.toString()


diff => selectedLines.reduce<LineChange | null>((result, range) => result || intersectDiffWithRange(modifiedDocument, diff, range), null)


(result, range) => result || intersectDiffWithRange(modifiedDocument, diff, range)


d => !!d


async (repository, resource) => await repository.stage(resource, result)


change => {
			const modifiedRange = getModifiedRange(modifiedDocument, change);
			return selections.every(selection => !selection.intersection(modifiedRange));
		}


selection => !selection.intersection(modifiedRange)


s => s instanceof Resource && s.resourceGroupType === ResourceGroupType.Index


async (repository, resources) => repository.revert(resources)


s => s instanceof Resource
			&& (s.resourceGroupType === ResourceGroupType.WorkingTree || s.resourceGroupType === ResourceGroupType.Untracked)


(s, r) => s + (r.type === Status.UNTRACKED ? 1 : 0)


resource => resource.type === Status.DELETED


async (repository, resources) => repository.clean(resources)


d => !d.isUntitled && d.isDirty && isDescendant(repository.root, d.uri.fsPath)


d => repository.indexGroup.resourceStates.some(s => pathEquals(s.resourceUri.fsPath, d.uri.fsPath))


s => pathEquals(s.resourceUri.fsPath, d.uri.fsPath)


d => d.save()


d => d.uri


r => r.type === Status.UNTRACKED


async () => {
			let _message: string | undefined = message;

			if (!_message) {
				let value: string | undefined = undefined;

				if (opts && opts.amend && repository.HEAD && repository.HEAD.commit) {
					return undefined;
				}

				const branchName = repository.headShortName;
				let placeHolder: string;

				if (branchName) {
					placeHolder = localize('commitMessageWithHeadLabel2', "Message (commit on '{0}')", branchName);
				} else {
					placeHolder = localize('commit message', "Commit message");
				}

				_message = await window.showInputBox({
					value,
					placeHolder,
					prompt: localize('provide commit message', "Please provide a commit message"),
					ignoreFocusOut: true
				});
			}

			return _message;
		}


c => quickpick.onDidAccept(() => c(quickpick.activeItems[0]))


() => c(quickpick.activeItems[0])


r => r.type === RefType.Head && r.name === randomName


(name: string) => name ?
			name.trim().replace(/^-+/, '').replace(/^\.|\/\.|\.\.|~|\^|:|\/$|\.lock$|\.lock\/|\\|\*|\s|^\s*$|\.$|\[|\]$/g, branchWhitespaceChar)
			: name


(name: string) => {
					const validateName = new RegExp(branchValidationRegex);
					const sanitizedName = sanitize(name);
					if (validateName.test(sanitizedName)) {
						// If the sanitized name that we will use is different than what is
						// in the input box, show an info message to the user informing them
						// the branch name that will be used.
						return name === sanitizedName
							? null
							: {
								message: localize('branch name does not match sanitized', "The new branch will be '{0}'", sanitizedName),
								severity: InputBoxValidationSeverity.Info
							};
					}

					return localize('branch name format invalid', "Branch name needs to match regex: {0}", branchValidationRegex);
				}


force => repository.deleteBranch(name, force)


ref => ref.type === RefType.Head && ref.name !== currentHead


ref => new BranchDeleteItem(ref)


force => choice.run(repository, force)


ref => ref.type === RefType.Head


ref => ref.name || ref.commit


ref => new MergeItem(ref as Branch)


ref => ref.type === RefType.RemoteHead


ref => ref.name !== repository.HEAD?.name


ref => new RebaseItem(ref)


e => e.ref.name === upstreamName


ref => ref.type === RefType.Tag


ref => new TagItem(ref)


r => r.fetchUrl !== undefined


r => ({ label: r.name, description: r.fetchUrl! })


r => (r.remote === remotePick.label)


r => ({ label: r.name! })


r => r.pushUrl !== undefined


r => ({ label: r.name, description: r.pushUrl })


provider => localize('addfrom', "Add remote from {0}", provider.name)


(name: string) => {
				if (!sanitizeRemoteName(name)) {
					return localize('remote name format invalid', "Remote name format invalid");
				} else if (repository.remotes.find(r => r.name === name)) {
					return localize('remote already exists', "Remote '{0}' already exists.", name);
				}

				return null;
			}


r => r.name === name


r => r.name


r => r.name === remoteName


async repository => {
			const HEAD = repository.HEAD;

			if (!HEAD || !HEAD.upstream) {
				return;
			}

			await repository.sync(HEAD);
		}


provider => ({ label: (provider.icon ? `$(${provider.icon}) ` : '') + localize('publish to', "Publish to {0}", provider.name), alwaysShow: true, provider })


async (repository, resources) => repository.ignore(resources)


stash => ({ label: `#${stash.index}:  ${stash.description}`, description: '', details: '', stash })


r => r.resourceUri.fsPath


(...args: any[]) => {
			let result: Promise<any>;

			if (!options.repository) {
				result = Promise.resolve(method.apply(this, args));
			} else {
				// try to guess the repository based on the first argument
				const repository = this.model.getRepository(args[0]);
				let repositoryPromise: Promise<Repository | undefined>;

				if (repository) {
					repositoryPromise = Promise.resolve(repository);
				} else if (this.model.repositories.length === 1) {
					repositoryPromise = Promise.resolve(this.model.repositories[0]);
				} else {
					repositoryPromise = this.model.pickRepository();
				}

				result = repositoryPromise.then(repository => {
					if (!repository) {
						return Promise.resolve();
					}

					return Promise.resolve(method.apply(this, [repository, ...args.slice(1)]));
				});
			}

			/* __GDPR__
				"git.command" : {
					"command" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
				}
			*/
			this.telemetryReporter.sendTelemetryEvent('git.command', { command: id });

			return result.catch(async err => {
				const options: MessageOptions = {
					modal: true
				};

				let message: string;
				let type: 'error' | 'warning' = 'error';

				const choices = new Map<string, () => void>();
				const openOutputChannelChoice = localize('open git log', "Open Git Log");
				const outputChannelLogger = this.outputChannelLogger;
				choices.set(openOutputChannelChoice, () => outputChannelLogger.showOutputChannel());

				const showCommandOutputChoice = localize('show command output', "Show Command Output");
				if (err.stderr) {
					choices.set(showCommandOutputChoice, async () => {
						const timestamp = new Date().getTime();
						const uri = Uri.parse(`git-output:/git-error-${timestamp}`);

						let command = 'git';

						if (err.gitArgs) {
							command = `${command} ${err.gitArgs.join(' ')}`;
						} else if (err.gitCommand) {
							command = `${command} ${err.gitCommand}`;
						}

						this.commandErrors.set(uri, `> ${command}\n${err.stderr}`);

						try {
							const doc = await workspace.openTextDocument(uri);
							await window.showTextDocument(doc);
						} finally {
							this.commandErrors.delete(uri);
						}
					});
				}

				switch (err.gitErrorCode) {
					case GitErrorCodes.DirtyWorkTree:
						message = localize('clean repo', "Please clean your repository working tree before checkout.");
						break;
					case GitErrorCodes.PushRejected:
						message = localize('cant push', "Can't push refs to remote. Try running 'Pull' first to integrate your changes.");
						break;
					case GitErrorCodes.Conflict:
						message = localize('merge conflicts', "There are merge conflicts. Resolve them before committing.");
						type = 'warning';
						options.modal = false;
						break;
					case GitErrorCodes.StashConflict:
						message = localize('stash merge conflicts', "There were merge conflicts while applying the stash.");
						type = 'warning';
						options.modal = false;
						break;
					case GitErrorCodes.AuthenticationFailed: {
						const regex = /Authentication failed for '(.*)'/i;
						const match = regex.exec(err.stderr || String(err));

						message = match
							? localize('auth failed specific', "Failed to authenticate to git remote:\n\n{0}", match[1])
							: localize('auth failed', "Failed to authenticate to git remote.");
						break;
					}
					case GitErrorCodes.NoUserNameConfigured:
					case GitErrorCodes.NoUserEmailConfigured:
						message = localize('missing user info', "Make sure you configure your 'user.name' and 'user.email' in git.");
						choices.set(localize('learn more', "Learn More"), () => commands.executeCommand('vscode.open', Uri.parse('https://aka.ms/vscode-setup-git')));
						break;
					default: {
						const hint = (err.stderr || err.message || String(err))
							.replace(/^error: /mi, '')
							.replace(/^> husky.*$/mi, '')
							.split(/[\r\n]/)
							.filter((line: string) => !!line)
						[0];

						message = hint
							? localize('git error details', "Git: {0}", hint)
							: localize('git error', "Git error");

						break;
					}
				}

				if (!message) {
					console.error(err);
					return;
				}

				const allChoices = Array.from(choices.keys());
				const result = type === 'error'
					? await window.showErrorMessage(message, options, ...allChoices)
					: await window.showWarningMessage(message, options, ...allChoices);

				if (result) {
					const resultFn = choices.get(result);

					if (resultFn) {
						resultFn();
					}
				}
			});
		}


repository => {
					if (!repository) {
						return Promise.resolve();
					}

					return Promise.resolve(method.apply(this, [repository, ...args.slice(1)]));
				}


async err => {
				const options: MessageOptions = {
					modal: true
				};

				let message: string;
				let type: 'error' | 'warning' = 'error';

				const choices = new Map<string, () => void>();
				const openOutputChannelChoice = localize('open git log', "Open Git Log");
				const outputChannelLogger = this.outputChannelLogger;
				choices.set(openOutputChannelChoice, () => outputChannelLogger.showOutputChannel());

				const showCommandOutputChoice = localize('show command output', "Show Command Output");
				if (err.stderr) {
					choices.set(showCommandOutputChoice, async () => {
						const timestamp = new Date().getTime();
						const uri = Uri.parse(`git-output:/git-error-${timestamp}`);

						let command = 'git';

						if (err.gitArgs) {
							command = `${command} ${err.gitArgs.join(' ')}`;
						} else if (err.gitCommand) {
							command = `${command} ${err.gitCommand}`;
						}

						this.commandErrors.set(uri, `> ${command}\n${err.stderr}`);

						try {
							const doc = await workspace.openTextDocument(uri);
							await window.showTextDocument(doc);
						} finally {
							this.commandErrors.delete(uri);
						}
					});
				}

				switch (err.gitErrorCode) {
					case GitErrorCodes.DirtyWorkTree:
						message = localize('clean repo', "Please clean your repository working tree before checkout.");
						break;
					case GitErrorCodes.PushRejected:
						message = localize('cant push', "Can't push refs to remote. Try running 'Pull' first to integrate your changes.");
						break;
					case GitErrorCodes.Conflict:
						message = localize('merge conflicts', "There are merge conflicts. Resolve them before committing.");
						type = 'warning';
						options.modal = false;
						break;
					case GitErrorCodes.StashConflict:
						message = localize('stash merge conflicts', "There were merge conflicts while applying the stash.");
						type = 'warning';
						options.modal = false;
						break;
					case GitErrorCodes.AuthenticationFailed: {
						const regex = /Authentication failed for '(.*)'/i;
						const match = regex.exec(err.stderr || String(err));

						message = match
							? localize('auth failed specific', "Failed to authenticate to git remote:\n\n{0}", match[1])
							: localize('auth failed', "Failed to authenticate to git remote.");
						break;
					}
					case GitErrorCodes.NoUserNameConfigured:
					case GitErrorCodes.NoUserEmailConfigured:
						message = localize('missing user info', "Make sure you configure your 'user.name' and 'user.email' in git.");
						choices.set(localize('learn more', "Learn More"), () => commands.executeCommand('vscode.open', Uri.parse('https://aka.ms/vscode-setup-git')));
						break;
					default: {
						const hint = (err.stderr || err.message || String(err))
							.replace(/^error: /mi, '')
							.replace(/^> husky.*$/mi, '')
							.split(/[\r\n]/)
							.filter((line: string) => !!line)
						[0];

						message = hint
							? localize('git error details', "Git: {0}", hint)
							: localize('git error', "Git error");

						break;
					}
				}

				if (!message) {
					console.error(err);
					return;
				}

				const allChoices = Array.from(choices.keys());
				const result = type === 'error'
					? await window.showErrorMessage(message, options, ...allChoices)
					: await window.showWarningMessage(message, options, ...allChoices);

				if (result) {
					const resultFn = choices.get(result);

					if (resultFn) {
						resultFn();
					}
				}
			}


() => outputChannelLogger.showOutputChannel()


async () => {
						const timestamp = new Date().getTime();
						const uri = Uri.parse(`git-output:/git-error-${timestamp}`);

						let command = 'git';

						if (err.gitArgs) {
							command = `${command} ${err.gitArgs.join(' ')}`;
						} else if (err.gitCommand) {
							command = `${command} ${err.gitCommand}`;
						}

						this.commandErrors.set(uri, `> ${command}\n${err.stderr}`);

						try {
							const doc = await workspace.openTextDocument(uri);
							await window.showTextDocument(doc);
						} finally {
							this.commandErrors.delete(uri);
						}
					}


() => commands.executeCommand('vscode.open', Uri.parse('https://aka.ms/vscode-setup-git'))


(line: string) => !!line


r => r.root


(result, resource) => {
			let repository = this.model.getRepository(resource);

			if (!repository) {
				console.warn('Could not find git repository for ', resource);
				return result;
			}

			// Could it be a submodule?
			if (pathEquals(resource.fsPath, repository.root)) {
				repository = this.model.getRepositoryForSubmodule(resource) || repository;
			}

			const tuple = result.filter(p => p.repository === repository)[0];

			if (tuple) {
				tuple.resources.push(resource);
			} else {
				result.push({ repository, resources: [resource] });
			}

			return result;
		}


p => p.repository === repository


({ repository, resources }) => fn(repository as Repository, isSingleResource ? resources[0] : resources)


d => d.dispose()


class CheckoutItem implements QuickPickItem {

	protected get shortCommit(): string { return (this.ref.commit || '').substr(0, 8); }
	get label(): string { return this.ref.name || this.shortCommit; }
	get description(): string { return this.shortCommit; }

	constructor(protected ref: Ref) { }

	async run(repository: Repository, opts?: { detached?: boolean }): Promise<void> {
		const ref = this.ref.name;

		if (!ref) {
			return;
		}

		await repository.checkout(ref, opts);
	}
}


class CheckoutTagItem extends CheckoutItem {

	override get description(): string {
		return localize('tag at', "Tag at {0}", this.shortCommit);
	}
}


class CheckoutRemoteHeadItem extends CheckoutItem {

	override get description(): string {
		return localize('remote branch at', "Remote branch at {0}", this.shortCommit);
	}

	override async run(repository: Repository, opts?: { detached?: boolean }): Promise<void> {
		if (!this.ref.name) {
			return;
		}

		const branches = await repository.findTrackingBranches(this.ref.name);

		if (branches.length > 0) {
			await repository.checkout(branches[0].name!, opts);
		} else {
			await repository.checkoutTracking(this.ref.name, opts);
		}
	}
}


class BranchDeleteItem implements QuickPickItem {

	private get shortCommit(): string { return (this.ref.commit || '').substr(0, 8); }
	get branchName(): string | undefined { return this.ref.name; }
	get label(): string { return this.branchName || ''; }
	get description(): string { return this.shortCommit; }

	constructor(private ref: Ref) { }

	async run(repository: Repository, force?: boolean): Promise<void> {
		if (!this.branchName) {
			return;
		}
		await repository.deleteBranch(this.branchName, force);
	}
}


class MergeItem implements QuickPickItem {

	get label(): string { return this.ref.name || ''; }
	get description(): string { return this.ref.name || ''; }

	constructor(protected ref: Ref) { }

	async run(repository: Repository): Promise<void> {
		await repository.merge(this.ref.name! || this.ref.commit!);
	}
}


class RebaseItem implements QuickPickItem {

	get label(): string { return this.ref.name || ''; }
	description: string = '';

	constructor(readonly ref: Ref) { }

	async run(repository: Repository): Promise<void> {
		if (this.ref?.name) {
			await repository.rebase(this.ref.name);
		}
	}
}


class CreateBranchItem implements QuickPickItem {
	get label(): string { return '$(plus) ' + localize('create branch', 'Create new branch...'); }
	get description(): string { return ''; }
	get alwaysShow(): boolean { return true; }
}


class CreateBranchFromItem implements QuickPickItem {
	get label(): string { return '$(plus) ' + localize('create branch from', 'Create new branch from...'); }
	get description(): string { return ''; }
	get alwaysShow(): boolean { return true; }
}


class CheckoutDetachedItem implements QuickPickItem {
	get label(): string { return '$(debug-disconnect) ' + localize('checkout detached', 'Checkout detached...'); }
	get description(): string { return ''; }
	get alwaysShow(): boolean { return true; }
}


class HEADItem implements QuickPickItem {

	constructor(private repository: Repository) { }

	get label(): string { return 'HEAD'; }
	get description(): string { return (this.repository.HEAD && this.repository.HEAD.commit || '').substr(0, 8); }
	get alwaysShow(): boolean { return true; }
}


class AddRemoteItem implements QuickPickItem {

	constructor(private cc: CommandCenter) { }

	get label(): string { return '$(plus) ' + localize('add remote', 'Add a new remote...'); }
	get description(): string { return ''; }

	get alwaysShow(): boolean { return true; }

	async run(repository: Repository): Promise<void> {
		await this.cc.addRemote(repository);
	}
}


class CheckoutProcessor {

	private refs: Ref[] = [];
	get items(): CheckoutItem[] { return this.refs.map(r => new this.ctor(r)); }
	constructor(private type: RefType, private ctor: { new(ref: Ref): CheckoutItem }) { }

	onRef(ref: Ref): void {
		if (ref.type === this.type) {
			this.refs.push(ref);
		}
	}
}


class TagItem implements QuickPickItem {
	get label(): string { return this.ref.name ?? ''; }
	get description(): string { return this.ref.commit?.substr(0, 8) ?? ''; }
	constructor(readonly ref: Ref) { }
}


class CommandErrorOutputTextDocumentContentProvider implements TextDocumentContentProvider {

	private items = new Map<string, string>();

	set(uri: Uri, contents: string): void {
		this.items.set(uri.path, contents);
	}

	delete(uri: Uri): void {
		this.items.delete(uri.path);
	}

	provideTextDocumentContent(uri: Uri): string | undefined {
		return this.items.get(uri.path);
	}
}


interface ScmCommandOptions {
	repository?: boolean;
	diff?: boolean;
}


interface ScmCommand {
	commandId: string;
	key: string;
	method: Function;
	options: ScmCommandOptions;
}


interface PushOptions {
	pushType: PushType;
	forcePush?: boolean;
	silent?: boolean;

	pushTo?: {
		remote?: string;
		refspec?: string;
		setUpstream?: boolean;
	};
}


enum PushType {
	Push,
	PushTo,
	PushFollowTags,
	PushTags
}


enum PostCloneAction { Open, OpenNewWindow, AddToWorkspace }


async run(repository: Repository, opts?: { detached?: boolean }): Promise<void> {
		const ref = this.ref.name;

		if (!ref) {
			return;
		}

		await repository.checkout(ref, opts);
	}


override async run(repository: Repository, opts?: { detached?: boolean }): Promise<void> {
		if (!this.ref.name) {
			return;
		}

		const branches = await repository.findTrackingBranches(this.ref.name);

		if (branches.length > 0) {
			await repository.checkout(branches[0].name!, opts);
		} else {
			await repository.checkoutTracking(this.ref.name, opts);
		}
	}


async run(repository: Repository, force?: boolean): Promise<void> {
		if (!this.branchName) {
			return;
		}
		await repository.deleteBranch(this.branchName, force);
	}


async run(repository: Repository): Promise<void> {
		await repository.merge(this.ref.name! || this.ref.commit!);
	}


async run(repository: Repository): Promise<void> {
		if (this.ref?.name) {
			await repository.rebase(this.ref.name);
		}
	}


async run(repository: Repository): Promise<void> {
		await this.cc.addRemote(repository);
	}


onRef(ref: Ref): void {
		if (ref.type === this.type) {
			this.refs.push(ref);
		}
	}


set(uri: Uri, contents: string): void {
		this.items.set(uri.path, contents);
	}


delete(uri: Uri): void {
		this.items.delete(uri.path);
	}


provideTextDocumentContent(uri: Uri): string | undefined {
		return this.items.get(uri.path);
	}


@command('git.setLogLevel')
	async setLogLevel(): Promise<void> {
		const createItem = (logLevel: LogLevel) => {
			let description: string | undefined;
			const defaultDescription = localize('default', "Default");
			const currentDescription = localize('current', "Current");

			if (logLevel === this.outputChannelLogger.defaultLogLevel && logLevel === this.outputChannelLogger.currentLogLevel) {
				description = `${defaultDescription} & ${currentDescription} `;
			} else if (logLevel === this.outputChannelLogger.defaultLogLevel) {
				description = defaultDescription;
			} else if (logLevel === this.outputChannelLogger.currentLogLevel) {
				description = currentDescription;
			}

			return {
				label: LogLevel[logLevel],
				logLevel,
				description
			};
		};

		const items = [
			createItem(LogLevel.Trace),
			createItem(LogLevel.Debug),
			createItem(LogLevel.Info),
			createItem(LogLevel.Warning),
			createItem(LogLevel.Error),
			createItem(LogLevel.Critical),
			createItem(LogLevel.Off)
		];

		const choice = await window.showQuickPick(items, {
			placeHolder: localize('select log level', "Select log level")
		});

		if (!choice) {
			return;
		}

		this.outputChannelLogger.setLogLevel(choice.logLevel);
	}


@command('git.refresh', { repository: true })
	async refresh(repository: Repository): Promise<void> {
		await repository.status();
	}


@command('git.openResource')
	async openResource(resource: Resource): Promise<void> {
		const repository = this.model.getRepository(resource.resourceUri);

		if (!repository) {
			return;
		}

		await resource.open();
	}


@command('git.openAllChanges', { repository: true })
	async openChanges(repository: Repository): Promise<void> {
		for (const resource of [...repository.workingTreeGroup.resourceStates, ...repository.untrackedGroup.resourceStates]) {
			if (
				resource.type === Status.DELETED || resource.type === Status.DELETED_BY_THEM ||
				resource.type === Status.DELETED_BY_US || resource.type === Status.BOTH_DELETED
			) {
				continue;
			}

			void commands.executeCommand(
				'vscode.open',
				resource.resourceUri,
				{ background: true, preview: false, }
			);
		}
	}


async cloneRepository(url?: string, parentPath?: string, options: { recursive?: boolean } = {}): Promise<void> {
		if (!url || typeof url !== 'string') {
			url = await pickRemoteSource({
				providerLabel: provider => localize('clonefrom', "Clone from {0}", provider.name),
				urlLabel: localize('repourl', "Clone from URL")
			});
		}

		if (!url) {
			/* __GDPR__
				"clone" : {
					"outcome" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
				}
			*/
			this.telemetryReporter.sendTelemetryEvent('clone', { outcome: 'no_URL' });
			return;
		}

		url = url.trim().replace(/^git\s+clone\s+/, '');

		if (!parentPath) {
			const config = workspace.getConfiguration('git');
			let defaultCloneDirectory = config.get<string>('defaultCloneDirectory') || os.homedir();
			defaultCloneDirectory = defaultCloneDirectory.replace(/^~/, os.homedir());

			const uris = await window.showOpenDialog({
				canSelectFiles: false,
				canSelectFolders: true,
				canSelectMany: false,
				defaultUri: Uri.file(defaultCloneDirectory),
				openLabel: localize('selectFolder', "Select Repository Location")
			});

			if (!uris || uris.length === 0) {
				/* __GDPR__
					"clone" : {
						"outcome" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
					}
				*/
				this.telemetryReporter.sendTelemetryEvent('clone', { outcome: 'no_directory' });
				return;
			}

			const uri = uris[0];
			parentPath = uri.fsPath;
		}

		try {
			const opts = {
				location: ProgressLocation.Notification,
				title: localize('cloning', "Cloning git repository '{0}'...", url),
				cancellable: true
			};

			const repositoryPath = await window.withProgress(
				opts,
				(progress, token) => this.git.clone(url!, { parentPath: parentPath!, progress, recursive: options.recursive }, token)
			);

			const config = workspace.getConfiguration('git');
			const openAfterClone = config.get<'always' | 'alwaysNewWindow' | 'whenNoFolderOpen' | 'prompt'>('openAfterClone');

			enum PostCloneAction { Open, OpenNewWindow, AddToWorkspace }
			let action: PostCloneAction | undefined = undefined;

			if (openAfterClone === 'always') {
				action = PostCloneAction.Open;
			} else if (openAfterClone === 'alwaysNewWindow') {
				action = PostCloneAction.OpenNewWindow;
			} else if (openAfterClone === 'whenNoFolderOpen' && !workspace.workspaceFolders) {
				action = PostCloneAction.Open;
			}

			if (action === undefined) {
				let message = localize('proposeopen', "Would you like to open the cloned repository?");
				const open = localize('openrepo', "Open");
				const openNewWindow = localize('openreponew', "Open in New Window");
				const choices = [open, openNewWindow];

				const addToWorkspace = localize('add', "Add to Workspace");
				if (workspace.workspaceFolders) {
					message = localize('proposeopen2', "Would you like to open the cloned repository, or add it to the current workspace?");
					choices.push(addToWorkspace);
				}

				const result = await window.showInformationMessage(message, ...choices);

				action = result === open ? PostCloneAction.Open
					: result === openNewWindow ? PostCloneAction.OpenNewWindow
						: result === addToWorkspace ? PostCloneAction.AddToWorkspace : undefined;
			}

			/* __GDPR__
				"clone" : {
					"outcome" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
					"openFolder": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }
				}
			*/
			this.telemetryReporter.sendTelemetryEvent('clone', { outcome: 'success' }, { openFolder: action === PostCloneAction.Open || action === PostCloneAction.OpenNewWindow ? 1 : 0 });

			const uri = Uri.file(repositoryPath);

			if (action === PostCloneAction.Open) {
				commands.executeCommand('vscode.openFolder', uri, { forceReuseWindow: true });
			} else if (action === PostCloneAction.AddToWorkspace) {
				workspace.updateWorkspaceFolders(workspace.workspaceFolders!.length, 0, { uri });
			} else if (action === PostCloneAction.OpenNewWindow) {
				commands.executeCommand('vscode.openFolder', uri, { forceNewWindow: true });
			}
		} catch (err) {
			if (/already exists and is not an empty directory/.test(err && err.stderr || '')) {
				/* __GDPR__
					"clone" : {
						"outcome" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
					}
				*/
				this.telemetryReporter.sendTelemetryEvent('clone', { outcome: 'directory_not_empty' });
			} else if (/Cancelled/i.test(err && (err.message || err.stderr || ''))) {
				return;
			} else {
				/* __GDPR__
					"clone" : {
						"outcome" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
					}
				*/
				this.telemetryReporter.sendTelemetryEvent('clone', { outcome: 'error' });
			}

			throw err;
		}
	}


@command('git.clone')
	async clone(url?: string, parentPath?: string): Promise<void> {
		await this.cloneRepository(url, parentPath);
	}


@command('git.cloneRecursive')
	async cloneRecursive(url?: string, parentPath?: string): Promise<void> {
		await this.cloneRepository(url, parentPath, { recursive: true });
	}


@command('git.init')
	async init(skipFolderPrompt = false): Promise<void> {
		let repositoryPath: string | undefined = undefined;
		let askToOpen = true;

		if (workspace.workspaceFolders) {
			if (skipFolderPrompt && workspace.workspaceFolders.length === 1) {
				repositoryPath = workspace.workspaceFolders[0].uri.fsPath;
				askToOpen = false;
			} else {
				const placeHolder = localize('init', "Pick workspace folder to initialize git repo in");
				const pick = { label: localize('choose', "Choose Folder...") };
				const items: { label: string; folder?: WorkspaceFolder }[] = [
					...workspace.workspaceFolders.map(folder => ({ label: folder.name, description: folder.uri.fsPath, folder })),
					pick
				];
				const item = await window.showQuickPick(items, { placeHolder, ignoreFocusOut: true });

				if (!item) {
					return;
				} else if (item.folder) {
					repositoryPath = item.folder.uri.fsPath;
					askToOpen = false;
				}
			}
		}

		if (!repositoryPath) {
			const homeUri = Uri.file(os.homedir());
			const defaultUri = workspace.workspaceFolders && workspace.workspaceFolders.length > 0
				? Uri.file(workspace.workspaceFolders[0].uri.fsPath)
				: homeUri;

			const result = await window.showOpenDialog({
				canSelectFiles: false,
				canSelectFolders: true,
				canSelectMany: false,
				defaultUri,
				openLabel: localize('init repo', "Initialize Repository")
			});

			if (!result || result.length === 0) {
				return;
			}

			const uri = result[0];

			if (homeUri.toString().startsWith(uri.toString())) {
				const yes = localize('create repo', "Initialize Repository");
				const answer = await window.showWarningMessage(localize('are you sure', "This will create a Git repository in '{0}'. Are you sure you want to continue?", uri.fsPath), yes);

				if (answer !== yes) {
					return;
				}
			}

			repositoryPath = uri.fsPath;

			if (workspace.workspaceFolders && workspace.workspaceFolders.some(w => w.uri.toString() === uri.toString())) {
				askToOpen = false;
			}
		}

		await this.git.init(repositoryPath);

		let message = localize('proposeopen init', "Would you like to open the initialized repository?");
		const open = localize('openrepo', "Open");
		const openNewWindow = localize('openreponew', "Open in New Window");
		const choices = [open, openNewWindow];

		if (!askToOpen) {
			return;
		}

		const addToWorkspace = localize('add', "Add to Workspace");
		if (workspace.workspaceFolders) {
			message = localize('proposeopen2 init', "Would you like to open the initialized repository, or add it to the current workspace?");
			choices.push(addToWorkspace);
		}

		const result = await window.showInformationMessage(message, ...choices);
		const uri = Uri.file(repositoryPath);

		if (result === open) {
			commands.executeCommand('vscode.openFolder', uri);
		} else if (result === addToWorkspace) {
			workspace.updateWorkspaceFolders(workspace.workspaceFolders!.length, 0, { uri });
		} else if (result === openNewWindow) {
			commands.executeCommand('vscode.openFolder', uri, true);
		} else {
			await this.model.openRepository(repositoryPath);
		}
	}


@command('git.openRepository', { repository: false })
	async openRepository(path?: string): Promise<void> {
		if (!path) {
			const result = await window.showOpenDialog({
				canSelectFiles: false,
				canSelectFolders: true,
				canSelectMany: false,
				defaultUri: Uri.file(os.homedir()),
				openLabel: localize('open repo', "Open Repository")
			});

			if (!result || result.length === 0) {
				return;
			}

			path = result[0].fsPath;
		}

		await this.model.openRepository(path);
	}


@command('git.close', { repository: true })
	async close(repository: Repository): Promise<void> {
		this.model.close(repository);
	}


@command('git.openFile')
	async openFile(arg?: Resource | Uri, ...resourceStates: SourceControlResourceState[]): Promise<void> {
		const preserveFocus = arg instanceof Resource;

		let uris: Uri[] | undefined;

		if (arg instanceof Uri) {
			if (isGitUri(arg)) {
				uris = [Uri.file(fromGitUri(arg).path)];
			} else if (arg.scheme === 'file') {
				uris = [arg];
			}
		} else {
			let resource = arg;

			if (!(resource instanceof Resource)) {
				// can happen when called from a keybinding
				resource = this.getSCMResource();
			}

			if (resource) {
				uris = ([resource, ...resourceStates] as Resource[])
					.filter(r => r.type !== Status.DELETED && r.type !== Status.INDEX_DELETED)
					.map(r => r.resourceUri);
			} else if (window.activeTextEditor) {
				uris = [window.activeTextEditor.document.uri];
			}
		}

		if (!uris) {
			return;
		}

		const activeTextEditor = window.activeTextEditor;
		// Must extract these now because opening a new document will change the activeTextEditor reference
		const previousVisibleRange = activeTextEditor?.visibleRanges[0];
		const previousURI = activeTextEditor?.document.uri;
		const previousSelection = activeTextEditor?.selection;

		for (const uri of uris) {
			const opts: TextDocumentShowOptions = {
				preserveFocus,
				preview: false,
				viewColumn: ViewColumn.Active
			};

			await commands.executeCommand('vscode.open', uri, {
				...opts,
				override: arg instanceof Resource && arg.type === Status.BOTH_MODIFIED ? false : undefined
			});

			const document = window.activeTextEditor?.document;

			// If the document doesn't match what we opened then don't attempt to select the range
			// Additioanlly if there was no previous document we don't have information to select a range
			if (document?.uri.toString() !== uri.toString() || !activeTextEditor || !previousURI || !previousSelection) {
				continue;
			}

			// Check if active text editor has same path as other editor. we cannot compare via
			// URI.toString() here because the schemas can be different. Instead we just go by path.
			if (previousURI.path === uri.path && document) {
				// preserve not only selection but also visible range
				opts.selection = previousSelection;
				const editor = await window.showTextDocument(document, opts);
				// This should always be defined but just in case
				if (previousVisibleRange) {
					editor.revealRange(previousVisibleRange);
				}
			}
		}
	}


@command('git.openFile2')
	async openFile2(arg?: Resource | Uri, ...resourceStates: SourceControlResourceState[]): Promise<void> {
		this.openFile(arg, ...resourceStates);
	}


@command('git.openHEADFile')
	async openHEADFile(arg?: Resource | Uri): Promise<void> {
		let resource: Resource | undefined = undefined;
		const preview = !(arg instanceof Resource);

		if (arg instanceof Resource) {
			resource = arg;
		} else if (arg instanceof Uri) {
			resource = this.getSCMResource(arg);
		} else {
			resource = this.getSCMResource();
		}

		if (!resource) {
			return;
		}

		const HEAD = resource.leftUri;
		const basename = path.basename(resource.resourceUri.fsPath);
		const title = `${basename} (HEAD)`;

		if (!HEAD) {
			window.showWarningMessage(localize('HEAD not available', "HEAD version of '{0}' is not available.", path.basename(resource.resourceUri.fsPath)));
			return;
		}

		const opts: TextDocumentShowOptions = {
			preview
		};

		return await commands.executeCommand<void>('vscode.open', HEAD, opts, title);
	}


@command('git.openChange')
	async openChange(arg?: Resource | Uri, ...resourceStates: SourceControlResourceState[]): Promise<void> {
		let resources: Resource[] | undefined = undefined;

		if (arg instanceof Uri) {
			const resource = this.getSCMResource(arg);
			if (resource !== undefined) {
				resources = [resource];
			}
		} else {
			let resource: Resource | undefined = undefined;

			if (arg instanceof Resource) {
				resource = arg;
			} else {
				resource = this.getSCMResource();
			}

			if (resource) {
				resources = [...resourceStates as Resource[], resource];
			}
		}

		if (!resources) {
			return;
		}

		for (const resource of resources) {
			await resource.openChange();
		}
	}


@command('git.rename', { repository: true })
	async rename(repository: Repository, fromUri: Uri | undefined): Promise<void> {
		fromUri = fromUri ?? window.activeTextEditor?.document.uri;

		if (!fromUri) {
			return;
		}

		const from = relativePath(repository.root, fromUri.fsPath);
		let to = await window.showInputBox({
			value: from,
			valueSelection: [from.length - path.basename(from).length, from.length]
		});

		to = to?.trim();

		if (!to) {
			return;
		}

		await repository.move(from, to);
	}


@command('git.stage')
	async stage(...resourceStates: SourceControlResourceState[]): Promise<void> {
		this.outputChannelLogger.logDebug(`git.stage ${resourceStates.length} `);

		resourceStates = resourceStates.filter(s => !!s);

		if (resourceStates.length === 0 || (resourceStates[0] && !(resourceStates[0].resourceUri instanceof Uri))) {
			const resource = this.getSCMResource();

			this.outputChannelLogger.logDebug(`git.stage.getSCMResource ${resource ? resource.resourceUri.toString() : null} `);

			if (!resource) {
				return;
			}

			resourceStates = [resource];
		}

		const selection = resourceStates.filter(s => s instanceof Resource) as Resource[];
		const { resolved, unresolved, deletionConflicts } = await categorizeResourceByResolution(selection);

		if (unresolved.length > 0) {
			const message = unresolved.length > 1
				? localize('confirm stage files with merge conflicts', "Are you sure you want to stage {0} files with merge conflicts?", unresolved.length)
				: localize('confirm stage file with merge conflicts', "Are you sure you want to stage {0} with merge conflicts?", path.basename(unresolved[0].resourceUri.fsPath));

			const yes = localize('yes', "Yes");
			const pick = await window.showWarningMessage(message, { modal: true }, yes);

			if (pick !== yes) {
				return;
			}
		}

		try {
			await this.runByRepository(deletionConflicts.map(r => r.resourceUri), async (repository, resources) => {
				for (const resource of resources) {
					await this._stageDeletionConflict(repository, resource);
				}
			});
		} catch (err) {
			if (/Cancelled/.test(err.message)) {
				return;
			}

			throw err;
		}

		const workingTree = selection.filter(s => s.resourceGroupType === ResourceGroupType.WorkingTree);
		const untracked = selection.filter(s => s.resourceGroupType === ResourceGroupType.Untracked);
		const scmResources = [...workingTree, ...untracked, ...resolved, ...unresolved];

		this.outputChannelLogger.logDebug(`git.stage.scmResources ${scmResources.length} `);
		if (!scmResources.length) {
			return;
		}

		const resources = scmResources.map(r => r.resourceUri);
		await this.runByRepository(resources, async (repository, resources) => repository.add(resources));
	}


@command('git.stageAll', { repository: true })
	async stageAll(repository: Repository): Promise<void> {
		const resources = [...repository.workingTreeGroup.resourceStates, ...repository.untrackedGroup.resourceStates];
		const uris = resources.map(r => r.resourceUri);

		if (uris.length > 0) {
			const config = workspace.getConfiguration('git', Uri.file(repository.root));
			const untrackedChanges = config.get<'mixed' | 'separate' | 'hidden'>('untrackedChanges');
			await repository.add(uris, untrackedChanges === 'mixed' ? undefined : { update: true });
		}
	}


private async _stageDeletionConflict(repository: Repository, uri: Uri): Promise<void> {
		const uriString = uri.toString();
		const resource = repository.mergeGroup.resourceStates.filter(r => r.resourceUri.toString() === uriString)[0];

		if (!resource) {
			return;
		}

		if (resource.type === Status.DELETED_BY_THEM) {
			const keepIt = localize('keep ours', "Keep Our Version");
			const deleteIt = localize('delete', "Delete File");
			const result = await window.showInformationMessage(localize('deleted by them', "File '{0}' was deleted by them and modified by us.\n\nWhat would you like to do?", path.basename(uri.fsPath)), { modal: true }, keepIt, deleteIt);

			if (result === keepIt) {
				await repository.add([uri]);
			} else if (result === deleteIt) {
				await repository.rm([uri]);
			} else {
				throw new Error('Cancelled');
			}
		} else if (resource.type === Status.DELETED_BY_US) {
			const keepIt = localize('keep theirs', "Keep Their Version");
			const deleteIt = localize('delete', "Delete File");
			const result = await window.showInformationMessage(localize('deleted by us', "File '{0}' was deleted by us and modified by them.\n\nWhat would you like to do?", path.basename(uri.fsPath)), { modal: true }, keepIt, deleteIt);

			if (result === keepIt) {
				await repository.add([uri]);
			} else if (result === deleteIt) {
				await repository.rm([uri]);
			} else {
				throw new Error('Cancelled');
			}
		}
	}


@command('git.stageAllTracked', { repository: true })
	async stageAllTracked(repository: Repository): Promise<void> {
		const resources = repository.workingTreeGroup.resourceStates
			.filter(r => r.type !== Status.UNTRACKED && r.type !== Status.IGNORED);
		const uris = resources.map(r => r.resourceUri);

		await repository.add(uris);
	}


@command('git.stageAllUntracked', { repository: true })
	async stageAllUntracked(repository: Repository): Promise<void> {
		const resources = [...repository.workingTreeGroup.resourceStates, ...repository.untrackedGroup.resourceStates]
			.filter(r => r.type === Status.UNTRACKED || r.type === Status.IGNORED);
		const uris = resources.map(r => r.resourceUri);

		await repository.add(uris);
	}


@command('git.stageAllMerge', { repository: true })
	async stageAllMerge(repository: Repository): Promise<void> {
		const resources = repository.mergeGroup.resourceStates.filter(s => s instanceof Resource) as Resource[];
		const { merge, unresolved, deletionConflicts } = await categorizeResourceByResolution(resources);

		try {
			for (const deletionConflict of deletionConflicts) {
				await this._stageDeletionConflict(repository, deletionConflict.resourceUri);
			}
		} catch (err) {
			if (/Cancelled/.test(err.message)) {
				return;
			}

			throw err;
		}

		if (unresolved.length > 0) {
			const message = unresolved.length > 1
				? localize('confirm stage files with merge conflicts', "Are you sure you want to stage {0} files with merge conflicts?", merge.length)
				: localize('confirm stage file with merge conflicts', "Are you sure you want to stage {0} with merge conflicts?", path.basename(merge[0].resourceUri.fsPath));

			const yes = localize('yes', "Yes");
			const pick = await window.showWarningMessage(message, { modal: true }, yes);

			if (pick !== yes) {
				return;
			}
		}

		const uris = resources.map(r => r.resourceUri);

		if (uris.length > 0) {
			await repository.add(uris);
		}
	}


@command('git.stageChange')
	async stageChange(uri: Uri, changes: LineChange[], index: number): Promise<void> {
		if (!uri) {
			return;
		}

		const textEditor = window.visibleTextEditors.filter(e => e.document.uri.toString() === uri.toString())[0];

		if (!textEditor) {
			return;
		}

		await this._stageChanges(textEditor, [changes[index]]);

		const firstStagedLine = changes[index].modifiedStartLineNumber - 1;
		textEditor.selections = [new Selection(firstStagedLine, 0, firstStagedLine, 0)];
	}


@command('git.stageSelectedRanges', { diff: true })
	async stageSelectedChanges(changes: LineChange[]): Promise<void> {
		const textEditor = window.activeTextEditor;

		if (!textEditor) {
			return;
		}

		const modifiedDocument = textEditor.document;
		const selectedLines = toLineRanges(textEditor.selections, modifiedDocument);
		const selectedChanges = changes
			.map(diff => selectedLines.reduce<LineChange | null>((result, range) => result || intersectDiffWithRange(modifiedDocument, diff, range), null))
			.filter(d => !!d) as LineChange[];

		if (!selectedChanges.length) {
			return;
		}

		await this._stageChanges(textEditor, selectedChanges);
	}


private async _stageChanges(textEditor: TextEditor, changes: LineChange[]): Promise<void> {
		const modifiedDocument = textEditor.document;
		const modifiedUri = modifiedDocument.uri;

		if (modifiedUri.scheme !== 'file') {
			return;
		}

		const originalUri = toGitUri(modifiedUri, '~');
		const originalDocument = await workspace.openTextDocument(originalUri);
		const result = applyLineChanges(originalDocument, modifiedDocument, changes);

		await this.runByRepository(modifiedUri, async (repository, resource) => await repository.stage(resource, result));
	}


@command('git.revertChange')
	async revertChange(uri: Uri, changes: LineChange[], index: number): Promise<void> {
		if (!uri) {
			return;
		}

		const textEditor = window.visibleTextEditors.filter(e => e.document.uri.toString() === uri.toString())[0];

		if (!textEditor) {
			return;
		}

		await this._revertChanges(textEditor, [...changes.slice(0, index), ...changes.slice(index + 1)]);

		const firstStagedLine = changes[index].modifiedStartLineNumber - 1;
		textEditor.selections = [new Selection(firstStagedLine, 0, firstStagedLine, 0)];
	}


@command('git.revertSelectedRanges', { diff: true })
	async revertSelectedRanges(changes: LineChange[]): Promise<void> {
		const textEditor = window.activeTextEditor;

		if (!textEditor) {
			return;
		}

		const modifiedDocument = textEditor.document;
		const selections = textEditor.selections;
		const selectedChanges = changes.filter(change => {
			const modifiedRange = getModifiedRange(modifiedDocument, change);
			return selections.every(selection => !selection.intersection(modifiedRange));
		});

		if (selectedChanges.length === changes.length) {
			return;
		}

		const selectionsBeforeRevert = textEditor.selections;
		await this._revertChanges(textEditor, selectedChanges);
		textEditor.selections = selectionsBeforeRevert;
	}


private async _revertChanges(textEditor: TextEditor, changes: LineChange[]): Promise<void> {
		const modifiedDocument = textEditor.document;
		const modifiedUri = modifiedDocument.uri;

		if (modifiedUri.scheme !== 'file') {
			return;
		}

		const originalUri = toGitUri(modifiedUri, '~');
		const originalDocument = await workspace.openTextDocument(originalUri);
		const visibleRangesBeforeRevert = textEditor.visibleRanges;
		const result = applyLineChanges(originalDocument, modifiedDocument, changes);

		const edit = new WorkspaceEdit();
		edit.replace(modifiedUri, new Range(new Position(0, 0), modifiedDocument.lineAt(modifiedDocument.lineCount - 1).range.end), result);
		workspace.applyEdit(edit);

		await modifiedDocument.save();

		textEditor.revealRange(visibleRangesBeforeRevert[0]);
	}