<commit_before>/**
Copyright (c) 2017 Kenny Do

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
**/
/* global browserDetect */
import {updateSetting, validateSettings, cacheCookieStoreIdNames, addExpression, incrementCookieDeletedCounter, cookieCleanup} from "./redux/Actions";
import {getSetting, getHostname, isAWebpage, returnOptionalCookieAPIAttributes, extractMainDomain} from "./services/libs";
import {checkIfProtected, showNumberOfCookiesInIcon} from "./services/BrowserActionService";
import createStore from "./redux/Store";
import shortid from "shortid";

let store;
let currentSettings;

// Delay saving to disk to queue up actions
let delaySave = false;
const saveToStorage = () => {
	if (!delaySave) {
		delaySave = true;
		setTimeout(() => {
			delaySave = false;
			return browser.storage.local.set({
				state: JSON.stringify(store.getState())
			});
		}, 1000);
	}
};

const onSettingsChange = () => {
	let previousSettings = currentSettings;
	currentSettings = store.getState().settings;
	// Container Mode enabled
	if (!previousSettings.contextualIdentities.value && currentSettings.contextualIdentities.value) {
		store.dispatch(
			cacheCookieStoreIdNames()
		);
	}

	// Localstorage support enabled
	if (!previousSettings.localstorageCleanup.value && currentSettings.localstorageCleanup.value) {
		browser.browsingData.removeLocalStorage({
			since: 0
		});
	}

	if (previousSettings.activeMode.value && !currentSettings.activeMode.value) {
		browser.alarms.clear("activeModeAlarm");
	}
};

// Create an alarm delay or use setTimeout before cookie cleanup
let alarmFlag = false;
const createActiveModeAlarm = () => {
	const seconds = parseInt(getSetting(store.getState(), "delayBeforeClean"), 10);
	const minutes = seconds / 60;
	const milliseconds = seconds * 1000;
	if (alarmFlag) {
		return;
	}
	alarmFlag = true;
	if (seconds < 1) {
		setTimeout(() => {
			store.dispatch(
				cookieCleanup({
					greyCleanup: false, ignoreOpenTabs: false
				})
			);
			alarmFlag = false;
		}, 500);
	} else if (browserDetect() === "Firefox" || (browserDetect() === "Chrome" && seconds >= 60)) {
		browser.alarms.create("activeModeAlarm", {
			delayInMinutes: minutes
		});
	} else {
		setTimeout(() => {
			if (getSetting(store.getState(), "activeMode")) {
				store.dispatch(
					cookieCleanup({
						greyCleanup: false, ignoreOpenTabs: false
					})
				);
			}
			alarmFlag = false;
		}, milliseconds);
	}
};

const cleanFromFromTabEvents = async () => {
	if (getSetting(store.getState(), "activeMode")) {
		const alarm = await browser.alarms.get("activeModeAlarm");
		// This is to resolve differences between Firefox and Chrome implementation of browser.alarms.get()
		// in chrome, it returns an array
		if (store.getState().cache.browserDetect === "Firefox" && !alarm) {
			createActiveModeAlarm();
		} else if (alarm.name !== "activeModeAlarm") {
			createActiveModeAlarm();
		}
	}
};

const getAllCookieActions = async (tab) => {
	const cookies = await browser.cookies.getAll(
		returnOptionalCookieAPIAttributes(store.getState(), {
			domain: getHostname(tab.url),
			storeId: tab.cookieStoreId,
			firstPartyDomain: getHostname(tab.url)
		})
	);
	let cookieLength = cookies.length;
	if (cookies.length === 0 && getSetting(store.getState(), "localstorageCleanup") && isAWebpage(tab.url)) {
		browser.cookies.set(
			returnOptionalCookieAPIAttributes(store.getState(), {
				url: tab.url,
				name: "CookieAutoDelete",
				value: "cookieForLocalstorageCleanup",
				path: `/${shortid.generate()}`,
				firstPartyDomain: getHostname(tab.url),
				expirationDate: Math.floor(Date.now() / 1000 + 31557600)
			})
		);
		cookieLength = 1;
	}
	if (getSetting(store.getState(), "showNumOfCookiesInIcon")) {
		showNumberOfCookiesInIcon(tab, cookieLength);
	} else {
		browser.browserAction.setBadgeText({
			text: "", tabId: tab.id
		});
	}
};

// Add a delay to prevent multiple spawns of the localstorage cookie
let onTabUpdateDelay = false;
export const onTabUpdate = (tabId, changeInfo, tab) => {
	if (tab.status === "complete") {
		checkIfProtected(store.getState(), tab);
		if (!onTabUpdateDelay) {
			onTabUpdateDelay = true;
			setTimeout(() => {
				getAllCookieActions(tab);
				onTabUpdateDelay = false;
			}, 750);
		}
	}
};

let tabToDomain = {};
export const onDomainChange = (tabId, changeInfo, tab) => {
	if (tab.status === "complete") {
		const mainDomain = extractMainDomain(getHostname(tab.url));
		if (tabToDomain[tabId] === undefined && mainDomain !== "") {
			tabToDomain[tabId] = mainDomain;
		} else if (tabToDomain[tabId] !== mainDomain && mainDomain !== "") {
			tabToDomain[tabId] = mainDomain;
			if (getSetting(store.getState(), "domainChangeCleanup")) {
				cleanFromFromTabEvents();
			}
		}
	}
};

export const onDomainChangeRemove = (tabId) => {
	delete tabToDomain[tabId];
};

const migration = (oldSettings) => {
	if (Object.keys(oldSettings) !== 0 && oldSettings.migration_1 === undefined && oldSettings.activeMode !== undefined) {
		store.dispatch(
			incrementCookieDeletedCounter(oldSettings.cookieDeletedCounterTotal)
		);
		store.dispatch(
			updateSetting({
				payload: {
					id: 1, name: "activeMode", value: oldSettings.activeMode
				}
			})
		);
		store.dispatch(
			updateSetting({
				payload: {
					id: 2, name: "delayBeforeClean", value: oldSettings.delayBeforeClean
				}
			})
		);
		store.dispatch(
			updateSetting({
				payload: {
					id: 3, name: "statLogging", value: oldSettings.statLoggingSetting
				}
			})
		);
		store.dispatch(
			updateSetting({
				payload: {
					id: 4, name: "showNumOfCookiesInIcon", value: oldSettings.showNumberOfCookiesInIconSetting
				}
			})
		);
		store.dispatch(
			updateSetting({
				payload: {
					id: 5, name: "showNotificationAfterCleanup", value: oldSettings.notifyCookieCleanUpSetting
				}
			})
		);
		store.dispatch(
			updateSetting({
				payload: {
					id: 6, name: "cleanCookiesFromOpenTabsOnStartup", value: oldSettings.cookieCleanUpOnStartSetting
				}
			})
		);
		store.dispatch(
			updateSetting({
				payload: {
					id: 7, name: "contextualIdentities", value: oldSettings.contextualIdentitiesEnabledSetting
				}
			})
		);
		if (oldSettings.contextualIdentitiesEnabledSetting) {
			store.dispatch(
				cacheCookieStoreIdNames()
			);
			const newContainerCache = [
				...oldSettings.containerCache,
				{
					cookieStoreId: "firefox-default"
				}
			];
			newContainerCache.forEach((container) => {
				const {
					cookieStoreId
				} = container;
				const greyPrefixed = `${cookieStoreId}-Grey`;
				if (oldSettings[cookieStoreId] !== undefined) {
					oldSettings[cookieStoreId].forEach((domain) => {
						const expression = oldSettings.enableGlobalSubdomainSetting ? `*.${domain}` : domain;
						store.dispatch(
							addExpression({
								payload: {
									expression,
									listType: "WHITE",
									storeId: cookieStoreId
								}
							})
						);
					});
				}
				if (oldSettings[greyPrefixed] !== undefined) {
					oldSettings[greyPrefixed].forEach((domain) => {
						const expression = oldSettings.enableGlobalSubdomainSetting ? `*.${domain}` : domain;
						store.dispatch(
							addExpression({
								payload: {
									expression,
									listType: "GREY",
									storeId: cookieStoreId
								}
							})
						);
					});
				}
			});
		} else {
			const cookieStoreId = "defaultWhiteList";
			const greyPrefixed = `${cookieStoreId}-Grey`;
			if (oldSettings[cookieStoreId] !== undefined) {
				oldSettings[cookieStoreId].forEach((domain) => {
					const expression = oldSettings.enableGlobalSubdomainSetting ? `*.${domain}` : domain;
					store.dispatch(
						addExpression({
							payload: {
								expression,
								listType: "WHITE",
								storeId: cookieStoreId
							}
						})
					);
				});
			}
			if (oldSettings[greyPrefixed] !== undefined) {
				oldSettings[greyPrefixed].forEach((domain) => {
					const expression = oldSettings.enableGlobalSubdomainSetting ? `*.${domain}` : domain;
					store.dispatch(
						addExpression({
							payload: {
								expression,
								listType: "GREY",
								storeId: cookieStoreId
							}
						})
					);
				});
			}
		}
		saveToStorage();
		browser.storage.local.set({
			migration_1: true
		});
	}
};

const onStartUp = async () => {
	const storage = await browser.storage.local.get();
	let stateFromStorage;
	try {
		if (storage.state !== undefined) {
			stateFromStorage = JSON.parse(storage.state);
		} else {
			stateFromStorage = {};
		}
	} catch (err) {
		stateFromStorage = {};
	}
	store = createStore(stateFromStorage);
	migration(storage);
	store.dispatch(
		validateSettings()
	);
	store.dispatch({
		type: "ON_STARTUP"
	});
	// Store the FF version in cache
	if (browserDetect() === "Firefox") {
		const browserInfo = await browser.runtime.getBrowserInfo();
		const browserVersion = browserInfo.version.split(".")[0];
		store.dispatch({
			type: "ADD_CACHE",
			map: {
				key: "browserVersion", value: browserVersion
			}
		});

		// Store whether firstPartyIsolate is true or false
		if (browserVersion >= 58) {
			const setting = await browser.privacy.websites.firstPartyIsolate.get({});
			store.dispatch({
				type: "ADD_CACHE",
				map: {
					key: "firstPartyIsolateSetting", value: setting.value
				}
			});
			if (browserVersion === "58" && setting.value) {
				browser.notifications.create("FPI_NOTIFICATION", {
					"type": "basic",
					"iconUrl": browser.extension.getURL("icons/icon_48.png"),
					"title": "First Party Isolation Detected",
					"message": "Please turn off privacy.firstparty.isolate and restart the browser as it breaks cookie cleanup"
				});
			}
		}
	}
	// Store which browser environment in cache
	store.dispatch({
		type: "ADD_CACHE",
		map: {
			key: "browserDetect", value: browserDetect()
		}
	});

	// Temporary fix until contextualIdentities events land
	if (getSetting(store.getState(), "contextualIdentities")) {
		store.dispatch(
			cacheCookieStoreIdNames()
		);
	}
	if (getSetting(store.getState(), "activeMode")) {
		store.dispatch(
			cookieCleanup({
				greyCleanup: true, ignoreOpenTabs: getSetting(store.getState(), "cleanCookiesFromOpenTabsOnStartup")
			})
		);
	}
	currentSettings = store.getState().settings;
	store.subscribe(onSettingsChange);
	store.subscribe(saveToStorage);
};

onStartUp();

