@sapui5/sap.fe.core 1.98.0 → 1.99.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -2
- package/src/sap/fe/core/.library +1 -1
- package/src/sap/fe/core/AnnotationHelper.js +377 -405
- package/src/sap/fe/core/AnnotationHelper.ts +385 -0
- package/src/sap/fe/core/AppComponent.js +3 -2
- package/src/sap/fe/core/AppStateHandler.js +229 -181
- package/src/sap/fe/core/AppStateHandler.ts +171 -0
- package/src/sap/fe/core/BaseController.js +3 -2
- package/src/sap/fe/core/BusyLocker.js +105 -121
- package/src/sap/fe/core/BusyLocker.ts +98 -0
- package/src/sap/fe/core/CommonUtils.js +2073 -2399
- package/src/sap/fe/core/CommonUtils.ts +2078 -0
- package/src/sap/fe/core/ExtensionAPI.js +3 -2
- package/src/sap/fe/core/PageController.js +84 -125
- package/src/sap/fe/core/PageController.ts +101 -0
- package/src/sap/fe/core/RouterProxy.js +986 -823
- package/src/sap/fe/core/RouterProxy.ts +838 -0
- package/src/sap/fe/core/Synchronization.js +51 -35
- package/src/sap/fe/core/Synchronization.ts +29 -0
- package/src/sap/fe/core/TemplateComponent.js +173 -164
- package/src/sap/fe/core/TemplateComponent.ts +166 -0
- package/src/sap/fe/core/TemplateModel.js +97 -54
- package/src/sap/fe/core/TemplateModel.ts +64 -0
- package/src/sap/fe/core/TransactionHelper.js +1576 -1664
- package/src/sap/fe/core/TransactionHelper.ts +1706 -0
- package/src/sap/fe/core/actions/draft.js +561 -608
- package/src/sap/fe/core/actions/draft.ts +595 -0
- package/src/sap/fe/core/actions/messageHandling.js +545 -514
- package/src/sap/fe/core/actions/messageHandling.ts +532 -0
- package/src/sap/fe/core/actions/nonDraft.js +17 -19
- package/src/sap/fe/core/actions/nonDraft.ts +12 -0
- package/src/sap/fe/core/actions/operations.js +1074 -1215
- package/src/sap/fe/core/actions/operations.ts +1162 -0
- package/src/sap/fe/core/actions/sticky.js +102 -105
- package/src/sap/fe/core/actions/sticky.ts +102 -0
- package/src/sap/fe/core/controllerextensions/ControllerExtensionMetadata.js +3 -2
- package/src/sap/fe/core/controllerextensions/EditFlow.js +170 -392
- package/src/sap/fe/core/controllerextensions/IntentBasedNavigation.js +3 -2
- package/src/sap/fe/core/controllerextensions/InternalEditFlow.js +279 -11
- package/src/sap/fe/core/controllerextensions/InternalIntentBasedNavigation.js +8 -6
- package/src/sap/fe/core/controllerextensions/InternalRouting.js +52 -47
- package/src/sap/fe/core/controllerextensions/KPIManagement.js +2 -3
- package/src/sap/fe/core/controllerextensions/KPIManagement.ts +29 -26
- package/src/sap/fe/core/controllerextensions/MassEdit.js +101 -8
- package/src/sap/fe/core/controllerextensions/MessageHandler.js +17 -8
- package/src/sap/fe/core/controllerextensions/PageReady.js +2 -2
- package/src/sap/fe/core/controllerextensions/PageReady.ts +12 -8
- package/src/sap/fe/core/controllerextensions/Paginator.js +3 -2
- package/src/sap/fe/core/controllerextensions/Placeholder.js +3 -2
- package/src/sap/fe/core/controllerextensions/Routing.js +9 -4
- package/src/sap/fe/core/controllerextensions/RoutingListener.js +3 -2
- package/src/sap/fe/core/controllerextensions/Share.js +3 -2
- package/src/sap/fe/core/controllerextensions/SideEffects.js +3 -3
- package/src/sap/fe/core/controllerextensions/SideEffects.ts +18 -18
- package/src/sap/fe/core/controllerextensions/ViewState.js +3 -2
- package/src/sap/fe/core/controls/CommandExecution.js +3 -2
- package/src/sap/fe/core/controls/ConditionalWrapper.js +3 -2
- package/src/sap/fe/core/controls/CustomQuickViewPage.js +3 -2
- package/src/sap/fe/core/controls/DataLossOrDraftDiscard/DataLossOrDraftDiscardHandler.js +3 -2
- package/src/sap/fe/core/controls/FieldWrapper.js +11 -22
- package/src/sap/fe/core/controls/FilterBar.js +3 -2
- package/src/sap/fe/core/controls/FormElementWrapper.js +3 -2
- package/src/sap/fe/core/controls/MultiValueParameterDelegate.js +3 -2
- package/src/sap/fe/core/controls/NonComputedVisibleKeyFieldsDialog.fragment.xml +1 -0
- package/src/sap/fe/core/controls/filterbar/FilterContainer.js +3 -2
- package/src/sap/fe/core/controls/filterbar/VisualFilter.js +5 -4
- package/src/sap/fe/core/controls/filterbar/VisualFilterContainer.js +3 -2
- package/src/sap/fe/core/controls/filterbar/utils/VisualFilterUtils.js +3 -2
- package/src/sap/fe/core/controls/massEdit/MassEditHandlers.js +3 -2
- package/src/sap/fe/core/converters/ConverterContext.js +2 -2
- package/src/sap/fe/core/converters/ConverterContext.ts +4 -3
- package/src/sap/fe/core/converters/ManifestSettings.js +1 -1
- package/src/sap/fe/core/converters/ManifestSettings.ts +1 -0
- package/src/sap/fe/core/converters/ManifestWrapper.js +39 -26
- package/src/sap/fe/core/converters/ManifestWrapper.ts +9 -0
- package/src/sap/fe/core/converters/MetaModelConverter.js +11 -8
- package/src/sap/fe/core/converters/MetaModelConverter.ts +16 -16
- package/src/sap/fe/core/converters/TemplateConverter.js +1 -1
- package/src/sap/fe/core/converters/TemplateConverter.ts +2 -2
- package/src/sap/fe/core/converters/common/AnnotationConverter.js +17 -13
- package/src/sap/fe/core/converters/controls/Common/Action.js +2 -2
- package/src/sap/fe/core/converters/controls/Common/Action.ts +2 -2
- package/src/sap/fe/core/converters/controls/Common/Chart.js +5 -3
- package/src/sap/fe/core/converters/controls/Common/Chart.ts +11 -3
- package/src/sap/fe/core/converters/controls/Common/DataVisualization.js +3 -3
- package/src/sap/fe/core/converters/controls/Common/DataVisualization.ts +2 -2
- package/src/sap/fe/core/converters/controls/Common/Form.js +4 -2
- package/src/sap/fe/core/converters/controls/Common/Form.ts +16 -2
- package/src/sap/fe/core/converters/controls/Common/Table.js +250 -146
- package/src/sap/fe/core/converters/controls/Common/Table.ts +300 -167
- package/src/sap/fe/core/converters/controls/Common/table/StandardActions.js +115 -56
- package/src/sap/fe/core/converters/controls/Common/table/StandardActions.ts +120 -74
- package/src/sap/fe/core/converters/controls/ListReport/FilterBar.js +467 -32
- package/src/sap/fe/core/converters/controls/ListReport/FilterBar.ts +483 -54
- package/src/sap/fe/core/converters/controls/ObjectPage/SubSection.js +6 -6
- package/src/sap/fe/core/converters/controls/ObjectPage/SubSection.ts +9 -15
- package/src/sap/fe/core/converters/helpers/Aggregation.js +13 -5
- package/src/sap/fe/core/converters/helpers/Aggregation.ts +20 -5
- package/src/sap/fe/core/converters/helpers/IssueManager.js +4 -1
- package/src/sap/fe/core/converters/helpers/IssueManager.ts +3 -0
- package/src/sap/fe/core/converters/objectPage/HeaderAndFooterAction.js +4 -4
- package/src/sap/fe/core/converters/objectPage/HeaderAndFooterAction.ts +3 -3
- package/src/sap/fe/core/converters/templates/ListReportConverter.js +13 -6
- package/src/sap/fe/core/converters/templates/ListReportConverter.ts +26 -15
- package/src/sap/fe/core/designtime/AppComponent.designtime.js +3 -2
- package/src/sap/fe/core/formatters/CriticalityFormatter.js +1 -1
- package/src/sap/fe/core/formatters/CriticalityFormatter.ts +1 -1
- package/src/sap/fe/core/formatters/FPMFormatter.js +1 -1
- package/src/sap/fe/core/formatters/FPMFormatter.ts +4 -10
- package/src/sap/fe/core/formatters/KPIFormatter.js +1 -1
- package/src/sap/fe/core/formatters/KPIFormatter.ts +3 -1
- package/src/sap/fe/core/formatters/TableFormatter.js +39 -28
- package/src/sap/fe/core/formatters/TableFormatter.ts +43 -28
- package/src/sap/fe/core/formatters/ValueFormatter.js +1 -1
- package/src/sap/fe/core/formatters/ValueFormatter.ts +7 -6
- package/src/sap/fe/core/fpm/Component.js +3 -2
- package/src/sap/fe/core/helpers/AppStartupHelper.js +2 -2
- package/src/sap/fe/core/helpers/AppStartupHelper.ts +7 -4
- package/src/sap/fe/core/helpers/BindingExpression.js +314 -355
- package/src/sap/fe/core/helpers/BindingExpression.ts +317 -391
- package/src/sap/fe/core/helpers/ClassSupport.js +27 -15
- package/src/sap/fe/core/helpers/ClassSupport.ts +31 -20
- package/src/sap/fe/core/helpers/DynamicAnnotationPathHelper.js +63 -59
- package/src/sap/fe/core/helpers/DynamicAnnotationPathHelper.ts +56 -0
- package/src/sap/fe/core/helpers/EditState.js +81 -84
- package/src/sap/fe/core/helpers/EditState.ts +81 -0
- package/src/sap/fe/core/helpers/ExcelFormatHelper.js +62 -48
- package/src/sap/fe/core/helpers/ExcelFormatHelper.ts +49 -0
- package/src/sap/fe/core/helpers/FPMHelper.js +52 -56
- package/src/sap/fe/core/helpers/FPMHelper.ts +62 -0
- package/src/sap/fe/core/helpers/KeepAliveHelper.js +3 -4
- package/src/sap/fe/core/helpers/KeepAliveHelper.ts +9 -9
- package/src/sap/fe/core/helpers/MassEditHelper.js +27 -18
- package/src/sap/fe/core/helpers/ModelHelper.js +229 -225
- package/src/sap/fe/core/helpers/ModelHelper.ts +227 -0
- package/src/sap/fe/core/helpers/PasteHelper.js +210 -132
- package/src/sap/fe/core/helpers/PasteHelper.ts +196 -0
- package/src/sap/fe/core/helpers/SemanticDateOperators.js +332 -313
- package/src/sap/fe/core/helpers/SemanticDateOperators.ts +330 -0
- package/src/sap/fe/core/helpers/SemanticKeyHelper.js +66 -67
- package/src/sap/fe/core/helpers/SemanticKeyHelper.ts +73 -0
- package/src/sap/fe/core/helpers/StableIdHelper.js +4 -7
- package/src/sap/fe/core/helpers/StableIdHelper.ts +2 -6
- package/src/sap/fe/core/jsx-runtime/jsx.js +1 -1
- package/src/sap/fe/core/jsx-runtime/jsx.ts +1 -1
- package/src/sap/fe/core/library.js +4 -3
- package/src/sap/fe/core/library.support.js +3 -2
- package/src/sap/fe/core/manifestMerger/ChangePageConfiguration.js +1 -1
- package/src/sap/fe/core/manifestMerger/ChangePageConfiguration.ts +1 -1
- package/src/sap/fe/core/messagebundle.properties +23 -10
- package/src/sap/fe/core/messagebundle_ar.properties +9 -0
- package/src/sap/fe/core/messagebundle_bg.properties +9 -0
- package/src/sap/fe/core/messagebundle_ca.properties +9 -0
- package/src/sap/fe/core/messagebundle_cs.properties +10 -1
- package/src/sap/fe/core/messagebundle_cy.properties +9 -0
- package/src/sap/fe/core/messagebundle_da.properties +9 -0
- package/src/sap/fe/core/messagebundle_de.properties +9 -0
- package/src/sap/fe/core/messagebundle_el.properties +9 -0
- package/src/sap/fe/core/messagebundle_en.properties +9 -0
- package/src/sap/fe/core/messagebundle_en_GB.properties +9 -0
- package/src/sap/fe/core/messagebundle_en_US_sappsd.properties +9 -0
- package/src/sap/fe/core/messagebundle_en_US_saprigi.properties +9 -0
- package/src/sap/fe/core/messagebundle_en_US_saptrc.properties +9 -0
- package/src/sap/fe/core/messagebundle_es.properties +9 -0
- package/src/sap/fe/core/messagebundle_es_MX.properties +9 -0
- package/src/sap/fe/core/messagebundle_et.properties +9 -0
- package/src/sap/fe/core/messagebundle_fi.properties +9 -0
- package/src/sap/fe/core/messagebundle_fr.properties +13 -4
- package/src/sap/fe/core/messagebundle_fr_CA.properties +15 -6
- package/src/sap/fe/core/messagebundle_hi.properties +15 -6
- package/src/sap/fe/core/messagebundle_hr.properties +15 -6
- package/src/sap/fe/core/messagebundle_hu.properties +9 -0
- package/src/sap/fe/core/messagebundle_id.properties +10 -1
- package/src/sap/fe/core/messagebundle_it.properties +9 -0
- package/src/sap/fe/core/messagebundle_iw.properties +9 -0
- package/src/sap/fe/core/messagebundle_ja.properties +9 -0
- package/src/sap/fe/core/messagebundle_kk.properties +15 -6
- package/src/sap/fe/core/messagebundle_ko.properties +9 -0
- package/src/sap/fe/core/messagebundle_lt.properties +9 -0
- package/src/sap/fe/core/messagebundle_lv.properties +15 -6
- package/src/sap/fe/core/messagebundle_ms.properties +14 -5
- package/src/sap/fe/core/messagebundle_nl.properties +9 -0
- package/src/sap/fe/core/messagebundle_no.properties +9 -0
- package/src/sap/fe/core/messagebundle_pl.properties +9 -0
- package/src/sap/fe/core/messagebundle_pt.properties +9 -0
- package/src/sap/fe/core/messagebundle_pt_PT.properties +9 -0
- package/src/sap/fe/core/messagebundle_ro.properties +9 -0
- package/src/sap/fe/core/messagebundle_ru.properties +9 -0
- package/src/sap/fe/core/messagebundle_sh.properties +11 -2
- package/src/sap/fe/core/messagebundle_sk.properties +9 -0
- package/src/sap/fe/core/messagebundle_sl.properties +15 -6
- package/src/sap/fe/core/messagebundle_sv.properties +9 -0
- package/src/sap/fe/core/messagebundle_th.properties +9 -0
- package/src/sap/fe/core/messagebundle_tr.properties +9 -0
- package/src/sap/fe/core/messagebundle_uk.properties +9 -0
- package/src/sap/fe/core/messagebundle_vi.properties +9 -0
- package/src/sap/fe/core/messagebundle_zh_CN.properties +9 -0
- package/src/sap/fe/core/messagebundle_zh_TW.properties +15 -6
- package/src/sap/fe/core/services/AsyncComponentServiceFactory.js +1 -1
- package/src/sap/fe/core/services/AsyncComponentServiceFactory.ts +3 -1
- package/src/sap/fe/core/services/CacheHandlerServiceFactory.js +269 -202
- package/src/sap/fe/core/services/CacheHandlerServiceFactory.ts +212 -0
- package/src/sap/fe/core/services/EnvironmentServiceFactory.js +1 -1
- package/src/sap/fe/core/services/EnvironmentServiceFactory.ts +7 -6
- package/src/sap/fe/core/services/NavigationServiceFactory.js +406 -300
- package/src/sap/fe/core/services/NavigationServiceFactory.ts +316 -0
- package/src/sap/fe/core/services/ResourceModelServiceFactory.js +149 -81
- package/src/sap/fe/core/services/ResourceModelServiceFactory.ts +80 -0
- package/src/sap/fe/core/services/RoutingServiceFactory.js +987 -899
- package/src/sap/fe/core/services/RoutingServiceFactory.ts +898 -0
- package/src/sap/fe/core/services/ShellServicesFactory.js +1 -1
- package/src/sap/fe/core/services/ShellServicesFactory.ts +15 -10
- package/src/sap/fe/core/services/SideEffectsServiceFactory.js +35 -81
- package/src/sap/fe/core/services/SideEffectsServiceFactory.ts +46 -94
- package/src/sap/fe/core/services/TemplatedViewServiceFactory.js +461 -487
- package/src/sap/fe/core/services/TemplatedViewServiceFactory.ts +453 -0
- package/src/sap/fe/core/services/view/TemplatingErrorPage.controller.js +10 -8
- package/src/sap/fe/core/services/view/TemplatingErrorPage.controller.ts +8 -0
- package/src/sap/fe/core/support/AnnotationIssue.support.js +15 -3
- package/src/sap/fe/core/support/AnnotationIssue.support.ts +16 -2
- package/src/sap/fe/core/support/CollectionFacetUnsupportedLevel.support.js +2 -2
- package/src/sap/fe/core/support/CollectionFacetUnsupportedLevel.support.ts +1 -1
- package/src/sap/fe/core/templating/FieldControlHelper.js +8 -8
- package/src/sap/fe/core/templating/FieldControlHelper.ts +25 -7
- package/src/sap/fe/core/templating/FilterHelper.js +138 -72
- package/src/sap/fe/core/templating/FilterHelper.ts +139 -71
- package/src/sap/fe/core/templating/UIFormatters.js +31 -1
- package/src/sap/fe/core/templating/UIFormatters.ts +33 -2
- package/src/sap/fe/core/type/Email.js +1 -1
- package/src/sap/fe/core/type/Email.ts +4 -6
- package/ui5.yaml +0 -3
|
@@ -0,0 +1,898 @@
|
|
|
1
|
+
import Event from "sap/ui/base/Event";
|
|
2
|
+
import Service from "sap/ui/core/service/Service";
|
|
3
|
+
import ServiceFactory from "sap/ui/core/service/ServiceFactory";
|
|
4
|
+
import BindingParser from "sap/ui/base/BindingParser";
|
|
5
|
+
import SemanticKeyHelper from "sap/fe/core/helpers/SemanticKeyHelper";
|
|
6
|
+
import Log from "sap/base/Log";
|
|
7
|
+
import EventProvider from "sap/ui/base/EventProvider";
|
|
8
|
+
import BusyLocker from "sap/fe/core/BusyLocker";
|
|
9
|
+
import ModelHelper from "sap/fe/core/helpers/ModelHelper";
|
|
10
|
+
import EditState from "sap/fe/core/helpers/EditState";
|
|
11
|
+
import messageHandling from "sap/fe/core/actions/messageHandling";
|
|
12
|
+
import { Placeholder } from "sap/fe/core/controllerextensions";
|
|
13
|
+
import AppStartupHelper from "sap/fe/core/helpers/AppStartupHelper";
|
|
14
|
+
import ODataUtils from "sap/ui/model/odata/v4/ODataUtils";
|
|
15
|
+
import { ServiceContext } from "sap/ui/core/service";
|
|
16
|
+
import { AppComponent } from "sap/fe/core";
|
|
17
|
+
import ODataModel from "sap/ui/model/odata/v4/ODataModel";
|
|
18
|
+
import ODataMetaModel from "sap/ui/model/odata/v4/ODataMetaModel";
|
|
19
|
+
import Router from "sap/ui/core/routing/Router";
|
|
20
|
+
import RouterProxy from "sap/fe/core/RouterProxy";
|
|
21
|
+
import Context from "sap/ui/model/odata/v4/Context";
|
|
22
|
+
type RoutingServiceSettings = {};
|
|
23
|
+
const RoutingServiceEventing = EventProvider.extend("sap.fe.core.services.RoutingServiceEventing", {
|
|
24
|
+
metadata: {
|
|
25
|
+
events: {
|
|
26
|
+
"routeMatched": {},
|
|
27
|
+
"afterRouteMatched": {}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
class RoutingService extends Service<RoutingServiceSettings> {
|
|
32
|
+
oAppComponent!: AppComponent;
|
|
33
|
+
oModel!: ODataModel;
|
|
34
|
+
oMetaModel!: ODataMetaModel;
|
|
35
|
+
oRouter!: Router;
|
|
36
|
+
oRouterProxy!: RouterProxy;
|
|
37
|
+
eventProvider!: EventProvider;
|
|
38
|
+
initPromise!: Promise<any>;
|
|
39
|
+
outbounds: any;
|
|
40
|
+
_mTargets: any;
|
|
41
|
+
_mRoutes: any;
|
|
42
|
+
oLastSemanticMapping: any;
|
|
43
|
+
bExitOnNavigateBackToRoot?: boolean;
|
|
44
|
+
sCurrentRouteName?: string;
|
|
45
|
+
sCurrentRoutePattern?: string;
|
|
46
|
+
aCurrentViews?: any[];
|
|
47
|
+
navigationInfoQueue: any[] = [];
|
|
48
|
+
sContextPath!: string;
|
|
49
|
+
_fnOnRouteMatched!: Function;
|
|
50
|
+
init() {
|
|
51
|
+
const oContext = this.getContext();
|
|
52
|
+
if (oContext.scopeType === "component") {
|
|
53
|
+
this.oAppComponent = oContext.scopeObject;
|
|
54
|
+
this.oModel = this.oAppComponent.getModel();
|
|
55
|
+
this.oMetaModel = this.oModel.getMetaModel();
|
|
56
|
+
this.oRouter = this.oAppComponent.getRouter();
|
|
57
|
+
this.oRouterProxy = this.oAppComponent.getRouterProxy();
|
|
58
|
+
this.eventProvider = new (RoutingServiceEventing as any)();
|
|
59
|
+
|
|
60
|
+
const oRoutingConfig = this.oAppComponent.getManifestEntry("/sap.ui5/routing");
|
|
61
|
+
const oRootViewConfig = this.oAppComponent.getManifestEntry("/sap.ui5/rootView");
|
|
62
|
+
this._parseRoutingConfiguration(oRoutingConfig, oRootViewConfig);
|
|
63
|
+
|
|
64
|
+
const oAppConfig = this.oAppComponent.getManifestEntry("/sap.app");
|
|
65
|
+
this.outbounds = oAppConfig && oAppConfig.crossNavigation && oAppConfig.crossNavigation.outbounds;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
this.initPromise = Promise.resolve(this);
|
|
69
|
+
}
|
|
70
|
+
beforeExit() {
|
|
71
|
+
this.oRouter.detachRouteMatched(this._fnOnRouteMatched, this);
|
|
72
|
+
this.eventProvider.fireEvent("routeMatched", {});
|
|
73
|
+
}
|
|
74
|
+
exit() {
|
|
75
|
+
this.eventProvider.destroy();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Parse a manifest routing configuration for internal usage.
|
|
80
|
+
*
|
|
81
|
+
* @param {object} oRoutingConfig The routing configuration from the manifest
|
|
82
|
+
* @param {object} oRootViewConfig The root view configuration from the manifest
|
|
83
|
+
* @private
|
|
84
|
+
*/
|
|
85
|
+
_parseRoutingConfiguration(oRoutingConfig: any, oRootViewConfig: any) {
|
|
86
|
+
const isFCL = oRootViewConfig && oRootViewConfig.viewName === "sap.fe.templates.RootContainer.view.Fcl";
|
|
87
|
+
|
|
88
|
+
// Information of targets
|
|
89
|
+
this._mTargets = {};
|
|
90
|
+
Object.keys(oRoutingConfig.targets).forEach((sTargetName: string) => {
|
|
91
|
+
this._mTargets[sTargetName] = Object.assign({ targetName: sTargetName }, oRoutingConfig.targets[sTargetName]);
|
|
92
|
+
|
|
93
|
+
// View level for FCL cases is calculated from the target pattern
|
|
94
|
+
if (this._mTargets[sTargetName].contextPattern !== undefined) {
|
|
95
|
+
this._mTargets[sTargetName].viewLevel = this._getViewLevelFromPattern(this._mTargets[sTargetName].contextPattern, 0);
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// Information of routes
|
|
100
|
+
this._mRoutes = {};
|
|
101
|
+
for (const sRouteKey in oRoutingConfig.routes) {
|
|
102
|
+
const oRouteManifestInfo = oRoutingConfig.routes[sRouteKey],
|
|
103
|
+
aRouteTargets = Array.isArray(oRouteManifestInfo.target) ? oRouteManifestInfo.target : [oRouteManifestInfo.target],
|
|
104
|
+
sRouteName = Array.isArray(oRoutingConfig.routes) ? oRouteManifestInfo.name : sRouteKey,
|
|
105
|
+
sRoutePattern = oRouteManifestInfo.pattern;
|
|
106
|
+
|
|
107
|
+
// Check route pattern: all patterns need to end with ':?query:', that we use for parameters
|
|
108
|
+
if (sRoutePattern.length < 8 || sRoutePattern.indexOf(":?query:") !== sRoutePattern.length - 8) {
|
|
109
|
+
Log.warning("Pattern for route " + sRouteName + " doesn't end with ':?query:' : " + sRoutePattern);
|
|
110
|
+
}
|
|
111
|
+
const iRouteLevel = this._getViewLevelFromPattern(sRoutePattern, 0);
|
|
112
|
+
this._mRoutes[sRouteName] = {
|
|
113
|
+
name: sRouteName,
|
|
114
|
+
pattern: sRoutePattern,
|
|
115
|
+
targets: aRouteTargets,
|
|
116
|
+
routeLevel: iRouteLevel
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
// Add the parent targets in the list of targets for the route
|
|
120
|
+
for (let i = 0; i < aRouteTargets.length; i++) {
|
|
121
|
+
const sParentTargetName = this._mTargets[aRouteTargets[i]].parent;
|
|
122
|
+
if (sParentTargetName) {
|
|
123
|
+
aRouteTargets.push(sParentTargetName);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (!isFCL) {
|
|
128
|
+
// View level for non-FCL cases is calculated from the route pattern
|
|
129
|
+
if (this._mTargets[aRouteTargets[0]].viewLevel === undefined || this._mTargets[aRouteTargets[0]].viewLevel < iRouteLevel) {
|
|
130
|
+
// There are cases when different routes point to the same target. We take the
|
|
131
|
+
// largest viewLevel in that case.
|
|
132
|
+
this._mTargets[aRouteTargets[0]].viewLevel = iRouteLevel;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// FCL level for non-FCL cases is equal to -1
|
|
136
|
+
this._mTargets[aRouteTargets[0]].FCLLevel = -1;
|
|
137
|
+
} else if (aRouteTargets.length === 1 && this._mTargets[aRouteTargets[0]].controlAggregation !== "beginColumnPages") {
|
|
138
|
+
// We're in the case where there's only 1 target for the route, and it's not in the first column
|
|
139
|
+
// --> this is a fullscreen column after all columns in the FCL have been used
|
|
140
|
+
this._mTargets[aRouteTargets[0]].FCLLevel = 3;
|
|
141
|
+
} else {
|
|
142
|
+
// Other FCL cases
|
|
143
|
+
aRouteTargets.forEach((sTargetName: any) => {
|
|
144
|
+
switch (this._mTargets[sTargetName].controlAggregation) {
|
|
145
|
+
case "beginColumnPages":
|
|
146
|
+
this._mTargets[sTargetName].FCLLevel = 0;
|
|
147
|
+
break;
|
|
148
|
+
|
|
149
|
+
case "midColumnPages":
|
|
150
|
+
this._mTargets[sTargetName].FCLLevel = 1;
|
|
151
|
+
break;
|
|
152
|
+
|
|
153
|
+
default:
|
|
154
|
+
this._mTargets[sTargetName].FCLLevel = 2;
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Propagate viewLevel, contextPattern, FCLLevel and controlAggregation to parent targets
|
|
161
|
+
Object.keys(this._mTargets).forEach((sTargetName: string) => {
|
|
162
|
+
while (this._mTargets[sTargetName].parent) {
|
|
163
|
+
const sParentTargetName = this._mTargets[sTargetName].parent;
|
|
164
|
+
this._mTargets[sParentTargetName].viewLevel =
|
|
165
|
+
this._mTargets[sParentTargetName].viewLevel || this._mTargets[sTargetName].viewLevel;
|
|
166
|
+
this._mTargets[sParentTargetName].contextPattern =
|
|
167
|
+
this._mTargets[sParentTargetName].contextPattern || this._mTargets[sTargetName].contextPattern;
|
|
168
|
+
this._mTargets[sParentTargetName].FCLLevel =
|
|
169
|
+
this._mTargets[sParentTargetName].FCLLevel || this._mTargets[sTargetName].FCLLevel;
|
|
170
|
+
this._mTargets[sParentTargetName].controlAggregation =
|
|
171
|
+
this._mTargets[sParentTargetName].controlAggregation || this._mTargets[sTargetName].controlAggregation;
|
|
172
|
+
sTargetName = sParentTargetName;
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
// Determine the root entity for the app
|
|
177
|
+
const aLevel0RouteNames = [];
|
|
178
|
+
const aLevel1RouteNames = [];
|
|
179
|
+
let sDefaultRouteName;
|
|
180
|
+
|
|
181
|
+
for (const sName in this._mRoutes) {
|
|
182
|
+
const iLevel = this._mRoutes[sName].routeLevel;
|
|
183
|
+
if (iLevel === 0) {
|
|
184
|
+
aLevel0RouteNames.push(sName);
|
|
185
|
+
} else if (iLevel === 1) {
|
|
186
|
+
aLevel1RouteNames.push(sName);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (aLevel0RouteNames.length === 1) {
|
|
191
|
+
sDefaultRouteName = aLevel0RouteNames[0];
|
|
192
|
+
} else if (aLevel1RouteNames.length === 1) {
|
|
193
|
+
sDefaultRouteName = aLevel1RouteNames[0];
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (sDefaultRouteName) {
|
|
197
|
+
const sDefaultTargetName = this._mRoutes[sDefaultRouteName].targets.slice(-1)[0];
|
|
198
|
+
this.sContextPath = "";
|
|
199
|
+
if (this._mTargets[sDefaultTargetName].options && this._mTargets[sDefaultTargetName].options.settings) {
|
|
200
|
+
const oSettings = this._mTargets[sDefaultTargetName].options.settings;
|
|
201
|
+
this.sContextPath = oSettings.contextPath || "/" + oSettings.entitySet;
|
|
202
|
+
}
|
|
203
|
+
if (!this.sContextPath) {
|
|
204
|
+
Log.warning(
|
|
205
|
+
"Cannot determine default contextPath: contextPath or entitySet missing in default target: " + sDefaultTargetName
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
} else {
|
|
209
|
+
Log.warning("Cannot determine default contextPath: no default route found.");
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// We need to establish the correct path to the different pages, including the navigation properties
|
|
213
|
+
Object.keys(this._mTargets)
|
|
214
|
+
.map((sTargetKey: string) => {
|
|
215
|
+
return this._mTargets[sTargetKey];
|
|
216
|
+
})
|
|
217
|
+
.sort((a: any, b: any) => {
|
|
218
|
+
return a.viewLevel < b.viewLevel ? -1 : 1;
|
|
219
|
+
})
|
|
220
|
+
.forEach((target: any) => {
|
|
221
|
+
// After sorting the targets per level we can then go through their navigation object and update the paths accordingly.
|
|
222
|
+
if (target.options) {
|
|
223
|
+
const settings = target.options.settings;
|
|
224
|
+
const sContextPath = settings.contextPath || (settings.entitySet ? "/" + settings.entitySet : "");
|
|
225
|
+
if (!settings.fullContextPath && sContextPath) {
|
|
226
|
+
settings.fullContextPath = sContextPath + "/";
|
|
227
|
+
}
|
|
228
|
+
Object.keys(settings.navigation || {}).forEach((sNavName: string) => {
|
|
229
|
+
// Check if it's a navigation property
|
|
230
|
+
const targetRoute = this._mRoutes[settings.navigation[sNavName].detail.route];
|
|
231
|
+
if (targetRoute && targetRoute.targets) {
|
|
232
|
+
targetRoute.targets.forEach((sTargetName: any) => {
|
|
233
|
+
if (
|
|
234
|
+
this._mTargets[sTargetName].options &&
|
|
235
|
+
this._mTargets[sTargetName].options.settings &&
|
|
236
|
+
!this._mTargets[sTargetName].options.settings.fullContextPath
|
|
237
|
+
) {
|
|
238
|
+
if (target.viewLevel === 0) {
|
|
239
|
+
this._mTargets[sTargetName].options.settings.fullContextPath = "/" + sNavName + "/";
|
|
240
|
+
} else {
|
|
241
|
+
this._mTargets[sTargetName].options.settings.fullContextPath =
|
|
242
|
+
settings.fullContextPath + sNavName + "/";
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Calculates a view level from a pattern by counting the number of segments.
|
|
254
|
+
*
|
|
255
|
+
* @param {string} sPattern The pattern
|
|
256
|
+
* @param {number} viewLevel The current level of view
|
|
257
|
+
* @returns {number} The level
|
|
258
|
+
*/
|
|
259
|
+
_getViewLevelFromPattern(sPattern: string, viewLevel: number): number {
|
|
260
|
+
sPattern = sPattern.replace(":?query:", "");
|
|
261
|
+
const regex = new RegExp("/[^/]*$");
|
|
262
|
+
if (sPattern && sPattern[0] !== "/" && sPattern[0] !== "?") {
|
|
263
|
+
sPattern = "/" + sPattern;
|
|
264
|
+
}
|
|
265
|
+
if (sPattern.length) {
|
|
266
|
+
sPattern = sPattern.replace(regex, "");
|
|
267
|
+
if (this.oRouter.match(sPattern) || sPattern === "") {
|
|
268
|
+
return this._getViewLevelFromPattern(sPattern, ++viewLevel);
|
|
269
|
+
} else {
|
|
270
|
+
return this._getViewLevelFromPattern(sPattern, viewLevel);
|
|
271
|
+
}
|
|
272
|
+
} else {
|
|
273
|
+
return viewLevel;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
_getRouteInformation(sRouteName: any) {
|
|
278
|
+
return this._mRoutes[sRouteName];
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
_getTargetInformation(sTargetName: any) {
|
|
282
|
+
return this._mTargets[sTargetName];
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
_getComponentId(sOwnerId: any, sComponentId: any) {
|
|
286
|
+
if (sComponentId.indexOf(sOwnerId + "---") === 0) {
|
|
287
|
+
return sComponentId.substr(sOwnerId.length + 3);
|
|
288
|
+
}
|
|
289
|
+
return sComponentId;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Get target information for a given component.
|
|
294
|
+
*
|
|
295
|
+
* @param {object} oComponentInstance Instance of the component
|
|
296
|
+
* @returns {object} The configuration for the target
|
|
297
|
+
*/
|
|
298
|
+
getTargetInformationFor(oComponentInstance: any) {
|
|
299
|
+
const sTargetComponentId = this._getComponentId(oComponentInstance._sOwnerId, oComponentInstance.getId());
|
|
300
|
+
let sCorrectTargetName = null;
|
|
301
|
+
Object.keys(this._mTargets).forEach((sTargetName: string) => {
|
|
302
|
+
if (this._mTargets[sTargetName].id === sTargetComponentId || this._mTargets[sTargetName].viewId === sTargetComponentId) {
|
|
303
|
+
sCorrectTargetName = sTargetName;
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
return this._getTargetInformation(sCorrectTargetName);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
getLastSemanticMapping() {
|
|
310
|
+
return this.oLastSemanticMapping;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
setLastSemanticMapping(oMapping: any) {
|
|
314
|
+
this.oLastSemanticMapping = oMapping;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
navigateTo(oContext: any, sRouteName: any, mParameterMapping: any, bPreserveHistory: any) {
|
|
318
|
+
let sTargetURLPromise, bIsStickyMode: boolean;
|
|
319
|
+
if (oContext.getModel() && oContext.getModel().getMetaModel) {
|
|
320
|
+
bIsStickyMode = ModelHelper.isStickySessionSupported(oContext.getModel().getMetaModel());
|
|
321
|
+
}
|
|
322
|
+
if (!mParameterMapping) {
|
|
323
|
+
// if there is no parameter mapping define this mean we rely entirely on the binding context path
|
|
324
|
+
sTargetURLPromise = Promise.resolve(SemanticKeyHelper.getSemanticPath(oContext));
|
|
325
|
+
} else {
|
|
326
|
+
sTargetURLPromise = this.prepareParameters(mParameterMapping, sRouteName, oContext).then((mParameters: any) => {
|
|
327
|
+
return this.oRouter.getURL(sRouteName, mParameters);
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
return sTargetURLPromise.then((sTargetURL: any) => {
|
|
331
|
+
this.oRouterProxy.navToHash(sTargetURL, bPreserveHistory, false, false, !bIsStickyMode);
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Method to return a map of routing target parameters where the binding syntax is resolved to the current model.
|
|
337
|
+
*
|
|
338
|
+
* @param {object} mParameters Parameters map in the format [k: string] : ComplexBindingSyntax
|
|
339
|
+
* @param {string} sTargetRoute Name of the target route
|
|
340
|
+
* @param {object} oContext The instance of the binding context
|
|
341
|
+
* @returns {Promise} A promise which resolves to the routing target parameters
|
|
342
|
+
*/
|
|
343
|
+
prepareParameters(mParameters: any, sTargetRoute: string, oContext: Context) {
|
|
344
|
+
let oParametersPromise;
|
|
345
|
+
try {
|
|
346
|
+
const sContextPath = oContext.getPath();
|
|
347
|
+
const oMetaModel: ODataMetaModel = oContext.getModel().getMetaModel() as ODataMetaModel;
|
|
348
|
+
const aContextPathParts = sContextPath.split("/");
|
|
349
|
+
const aAllResolvedParameterPromises = Object.keys(mParameters).map((sParameterKey: any) => {
|
|
350
|
+
const sParameterMappingExpression = mParameters[sParameterKey];
|
|
351
|
+
// We assume the defined parameters will be compatible with a binding expression
|
|
352
|
+
const oParsedExpression = BindingParser.complexParser(sParameterMappingExpression);
|
|
353
|
+
const aParts = oParsedExpression.parts || [oParsedExpression];
|
|
354
|
+
const aResolvedParameterPromises = aParts.map(function(oPathPart: any) {
|
|
355
|
+
const aRelativeParts = oPathPart.path.split("../");
|
|
356
|
+
// We go up the current context path as many times as necessary
|
|
357
|
+
const aLocalParts = aContextPathParts.slice(0, aContextPathParts.length - aRelativeParts.length + 1);
|
|
358
|
+
aLocalParts.push(aRelativeParts[aRelativeParts.length - 1]);
|
|
359
|
+
|
|
360
|
+
const sPropertyPath = aLocalParts.join("/");
|
|
361
|
+
const oMetaContext = (oMetaModel as any).getMetaContext(sPropertyPath);
|
|
362
|
+
return oContext.requestProperty(sPropertyPath).then(function(oValue: any) {
|
|
363
|
+
const oPropertyInfo = oMetaContext.getObject();
|
|
364
|
+
const sEdmType = oPropertyInfo.$Type;
|
|
365
|
+
return ODataUtils.formatLiteral(oValue, sEdmType);
|
|
366
|
+
});
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
return Promise.all(aResolvedParameterPromises).then((aResolvedParameters: any) => {
|
|
370
|
+
const value = oParsedExpression.formatter
|
|
371
|
+
? oParsedExpression.formatter.apply(this, aResolvedParameters)
|
|
372
|
+
: aResolvedParameters.join("");
|
|
373
|
+
return { key: sParameterKey, value: value };
|
|
374
|
+
});
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
oParametersPromise = Promise.all(aAllResolvedParameterPromises).then(function(
|
|
378
|
+
aAllResolvedParameters: { key: any; value: any }[]
|
|
379
|
+
) {
|
|
380
|
+
const oParameters: any = {};
|
|
381
|
+
aAllResolvedParameters.forEach(function(oResolvedParameter: { key: any; value: any }) {
|
|
382
|
+
oParameters[oResolvedParameter.key] = oResolvedParameter.value;
|
|
383
|
+
});
|
|
384
|
+
return oParameters;
|
|
385
|
+
});
|
|
386
|
+
} catch (oError) {
|
|
387
|
+
Log.error("Could not parse the parameters for the navigation to route " + sTargetRoute);
|
|
388
|
+
oParametersPromise = Promise.resolve(undefined);
|
|
389
|
+
}
|
|
390
|
+
return oParametersPromise;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
_fireRouteMatchEvents(mParameters: any) {
|
|
394
|
+
this.eventProvider.fireEvent("routeMatched", mParameters);
|
|
395
|
+
this.eventProvider.fireEvent("afterRouteMatched", mParameters);
|
|
396
|
+
|
|
397
|
+
EditState.cleanProcessedEditState(); // Reset UI state when all bindings have been refreshed
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* Navigates to a context.
|
|
402
|
+
*
|
|
403
|
+
* @param {sap.ui.model.odata.v4.Context} oContext The Context to be navigated to
|
|
404
|
+
* @param {object} [mParameters] Optional, map containing the following attributes:
|
|
405
|
+
* @param {boolean} [mParameters.checkNoHashChange] Navigate to the context without changing the URL
|
|
406
|
+
* @param {Promise} [mParameters.asyncContext] The context is created async, navigate to (...) and
|
|
407
|
+
* wait for Promise to be resolved and then navigate into the context
|
|
408
|
+
* @param {boolean} [mParameters.bDeferredContext] The context shall be created deferred at the target page
|
|
409
|
+
* @param {boolean} [mParameters.editable] The target page shall be immediately in the edit mode to avoid flickering
|
|
410
|
+
* @param {boolean} [mParameters.bPersistOPScroll] The bPersistOPScroll will be used for scrolling to first tab
|
|
411
|
+
* @param {number} [mParameters.updateFCLLevel] `+1` if we add a column in FCL, `-1` to remove a column, 0 to stay on the same column
|
|
412
|
+
* @param {boolean} [mParameters.noPreservationCache] Do navigation without taking into account the preserved cache mechanism
|
|
413
|
+
* @param {boolean} [mParameters.bRecreateContext] Force re-creation of the context instead of using the one passed as parameter
|
|
414
|
+
* @param {boolean} [mParameters.bForceFocus] Forces focus selection after navigation
|
|
415
|
+
* @param {object} [oViewData] View data
|
|
416
|
+
* @param {object} [oCurrentTargetInfo] The target information from which the navigation is triggered
|
|
417
|
+
* @returns {Promise} Promise which is resolved once the navigation is triggered
|
|
418
|
+
*
|
|
419
|
+
* @ui5-restricted
|
|
420
|
+
* @final
|
|
421
|
+
*/
|
|
422
|
+
navigateToContext(
|
|
423
|
+
oContext: any,
|
|
424
|
+
mParameters:
|
|
425
|
+
| {
|
|
426
|
+
checkNoHashChange?: boolean;
|
|
427
|
+
asyncContext?: Promise<any>;
|
|
428
|
+
bDeferredContext?: boolean;
|
|
429
|
+
editable?: boolean;
|
|
430
|
+
transient?: boolean;
|
|
431
|
+
bPersistOPScroll?: boolean;
|
|
432
|
+
updateFCLLevel?: number;
|
|
433
|
+
noPreservationCache?: boolean;
|
|
434
|
+
bRecreateContext?: boolean;
|
|
435
|
+
bForceFocus?: boolean;
|
|
436
|
+
targetPath?: string;
|
|
437
|
+
showPlaceholder?: boolean;
|
|
438
|
+
bDraftNavigation?: boolean;
|
|
439
|
+
}
|
|
440
|
+
| undefined,
|
|
441
|
+
oViewData: any | undefined,
|
|
442
|
+
oCurrentTargetInfo: any | undefined
|
|
443
|
+
) {
|
|
444
|
+
let sTargetRoute: string = "",
|
|
445
|
+
oRouteParametersPromise,
|
|
446
|
+
bIsStickyMode: boolean = false;
|
|
447
|
+
|
|
448
|
+
if (oContext.getModel() && oContext.getModel().getMetaModel) {
|
|
449
|
+
bIsStickyMode = ModelHelper.isStickySessionSupported(oContext.getModel().getMetaModel());
|
|
450
|
+
}
|
|
451
|
+
// Manage parameter mapping
|
|
452
|
+
if (mParameters && mParameters.targetPath && oViewData && oViewData.navigation) {
|
|
453
|
+
const oRouteDetail = oViewData.navigation[mParameters.targetPath].detail;
|
|
454
|
+
sTargetRoute = oRouteDetail.route;
|
|
455
|
+
|
|
456
|
+
if (oRouteDetail.parameters) {
|
|
457
|
+
oRouteParametersPromise = this.prepareParameters(oRouteDetail.parameters, sTargetRoute, oContext);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
let sTargetPath = this._getPathFromContext(oContext, mParameters);
|
|
462
|
+
// If the path is empty, we're supposed to navigate to the first page of the app
|
|
463
|
+
// Check if we need to exit from the app instead
|
|
464
|
+
if (sTargetPath.length === 0 && this.bExitOnNavigateBackToRoot) {
|
|
465
|
+
return this.oRouterProxy.exitFromApp();
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// If the context is deferred or async, we add (...) to the path
|
|
469
|
+
if (mParameters?.asyncContext || mParameters?.bDeferredContext) {
|
|
470
|
+
sTargetPath += "(...)";
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
// Add layout parameter if needed
|
|
474
|
+
const sLayout = this._calculateLayout(sTargetPath, mParameters);
|
|
475
|
+
if (sLayout) {
|
|
476
|
+
sTargetPath += "?layout=" + sLayout;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// Navigation parameters for later usage
|
|
480
|
+
const oNavigationInfo = {
|
|
481
|
+
oAsyncContext: mParameters?.asyncContext,
|
|
482
|
+
bDeferredContext: mParameters?.bDeferredContext,
|
|
483
|
+
bTargetEditable: mParameters?.editable,
|
|
484
|
+
bPersistOPScroll: mParameters?.bPersistOPScroll,
|
|
485
|
+
useContext: mParameters?.updateFCLLevel === -1 || mParameters?.bRecreateContext ? undefined : oContext,
|
|
486
|
+
bDraftNavigation: mParameters?.bDraftNavigation,
|
|
487
|
+
bShowPlaceholder: mParameters?.showPlaceholder !== undefined ? mParameters?.showPlaceholder : true
|
|
488
|
+
};
|
|
489
|
+
|
|
490
|
+
if (mParameters?.checkNoHashChange) {
|
|
491
|
+
// Check if the new hash is different from the current one
|
|
492
|
+
const sCurrentHashNoAppState = this.oRouterProxy.getHash().replace(/[&?]{1}sap-iapp-state=[A-Z0-9]+/, "");
|
|
493
|
+
if (sTargetPath === sCurrentHashNoAppState) {
|
|
494
|
+
// The hash doesn't change, but we fire the routeMatch event to trigger page refresh / binding
|
|
495
|
+
const mEventParameters: any = this.oRouter.getRouteInfoByHash(this.oRouterProxy.getHash());
|
|
496
|
+
if (mEventParameters) {
|
|
497
|
+
mEventParameters.navigationInfo = oNavigationInfo;
|
|
498
|
+
mEventParameters.routeInformation = this._getRouteInformation(this.sCurrentRouteName);
|
|
499
|
+
mEventParameters.routePattern = this.sCurrentRoutePattern;
|
|
500
|
+
mEventParameters.views = this.aCurrentViews;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
this.oRouterProxy.setFocusForced(!!mParameters.bForceFocus);
|
|
504
|
+
|
|
505
|
+
this._fireRouteMatchEvents(mEventParameters);
|
|
506
|
+
|
|
507
|
+
return Promise.resolve();
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
if (mParameters?.transient && mParameters.editable == true && sTargetPath.indexOf("(...)") === -1) {
|
|
512
|
+
if (sTargetPath.indexOf("?") > -1) {
|
|
513
|
+
sTargetPath += "&i-action=create";
|
|
514
|
+
} else {
|
|
515
|
+
sTargetPath += "?i-action=create";
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
// Clear unbound messages upon navigating from LR to OP
|
|
520
|
+
// This is to ensure stale error messages from LR are not shown to the user after navigation to OP.
|
|
521
|
+
if (oCurrentTargetInfo && oCurrentTargetInfo.name === "sap.fe.templates.ListReport") {
|
|
522
|
+
const oRouteInfo = this.oRouter.getRouteInfoByHash(sTargetPath);
|
|
523
|
+
if (oRouteInfo) {
|
|
524
|
+
const oRoute = this._getRouteInformation(oRouteInfo.name);
|
|
525
|
+
if (oRoute && oRoute.targets && oRoute.targets.length > 0) {
|
|
526
|
+
const sLastTargetName = oRoute.targets[oRoute.targets.length - 1];
|
|
527
|
+
const oTarget = this._getTargetInformation(sLastTargetName);
|
|
528
|
+
if (oTarget && oTarget.name === "sap.fe.templates.ObjectPage") {
|
|
529
|
+
messageHandling.removeUnboundTransitionMessages();
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
// Add the navigation parameters in the queue
|
|
536
|
+
this.navigationInfoQueue.push(oNavigationInfo);
|
|
537
|
+
|
|
538
|
+
if (sTargetRoute && oRouteParametersPromise) {
|
|
539
|
+
return oRouteParametersPromise.then((oRouteParameters: any) => {
|
|
540
|
+
oRouteParameters.bIsStickyMode = bIsStickyMode;
|
|
541
|
+
this.oRouter.navTo(sTargetRoute, oRouteParameters);
|
|
542
|
+
return Promise.resolve();
|
|
543
|
+
});
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
return this.oRouterProxy
|
|
547
|
+
.navToHash(sTargetPath, false, mParameters?.noPreservationCache, mParameters?.bForceFocus, !bIsStickyMode)
|
|
548
|
+
.then((bNavigated: any) => {
|
|
549
|
+
if (!bNavigated) {
|
|
550
|
+
// The navigation did not happen --> remove the navigation parameters from the queue as they shouldn't be used
|
|
551
|
+
this.navigationInfoQueue.pop();
|
|
552
|
+
}
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
/**
|
|
557
|
+
* Navigates to a route.
|
|
558
|
+
*
|
|
559
|
+
* @function
|
|
560
|
+
* @name sap.fe.core.controllerextensions.Routing#navigateToRoute
|
|
561
|
+
* @memberof sap.fe.core.controllerextensions.Routing
|
|
562
|
+
* @static
|
|
563
|
+
* @param {string} [sTargetRouteName] Name of the target route
|
|
564
|
+
* @param {object} [oRouteParameters] Parameters to be used with route to create the target hash
|
|
565
|
+
* @returns {Promise} Promise that is resolved when the navigation is finalized
|
|
566
|
+
*
|
|
567
|
+
* @ui5-restricted
|
|
568
|
+
* @final
|
|
569
|
+
*/
|
|
570
|
+
navigateToRoute(sTargetRouteName: string, oRouteParameters: any | undefined) {
|
|
571
|
+
const sTargetURL = this.oRouter.getURL(sTargetRouteName, oRouteParameters);
|
|
572
|
+
return this.oRouterProxy.navToHash(sTargetURL, undefined, undefined, undefined, !oRouteParameters.bIsStickyMode);
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
/**
|
|
576
|
+
* Checks if one of the current views on the screen is bound to a given context.
|
|
577
|
+
*
|
|
578
|
+
* @param {sap.ui.model.odata.v4.Context} oContext The context
|
|
579
|
+
* @returns {boolean} `true` or `false` if the current state is impacted or not
|
|
580
|
+
*/
|
|
581
|
+
isCurrentStateImpactedBy(oContext: any) {
|
|
582
|
+
const sPath = oContext.getPath();
|
|
583
|
+
|
|
584
|
+
// First, check with the technical path. We have to try it, because for level > 1, we
|
|
585
|
+
// uses technical keys even if Semantic keys are enabled
|
|
586
|
+
if (this.oRouterProxy.isCurrentStateImpactedBy(sPath)) {
|
|
587
|
+
return true;
|
|
588
|
+
} else if (/^[^\(\)]+\([^\(\)]+\)$/.test(sPath)) {
|
|
589
|
+
// If the current path can be semantic (i.e. is like xxx(yyy))
|
|
590
|
+
// check with the semantic path if we can find it
|
|
591
|
+
let sSemanticPath;
|
|
592
|
+
if (this.oLastSemanticMapping && this.oLastSemanticMapping.technicalPath === sPath) {
|
|
593
|
+
// We have already resolved this semantic path
|
|
594
|
+
sSemanticPath = this.oLastSemanticMapping.semanticPath;
|
|
595
|
+
} else {
|
|
596
|
+
sSemanticPath = SemanticKeyHelper.getSemanticPath(oContext);
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
return sSemanticPath != sPath ? this.oRouterProxy.isCurrentStateImpactedBy(sSemanticPath) : false;
|
|
600
|
+
} else {
|
|
601
|
+
return false;
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
_findPathToNavigate(sPath: any): string {
|
|
606
|
+
const regex = new RegExp("/[^/]*$");
|
|
607
|
+
sPath = sPath.replace(regex, "");
|
|
608
|
+
if (this.oRouter.match(sPath) || sPath === "") {
|
|
609
|
+
return sPath;
|
|
610
|
+
} else {
|
|
611
|
+
return this._findPathToNavigate(sPath);
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
_checkContextForSemanticPath(oContext: any) {
|
|
616
|
+
const sPath = oContext.getPath();
|
|
617
|
+
|
|
618
|
+
// First, check if this is a level-1 object (path = /aaa(bbb))
|
|
619
|
+
if (!/^\/[^\(]+\([^\)]+\)$/.test(sPath)) {
|
|
620
|
+
return false;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
// Then check if the entity has semantic keys
|
|
624
|
+
const oMetaModel = oContext.getModel().getMetaModel(),
|
|
625
|
+
sEntitySetName = oMetaModel.getMetaContext(oContext.getPath()).getObject("@sapui.name");
|
|
626
|
+
if (!SemanticKeyHelper.getSemanticKeys(oMetaModel, sEntitySetName)) {
|
|
627
|
+
return false;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
// Then check the entity is draft-enabled
|
|
631
|
+
return ModelHelper.isDraftSupported(oMetaModel, sPath);
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
_getPathFromContext(oContext: any, mParameters: any) {
|
|
635
|
+
let sPath;
|
|
636
|
+
|
|
637
|
+
if (oContext.isA("sap.ui.model.odata.v4.ODataListBinding") && oContext.isRelative()) {
|
|
638
|
+
sPath = oContext.getHeaderContext().getPath();
|
|
639
|
+
} else {
|
|
640
|
+
sPath = oContext.getPath();
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
if (mParameters.updateFCLLevel === -1) {
|
|
644
|
+
// When navigating back from a context, we need to remove the last component of the path
|
|
645
|
+
sPath = this._findPathToNavigate(sPath);
|
|
646
|
+
|
|
647
|
+
// Check if we're navigating back to a semantic path that was previously resolved
|
|
648
|
+
if (this.oLastSemanticMapping && this.oLastSemanticMapping.technicalPath === sPath) {
|
|
649
|
+
sPath = this.oLastSemanticMapping.semanticPath;
|
|
650
|
+
}
|
|
651
|
+
} else if (this._checkContextForSemanticPath(oContext)) {
|
|
652
|
+
// We check if we have to use a semantic path
|
|
653
|
+
const sSemanticPath = SemanticKeyHelper.getSemanticPath(oContext, true);
|
|
654
|
+
|
|
655
|
+
if (!sSemanticPath) {
|
|
656
|
+
// We were not able to build the semantic path --> Use the technical path and
|
|
657
|
+
// clear the previous mapping, otherwise the old mapping is used in EditFlow#updateDocument
|
|
658
|
+
// and it leads to unwanted page reloads
|
|
659
|
+
this.setLastSemanticMapping(undefined);
|
|
660
|
+
} else if (sSemanticPath !== sPath) {
|
|
661
|
+
// Store the mapping technical <-> semantic path to avoid recalculating it later
|
|
662
|
+
// and use the semantic path instead of the technical one
|
|
663
|
+
this.setLastSemanticMapping({ technicalPath: sPath, semanticPath: sSemanticPath });
|
|
664
|
+
sPath = sSemanticPath;
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
// remove extra '/' at the beginning of path
|
|
669
|
+
if (sPath[0] === "/") {
|
|
670
|
+
sPath = sPath.substring(1);
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
return sPath;
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
_calculateLayout(sPath: any, mParameters: any) {
|
|
677
|
+
let FCLLevel = mParameters.FCLLevel;
|
|
678
|
+
if (mParameters.updateFCLLevel) {
|
|
679
|
+
FCLLevel += mParameters.updateFCLLevel;
|
|
680
|
+
if (FCLLevel < 0) {
|
|
681
|
+
FCLLevel = 0;
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
return this.oAppComponent.getRootViewController().calculateLayout(FCLLevel, sPath, mParameters.sLayout);
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
/**
|
|
689
|
+
* Event handler before a route is matched.
|
|
690
|
+
* Displays a busy indicator.
|
|
691
|
+
*
|
|
692
|
+
*/
|
|
693
|
+
_beforeRouteMatched(/*oEvent: Event*/) {
|
|
694
|
+
const bPlaceholderEnabled = new Placeholder().isPlaceholderEnabled();
|
|
695
|
+
if (!bPlaceholderEnabled) {
|
|
696
|
+
const oRootView = this.oAppComponent.getRootControl();
|
|
697
|
+
BusyLocker.lock(oRootView);
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
/**
|
|
702
|
+
* Event handler when a route is matched.
|
|
703
|
+
* Hides the busy indicator and fires its own 'routematched' event.
|
|
704
|
+
*
|
|
705
|
+
* @param {object} oEvent The event
|
|
706
|
+
*/
|
|
707
|
+
_onRouteMatched(oEvent: Event) {
|
|
708
|
+
const oAppStateHandler = this.oAppComponent.getAppStateHandler(),
|
|
709
|
+
oRootView = this.oAppComponent.getRootControl();
|
|
710
|
+
const bPlaceholderEnabled = new Placeholder().isPlaceholderEnabled();
|
|
711
|
+
if (BusyLocker.isLocked(oRootView) && !bPlaceholderEnabled) {
|
|
712
|
+
BusyLocker.unlock(oRootView);
|
|
713
|
+
}
|
|
714
|
+
const mParameters: any = oEvent.getParameters();
|
|
715
|
+
if (this.navigationInfoQueue.length) {
|
|
716
|
+
mParameters.navigationInfo = this.navigationInfoQueue[0];
|
|
717
|
+
this.navigationInfoQueue = this.navigationInfoQueue.slice(1);
|
|
718
|
+
} else {
|
|
719
|
+
mParameters.navigationInfo = {};
|
|
720
|
+
}
|
|
721
|
+
if (oAppStateHandler.checkIfRouteChangedByIApp()) {
|
|
722
|
+
mParameters.navigationInfo.bReasonIsIappState = true;
|
|
723
|
+
oAppStateHandler.resetRouteChangedByIApp();
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
this.sCurrentRouteName = oEvent.getParameter("name");
|
|
727
|
+
this.sCurrentRoutePattern = mParameters.config.pattern;
|
|
728
|
+
this.aCurrentViews = oEvent.getParameter("views");
|
|
729
|
+
|
|
730
|
+
mParameters.routeInformation = this._getRouteInformation(this.sCurrentRouteName);
|
|
731
|
+
mParameters.routePattern = this.sCurrentRoutePattern;
|
|
732
|
+
|
|
733
|
+
this._fireRouteMatchEvents(mParameters);
|
|
734
|
+
|
|
735
|
+
// Check if current hash has been set by the routerProxy.navToHash function
|
|
736
|
+
// If not, rebuild history properly (both in the browser and the RouterProxy)
|
|
737
|
+
if (!history.state || history.state.feLevel === undefined) {
|
|
738
|
+
this.oRouterProxy
|
|
739
|
+
.restoreHistory()
|
|
740
|
+
.then(() => {
|
|
741
|
+
this.oRouterProxy.resolveRouteMatch();
|
|
742
|
+
})
|
|
743
|
+
.catch(function(oError: any) {
|
|
744
|
+
Log.error("Error while restoring history", oError);
|
|
745
|
+
});
|
|
746
|
+
} else {
|
|
747
|
+
this.oRouterProxy.resolveRouteMatch();
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
attachRouteMatched(oData: any, fnFunction: any, oListener: any) {
|
|
752
|
+
this.eventProvider.attachEvent("routeMatched", oData, fnFunction, oListener);
|
|
753
|
+
}
|
|
754
|
+
detachRouteMatched(fnFunction: any, oListener: any) {
|
|
755
|
+
this.eventProvider.detachEvent("routeMatched", fnFunction, oListener);
|
|
756
|
+
}
|
|
757
|
+
attachAfterRouteMatched(oData: any, fnFunction: any, oListener: any) {
|
|
758
|
+
this.eventProvider.attachEvent("afterRouteMatched", oData, fnFunction, oListener);
|
|
759
|
+
}
|
|
760
|
+
detachAfterRouteMatched(fnFunction: any, oListener: any) {
|
|
761
|
+
this.eventProvider.detachEvent("afterRouteMatched", fnFunction, oListener);
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
getRouteFromHash(oRouter: any, oAppComponent: any) {
|
|
765
|
+
const sHash = oRouter.getHashChanger().hash;
|
|
766
|
+
const oRouteInfo = oRouter.getRouteInfoByHash(sHash);
|
|
767
|
+
return oAppComponent
|
|
768
|
+
.getMetadata()
|
|
769
|
+
.getManifestEntry("/sap.ui5/routing/routes")
|
|
770
|
+
.filter(function(oRoute: any) {
|
|
771
|
+
return oRoute.name === oRouteInfo.name;
|
|
772
|
+
})[0];
|
|
773
|
+
}
|
|
774
|
+
getTargetsFromRoute(oRoute: any) {
|
|
775
|
+
const oTarget = oRoute.target;
|
|
776
|
+
if (typeof oTarget === "string") {
|
|
777
|
+
return [this._mTargets[oTarget]];
|
|
778
|
+
} else {
|
|
779
|
+
const aTarget: any[] = [];
|
|
780
|
+
oTarget.forEach((sTarget: any) => {
|
|
781
|
+
aTarget.push(this._mTargets[sTarget]);
|
|
782
|
+
});
|
|
783
|
+
return aTarget;
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
initializeRouting() {
|
|
788
|
+
// Attach router handlers
|
|
789
|
+
this._fnOnRouteMatched = this._onRouteMatched.bind(this);
|
|
790
|
+
this.oRouter.attachRouteMatched(this._fnOnRouteMatched, this);
|
|
791
|
+
const bPlaceholderEnabled = new Placeholder().isPlaceholderEnabled();
|
|
792
|
+
if (!bPlaceholderEnabled) {
|
|
793
|
+
this.oRouter.attachBeforeRouteMatched(this._beforeRouteMatched.bind(this));
|
|
794
|
+
}
|
|
795
|
+
// Reset internal state
|
|
796
|
+
this.navigationInfoQueue = [];
|
|
797
|
+
EditState.resetEditState();
|
|
798
|
+
this.bExitOnNavigateBackToRoot = !this.oRouter.match("");
|
|
799
|
+
|
|
800
|
+
const bIsIappState =
|
|
801
|
+
this.oRouter
|
|
802
|
+
.getHashChanger()
|
|
803
|
+
.getHash()
|
|
804
|
+
.indexOf("sap-iapp-state") !== -1;
|
|
805
|
+
const initPromise = this.oAppComponent
|
|
806
|
+
.getStartupParameters()
|
|
807
|
+
.then((oStartupParameters: any) => {
|
|
808
|
+
const bHasStartUpParameters = oStartupParameters !== undefined && Object.keys(oStartupParameters).length !== 0;
|
|
809
|
+
const sHash = this.oRouter.getHashChanger().getHash();
|
|
810
|
+
// Manage startup parameters (in case of no iapp-state)
|
|
811
|
+
if (!bIsIappState && bHasStartUpParameters && !sHash) {
|
|
812
|
+
if (oStartupParameters.preferredMode && oStartupParameters.preferredMode[0].indexOf("create") !== -1) {
|
|
813
|
+
// Create mode
|
|
814
|
+
return this._manageCreateStartup(oStartupParameters);
|
|
815
|
+
} else {
|
|
816
|
+
// Deep link
|
|
817
|
+
return this._manageDeepLinkStartup(oStartupParameters);
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
})
|
|
821
|
+
.catch(function(oError: any) {
|
|
822
|
+
Log.error("Error during routing initialization", oError);
|
|
823
|
+
});
|
|
824
|
+
|
|
825
|
+
return initPromise;
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
getDefaultCreateHash(oStartupParameters: any) {
|
|
829
|
+
return AppStartupHelper.getDefaultCreateHash(oStartupParameters, this.getContextPath(), this.oRouter);
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
_manageCreateStartup(oStartupParameters: any) {
|
|
833
|
+
return AppStartupHelper.getCreateStartupHash(oStartupParameters, this.getContextPath(), this.oRouter, this.oMetaModel).then(
|
|
834
|
+
(sNewHash: any) => {
|
|
835
|
+
if (sNewHash) {
|
|
836
|
+
(this.oRouter.getHashChanger().replaceHash as any)(sNewHash);
|
|
837
|
+
this.bExitOnNavigateBackToRoot = true;
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
);
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
_manageDeepLinkStartup(oStartupParameters: any) {
|
|
844
|
+
return AppStartupHelper.getDeepLinkStartupHash(
|
|
845
|
+
this.oAppComponent.getManifest()["sap.ui5"].routing,
|
|
846
|
+
oStartupParameters,
|
|
847
|
+
this.oModel
|
|
848
|
+
).then((oDeepLink: any) => {
|
|
849
|
+
let sHash;
|
|
850
|
+
if (oDeepLink.context) {
|
|
851
|
+
const sTechnicalPath = oDeepLink.context.getPath(),
|
|
852
|
+
sSemanticPath = SemanticKeyHelper.getSemanticPath(oDeepLink.context);
|
|
853
|
+
|
|
854
|
+
if (sSemanticPath !== sTechnicalPath) {
|
|
855
|
+
// Store the mapping technical <-> semantic path to avoid recalculating it later
|
|
856
|
+
// and use the semantic path instead of the technical one
|
|
857
|
+
this.setLastSemanticMapping({ technicalPath: sTechnicalPath, semanticPath: sSemanticPath });
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
sHash = sSemanticPath.substring(1); // To remove the leading '/'
|
|
861
|
+
} else if (oDeepLink.hash) {
|
|
862
|
+
sHash = oDeepLink.hash;
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
if (sHash) {
|
|
866
|
+
//Replace the hash with newly created hash
|
|
867
|
+
(this.oRouter.getHashChanger().replaceHash as any)(sHash);
|
|
868
|
+
}
|
|
869
|
+
});
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
getOutbounds() {
|
|
873
|
+
return this.outbounds;
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
/**
|
|
877
|
+
* Gets the name of the Draft root entity set or the sticky-enabled entity set.
|
|
878
|
+
*
|
|
879
|
+
* @returns {string} The name of the root EntitySet
|
|
880
|
+
*
|
|
881
|
+
* @ui5-restricted
|
|
882
|
+
*/
|
|
883
|
+
getContextPath() {
|
|
884
|
+
return this.sContextPath;
|
|
885
|
+
}
|
|
886
|
+
getInterface(): any {
|
|
887
|
+
return this;
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
class RoutingServiceFactory extends ServiceFactory<RoutingServiceSettings> {
|
|
892
|
+
createInstance(oServiceContext: ServiceContext<RoutingServiceSettings>) {
|
|
893
|
+
const oRoutingService = new RoutingService(oServiceContext);
|
|
894
|
+
return oRoutingService.initPromise;
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
export default RoutingServiceFactory;
|