<commit_before>'use strict';

import { ipcRenderer, remote, clipboard, shell } from 'electron';
import { feedbackHolder } from './feedback';

import path = require('path');
import escape = require('escape-html');
import isDev = require('electron-is-dev');
const { session, app, Menu, dialog } = remote;

// eslint-disable-next-line import/no-unassigned-import
require('./tray');

import DomainUtil = require('./utils/domain-util');
import WebView = require('./components/webview');
import ServerTab = require('./components/server-tab');
import FunctionalTab = require('./components/functional-tab');
import ConfigUtil = require('./utils/config-util');
import DNDUtil = require('./utils/dnd-util');
import ReconnectUtil = require('./utils/reconnect-util');
import Logger = require('./utils/logger-util');
import CommonUtil = require('./utils/common-util');
import EnterpriseUtil = require('./utils/enterprise-util');

const logger = new Logger({
	file: 'errors.log',
	timestamp: true
});

interface FunctionalTabProps {
	name: string;
	materialIcon: string;
	url: string;
}

interface AnyObject {
	[key: string]: any;
}

interface SettingsOptions {
	autoHideMenubar: boolean;
	trayIcon: boolean;
	useManualProxy: boolean;
	useSystemProxy: boolean;
	showSidebar: boolean;
	badgeOption: boolean;
	startAtLogin: boolean;
	startMinimized: boolean;
	enableSpellchecker: boolean;
	showNotification: boolean;
	autoUpdate: boolean;
	betaUpdate: boolean;
	errorReporting: boolean;
	customCSS: boolean;
	silent: boolean;
	lastActiveTab: number;
	dnd: boolean;
	dndPreviousSettings: {
		showNotification: boolean;
		silent: boolean;
		flashTaskbarOnMessage?: boolean;
	};
	downloadsPath: string;
	showDownloadFolder: boolean;
	flashTaskbarOnMessage?: boolean;
	dockBouncing?: boolean;
	loading?: AnyObject;
}

const rendererDirectory = path.resolve(__dirname, '..');
type ServerOrFunctionalTab = ServerTab | FunctionalTab;

class ServerManagerView {
	$addServerButton: HTMLButtonElement;
	$tabsContainer: Element;
	$reloadButton: HTMLButtonElement;
	$loadingIndicator: HTMLButtonElement;
	$settingsButton: HTMLButtonElement;
	$webviewsContainer: Element;
	$backButton: HTMLButtonElement;
	$dndButton: HTMLButtonElement;
	$addServerTooltip: HTMLElement;
	$reloadTooltip: HTMLElement;
	$loadingTooltip: HTMLElement;
	$settingsTooltip: HTMLElement;
	$serverIconTooltip: HTMLCollectionOf<HTMLElement>;
	$backTooltip: HTMLElement;
	$dndTooltip: HTMLElement;
	$sidebar: Element;
	$fullscreenPopup: Element;
	$fullscreenEscapeKey: string;
	loading: AnyObject;
	activeTabIndex: number;
	tabs: ServerOrFunctionalTab[];
	functionalTabs: AnyObject;
	tabIndex: number;
	constructor() {
		this.$addServerButton = document.querySelector('#add-tab');
		this.$tabsContainer = document.querySelector('#tabs-container');

		const $actionsContainer = document.querySelector('#actions-container');
		this.$reloadButton = $actionsContainer.querySelector('#reload-action');
		this.$loadingIndicator = $actionsContainer.querySelector('#loading-action');
		this.$settingsButton = $actionsContainer.querySelector('#settings-action');
		this.$webviewsContainer = document.querySelector('#webviews-container');
		this.$backButton = $actionsContainer.querySelector('#back-action');
		this.$dndButton = $actionsContainer.querySelector('#dnd-action');

		this.$addServerTooltip = document.querySelector('#add-server-tooltip');
		this.$reloadTooltip = $actionsContainer.querySelector('#reload-tooltip');
		this.$loadingTooltip = $actionsContainer.querySelector('#loading-tooltip');
		this.$settingsTooltip = $actionsContainer.querySelector('#setting-tooltip');

		// TODO: This should have been querySelector but the problem is that
		// querySelector doesn't return elements not present in dom whereas somehow
		// getElementsByClassName does. To fix this we need to call this after this.initTabs
		// is called in this.init.
		// eslint-disable-next-line unicorn/prefer-query-selector
		this.$serverIconTooltip = document.getElementsByClassName('server-tooltip') as HTMLCollectionOf<HTMLElement>;
		this.$backTooltip = $actionsContainer.querySelector('#back-tooltip');
		this.$dndTooltip = $actionsContainer.querySelector('#dnd-tooltip');

		this.$sidebar = document.querySelector('#sidebar');

		this.$fullscreenPopup = document.querySelector('#fullscreen-popup');
		this.$fullscreenEscapeKey = process.platform === 'darwin' ? '^⌘F' : 'F11';
		this.$fullscreenPopup.innerHTML = `Press ${this.$fullscreenEscapeKey} to exit full screen`;

		this.loading = {};
		this.activeTabIndex = -1;
		this.tabs = [];
		this.functionalTabs = {};
		this.tabIndex = 0;
	}