// Logic that controls when to disable the browser action
browser.tabs.onUpdated.addListener(onTabUpdate);
browser.tabs.onUpdated.addListener(onDomainChange);
browser.tabs.onRemoved.addListener(onDomainChangeRemove);
browser.tabs.onRemoved.addListener(cleanFromFromTabEvents);

// Alarm event handler for Active Mode
browser.alarms.onAlarm.addListener((alarmInfo) => {
	// console.log(alarmInfo.name);
	if (alarmInfo.name === "activeModeAlarm") {
		store.dispatch(
			cookieCleanup({
				greyCleanup: false, ignoreOpenTabs: false
			})
		);
		alarmFlag = false;
		browser.alarms.clear(alarmInfo.name);
	}
});
<commit_msg>Use the tab storeId to set the localstorage cookie<commit_after>/**
Copyright (c) 2017 Kenny Do

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
**/
/* global browserDetect */
import {updateSetting, validateSettings, cacheCookieStoreIdNames, addExpression, incrementCookieDeletedCounter, cookieCleanup} from "./redux/Actions";
import {getSetting, getHostname, isAWebpage, returnOptionalCookieAPIAttributes, extractMainDomain} from "./services/libs";
import {checkIfProtected, showNumberOfCookiesInIcon} from "./services/BrowserActionService";
import createStore from "./redux/Store";
import shortid from "shortid";

let store;
let currentSettings;

// Delay saving to disk to queue up actions
let delaySave = false;
const saveToStorage = () => {
	if (!delaySave) {
		delaySave = true;
		setTimeout(() => {
			delaySave = false;
			return browser.storage.local.set({
				state: JSON.stringify(store.getState())
			});
		}, 1000);
	}
};

const onSettingsChange = () => {
	let previousSettings = currentSettings;
	currentSettings = store.getState().settings;
	// Container Mode enabled
	if (!previousSettings.contextualIdentities.value && currentSettings.contextualIdentities.value) {
		store.dispatch(
			cacheCookieStoreIdNames()
		);
	}

	// Localstorage support enabled
	if (!previousSettings.localstorageCleanup.value && currentSettings.localstorageCleanup.value) {
		browser.browsingData.removeLocalStorage({
			since: 0
		});
	}

	if (previousSettings.activeMode.value && !currentSettings.activeMode.value) {
		browser.alarms.clear("activeModeAlarm");
	}
};

// Create an alarm delay or use setTimeout before cookie cleanup
let alarmFlag = false;
const createActiveModeAlarm = () => {
	const seconds = parseInt(getSetting(store.getState(), "delayBeforeClean"), 10);
	const minutes = seconds / 60;
	const milliseconds = seconds * 1000;
	if (alarmFlag) {
		return;
	}
	alarmFlag = true;
	if (seconds < 1) {
		setTimeout(() => {
			store.dispatch(
				cookieCleanup({
					greyCleanup: false, ignoreOpenTabs: false
				})
			);
			alarmFlag = false;
		}, 500);
	} else if (browserDetect() === "Firefox" || (browserDetect() === "Chrome" && seconds >= 60)) {
		browser.alarms.create("activeModeAlarm", {
			delayInMinutes: minutes
		});
	} else {
		setTimeout(() => {
			if (getSetting(store.getState(), "activeMode")) {
				store.dispatch(
					cookieCleanup({
						greyCleanup: false, ignoreOpenTabs: false
					})
				);
			}
			alarmFlag = false;
		}, milliseconds);
	}
};

const cleanFromFromTabEvents = async () => {
	if (getSetting(store.getState(), "activeMode")) {
		const alarm = await browser.alarms.get("activeModeAlarm");
		// This is to resolve differences between Firefox and Chrome implementation of browser.alarms.get()
		// in chrome, it returns an array
		if (store.getState().cache.browserDetect === "Firefox" && !alarm) {
			createActiveModeAlarm();
		} else if (alarm.name !== "activeModeAlarm") {
			createActiveModeAlarm();
		}
	}
};

const getAllCookieActions = async (tab) => {
	const cookies = await browser.cookies.getAll(
		returnOptionalCookieAPIAttributes(store.getState(), {
			domain: getHostname(tab.url),
			storeId: tab.cookieStoreId,
			firstPartyDomain: getHostname(tab.url)
		})
	);
	let cookieLength = cookies.length;
	if (cookies.length === 0 && getSetting(store.getState(), "localstorageCleanup") && isAWebpage(tab.url)) {
		browser.cookies.set(
			returnOptionalCookieAPIAttributes(store.getState(), {
				url: tab.url,
				name: "CookieAutoDelete",
				value: "cookieForLocalstorageCleanup",
				path: `/${shortid.generate()}`,
				storeId: tab.cookieStoreId,
				firstPartyDomain: extractMainDomain(getHostname(tab.url)),
				expirationDate: Math.floor(Date.now() / 1000 + 31557600)
			})
		);
		cookieLength = 1;
	}
	if (getSetting(store.getState(), "showNumOfCookiesInIcon")) {
		showNumberOfCookiesInIcon(tab, cookieLength);
	} else {
		browser.browserAction.setBadgeText({
			text: "", tabId: tab.id
		});
	}
};

// Add a delay to prevent multiple spawns of the localstorage cookie
let onTabUpdateDelay = false;
export const onTabUpdate = (tabId, changeInfo, tab) => {
	if (tab.status === "complete") {
		checkIfProtected(store.getState(), tab);
		if (!onTabUpdateDelay) {
			onTabUpdateDelay = true;
			setTimeout(() => {
				getAllCookieActions(tab);
				onTabUpdateDelay = false;
			}, 750);
		}
	}
};

let tabToDomain = {};
export const onDomainChange = (tabId, changeInfo, tab) => {
	if (tab.status === "complete") {
		const mainDomain = extractMainDomain(getHostname(tab.url));
		if (tabToDomain[tabId] === undefined && mainDomain !== "") {
			tabToDomain[tabId] = mainDomain;
		} else if (tabToDomain[tabId] !== mainDomain && mainDomain !== "") {
			tabToDomain[tabId] = mainDomain;
			if (getSetting(store.getState(), "domainChangeCleanup")) {
				cleanFromFromTabEvents();
			}
		}
	}
};

export const onDomainChangeRemove = (tabId) => {
	delete tabToDomain[tabId];
};

const migration = (oldSettings) => {
	if (Object.keys(oldSettings) !== 0 && oldSettings.migration_1 === undefined && oldSettings.activeMode !== undefined) {
		store.dispatch(
			incrementCookieDeletedCounter(oldSettings.cookieDeletedCounterTotal)
		);
		store.dispatch(
			updateSetting({
				payload: {
					id: 1, name: "activeMode", value: oldSettings.activeMode
				}
			})
		);
		store.dispatch(
			updateSetting({
				payload: {
					id: 2, name: "delayBeforeClean", value: oldSettings.delayBeforeClean
				}
			})
		);
		store.dispatch(
			updateSetting({
				payload: {
					id: 3, name: "statLogging", value: oldSettings.statLoggingSetting
				}
			})
		);
		store.dispatch(
			updateSetting({
				payload: {
					id: 4, name: "showNumOfCookiesInIcon", value: oldSettings.showNumberOfCookiesInIconSetting
				}
			})
		);
		store.dispatch(
			updateSetting({
				payload: {
					id: 5, name: "showNotificationAfterCleanup", value: oldSettings.notifyCookieCleanUpSetting
				}
			})
		);
		store.dispatch(
			updateSetting({
				payload: {
					id: 6, name: "cleanCookiesFromOpenTabsOnStartup", value: oldSettings.cookieCleanUpOnStartSetting
				}
			})
		);
		store.dispatch(
			updateSetting({
				payload: {
					id: 7, name: "contextualIdentities", value: oldSettings.contextualIdentitiesEnabledSetting
				}
			})
		);
		if (oldSettings.contextualIdentitiesEnabledSetting) {
			store.dispatch(
				cacheCookieStoreIdNames()
			);
			const newContainerCache = [
				...oldSettings.containerCache,
				{
					cookieStoreId: "firefox-default"
				}
			];
			newContainerCache.forEach((container) => {
				const {
					cookieStoreId
				} = container;
				const greyPrefixed = `${cookieStoreId}-Grey`;
				if (oldSettings[cookieStoreId] !== undefined) {
					oldSettings[cookieStoreId].forEach((domain) => {
						const expression = oldSettings.enableGlobalSubdomainSetting ? `*.${domain}` : domain;
						store.dispatch(
							addExpression({
								payload: {
									expression,
									listType: "WHITE",
									storeId: cookieStoreId
								}
							})
						);
					});
				}
				if (oldSettings[greyPrefixed] !== undefined) {
					oldSettings[greyPrefixed].forEach((domain) => {
						const expression = oldSettings.enableGlobalSubdomainSetting ? `*.${domain}` : domain;
						store.dispatch(
							addExpression({
								payload: {
									expression,
									listType: "GREY",
									storeId: cookieStoreId
								}
							})
						);
					});
				}
			});
		} else {
			const cookieStoreId = "defaultWhiteList";
			const greyPrefixed = `${cookieStoreId}-Grey`;
			if (oldSettings[cookieStoreId] !== undefined) {
				oldSettings[cookieStoreId].forEach((domain) => {
					const expression = oldSettings.enableGlobalSubdomainSetting ? `*.${domain}` : domain;
					store.dispatch(
						addExpression({
							payload: {
								expression,
								listType: "WHITE",
								storeId: cookieStoreId
							}
						})
					);
				});
			}
			if (oldSettings[greyPrefixed] !== undefined) {
				oldSettings[greyPrefixed].forEach((domain) => {
					const expression = oldSettings.enableGlobalSubdomainSetting ? `*.${domain}` : domain;
					store.dispatch(
						addExpression({
							payload: {
								expression,
								listType: "GREY",
								storeId: cookieStoreId
							}
						})
					);
				});
			}
		}
		saveToStorage();
		browser.storage.local.set({
			migration_1: true
		});
	}
};

const onStartUp = async () => {
	const storage = await browser.storage.local.get();
	let stateFromStorage;
	try {
		if (storage.state !== undefined) {
			stateFromStorage = JSON.parse(storage.state);
		} else {
			stateFromStorage = {};
		}
	} catch (err) {
		stateFromStorage = {};
	}
	store = createStore(stateFromStorage);
	migration(storage);
	store.dispatch(
		validateSettings()
	);
	store.dispatch({
		type: "ON_STARTUP"
	});
	// Store the FF version in cache
	if (browserDetect() === "Firefox") {
		const browserInfo = await browser.runtime.getBrowserInfo();
		const browserVersion = browserInfo.version.split(".")[0];
		store.dispatch({
			type: "ADD_CACHE",
			map: {
				key: "browserVersion", value: browserVersion
			}
		});

		// Store whether firstPartyIsolate is true or false
		if (browserVersion >= 58) {
			const setting = await browser.privacy.websites.firstPartyIsolate.get({});
			store.dispatch({
				type: "ADD_CACHE",
				map: {
					key: "firstPartyIsolateSetting", value: setting.value
				}
			});
			if (browserVersion === "58" && setting.value) {
				browser.notifications.create("FPI_NOTIFICATION", {
					"type": "basic",
					"iconUrl": browser.extension.getURL("icons/icon_48.png"),
					"title": "First Party Isolation Detected",
					"message": "Please turn off privacy.firstparty.isolate and restart the browser as it breaks cookie cleanup"
				});
			}
		}
	}
	// Store which browser environment in cache
	store.dispatch({
		type: "ADD_CACHE",
		map: {
			key: "browserDetect", value: browserDetect()
		}
	});

	// Temporary fix until contextualIdentities events land
	if (getSetting(store.getState(), "contextualIdentities")) {
		store.dispatch(
			cacheCookieStoreIdNames()
		);
	}
	if (getSetting(store.getState(), "activeMode")) {
		store.dispatch(
			cookieCleanup({
				greyCleanup: true, ignoreOpenTabs: getSetting(store.getState(), "cleanCookiesFromOpenTabsOnStartup")
			})
		);
	}
	currentSettings = store.getState().settings;
	store.subscribe(onSettingsChange);
	store.subscribe(saveToStorage);
};

