@ui5/webcomponents-base 0.0.0-306572ffa → 0.0.0-3236d56be
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/.eslintignore +3 -0
- package/CHANGELOG.md +308 -0
- package/README.md +26 -2
- package/bundle.esm.js +13 -11
- package/dist/AssetRegistry.js +8 -7
- package/dist/Boot.js +59 -0
- package/dist/CSP.js +59 -0
- package/dist/CustomElementsRegistry.js +60 -4
- package/dist/CustomElementsScope.js +20 -98
- package/dist/CustomElementsScopeUtils.js +108 -0
- package/dist/DOMObserver.js +65 -0
- package/dist/Device.js +71 -773
- package/dist/EventProvider.js +30 -26
- package/dist/FeaturesRegistry.js +4 -1
- package/dist/FontFace.js +19 -62
- package/dist/InitialConfiguration.js +43 -8
- package/dist/Keys.js +107 -0
- package/dist/ManagedStyles.js +83 -0
- package/dist/MediaRange.js +109 -0
- package/{src/util/parseProperties.js → dist/PropertiesFileFormat.js} +6 -0
- package/dist/Render.js +175 -0
- package/dist/RenderScheduler.js +16 -138
- package/dist/Runtimes.js +107 -0
- package/dist/StaticArea.js +1 -29
- package/dist/StaticAreaItem.js +58 -65
- package/dist/SystemCSSVars.js +4 -25
- package/dist/Theming.js +2 -1
- package/dist/UI5Element.js +319 -364
- package/dist/UI5ElementMetadata.js +132 -17
- package/dist/asset-registries/Icons.js +117 -13
- package/dist/asset-registries/Illustrations.js +30 -0
- package/dist/asset-registries/LocaleData.js +105 -66
- package/dist/asset-registries/Themes.js +23 -32
- package/dist/asset-registries/i18n.js +62 -48
- package/dist/assets-meta/IconCollectionsAlias.js +18 -0
- package/dist/config/Icons.js +52 -0
- package/dist/config/Language.js +33 -6
- package/dist/config/Theme.js +25 -0
- package/dist/css/BusyIndicator.css +76 -0
- package/dist/css/FontFace.css +75 -0
- package/dist/css/OverrideFontFace.css +32 -0
- package/dist/css/SystemCSSVars.css +17 -0
- package/dist/delegate/ItemNavigation.js +239 -170
- package/dist/delegate/ResizeHandler.js +78 -37
- package/dist/delegate/ScrollEnablement.js +49 -20
- package/dist/features/F6Navigation.js +108 -0
- package/dist/features/OpenUI5Enablement.js +119 -0
- package/dist/features/OpenUI5Support.js +41 -6
- package/dist/generated/AssetParameters.js +1 -1
- package/dist/generated/VersionInfo.js +10 -0
- package/dist/generated/css/BusyIndicator.css.js +5 -0
- package/dist/generated/css/FontFace.css.js +5 -0
- package/dist/generated/css/OverrideFontFace.css.js +5 -0
- package/dist/generated/css/SystemCSSVars.css.js +5 -0
- package/dist/i18nBundle.js +38 -4
- package/dist/isLegacyBrowser.js +3 -0
- package/dist/locale/applyDirection.js +6 -4
- package/dist/locale/directionChange.js +32 -0
- package/dist/locale/getEffectiveDir.js +28 -0
- package/dist/locale/getLocale.js +12 -2
- package/dist/locale/languageChange.js +1 -1
- package/dist/renderer/LitRenderer.js +39 -18
- package/dist/renderer/directives/style-map.js +72 -0
- package/dist/renderer/executeTemplate.js +19 -2
- package/dist/resources/bundle.esm.js +15 -112
- package/dist/resources/bundle.esm.js.map +1 -1
- package/dist/sap/base/Log.js +241 -0
- package/dist/sap/base/assert.js +8 -0
- package/dist/sap/base/security/URLListValidator.js +148 -0
- package/dist/sap/base/security/encodeCSS.js +14 -0
- package/dist/sap/base/security/encodeXML.js +23 -0
- package/dist/sap/base/security/sanitizeHTML.js +16 -0
- package/dist/sap/base/strings/toHex.js +8 -0
- package/dist/sap/base/util/now.js +7 -0
- package/dist/sap/ui/thirdparty/caja-html-sanitizer.js +3585 -0
- package/dist/test-resources/assets/Themes.js +8 -8
- package/dist/test-resources/elements/Parent.js +6 -2
- package/dist/test-resources/elements/WithStaticArea.js +2 -1
- package/dist/test-resources/pages/AllTestElements.html +3 -3
- package/dist/test-resources/pages/Configuration.html +0 -2
- package/dist/test-resources/pages/ConfigurationScript.html +0 -2
- package/dist/test-resources/pages/i18n.html +2 -9
- package/dist/test-resources/specs/ConfigurationChange.spec.js +10 -8
- package/dist/test-resources/specs/ConfigurationScript.spec.js +25 -23
- package/dist/test-resources/specs/ConfigurationURL.spec.js +65 -17
- package/dist/test-resources/specs/CustomTheme.spec.js +9 -7
- package/dist/test-resources/specs/EventProvider.spec.js +63 -0
- package/dist/test-resources/specs/StaticArea.spec.js +56 -12
- package/dist/test-resources/specs/Theming.spec.js +12 -10
- package/dist/test-resources/specs/UI5ElementInvalidation.js +54 -70
- package/dist/test-resources/specs/UI5ElementLifecycle.js +17 -15
- package/dist/test-resources/specs/UI5ElementListenForChildPropChanges.spec.js +25 -51
- package/dist/test-resources/specs/UI5ElementMetadataExt.js +9 -7
- package/dist/test-resources/specs/UI5ElementPropertyValidation.js +7 -5
- package/dist/test-resources/specs/UI5ElementPropsAndAttrs.spec.js +37 -35
- package/dist/test-resources/specs/UI5ElementShadowDOM.js +10 -8
- package/dist/test-resources/specs/UI5ElementSlots.js +10 -8
- package/dist/theming/CustomStyle.js +25 -5
- package/dist/theming/ThemeLoaded.js +22 -0
- package/dist/theming/applyTheme.js +13 -15
- package/dist/theming/getConstructableStyle.js +7 -6
- package/dist/theming/getEffectiveLinksHrefs.js +27 -0
- package/dist/theming/getEffectiveStyle.js +24 -8
- package/dist/theming/getStylesString.js +4 -2
- package/dist/theming/getThemeDesignerTheme.js +25 -5
- package/dist/theming/preloadLinks.js +23 -0
- package/dist/types/CSSColor.js +9 -0
- package/dist/types/CalendarType.js +1 -1
- package/dist/types/DataType.js +13 -1
- package/dist/types/Float.js +4 -0
- package/dist/types/Integer.js +4 -0
- package/dist/types/InvisibleMessageMode.js +30 -0
- package/dist/types/ItemNavigationBehavior.js +2 -7
- package/dist/types/PopupState.js +1 -1
- package/dist/types/ValueState.js +1 -1
- package/dist/updateShadowRoot.js +30 -0
- package/dist/util/AriaLabelHelper.js +41 -0
- package/dist/util/Caret.js +45 -0
- package/dist/util/ColorConversion.js +370 -0
- package/dist/util/FocusableElements.js +22 -9
- package/dist/util/HTMLSanitizer.js +7 -0
- package/dist/util/InvisibleMessage.js +60 -0
- package/dist/util/PopupUtils.js +93 -0
- package/dist/util/SlotsHelper.js +43 -0
- package/dist/util/TabbableElements.js +5 -1
- package/dist/util/arraysAreEqual.js +15 -0
- package/dist/util/clamp.js +12 -0
- package/dist/util/createLinkInHead.js +19 -0
- package/dist/util/debounce.js +17 -0
- package/dist/util/escapeRegex.js +10 -0
- package/dist/util/generateHighlightedMarkup.js +42 -0
- package/dist/util/getActiveElement.js +11 -0
- package/dist/util/getClassCopy.js +10 -0
- package/dist/util/getEffectiveContentDensity.js +5 -0
- package/dist/util/getNormalizedTarget.js +16 -0
- package/dist/util/isElementInView.js +15 -0
- package/dist/util/isNodeHidden.js +1 -1
- package/dist/util/isNodeTabbable.js +4 -4
- package/dist/util/isValidPropertyName.js +4 -2
- package/index.js +1 -1
- package/lib/generate-asset-parameters/index.js +8 -5
- package/lib/generate-styles/index.js +26 -0
- package/lib/generate-version-info/index.js +32 -0
- package/package-scripts.js +30 -19
- package/package.json +19 -13
- package/src/AssetRegistry.js +8 -7
- package/src/Boot.js +59 -0
- package/src/CSP.js +59 -0
- package/src/CustomElementsRegistry.js +60 -4
- package/src/CustomElementsScope.js +20 -98
- package/src/CustomElementsScopeUtils.js +108 -0
- package/src/DOMObserver.js +65 -0
- package/src/Device.js +71 -773
- package/src/EventProvider.js +30 -26
- package/src/FeaturesRegistry.js +4 -1
- package/src/FontFace.js +19 -62
- package/src/InitialConfiguration.js +43 -8
- package/src/Keys.js +107 -0
- package/src/ManagedStyles.js +83 -0
- package/src/MediaRange.js +109 -0
- package/{dist/util/parseProperties.js → src/PropertiesFileFormat.js} +6 -0
- package/src/Render.js +175 -0
- package/src/RenderScheduler.js +16 -138
- package/src/Runtimes.js +107 -0
- package/src/StaticArea.js +1 -29
- package/src/StaticAreaItem.js +58 -65
- package/src/SystemCSSVars.js +4 -25
- package/src/Theming.js +2 -1
- package/src/UI5Element.js +319 -364
- package/src/UI5ElementMetadata.js +132 -17
- package/src/asset-registries/Icons.js +117 -13
- package/src/asset-registries/Illustrations.js +30 -0
- package/src/asset-registries/LocaleData.js +105 -66
- package/src/asset-registries/Themes.js +23 -32
- package/src/asset-registries/i18n.js +62 -48
- package/src/assets-meta/IconCollectionsAlias.js +18 -0
- package/src/config/Icons.js +52 -0
- package/src/config/Language.js +33 -6
- package/src/config/Theme.js +25 -0
- package/src/css/BusyIndicator.css +76 -0
- package/src/css/FontFace.css +75 -0
- package/src/css/OverrideFontFace.css +32 -0
- package/src/css/SystemCSSVars.css +17 -0
- package/src/delegate/ItemNavigation.js +239 -170
- package/src/delegate/ResizeHandler.js +78 -37
- package/src/delegate/ScrollEnablement.js +49 -20
- package/src/features/F6Navigation.js +108 -0
- package/src/features/OpenUI5Enablement.js +119 -0
- package/src/features/OpenUI5Support.js +41 -6
- package/src/i18nBundle.js +38 -4
- package/src/isLegacyBrowser.js +3 -0
- package/src/locale/applyDirection.js +6 -4
- package/src/locale/directionChange.js +32 -0
- package/src/locale/getEffectiveDir.js +28 -0
- package/src/locale/getLocale.js +12 -2
- package/src/locale/languageChange.js +1 -1
- package/src/renderer/LitRenderer.js +39 -18
- package/src/renderer/directives/style-map.js +72 -0
- package/src/renderer/executeTemplate.js +19 -2
- package/src/theming/CustomStyle.js +25 -5
- package/src/theming/ThemeLoaded.js +22 -0
- package/src/theming/applyTheme.js +13 -15
- package/src/theming/getConstructableStyle.js +7 -6
- package/src/theming/getEffectiveLinksHrefs.js +27 -0
- package/src/theming/getEffectiveStyle.js +24 -8
- package/src/theming/getStylesString.js +4 -2
- package/src/theming/getThemeDesignerTheme.js +25 -5
- package/src/theming/preloadLinks.js +23 -0
- package/src/types/CSSColor.js +9 -0
- package/src/types/CalendarType.js +1 -1
- package/src/types/DataType.js +13 -1
- package/src/types/Float.js +4 -0
- package/src/types/Integer.js +4 -0
- package/src/types/InvisibleMessageMode.js +30 -0
- package/src/types/ItemNavigationBehavior.js +2 -7
- package/src/types/PopupState.js +1 -1
- package/src/types/ValueState.js +1 -1
- package/src/updateShadowRoot.js +30 -0
- package/src/util/AriaLabelHelper.js +41 -0
- package/src/util/Caret.js +45 -0
- package/src/util/ColorConversion.js +370 -0
- package/src/util/FocusableElements.js +22 -9
- package/src/util/HTMLSanitizer.js +7 -0
- package/src/util/InvisibleMessage.js +60 -0
- package/src/util/PopupUtils.js +93 -0
- package/src/util/SlotsHelper.js +43 -0
- package/src/util/TabbableElements.js +5 -1
- package/src/util/arraysAreEqual.js +15 -0
- package/src/util/clamp.js +12 -0
- package/src/util/createLinkInHead.js +19 -0
- package/src/util/debounce.js +17 -0
- package/src/util/escapeRegex.js +10 -0
- package/src/util/generateHighlightedMarkup.js +42 -0
- package/src/util/getActiveElement.js +11 -0
- package/src/util/getClassCopy.js +10 -0
- package/src/util/getEffectiveContentDensity.js +5 -0
- package/src/util/getNormalizedTarget.js +16 -0
- package/src/util/isElementInView.js +15 -0
- package/src/util/isNodeHidden.js +1 -1
- package/src/util/isNodeTabbable.js +4 -4
- package/src/util/isValidPropertyName.js +4 -2
- package/used-modules.txt +10 -0
- package/bundle.es5.js +0 -28
- package/dist/SVGIconRegistry.js +0 -62
- package/dist/boot.js +0 -34
- package/dist/compatibility/DOMObserver.js +0 -62
- package/dist/compatibility/patchNodeValue.js +0 -24
- package/dist/compatibility/whenPolyfillLoaded.js +0 -26
- package/dist/delegate/CustomResize.js +0 -78
- package/dist/delegate/NativeResize.js +0 -44
- package/dist/features/PropertiesFormatSupport.js +0 -8
- package/dist/features/browsersupport/Edge.js +0 -6
- package/dist/features/browsersupport/IE11.js +0 -41
- package/dist/features/browsersupport/IE11WithWebComponentsPolyfill.js +0 -5
- package/dist/renderer/ifDefined.js +0 -21
- package/dist/renderer/scopeHTML.js +0 -32
- package/dist/resources/bundle.es5.js +0 -212
- package/dist/resources/bundle.es5.js.map +0 -1
- package/dist/theming/CSSVarsPonyfill.js +0 -24
- package/dist/theming/adaptCSSForIE.js +0 -95
- package/dist/theming/createComponentStyleTag.js +0 -55
- package/dist/theming/createThemePropertiesStyleTag.js +0 -20
- package/dist/thirdparty/Array.from.js +0 -16
- package/dist/thirdparty/Array.prototype.fill.js +0 -44
- package/dist/thirdparty/Array.prototype.find.js +0 -46
- package/dist/thirdparty/Array.prototype.includes.js +0 -51
- package/dist/thirdparty/Element.prototype.closest.js +0 -11
- package/dist/thirdparty/Element.prototype.matches.js +0 -6
- package/dist/thirdparty/Map.prototype.keys.js +0 -9
- package/dist/thirdparty/Number.isInteger.js +0 -5
- package/dist/thirdparty/Number.isNaN.js +0 -1
- package/dist/thirdparty/Number.parseInt.js +0 -1
- package/dist/thirdparty/Object.assign.js +0 -31
- package/dist/thirdparty/Object.entries.js +0 -11
- package/dist/thirdparty/Symbol.js +0 -2
- package/dist/thirdparty/WeakSet.js +0 -27
- package/dist/thirdparty/es6-string-methods.js +0 -182
- package/dist/thirdparty/events-polyfills.js +0 -89
- package/dist/thirdparty/fetch.js +0 -1
- package/dist/thirdparty/template.js +0 -600
- package/dist/thirdparty/webcomponents-sd-ce-pf.js +0 -318
- package/dist/util/getEffectiveAriaLabelText.js +0 -28
- package/dist/util/isSlot.js +0 -3
- package/dist/webcomponentsjs/LICENSE.md +0 -19
- package/dist/webcomponentsjs/README.md +0 -229
- package/dist/webcomponentsjs/bundles/webcomponents-ce.js +0 -63
- package/dist/webcomponentsjs/bundles/webcomponents-ce.js.map +0 -1
- package/dist/webcomponentsjs/bundles/webcomponents-sd-ce-pf.js +0 -297
- package/dist/webcomponentsjs/bundles/webcomponents-sd-ce-pf.js.map +0 -1
- package/dist/webcomponentsjs/bundles/webcomponents-sd-ce.js +0 -208
- package/dist/webcomponentsjs/bundles/webcomponents-sd-ce.js.map +0 -1
- package/dist/webcomponentsjs/bundles/webcomponents-sd.js +0 -166
- package/dist/webcomponentsjs/bundles/webcomponents-sd.js.map +0 -1
- package/dist/webcomponentsjs/custom-elements-es5-adapter.js +0 -15
- package/dist/webcomponentsjs/package.json +0 -46
- package/dist/webcomponentsjs/src/entrypoints/custom-elements-es5-adapter-index.js +0 -16
- package/dist/webcomponentsjs/src/entrypoints/webcomponents-bundle-index.js +0 -53
- package/dist/webcomponentsjs/src/entrypoints/webcomponents-ce-index.js +0 -17
- package/dist/webcomponentsjs/src/entrypoints/webcomponents-sd-ce-index.js +0 -19
- package/dist/webcomponentsjs/src/entrypoints/webcomponents-sd-ce-pf-index.js +0 -28
- package/dist/webcomponentsjs/src/entrypoints/webcomponents-sd-index.js +0 -18
- package/dist/webcomponentsjs/webcomponents-bundle.js +0 -298
- package/dist/webcomponentsjs/webcomponents-bundle.js.map +0 -1
- package/dist/webcomponentsjs/webcomponents-loader.js +0 -185
- package/src/SVGIconRegistry.js +0 -62
- package/src/boot.js +0 -34
- package/src/compatibility/DOMObserver.js +0 -62
- package/src/compatibility/patchNodeValue.js +0 -24
- package/src/compatibility/whenPolyfillLoaded.js +0 -26
- package/src/delegate/CustomResize.js +0 -78
- package/src/delegate/NativeResize.js +0 -44
- package/src/features/PropertiesFormatSupport.js +0 -8
- package/src/features/browsersupport/Edge.js +0 -6
- package/src/features/browsersupport/IE11.js +0 -41
- package/src/features/browsersupport/IE11WithWebComponentsPolyfill.js +0 -5
- package/src/renderer/ifDefined.js +0 -21
- package/src/renderer/scopeHTML.js +0 -32
- package/src/theming/CSSVarsPonyfill.js +0 -24
- package/src/theming/adaptCSSForIE.js +0 -95
- package/src/theming/createComponentStyleTag.js +0 -55
- package/src/theming/createThemePropertiesStyleTag.js +0 -20
- package/src/thirdparty/Array.from.js +0 -16
- package/src/thirdparty/Array.prototype.fill.js +0 -44
- package/src/thirdparty/Array.prototype.find.js +0 -46
- package/src/thirdparty/Array.prototype.includes.js +0 -51
- package/src/thirdparty/Element.prototype.closest.js +0 -11
- package/src/thirdparty/Element.prototype.matches.js +0 -6
- package/src/thirdparty/Map.prototype.keys.js +0 -9
- package/src/thirdparty/Number.isInteger.js +0 -5
- package/src/thirdparty/Number.isNaN.js +0 -1
- package/src/thirdparty/Number.parseInt.js +0 -1
- package/src/thirdparty/Object.assign.js +0 -31
- package/src/thirdparty/Object.entries.js +0 -11
- package/src/thirdparty/Symbol.js +0 -2
- package/src/thirdparty/WeakSet.js +0 -27
- package/src/thirdparty/es6-string-methods.js +0 -182
- package/src/thirdparty/events-polyfills.js +0 -89
- package/src/thirdparty/fetch.js +0 -1
- package/src/thirdparty/template.js +0 -600
- package/src/thirdparty/webcomponents-sd-ce-pf.js +0 -318
- package/src/util/getEffectiveAriaLabelText.js +0 -28
- package/src/util/isSlot.js +0 -3
package/dist/UI5Element.js
CHANGED
|
@@ -1,36 +1,52 @@
|
|
|
1
1
|
import merge from "./thirdparty/merge.js";
|
|
2
|
-
import boot from "./
|
|
2
|
+
import { boot } from "./Boot.js";
|
|
3
3
|
import UI5ElementMetadata from "./UI5ElementMetadata.js";
|
|
4
|
-
import
|
|
4
|
+
import EventProvider from "./EventProvider.js";
|
|
5
|
+
import getSingletonElementInstance from "./util/getSingletonElementInstance.js";
|
|
5
6
|
import StaticAreaItem from "./StaticAreaItem.js";
|
|
6
|
-
import
|
|
7
|
+
import updateShadowRoot from "./updateShadowRoot.js";
|
|
8
|
+
import { renderDeferred, renderImmediately, cancelRender } from "./Render.js";
|
|
7
9
|
import { registerTag, isTagRegistered, recordTagRegistrationFailure } from "./CustomElementsRegistry.js";
|
|
8
|
-
import
|
|
10
|
+
import { observeDOMNode, unobserveDOMNode } from "./DOMObserver.js";
|
|
9
11
|
import { skipOriginalEvent } from "./config/NoConflict.js";
|
|
10
|
-
import
|
|
11
|
-
import
|
|
12
|
-
import createComponentStyleTag from "./theming/createComponentStyleTag.js";
|
|
13
|
-
import getEffectiveStyle from "./theming/getEffectiveStyle.js";
|
|
14
|
-
import Integer from "./types/Integer.js";
|
|
15
|
-
import Float from "./types/Float.js";
|
|
12
|
+
import getEffectiveDir from "./locale/getEffectiveDir.js";
|
|
13
|
+
import DataType from "./types/DataType.js";
|
|
16
14
|
import { kebabToCamelCase, camelToKebabCase } from "./util/StringHelper.js";
|
|
17
15
|
import isValidPropertyName from "./util/isValidPropertyName.js";
|
|
18
|
-
import
|
|
16
|
+
import isDescendantOf from "./util/isDescendantOf.js";
|
|
17
|
+
import { isSlot, getSlotName, getSlottedElementsList } from "./util/SlotsHelper.js";
|
|
18
|
+
import arraysAreEqual from "./util/arraysAreEqual.js";
|
|
19
|
+
import getClassCopy from "./util/getClassCopy.js";
|
|
19
20
|
import { markAsRtlAware } from "./locale/RTLAwareRegistry.js";
|
|
20
|
-
|
|
21
|
-
const metadata = {
|
|
22
|
-
events: {
|
|
23
|
-
"_property-change": {},
|
|
24
|
-
},
|
|
25
|
-
};
|
|
21
|
+
import preloadLinks from "./theming/preloadLinks.js";
|
|
26
22
|
|
|
27
23
|
let autoId = 0;
|
|
28
24
|
|
|
29
25
|
const elementTimeouts = new Map();
|
|
30
26
|
const uniqueDependenciesCache = new Map();
|
|
31
27
|
|
|
32
|
-
|
|
33
|
-
|
|
28
|
+
/**
|
|
29
|
+
* Triggers re-rendering of a UI5Element instance due to state change.
|
|
30
|
+
*
|
|
31
|
+
* @param changeInfo An object with information about the change that caused invalidation.
|
|
32
|
+
* @private
|
|
33
|
+
*/
|
|
34
|
+
function _invalidate(changeInfo) {
|
|
35
|
+
// Invalidation should be suppressed: 1) before the component is rendered for the first time 2) and during the execution of onBeforeRendering
|
|
36
|
+
// This is necessary not only as an optimization, but also to avoid infinite loops on invalidation between children and parents (when invalidateOnChildChange is used)
|
|
37
|
+
if (this._suppressInvalidation) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Call the onInvalidation hook
|
|
42
|
+
this.onInvalidation(changeInfo);
|
|
43
|
+
|
|
44
|
+
this._changedState.push(changeInfo);
|
|
45
|
+
renderDeferred(this);
|
|
46
|
+
this._eventProvider.fireEvent("invalidate", { ...changeInfo, target: this });
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
let metadata = {};
|
|
34
50
|
|
|
35
51
|
/**
|
|
36
52
|
* Base class for all UI5 Web Components
|
|
@@ -45,22 +61,26 @@ const GLOBAL_DIR_CSS_VAR = "--_ui5_dir";
|
|
|
45
61
|
class UI5Element extends HTMLElement {
|
|
46
62
|
constructor() {
|
|
47
63
|
super();
|
|
48
|
-
this._initializeState();
|
|
49
|
-
this._upgradeAllProperties();
|
|
50
|
-
this._initializeContainers();
|
|
51
|
-
this._upToDate = false;
|
|
52
|
-
this._inDOM = false;
|
|
53
|
-
this._fullyConnected = false;
|
|
54
64
|
|
|
65
|
+
this._changedState = []; // Filled on each invalidation, cleared on re-render (used for debugging)
|
|
66
|
+
this._suppressInvalidation = true; // A flag telling whether all invalidations should be ignored. Initialized with "true" because a UI5Element can not be invalidated until it is rendered for the first time
|
|
67
|
+
this._inDOM = false; // A flag telling whether the UI5Element is currently in the DOM tree of the document or not
|
|
68
|
+
this._fullyConnected = false; // A flag telling whether the UI5Element's onEnterDOM hook was called (since it's possible to have the element removed from DOM before that)
|
|
69
|
+
this._childChangeListeners = new Map(); // used to store lazy listeners per slot for the child change event of every child inside that slot
|
|
70
|
+
this._slotChangeListeners = new Map(); // used to store lazy listeners per slot for the slotchange event of all slot children inside that slot
|
|
71
|
+
this._eventProvider = new EventProvider(); // used by parent components for listening to changes to child components
|
|
55
72
|
let deferredResolve;
|
|
56
73
|
this._domRefReadyPromise = new Promise(resolve => {
|
|
57
74
|
deferredResolve = resolve;
|
|
58
75
|
});
|
|
59
76
|
this._domRefReadyPromise._deferredResolve = deferredResolve;
|
|
60
77
|
|
|
61
|
-
this.
|
|
62
|
-
this.
|
|
63
|
-
|
|
78
|
+
this._initializeState();
|
|
79
|
+
this._upgradeAllProperties();
|
|
80
|
+
|
|
81
|
+
if (this.constructor._needsShadowDOM()) {
|
|
82
|
+
this.attachShadow({ mode: "open" });
|
|
83
|
+
}
|
|
64
84
|
}
|
|
65
85
|
|
|
66
86
|
/**
|
|
@@ -77,32 +97,16 @@ class UI5Element extends HTMLElement {
|
|
|
77
97
|
return this.__id;
|
|
78
98
|
}
|
|
79
99
|
|
|
80
|
-
/**
|
|
81
|
-
* @private
|
|
82
|
-
*/
|
|
83
|
-
_initializeContainers() {
|
|
84
|
-
const needsShadowDOM = this.constructor._needsShadowDOM();
|
|
85
|
-
const needsStaticArea = this.constructor._needsStaticArea();
|
|
86
|
-
|
|
87
|
-
// Init Shadow Root
|
|
88
|
-
if (needsShadowDOM) {
|
|
89
|
-
this.attachShadow({ mode: "open" });
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// Init StaticAreaItem only if needed
|
|
93
|
-
if (needsStaticArea) {
|
|
94
|
-
this.staticAreaItem = new StaticAreaItem(this);
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
100
|
/**
|
|
99
101
|
* Do not call this method from derivatives of UI5Element, use "onEnterDOM" only
|
|
100
102
|
* @private
|
|
101
103
|
*/
|
|
102
104
|
async connectedCallback() {
|
|
103
105
|
this.setAttribute(this.constructor.getMetadata().getPureTag(), "");
|
|
106
|
+
if (this.constructor.getMetadata().supportsF6FastNavigation()) {
|
|
107
|
+
this.setAttribute("data-sap-ui-fastnavgroup", "true");
|
|
108
|
+
}
|
|
104
109
|
|
|
105
|
-
const needsShadowDOM = this.constructor._needsShadowDOM();
|
|
106
110
|
const slotsAreManaged = this.constructor.getMetadata().slotsAreManaged();
|
|
107
111
|
|
|
108
112
|
this._inDOM = true;
|
|
@@ -113,23 +117,15 @@ class UI5Element extends HTMLElement {
|
|
|
113
117
|
await this._processChildren();
|
|
114
118
|
}
|
|
115
119
|
|
|
116
|
-
//
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
await Promise.resolve();
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
if (!this._inDOM) { // Component removed from DOM while _processChildren was running
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
120
|
+
if (!this._inDOM) { // Component removed from DOM while _processChildren was running
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
125
123
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
this.onEnterDOM();
|
|
132
|
-
}
|
|
124
|
+
renderImmediately(this);
|
|
125
|
+
this._domRefReadyPromise._deferredResolve();
|
|
126
|
+
this._fullyConnected = true;
|
|
127
|
+
if (typeof this.onEnterDOM === "function") {
|
|
128
|
+
this.onEnterDOM();
|
|
133
129
|
}
|
|
134
130
|
}
|
|
135
131
|
|
|
@@ -138,8 +134,6 @@ class UI5Element extends HTMLElement {
|
|
|
138
134
|
* @private
|
|
139
135
|
*/
|
|
140
136
|
disconnectedCallback() {
|
|
141
|
-
const needsShadowDOM = this.constructor._needsShadowDOM();
|
|
142
|
-
const needsStaticArea = this.constructor._needsStaticArea();
|
|
143
137
|
const slotsAreManaged = this.constructor.getMetadata().slotsAreManaged();
|
|
144
138
|
|
|
145
139
|
this._inDOM = false;
|
|
@@ -148,21 +142,18 @@ class UI5Element extends HTMLElement {
|
|
|
148
142
|
this._stopObservingDOMChildren();
|
|
149
143
|
}
|
|
150
144
|
|
|
151
|
-
if (
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
if (typeof this.onExitDOM === "function") {
|
|
155
|
-
this.onExitDOM();
|
|
156
|
-
}
|
|
157
|
-
this._fullyConnected = false;
|
|
145
|
+
if (this._fullyConnected) {
|
|
146
|
+
if (typeof this.onExitDOM === "function") {
|
|
147
|
+
this.onExitDOM();
|
|
158
148
|
}
|
|
149
|
+
this._fullyConnected = false;
|
|
159
150
|
}
|
|
160
151
|
|
|
161
|
-
if (
|
|
162
|
-
this.staticAreaItem.
|
|
152
|
+
if (this.staticAreaItem && this.staticAreaItem.parentElement) {
|
|
153
|
+
this.staticAreaItem.parentElement.removeChild(this.staticAreaItem);
|
|
163
154
|
}
|
|
164
155
|
|
|
165
|
-
|
|
156
|
+
cancelRender(this);
|
|
166
157
|
}
|
|
167
158
|
|
|
168
159
|
/**
|
|
@@ -178,16 +169,16 @@ class UI5Element extends HTMLElement {
|
|
|
178
169
|
const mutationObserverOptions = {
|
|
179
170
|
childList: true,
|
|
180
171
|
subtree: canSlotText,
|
|
181
|
-
characterData:
|
|
172
|
+
characterData: canSlotText,
|
|
182
173
|
};
|
|
183
|
-
|
|
174
|
+
observeDOMNode(this, this._processChildren.bind(this), mutationObserverOptions);
|
|
184
175
|
}
|
|
185
176
|
|
|
186
177
|
/**
|
|
187
178
|
* @private
|
|
188
179
|
*/
|
|
189
180
|
_stopObservingDOMChildren() {
|
|
190
|
-
|
|
181
|
+
unobserveDOMNode(this);
|
|
191
182
|
}
|
|
192
183
|
|
|
193
184
|
/**
|
|
@@ -209,8 +200,14 @@ class UI5Element extends HTMLElement {
|
|
|
209
200
|
const canSlotText = this.constructor.getMetadata().canSlotText();
|
|
210
201
|
const domChildren = Array.from(canSlotText ? this.childNodes : this.children);
|
|
211
202
|
|
|
212
|
-
//
|
|
203
|
+
const slotsCachedContentMap = new Map(); // Store here the content of each slot before the mutation occurred
|
|
204
|
+
const propertyNameToSlotMap = new Map(); // Used for reverse lookup to determine to which slot the property name corresponds
|
|
205
|
+
|
|
206
|
+
// Init the _state object based on the supported slots and store the previous values
|
|
213
207
|
for (const [slotName, slotData] of Object.entries(slotsMap)) { // eslint-disable-line
|
|
208
|
+
const propertyName = slotData.propertyName || slotName;
|
|
209
|
+
propertyNameToSlotMap.set(propertyName, slotName);
|
|
210
|
+
slotsCachedContentMap.set(propertyName, [...this._state[propertyName]]);
|
|
214
211
|
this._clearSlot(slotName, slotData);
|
|
215
212
|
}
|
|
216
213
|
|
|
@@ -219,7 +216,7 @@ class UI5Element extends HTMLElement {
|
|
|
219
216
|
|
|
220
217
|
const allChildrenUpgraded = domChildren.map(async (child, idx) => {
|
|
221
218
|
// Determine the type of the child (mainly by the slot attribute)
|
|
222
|
-
const slotName =
|
|
219
|
+
const slotName = getSlotName(child);
|
|
223
220
|
const slotData = slotsMap[slotName];
|
|
224
221
|
|
|
225
222
|
// Check if the slotName is supported
|
|
@@ -257,16 +254,15 @@ class UI5Element extends HTMLElement {
|
|
|
257
254
|
|
|
258
255
|
child = this.constructor.getMetadata().constructor.validateSlotValue(child, slotData);
|
|
259
256
|
|
|
260
|
-
if (
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
if (child.isUI5Element && slotData.invalidateParent) {
|
|
265
|
-
child._shouldInvalidateParent = true;
|
|
257
|
+
// Listen for any invalidation on the child if invalidateOnChildChange is true or an object (ignore when false or not set)
|
|
258
|
+
if (child.isUI5Element && slotData.invalidateOnChildChange) {
|
|
259
|
+
const method = (child.attachInvalidate || child._attachChange).bind(child);
|
|
260
|
+
method(this._getChildChangeListener(slotName));
|
|
266
261
|
}
|
|
267
262
|
|
|
263
|
+
// Listen for the slotchange event if the child is a slot itself
|
|
268
264
|
if (isSlot(child)) {
|
|
269
|
-
this._attachSlotChange(child);
|
|
265
|
+
this._attachSlotChange(child, slotName);
|
|
270
266
|
}
|
|
271
267
|
|
|
272
268
|
const propertyName = slotData.propertyName || slotName;
|
|
@@ -282,10 +278,33 @@ class UI5Element extends HTMLElement {
|
|
|
282
278
|
|
|
283
279
|
// Distribute the child in the _state object, keeping the Light DOM order,
|
|
284
280
|
// not the order elements are defined.
|
|
285
|
-
slottedChildrenMap.forEach((children,
|
|
286
|
-
this._state[
|
|
281
|
+
slottedChildrenMap.forEach((children, propertyName) => {
|
|
282
|
+
this._state[propertyName] = children.sort((a, b) => a.idx - b.idx).map(_ => _.child);
|
|
287
283
|
});
|
|
288
|
-
|
|
284
|
+
|
|
285
|
+
// Compare the content of each slot with the cached values and invalidate for the ones that changed
|
|
286
|
+
let invalidated = false;
|
|
287
|
+
for (const [slotName, slotData] of Object.entries(slotsMap)) { // eslint-disable-line
|
|
288
|
+
const propertyName = slotData.propertyName || slotName;
|
|
289
|
+
if (!arraysAreEqual(slotsCachedContentMap.get(propertyName), this._state[propertyName])) {
|
|
290
|
+
_invalidate.call(this, {
|
|
291
|
+
type: "slot",
|
|
292
|
+
name: propertyNameToSlotMap.get(propertyName),
|
|
293
|
+
reason: "children",
|
|
294
|
+
});
|
|
295
|
+
invalidated = true;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// If none of the slots had an invalidation due to changes to immediate children,
|
|
300
|
+
// the change is considered to be text content of the default slot
|
|
301
|
+
if (!invalidated) {
|
|
302
|
+
_invalidate.call(this, {
|
|
303
|
+
type: "slot",
|
|
304
|
+
name: "default",
|
|
305
|
+
reason: "textcontent",
|
|
306
|
+
});
|
|
307
|
+
}
|
|
289
308
|
}
|
|
290
309
|
|
|
291
310
|
/**
|
|
@@ -294,25 +313,62 @@ class UI5Element extends HTMLElement {
|
|
|
294
313
|
*/
|
|
295
314
|
_clearSlot(slotName, slotData) {
|
|
296
315
|
const propertyName = slotData.propertyName || slotName;
|
|
297
|
-
|
|
298
|
-
let children = this._state[propertyName];
|
|
299
|
-
if (!Array.isArray(children)) {
|
|
300
|
-
children = [children];
|
|
301
|
-
}
|
|
316
|
+
const children = this._state[propertyName];
|
|
302
317
|
|
|
303
318
|
children.forEach(child => {
|
|
304
319
|
if (child && child.isUI5Element) {
|
|
305
|
-
|
|
306
|
-
|
|
320
|
+
const method = (child.detachInvalidate || child._detachChange).bind(child);
|
|
321
|
+
method(this._getChildChangeListener(slotName));
|
|
307
322
|
}
|
|
308
323
|
|
|
309
324
|
if (isSlot(child)) {
|
|
310
|
-
this._detachSlotChange(child);
|
|
325
|
+
this._detachSlotChange(child, slotName);
|
|
311
326
|
}
|
|
312
327
|
});
|
|
313
328
|
|
|
314
329
|
this._state[propertyName] = [];
|
|
315
|
-
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Attach a callback that will be executed whenever the component is invalidated
|
|
334
|
+
*
|
|
335
|
+
* @param callback
|
|
336
|
+
* @public
|
|
337
|
+
*/
|
|
338
|
+
attachInvalidate(callback) {
|
|
339
|
+
this._eventProvider.attachEvent("invalidate", callback);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Detach the callback that is executed whenever the component is invalidated
|
|
344
|
+
*
|
|
345
|
+
* @param callback
|
|
346
|
+
* @public
|
|
347
|
+
*/
|
|
348
|
+
detachInvalidate(callback) {
|
|
349
|
+
this._eventProvider.detachEvent("invalidate", callback);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Callback that is executed whenever a monitored child changes its state
|
|
354
|
+
*
|
|
355
|
+
* @param slotName the slot in which a child was invalidated
|
|
356
|
+
* @param childChangeInfo the changeInfo object for the child in the given slot
|
|
357
|
+
* @private
|
|
358
|
+
*/
|
|
359
|
+
_onChildChange(slotName, childChangeInfo) {
|
|
360
|
+
if (!this.constructor.getMetadata().shouldInvalidateOnChildChange(slotName, childChangeInfo.type, childChangeInfo.name)) {
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// The component should be invalidated as this type of change on the child is listened for
|
|
365
|
+
// However, no matter what changed on the child (property/slot), the invalidation is registered as "type=slot" for the component itself
|
|
366
|
+
_invalidate.call(this, {
|
|
367
|
+
type: "slot",
|
|
368
|
+
name: slotName,
|
|
369
|
+
reason: "childchange",
|
|
370
|
+
child: childChangeInfo.target,
|
|
371
|
+
});
|
|
316
372
|
}
|
|
317
373
|
|
|
318
374
|
/**
|
|
@@ -327,12 +383,8 @@ class UI5Element extends HTMLElement {
|
|
|
327
383
|
const propertyTypeClass = properties[nameInCamelCase].type;
|
|
328
384
|
if (propertyTypeClass === Boolean) {
|
|
329
385
|
newValue = newValue !== null;
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
newValue = parseInt(newValue);
|
|
333
|
-
}
|
|
334
|
-
if (propertyTypeClass === Float) {
|
|
335
|
-
newValue = parseFloat(newValue);
|
|
386
|
+
} else if (isDescendantOf(propertyTypeClass, DataType)) {
|
|
387
|
+
newValue = propertyTypeClass.attributeToProperty(newValue);
|
|
336
388
|
}
|
|
337
389
|
this[nameInCamelCase] = newValue;
|
|
338
390
|
}
|
|
@@ -345,22 +397,24 @@ class UI5Element extends HTMLElement {
|
|
|
345
397
|
if (!this.constructor.getMetadata().hasAttribute(name)) {
|
|
346
398
|
return;
|
|
347
399
|
}
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
return;
|
|
351
|
-
}
|
|
352
|
-
|
|
400
|
+
const properties = this.constructor.getMetadata().getProperties();
|
|
401
|
+
const propertyTypeClass = properties[name].type;
|
|
353
402
|
const attrName = camelToKebabCase(name);
|
|
354
403
|
const attrValue = this.getAttribute(attrName);
|
|
355
|
-
|
|
404
|
+
|
|
405
|
+
if (propertyTypeClass === Boolean) {
|
|
356
406
|
if (newValue === true && attrValue === null) {
|
|
357
407
|
this.setAttribute(attrName, "");
|
|
358
408
|
} else if (newValue === false && attrValue !== null) {
|
|
359
409
|
this.removeAttribute(attrName);
|
|
360
410
|
}
|
|
361
|
-
} else if (
|
|
362
|
-
this.setAttribute(attrName, newValue);
|
|
363
|
-
}
|
|
411
|
+
} else if (isDescendantOf(propertyTypeClass, DataType)) {
|
|
412
|
+
this.setAttribute(attrName, propertyTypeClass.propertyToAttribute(newValue));
|
|
413
|
+
} else if (typeof newValue !== "object") {
|
|
414
|
+
if (attrValue !== newValue) {
|
|
415
|
+
this.setAttribute(attrName, newValue);
|
|
416
|
+
}
|
|
417
|
+
} // else { return; } // old object handling
|
|
364
418
|
}
|
|
365
419
|
|
|
366
420
|
/**
|
|
@@ -386,124 +440,96 @@ class UI5Element extends HTMLElement {
|
|
|
386
440
|
* @private
|
|
387
441
|
*/
|
|
388
442
|
_initializeState() {
|
|
389
|
-
|
|
390
|
-
this._state = Object.assign({}, defaultState);
|
|
443
|
+
this._state = { ...this.constructor.getMetadata().getInitialState() };
|
|
391
444
|
}
|
|
392
445
|
|
|
393
446
|
/**
|
|
447
|
+
* Returns a singleton event listener for the "change" event of a child in a given slot
|
|
448
|
+
*
|
|
449
|
+
* @param slotName the name of the slot, where the child is
|
|
450
|
+
* @returns {any}
|
|
394
451
|
* @private
|
|
395
452
|
*/
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
slotName
|
|
399
|
-
childProperties = childMetadata.getProperties();
|
|
400
|
-
|
|
401
|
-
let observedProps = [],
|
|
402
|
-
notObservedProps = [];
|
|
403
|
-
|
|
404
|
-
if (Array.isArray(listenFor)) {
|
|
405
|
-
observedProps = listenFor;
|
|
406
|
-
} else {
|
|
407
|
-
observedProps = Array.isArray(listenFor.props) ? listenFor.props : Object.keys(childProperties);
|
|
408
|
-
notObservedProps = Array.isArray(listenFor.exclude) ? listenFor.exclude : [];
|
|
453
|
+
_getChildChangeListener(slotName) {
|
|
454
|
+
if (!this._childChangeListeners.has(slotName)) {
|
|
455
|
+
this._childChangeListeners.set(slotName, this._onChildChange.bind(this, slotName));
|
|
409
456
|
}
|
|
410
|
-
|
|
411
|
-
if (!this._monitoredChildProps.has(slotName)) {
|
|
412
|
-
this._monitoredChildProps.set(slotName, { observedProps, notObservedProps });
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
child.addEventListener("_property-change", this._invalidateParentOnPropertyUpdate);
|
|
416
|
-
child._firePropertyChange = true;
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
/**
|
|
420
|
-
* @private
|
|
421
|
-
*/
|
|
422
|
-
_detachChildPropertyUpdated(child) {
|
|
423
|
-
child.removeEventListener("_property-change", this._invalidateParentOnPropertyUpdate);
|
|
424
|
-
child._firePropertyChange = false;
|
|
457
|
+
return this._childChangeListeners.get(slotName);
|
|
425
458
|
}
|
|
426
459
|
|
|
427
460
|
/**
|
|
461
|
+
* Returns a singleton slotchange event listener that invalidates the component due to changes in the given slot
|
|
462
|
+
*
|
|
463
|
+
* @param slotName the name of the slot, where the slot element (whose slotchange event we're listening to) is
|
|
464
|
+
* @returns {any}
|
|
428
465
|
* @private
|
|
429
466
|
*/
|
|
430
|
-
|
|
431
|
-
this.
|
|
432
|
-
|
|
433
|
-
if (this._firePropertyChange) {
|
|
434
|
-
this.dispatchEvent(new CustomEvent("_property-change", {
|
|
435
|
-
detail: { name, newValue: value },
|
|
436
|
-
composed: false,
|
|
437
|
-
bubbles: true,
|
|
438
|
-
}));
|
|
467
|
+
_getSlotChangeListener(slotName) {
|
|
468
|
+
if (!this._slotChangeListeners.has(slotName)) {
|
|
469
|
+
this._slotChangeListeners.set(slotName, this._onSlotChange.bind(this, slotName));
|
|
439
470
|
}
|
|
471
|
+
return this._slotChangeListeners.get(slotName);
|
|
440
472
|
}
|
|
441
473
|
|
|
442
474
|
/**
|
|
443
475
|
* @private
|
|
444
476
|
*/
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
const parentNode = this.parentNode;
|
|
448
|
-
if (!parentNode) {
|
|
449
|
-
return;
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
const slotName = parentNode.constructor._getSlotName(this);
|
|
453
|
-
const propsMetadata = parentNode._monitoredChildProps.get(slotName);
|
|
454
|
-
|
|
455
|
-
if (!propsMetadata) {
|
|
456
|
-
return;
|
|
457
|
-
}
|
|
458
|
-
const { observedProps, notObservedProps } = propsMetadata;
|
|
459
|
-
|
|
460
|
-
if (observedProps.includes(prop.detail.name) && !notObservedProps.includes(prop.detail.name)) {
|
|
461
|
-
parentNode._invalidate("_parent_", this);
|
|
462
|
-
}
|
|
477
|
+
_attachSlotChange(child, slotName) {
|
|
478
|
+
child.addEventListener("slotchange", this._getSlotChangeListener(slotName));
|
|
463
479
|
}
|
|
464
480
|
|
|
465
481
|
/**
|
|
466
482
|
* @private
|
|
467
483
|
*/
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
this._invalidateOnSlotChange = () => {
|
|
471
|
-
this._invalidate("slotchange");
|
|
472
|
-
};
|
|
473
|
-
}
|
|
474
|
-
child.addEventListener("slotchange", this._invalidateOnSlotChange);
|
|
484
|
+
_detachSlotChange(child, slotName) {
|
|
485
|
+
child.removeEventListener("slotchange", this._getSlotChangeListener(slotName));
|
|
475
486
|
}
|
|
476
487
|
|
|
477
488
|
/**
|
|
489
|
+
* Whenever a slot element is slotted inside a UI5 Web Component, its slotchange event invalidates the component
|
|
490
|
+
*
|
|
491
|
+
* @param slotName the name of the slot, where the slot element (whose slotchange event we're listening to) is
|
|
478
492
|
* @private
|
|
479
493
|
*/
|
|
480
|
-
|
|
481
|
-
|
|
494
|
+
_onSlotChange(slotName) {
|
|
495
|
+
_invalidate.call(this, {
|
|
496
|
+
type: "slot",
|
|
497
|
+
name: slotName,
|
|
498
|
+
reason: "slotchange",
|
|
499
|
+
});
|
|
482
500
|
}
|
|
483
501
|
|
|
484
502
|
/**
|
|
485
|
-
*
|
|
486
|
-
*
|
|
503
|
+
* A callback that is executed each time an already rendered component is invalidated (scheduled for re-rendering)
|
|
504
|
+
*
|
|
505
|
+
* @param changeInfo An object with information about the change that caused invalidation.
|
|
506
|
+
* The object can have the following properties:
|
|
507
|
+
* - type: (property|slot) tells what caused the invalidation
|
|
508
|
+
* 1) property: a property value was changed either directly or as a result of changing the corresponding attribute
|
|
509
|
+
* 2) slot: a slotted node(nodes) changed in one of several ways (see "reason")
|
|
510
|
+
*
|
|
511
|
+
* - name: the name of the property or slot that caused the invalidation
|
|
512
|
+
*
|
|
513
|
+
* - reason: (children|textcontent|childchange|slotchange) relevant only for type="slot" only and tells exactly what changed in the slot
|
|
514
|
+
* 1) children: immediate children (HTML elements or text nodes) were added, removed or reordered in the slot
|
|
515
|
+
* 2) textcontent: text nodes in the slot changed value (or nested text nodes were added or changed value). Can only trigger for slots of "type: Node"
|
|
516
|
+
* 3) slotchange: a slot element, slotted inside that slot had its "slotchange" event listener called. This practically means that transitively slotted children changed.
|
|
517
|
+
* Can only trigger if the child of a slot is a slot element itself.
|
|
518
|
+
* 4) childchange: indicates that a UI5Element child in that slot was invalidated and in turn invalidated the component.
|
|
519
|
+
* Can only trigger for slots with "invalidateOnChildChange" metadata descriptor
|
|
520
|
+
*
|
|
521
|
+
* - newValue: the new value of the property (for type="property" only)
|
|
522
|
+
*
|
|
523
|
+
* - oldValue: the old value of the property (for type="property" only)
|
|
524
|
+
*
|
|
525
|
+
* - child the child that was changed (for type="slot" and reason="childchange" only)
|
|
526
|
+
*
|
|
527
|
+
* @public
|
|
487
528
|
*/
|
|
488
|
-
|
|
489
|
-
if (this._shouldInvalidateParent) {
|
|
490
|
-
this.parentNode._invalidate();
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
if (!this._upToDate) {
|
|
494
|
-
// console.log("already invalidated", this, ...arguments);
|
|
495
|
-
return;
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
if (this.getDomRef() && !this._suppressInvalidation) {
|
|
499
|
-
this._upToDate = false;
|
|
500
|
-
// console.log("INVAL", this, ...arguments);
|
|
501
|
-
RenderScheduler.renderDeferred(this);
|
|
502
|
-
}
|
|
503
|
-
}
|
|
529
|
+
onInvalidation(changeInfo) {}
|
|
504
530
|
|
|
505
531
|
/**
|
|
506
|
-
* Do not call this method directly, only intended to be called by
|
|
532
|
+
* Do not call this method directly, only intended to be called by js
|
|
507
533
|
* @protected
|
|
508
534
|
*/
|
|
509
535
|
_render() {
|
|
@@ -522,15 +548,37 @@ class UI5Element extends HTMLElement {
|
|
|
522
548
|
}
|
|
523
549
|
|
|
524
550
|
// resume normal invalidation handling
|
|
525
|
-
|
|
551
|
+
this._suppressInvalidation = false;
|
|
526
552
|
|
|
527
553
|
// Update the shadow root with the render result
|
|
528
|
-
|
|
529
|
-
this.
|
|
530
|
-
|
|
554
|
+
/*
|
|
555
|
+
if (this._changedState.length) {
|
|
556
|
+
let element = this.localName;
|
|
557
|
+
if (this.id) {
|
|
558
|
+
element = `${element}#${this.id}`;
|
|
559
|
+
}
|
|
560
|
+
console.log("Re-rendering:", element, this._changedState.map(x => { // eslint-disable-line
|
|
561
|
+
let res = `${x.type}`;
|
|
562
|
+
if (x.reason) {
|
|
563
|
+
res = `${res}(${x.reason})`;
|
|
564
|
+
}
|
|
565
|
+
res = `${res}: ${x.name}`;
|
|
566
|
+
if (x.type === "property") {
|
|
567
|
+
res = `${res} ${x.oldValue} => ${x.newValue}`;
|
|
568
|
+
}
|
|
531
569
|
|
|
532
|
-
|
|
533
|
-
|
|
570
|
+
return res;
|
|
571
|
+
}));
|
|
572
|
+
}
|
|
573
|
+
*/
|
|
574
|
+
this._changedState = [];
|
|
575
|
+
|
|
576
|
+
// Update shadow root and static area item
|
|
577
|
+
if (this.constructor._needsShadowDOM()) {
|
|
578
|
+
updateShadowRoot(this);
|
|
579
|
+
}
|
|
580
|
+
if (this.staticAreaItem) {
|
|
581
|
+
this.staticAreaItem.update();
|
|
534
582
|
}
|
|
535
583
|
|
|
536
584
|
// Safari requires that children get the slot attribute only after the slot tags have been rendered in the shadow DOM
|
|
@@ -544,35 +592,6 @@ class UI5Element extends HTMLElement {
|
|
|
544
592
|
}
|
|
545
593
|
}
|
|
546
594
|
|
|
547
|
-
/**
|
|
548
|
-
* @private
|
|
549
|
-
*/
|
|
550
|
-
_updateShadowRoot() {
|
|
551
|
-
if (!this.constructor._needsShadowDOM()) {
|
|
552
|
-
return;
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
let styleToPrepend;
|
|
556
|
-
const renderResult = executeTemplate(this.constructor.template, this);
|
|
557
|
-
|
|
558
|
-
// IE11, Edge
|
|
559
|
-
if (window.ShadyDOM) {
|
|
560
|
-
createComponentStyleTag(this.constructor);
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
// Chrome
|
|
564
|
-
if (document.adoptedStyleSheets) {
|
|
565
|
-
this.shadowRoot.adoptedStyleSheets = getConstructableStyle(this.constructor);
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
// FF, Safari
|
|
569
|
-
if (!document.adoptedStyleSheets && !window.ShadyDOM) {
|
|
570
|
-
styleToPrepend = getEffectiveStyle(this.constructor);
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
this.constructor.render(renderResult, this.shadowRoot, styleToPrepend, { eventContext: this });
|
|
574
|
-
}
|
|
575
|
-
|
|
576
595
|
/**
|
|
577
596
|
* @private
|
|
578
597
|
*/
|
|
@@ -595,16 +614,27 @@ class UI5Element extends HTMLElement {
|
|
|
595
614
|
|
|
596
615
|
/**
|
|
597
616
|
* Returns the DOM Element inside the Shadow Root that corresponds to the opening tag in the UI5 Web Component's template
|
|
617
|
+
* *Note:* For logical (abstract) elements (items, options, etc...), returns the part of the parent's DOM that represents this option
|
|
598
618
|
* Use this method instead of "this.shadowRoot" to read the Shadow DOM, if ever necessary
|
|
619
|
+
*
|
|
599
620
|
* @public
|
|
600
621
|
*/
|
|
601
622
|
getDomRef() {
|
|
623
|
+
// If a component set _getRealDomRef to its children, use the return value of this function
|
|
624
|
+
if (typeof this._getRealDomRef === "function") {
|
|
625
|
+
return this._getRealDomRef();
|
|
626
|
+
}
|
|
627
|
+
|
|
602
628
|
if (!this.shadowRoot || this.shadowRoot.children.length === 0) {
|
|
603
629
|
return;
|
|
604
630
|
}
|
|
605
631
|
|
|
606
|
-
|
|
607
|
-
|
|
632
|
+
const children = [...this.shadowRoot.children].filter(child => !["link", "style"].includes(child.localName));
|
|
633
|
+
if (children.length !== 1) {
|
|
634
|
+
console.warn(`The shadow DOM for ${this.constructor.getMetadata().getTag()} does not have a top level element, the getDomRef() method might not work as expected`); // eslint-disable-line
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
return children[0];
|
|
608
638
|
}
|
|
609
639
|
|
|
610
640
|
/**
|
|
@@ -621,12 +651,13 @@ class UI5Element extends HTMLElement {
|
|
|
621
651
|
}
|
|
622
652
|
|
|
623
653
|
/**
|
|
624
|
-
*
|
|
654
|
+
* Waits for dom ref and then returns the DOM Element marked with "data-sap-focus-ref" inside the template.
|
|
655
|
+
* This is the element that will receive the focus by default.
|
|
625
656
|
* @public
|
|
626
|
-
* @param {String} refName Defines the name of the stable DOM ref
|
|
627
657
|
*/
|
|
628
|
-
|
|
629
|
-
|
|
658
|
+
async getFocusDomRefAsync() {
|
|
659
|
+
await this._waitForDomRef();
|
|
660
|
+
return this.getFocusDomRef();
|
|
630
661
|
}
|
|
631
662
|
|
|
632
663
|
/**
|
|
@@ -664,8 +695,6 @@ class UI5Element extends HTMLElement {
|
|
|
664
695
|
}
|
|
665
696
|
|
|
666
697
|
_fireEvent(name, data, cancelable = false, bubbles = true) {
|
|
667
|
-
let compatEventResult = true; // Initialized to true, because if the event is not fired at all, it should be considered "not-prevented"
|
|
668
|
-
|
|
669
698
|
const noConflictEvent = new CustomEvent(`ui5-${name}`, {
|
|
670
699
|
detail: data,
|
|
671
700
|
composed: false,
|
|
@@ -673,14 +702,14 @@ class UI5Element extends HTMLElement {
|
|
|
673
702
|
cancelable,
|
|
674
703
|
});
|
|
675
704
|
|
|
676
|
-
// This will be false if the
|
|
677
|
-
|
|
705
|
+
// This will be false if the no-conflict event is prevented
|
|
706
|
+
const noConflictEventResult = this.dispatchEvent(noConflictEvent);
|
|
678
707
|
|
|
679
708
|
if (skipOriginalEvent(name)) {
|
|
680
|
-
return
|
|
709
|
+
return noConflictEventResult;
|
|
681
710
|
}
|
|
682
711
|
|
|
683
|
-
const
|
|
712
|
+
const normalEvent = new CustomEvent(name, {
|
|
684
713
|
detail: data,
|
|
685
714
|
composed: false,
|
|
686
715
|
bubbles,
|
|
@@ -688,10 +717,10 @@ class UI5Element extends HTMLElement {
|
|
|
688
717
|
});
|
|
689
718
|
|
|
690
719
|
// This will be false if the normal event is prevented
|
|
691
|
-
const normalEventResult = this.dispatchEvent(
|
|
720
|
+
const normalEventResult = this.dispatchEvent(normalEvent);
|
|
692
721
|
|
|
693
722
|
// Return false if any of the two events was prevented (its result was false).
|
|
694
|
-
return normalEventResult &&
|
|
723
|
+
return normalEventResult && noConflictEventResult;
|
|
695
724
|
}
|
|
696
725
|
|
|
697
726
|
/**
|
|
@@ -700,18 +729,7 @@ class UI5Element extends HTMLElement {
|
|
|
700
729
|
* @public
|
|
701
730
|
*/
|
|
702
731
|
getSlottedNodes(slotName) {
|
|
703
|
-
|
|
704
|
-
if (!isSlot(curr)) {
|
|
705
|
-
return acc.concat([curr]);
|
|
706
|
-
}
|
|
707
|
-
return acc.concat(curr.assignedNodes({ flatten: true }).filter(item => item instanceof HTMLElement));
|
|
708
|
-
};
|
|
709
|
-
|
|
710
|
-
return this[slotName].reduce(reducer, []);
|
|
711
|
-
}
|
|
712
|
-
|
|
713
|
-
get isCompact() {
|
|
714
|
-
return getComputedStyle(this).getPropertyValue(GLOBAL_CONTENT_DENSITY_CSS_VAR) === "compact";
|
|
732
|
+
return getSlottedElementsList(this[slotName]);
|
|
715
733
|
}
|
|
716
734
|
|
|
717
735
|
/**
|
|
@@ -723,33 +741,7 @@ class UI5Element extends HTMLElement {
|
|
|
723
741
|
*/
|
|
724
742
|
get effectiveDir() {
|
|
725
743
|
markAsRtlAware(this.constructor); // if a UI5 Element calls this method, it's considered to be rtl-aware
|
|
726
|
-
|
|
727
|
-
const doc = window.document;
|
|
728
|
-
const dirValues = ["ltr", "rtl"]; // exclude "auto" and "" from all calculations
|
|
729
|
-
const locallyAppliedDir = getComputedStyle(this).getPropertyValue(GLOBAL_DIR_CSS_VAR);
|
|
730
|
-
|
|
731
|
-
// In that order, inspect the CSS Var (for modern browsers), the element itself, html and body (for IE fallback)
|
|
732
|
-
if (dirValues.includes(locallyAppliedDir)) {
|
|
733
|
-
return locallyAppliedDir;
|
|
734
|
-
}
|
|
735
|
-
if (dirValues.includes(this.dir)) {
|
|
736
|
-
return this.dir;
|
|
737
|
-
}
|
|
738
|
-
if (dirValues.includes(doc.documentElement.dir)) {
|
|
739
|
-
return doc.documentElement.dir;
|
|
740
|
-
}
|
|
741
|
-
if (dirValues.includes(doc.body.dir)) {
|
|
742
|
-
return doc.body.dir;
|
|
743
|
-
}
|
|
744
|
-
|
|
745
|
-
// Finally, check the configuration for explicitly set RTL or language-implied RTL
|
|
746
|
-
return getRTL() ? "rtl" : undefined;
|
|
747
|
-
}
|
|
748
|
-
|
|
749
|
-
updateStaticAreaItemContentDensity() {
|
|
750
|
-
if (this.staticAreaItem) {
|
|
751
|
-
this.staticAreaItem._updateContentDensity(this.isCompact);
|
|
752
|
-
}
|
|
744
|
+
return getEffectiveDir(this);
|
|
753
745
|
}
|
|
754
746
|
|
|
755
747
|
/**
|
|
@@ -769,26 +761,6 @@ class UI5Element extends HTMLElement {
|
|
|
769
761
|
return this.getMetadata().getAttributesList();
|
|
770
762
|
}
|
|
771
763
|
|
|
772
|
-
/**
|
|
773
|
-
* @private
|
|
774
|
-
*/
|
|
775
|
-
static _getSlotName(child) {
|
|
776
|
-
// Text nodes can only go to the default slot
|
|
777
|
-
if (!(child instanceof HTMLElement)) {
|
|
778
|
-
return "default";
|
|
779
|
-
}
|
|
780
|
-
|
|
781
|
-
// Discover the slot based on the real slot name (f.e. footer => footer, or content-32 => content)
|
|
782
|
-
const slot = child.getAttribute("slot");
|
|
783
|
-
if (slot) {
|
|
784
|
-
const match = slot.match(/^(.+?)-\d+$/);
|
|
785
|
-
return match ? match[1] : slot;
|
|
786
|
-
}
|
|
787
|
-
|
|
788
|
-
// Use default slot as a fallback
|
|
789
|
-
return "default";
|
|
790
|
-
}
|
|
791
|
-
|
|
792
764
|
/**
|
|
793
765
|
* @private
|
|
794
766
|
*/
|
|
@@ -796,70 +768,30 @@ class UI5Element extends HTMLElement {
|
|
|
796
768
|
return !!this.template;
|
|
797
769
|
}
|
|
798
770
|
|
|
799
|
-
_shouldUpdateFragment() {
|
|
800
|
-
return this.constructor._needsStaticArea() && this.staticAreaItem.isRendered();
|
|
801
|
-
}
|
|
802
|
-
|
|
803
771
|
/**
|
|
804
772
|
* @private
|
|
805
773
|
*/
|
|
806
774
|
static _needsStaticArea() {
|
|
807
|
-
return
|
|
775
|
+
return !!this.staticAreaTemplate;
|
|
808
776
|
}
|
|
809
777
|
|
|
810
778
|
/**
|
|
811
779
|
* @public
|
|
812
780
|
*/
|
|
813
781
|
getStaticAreaItemDomRef() {
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
/**
|
|
818
|
-
* @private
|
|
819
|
-
*/
|
|
820
|
-
static _getDefaultState() {
|
|
821
|
-
if (this._defaultState) {
|
|
822
|
-
return this._defaultState;
|
|
782
|
+
if (!this.constructor._needsStaticArea()) {
|
|
783
|
+
throw new Error("This component does not use the static area");
|
|
823
784
|
}
|
|
824
785
|
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
// Initialize properties
|
|
830
|
-
const props = MetadataClass.getProperties();
|
|
831
|
-
for (const propName in props) { // eslint-disable-line
|
|
832
|
-
const propType = props[propName].type;
|
|
833
|
-
const propDefaultValue = props[propName].defaultValue;
|
|
834
|
-
|
|
835
|
-
if (propType === Boolean) {
|
|
836
|
-
defaultState[propName] = false;
|
|
837
|
-
|
|
838
|
-
if (propDefaultValue !== undefined) {
|
|
839
|
-
console.warn("The 'defaultValue' metadata key is ignored for all booleans properties, they would be initialized with 'false' by default"); // eslint-disable-line
|
|
840
|
-
}
|
|
841
|
-
} else if (props[propName].multiple) {
|
|
842
|
-
defaultState[propName] = [];
|
|
843
|
-
} else if (propType === Object) {
|
|
844
|
-
defaultState[propName] = "defaultValue" in props[propName] ? props[propName].defaultValue : {};
|
|
845
|
-
} else if (propType === String) {
|
|
846
|
-
defaultState[propName] = "defaultValue" in props[propName] ? props[propName].defaultValue : "";
|
|
847
|
-
} else {
|
|
848
|
-
defaultState[propName] = propDefaultValue;
|
|
849
|
-
}
|
|
786
|
+
if (!this.staticAreaItem) {
|
|
787
|
+
this.staticAreaItem = StaticAreaItem.createInstance();
|
|
788
|
+
this.staticAreaItem.setOwnerElement(this);
|
|
850
789
|
}
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
if (slotsAreManaged) {
|
|
854
|
-
const slots = MetadataClass.getSlots();
|
|
855
|
-
for (const [slotName, slotData] of Object.entries(slots)) { // eslint-disable-line
|
|
856
|
-
const propertyName = slotData.propertyName || slotName;
|
|
857
|
-
defaultState[propertyName] = [];
|
|
858
|
-
}
|
|
790
|
+
if (!this.staticAreaItem.parentElement) {
|
|
791
|
+
getSingletonElementInstance("ui5-static-area").appendChild(this.staticAreaItem);
|
|
859
792
|
}
|
|
860
793
|
|
|
861
|
-
this.
|
|
862
|
-
return defaultState;
|
|
794
|
+
return this.staticAreaItem.getDomRef();
|
|
863
795
|
}
|
|
864
796
|
|
|
865
797
|
/**
|
|
@@ -873,7 +805,7 @@ class UI5Element extends HTMLElement {
|
|
|
873
805
|
const properties = this.getMetadata().getProperties();
|
|
874
806
|
for (const [prop, propData] of Object.entries(properties)) { // eslint-disable-line
|
|
875
807
|
if (!isValidPropertyName(prop)) {
|
|
876
|
-
|
|
808
|
+
console.warn(`"${prop}" is not a valid property name. Use a name that does not collide with DOM APIs`); /* eslint-disable-line */
|
|
877
809
|
}
|
|
878
810
|
|
|
879
811
|
if (propData.type === Boolean && propData.defaultValue) {
|
|
@@ -911,14 +843,27 @@ class UI5Element extends HTMLElement {
|
|
|
911
843
|
}
|
|
912
844
|
},
|
|
913
845
|
set(value) {
|
|
846
|
+
let isDifferent;
|
|
914
847
|
value = this.constructor.getMetadata().constructor.validatePropertyValue(value, propData);
|
|
915
848
|
|
|
916
849
|
const oldState = this._state[prop];
|
|
850
|
+
if (propData.multiple && propData.compareValues) {
|
|
851
|
+
isDifferent = !arraysAreEqual(oldState, value);
|
|
852
|
+
} else if (isDescendantOf(propData.type, DataType)) {
|
|
853
|
+
isDifferent = !propData.type.valuesAreEqual(oldState, value);
|
|
854
|
+
} else {
|
|
855
|
+
isDifferent = oldState !== value;
|
|
856
|
+
}
|
|
917
857
|
|
|
918
|
-
if (
|
|
858
|
+
if (isDifferent) {
|
|
919
859
|
this._state[prop] = value;
|
|
920
|
-
|
|
921
|
-
|
|
860
|
+
_invalidate.call(this, {
|
|
861
|
+
type: "property",
|
|
862
|
+
name: prop,
|
|
863
|
+
newValue: value,
|
|
864
|
+
oldValue: oldState,
|
|
865
|
+
});
|
|
866
|
+
this._updateAttribute(prop, value);
|
|
922
867
|
}
|
|
923
868
|
},
|
|
924
869
|
});
|
|
@@ -929,7 +874,7 @@ class UI5Element extends HTMLElement {
|
|
|
929
874
|
const slots = this.getMetadata().getSlots();
|
|
930
875
|
for (const [slotName, slotData] of Object.entries(slots)) { // eslint-disable-line
|
|
931
876
|
if (!isValidPropertyName(slotName)) {
|
|
932
|
-
|
|
877
|
+
console.warn(`"${slotName}" is not a valid property name. Use a name that does not collide with DOM APIs`); /* eslint-disable-line */
|
|
933
878
|
}
|
|
934
879
|
|
|
935
880
|
const propertyName = slotData.propertyName || slotName;
|
|
@@ -941,7 +886,7 @@ class UI5Element extends HTMLElement {
|
|
|
941
886
|
return [];
|
|
942
887
|
},
|
|
943
888
|
set() {
|
|
944
|
-
throw new Error("Cannot set
|
|
889
|
+
throw new Error("Cannot set slot content directly, use the DOM APIs (appendChild, removeChild, etc...)");
|
|
945
890
|
},
|
|
946
891
|
});
|
|
947
892
|
}
|
|
@@ -956,6 +901,14 @@ class UI5Element extends HTMLElement {
|
|
|
956
901
|
return metadata;
|
|
957
902
|
}
|
|
958
903
|
|
|
904
|
+
/**
|
|
905
|
+
* Sets a new metadata object for this UI5 Web Component Class
|
|
906
|
+
* @protected
|
|
907
|
+
*/
|
|
908
|
+
static set metadata(newMetadata) {
|
|
909
|
+
metadata = newMetadata;
|
|
910
|
+
}
|
|
911
|
+
|
|
959
912
|
/**
|
|
960
913
|
* Returns the CSS for this UI5 Web Component Class
|
|
961
914
|
* @protected
|
|
@@ -1041,11 +994,13 @@ class UI5Element extends HTMLElement {
|
|
|
1041
994
|
this._generateAccessors();
|
|
1042
995
|
registerTag(tag);
|
|
1043
996
|
window.customElements.define(tag, this);
|
|
997
|
+
preloadLinks(this);
|
|
1044
998
|
|
|
1045
999
|
if (altTag && !customElements.get(altTag)) {
|
|
1046
|
-
class oldClassName extends this {}
|
|
1047
1000
|
registerTag(altTag);
|
|
1048
|
-
window.customElements.define(altTag,
|
|
1001
|
+
window.customElements.define(altTag, getClassCopy(this, () => {
|
|
1002
|
+
console.log(`The ${altTag} tag is deprecated and will be removed in the next release, please use ${tag} instead.`); // eslint-disable-line
|
|
1003
|
+
}));
|
|
1049
1004
|
}
|
|
1050
1005
|
}
|
|
1051
1006
|
return this;
|