	init(): void {
		this.loadProxy().then(() => {
			this.initSidebar();
			this.initPresetOrgs();
			this.initTabs();
			this.initActions();
			this.registerIpcs();
			this.initDefaultSettings();
		});
	}

	loadProxy(): Promise<boolean> {
		return new Promise(resolve => {
			// To change proxyEnable to useManualProxy in older versions
			const proxyEnabledOld = ConfigUtil.isConfigItemExists('useProxy');
			if (proxyEnabledOld) {
				const proxyEnableOldState = ConfigUtil.getConfigItem('useProxy');
				if (proxyEnableOldState) {
					ConfigUtil.setConfigItem('useManualProxy', true);
				}
				ConfigUtil.removeConfigItem('useProxy');
			}

			const proxyEnabled = ConfigUtil.getConfigItem('useManualProxy') || ConfigUtil.getConfigItem('useSystemProxy');
			if (proxyEnabled) {
				session.fromPartition('persist:webviewsession').setProxy({
					pacScript: ConfigUtil.getConfigItem('proxyPAC', ''),
					proxyRules: ConfigUtil.getConfigItem('proxyRules', ''),
					proxyBypassRules: ConfigUtil.getConfigItem('proxyBypass', '')
				}, resolve);
			} else {
				session.fromPartition('persist:webviewsession').setProxy({
					pacScript: '',
					proxyRules: '',
					proxyBypassRules: ''
				}, resolve);
			}
		});
	}

	// Settings are initialized only when user clicks on General/Server/Network section settings
	// In case, user doesn't visit these section, those values set to be null automatically
	// This will make sure the default settings are correctly set to either true or false
	initDefaultSettings(): void {
		// Default settings which should be respected
		const settingOptions: SettingsOptions = {
			autoHideMenubar: false,
			trayIcon: true,
			useManualProxy: false,
			useSystemProxy: false,
			showSidebar: true,
			badgeOption: true,
			startAtLogin: true,
			startMinimized: false,
			enableSpellchecker: true,
			showNotification: true,
			autoUpdate: true,
			betaUpdate: false,
			errorReporting: true,
			customCSS: false,
			silent: false,
			lastActiveTab: 0,
			dnd: false,
			dndPreviousSettings: {
				showNotification: true,
				silent: false
			},
			downloadsPath: `${app.getPath('downloads')}`,
			showDownloadFolder: false
		};

		// Platform specific settings

		if (process.platform === 'win32') {
			// Only available on Windows
			settingOptions.flashTaskbarOnMessage = true;
			settingOptions.dndPreviousSettings.flashTaskbarOnMessage = true;
		}

		if (process.platform === 'darwin') {
			// Only available on macOS
			settingOptions.dockBouncing = true;
		}

		if (process.platform !== 'darwin') {
			settingOptions.autoHideMenubar = false;
		}

		for (const i in settingOptions) {
			const setting = i as keyof SettingsOptions;
			if (ConfigUtil.getConfigItem(i) === null) {
				ConfigUtil.setConfigItem(i, settingOptions[setting]);
			}
		}
	}