onStartUp();

// Logic that controls when to disable the browser action
browser.tabs.onUpdated.addListener(onTabUpdate);
browser.tabs.onUpdated.addListener(onDomainChange);
browser.tabs.onRemoved.addListener(onDomainChangeRemove);
browser.tabs.onRemoved.addListener(cleanFromFromTabEvents);

// Alarm event handler for Active Mode
browser.alarms.onAlarm.addListener((alarmInfo) => {
	// console.log(alarmInfo.name);
	if (alarmInfo.name === "activeModeAlarm") {
		store.dispatch(
			cookieCleanup({
				greyCleanup: false, ignoreOpenTabs: false
			})
		);
		alarmFlag = false;
		browser.alarms.clear(alarmInfo.name);
	}
});
<|endoftext|>


<commit_before>import ConnectionDefinitionGenerator from './ConnectionDefinitionGenerator';

const defaultTestId = "1234"

describe('createLabel', () => {
  function create(connections) {
      const generator = new ConnectionDefinitionGenerator(connections)
      return generator;
  }

  test('with empty connetions', () => {
    const generator = create([])

    expect(generator.getLabels('Connection1')).toEqual([])
  })
});

describe('createPlacing', () => {

  test('with empty connetions', () => {
    const generator = new ConnectionDefinitionGenerator([])

    expect(generator.getPlacings('Connection1')).toEqual([])
  })
});

describe('getConnectionStyle', () => {
  function create(connections) {
    const generator = new ConnectionDefinitionGenerator(connections)
    return generator;
  }

  test('with empty connetions', () => {
    const generator = create([])

    expect(generator.getConnectionStyle('Connection1')).toEqual({})
  })

  // Braucht eine Version mit positionOffset 1.0
  test('a Style by a Connection with a Line Placing', () => {
    const connections = [
      {
        "name": "Connection1",
        "placings": [
          {
            "positionOffset": 0.0,
            "shape": {
              "endPoint": {
                "x": 1,
                "y": 1,
              },
              "startPoint": {
                "x": 0,
                "y": 0,
              },
              "type": "line",
            }
          }
        ]
      }
    ]

    const generator = create(connections);
    expect(generator.getConnectionStyle('Connection1')).toEqual(
      {
        ".connection": {"stroke": "black"},
        ".marker-source": {"d": "M 0 0 L 1 1",
        "transform": "scale(1,1)"},
        ".marker-target": {"d": "M 0 0"}
      }
    )
  })

  test('a Style by a Connection with a Line Placing', () => {
    const connections = [
      {
        "name": "Connection1",
        "placings": [
          {
            "positionOffset": 0.0,
            "shape": {
              "endPoint": {
                "x": 1,
                "y": 1,
              },
              "startPoint": {
                "x": 0,
                "y": 0,
              },
              "type": "line",
            }
          }
        ]
      }
    ]

    const generator = create(connections);
    expect(generator.getConnectionStyle('Connection1')).toEqual(
      {
        ".connection": {"stroke": "black"},
        ".marker-source": {"d": "M 0 0 L 1 1",
        "transform": "scale(1,1)"},
        ".marker-target": {"d": "M 0 0"}
      }
    )
  })

  test('a Style by a Connection with a Polyline Placing with one point', () => {
    const connections = [
      {
        "name": "Connection1",
        "placings": [
          {
            "positionOffset": 0.0,
            "shape": {
              "points": [
                {
                  "x": 0,
                  "y": 0
                }
              ],
              "type": "polyline",
            }
          }

        ]
      }
    ]

    const generator = create(connections);
    expect(generator.getConnectionStyle('Connection1')).toEqual(
      {
        ".connection": {
        "stroke": "black",
        },
         ".marker-source": {
           "d": "M 0 0 ",
           "transform": "scale(1,1)",
         },
         ".marker-target": {
             "d": "M 0 0",
         }
       })
  })

  test('a Style by a Connection with a Polyline Placing with one point (mirrored-point)', () => {
    const connections = [
      {
        "name": "Connection1",
        "placings": [
          {
            "positionOffset": 1.0,
            "shape": {
              "points": [
                {
                  "x": 1,
                  "y": 1
                }
              ],
              "type": "polyline",
            }
          }

        ]
      }
    ]

    const generator = create(connections);
    expect(generator.getConnectionStyle('Connection1')).toEqual(
      {
        ".connection": {"stroke": "black"},
        ".marker-target": {
          "d": "M -1 1 ",
          "transform": "scale(1,1)"
        }
      })
  })

  test('a Style by a Connection with a Polyline Placing with multiple point', () => {
    const connections = [
      {
        "name": "Connection1",
        "placings": [
          {
            "positionOffset": 0.0,
            "shape": {
              "points": [
                {
                  "x": 0,
                  "y": 0
                },
                {
                  "x": 1,
                  "y": 1
                }
                ,
                {
                  "x": 2,
                  "y": 2
                }
              ],
              "type": "polyline",
            }
          }

        ]
      }
    ]

    const generator = create(connections);
    expect(generator.getConnectionStyle('Connection1')).toEqual(
      {
        ".connection": {
        "stroke": "black",
        },
         ".marker-source": {
           "d": "M 0 0 L 1 1L 2 2",
           "transform": "scale(1,1)",
         },
         ".marker-target": {
             "d": "M 0 0",
         }
       })
  })

  test('a Style by a Connection with a Polyline Placing with multiple point (mirrored points)', () => {
    const connections = [
      {
        "name": "Connection1",
        "placings": [
          {
            "positionOffset": 1.0,
            "shape": {
              "points": [
                {
                  "x": 1,
                  "y": 1
                },
                {
                  "x": 2,
                  "y": 2
                }
                ,
                {
                  "x": 3,
                  "y": 3
                }
              ],
              "type": "polyline",
            }
          }

        ]
      }
    ]

    const generator = create(connections);
    expect(generator.getConnectionStyle('Connection1')).toEqual(
      {
        ".connection": {"stroke": "black"},
        ".marker-target": {
          "d": "M -1 1 L -2 2L -3 3",
          "transform": "scale(1,1)"
        }
      }
)
  })

  test('a Style by a Connection with a Polygon Placing', () => {
    const connections = [
      {
        "name": "Connection1",
        "placings": [
          {
            "positionOffset": 0.0,
            "shape": {
              "points": [
                {
                  "x": 0,
                  "y": 0
                },
                {
                  "x": 1,
                  "y": 1
                }
                ,
                {
                  "x": 2,
                  "y": 2
                }
              ],
              "type": "polygon",
            }
          }
        ]
      }
    ]

    const generator = create(connections);
    expect(generator.getConnectionStyle('Connection1')).toEqual( {
         ".connection": {
           "stroke": "black",
         },
         ".marker-source": {
           "d": "M 0 0 L 1 1L 2 2z",
           "transform": "scale(1,1)",
         },
         ".marker-target": {
           "d": "M 0 0",
         },
       }
    )
  })

  test('a Style by a Connection with a Polygon Placing (mirrored points)', () => {
    const connections = [
      {
        "name": "Connection1",
        "placings": [
          {
            "positionOffset": 1.0,
            "shape": {
              "points": [
                {
                  "x": 0,
                  "y": 0
                },
                {
                  "x": 1,
                  "y": 1
                }
                ,
                {
                  "x": 2,
                  "y": 2
                }
              ],
              "type": "polygon",
            }
          }
        ]
      }
    ]

    const generator = create(connections);
    expect(generator.getConnectionStyle('Connection1')).toEqual( {
         ".connection": {
           "stroke": "black",
         },
         ".marker-target": {
           "d": "M 0 0 L -1 -1L -2 -2z",
           "transform": "scale(1,1)",
         }
       }
    )
  })

  test('a Style by a Connection with a Rectangle Placing', () => {
    const connections = [
      {
        "name": "Connection1",
        "placings": [
          {
            "positionOffset": 0.0,
            "shape": {
              "textBody": "positionOffset1",
              "sizeHeight": 2,
              "sizeWidth": 3,
              "position": {
                "x": 0,
                "y": 0,
              },
              "type": "rectangle",
            }
          }
        ]
      }
    ]

    const generator = create(connections);
    expect(generator.getConnectionStyle('Connection1')).toEqual({
      ".connection": {"stroke": "black"},
      ".marker-source": {
        "d": "M 0 0l 3 0 l 0 2 l -3 0 z",
        "transform": "scale(1,1)"},
      ".marker-target": {"d": "M 0 0"}}
  )
  })

  test('a Style by a Connection with a RoundedRectangle Placing', () => {
    const connections = [
      {
        "name": "Connection1",
        "placings": [
          {
            "positionOffset": 0.0,
            "shape": {
              "curveWidth": 4,
              "curveHeight": 4,
              "position": {
                "x": 1,
                "y": 1
              },
              "sizeHeight": 3,
              "sizeWidth": 3,
              "type": "roundedRectangle",
            }
          }
        ]
      }
    ]
    const generator = create(connections);
    expect(generator.getConnectionStyle('Connection1')).toEqual({
      ".connection": {"stroke": "black"},
      ".marker-source": {
        "d": "M 1 4 1 4 l -5l 0 a 4 4 0 0 1 4 4l 0 -5 a 4 4 0 0 1 -4 4 l --5 0 a 4 4 0 0 1 -4 -4 l 0 --5 a 4 4 0 0 1 4 -4",
        "transform": "scale(1,1)"
      },
      ".marker-target": {"d": "M 0 0"}}
  )
  })

  test('a Style by a Connection with a Ellipse Placing', () => {
    const connections = [
      {
        "name": "Connection1",
        "style": "testStyle",
        "placings": [
          {
            "positionOffset": 0.0,
            "shape": {
              "sizeHeight": 4,
              "sizeWidth": 4,
              "position": {
                "x": 1,
                "y": 1
              },
              "type": "ellipse"
            }
          }
        ]
      }
    ]

    const generator = create(connections);
    expect(generator.getConnectionStyle('Connection1')).toEqual({
      ".marker-source": {
        "d": "M 1 1 a  2 2 0 0 1 2 -2 a  2 2 0 0 1 2 2 a  2 2 0 0 1 -2 2 a  2 2 0 0 1 -2 -2",
        "transform": "scale(1,1)"
      },
      ".marker-target": {"d": "M 0 0"}
    }
  )
  })

  test('a Connection without a Style', () => {
    const connections = [
      {
        "name": "Connection1"
      }
    ]

    const generator = create(connections);
    expect(generator.getConnectionStyle('Connection1')).toEqual({
      '.connection':{stroke: 'black'},
      '.marker-target': {"d": "M 0 0"}
    })
  })

  test('a Connection with a Style', () => {
    const connections = [
      {
        "name": "Connection1",
        "style": "testStyle"
      }
    ]

    const generator = create(connections);
    expect(generator.getConnectionStyle('Connection1')).toEqual({'.marker-target': {"d": "M 0 0"}}
    )
  })

});<commit_msg>#29 replace dummy style in test<commit_after>import ConnectionDefinitionGenerator from './ConnectionDefinitionGenerator';
import StyleGenerator from './StyleGenerator'

