UNPKG

@c8y/ngx-components

Version:

Angular modules for Cumulocity IoT applications

1,179 lines (1,156 loc) 70.1 kB
import * as i0 from '@angular/core'; import { NgZone, Injectable, NgModule, Inject, inject, Optional, ChangeDetectionStrategy, Component } from '@angular/core'; import { setAngularJSGlobal, downgradeComponent, downgradeInjectable } from '@angular/upgrade/static'; import * as i2 from '@c8y/ngx-components'; import { DatePipe, ViewContext, EmptyComponent, gettext, AppStateService, RouterService, ActionService, PluginsResolveService, TenantUiService, RouterModule, hookNavigator, hookTab, hookActionBar, hookAction, hookBreadcrumb, hookDocs, BootstrapComponent, UserTotpRevokeComponent, DataGridComponent, LoadingComponent, RangeDisplayComponent, HelpComponent, HighlightComponent, EmptyStateComponent, PasswordInputComponent, HOOK_PATTERN_MESSAGES, HeaderService, AlertService, UserMenuService, DocsService, PasswordService, CachedLocaleDictionaryService, GlobalConfigService, ModalService, GainsightService, FilesService, ServiceRegistry, AssetLinkPipe, PropertyValueTransformService, FeatureCacheService, DateFormatService, NavigatorNodeRoot, getActivatedRoute, DynamicDatapointsResolver, DynamicManagedObjectResolver, C8yTranslateModule, hookComponent } from '@c8y/ngx-components'; import { UpgradedServicesModule } from '@c8y/ngx-components/upgrade/upgraded-services'; import * as angular from 'angular'; import { map, unary, find, forEach, startsWith, isArray, assign, every, pick, property, some, get } from 'lodash-es'; import { ReplaySubject, BehaviorSubject, merge, of, combineLatest, Observable, from, fromEventPattern, Subject, defer, debounceTime as debounceTime$1 } from 'rxjs'; import { filter, take, delay, map as map$1, debounceTime, switchMap, startWith, merge as merge$1, shareReplay } from 'rxjs/operators'; import { ResolveEnd, ActivationEnd, Router } from '@angular/router'; import { BasicAuth, FetchClient, QueriesUtil } from '@c8y/client'; import { AppLogsAutoRefreshComponent } from '@c8y/ngx-components/app-logs'; import { DatapointSelectionListComponent, DatapointSelectorService } from '@c8y/ngx-components/datapoint-selector'; import { PaginationComponent } from 'ngx-bootstrap/pagination'; import { RolesAssetTreeComponent } from '@c8y/ngx-components/user-roles'; import { PlatformConfigurationFormComponent } from '@c8y/ngx-components/platform-configuration'; import { WidgetPreviewWrapperComponent, ContextDashboardService } from '@c8y/ngx-components/context-dashboard'; import { ApiService } from '@c8y/ngx-components/api'; import { DeviceGridService } from '@c8y/ngx-components/device-grid'; import { DeviceTypeDetailEditedService } from '@c8y/ngx-components/device-protocols'; import * as i1 from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core'; import { CommonModule } from '@angular/common'; import { AssetSelectorModule } from '@c8y/ngx-components/assets-navigator'; class AbsoluteDateService { constructor(datePipe) { this.datePipe = datePipe; } getFilter() { return (value, format = 'medium', timezone, locale) => this.datePipe.transform(value, format, timezone, locale); } } function absoluteDateServiceFactory(datePipe) { return new AbsoluteDateService(datePipe).getFilter(); } const absoluteDateServiceProvider = { provide: AbsoluteDateService, useFactory: absoluteDateServiceFactory, deps: [DatePipe] }; var ViewContextLegacyParameter; (function (ViewContextLegacyParameter) { ViewContextLegacyParameter["Device"] = "deviceId"; ViewContextLegacyParameter["Group"] = "groupId"; ViewContextLegacyParameter["User"] = "userId"; ViewContextLegacyParameter["Application"] = "applicationId"; ViewContextLegacyParameter["Microservice"] = "applicationId"; ViewContextLegacyParameter["SubscribedApplications"] = "applicationId"; ViewContextLegacyParameter["Tenant"] = "tenantId"; ViewContextLegacyParameter["Service"] = "deviceId"; ViewContextLegacyParameter["Simulators"] = "deviceId"; // required to hook the Alarms tab to a Simulator view })(ViewContextLegacyParameter || (ViewContextLegacyParameter = {})); function c8yViewsProvider($routeProvider, c8yTabsProvider, c8yPathUtils) { 'ngInject'; const viewMap = {}; const contextViews = new ReplaySubject(); return { when, $get() { return { contextViews, when(path, cfg) { return when(path, cfg, true); }, getByPath, prefixWithSlash }; } }; /** * @ngdoc function * @name when * @methodOf c8y.ui.provider:c8yViewsProvider * * @description * Defines a view for given route. * If multiple views are defined for a single route then there will be a separate tab for each view available when user visits that route. * * @param path Target route. * @param cfg View configuration object with the following properties: * * - **name** - `string` - View's name (in case of multiple views at single route this will be displayed as tab's title). * - **priority** - `integer` - View's priority (in case of multiple views at single route this will determine the position of view's tab in the tabs stack). * - **icon** - `string` - Font Awesome icon name for the view (displayed on the tab's header). * - **showIf** - `function` - Function returning boolean value indicating whether to show a tab for the view or not. * - **templateUrl** - `string` - Path to the template to use for displaying the view. * * You can also provide other view options - the same as available for standard {@link https://docs.angularjs.org/api/ngRoute/provider/$routeProvider $routeProvider} in AngularJS. * * ```html * The following example demonstrates how to add a new view to device details route * (which will be displayed as a tab if other views are assigned to the same route): * <pre> * c8yViewsProvider.when('/device/:deviceId', { * name: 'Tracking', * templateUrl: ':::PLUGIN_PATH:::/views/index.html', * icon: 'crosshairs', * showIf: ['$routeParams', 'c8yDevices', function ($routeParams, c8yDevices) { * var deviceId = $routeParams.deviceId; * return c8yDevices.detailCached(deviceId).then(function (res) { * var device = res.data; * return device && (device.c8y_MotionTracking || device.c8y_Geofence); * }); * }] * }); * </pre> * ``` */ function when(path, cfg, runPhase) { const newPath = prefixWithSlash(path); cfg.resolve = cfg.resolve || {}; // eslint-disable-next-line no-underscore-dangle cfg.resolve.__c8y_locales = [ 'c8yLocales', c8yLocales => { return c8yLocales.initDone; } ]; let currentCfg = viewMap[newPath]; const originalPath = newPath; if (!cfg.name) { // console.warn('View name not defined'); } if (!currentCfg) { viewMap[newPath] = []; currentCfg = viewMap[newPath]; } const upgradedContext = Object.keys(ViewContext) .map(key => ({ key, isUpgrade: prefixWithSlash(ViewContext[key].replace('id', ViewContextLegacyParameter[key])) === path })) .find(({ isUpgrade }) => isUpgrade); if (upgradedContext) { currentCfg.push(cfg); cfg.path = newPath; const p = c8yPathUtils.appendSegment(originalPath.replace(path, ''), cfg.name); contextViews.next({ ...cfg, path: cfg.name ? p.substring(1) : '', contextKey: upgradedContext.key, runPhase }); cfg.showIf = undefined; if (cfg.name) { cfg.path = c8yPathUtils.appendSegment(originalPath, cfg.name); } } else { if (currentCfg.length === 1) { const [existingConfig] = currentCfg; existingConfig.path = c8yPathUtils.appendSegment(originalPath, existingConfig.name); existingConfig.tab = createTab(originalPath, existingConfig); $routeProvider.when(existingConfig.path, existingConfig); } currentCfg.push(cfg); cfg.path = newPath; if (currentCfg.length > 1) { cfg.path = c8yPathUtils.appendSegment(originalPath, cfg.name); createTab(originalPath, cfg); $routeProvider.when(prefixWithSlash(originalPath), { resolveRedirectTo($route, $q, c8yUiUtil, c8yTabs, gettextCatalog) { 'ngInject'; const sortedCurrentCfg = c8yTabsProvider.sortTabsViews(currentCfg, gettextCatalog); const params = $route.current.pathParams; return $q .all(map(sortedCurrentCfg, unary(c8yUiUtil.configureVisibility))) .then(views => { const first = find(views, 'show'); let url = first.path; forEach(params, (val, key) => { url = url.replace(`:${key}`, val); }); c8yTabs.redirectedViewPath = url; return url; }); } }); } } return $routeProvider.when(prefixWithSlash(cfg.path), cfg); } function getByPath(path) { return viewMap[prefixWithSlash(path)]; } function createTab(path, cfg) { c8yTabsProvider.addTab(path, cfg); } function prefixWithSlash(path) { const prefix = startsWith(path, '/') ? '' : '/'; return prefix + path; } } class BridgeService { constructor(injector, appState, router, ngZone, routerService, actionService, plugins) { this.injector = injector; this.appState = appState; this.router = router; this.ngZone = ngZone; this.routerService = routerService; this.actionService = actionService; this.$liveTabs = new BehaviorSubject([]); this.initialNavigationDone = false; this.fixE2eIssues(); this.$ng1RouteChangeSuccess = this.fromNg1Event(this.injector.get('$rootScope'), '$routeChangeSuccess'); this.$ng1RouteChangeStart = this.fromNg1Event(this.injector.get('$rootScope'), '$routeChangeStart'); this.hookLanguage(); this.hookTab(); this.hookNavigator(); this.hookUserMenu(); this.hookViewProvider(); this.hookRoute(); plugins.allPluginsLoaded$ .pipe(filter(tmp => !!tmp), take(1), delay(1)) .subscribe(() => { this.initialNavigationDone = true; this.router.initialNavigation(); }); this.ng1Routes(); } /** * Ensure that angularjs routes are not using any * secondary router outlets */ hookRoute() { this.router.events .pipe(filter(event => event instanceof ResolveEnd), map$1((event) => event.state.root.firstChild), filter(route => route && route.routeConfig && route.routeConfig.path === '**')) .subscribe((event) => { if (event.root.children.length > 1) { window.location.hash = event.root.children[0].url.toString(); } }); } hookViewProvider() { const c8yViews = this.injector.get('c8yViews'); // fix to trigger an angularjs route change success // event on context route match to make legacy // view-providers resolve. c8yViews.when('/device/:id', { template: '' }); c8yViews.when('/group/:id', { template: '' }); c8yViews.contextViews.subscribe(cfg => this.addRoute(cfg)); } addRoute(cfg) { this.routerService.addRoute({ label: cfg.label || cfg.name, path: cfg.path, icon: cfg.icon, context: ViewContext[cfg.contextKey], priority: cfg.priority, component: EmptyComponent, data: { showIf: cfg.showIf ? ngxRoute => { const params = { ...ngxRoute.params, [ViewContextLegacyParameter[cfg.contextKey]]: ngxRoute.params.id }; const showIfResult = this.injector.invoke(cfg.showIf, undefined, { $routeParams: params }); // make sure showIf result is a promise with boolean result: return this.injector.get('$q').when(showIfResult).then(Boolean); } : undefined }, ...(cfg.featureId && { featureId: cfg.featureId }) }); if (cfg.runPhase) { this.routerService.refresh(); } } ng1Routes() { const template = ''; const fallbackRoutes = []; for (const context in ViewContext) { if (ViewContext[context] !== ViewContext.Alarms) { const path = ViewContext[context].match(/(\w+)\//)[1]; const regexp = new RegExp(`^/${path}/(?:([^/]+)).*$`); fallbackRoutes.push({ keys: [{ name: ViewContextLegacyParameter[context], optional: false }], regexp, template }); } } /** * When asset detail routes (/device/:id, /group/:id) are matched in Angular Router, ngRoute in * angular.js must also have matching generic routes so that the ids can be extracted from the paths and * injected in multiple calls (showIf, c8yActions, etc) as properties of $routeParams. * * The function in src/ngRoute/route.js (angular.js) where the routes are matched is called parseRoute(). This * function calls angular.forEach and in turn this function checks for the presence of a forEach method before * trying object key iteration. * By attaching a non enumerable forEach method to the routes object we guarantee that the fallback generic routes * are only matched after any other registered through $routeProvider.when. */ const $route = this.injector.get('$route'); Object.defineProperty($route.routes, 'forEach', { // make non enumerable value: function forEach(iterator, context) { // tslint:disable-next-line: forin for (const key in this) { iterator.call(context, this[key], key, this); } fallbackRoutes.forEach(r => iterator.call(context, r)); }, configurable: true }); /** * Some functions use the current context. As some parts are upgraded and some not, the following updates the * angularjs getContext function to resolve always the right context. */ const c8yUiUtil = this.injector.get('c8yUiUtil'); const _getContext = c8yUiUtil.getContext; this.router.events .pipe(filter(event => event instanceof ActivationEnd)) .subscribe((event) => { if (event.snapshot.routeConfig.path === '**') { c8yUiUtil.getContext = _getContext; } else if (event.snapshot.data && event.snapshot.data.context) { c8yUiUtil.getContext = () => { return { context: event.snapshot.data.context.replace('/:id', ''), id: event.snapshot.data.contextData.id, contextData: event.snapshot.data.contextData }; }; } else { c8yUiUtil.getContext = () => ({ context: null, id: null }); } }); } fixE2eIssues() { try { const { ngZone } = this; const { Utils } = window.org.cometd; const timeoutFn = Utils.setTimeout; // tslint:disable-next-line:only-arrow-functions Utils.setTimeout = function (...args) { return ngZone.runOutsideAngular(() => timeoutFn.apply(Utils, args)); }; } catch (e) { // do nothing } try { const { ace } = window; const editFn = ace.edit; const { ngZone } = this; // tslint:disable-next-line:only-arrow-functions ace.edit = function (...args) { return ngZone.runOutsideAngular(() => editFn.apply(ace, args)); }; } catch (e) { // do nothing } } hookLanguage() { let first = true; this.appState .map(store => store.lang) .subscribe(lang => { this.injector.get('c8yLocales').switchToLanguage(lang); if (!first) { this.injector.get('$rootScope').$apply(); } first = false; }); } hookTab() { // Just for instantiation of the c8yAction service this.injector.get('c8yActions'); const $location = this.injector.get('$location'); const c8yTabs = this.injector.get('c8yTabs'); let liveTabs = []; c8yTabs.addTab = tab => { liveTabs.push({ ...tab, label: tab.label || tab.name, path: decodeURIComponent(tab.path) }); this.$liveTabs.next(liveTabs); }; this.$ng1RouteChangeStart.subscribe(() => { liveTabs = []; this.$liveTabs.next(liveTabs); }); this.$ng1RouteChangeSuccess.subscribe(() => { const path = $location.path(); if (this.router.url !== path && this.initialNavigationDone) { this.router.navigate(path === '/' ? '' : path.split('/'), { queryParams: $location.search(), skipLocationChange: true }); } if (this.actionService) { this.actionService.refresh(); } }); this.$routeChanges = merge(this.$ng1RouteChangeSuccess, this.fromNg1Event(c8yTabs, c8yTabs.EVENT_UPDATE), of(1)).pipe(debounceTime(100)); } hookNavigator() { this.navigationNodes$ = this.injector.get('c8yNavigator').rootNodes$; } getTabs() { const onlyVisible = ({ show }) => show; const upgradeTab = tab => ({ ...tab, label: tab.label || tab.name, path: decodeURIComponent(tab.path) }); const routeTabs = this.$routeChanges.pipe(switchMap(() => { const routes = this.injector.get('c8yTabs').routeTabs; const visibilityPromise = Promise.all(routes.map(({ checkingVisibility }) => checkingVisibility)); return visibilityPromise.then(() => routes.filter(onlyVisible).map(upgradeTab)); }), startWith([])); return combineLatest([routeTabs, this.$liveTabs]).pipe(map$1(([route, live]) => route.concat(live))); } getQuickLinks() { const c8yQuickLinks = this.injector.get('c8yQuickLinks'); return c8yQuickLinks.list(); } getActionBarItems() { const c8yActionBar = this.injector.get('c8yActionBar'); const $rootScope = this.injector.get('$rootScope'); const getActionBarElements = () => c8yActionBar.elements.map(element => ({ priority: element.getAttribute('action-bar-priority') || 0, template: element, placement: element.getAttribute('action-bar-position') || 'right' })); return this.fromNg1Event($rootScope, 'c8yActionBarChanged').pipe(startWith(1), map$1(getActionBarElements)); } getBreadcrumbs() { const $location = this.injector.get('$location'); const c8yBreadcrumbs = this.injector.get('c8yBreadcrumbs'); const breadcrumbsUpdate$ = new Observable(subscriber => c8yBreadcrumbs.$on('update', () => subscriber.next())); return breadcrumbsUpdate$.pipe(startWith(0), switchMap(() => { const path = $location.path(); const breadcrumbs = c8yBreadcrumbs.get(path) || {}; const breadcrumbsData = this.resolveBreadcrumbsData(breadcrumbs.data); return from(breadcrumbsData).pipe(map$1((value) => { const liveBreadcrumbs = c8yBreadcrumbs.getLiveBreadcrumbs(); value = value.concat(liveBreadcrumbs); return value.map(items => ({ items: items })); })); })); } resolveBreadcrumbsData(data) { try { return this.injector.invoke(data); } catch (ex) { // empty } if (isArray(data)) { return of([data]); } return of([]); } getSearch() { const c8ySearch = this.injector.get('c8ySearch'); return c8ySearch.list().map(item => { return { icon: 'search', name: item.name, term: '', onSearch() { if (this.term) { c8ySearch.search(this.term); } } }; }); } getActions() { const registeredActions = this.injector.get('c8yActions').registeredActions; return of(registeredActions .filter(action => !action.hidden) .map(action => ({ // The priority was reversed: Aligned it to dashboard, high first, low last. priority: (action.priority || 0) * -1, label: action.text, icon: action.icon, disabled: action.disabled, action: () => { this.injector.invoke(action.action, action); } }))); } fromNg1Event(obj, evt) { let stopListening; function add(handler) { stopListening = obj.$on(evt, handler); } return fromEventPattern(add, () => stopListening()); } hookUserMenu() { const userMenuService = this.injector.get('c8yUserMenuService'); const c8yAccessDenied = this.injector.get('c8yAccessDenied'); userMenuService.add({ icon: 'access', priority: 10, label: gettext('Access denied requests'), click: c8yAccessDenied.showAccessDeniedRequestsList }); } } function bridgeServiceFactory(injector, appState, router, ngZone, routerService, actionService, plugins) { return new BridgeService(injector, appState, router, ngZone, routerService, actionService, plugins); } const bridgeServiceProvider = { provide: BridgeService, useFactory: bridgeServiceFactory, deps: [ '$injector', AppStateService, Router, NgZone, RouterService, ActionService, PluginsResolveService ] }; class Ng1ActionBarFactoryService { constructor(bridge) { this.bridge = bridge; this.routeChanges$ = bridge.$routeChanges; this.$ng1RouteChangeSuccess = bridge.$ng1RouteChangeSuccess; } get() { return this.routeChanges$.pipe(switchMap(() => { return this.bridge.getActionBarItems(); })); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: Ng1ActionBarFactoryService, deps: [{ token: BridgeService }], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: Ng1ActionBarFactoryService }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: Ng1ActionBarFactoryService, decorators: [{ type: Injectable }], ctorParameters: () => [{ type: BridgeService }] }); class Ng1ActionFactoryService { constructor(bridge, tabs) { this.bridge = bridge; this.tabs = tabs; this.routeChanges$ = bridge.$routeChanges; this.$location = bridge.injector.get('$location'); this.tabs.items$.subscribe(newTabs => this.handleTabsRedirect(newTabs)); } handleTabsRedirect(tabs) { /** * This function is doing the same process as function redirect in the file * modules/core/ui/navigation/tabs.provider.js * That function is not run because bridge.service.ts overrides the method addTab where the redirect() was called. */ const redirectedTab = tabs.find(tab => tab.redirectedTo); const [topPriorityTab] = tabs; if (redirectedTab && !topPriorityTab.redirectedTo) { this.$location.replace(); this.$location.path(topPriorityTab.path); topPriorityTab.redirectedTo = true; redirectedTab.redirectedTo = false; } } get() { return this.bridge.getActions(); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: Ng1ActionFactoryService, deps: [{ token: BridgeService }, { token: i2.TabsService }], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: Ng1ActionFactoryService }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: Ng1ActionFactoryService, decorators: [{ type: Injectable }], ctorParameters: () => [{ type: BridgeService }, { type: i2.TabsService }] }); class AuthBridgeService { constructor(injector, basicAuth, fetchClient, appState, tenantUiService) { this.injector = injector; this.basicAuth = basicAuth; this.fetchClient = fetchClient; this.appState = appState; this.tenantUiService = tenantUiService; this.hookAuth(); } updateBasicAuth(credentials) { const { headers } = this.fetchClient.getFetchOptions({}); if (headers.Authorization) { const token = headers.Authorization.match(/basic\s(.*)$/i)[1]; if (token) { this.basicAuth.updateCredentials(credentials); this.fetchClient.setAuth(this.basicAuth); } } } hookAuth() { this.appState.currentUser.subscribe(user => { if (!user) { this.injector.get('$rootScope').$emit('authStateChange', { hasAuth: false }); return; } this.injector.get('c8yAuth').headers = () => this.fetchClient.getFetchOptions({}).headers; const { headers } = this.fetchClient.getFetchOptions({}); const authorizationHeader = headers.Authorization; if (typeof authorizationHeader === 'string' && authorizationHeader.startsWith('Basic')) { const matches = authorizationHeader.match(/basic\s(.*)$/i); const token = matches && matches[1]; if (token) { this.setToken(token, headers.tfatoken); } } else if (typeof authorizationHeader === 'string' && authorizationHeader.startsWith('Bearer')) { this.setToken(undefined, headers.tfatoken, 'Bearer'); } else { this.setToken(undefined, headers.tfatoken, 'Oauth'); } this.injector.get('$rootScope').$emit('authStateChange', { hasAuth: true }); }); } setToken(token, tfa, type = 'Basic') { const c8yAuth = this.injector.get('c8yAuth'); if (type === 'Basic') { c8yAuth.onSetToken({ token, type }); if (tfa) { c8yAuth.setTFAToken(tfa); } } else { c8yAuth.authReady(); } } getPreferredLoginOption() { return this.tenantUiService.getPreferredLoginOption(this.appState.state.loginOptions); } } function authBridgeServiceFactory(injector, basicAuth, fetchClient, appState, tenantUiService) { return new AuthBridgeService(injector, basicAuth, fetchClient, appState, tenantUiService); } const authBridgeServiceProvider = { provide: AuthBridgeService, useFactory: authBridgeServiceFactory, deps: ['$injector', BasicAuth, FetchClient, AppStateService, TenantUiService] }; class Ng1BreadcrumbFactoryService { constructor(bridge) { this.bridge = bridge; this.trigger = new ReplaySubject(1); this.breadcrumbs = this.trigger.pipe(debounceTime(100), switchMap(() => { return this.bridge.getBreadcrumbs(); })); } get() { this.trigger.next(); return this.breadcrumbs; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: Ng1BreadcrumbFactoryService, deps: [{ token: BridgeService }], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: Ng1BreadcrumbFactoryService }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: Ng1BreadcrumbFactoryService, decorators: [{ type: Injectable }], ctorParameters: () => [{ type: BridgeService }] }); class Ng1DocsFactoryService { constructor(bridge) { this.bridge = bridge; this.links = this.bridge.getQuickLinks(); this.links.then(list => { list.map(el => { el.type = el.type || 'quicklink'; return el; }); }); } get() { return this.links; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: Ng1DocsFactoryService, deps: [{ token: BridgeService }], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: Ng1DocsFactoryService }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: Ng1DocsFactoryService, decorators: [{ type: Injectable }], ctorParameters: () => [{ type: BridgeService }] }); class Ng1NodesFactoryService { constructor(bridge) { this.bridge = bridge; } get() { return this.bridge.navigationNodes$; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: Ng1NodesFactoryService, deps: [{ token: BridgeService }], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: Ng1NodesFactoryService }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: Ng1NodesFactoryService, decorators: [{ type: Injectable }], ctorParameters: () => [{ type: BridgeService }] }); class Ng1SmartRulesService { } function SmartRulesServiceFactory(injector) { return injector.get('smartRulesSvc'); } const smartRulesServiceProvider = { provide: Ng1SmartRulesService, useFactory: SmartRulesServiceFactory, deps: ['$injector'] }; class Ng1TabsFactoryService { constructor(bridge) { this.bridge = bridge; this.tabsObservable = bridge.getTabs(); } get() { return this.tabsObservable; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: Ng1TabsFactoryService, deps: [{ token: BridgeService }], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: Ng1TabsFactoryService }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: Ng1TabsFactoryService, decorators: [{ type: Injectable }], ctorParameters: () => [{ type: BridgeService }] }); setAngularJSGlobal(angular); class UpgradeModule { static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: UpgradeModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); } static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: UpgradeModule, imports: [RouterModule, UpgradedServicesModule] }); } static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: UpgradeModule, providers: [ absoluteDateServiceProvider, bridgeServiceProvider, authBridgeServiceProvider, smartRulesServiceProvider, hookNavigator(Ng1NodesFactoryService), hookTab(Ng1TabsFactoryService), hookActionBar(Ng1ActionBarFactoryService), hookAction(Ng1ActionFactoryService), hookBreadcrumb(Ng1BreadcrumbFactoryService), hookDocs(Ng1DocsFactoryService) ], imports: [RouterModule, UpgradedServicesModule] }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: UpgradeModule, decorators: [{ type: NgModule, args: [{ imports: [RouterModule, UpgradedServicesModule], exports: [], providers: [ absoluteDateServiceProvider, bridgeServiceProvider, authBridgeServiceProvider, smartRulesServiceProvider, hookNavigator(Ng1NodesFactoryService), hookTab(Ng1TabsFactoryService), hookActionBar(Ng1ActionBarFactoryService), hookAction(Ng1ActionFactoryService), hookBreadcrumb(Ng1BreadcrumbFactoryService), hookDocs(Ng1DocsFactoryService) ] }] }] }); function c8yAlertDecorator($delegate, $rootScope, $injector) { 'ngInject'; $delegate.add = alert => { $delegate.addAlert(transformAlert(alert)); }; $rootScope.$on('alert', (evt, alert) => { $delegate.addAlert(transformAlert(alert)); }); $rootScope.$on('message', (evt, alert) => { $delegate.addAlert(transformAlert(alert)); }); /** * Solution based on the: https://stackoverflow.com/questions/40102148/how-to-iterate-over-all-properties-in-objects-prototype-chain. * Problem came after switching to ES6, as all prototype properties of classes are non-enumerable. */ const allNames = new Set(); for (let o = $delegate; o !== Object.prototype; o = Object.getPrototypeOf(o)) { for (const name of Object.getOwnPropertyNames(o)) { allNames.add(name); } } Array.from(allNames).forEach((property) => { if (typeof $delegate[property] === 'function') { $delegate[property] = $delegate[property].bind($delegate); } }); function transformAlert(alert) { const newAlert = { ...alert }; if (alert.onClose) { newAlert.onClose = () => { $injector.invoke(alert.onClose); }; } if (alert.onDetail) { newAlert.onDetail = () => { $injector.invoke(alert.onDetail); }; } return newAlert; } return $delegate; } const bootstrapComponentDowngradedComponent = downgradeComponent({ component: BootstrapComponent }); const userTotpComponentDowngradedComponent = downgradeComponent({ component: UserTotpRevokeComponent }); const appLogsAutoRefreshComponentDowngradedComponent = downgradeComponent({ component: AppLogsAutoRefreshComponent }); const dataGridComponentDowngradedComponent = downgradeComponent({ component: DataGridComponent }); const loadingComponentDowngradedComponent = downgradeComponent({ component: LoadingComponent }); const rangeDisplayComponentDowngradedComponent = downgradeComponent({ component: RangeDisplayComponent }); const helpComponentDowngradedComponent = downgradeComponent({ component: HelpComponent }); const highlightComponentDowngradedComponent = downgradeComponent({ component: HighlightComponent, inputs: ['pattern', 'text'] }); const emptyStateComponentDowngradedComponent = downgradeComponent({ component: EmptyStateComponent }); const datapointSelectionListComponentDowngradedComponent = downgradeComponent({ component: DatapointSelectionListComponent }); const paginationComponentDowngradedComponent = downgradeComponent({ component: PaginationComponent }); const rolesAssetTreeComponentDowngradedComponent = downgradeComponent({ component: RolesAssetTreeComponent }); const passwordInputComponentDowngradedComponent = downgradeComponent({ component: PasswordInputComponent }); const platformConfigurationFormDowngradedComponent = downgradeComponent({ component: PlatformConfigurationFormComponent }); const widgetPreviewWrapperDowngradedComponent = downgradeComponent({ component: WidgetPreviewWrapperComponent }); class ServerMessagesService { constructor(translateService, patterns) { this.translateService = translateService; this.MESSAGE_PATTERNS = patterns; } translate(s) { return this.translateService.instant(s); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ServerMessagesService, deps: [{ token: i1.TranslateService }, { token: HOOK_PATTERN_MESSAGES }], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ServerMessagesService }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ServerMessagesService, decorators: [{ type: Injectable }], ctorParameters: () => [{ type: i1.TranslateService }, { type: undefined, decorators: [{ type: Inject, args: [HOOK_PATTERN_MESSAGES] }] }] }); const absoluteDateServiceDowngradedInjectable = downgradeInjectable(AbsoluteDateService); const bridgeServiceDowngradedInjectable = downgradeInjectable(BridgeService); const authBridgeServiceDowngradedInjectable = downgradeInjectable(AuthBridgeService); const appStateServiceDowngradedInjectable = downgradeInjectable(AppStateService); const headerServiceDowngradedInjectable = downgradeInjectable(HeaderService); const alertsServiceDowngradedInjectable = downgradeInjectable(AlertService); const userMenuServiceDowngradedInjectable = downgradeInjectable(UserMenuService); const apiServiceDowngradedInjectable = downgradeInjectable(ApiService); const docsServiceDowngradedInjectable = downgradeInjectable(DocsService); const passwordServiceDowngradedInjectable = downgradeInjectable(PasswordService); const translateServiceDowngradedInjectable = downgradeInjectable(TranslateService); const cachedLocaleDictionaryServiceDowngradedInjectable = downgradeInjectable(CachedLocaleDictionaryService); const globalConfigServiceDowngradedInjectable = downgradeInjectable(GlobalConfigService); const serverMessagesServiceDowngradedInjectable = downgradeInjectable(ServerMessagesService); const modalServiceDowngradedInjectable = downgradeInjectable(ModalService); const gainsightServiceDowngradedInjectable = downgradeInjectable(GainsightService); const filesServiceDowngradedInjectable = downgradeInjectable(FilesService); const deviceTypeDetailEditedServiceDowngradedInjectable = downgradeInjectable(DeviceTypeDetailEditedService); const deviceGridServiceDowngradedInjectable = downgradeInjectable(DeviceGridService); const serviceRegistryInjectable = downgradeInjectable(ServiceRegistry); const assetLinkPipeDowngradedInjectable = downgradeInjectable(AssetLinkPipe); const propertyValueTransformServiceDowngradedInjectable = downgradeInjectable(PropertyValueTransformService); const featureCacheServiceDowngradedInjectable = downgradeInjectable(FeatureCacheService); const datapointSelectorServiceDowngradedInjectable = downgradeInjectable(DatapointSelectorService); const dateFormatServiceInjectable = downgradeInjectable(DateFormatService); function gettextCatalogDecorator($delegate, $interpolate, c8yTranslate) { 'ngInject'; const gettextCatalog = $delegate; const originalGetString = angular.bind(gettextCatalog, gettextCatalog.getString); function newGetString(input, scope, context) { if (typeof input === 'string') { const translatedString = originalGetString(input, scope, context); const interpolatedString = scope ? $interpolate(input)(scope) : input; let stringToReturn = translatedString; if (translatedString && translatedString === interpolatedString) { const translatedServerMessage = c8yTranslate.instant(interpolatedString); stringToReturn = translatedServerMessage; } return stringToReturn; } return input; } gettextCatalog.getString = newGetString; return gettextCatalog; } function groupTypesHierarchyNavigatorDecorator($delegate, $q) { 'ngInject'; $delegate.loadAll = () => $q.when(); $delegate.addGroupNavigation = () => $q.when(); return $delegate; } function c8yNg1HttpInterceptor($q, c8yLoadingIndicator, c8yApiService) { 'ngInject'; function request(config) { const { url, method } = config; c8yApiService.onStart({ url, method, options: config }); return config; } function requestError(rejection) { finishRequest(rejection); return $q.reject(rejection); } function response(res) { finishRequest(res); return res; } function responseError(rejection) { finishRequest(rejection); c8yLoadingIndicator.responseError(rejection); return $q.reject(rejection); } function finishRequest(res) { const { url, method } = res.config; c8yApiService.onFinish({ url, method, response: res, options: res.config }); } return { request, requestError, response, responseError }; } class NavigatorNodeRootLegacy extends NavigatorNodeRoot { addRoot(nodeData) { let duplicate; if (nodeData.path === '') { nodeData.path = '/'; } nodeData.label = nodeData.name; if (typeof nodeData.parent === 'object') { nodeData.parent.label = nodeData.parent.name; } if (nodeData.preventDuplicates) { duplicate = this.find(({ path, parents, label }) => { return (path === nodeData.path && label === nodeData.label && parents.some(p => p.label === nodeData.parent)); }); if (duplicate) { duplicate.routerLinkExact = false; } } return duplicate || super.addRoot(nodeData); } createNode(node) { const newNode = super.createNode(node); const update = newNode.update.bind(newNode); // eslint-disable-next-line @typescript-eslint/no-this-alias const root = this; return Object.defineProperties(assign(newNode, { realName: newNode.name || newNode.label, _parent: true, // just use it to detect if it has been deleted update(data) { if (this._parent === undefined) { // _parent was deleted somene instead to put this in root root.addRoot(this); // put it back so it can be deleted again this._parent = true; } update(data); }, addChild(nodeChild) { this.add(root.createNode(nodeChild)); } }), { label: { get() { return this.realName || ''; }, set(name) { this.realName = name; } }, name: { get() { return this.realName || ''; }, set(name) { this.realName = name; } }, show: { get() { return !this.hidden; }, set(show) { this.hidden = !show; }, configurable: true } }); } } // Just to hook into the bridge service function c8yNavigatorProvider() { const root = new NavigatorNodeRootLegacy(); const rootNodesSubject = new Subject(); const conditionalNodes = []; const rootNodes$ = rootNodesSubject.pipe(merge$1(defer(() => of(root.children)))); function addNavigation(nodes) { const nodeList = Array.isArray(nodes) ? nodes : [nodes]; nodeList.forEach(node => { if (isConditional(node)) { node.hidden = undefined; conditionalNodes.push(node); } node.navNode = root.addRoot(node); }); rootNodesSubject.next(root.children); } function removeNavigation(node) { const found = root.find(n => n === node); if (found) { found.parents.forEach(p => p.remove(found)); rootNodesSubject.next(root.children); } } function findNode(node) { return root.find(node); } function isConditional(node) { return node.showIf || node.showIfPermissions || node.showIfContainsVisibleViews; } function $get($q, $injector) { 'ngInject'; // This avoids the circular dependency setTimeout(() => conditionalNodes.forEach(processShowIf)); function processShowIf(node) { const c8yUiUtil = $injector.get('c8yUiUtil'); const visibilityPromises = []; const { showIf, showIfPermissions, showIfContainsVisibleViews } = node; if (showIf) { visibilityPromises.push($injector.invoke(showIf)); } if (showIfContainsVisibleViews) { visibilityPromises.push(viewsConditionalVisibility(node)); } c8yUiUtil .configureVisibility({ showIf: () => $q.all(visibilityPromises).then(every), showIfPermissions }, 'visible') .then(({ visible }) => { if (visible) { node.navNode.update({ hidden: false, showIf: null, showIfPermission: null, showIfContainsVisibleViews: null }); } else { node.navNode.update({ hidden: true }); } }); } function viewsConditionalVisibility(node) { const c8yUiUtil = $injector.get('c8yUiUtil'); const c8yViews = $injector.get('c8yViews'); const views = c8yViews.getByPath(node.path); return $q .all(map(views, view => c8yUiUtil .configureVisibility(pick(view, ['showIf', 'showIfPermissions']), 'show', false) .then(property('show')))) .then(some); } return { rootNodes() { return root.children; }, findNode, addNavigation, removeNavigation, rootNodes$ }; } return { $get, addNavigation, removeNavigation }; } const rootComponent = { template: ` <c8y-bootstrap> <div id="c8y-legacy-view"> <div ng-view ng-if="vm.widthSet && vm.authState.hasAuth"></div> </div> </c8y-bootstrap>`, controller: c8yUiRootController, controllerAs: 'vm' }; function c8yUiRootController($rootScope, $timeout, c8yBase, c8yNavigator, c8yApplication, c8yHeaderService) { 'ngInject'; // eslint-disable-next-line @typescript-eslint/no-this-alias const vm = this; Object.assign(vm, { $onInit, navOpen: false }); //////////// function $onInit() { c8yHeaderService .map(states => states.nav.open) .subscribe(isOpen => { vm.navOpen = isOpen; }); c8yHeaderService.configNavigator({ canToggle: true }); $rootScope.$on('authStateChange', onAuthStateChange); vm.rootNodes = c8yNavigator.rootNodes; c8yApplication.currentAppCached().then(onAppInfo); vm.navHiddenOnStartup = c8yBase.appOption('hide_navigator'); checkReady(); } function onAuthStateChange(evt, data) { vm.authState = data; } function onAppInfo() { vm.tabsHorizontal = c8yBase.appOption('tabsHorizontal'); } function checkReady() { const element = document.querySelector('#c8y-legacy-view'); const hasWidth = element && element.clientWidth; if (hasWidth) { vm.widthSet = true; }