	initSidebar(): void {
		const showSidebar = ConfigUtil.getConfigItem('showSidebar', true);
		this.toggleSidebar(showSidebar);
	}

	async queueDomain(domain: any): Promise<boolean> {
		// allows us to start adding multiple domains to the app simultaneously
		// promise of addition resolves in both cases, but we consider it rejected
		// if the resolved value is false
		try {
			const serverConf = await DomainUtil.checkDomain(domain);
			await DomainUtil.addDomain(serverConf);
			return true;
		} catch (err) {
			logger.error(err);
			logger.error('Could not add ' + domain + '. Please contact your system administrator.');
			return false;
		}
	}

	async initPresetOrgs(): Promise<void> {
		// read preset organizations from global_config.json and queues them
		// for addition to the app's domains
		const presetOrgs = EnterpriseUtil.getConfigItem('presetOrganizations', []);
		// set to true if at least one new domain is added
		const domainPromises = [];
		for (const url in presetOrgs) {
			if (DomainUtil.duplicateDomain(presetOrgs[url])) {
				continue;
			}
			domainPromises.push(this.queueDomain(presetOrgs[url]));
		}
		const domainsAdded = await Promise.all(domainPromises);
		if (domainsAdded.includes(true)) {
			// at least one domain was resolved
			ipcRenderer.send('reload-full-app');
		}
	}