const defaultTestId = "1234"

describe('createLabel', () => {
  function create(connections) {
      const generator = new ConnectionDefinitionGenerator(connections)
      return generator;
  }

  test('with empty connetions', () => {
    const generator = create([])

    expect(generator.getLabels('Connection1')).toEqual([])
  })
});

describe('createPlacing', () => {

  test('with empty connetions', () => {
    const generator = new ConnectionDefinitionGenerator([])

    expect(generator.getPlacings('Connection1')).toEqual([])
  })
});

describe('getConnectionStyle', () => {
  function create(connections) {
    const defaultStyle = [
      {
        name: 'DefaultStyle',
      }
    ];

    const styleGenerator = new StyleGenerator(defaultStyle)
    const generator = new ConnectionDefinitionGenerator(connections, styleGenerator)
    return generator;
  }

  test('with empty connetions', () => {
    const generator = create([])

    expect(generator.getConnectionStyle('Connection1')).toEqual({})
  })

  test('a Style by a Connection with a Line Placing', () => {
    const connections = [
      {
        "name": "Connection1",
        "placings": [
          {
            "positionOffset": 0.0,
            "shape": {
              "endPoint": {
                "x": 1,
                "y": 1,
              },
              "startPoint": {
                "x": 0,
                "y": 0,
              },
              "type": "line",
            }
          }
        ]
      }
    ]

    const generator = create(connections);
    expect(generator.getConnectionStyle('Connection1')).toEqual(
      {
        ".connection": {"stroke": "black"},
        ".marker-source": {"d": "M 0 0 L 1 1",
        "transform": "scale(1,1)"},
        ".marker-target": {"d": "M 0 0"}
      }
    )
  })

  test('a Style by a Connection with a Line Placing', () => {
    const connections = [
      {
        "name": "Connection1",
        "placings": [
          {
            "positionOffset": 0.0,
            "shape": {
              "endPoint": {
                "x": 1,
                "y": 1,
              },
              "startPoint": {
                "x": 0,
                "y": 0,
              },
              "type": "line",
            }
          }
        ]
      }
    ]

    const generator = create(connections);
    expect(generator.getConnectionStyle('Connection1')).toEqual(
      {
        ".connection": {"stroke": "black"},
        ".marker-source": {"d": "M 0 0 L 1 1",
        "transform": "scale(1,1)"},
        ".marker-target": {"d": "M 0 0"}
      }
    )
  })

  test('a Style by a Connection with a Polyline Placing with one point', () => {
    const connections = [
      {
        "name": "Connection1",
        "placings": [
          {
            "positionOffset": 0.0,
            "shape": {
              "points": [
                {
                  "x": 0,
                  "y": 0
                }
              ],
              "type": "polyline",
            }
          }

        ]
      }
    ]

    const generator = create(connections);
    expect(generator.getConnectionStyle('Connection1')).toEqual(
      {
        ".connection": {
        "stroke": "black",
        },
         ".marker-source": {
           "d": "M 0 0 ",
           "transform": "scale(1,1)",
         },
         ".marker-target": {
             "d": "M 0 0",
         }
       })
  })

  test('a Style by a Connection with a Polyline Placing with one point (mirrored-point)', () => {
    const connections = [
      {
        "name": "Connection1",
        "placings": [
          {
            "positionOffset": 1.0,
            "shape": {
              "points": [
                {
                  "x": 1,
                  "y": 1
                }
              ],
              "type": "polyline",
            }
          }

        ]
      }
    ]

    const generator = create(connections);
    expect(generator.getConnectionStyle('Connection1')).toEqual(
      {
        ".connection": {"stroke": "black"},
        ".marker-target": {
          "d": "M -1 1 ",
          "transform": "scale(1,1)"
        }
      })
  })

  test('a Style by a Connection with a Polyline Placing with multiple point', () => {
    const connections = [
      {
        "name": "Connection1",
        "placings": [
          {
            "positionOffset": 0.0,
            "shape": {
              "points": [
                {
                  "x": 0,
                  "y": 0
                },
                {
                  "x": 1,
                  "y": 1
                }
                ,
                {
                  "x": 2,
                  "y": 2
                }
              ],
              "type": "polyline",
            }
          }

        ]
      }
    ]

    const generator = create(connections);
    expect(generator.getConnectionStyle('Connection1')).toEqual(
      {
        ".connection": {
        "stroke": "black",
        },
         ".marker-source": {
           "d": "M 0 0 L 1 1L 2 2",
           "transform": "scale(1,1)",
         },
         ".marker-target": {
             "d": "M 0 0",
         }
       })
  })

  test('a Style by a Connection with a Polyline Placing with multiple point (mirrored points)', () => {
    const connections = [
      {
        "name": "Connection1",
        "placings": [
          {
            "positionOffset": 1.0,
            "shape": {
              "points": [
                {
                  "x": 1,
                  "y": 1
                },
                {
                  "x": 2,
                  "y": 2
                }
                ,
                {
                  "x": 3,
                  "y": 3
                }
              ],
              "type": "polyline",
            }
          }

        ]
      }
    ]

    const generator = create(connections);
    expect(generator.getConnectionStyle('Connection1')).toEqual(
      {
        ".connection": {"stroke": "black"},
        ".marker-target": {
          "d": "M -1 1 L -2 2L -3 3",
          "transform": "scale(1,1)"
        }
      }
)
  })

  test('a Style by a Connection with a Polygon Placing', () => {
    const connections = [
      {
        "name": "Connection1",
        "placings": [
          {
            "positionOffset": 0.0,
            "shape": {
              "points": [
                {
                  "x": 0,
                  "y": 0
                },
                {
                  "x": 1,
                  "y": 1
                }
                ,
                {
                  "x": 2,
                  "y": 2
                }
              ],
              "type": "polygon",
            }
          }
        ]
      }
    ]

    const generator = create(connections);
    expect(generator.getConnectionStyle('Connection1')).toEqual( {
         ".connection": {
           "stroke": "black",
         },
         ".marker-source": {
           "d": "M 0 0 L 1 1L 2 2z",
           "transform": "scale(1,1)",
         },
         ".marker-target": {
           "d": "M 0 0",
         },
       }
    )
  })

  test('a Style by a Connection with a Polygon Placing (mirrored points)', () => {
    const connections = [
      {
        "name": "Connection1",
        "placings": [
          {
            "positionOffset": 1.0,
            "shape": {
              "points": [
                {
                  "x": 0,
                  "y": 0
                },
                {
                  "x": 1,
                  "y": 1
                }
                ,
                {
                  "x": 2,
                  "y": 2
                }
              ],
              "type": "polygon",
            }
          }
        ]
      }
    ]

    const generator = create(connections);
    expect(generator.getConnectionStyle('Connection1')).toEqual( {
         ".connection": {
           "stroke": "black",
         },
         ".marker-target": {
           "d": "M 0 0 L -1 -1L -2 -2z",
           "transform": "scale(1,1)",
         }
       }
    )
  })

  test('a Style by a Connection with a Rectangle Placing', () => {
    const connections = [
      {
        "name": "Connection1",
        "placings": [
          {
            "positionOffset": 0.0,
            "shape": {
              "textBody": "positionOffset1",
              "sizeHeight": 2,
              "sizeWidth": 3,
              "position": {
                "x": 0,
                "y": 0,
              },
              "type": "rectangle",
            }
          }
        ]
      }
    ]

    const generator = create(connections);
    expect(generator.getConnectionStyle('Connection1')).toEqual({
      ".connection": {"stroke": "black"},
      ".marker-source": {
        "d": "M 0 0l 3 0 l 0 2 l -3 0 z",
        "transform": "scale(1,1)"},
      ".marker-target": {"d": "M 0 0"}}
  )
  })

  test('a Style by a Connection with a RoundedRectangle Placing', () => {
    const connections = [
      {
        "name": "Connection1",
        "placings": [
          {
            "positionOffset": 0.0,
            "shape": {
              "curveWidth": 4,
              "curveHeight": 4,
              "position": {
                "x": 1,
                "y": 1
              },
              "sizeHeight": 3,
              "sizeWidth": 3,
              "type": "roundedRectangle",
            }
          }
        ]
      }
    ]
    const generator = create(connections);
    expect(generator.getConnectionStyle('Connection1')).toEqual({
      ".connection": {"stroke": "black"},
      ".marker-source": {
        "d": "M 1 4 1 4 l -5l 0 a 4 4 0 0 1 4 4l 0 -5 a 4 4 0 0 1 -4 4 l --5 0 a 4 4 0 0 1 -4 -4 l 0 --5 a 4 4 0 0 1 4 -4",
        "transform": "scale(1,1)"
      },
      ".marker-target": {"d": "M 0 0"}}
  )
  })

  test('a Style by a Connection with a Ellipse Placing', () => {
    const connections = [
      {
        "name": "Connection1",
        "placings": [
          {
            "positionOffset": 0.0,
            "shape": {
              "sizeHeight": 4,
              "sizeWidth": 4,
              "position": {
                "x": 1,
                "y": 1
              },
              "type": "ellipse"
            }
          }
        ]
      }
    ]

    const generator = create(connections);
    expect(generator.getConnectionStyle('Connection1')).toEqual({
      ".connection": {"stroke": "black"},
      ".marker-source": {
        "d": "M 1 1 a  2 2 0 0 1 2 -2 a  2 2 0 0 1 2 2 a  2 2 0 0 1 -2 2 a  2 2 0 0 1 -2 -2",
        "transform": "scale(1,1)"
      },
      ".marker-target": {"d": "M 0 0"}
    }
  )
  })

  test('a Connection without a Style', () => {
    const connections = [
      {
        "name": "Connection1"
      }
    ]

    const generator = create(connections);
    expect(generator.getConnectionStyle('Connection1')).toEqual({
      '.connection':{stroke: 'black'},
      '.marker-target': {"d": "M 0 0"}
    })
  })

  test('a Connection with a Style', () => {
    const connections = [
      {
        "name": "Connection1",
        "style": "DefaultStyle"
      }
    ]

    const generator = create(connections);
    expect(generator.getConnectionStyle('Connection1')).toEqual(
      {
        '.marker-target': {"d": "M 0 0"},
        "style": {
          "fill-opacity": 1,
          "stroke": "#000000",
          "stroke-dasharray": "0",
          "stroke-width": 0,
          "text": {
            "dominant-baseline": "text-before-edge",
            "fill": "#000000",
            "font-family": "sans-serif",
            "font-size": "11",
            "font-weight": "normal"
          }
        }
      }
    )
  })

});<|endoftext|>


<commit_before>import React from 'react';
import { connect } from 'react-redux';
import classnames from 'classnames';
//
import * as Basic from '../../basic';
import {
  IdentityManager,
  RoleManager,
  SecurityManager
} from '../../../redux';
import SearchParameters from '../../../domain/SearchParameters';

const identityManager = new IdentityManager();
const roleManager = new RoleManager();

/**
 * Search box in navigation.
 * @FIXME: Search single identity and role is supported now only (implement service registration).
 * @FIXME: both READ authorities are required now to show search box - search by permission.
 *
 * @author Radek Tomiška
 * @since 10.3.0
 */
class NavigationSearch extends Basic.AbstractContextComponent {

  constructor(props, context) {
    super(props, context);
    //
    this.state = {
      searchShowLoading: false
    };
  }

  getComponentKey() {
    return 'component.advanced.NavigationSearch';
  }

  /**
   * Seach identity or role:
   * - by codeable identifier
   * - then by text usage.
   */
  search(event) {
    if (event) {
      event.preventDefault();
    }
    //
    const text = this.refs['input-search'].getValue();
    if (!text) {
      return;
    }
    //
    // FIXME: implement service registration
    // FIXME: service vs manager usage - try to rewire na e.g.  Promise.All
    this.setState({
      searchShowLoading: true
    }, () => {
      let counter = 0;
      this.context.store.dispatch(identityManager.fetchEntity(text, 'search', (identity, e1) => {
        if (e1 && e1.statusCode === 404) {
          this.context.store.dispatch(roleManager.fetchEntity(text, 'search2', (role, e2) => {
            if (e2 && e2.statusCode === 404) {
              // text search is used => when at least record is found, then first detial is shown. Warning message is shown otherwise.
              identityManager.getService()
                .search(new SearchParameters().setFilter('text', text).setSort('disabled', true).setSort('username', true))
                .then(json => {
                  // ok
                  const entities = json._embedded[identityManager.getCollectionType()] || [];
                  if (entities.length === 1) {
                    this.context.history.push(identityManager.getDetailLink(entities[0]));
                    this.setState({
                      searchShowLoading: false
                    });
                  } else {
                    counter += entities.length;
                    roleManager.getService()
                      .search(new SearchParameters().setFilter('text', text).setSort('disabled', true).setSort('code', true))
                      .then(json2 => {
                        // ok
                        const entities2 = json2._embedded[roleManager.getCollectionType()] || [];
                        if (entities2.length === 1) {
                          this.context.history.push(`/role/${ encodeURIComponent(entities2[0].id) }/detail`);
                          this.refs['input-search'].setValue(null);
                        } else if (entities2.length > 1) {
                          counter += entities2.length;
                          // find more
                          this.addMessage({
                            key: 'search-result',
                            level: 'info',
                            title: this.i18n('message.foundMore.title'),
                            message: this.i18n('message.foundMore.message', { counter, text })
                          });
                        } else {
                          // not found message
                          this.addMessage({
                            key: 'search-result',
                            level: 'info',
                            title: this.i18n('message.notFound.title'),
                            message: this.i18n('message.notFound.message', { text })
                          });
                        }
                        this.setState({
                          searchShowLoading: false
                        });
                      })
                      .catch(error => {
                        this.addError(error);
                        this.setState({
                          searchShowLoading: false
                        });
                      });
                  }
                })
                .catch(error => {
                  this.addError(error);
                  this.setState({
                    searchShowLoading: false
                  });
                });
            } else if (e2) {
              this.addError(e2);
            } else {
              this.context.history.push(`/role/${ encodeURIComponent(role.id) }/detail`);
              this.refs['input-search'].setValue(null);
            }
            this.setState({
              searchShowLoading: false
            });
          }));
        } else if (e1) {
          this.addError(e1);
          this.setState({
            searchShowLoading: false
          });
        } else {
          this.context.history.push(identityManager.getDetailLink(identity));
          this.refs['input-search'].setValue(null);
          this.setState({
            searchShowLoading: false
          });
        }
      }));
    });
  }

  render() {
    const { rendered, showLoading, className, userContext } = this.props;
    const { searchShowLoading } = this.state;
    //
    if (!rendered) {
      return null;
    }
    // FIXME - by registered services
    if (!SecurityManager.hasAllAuthorities(['IDENTITY_READ', 'ROLE_READ'], userContext)) {
      return null;
    }
    //
    return (
      <form
        className={ classnames(className, 'navigation-search') }
        onSubmit={ this.search.bind(this) }>
        <Basic.Div className="input-group">
          <Basic.TextField
            label={ null }
            ref="input-search"
            placeholder={ this.i18n('search.placeholder') }/>

            <Basic.Button
              showLoading={ searchShowLoading || showLoading }
              title={ this.i18n('button.title') }
              showLoadingIcon
              level="default"
              type="submit"
              icon="search"/>

        </Basic.Div>
      </form>
    );
  }
}

function select(state) {
  //
  return {
    userContext: state.security.userContext,
    i18nReady: state.config.get('i18nReady')
  };
}

export default connect(select)(NavigationSearch);
<commit_msg>FE: fix: skip 403 search error by codeable<commit_after>import React from 'react';
import { connect } from 'react-redux';
import classnames from 'classnames';
//
import * as Basic from '../../basic';
import {
  IdentityManager,
  RoleManager,
  SecurityManager
} from '../../../redux';
import SearchParameters from '../../../domain/SearchParameters';

const identityManager = new IdentityManager();
const roleManager = new RoleManager();

/**
 * Search box in navigation.
 * @FIXME: Search single identity and role is supported now only (implement service registration).
 * @FIXME: both READ authorities are required now to show search box - search by permission.
 *
 * @author Radek Tomiška
 * @since 10.3.0
 */
class NavigationSearch extends Basic.AbstractContextComponent {

  constructor(props, context) {
    super(props, context);
    //
    this.state = {
      searchShowLoading: false
    };
  }

  getComponentKey() {
    return 'component.advanced.NavigationSearch';
  }

  /**
   * Seach identity or role:
   * - by codeable identifier
   * - then by text usage.
   */
  search(event) {
    if (event) {
      event.preventDefault();
    }
    //
    const text = this.refs['input-search'].getValue();
    if (!text) {
      return;
    }
    //
    // FIXME: implement service registration
    // FIXME: service vs manager usage - try to rewire na e.g.  Promise.All
    this.setState({
      searchShowLoading: true
    }, () => {
      let counter = 0;
      this.context.store.dispatch(identityManager.fetchEntity(text, 'search', (identity, e1) => {
        if (e1 && (e1.statusCode === 404 || e1.statusCode === 403)) {
          this.context.store.dispatch(roleManager.fetchEntity(text, 'search2', (role, e2) => {
            if (e2 && (e2.statusCode === 404 || e2.statusCode === 403)) {
              // text search is used => when at least record is found, then first detial is shown. Warning message is shown otherwise.
              identityManager.getService()
                .search(new SearchParameters().setFilter('text', text).setSort('disabled', true).setSort('username', true))
                .then(json => {
                  // ok
                  const entities = json._embedded[identityManager.getCollectionType()] || [];
                  if (entities.length === 1) {
                    this.context.history.push(identityManager.getDetailLink(entities[0]));
                    this.setState({
                      searchShowLoading: false
                    });
                  } else {
                    counter += entities.length;
                    roleManager.getService()
                      .search(new SearchParameters().setFilter('text', text).setSort('disabled', true).setSort('code', true))
                      .then(json2 => {
                        // ok
                        const entities2 = json2._embedded[roleManager.getCollectionType()] || [];
                        if (entities2.length === 1) {
                          this.context.history.push(`/role/${ encodeURIComponent(entities2[0].id) }/detail`);
                          this.refs['input-search'].setValue(null);
                        } else if (entities2.length > 1) {
                          counter += entities2.length;
                          // find more
                          this.addMessage({
                            key: 'search-result',
                            level: 'info',
                            title: this.i18n('message.foundMore.title'),
                            message: this.i18n('message.foundMore.message', { counter, text })
                          });
                        } else {
                          // not found message
                          this.addMessage({
                            key: 'search-result',
                            level: 'info',
                            title: this.i18n('message.notFound.title'),
                            message: this.i18n('message.notFound.message', { text })
                          });
                        }
                        this.setState({
                          searchShowLoading: false
                        });
                      })
                      .catch(error => {
                        this.addError(error);
                        this.setState({
                          searchShowLoading: false
                        });
                      });
                  }
                })
                .catch(error => {
                  this.addError(error);
                  this.setState({
                    searchShowLoading: false
                  });
                });
            } else if (e2) {
              this.addError(e2);
            } else {
              this.context.history.push(`/role/${ encodeURIComponent(role.id) }/detail`);
              this.refs['input-search'].setValue(null);
            }
            this.setState({
              searchShowLoading: false
            });
          }));
        } else if (e1) {
          this.addError(e1);
          this.setState({
            searchShowLoading: false
          });
        } else {
          this.context.history.push(identityManager.getDetailLink(identity));
          this.refs['input-search'].setValue(null);
          this.setState({
            searchShowLoading: false
          });
        }
      }));
    });
  }

  render() {
    const { rendered, showLoading, className, userContext } = this.props;
    const { searchShowLoading } = this.state;
    //
    if (!rendered) {
      return null;
    }
    // FIXME - by registered services
    if (!SecurityManager.hasAllAuthorities(['IDENTITY_READ', 'ROLE_READ'], userContext)) {
      return null;
    }
    //
    return (
      <form
        className={ classnames(className, 'navigation-search') }
        onSubmit={ this.search.bind(this) }>
        <Basic.Div className="input-group">
          <Basic.TextField
            label={ null }
            ref="input-search"
            placeholder={ this.i18n('search.placeholder') }/>

            <Basic.Button
              showLoading={ searchShowLoading || showLoading }
              title={ this.i18n('button.title') }
              showLoadingIcon
              level="default"
              type="submit"
              icon="search"/>

        </Basic.Div>
      </form>
    );
  }
}

function select(state) {
  //
  return {
    userContext: state.security.userContext,
    i18nReady: state.config.get('i18nReady')
  };
}

export default connect(select)(NavigationSearch);
<|endoftext|>