	initTabs(): void {
		const servers = DomainUtil.getDomains();
		if (servers.length > 0) {
			for (let i = 0; i < servers.length; i++) {
				this.initServer(servers[i], i);
				DomainUtil.updateSavedServer(servers[i].url, i);
				this.activateTab(i);<commit_msg>enterprise: Detect changes in orgs config.<commit_after>'use strict';

import { ipcRenderer, remote, clipboard, shell } from 'electron';
import { feedbackHolder } from './feedback';

import path = require('path');
import escape = require('escape-html');
import isDev = require('electron-is-dev');
const { session, app, Menu, dialog } = remote;

// eslint-disable-next-line import/no-unassigned-import
require('./tray');

import DomainUtil = require('./utils/domain-util');
import WebView = require('./components/webview');
import ServerTab = require('./components/server-tab');
import FunctionalTab = require('./components/functional-tab');
import ConfigUtil = require('./utils/config-util');
import DNDUtil = require('./utils/dnd-util');
import ReconnectUtil = require('./utils/reconnect-util');
import Logger = require('./utils/logger-util');
import CommonUtil = require('./utils/common-util');
import EnterpriseUtil = require('./utils/enterprise-util');

const logger = new Logger({
	file: 'errors.log',
	timestamp: true
});

interface FunctionalTabProps {
	name: string;
	materialIcon: string;
	url: string;
}

interface AnyObject {
	[key: string]: any;
}

interface SettingsOptions {
	autoHideMenubar: boolean;
	trayIcon: boolean;
	useManualProxy: boolean;
	useSystemProxy: boolean;
	showSidebar: boolean;
	badgeOption: boolean;
	startAtLogin: boolean;
	startMinimized: boolean;
	enableSpellchecker: boolean;
	showNotification: boolean;
	autoUpdate: boolean;
	betaUpdate: boolean;
	errorReporting: boolean;
	customCSS: boolean;
	silent: boolean;
	lastActiveTab: number;
	dnd: boolean;
	dndPreviousSettings: {
		showNotification: boolean;
		silent: boolean;
		flashTaskbarOnMessage?: boolean;
	};
	downloadsPath: string;
	showDownloadFolder: boolean;
	flashTaskbarOnMessage?: boolean;
	dockBouncing?: boolean;
	loading?: AnyObject;
}

const rendererDirectory = path.resolve(__dirname, '..');
type ServerOrFunctionalTab = ServerTab | FunctionalTab;

class ServerManagerView {
	$addServerButton: HTMLButtonElement;
	$tabsContainer: Element;
	$reloadButton: HTMLButtonElement;
	$loadingIndicator: HTMLButtonElement;
	$settingsButton: HTMLButtonElement;
	$webviewsContainer: Element;
	$backButton: HTMLButtonElement;
	$dndButton: HTMLButtonElement;
	$addServerTooltip: HTMLElement;
	$reloadTooltip: HTMLElement;
	$loadingTooltip: HTMLElement;
	$settingsTooltip: HTMLElement;
	$serverIconTooltip: HTMLCollectionOf<HTMLElement>;
	$backTooltip: HTMLElement;
	$dndTooltip: HTMLElement;
	$sidebar: Element;
	$fullscreenPopup: Element;
	$fullscreenEscapeKey: string;
	loading: AnyObject;
	activeTabIndex: number;
	tabs: ServerOrFunctionalTab[];
	functionalTabs: AnyObject;
	tabIndex: number;
	constructor() {
		this.$addServerButton = document.querySelector('#add-tab');
		this.$tabsContainer = document.querySelector('#tabs-container');

		const $actionsContainer = document.querySelector('#actions-container');
		this.$reloadButton = $actionsContainer.querySelector('#reload-action');
		this.$loadingIndicator = $actionsContainer.querySelector('#loading-action');
		this.$settingsButton = $actionsContainer.querySelector('#settings-action');
		this.$webviewsContainer = document.querySelector('#webviews-container');
		this.$backButton = $actionsContainer.querySelector('#back-action');
		this.$dndButton = $actionsContainer.querySelector('#dnd-action');

		this.$addServerTooltip = document.querySelector('#add-server-tooltip');
		this.$reloadTooltip = $actionsContainer.querySelector('#reload-tooltip');
		this.$loadingTooltip = $actionsContainer.querySelector('#loading-tooltip');
		this.$settingsTooltip = $actionsContainer.querySelector('#setting-tooltip');

		// TODO: This should have been querySelector but the problem is that
		// querySelector doesn't return elements not present in dom whereas somehow
		// getElementsByClassName does. To fix this we need to call this after this.initTabs
		// is called in this.init.
		// eslint-disable-next-line unicorn/prefer-query-selector
		this.$serverIconTooltip = document.getElementsByClassName('server-tooltip') as HTMLCollectionOf<HTMLElement>;
		this.$backTooltip = $actionsContainer.querySelector('#back-tooltip');
		this.$dndTooltip = $actionsContainer.querySelector('#dnd-tooltip');

		this.$sidebar = document.querySelector('#sidebar');

		this.$fullscreenPopup = document.querySelector('#fullscreen-popup');
		this.$fullscreenEscapeKey = process.platform === 'darwin' ? '^⌘F' : 'F11';
		this.$fullscreenPopup.innerHTML = `Press ${this.$fullscreenEscapeKey} to exit full screen`;

		this.loading = {};
		this.activeTabIndex = -1;
		this.tabs = [];
		this.functionalTabs = {};
		this.tabIndex = 0;
	}

	init(): void {
		this.loadProxy().then(() => {
			this.initSidebar();
			this.initPresetOrgs();
			this.initTabs();
			this.initActions();
			this.registerIpcs();
			this.initDefaultSettings();
		});
	}

	loadProxy(): Promise<boolean> {
		return new Promise(resolve => {
			// To change proxyEnable to useManualProxy in older versions
			const proxyEnabledOld = ConfigUtil.isConfigItemExists('useProxy');
			if (proxyEnabledOld) {
				const proxyEnableOldState = ConfigUtil.getConfigItem('useProxy');
				if (proxyEnableOldState) {
					ConfigUtil.setConfigItem('useManualProxy', true);
				}
				ConfigUtil.removeConfigItem('useProxy');
			}

			const proxyEnabled = ConfigUtil.getConfigItem('useManualProxy') || ConfigUtil.getConfigItem('useSystemProxy');
			if (proxyEnabled) {
				session.fromPartition('persist:webviewsession').setProxy({
					pacScript: ConfigUtil.getConfigItem('proxyPAC', ''),
					proxyRules: ConfigUtil.getConfigItem('proxyRules', ''),
					proxyBypassRules: ConfigUtil.getConfigItem('proxyBypass', '')
				}, resolve);
			} else {
				session.fromPartition('persist:webviewsession').setProxy({
					pacScript: '',
					proxyRules: '',
					proxyBypassRules: ''
				}, resolve);
			}
		});
	}

	// Settings are initialized only when user clicks on General/Server/Network section settings
	// In case, user doesn't visit these section, those values set to be null automatically
	// This will make sure the default settings are correctly set to either true or false
	initDefaultSettings(): void {
		// Default settings which should be respected
		const settingOptions: SettingsOptions = {
			autoHideMenubar: false,
			trayIcon: true,
			useManualProxy: false,
			useSystemProxy: false,
			showSidebar: true,
			badgeOption: true,
			startAtLogin: true,
			startMinimized: false,
			enableSpellchecker: true,
			showNotification: true,
			autoUpdate: true,
			betaUpdate: false,
			errorReporting: true,
			customCSS: false,
			silent: false,
			lastActiveTab: 0,
			dnd: false,
			dndPreviousSettings: {
				showNotification: true,
				silent: false
			},
			downloadsPath: `${app.getPath('downloads')}`,
			showDownloadFolder: false
		};

		// Platform specific settings

		if (process.platform === 'win32') {
			// Only available on Windows
			settingOptions.flashTaskbarOnMessage = true;
			settingOptions.dndPreviousSettings.flashTaskbarOnMessage = true;
		}

		if (process.platform === 'darwin') {
			// Only available on macOS
			settingOptions.dockBouncing = true;
		}

		if (process.platform !== 'darwin') {
			settingOptions.autoHideMenubar = false;
		}

		for (const i in settingOptions) {
			const setting = i as keyof SettingsOptions;
			if (ConfigUtil.getConfigItem(i) === null) {
				ConfigUtil.setConfigItem(i, settingOptions[setting]);
			}
		}
	}

	initSidebar(): void {
		const showSidebar = ConfigUtil.getConfigItem('showSidebar', true);
		this.toggleSidebar(showSidebar);
	}

	async queueDomain(domain: any): Promise<boolean> {
		// allows us to start adding multiple domains to the app simultaneously
		// promise of addition resolves in both cases, but we consider it rejected
		// if the resolved value is false
		try {
			const serverConf = await DomainUtil.checkDomain(domain);
			await DomainUtil.addDomain(serverConf);
			return true;
		} catch (err) {
			logger.error(err);
			logger.error('Could not add ' + domain + '. Please contact your system administrator.');
			return false;
		}
	}

	async initPresetOrgs(): Promise<void> {
		// read preset organizations from global_config.json and queues them
		// for addition to the app's domains
		const preAddedDomains = DomainUtil.getDomains();
		const presetOrgs = EnterpriseUtil.getConfigItem('presetOrganizations', []);
		// set to true if at least one new domain is added
		const domainPromises = [];
		for (const url in presetOrgs) {
			if (DomainUtil.duplicateDomain(presetOrgs[url])) {
				continue;
			}
			domainPromises.push(this.queueDomain(presetOrgs[url]));
		}
		const domainsAdded = await Promise.all(domainPromises);
		if (domainsAdded.includes(true)) {
			// at least one domain was resolved
			if (preAddedDomains.length > 0) {
				// user already has servers added
				// ask them before reloading the app
				dialog.showMessageBox({
					type: 'question',
					buttons: ['Yes', 'Later'],
					defaultId: 0,
					message: 'New server' + (domainsAdded.length > 1 ? 's' : '') + ' added. Reload app now?'
				}, response => {
					if (response === 0) {
						ipcRenderer.send('reload-full-app');
					}
				});
			} else {
				ipcRenderer.send('reload-full-app');
			}
		}
	}

	initTabs(): void {
		const servers = DomainUtil.getDomains();
		if (servers.length > 0) {
			for (let i = 0; i < servers.length; i++) {
				this.initServer(servers[i], i);
				DomainUtil.updateSavedServer(servers[i].url, i);
				this.activateTab(i);
{'fraction_non_alphanumeric': 0.12627081981397362, 'fraction_numerical': 0.0011897036556348692, 'mean_word_length': 4.764650872817955, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 22, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 10, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5594365', 'n_tokens_mistral': 6025, 'n_tokens_neox': 5467, 'n_words': 1283}