<commit_before>define(function(require) {
  'use strict';

  var modules = require('modules');
  var states = require('states');
  var _ = require('lodash');
  var angular = require('angular');

  require('scripts/common/filters/inputs');

  require('scripts/applications/services/application_services');
  require('scripts/applications/services/application_environment_services');
  require('scripts/applications/services/application_version_services');
  require('scripts/applications/services/deployment_topology_processor.js');
  require('scripts/applications/services/tasks_processor.js');

  require('scripts/applications/controllers/application_deployment_locations');
  require('scripts/applications/controllers/application_deployment_match');
  require('scripts/applications/controllers/application_deployment_input');
  require('scripts/applications/controllers/application_deployment_deploy');

  require('scripts/deployment/directives/display_outputs');
  require('scripts/applications/directives/topology_errors_display');

  var globalConfTaskCodes = ['SCALABLE_CAPABILITY_INVALID', 'PROPERTIES', 'NODE_FILTER_INVALID', 'ARTIFACT_INVALID', 'INPUT_ARTIFACT_INVALID'];

  function refreshDeploymentContext(deploymentContext, application, deploymentTopologyServices, deploymentTopologyProcessor, tasksProcessor, menus) {
    return deploymentTopologyServices.get({
      appId: application.id,
      envId: deploymentContext.selectedEnvironment.id
    }).$promise.then(function(response) {
        deploymentTopologyProcessor.process(response.data);
        deploymentContext.deploymentTopologyDTO = response.data;
        tasksProcessor.processAll(deploymentContext.deploymentTopologyDTO.validation);
        updateStepsStatuses(menus, deploymentContext.deploymentTopologyDTO.validation);
        return deploymentContext;
      });
  }

  var setNextStepMenuEnabled = function(currentStepMenu, nextStepMenu){
    nextStepMenu.disabled = currentStepMenu.disabled || (_.get(currentStepMenu, 'step.status', 'SUCCESS')!=='SUCCESS');
  };

  function buildMenuTree(menus) {
    _.each(menus, function(menu){
      if (_.definedPath(menu, 'step.nextStepId')){
        menu.nextStep = _.find(menus, function(item){
          return item.id===menu.step.nextStepId;
        });
      }
    });
  }

  function enabledOrDisableMenus(menus){
    _.each(menus, function(menu){
      if (_.defined(menu.nextStep)){
        setNextStepMenuEnabled(menu, menu.nextStep);
      }
    });
  }

  function updateStepsStatuses(menus, validationDTO){
    _.each(menus, function(menu){
      if(_.definedPath(menu, 'step.taskCodes')){
        delete menu.step.status;
        _.each(menu.step.taskCodes, function(taskCode){
          if(_.definedPath(validationDTO, 'taskList['+taskCode+']')){
            menu.step.status = 'ERROR';
            return;
          }
        });
      }
    });

    enabledOrDisableMenus(menus);
  }

  states.state('applications.detail.deployment', {
    url: '/deployment',
    resolve: {
      deploymentContext: ['application', 'appEnvironments', 'deploymentTopologyServices', 'deploymentTopologyProcessor', 'tasksProcessor', 'menu',
        function(application, appEnvironments, deploymentTopologyServices, deploymentTopologyProcessor, tasksProcessor, menu) {
          //build the menu tree
          buildMenuTree(menu);
          var deploymentContextResult = {
            selectedEnvironment: appEnvironments.selected
          };
          return refreshDeploymentContext(deploymentContextResult, application.data, deploymentTopologyServices, deploymentTopologyProcessor, tasksProcessor, menu);
        }
      ]
    },
    templateUrl: 'views/applications/application_deployment.html',
    controller: 'ApplicationDeploymentCtrl',
    menu: {
      id: 'am.applications.detail.deployment',
      state: 'applications.detail.deployment',
      key: 'NAVAPPLICATIONS.MENU_DEPLOYMENT',
      icon: 'fa fa-cloud-upload',
      roles: ['APPLICATION_MANAGER', 'APPLICATION_DEPLOYER'], // is deployer
      priority: 300
    }
  });

  modules.get('a4c-applications').controller('ApplicationDeploymentCtrl',
    ['$scope', 'authService', 'application', '$state', 'appEnvironments', 'menu', 'deploymentContext', 'deploymentTopologyServices', 'deploymentTopologyProcessor', 'applicationServices', 'tasksProcessor',
      function($scope, authService, applicationResult, $state, appEnvironments, menu, deploymentContext, deploymentTopologyServices, deploymentTopologyProcessor, applicationServices, tasksProcessor) {
        $scope.validTopologyDTOLoaded = false;
        $scope.deploymentContext = deploymentContext;
        var pageStateId = $state.current.name;
        $scope.menu = menu;

        // Initialization
        $scope.application = applicationResult.data;
        $scope.envs = appEnvironments.deployEnvironments;

        $scope.undeploy = function() {
          $scope.isUnDeploying = true;
          applicationServices.deployment.undeploy({
            applicationId: $scope.application.id,
            applicationEnvironmentId: $scope.deploymentContext.selectedEnvironment.id
          }, function() {
            $scope.deploymentContext.selectedEnvironment.status = 'UNDEPLOYMENT_IN_PROGRESS';
            $scope.isUnDeploying = false;
            $scope.stopEvent();
          }, function() {
            $scope.isUnDeploying = false;
          });
        };

        // Retrieval and validation of the topology associated with the deployment.
        var checkTopology = function() {

          $scope.isTopologyValid($scope.topologyId).$promise.then(function(validTopologyResult) {
            $scope.validTopologyDTO = validTopologyResult.data;
            tasksProcessor.processAll($scope.validTopologyDTO);
            $scope.validTopologyDTOLoaded = true;
          });

          var processTopologyInfoResult = $scope.processTopologyInformations($scope.topologyId);

          // when the selected environment is deployed => refresh output properties
          processTopologyInfoResult.$promise.then(function() {
            if ($scope.deploymentContext.selectedEnvironment.status === 'DEPLOYED') {
              $scope.refreshInstancesStatuses($scope.application.id, $scope.deploymentContext.selectedEnvironment.id, pageStateId);
            }
          });

        };

        //register the checking topo function for others states to use it
        $scope.checkTopology = checkTopology;

        function doGoToNextInvalidStep(){
          //first step is locations
          var stepToGo = _.find($scope.menu, function(menu){
            return menu.id==='am.applications.detail.deployment.locations';
          });

          //look for the first invalid step, or the last one if all are valid
          while(stepToGo.nextStep){
            if(_.get(stepToGo, 'step.status', 'SUCCESS') !== 'SUCCESS'){
              break;
            }
            stepToGo = stepToGo.nextStep;
          }

          //go to the found step
          $state.go(stepToGo.state);
        }

        function goToNextInvalidStep() {
          //refresh initial topo validation first
          $scope.setTopologyId($scope.application.id, $scope.deploymentContext.selectedEnvironment.id, checkTopology).$promise.then(function() {
            //then refresh deployment context
            refreshDeploymentContext($scope.deploymentContext, $scope.application, deploymentTopologyServices, deploymentTopologyProcessor, tasksProcessor, menu).then(function() {
              //finally, go to the next invalid step
              doGoToNextInvalidStep();
            });
          });
        }

        $scope.onEnvironmentChange = function() {
          // update the global environment
          appEnvironments.select($scope.deploymentContext.selectedEnvironment.id, goToNextInvalidStep);
        };

        if(_.defined($state.params.openOnEnvironment) && appEnvironments.selected.id !== $state.params.openOnEnvironment){
          appEnvironments.select($state.params.openOnEnvironment, function(){
            $scope.deploymentContext.selectedEnvironment = appEnvironments.selected;
            goToNextInvalidStep();
          });
        }else{
          goToNextInvalidStep(); // immediately go to the next invalid tab
        }

        $scope.showTodoList = function() {
          return $scope.validTopologyDTOLoaded && !$scope.validTopologyDTO.valid && $scope.isManager;
        };

        $scope.showWarningList = function() {
          return $scope.validTopologyDTOLoaded && angular.isObject($scope.validTopologyDTO.warningList) && Object.keys($scope.validTopologyDTO.warningList).length > 0;
        };

        $scope.showConfgurationsErrors = function() {
          var show = false;
          if( _.definedPath($scope.deploymentContext, 'deploymentTopologyDTO.validation.taskList')){
            _.each(globalConfTaskCodes, function(taskCode){
              if (_.defined($scope.deploymentContext.deploymentTopologyDTO.validation.taskList[taskCode])){
                show= true;
                return;
              }
            });
          }
          return show;
        };

        // Map functions that should be available from scope.

        // Application rights
        $scope.isManager = authService.hasResourceRole($scope.application, 'APPLICATION_MANAGER');
        $scope.isDevops = authService.hasResourceRole($scope.application, 'APPLICATION_DEVOPS');
        // Application environment rights
        $scope.isDeployer = authService.hasResourceRole($scope.deploymentContext.selectedEnvironment, 'DEPLOYMENT_MANAGER');
        $scope.isUser = authService.hasResourceRole($scope.deploymentContext.selectedEnvironment, 'APPLICATION_USER');

        $scope.finalOutputAttributesValue = $scope.outputAttributesValue;
        $scope.validTopologyDTO = false;

        $scope.isAllowedDeployment = function() {
          return $scope.isDeployer || $scope.isManager;
        };

        // if the status or the environment changes we must update the event registration.
        $scope.$watch(function() {
          if (_.defined($scope.deploymentContext.selectedEnvironment)) {
            return $scope.deploymentContext.selectedEnvironment.id + '__' + $scope.deploymentContext.selectedEnvironment.status;
          }
          return 'UNDEPLOYED';
        }, function(newValue) {
          var undeployedValue = $scope.deploymentContext.selectedEnvironment.id + '__UNDEPLOYED';
          // no registration for this environement -> register if not undeployed!
          if (newValue === undeployedValue) {
            // if status the application is not undeployed we should register for events.
            $scope.stopEvent();
          } else {
            $scope.stopEvent();
            $scope.setTopologyId($scope.application.id, $scope.deploymentContext.selectedEnvironment.id, checkTopology).$promise.then(function(result) {
              $scope.processTopologyInformations(result.data).$promise.then(function() {
                $scope.refreshInstancesStatuses($scope.application.id, $scope.deploymentContext.selectedEnvironment.id, pageStateId);
              });
            });
          }
        });

        //proceed the deploymentTopologyDTO
        $scope.updateScopeDeploymentTopologyDTO = function(deploymentTopologyDTO){
          if(_.undefined(deploymentTopologyDTO)){
            return;
          }
          deploymentTopologyProcessor.process(deploymentTopologyDTO);
          tasksProcessor.processAll(deploymentTopologyDTO.validation);
          $scope.deploymentContext.deploymentTopologyDTO = deploymentTopologyDTO;
          updateStepsStatuses($scope.menu, $scope.deploymentContext.deploymentTopologyDTO.validation);
        };

        //show or not the status icon
        $scope.showStatusIcon = function(menuItem){
          if(menuItem.disabled || _.get(menuItem, 'step.status','SUCCESS')==='SUCCESS'){
            return false;
          }
          return true;
        };

        // when scope change, stop current event listener
        $scope.$on('$destroy', function() {
          $scope.stopEvent();
        });

      }
    ]); //controller
}); //Define
<commit_msg>ALIEN-1828 : Deployer role =>User can't display deploiement view from apllication list<commit_after>define(function(require) {
  'use strict';

  var modules = require('modules');
  var states = require('states');
  var _ = require('lodash');
  var angular = require('angular');

  require('scripts/common/filters/inputs');

  require('scripts/applications/services/application_services');
  require('scripts/applications/services/application_environment_services');
  require('scripts/applications/services/application_version_services');
  require('scripts/applications/services/deployment_topology_processor.js');
  require('scripts/applications/services/tasks_processor.js');

  require('scripts/applications/controllers/application_deployment_locations');
  require('scripts/applications/controllers/application_deployment_match');
  require('scripts/applications/controllers/application_deployment_input');
  require('scripts/applications/controllers/application_deployment_deploy');

  require('scripts/deployment/directives/display_outputs');
  require('scripts/applications/directives/topology_errors_display');

  var globalConfTaskCodes = ['SCALABLE_CAPABILITY_INVALID', 'PROPERTIES', 'NODE_FILTER_INVALID', 'ARTIFACT_INVALID', 'INPUT_ARTIFACT_INVALID'];

  function refreshDeploymentContext(deploymentContext, application, deploymentTopologyServices, deploymentTopologyProcessor, tasksProcessor, menus) {
    return deploymentTopologyServices.get({
      appId: application.id,
      envId: deploymentContext.selectedEnvironment.id
    }).$promise.then(function(response) {
        deploymentTopologyProcessor.process(response.data);
        deploymentContext.deploymentTopologyDTO = response.data;
        tasksProcessor.processAll(deploymentContext.deploymentTopologyDTO.validation);
        updateStepsStatuses(menus, deploymentContext.deploymentTopologyDTO.validation);
        return deploymentContext;
      });
  }

  var setNextStepMenuEnabled = function(currentStepMenu, nextStepMenu){
    nextStepMenu.disabled = currentStepMenu.disabled || (_.get(currentStepMenu, 'step.status', 'SUCCESS')!=='SUCCESS');
  };

  function buildMenuTree(menus) {
    _.each(menus, function(menu){
      if (_.definedPath(menu, 'step.nextStepId')){
        menu.nextStep = _.find(menus, function(item){
          return item.id===menu.step.nextStepId;
        });
      }
    });
  }

  function enabledOrDisableMenus(menus){
    _.each(menus, function(menu){
      if (_.defined(menu.nextStep)){
        setNextStepMenuEnabled(menu, menu.nextStep);
      }
    });
  }

  function updateStepsStatuses(menus, validationDTO){
    _.each(menus, function(menu){
      if(_.definedPath(menu, 'step.taskCodes')){
        delete menu.step.status;
        _.each(menu.step.taskCodes, function(taskCode){
          if(_.definedPath(validationDTO, 'taskList['+taskCode+']')){
            menu.step.status = 'ERROR';
            return;
          }
        });
      }
    });

    enabledOrDisableMenus(menus);
  }

  states.state('applications.detail.deployment', {
    url: '/deployment',
    resolve: {
      deploymentContext: ['application', 'appEnvironments', 'deploymentTopologyServices', 'deploymentTopologyProcessor', 'tasksProcessor', 'menu',
        function(application, appEnvironments, deploymentTopologyServices, deploymentTopologyProcessor, tasksProcessor, menu) {
          //build the menu tree
          buildMenuTree(menu);
          if(appEnvironments.deployEnvironments.indexOf(appEnvironments.selected) < 0) {
            // If the selected environment is not the one that the user has deployer role then select the first one with this role enabled
            appEnvironments.select(appEnvironments.deployEnvironments[0].id);
          }
          var deploymentContextResult = {
            selectedEnvironment: appEnvironments.selected
          };
          return refreshDeploymentContext(deploymentContextResult, application.data, deploymentTopologyServices, deploymentTopologyProcessor, tasksProcessor, menu);
        }
      ]
    },
    templateUrl: 'views/applications/application_deployment.html',
    controller: 'ApplicationDeploymentCtrl',
    menu: {
      id: 'am.applications.detail.deployment',
      state: 'applications.detail.deployment',
      key: 'NAVAPPLICATIONS.MENU_DEPLOYMENT',
      icon: 'fa fa-cloud-upload',
      roles: ['APPLICATION_MANAGER', 'APPLICATION_DEPLOYER'], // is deployer
      priority: 300
    }
  });

  modules.get('a4c-applications').controller('ApplicationDeploymentCtrl',
    ['$scope', 'authService', 'application', '$state', 'appEnvironments', 'menu', 'deploymentContext', 'deploymentTopologyServices', 'deploymentTopologyProcessor', 'applicationServices', 'tasksProcessor',
      function($scope, authService, applicationResult, $state, appEnvironments, menu, deploymentContext, deploymentTopologyServices, deploymentTopologyProcessor, applicationServices, tasksProcessor) {
        $scope.validTopologyDTOLoaded = false;
        $scope.deploymentContext = deploymentContext;
        var pageStateId = $state.current.name;
        $scope.menu = menu;

        // Initialization
        $scope.application = applicationResult.data;
        $scope.envs = appEnvironments.deployEnvironments;

        $scope.undeploy = function() {
          $scope.isUnDeploying = true;
          applicationServices.deployment.undeploy({
            applicationId: $scope.application.id,
            applicationEnvironmentId: $scope.deploymentContext.selectedEnvironment.id
          }, function() {
            $scope.deploymentContext.selectedEnvironment.status = 'UNDEPLOYMENT_IN_PROGRESS';
            $scope.isUnDeploying = false;
            $scope.stopEvent();
          }, function() {
            $scope.isUnDeploying = false;
          });
        };

        // Retrieval and validation of the topology associated with the deployment.
        var checkTopology = function() {

          $scope.isTopologyValid($scope.topologyId).$promise.then(function(validTopologyResult) {
            $scope.validTopologyDTO = validTopologyResult.data;
            tasksProcessor.processAll($scope.validTopologyDTO);
            $scope.validTopologyDTOLoaded = true;
          });

          var processTopologyInfoResult = $scope.processTopologyInformations($scope.topologyId);

          // when the selected environment is deployed => refresh output properties
          processTopologyInfoResult.$promise.then(function() {
            if ($scope.deploymentContext.selectedEnvironment.status === 'DEPLOYED') {
              $scope.refreshInstancesStatuses($scope.application.id, $scope.deploymentContext.selectedEnvironment.id, pageStateId);
            }
          });

        };

        //register the checking topo function for others states to use it
        $scope.checkTopology = checkTopology;

        function doGoToNextInvalidStep(){
          //first step is locations
          var stepToGo = _.find($scope.menu, function(menu){
            return menu.id==='am.applications.detail.deployment.locations';
          });

          //look for the first invalid step, or the last one if all are valid
          while(stepToGo.nextStep){
            if(_.get(stepToGo, 'step.status', 'SUCCESS') !== 'SUCCESS'){
              break;
            }
            stepToGo = stepToGo.nextStep;
          }

          //go to the found step
          $state.go(stepToGo.state);
        }

        function goToNextInvalidStep() {
          //refresh initial topo validation first
          $scope.setTopologyId($scope.application.id, $scope.deploymentContext.selectedEnvironment.id, checkTopology).$promise.then(function() {
            //then refresh deployment context
            refreshDeploymentContext($scope.deploymentContext, $scope.application, deploymentTopologyServices, deploymentTopologyProcessor, tasksProcessor, menu).then(function() {
              //finally, go to the next invalid step
              doGoToNextInvalidStep();
            });
          });
        }

        $scope.onEnvironmentChange = function() {
          // update the global environment
          appEnvironments.select($scope.deploymentContext.selectedEnvironment.id, goToNextInvalidStep);
        };

        if(_.defined($state.params.openOnEnvironment) && appEnvironments.selected.id !== $state.params.openOnEnvironment){
          appEnvironments.select($state.params.openOnEnvironment, function(){
            $scope.deploymentContext.selectedEnvironment = appEnvironments.selected;
            goToNextInvalidStep();
          });
        }else{
          goToNextInvalidStep(); // immediately go to the next invalid tab
        }

        $scope.showTodoList = function() {
          return $scope.validTopologyDTOLoaded && !$scope.validTopologyDTO.valid && $scope.isManager;
        };

        $scope.showWarningList = function() {
          return $scope.validTopologyDTOLoaded && angular.isObject($scope.validTopologyDTO.warningList) && Object.keys($scope.validTopologyDTO.warningList).length > 0;
        };

        $scope.showConfgurationsErrors = function() {
          var show = false;
          if( _.definedPath($scope.deploymentContext, 'deploymentTopologyDTO.validation.taskList')){
            _.each(globalConfTaskCodes, function(taskCode){
              if (_.defined($scope.deploymentContext.deploymentTopologyDTO.validation.taskList[taskCode])){
                show= true;
                return;
              }
            });
          }
          return show;
        };

        // Map functions that should be available from scope.

        // Application rights
        $scope.isManager = authService.hasResourceRole($scope.application, 'APPLICATION_MANAGER');
        $scope.isDevops = authService.hasResourceRole($scope.application, 'APPLICATION_DEVOPS');
        // Application environment rights
        $scope.isDeployer = authService.hasResourceRole($scope.deploymentContext.selectedEnvironment, 'DEPLOYMENT_MANAGER');
        $scope.isUser = authService.hasResourceRole($scope.deploymentContext.selectedEnvironment, 'APPLICATION_USER');

        $scope.finalOutputAttributesValue = $scope.outputAttributesValue;
        $scope.validTopologyDTO = false;

        $scope.isAllowedDeployment = function() {
          return $scope.isDeployer || $scope.isManager;
        };

        // if the status or the environment changes we must update the event registration.
        $scope.$watch(function() {
          if (_.defined($scope.deploymentContext.selectedEnvironment)) {
            return $scope.deploymentContext.selectedEnvironment.id + '__' + $scope.deploymentContext.selectedEnvironment.status;
          }
          return 'UNDEPLOYED';
        }, function(newValue) {
          var undeployedValue = $scope.deploymentContext.selectedEnvironment.id + '__UNDEPLOYED';
          // no registration for this environement -> register if not undeployed!
          if (newValue === undeployedValue) {
            // if status the application is not undeployed we should register for events.
            $scope.stopEvent();
          } else {
            $scope.stopEvent();
            $scope.setTopologyId($scope.application.id, $scope.deploymentContext.selectedEnvironment.id, checkTopology).$promise.then(function(result) {
              $scope.processTopologyInformations(result.data).$promise.then(function() {
                $scope.refreshInstancesStatuses($scope.application.id, $scope.deploymentContext.selectedEnvironment.id, pageStateId);
              });
            });
          }
        });

        //proceed the deploymentTopologyDTO
        $scope.updateScopeDeploymentTopologyDTO = function(deploymentTopologyDTO){
          if(_.undefined(deploymentTopologyDTO)){
            return;
          }
          deploymentTopologyProcessor.process(deploymentTopologyDTO);
          tasksProcessor.processAll(deploymentTopologyDTO.validation);
          $scope.deploymentContext.deploymentTopologyDTO = deploymentTopologyDTO;
          updateStepsStatuses($scope.menu, $scope.deploymentContext.deploymentTopologyDTO.validation);
        };

        //show or not the status icon
        $scope.showStatusIcon = function(menuItem){
          if(menuItem.disabled || _.get(menuItem, 'step.status','SUCCESS')==='SUCCESS'){
            return false;
          }
          return true;
        };

        // when scope change, stop current event listener
        $scope.$on('$destroy', function() {
          $scope.stopEvent();
        });

      }
    ]); //controller
}); //Define
<|endoftext|>


<commit_before>//Connector
window.annis_visualizers_component_visjs_VisJsComponent = function() {

var div = this.getElement();

var containerWidth = $(div).parent().width();
var containerHeight =  $(div).parent().height();

var parentWidthOld = containerWidth;
var parentHeightOld = containerHeight;
var parentWidthNew = parentWidthOld;
var parentHeightNew = parentHeightOld;

var container = div;
var minHeight = containerHeight;

$(container).css({"width": containerWidth, "height": containerHeight});

var strNodes =  this.getState().strNodes;
var strEdges = this.getState().strEdges;
var visjscomponent = null;

// Handle changes from the server-side
this.onStateChange = function(){

	// cleanup old graph
    if(typeof visjscomponent !== 'undefined' && visjscomponent != null){
    	visjscomponent.destroy();
    }

    strNodes = this.getState().strNodes;
    strEdges = this.getState().strEdges;
    this.init(strNodes, strEdges);
    };

this.init = function(strNodes, strEdges){

var json_nodes = JSON.parse(strNodes);
var json_edges = JSON.parse(strEdges);

var nodeDist;
var nNodes = json_nodes.length;
if (nNodes < 20){
	nodeDist = 100;
} else if (nNodes >=20 && nNodes < 100){
	nodeDist = 150
} else if (nNodes >= 100 && nNodes < 400) {
	nodeDist = 200
} else {
	nodeDist = 400
};

var data = {
nodes: json_nodes,
edges: json_edges
};
var options = {
nodes:{
shape: "box"
},
edges: {
smooth: true,
arrows: {
to: {
enabled: true
}
}
},
layout: {
hierarchical:{
}
},
physics: {
hierarchicalRepulsion: {
centralGravity: 0.05,
springLength: 100,
springConstant: 0.0007,
nodeDistance: nodeDist,
damping: 0.04
},
maxVelocity: 27,
solver: 'hierarchicalRepulsion',
timestep: 0.5,
stabilization: {
iterations: 800
}
}
}
;
$(container).remove("canvas");

visjscomponent = new vis.Network(container, data, options);

visjscomponent.on("zoom", function (params) {
		var canvasWidth = $(".vis-network canvas:first-child").width();
		var canvasHeight = $(".vis-network canvas:first-child").height();
		var minContainerWidth = $(div).parent().width();

       var zoomParams = JSON.stringify(params, null, 4);
      // window.alert(zoomParams);

       var zoomValues = JSON.parse(zoomParams);

       var direction = zoomValues.direction;
       var scale = zoomValues.scale;
       if (direction === '+') {
       	 visjscomponent.setSize(canvasWidth*(1+scale), canvasHeight*(1+scale));
       }
       else if  (direction === '-'){
	       	if (canvasWidth*(1-scale) < minContainerWidth ||  canvasHeight*(1-scale) < minHeight){
	       		//prevent zoom out of graph to the size smaller then the container size
	       		visjscomponent.setSize(minContainerWidth, minHeight);

	       	}
	       	else{
	       		visjscomponent.setSize(canvasWidth*(1-scale), canvasHeight*(1-scale));
	       	}

       }

		var newCanvasWidth = $(".vis-network canvas:first-child").width();
		var newCanvasHeight = $(".vis-network canvas:first-child").height();

		$(container).css({"width": Math.max(newCanvasWidth, minContainerWidth), "height": Math.max(newCanvasHeight, minHeight)});
		visjscomponent.fit();

    });

};

window.addEventListener("resize", function(){
	parentWidthNew = $(div).parent().width();
	parentHeightNew = $(div).parent().height();

	if (parentWidthNew > parentWidthOld || parentHeightNew > parentWidthOld){
		containerWidth = Math.max(parentWidthNew, $(container).width());
		containerHeight =  Math.max(parentHeightNew, $(container).height());
	}
	else{
		containerWidth = Math.min(parentWidthNew, $(container).width());
		containerHeight =  Math.min(parentHeightNew, $(container).height());
	}

 	minContainerWidth = containerWidth;

	$(container).css({"width": containerWidth, "height": containerHeight});
	visjscomponent.setSize(containerWidth, containerHeight);
	visjscomponent.fit();

	parentWidthOld = parentWidthNew;
	parentHeightOld = parentHeightNew;
});

};
<commit_msg>alternative scale method (as comments)<commit_after>//Connector
window.annis_visualizers_component_visjs_VisJsComponent = function() {

var div = this.getElement();

var containerWidth = $(div).parent().width();
var containerHeight =  $(div).parent().height();

var parentWidthOld = containerWidth;
var parentHeightOld = containerHeight;
var parentWidthNew = parentWidthOld;
var parentHeightNew = parentHeightOld;

var container = div;
var minHeight = containerHeight;

$(container).css({"width": containerWidth, "height": containerHeight});

var strNodes =  this.getState().strNodes;
var strEdges = this.getState().strEdges;
var visjscomponent = null;

// Handle changes from the server-side
this.onStateChange = function(){

	// cleanup old graph
    if(typeof visjscomponent !== 'undefined' && visjscomponent != null){
    	visjscomponent.destroy();
    }

    strNodes = this.getState().strNodes;
    strEdges = this.getState().strEdges;
    this.init(strNodes, strEdges);
    };

this.init = function(strNodes, strEdges){

var json_nodes = JSON.parse(strNodes);
var json_edges = JSON.parse(strEdges);

var nodeDist;
var nNodes = json_nodes.length;
if (nNodes < 20){
	nodeDist = 100;
} else if (nNodes >=20 && nNodes < 100){
	nodeDist = 150
} else if (nNodes >= 100 && nNodes < 400) {
	nodeDist = 200
} else {
	nodeDist = 400
};

var data = {
nodes: json_nodes,
edges: json_edges
};
var options = {
nodes:{
shape: "box"
},
edges: {
smooth: true,
arrows: {
to: {
enabled: true
}
}
},
layout: {
hierarchical:{
}
},
physics: {
hierarchicalRepulsion: {
centralGravity: 0.05,
springLength: 100,
springConstant: 0.0007,
nodeDistance: nodeDist,
damping: 0.04
},
maxVelocity: 27,
solver: 'hierarchicalRepulsion',
timestep: 0.5,
stabilization: {
iterations: 800
}
}
}
;
$(container).remove("canvas");

visjscomponent = new vis.Network(container, data, options);

var initCanvasWidth = $(".vis-network canvas:first-child").width();
var initCanvasHeight = $(".vis-network canvas:first-child").height();

visjscomponent.on("zoom", function (params) {
		//window.alert(JSON.stringify(visjscomponent.getScale(), null, 4));

		var canvasWidth = $(".vis-network canvas:first-child").width();
		var canvasHeight = $(".vis-network canvas:first-child").height();
		var minContainerWidth = $(div).parent().width();

       var zoomParams = JSON.stringify(params, null, 4);
      // window.alert(zoomParams);

       var zoomValues = JSON.parse(zoomParams);

       var direction = zoomValues.direction;
       var scale = zoomValues.scale;
       if (direction === '+' && scale <= 1) {
       	 visjscomponent.setSize(canvasWidth*(1+scale), canvasHeight*(1+scale));
       //	 visjscomponent.setSize(initCanvasWidth*(scale*10), initCanvasHeight*(scale*10));
       }
       else if  (direction === '-'){
	       	if (canvasWidth*(1-scale) < minContainerWidth ||  canvasHeight*(1-scale) < minHeight){
	       	//if (initCanvasWidth*(scale*10) < minContainerWidth ||  initCanvasHeight*(scale*10) < minHeight){

	       		//prevent zoom out of graph to the size smaller then the container size
	       		visjscomponent.setSize(minContainerWidth, minHeight);

	       	}
	       	else{
	       		visjscomponent.setSize(canvasWidth*(1-scale), canvasHeight*(1-scale));
	       		//visjscomponent.setSize(initCanvasWidth*(scale*10), initCanvasHeight*(scale*10));
	       	}

       }

		var newCanvasWidth = $(".vis-network canvas:first-child").width();
		var newCanvasHeight = $(".vis-network canvas:first-child").height();

		$(container).css({"width": Math.max(newCanvasWidth, minContainerWidth), "height": Math.max(newCanvasHeight, minHeight)});
		visjscomponent.fit();
		/*visjscomponent.moveTo({
					  position: {x:100, y:100}
					  });*/

    });

};

window.addEventListener("resize", function(){
	parentWidthNew = $(div).parent().width();
	parentHeightNew = $(div).parent().height();

	if (parentWidthNew > parentWidthOld || parentHeightNew > parentWidthOld){
		containerWidth = Math.max(parentWidthNew, $(container).width());
		containerHeight =  Math.max(parentHeightNew, $(container).height());
	}
	else{
		containerWidth = Math.min(parentWidthNew, $(container).width());
		containerHeight =  Math.min(parentHeightNew, $(container).height());
	}

 	minContainerWidth = containerWidth;

	$(container).css({"width": containerWidth, "height": containerHeight});
	visjscomponent.setSize(containerWidth, containerHeight);
	visjscomponent.fit();

	parentWidthOld = parentWidthNew;
	parentHeightOld = parentHeightNew;
});

};
<|endoftext|>


<commit_before>/*
 * Copyright 2012 - present by OpenGamma Inc. and the OpenGamma group of companies
 * Please see distribution for license.
 */
$.register_module({
    name: 'og.analytics.resize',
    dependencies: ['og.common.gadgets.manager'],
    obj: function () {
        return function (config) {
            var $main = $(config.selector), $menu, block_menu = false, $resizer = $(config.tmpl),
                icon_size = 16, offset = $main.offset(), right_handler = config.right_handler || $.noop;
            var left_handler = function () {
                var $bars = $('

'),
                    $overlay = $('

'),
                    offset = $main.offset(), right, bottom;
                if ($menu) $menu.remove();
                $bars.css({width: $main.width(), height: $main.outerHeight(true) + 1,
                    top: offset.top, left: offset.left + 1});
                $overlay.on('mousemove', function (event) {
                    $bars.css({
                        width: 'auto', height: 'auto',
                        right: right = $(document).outerWidth() - event.pageX,
                        bottom: bottom = $(document).outerHeight() - event.pageY
                    });
                }).on('mouseup', function () {
                    if (!bottom || !right) return;
                    config.handler(right, bottom);
                    $bars.remove(), $overlay.remove();
                    resize();
                });
                $([$bars, $overlay]).appendTo('body');
                return false;
            };
            var mousedown_handler = function (event) {
                return event.which !== 3 || event.button !== 2 ? left_handler()
                    : (event.stopPropagation(), (block_menu = true), right_handler($resizer));
            };
            var resize = function () {
                $resizer.css({
                    left: offset.left + $main.width() - icon_size,
                    top: offset.top + $main.outerHeight(true) - icon_size
                });
            };
            og.common.gadgets.manager.register({alive: function () {return true;}, resize: resize});
            resize();
            $(document).on('contextmenu.og_analytics_resize', function (event) {
                if (block_menu) return event.stopPropagation(), (block_menu = false);
            });
            $resizer.on('mousedown', mousedown_handler).appendTo('body');
        };
    }
});<commit_msg>added mouse out handler for resize<commit_after>/*
 * Copyright 2012 - present by OpenGamma Inc. and the OpenGamma group of companies
 * Please see distribution for license.
 */
$.register_module({
    name: 'og.analytics.resize',
    dependencies: ['og.common.gadgets.manager'],
    obj: function () {
        return function (config) {
            var $main = $(config.selector), $menu, block_menu = false, $resizer = $(config.tmpl),
                icon_size = 16, offset = $main.offset(), right_handler = config.right_handler || $.noop,
                $bars = $('

'),
                $overlay = $('

');
            var left_handler = function () {
                var offset = $main.offset(), right, bottom;
                if ($menu) $menu.remove();
                $bars.css({width: $main.width(), height: $main.outerHeight(true) + 1,
                    top: offset.top, left: offset.left + 1});
                $overlay.on('mousemove', function (event) {
                    $bars.css({
                        width: 'auto', height: 'auto',
                        right: right = $(document).outerWidth() - event.pageX,
                        bottom: bottom = $(document).outerHeight() - event.pageY
                    });
                }).on('mouseup', function () {
                    if (!bottom || !right) return;
                    config.handler(right, bottom);
                    $bars.remove(), $overlay.remove();
                    resize();
                     $(window).off('mouseout');
                });
                $([$bars, $overlay]).appendTo('body');
                return false;
            };
            var mousedown_handler = function (event) {
                $(window).on('mouseout',  function(event) {
                    event = event ? event : window.event;
                    var from = event.relatedTarget || event.toElement;
                    if (!from || from.nodeName == "HTML") reset();
                });
                return event.which !== 3 || event.button !== 2 ? left_handler()
                    : (event.stopPropagation(), (block_menu = true), right_handler($resizer));
            };
            var resize = function () {
                $resizer.css({
                    left: offset.left + $main.width() - icon_size,
                    top: offset.top + $main.outerHeight(true) - icon_size
                });
            };
            var reset = function () {
                $bars.remove();
                $overlay.remove();
                $(window).off('mouseout');
            };
            og.common.gadgets.manager.register({alive: function () {return true;}, resize: resize});
            resize();
            $(document).on('contextmenu.og_analytics_resize', function (event) {
                if (block_menu) return event.stopPropagation(), (block_menu = false);
            });
            $resizer.on('mousedown', mousedown_handler).appendTo('body');
        };
    }
});<|endoftext|>