Repository: exadel-inc/esl
Pull request #2129 (2023-12-20 01:58:41)

epic: drop IE11 support and legacy properties (`main-next` -> `main-beta`)
Closes: #1462
Closes: #1461


Patch:
diff --git a/README.md b/README.md
index 02a9d9a21..48b10cde0 100644
--- a/README.md
+++ b/README.md
@@ -40,7 +40,6 @@ to meet your project needs.
 
 ESL is based solely on Web API and ECMAScript 6 features, which let our web UI components library work 
 well across all modern browsers such as Firefox, Chrome, Safari, Opera, and Edge. 
-ESL can also work on IE11 when the whole list of polyfills is provided (see [Browser support & Polyfills](https://github.com/exadel-inc/esl/blob/HEAD/docs/BROWSER_SUPPORT.md) for more details).
 
 ## Library Structure
 
diff --git a/docs/BROWSER_SUPPORT.md b/docs/BROWSER_SUPPORT.md
index b1c998b86..06e615e40 100644
--- a/docs/BROWSER_SUPPORT.md
+++ b/docs/BROWSER_SUPPORT.md
@@ -6,17 +6,11 @@ Exadel Smart Library does not have dependencies but uses the following list of n
 
 - ECMAScript 6 features
     - [Array.from](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from) (no Iterable Objects support required)
-      ([ESL polyfill](../src/polyfills/list/es6.array.from.ts) provided)
     - [Array.prototype.find](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find)
-      ([ESL polyfill](../src/polyfills/list/es6.array.find.ts) provided)
     - [Array.prototype.findIndex](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findIndex)
-      ([ESL polyfill](../src/polyfills/list/es6.array.find.ts) provided)
     - [Array.prototype.include](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes)
-      ([ESL polyfill](../src/polyfills/list/es6.array.includes.ts) provided)
     - [Object.is](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is)
-      ([ESL polyfill](../src/polyfills/list/es6.object.is.ts) provided)
     - [Object.assign](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign) (accurate polyfill required)
-      ([ESL polyfill](../src/polyfills/list/es6.object.assign.ts) provided)
     - [ES6 Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)
 - Web API
     - [Event](https://developer.mozilla.org/en-US/docs/Web/API/Event) constructor + `preventDefault` polyfill
@@ -30,25 +24,6 @@ Exadel Smart Library does not have dependencies but uses the following list of n
     - [ResizeObserver](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver)
     - [Custom Elements](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements)
 
-All of them are fully supported by modern browsers such as Chrome, Firefox, Safari or Edge (>43).
+All of them are fully supported by modern browsers such as Chrome, Firefox, Safari (>13) or Edge (>43).
 
-In order to make ESL work in older browsers, you can use a "light" polyfills list of IntersectionObserver, ResizeObserver and Custom Elements (for older versions of Edge and Safari).
-
-Or make the library work in IE11 or Edge (<14) by using the "full" polyfills list provided.
-
-See more details on what polyfill approach might look like in the demo pages source code.
-
-Also, ESL has built-in polyfills for some of DOM and ES6 features. They are available under [polyfills](../src/polyfills) directory:
-  - ECMA Script 5: output shim (`HTMLElement` constructor call) - [es5-target-shim.ts](../src/polyfills/es5-target-shim.ts)
-  - ECMA Script 5: IE11, Edge <14 support - [polyfills.es5.ts](../src/polyfills/polyfills.es5.ts)
-    - `Object.is`, `Object.assign` 
-    - `Array.from`, `Array.prototype.find`, `Array.prototype.findIndex`, `Array.prototype.index`
-    - `Event`, `CustomEvent`, `MouseEvent`, `KeyboardEvent`, `FocusEvent` shim
-    - `Event.prototype.preventDefault` shim
-    - `Element.prototype.closest`
-    - `DOMTokenList.prototype.toggle`
-    - *Note: there is no promise polyfill embedded*, use npm:[promise-polyfill](https://www.npmjs.com/package/promise-polyfill) or similar
-  - ECMA Script 6: DOM - [polyfills.es6.ts](../src/polyfills/polyfills.es6.ts)
-    - `Node.isConnected` 
-    - `KeyboardKey.prototype.key` 
-    - `Element.prototype.toggleAttribute` 
+In order to make ESL work in older browsers, you can use polyfill (for older versions of Safari).
diff --git a/eslint/package.json b/eslint/package.json
index 251b8e16a..910cf9748 100644
--- a/eslint/package.json
+++ b/eslint/package.json
@@ -9,6 +9,10 @@
   "license": "MIT",
   "description": "Helper ESLint rules to find and migrate ESL (@exadel/esl) library deprecations",
   "main": "dist/index.js",
+  "types": "dist/index.d.ts",
+  "files": [
+    "dist/**/*"
+  ],
   "scripts": {
     "prepare": "npm run build",
     "test": "jest",
@@ -16,10 +20,6 @@
     "build": "npm run clean && tsc --project tsconfig.json"
   },
   "dependencies": {},
-  "files": [
-    "dist/**/*"
-  ],
-  "types": "dist/index.d.ts",
   "peerDependencies": {
     "@exadel/esl": "*",
     "eslint": ">=8.0.0"
diff --git a/package-lock.json b/package-lock.json
index 61336cc64..67bcd6eeb 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -3798,11 +3798,6 @@
         "@xtuc/long": "4.2.2"
       }
     },
-    "node_modules/@webcomponents/custom-elements": {
-      "version": "1.6.0",
-      "resolved": "https://registry.npmjs.org/@webcomponents/custom-elements/-/custom-elements-1.6.0.tgz",
-      "integrity": "sha512-CqTpxOlUCPWRNUPZDxT5v2NnHXA4oox612iUGnmTUGQFhZ1Gkj8kirtl/2wcF6MqX7+PqqicZzOCBKKfIn0dww=="
-    },
     "node_modules/@webpack-cli/configtest": {
       "version": "2.1.1",
       "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.1.1.tgz",
@@ -8331,11 +8326,6 @@
         "node": ">=10.13.0"
       }
     },
-    "node_modules/intersection-observer": {
-      "version": "0.12.2",
-      "resolved": "https://registry.npmjs.org/intersection-observer/-/intersection-observer-0.12.2.tgz",
-      "integrity": "sha512-7m1vEcPCxXYI8HqnL8CKI6siDyD+eIWSwgB3DZA+ZTogxk9I4CDnj4wilt9x/+/QbHI4YG5YZNmC6458/e9Ktg=="
-    },
     "node_modules/into-stream": {
       "version": "7.0.0",
       "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-7.0.0.tgz",
@@ -15588,11 +15578,6 @@
         "any-promise": "^0.1.0"
       }
     },
-    "node_modules/promise-polyfill": {
-      "version": "8.3.0",
-      "resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-8.3.0.tgz",
-      "integrity": "sha512-H5oELycFml5yto/atYqmjyigJoAo3+OXwolYiH7OfQuYlAqhxNvTfiNMbV9hsC6Yp83yE5r2KTVmtrG6R9i6Pg=="
-    },
     "node_modules/prompts": {
       "version": "2.4.2",
       "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz",
@@ -19294,7 +19279,7 @@
     "pages": {
       "name": "@exadel/esl-website",
       "version": "5.0.0-beta.2",
-      "license": "ISC",
+      "license": "MIT",
       "dependencies": {
         "@11ty/eleventy": "^2.0.1",
         "@11ty/eleventy-dev-server": "^1.0.4",
@@ -19302,15 +19287,12 @@
         "@juggle/resize-observer": "^3.4.0",
         "@types/prismjs": "^1.26.3",
         "@types/smoothscroll-polyfill": "^0.3.3",
-        "@webcomponents/custom-elements": "1.6.0",
         "clean-css-cli": "^5.6.3",
         "html-minifier-terser": "^7.2.0",
         "image-size": "^1.0.2",
-        "intersection-observer": "^0.12.2",
         "js-yaml": "^4.1.0",
         "out-url": "^1.1.3",
         "prismjs": "^1.29.0",
-        "promise-polyfill": "^8.3.0",
         "smoothscroll-polyfill": "^0.4.4",
         "ts-loader": "^9.5.1",
         "webpack": "^5.89.0",
diff --git a/pages/package.json b/pages/package.json
index f1a54ab5d..6b0d49d81 100644
--- a/pages/package.json
+++ b/pages/package.json
@@ -4,7 +4,7 @@
   "private": true,
   "description": "ESL Website source",
   "homepage": "https://esl-ui.com/",
-  "license": "ISC",
+  "license": "MIT",
   "sideEffects": [
     "pages/src/**/*.ts"
   ],
@@ -30,15 +30,12 @@
     "@juggle/resize-observer": "^3.4.0",
     "@types/prismjs": "^1.26.3",
     "@types/smoothscroll-polyfill": "^0.3.3",
-    "@webcomponents/custom-elements": "1.6.0",
     "clean-css-cli": "^5.6.3",
     "html-minifier-terser": "^7.2.0",
     "image-size": "^1.0.2",
-    "intersection-observer": "^0.12.2",
     "js-yaml": "^4.1.0",
     "out-url": "^1.1.3",
     "prismjs": "^1.29.0",
-    "promise-polyfill": "^8.3.0",
     "smoothscroll-polyfill": "^0.4.4",
     "ts-loader": "^9.5.1",
     "webpack": "^5.89.0",
diff --git a/pages/src/landing/marquee/marquee.ts b/pages/src/landing/marquee/marquee.ts
index 3c705082f..2cd4ef640 100644
--- a/pages/src/landing/marquee/marquee.ts
+++ b/pages/src/landing/marquee/marquee.ts
@@ -1,7 +1,6 @@
 import {ESLBaseElement} from '@exadel/esl/modules/esl-base-element/core';
 import {bind, ready, memoize, attr} from '@exadel/esl/modules/esl-utils/decorators';
 import {range} from '@exadel/esl/modules/esl-utils/misc/array';
-import {isIE} from '@exadel/esl/modules/esl-utils/environment/device-detector';
 
 export class ESLDemoMarquee extends ESLBaseElement {
   static override is = 'esl-d-marquee';
@@ -16,7 +15,6 @@ export class ESLDemoMarquee extends ESLBaseElement {
   @ready
   protected override connectedCallback(): void {
     super.connectedCallback();
-    if (isIE) return;
     this.startAnimation();
   }
   protected override disconnectedCallback(): void {
diff --git a/pages/src/localdev.ts b/pages/src/localdev.ts
index 4567e6eb6..416a1a9ce 100644
--- a/pages/src/localdev.ts
+++ b/pages/src/localdev.ts
@@ -1,7 +1,5 @@
 // Support for ES5 bundle target
 import '@exadel/esl/polyfills/es5-target-shim';
-// Builtin polyfills
-import '@exadel/esl/polyfills/polyfills.es6';
 // Validate environment
 import '@exadel/esl/polyfills/polyfills.validate';
 
diff --git a/pages/src/polyfill-full.ts b/pages/src/polyfill-full.ts
deleted file mode 100644
index 91d4245c2..000000000
--- a/pages/src/polyfill-full.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-/**
- * Target: IE11, Edge\<14
- */
-import 'promise-polyfill/dist/polyfill';
-
-import '@exadel/esl/polyfills/polyfills.es5';
-
-import './polyfill-medium';
diff --git a/pages/src/polyfill-medium.ts b/pages/src/polyfill-medium.ts
deleted file mode 100644
index a75b910c6..000000000
--- a/pages/src/polyfill-medium.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-/**
- * Target: iOS, Safari 13.0.0
- */
-// IE, Edge < 18
-import '@webcomponents/custom-elements/custom-elements.min';
-
-// IE, Safari <12, Edge < 14
-import 'intersection-observer/intersection-observer';
-
-import './polyfill-light';
diff --git a/pages/src/polyfill-light.ts b/pages/src/polyfill.ts
similarity index 100%
rename from pages/src/polyfill-light.ts
rename to pages/src/polyfill.ts
diff --git a/pages/views/_includes/polyfill.njk b/pages/views/_includes/polyfill.njk
index c01d5dc6a..6f5a8bb73 100644
--- a/pages/views/_includes/polyfill.njk
+++ b/pages/views/_includes/polyfill.njk
@@ -1,13 +1,7 @@
 <script>
   (function () {
-    var version = '';
-    if (!window.Promise || !window.CustomEvent){
-      version = 'full';
-    } else if (!('getRootNode' in Element.prototype) || !window.customElements){
-      version = 'medium';
-    } else if (!window.scrollBy || !window.IntersectionObserver || !window.ResizeObserver || !window.ResizeObserver) {
-      version = 'light';
+    if (!window.scrollBy || !window.ResizeObserver) {
+      document.write('<script type="text/javascript" src="{{ '/bundles/polyfill.js' | url }}"><\/script>');
     }
-    (version) && document.write('<script type="text/javascript" src="{{ '/bundles/polyfill-' | url }}' + version + '.js"><\/script>');
   })();
 </script>
diff --git a/pages/views/blogs/ESL v5.0.0 Announce.md b/pages/views/blogs/ESL v5.0.0 Announce.md
deleted file mode 100644
index 2b1681e00..000000000
--- a/pages/views/blogs/ESL v5.0.0 Announce.md
+++ /dev/null
@@ -1,18 +0,0 @@
----
-layout: content
-name: ESL v5.0.0 Announce
-title: Our plans for July
-tags: [news, draft]
-date: 2023-07-01
-containerCls: container markdown-container
----
-
-ESL is going to have a lot of interesting updates soon:
-- `ESLAnimate` will be finally available as a stable module under `4.9.0`
-- `ESLShare` is going to have final updates, `popup` version will be available in stable `4.9.0` and going to be accepted as 
-  a stale module in `5.0.0`. 
-- `ESLCarousel` module is already available under the beta channel and in the process of testing, refactoring, and going
-  to be a stable version soon.
-- `ESLModal` is going to be available for preview in the nearest beta releases
-- First ESL 5.0.0 versions are going to be available on beta branch soon: 
-  IE11 support dropped, event API improvements, styles distribution improvements. 
diff --git a/pages/webpack.config.js b/pages/webpack.config.js
index 9dfaee096..238d54ac7 100644
--- a/pages/webpack.config.js
+++ b/pages/webpack.config.js
@@ -5,9 +5,7 @@ module.exports = {
   devtool: 'source-map',
   entry: {
     'localdev': './src/localdev.ts',
-    'polyfill-full': './src/polyfill-full.ts',
-    'polyfill-medium': './src/polyfill-medium.ts',
-    'polyfill-light': './src/polyfill-light.ts'
+    'polyfill': './src/polyfill.ts'
   },
   resolve: {
     modules: ['../node_modules'],
diff --git a/src/modules/all.ts b/src/modules/all.ts
index adc75b78d..dca9c88c9 100644
--- a/src/modules/all.ts
+++ b/src/modules/all.ts
@@ -1,6 +1,7 @@
 // Core
-export {ESLBaseElement, ESLBaseElementShape} from './esl-base-element/core';
-export {ESLMixinElement} from './esl-mixin-element/core';
+export * from './esl-base-element/core';
+export * from './esl-mixin-element/core';
+
 export * from './esl-utils/all';
 export * from './esl-media-query/core';
 export * from './esl-traversing-query/core';
diff --git a/src/modules/esl-alert/core/esl-alert.less b/src/modules/esl-alert/core/esl-alert.less
index 20e51a89e..36ba5a584 100644
--- a/src/modules/esl-alert/core/esl-alert.less
+++ b/src/modules/esl-alert/core/esl-alert.less
@@ -1,5 +1,3 @@
-@import '../../esl-utils/fixes/ie-fixes';
-
 esl-alert {
   display: flex;
   justify-content: center;
diff --git a/src/modules/esl-alert/core/esl-alert.ts b/src/modules/esl-alert/core/esl-alert.ts
index 559145b07..3412e07de 100644
--- a/src/modules/esl-alert/core/esl-alert.ts
+++ b/src/modules/esl-alert/core/esl-alert.ts
@@ -2,9 +2,7 @@ import {ExportNs} from '../../esl-utils/environment/export-ns';
 import {attr, jsonAttr, prop, listen} from '../../esl-utils/decorators';
 import {isMatches} from '../../esl-utils/dom/traversing';
 import {ESLToggleable} from '../../esl-toggleable/core';
-import {DeviceDetector} from '../../esl-utils/environment/device-detector';
 import {CSSClassUtils} from '../../esl-utils/dom/class';
-import {createZIndexIframe} from '../../esl-utils/fixes/ie-fixes';
 import {ESLTraversingQuery} from '../../esl-traversing-query/core';
 
 import type {ESLToggleableActionParams, ESLToggleableRequestDetails} from '../../esl-toggleable/core';
@@ -96,7 +94,6 @@ export class ESLAlert extends ESLToggleable {
     this.$content.className = 'esl-alert-content';
     this.innerHTML = '';
     this.appendChild(this.$content);
-    if (DeviceDetector.isIE) this.appendChild(createZIndexIframe());
     if (this.target) {
       this.$target = ESLTraversingQuery.first(this.target, this) as EventTarget;
     }
diff --git a/src/modules/esl-base-element/core.ts b/src/modules/esl-base-element/core.ts
index dabbc5d7c..4614966ec 100644
--- a/src/modules/esl-base-element/core.ts
+++ b/src/modules/esl-base-element/core.ts
@@ -1,10 +1,3 @@
 export type {ESLBaseElementShape} from './core/esl-base-element.shape';
 
 export * from './core/esl-base-element';
-
-export * from '../esl-utils/decorators/prop';
-export * from '../esl-utils/decorators/attr';
-export * from '../esl-utils/decorators/bool-attr';
-export * from '../esl-utils/decorators/json-attr';
-
-export * from '../esl-utils/decorators/listen';
diff --git a/src/modules/esl-event-listener/README.md b/src/modules/esl-event-listener/README.md
index a105a798c..826024b7d 100644
--- a/src/modules/esl-event-listener/README.md
+++ b/src/modules/esl-event-listener/README.md
@@ -308,12 +308,6 @@ ESLEventUtils.descriptors(host?: any): ESLListenerDescriptorFn[]
 
 - `host` - object to get auto-collectable descriptors from;
 
-<a name="-esleventutilsdescriptors"></a>
-
-### ⚡ `ESLEventUtils.descriptors`
-
-Deprecated alias for `ESLEventUtils.getAutoDescriptors`
-
 <a name="-esleventutilsinitdescriptor"></a>
 
 ### ⚡ `ESLEventUtils.initDescriptor`
diff --git a/src/modules/esl-event-listener/core/api.ts b/src/modules/esl-event-listener/core/api.ts
index 963afa600..617b3edc2 100644
--- a/src/modules/esl-event-listener/core/api.ts
+++ b/src/modules/esl-event-listener/core/api.ts
@@ -23,9 +23,6 @@ export class ESLEventUtils {
    */
   public static dispatch = dispatchCustomEvent;
 
-  /** @deprecated alias for {@link getAutoDescriptors} */
-  public static descriptors = getAutoDescriptors;
-
   /** Gets {@link ESLListenerDescriptorFn}s of the passed object */
   public static getAutoDescriptors = getAutoDescriptors;
 
@@ -88,11 +85,7 @@ export class ESLEventUtils {
   ): ESLEventListener[] {
     if (arguments.length === 1) {
       const descriptors = getAutoDescriptors(host);
-      // TODO: flatMap when ES5 will be out of support list
-      return descriptors.reduce(
-        (acc, desc) => acc.concat(ESLEventUtils.subscribe(host, desc)),
-        [] as ESLEventListener[]
-      );
+      return descriptors.flatMap((desc) => ESLEventUtils.subscribe(host, desc));
     }
     const desc = typeof eventDesc === 'string' ? {event: eventDesc} : eventDesc as ESLListenerDescriptor;
     if (Object.hasOwnProperty.call(desc, 'condition') && !resolveProperty(desc.condition, host)) return [];
@@ -114,6 +107,3 @@ function emptySubscriptionWarning(host: object, descriptor: ESLListenerDescripto
   const target = resolveProperty(descriptor.target, host);
   console.warn('[ESL]: Empty subscription %o', {host, descriptor, handler, event, target});
 }
-
-/** @deprecated alias for {@link ESLEventUtils} */
-export const EventUtils = ESLEventUtils;
diff --git a/src/modules/esl-footnotes/core/esl-note.ts b/src/modules/esl-footnotes/core/esl-note.ts
index 36f5c5fe5..819e8418d 100644
--- a/src/modules/esl-footnotes/core/esl-note.ts
+++ b/src/modules/esl-footnotes/core/esl-note.ts
@@ -1,6 +1,6 @@
 import {ExportNs} from '../../esl-utils/environment/export-ns';
-import {ESLBaseElement, prop} from '../../esl-base-element/core';
-import {ready, attr, boolAttr, memoize, listen} from '../../esl-utils/decorators';
+import {ESLBaseElement} from '../../esl-base-element/core';
+import {ready, attr, prop, boolAttr, memoize, listen} from '../../esl-utils/decorators';
 import {ESLTooltip} from '../../esl-tooltip/core';
 import {promisifyTimeout, repeatSequence} from '../../esl-utils/async';
 import {ESLEventUtils} from '../../esl-utils/dom/events';
diff --git a/src/modules/esl-forms/esl-select-list/core/esl-select-wrapper.ts b/src/modules/esl-forms/esl-select-list/core/esl-select-wrapper.ts
index ad71a29f6..e965f8976 100644
--- a/src/modules/esl-forms/esl-select-list/core/esl-select-wrapper.ts
+++ b/src/modules/esl-forms/esl-select-list/core/esl-select-wrapper.ts
@@ -1,4 +1,5 @@
-import {ESLBaseElement, listen} from '../../../esl-base-element/core';
+import {ESLBaseElement} from '../../../esl-base-element/core';
+import {listen} from '../../../esl-utils/decorators/listen';
 import {ESLEventUtils} from '../../../esl-utils/dom/events';
 
 /** Interface for option definition */
diff --git a/src/modules/esl-media-query/core/common/screen-breakpoint.ts b/src/modules/esl-media-query/core/common/screen-breakpoint.ts
index d6dfd289a..f08c2d818 100644
--- a/src/modules/esl-media-query/core/common/screen-breakpoint.ts
+++ b/src/modules/esl-media-query/core/common/screen-breakpoint.ts
@@ -43,9 +43,7 @@ export abstract class ESLScreenBreakpoints {
 
   /** All available breakpoints names */
   public static get names(): string[] {
-    const keys: string[] = [];
-    registry.forEach((value, key) => keys.push(key));
-    return keys;
+    return Array.from(registry.keys());
   }
 
   /** @returns breakpoints shortcut replacement */
diff --git a/src/modules/esl-media-query/core/conditions/media-query-base.ts b/src/modules/esl-media-query/core/conditions/media-query-base.ts
index 428f24673..6de981e6b 100644
--- a/src/modules/esl-media-query/core/conditions/media-query-base.ts
+++ b/src/modules/esl-media-query/core/conditions/media-query-base.ts
@@ -2,15 +2,11 @@ export interface IMediaQueryCondition extends EventTarget {
   /** @returns true if current environment satisfies query */
   matches: boolean;
 
-  /** @deprecated alias for `addEventListener` */
-  addListener(cb: EventListener): void;
   /** Subscribes to media query state change. Shortcut for `addEventListener('change', callback)` */
   addEventListener(callback: EventListener): void;
   /** Subscribes to media query state change. Implements {@link EventTarget} interface */
   addEventListener(type: 'change', callback: EventListener): void;
 
-  /** @deprecated alias for `removeEventListener` */
-  removeListener(cb: EventListener): void;
   /** Unsubscribes from media query state change event. Shortcut for `removeEventListener('change', callback)` */
   removeEventListener(callback: EventListener): void;
   /** Unsubscribes from media query state change event. Implements {@link EventTarget} interface */
diff --git a/src/modules/esl-media-query/core/conditions/media-query-const.ts b/src/modules/esl-media-query/core/conditions/media-query-const.ts
index 81f31ae58..2df664684 100644
--- a/src/modules/esl-media-query/core/conditions/media-query-const.ts
+++ b/src/modules/esl-media-query/core/conditions/media-query-const.ts
@@ -14,10 +14,8 @@ class MediaQueryConstCondition implements IMediaQueryCondition {
     return this._matches;
   }
 
-  public addListener(): void {}
   public addEventListener(): void {}
 
-  public removeListener(): void {}
   public removeEventListener(): void {}
 
   public dispatchEvent(): boolean {
diff --git a/src/modules/esl-media-query/core/esl-media-query.ts b/src/modules/esl-media-query/core/esl-media-query.ts
index dc1ab961c..391cca21e 100644
--- a/src/modules/esl-media-query/core/esl-media-query.ts
+++ b/src/modules/esl-media-query/core/esl-media-query.ts
@@ -105,11 +105,6 @@ export abstract class ESLMediaQuery implements IMediaQueryCondition {
   public abstract addEventListener(type: 'change', callback: EventListener): void;
   public abstract removeEventListener(callback: EventListener): void;
   public abstract removeEventListener(type: 'change', callback: EventListener): void;
-
-  /** @deprecated alias for `addEventListener` */
-  public abstract addListener(cb: EventListener): void;
-  /** @deprecated alias for `removeEventListener` */
-  public abstract removeListener(cb: EventListener): void;
 }
 
 // Register otb preprocessors
diff --git a/src/modules/esl-media/core/esl-media-registry.ts b/src/modules/esl-media/core/esl-media-registry.ts
index be595fc6f..773661ffc 100644
--- a/src/modules/esl-media/core/esl-media-registry.ts
+++ b/src/modules/esl-media/core/esl-media-registry.ts
@@ -21,9 +21,7 @@ export class ESLMediaProviderRegistry extends SyntheticEventTarget {
 
   /** List of registered providers */
   public get providers(): ProviderType[] {
-    const list: ProviderType[] = [];
-    this.providersMap.forEach((provider) => list.push(provider));
-    return list;
+    return Array.from(this.providersMap.values());
   }
 
   /** Register provider */
diff --git a/src/modules/esl-media/core/esl-media.shape.ts b/src/modules/esl-media/core/esl-media.shape.ts
index 0dbcfc095..7cd2c7810 100644
--- a/src/modules/esl-media/core/esl-media.shape.ts
+++ b/src/modules/esl-media/core/esl-media.shape.ts
@@ -23,11 +23,6 @@ export type ESLMediaTagShape = ({
   /** Define preferable aspect ratio */
   'aspect-ratio'?: string;
 
-  /**
-   * Define prevent loading marker
-   * @deprecated Use {@link lazy} instead
-   */
-  disabled?: boolean;
   /** Define lazy loading mode */
   lazy?: ESLMediaLazyMode;
   /** Define autoplay marker */
@@ -57,12 +52,10 @@ export type ESLMediaTagShape = ({
 
   /** Define condition {@link ESLMediaQuery} to allow load of media resource */
   'load-condition'?: string;
-  /** Define ESL Traversing Query to find target for load-cls-accepted / load-cls-declined */
-  'load-cls-target'?: string;
-  /** Define class to mark player accepted state */
-  'load-cls-accepted'?: string;
-  /** Define class to mark player declined state */
-  'load-cls-declined'?: string;
+  /** Define class / classes to add when load media is accepted. Supports multiple and inverted classes */
+  'load-condition-class'?: string;
+  /** Defines target element {@link ESLTraversingQuery} selector to toggle `load-condition-class` classes */
+  'load-condition-class-target'?: string;
 
   /** Children are not allowed for ESLMedia */
   children?: never[];
diff --git a/src/modules/esl-media/core/esl-media.ts b/src/modules/esl-media/core/esl-media.ts
index 55a716e36..26a8a5785 100644
--- a/src/modules/esl-media/core/esl-media.ts
+++ b/src/modules/esl-media/core/esl-media.ts
@@ -34,7 +34,6 @@ const parseLazyAttr = (v: string): ESLMediaLazyMode => isLazyAttr(v) ? v : 'auto
 export class ESLMedia extends ESLBaseElement {
   public static override is = 'esl-media';
   public static observedAttributes = [
-    'disabled',
     'load-condition',
     'media-type',
     'media-id',
@@ -76,11 +75,6 @@ export class ESLMedia extends ESLBaseElement {
   @attr() public fillMode: ESLMediaFillMode;
   /** Strict aspect ratio definition */
   @attr() public aspectRatio: string;
-  /**
-   * Disabled marker to prevent rendering
-   * @deprecated replaced with {@link lazy} = "manual" functionality
-   */
-  @boolAttr() public disabled: boolean;
   /** Allows lazy load resource */
   @attr({parser: parseLazyAttr, defaultValue: 'none'}) public lazy: ESLMediaLazyMode;
   /** Autoplay resource marker */
@@ -106,25 +100,9 @@ export class ESLMedia extends ESLBaseElement {
   /** Ready state class/classes target */
   @attr() public readyClassTarget: string;
 
-  /**
-   * Class / classes to add when media is accepted
-   * @deprecated use {@link loadConditionClass} instead (e.g. `load-condition-class="is-accepted"`)
-   */
-  @attr() public loadClsAccepted: string;
-  /**
-   * Class / classes to add when media is declined
-   * @deprecated use {@link loadConditionClass} with negative class param instead (e.g. `load-condition-class="!is-declined"`)
-   */
-  @attr() public loadClsDeclined: string;
-  /**
-   * Target element {@link ESLTraversingQuery} select to add accepted/declined classes
-   * @deprecated used with legacy load condition attributes, consider migration to {@link loadConditionClass}
-   */
-  @attr({defaultValue: '::parent'}) public loadClsTarget: string;
-
   /** Condition {@link ESLMediaQuery} to allow load of media resource. Default: `all` */
   @attr({defaultValue: 'all'}) public loadCondition: string;
-  /** Class / classes to add when load media is accepted */
+  /** Class / classes to add when load media is accepted. Supports multiple and inverted classes */
   @attr() public loadConditionClass: string;
   /** Target element {@link ESLTraversingQuery} select to toggle {@link loadConditionClass} classes */
   @attr({defaultValue: '::parent'}) public loadConditionClassTarget: string;
@@ -173,7 +151,6 @@ export class ESLMedia extends ESLBaseElement {
   protected override attributeChangedCallback(attrName: string, oldVal: string, newVal: string): void {
     if (!this.connected || oldVal === newVal) return;
     switch (attrName) {
-      case 'disabled':
       case 'media-id':
       case 'media-src':
       case 'media-type':
@@ -206,8 +183,7 @@ export class ESLMedia extends ESLBaseElement {
   }
 
   public canActivate(): boolean {
-    if (this.lazy !== 'none' || this.disabled) return false;
-    return this.conditionQuery.matches;
+    return this.lazy === 'none' && this.conditionQuery.matches;
   }
 
   private reinitInstance(): void {
@@ -229,15 +205,8 @@ export class ESLMedia extends ESLBaseElement {
   }
 
   public updateContainerMarkers(): void {
-    const active = this.conditionQuery.matches;
-
     const $target = ESLTraversingQuery.first(this.loadConditionClassTarget, this) as HTMLElement;
-    $target && CSSClassUtils.toggle($target, this.loadConditionClass, active);
-
-    // Legacy attributes support
-    const targetEl = ESLTraversingQuery.first(this.loadClsTarget, this) as HTMLElement;
-    targetEl && CSSClassUtils.toggle(targetEl, this.loadClsAccepted, active);
-    targetEl && CSSClassUtils.toggle(targetEl, this.loadClsDeclined, !active);
+    $target && CSSClassUtils.toggle($target, this.loadConditionClass, this.conditionQuery.matches);
   }
 
   /** Seek to given position of media */
@@ -247,12 +216,11 @@ export class ESLMedia extends ESLBaseElement {
 
   /**
    * Start playing media
-   * @param allowActivate - allows to remove manual lazy or disabled marker
+   * @param allowActivate - allows to remove manual lazy loading restrictions
    */
   public play(allowActivate: boolean = false): Promise<void> | null {
     if (!this.ready && allowActivate) {
       this.lazy = 'none';
-      this.disabled = false;
       this.deferredReinitialize.cancel();
       this.reinitInstance();
     }
diff --git a/src/modules/esl-mixin-element/core.ts b/src/modules/esl-mixin-element/core.ts
index de26e22f8..a1520b480 100644
--- a/src/modules/esl-mixin-element/core.ts
+++ b/src/modules/esl-mixin-element/core.ts
@@ -1,8 +1,1 @@
 export * from './ui/esl-mixin-element';
-
-export * from '../esl-utils/decorators/prop';
-export * from '../esl-utils/decorators/attr';
-export * from '../esl-utils/decorators/bool-attr';
-export * from '../esl-utils/decorators/json-attr';
-
-export * from '../esl-utils/decorators/listen';
diff --git a/src/modules/esl-mixin-element/ui/esl-mixin-registry.ts b/src/modules/esl-mixin-element/ui/esl-mixin-registry.ts
index 1206ca34b..b233a0a8a 100644
--- a/src/modules/esl-mixin-element/ui/esl-mixin-registry.ts
+++ b/src/modules/esl-mixin-element/ui/esl-mixin-registry.ts
@@ -136,7 +136,7 @@ export class ESLMixinRegistry {
   /** @returns all mixins initialized on passed host element */
   public static getAll(el: HTMLElement): ESLMixinElement[] {
     const store = (el as any)[STORE] as Record<string, ESLMixinElement> | undefined;
-    return store ? Object.keys(store).map((key: string) => store[key]) : [];
+    return store ? Object.values(store) : [];
   }
 
   /** @returns if the passed mixin exists on the element */
diff --git a/src/modules/esl-panel-group/core/esl-panel-group.shape.ts b/src/modules/esl-panel-group/core/esl-panel-group.shape.ts
index b41bd2e29..c85378b8c 100644
--- a/src/modules/esl-panel-group/core/esl-panel-group.shape.ts
+++ b/src/modules/esl-panel-group/core/esl-panel-group.shape.ts
@@ -39,9 +39,6 @@ export interface ESLPanelGroupTagShape extends ESLBaseElementShape<ESLPanelGroup
 
   /** Allowed children */
   children?: any;
-
-  /** @deprecated fallback time no longer required */
-  'fallback-duration'?: string | number;
 }
 
 declare global {
diff --git a/src/modules/esl-panel/core/esl-panel.shape.ts b/src/modules/esl-panel/core/esl-panel.shape.ts
index d9a7cd82f..72e6bcf05 100644
--- a/src/modules/esl-panel/core/esl-panel.shape.ts
+++ b/src/modules/esl-panel/core/esl-panel.shape.ts
@@ -15,9 +15,6 @@ export interface ESLPanelTagShape extends ESLToggleableTagShape<ESLPanel> {
 
   /** Define CSS selector of the parent group (default: 'esl-panel-group')*/
   'panel-group-sel'?: string;
-
-  /** @deprecated fallback time is no longer required */
-  'fallback-duration'?: string | number;
 }
 
 declare global {
diff --git a/src/modules/esl-tab/core/esl-tabs.arrows.less b/src/modules/esl-tab/core/esl-tabs.arrows.less
index a83eeebb7..295d61dff 100644
--- a/src/modules/esl-tab/core/esl-tabs.arrows.less
+++ b/src/modules/esl-tab/core/esl-tabs.arrows.less
@@ -19,8 +19,6 @@ esl-tabs[scrollable] {
     border: none;
     opacity: 0.5;
     cursor: pointer;
-
-    // TODO: IE11 support
     appearance: none;
   }
   .arrow-next {
diff --git a/src/modules/esl-toggleable/core/esl-toggleable-dispatcher.ts b/src/modules/esl-toggleable/core/esl-toggleable-dispatcher.ts
index 7fcf82520..fe33bf0b3 100644
--- a/src/modules/esl-toggleable/core/esl-toggleable-dispatcher.ts
+++ b/src/modules/esl-toggleable/core/esl-toggleable-dispatcher.ts
@@ -1,4 +1,5 @@
-import {ESLBaseElement, listen} from '../../esl-base-element/core';
+import {ESLBaseElement} from '../../esl-base-element/core';
+import {listen} from '../../esl-utils/decorators/listen';
 import {getCompositeTarget} from '../../esl-utils/dom/events';
 import {ExportNs} from '../../esl-utils/environment/export-ns';
 import {ESLToggleable} from './esl-toggleable';
diff --git a/src/modules/esl-toggleable/core/esl-toggleable.ts b/src/modules/esl-toggleable/core/esl-toggleable.ts
index 2fffc9d32..b7b380976 100644
--- a/src/modules/esl-toggleable/core/esl-toggleable.ts
+++ b/src/modules/esl-toggleable/core/esl-toggleable.ts
@@ -44,9 +44,6 @@ export interface ESLToggleableRequestDetails extends ESLToggleableActionParams {
   match?: string | ((target: Element) => boolean);
 }
 
-/** @deprecated alias for ESLToggleableActionParams, will be removed in 5.0.0*/
-export type ToggleableActionParams = ESLToggleableActionParams;
-
 const activators: WeakMap<ESLToggleable, HTMLElement | undefined> = new WeakMap();
 
 /**
diff --git a/src/modules/esl-traversing-query/core/esl-traversing-query.ts b/src/modules/esl-traversing-query/core/esl-traversing-query.ts
index fb40cc609..50cb9f7a4 100644
--- a/src/modules/esl-traversing-query/core/esl-traversing-query.ts
+++ b/src/modules/esl-traversing-query/core/esl-traversing-query.ts
@@ -145,9 +145,6 @@ export class ESLTraversingQuery {
   }
 }
 
-/** @deprecated alias for {@link ESLTraversingQuery} */
-export const TraversingQuery = ESLTraversingQuery;
-
 declare global {
   export interface ESLLibrary {
     TraversingQuery: typeof ESLTraversingQuery;
diff --git a/src/modules/esl-utils/dom/events/target.ts b/src/modules/esl-utils/dom/events/target.ts
index ee1a03f53..45c31b863 100644
--- a/src/modules/esl-utils/dom/events/target.ts
+++ b/src/modules/esl-utils/dom/events/target.ts
@@ -1,4 +1,4 @@
-import {flat, uniq} from '../../misc/array';
+import {uniq} from '../../misc/array';
 import {overrideEvent} from './misc';
 
 /** Key to store listeners on the {@link SyntheticEventTarget} instance*/
@@ -10,7 +10,7 @@ const LISTENERS: unique symbol  = (window.Symbol || String)('_listeners') as any
  * Doesn't give explicit access to callback storage
  */
 export class SyntheticEventTarget implements EventTarget {
-  // Event type to use in the shortcutted calls
+  // Event type to use in the shortcut calls
   public static DEFAULT_EVENT = 'change';
 
   private readonly [LISTENERS]: Record<string, EventListenerOrEventListenerObject[]> = {};
@@ -18,19 +18,13 @@ export class SyntheticEventTarget implements EventTarget {
   protected getEventListeners(): EventListenerOrEventListenerObject[];
   protected getEventListeners(type: string): EventListenerOrEventListenerObject[];
   protected getEventListeners(type?: string): EventListenerOrEventListenerObject[] {
-    if (typeof type !== 'string') return uniq(flat(Object.values(this[LISTENERS])));
+    if (typeof type !== 'string') return uniq(Object.values(this[LISTENERS]).flat(1));
     return this[LISTENERS][type] || [];
   }
 
-  public hasEventListener(): boolean;
-  public hasEventListener(type: string): boolean;
-  /** @deprecated alias for `addEventListener` */
-  public hasEventListener(type: string | number): boolean;
-  /** @deprecated alias for `addEventListener` */
-  public hasEventListener(type: string, minCount: number): boolean;
-  public hasEventListener(type: string | number = 'change', minCount: number = 0): boolean {
-    if (typeof type !== 'string') return this.hasEventListener('change', type || 0); // TODO: remove in 5.0.0
-    return this.getEventListeners(type).length > minCount;
+  public hasEventListener(type?: string): boolean {
+    if (typeof type === 'string') return this.getEventListeners(type).length > 0;
+    return this.hasEventListener((this.constructor as typeof SyntheticEventTarget).DEFAULT_EVENT);
   }
 
   public addEventListener(callback: EventListenerOrEventListenerObject): void;
@@ -56,29 +50,16 @@ export class SyntheticEventTarget implements EventTarget {
     this[LISTENERS][type] = listeners.filter((cb) => cb !== callback);
   }
 
-  public dispatchEvent(e: Event): boolean;
-  /** @deprecated use `overrideEvent` to declare `target` */
-  public dispatchEvent(e: Event, target?: EventTarget): boolean;
-  public dispatchEvent(e: Event, target?: EventTarget): boolean {
+  public dispatchEvent(e: Event): boolean {
     overrideEvent(e, 'currentTarget', this);
-    if (target) overrideEvent(e, 'target', target); // TODO: remove in 5.0.0
     if (!e.target) overrideEvent(e, 'target', this);
-    if (!e.srcElement) overrideEvent(e, 'srcElement', e.target); // TODO: remove in 5.0.0
+    if (!e.srcElement) overrideEvent(e, 'srcElement', e.target);
     this.getEventListeners(e.type).forEach((listener) => {
       if (typeof listener === 'function') listener.call(this, e);
       else listener.handleEvent.call(listener, e);
     });
     return e.defaultPrevented;
   }
-
-  /** @deprecated alias for `addEventListener` */
-  public addListener(cb: EventListener): void {
-    this.addEventListener(cb);
-  }
-  /** @deprecated alias for `removeEventListener` */
-  public removeListener(cb: EventListener): void {
-    this.removeEventListener(cb);
-  }
 }
 
 function validateEventListenerType(callback: any): void | never {
diff --git a/src/modules/esl-utils/dom/rtl.ts b/src/modules/esl-utils/dom/rtl.ts
index ef011694c..d6e1cd2d1 100644
--- a/src/modules/esl-utils/dom/rtl.ts
+++ b/src/modules/esl-utils/dom/rtl.ts
@@ -54,17 +54,3 @@ export const normalizeScrollLeft = (el: HTMLElement, value: number | null = null
       return value;
   }
 };
-
-/** @deprecated use separate functions from the module */
-export abstract class RTLUtils {
-  /** @deprecated use {@link isRTL} instead */
-  static readonly isRtl = isRTL;
-
-  /** @deprecated use {@link normalizeScrollLeft} instead */
-  static readonly normalizeScrollLeft = normalizeScrollLeft;
-
-  /** @deprecated use {@link RTLScroll}.type instead */
-  static get scrollType(): ScrollType {
-    return RTLScroll.type;
-  }
-}
diff --git a/src/modules/esl-utils/dom/traversing.ts b/src/modules/esl-utils/dom/traversing.ts
index a0c5cd286..f9934f1a1 100644
--- a/src/modules/esl-utils/dom/traversing.ts
+++ b/src/modules/esl-utils/dom/traversing.ts
@@ -72,18 +72,3 @@ export const findNextLooped =
  */
 export const findPrevLooped =
   createSequenceFinder((el) => el.previousElementSibling || (el.parentElement && el.parentElement.lastElementChild));
-
-/** @deprecated Cumulative traversing utility set */
-export abstract class TraversingUtils {
-  static isRelative = isRelativeNode;
-  static closestBy = findClosestBy;
-  static createSequenceFinder = createSequenceFinder;
-
-  static findNext = findNext;
-  static findPrev = findPrev;
-  static findParent = findParent;
-  static findClosest = findClosest;
-
-  static findAll = findAll;
-  static findChildren = findChildren;
-}
diff --git a/src/modules/esl-utils/fixes.ts b/src/modules/esl-utils/fixes.ts
index 5ea3790a0..6265f18a8 100644
--- a/src/modules/esl-utils/fixes.ts
+++ b/src/modules/esl-utils/fixes.ts
@@ -1,2 +1,1 @@
-export * from './fixes/ie-fixes';
 export * from './fixes/viewport';
diff --git a/src/modules/esl-utils/fixes/ie-fixes.less b/src/modules/esl-utils/fixes/ie-fixes.less
deleted file mode 100644
index f1b197cdb..000000000
--- a/src/modules/esl-utils/fixes/ie-fixes.less
+++ /dev/null
@@ -1,10 +0,0 @@
-.ie-zindex-fix {
-  position: absolute;
-  top: 0;
-  left: 0;
-  height: 100%;
-  width: 100%;
-  z-index: -1;
-  border: none;
-  visibility: hidden;
-}
diff --git a/src/modules/esl-utils/fixes/ie-fixes.ts b/src/modules/esl-utils/fixes/ie-fixes.ts
deleted file mode 100644
index 8f5980081..000000000
--- a/src/modules/esl-utils/fixes/ie-fixes.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-/** Fix IE browser to allow to display alert under iframe */
-export function createZIndexIframe(): HTMLElement {
-  const iframe = document.createElement('iframe');
-  iframe.className = 'ie-zindex-fix';
-  iframe.src = 'about:blank';
-  return iframe;
-}
diff --git a/src/modules/esl-utils/fixes/viewport.ts b/src/modules/esl-utils/fixes/viewport.ts
index b96e0ab29..87c8d0cda 100644
--- a/src/modules/esl-utils/fixes/viewport.ts
+++ b/src/modules/esl-utils/fixes/viewport.ts
@@ -1,5 +1,4 @@
 import {rafDecorator} from '../async/raf';
-import {DeviceDetector} from '../environment/device-detector';
 
 /**
  * Small utility to provide 100vw and 100vh alternative CSS Variables
@@ -12,9 +11,6 @@ export class ESLVSizeCSSProxy {
 
   /** Inits custom CSS variables for viewport sizes and it's observation */
   public static observe(): void {
-    // IE doesn’t support CSS Variables (hopefully same as 100vh issue :D)
-    if (DeviceDetector.isIE) return;
-
     this.update();
     window.addEventListener('resize', this.updateDebounced);
   }
diff --git a/src/modules/esl-utils/misc/array.ts b/src/modules/esl-utils/misc/array.ts
index 4662ed428..3e1eaa869 100644
--- a/src/modules/esl-utils/misc/array.ts
+++ b/src/modules/esl-utils/misc/array.ts
@@ -10,9 +10,11 @@ export const tuple = <T>(arr: T[]): Tuple<T>[] => arr.reduce((acc: Tuple<T>[], e
   return acc;
 }, []);
 
-/** Flat array - unwraps one level of nested arrays */
-export const flat = <T>(arr: (null | T | T[])[]): T[] =>
-  arr.reduce((acc: T[], el) => el ? acc.concat(el) : acc, []) as T[];
+/**
+ * Flat array - unwraps one level of nested arrays
+ * @deprecated use `Array.prototype.flat` instead
+ */
+export const flat = <T>(arr: (null | T | T[])[]): T[] => arr.flat(1) as T[];
 
 /** Wraps passed object or primitive to array */
 export const wrap = <T>(arr: undefined | null | T | T[]): T[] => {
@@ -36,14 +38,8 @@ export function unwrap(value: any): any {
 }
 
 /** Makes array values unique */
-export const uniq = <T> (arr: T[]): T[] => {
-  if (arr.length < 2) return arr.slice(0);
-  const result: T[] = [];
-  const set = new Set<T>();
-  arr.forEach((item) => set.add(item));
-  set.forEach((item) => result.push(item));
-  return result;
-};
+export const uniq = <T> (arr: T[]): T[] =>
+  arr.length > 1 ? [...new Set<T>(arr)] : arr.slice(0);
 
 /** Create an array filled with the range [0,..,N-1] */
 export function range(n: number): number[];
diff --git a/src/modules/esl-utils/misc/object/compare.ts b/src/modules/esl-utils/misc/object/compare.ts
index 86177795d..0c7af5d58 100644
--- a/src/modules/esl-utils/misc/object/compare.ts
+++ b/src/modules/esl-utils/misc/object/compare.ts
@@ -13,9 +13,6 @@ export function isEqual(obj1: any, obj2: any): boolean {
   return false;
 }
 
-/** @deprecated alias for `isEqual` method */
-export const deepCompare = isEqual;
-
 /** Check if arr and arr mask has intersection */
 function isIntersect(arrObj: any[], arrMask: any[], comparer: (a: any, b: any) => boolean): boolean {
   return arrMask.every((key) => arrObj.some((itm) => comparer(itm, key)));
diff --git a/src/modules/esl-utils/misc/set.ts b/src/modules/esl-utils/misc/set.ts
index 05ff92074..b66d85fb1 100644
--- a/src/modules/esl-utils/misc/set.ts
+++ b/src/modules/esl-utils/misc/set.ts
@@ -8,11 +8,9 @@ export function intersection<T>(a: T[], b: T[], ...rest: T[][]): T[] {
 
 /** Create an array with unique values from each of the passed arrays */
 export function union<T>(...rest: T[][]): T[]  {
-  const set = new Set();
+  const set = new Set<T>();
   rest.forEach((item) => item.forEach((i) => set.add(i)));
-  const result: any[] = [];
-  set.forEach((value) => result.push(value));
-  return result;
+  return [...set];
 }
 
 /** Creates an array of unique values from the first array that are not present in the other arrays */
diff --git a/src/modules/esl-utils/misc/uid.ts b/src/modules/esl-utils/misc/uid.ts
index d43d1ca53..721ba0de6 100644
--- a/src/modules/esl-utils/misc/uid.ts
+++ b/src/modules/esl-utils/misc/uid.ts
@@ -22,9 +22,3 @@ export const randUID = (prefix: string = ''): string => {
   const rand = Math.round(Math.random() * 1024 * 1024).toString(32);
   return prefix + time + '-' + rand;
 };
-
-/**
- * Generate unique id
- * @deprecated Alias for {@link randUID}
- */
-export const generateUId = randUID;
diff --git a/src/polyfills/list/dom.element.closest.ts b/src/polyfills/list/dom.element.closest.ts
deleted file mode 100644
index 45ca9868b..000000000
--- a/src/polyfills/list/dom.element.closest.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-/**
- * Group: DOM API shims ~ ES6 shim group
- * Target Browsers: `IE11`, `Edge < 14`
- * Element.closest polyfill
- */
-(function (e: ElementEx): void {
-  e.matches = e.matches || e.msMatchesSelector || e.mozMatchesSelector || e.webkitMatchesSelector;
-  e.closest = e.closest || function (css: string): Element | null {
-    // eslint-disable-next-line @typescript-eslint/no-this-alias
-    let node = this;
-    while (node) {
-      if (node.matches(css)) return node;
-      node = node.parentElement;
-    }
-    return null;
-  };
-})(Element.prototype as ElementEx);
-
-interface ElementEx extends Element {
-  msMatchesSelector?(selectors: string): boolean;
-  mozMatchesSelector?(selectors: string): boolean;
-}
diff --git a/src/polyfills/list/dom.element.toggleAttribute.ts b/src/polyfills/list/dom.element.toggleAttribute.ts
deleted file mode 100644
index fc794a33b..000000000
--- a/src/polyfills/list/dom.element.toggleAttribute.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-/**
- * Group: DOM API shims
- * Target Browsers: `IE11`, `Edge < 18`, `Safari < 13`
- * Element.toggleAttribute polyfill
- */
-if (!Element.prototype.toggleAttribute) {
-  Element.prototype.toggleAttribute = function (name: string, force: boolean | undefined): boolean {
-    const has = this.hasAttribute(name);
-    const state = force === void 0 ? !has : !!force;
-
-    if (has !== state) {
-      (state) ? this.setAttribute(name, '') : this.removeAttribute(name);
-    }
-    return state;
-  };
-}
diff --git a/src/polyfills/list/dom.event.base.ts b/src/polyfills/list/dom.event.base.ts
deleted file mode 100644
index 49ad7c8fa..000000000
--- a/src/polyfills/list/dom.event.base.ts
+++ /dev/null
@@ -1,40 +0,0 @@
-/**
- * Group: DOM API shims
- * Target Browsers: `IE11`, `Edge < 18`
- * Event constructor & prevent default polyfill
- */
-try {
-  new window.Event('event', {bubbles: true, cancelable: true});
-} catch (error) {
-  const EventOriginal = window.CustomEvent || window.Event;
-  const preventDefaultOriginal = EventOriginal.prototype.preventDefault;
-  const Event = function (eventName: string, params: EventInit): Event {
-    params = params || {};
-    const event = document.createEvent('Event');
-    event.initEvent(
-      eventName,
-      (params.bubbles === void 0) ? false : params.bubbles,
-      (params.cancelable === void 0) ? false : params.cancelable
-    );
-    return event;
-  };
-  Event.prototype = EventOriginal.prototype;
-  // defaultPrevented is broken in IE.
-  // https://connect.microsoft.com/IE/feedback/details/790389/event-defaultprevented-returns-false-after-preventdefault-was-called
-  Event.prototype.preventDefault = function (): void {
-    if (!this.cancelable) return;
-    preventDefaultOriginal.call(this);
-    Object.defineProperty(this, 'defaultPrevented', {
-      get() {
-        return true;
-      },
-      configurable: true
-    });
-  };
-  Object.defineProperty(window, 'Event', {
-    value: Event,
-    writable: true,
-    configurable: true
-  });
-}
-
diff --git a/src/polyfills/list/dom.event.custom.ts b/src/polyfills/list/dom.event.custom.ts
deleted file mode 100644
index de39ee31a..000000000
--- a/src/polyfills/list/dom.event.custom.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-/**
- * Group: DOM API shims
- * Target Browsers: `IE11`, `Edge < 18`
- * CustomEvent polyfill
- */
-try {
-  new window.CustomEvent('event', {bubbles: true, cancelable: true});
-} catch (error) {
-  const CustomEventOriginal = window.CustomEvent || window.Event;
-  const CustomEvent = function (eventName: string, params: CustomEventInit): CustomEvent {
-    params = params || {};
-    const event = document.createEvent('CustomEvent');
-    event.initCustomEvent(
-      eventName,
-      (params.bubbles === void 0) ? false : params.bubbles,
-      (params.cancelable === void 0) ? false : params.cancelable,
-      (params.detail === void 0) ? {} : params.detail
-    );
-    return event;
-  };
-  CustomEvent.prototype = CustomEventOriginal.prototype;
-  Object.defineProperty(window, 'CustomEvent', {
-    value: CustomEvent,
-    writable: true,
-    configurable: true
-  });
-}
diff --git a/src/polyfills/list/dom.event.focus.ts b/src/polyfills/list/dom.event.focus.ts
deleted file mode 100644
index 7c2a28801..000000000
--- a/src/polyfills/list/dom.event.focus.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-/**
- * Group: DOM API shims
- * Target Browsers: `IE11`, `Edge < 18`
- * FocusEvent polyfill: https://developer.mozilla.org/en-US/docs/Web/API/FocusEvent/FocusEvent
- */
-try {
-  new window.FocusEvent('event', {bubbles: true, cancelable: true});
-} catch (error) {
-  const FocusEventOriginal = window.FocusEvent || window.Event;
-  const FocusEvent = function (eventName: string, params: FocusEventInit): FocusEvent {
-    params = params || {};
-    const event: any = document.createEvent('FocusEvent');
-
-    // https://msdn.microsoft.com/en-us/library/ff975954(v=vs.85).aspx
-    event.initFocusEvent(
-      eventName,
-      (params.bubbles === void 0) ? false : params.bubbles,
-      (params.cancelable === void 0) ? false : params.cancelable,
-      (params.view === void 0) ? window : params.view,
-      (params.detail === void 0) ? {} : params.detail,
-      (params.relatedTarget === void 0) ? null : params.relatedTarget
-    );
-
-    return event;
-  };
-  FocusEvent.prototype = FocusEventOriginal.prototype;
-  Object.defineProperty(window, 'FocusEvent', {
-    value: FocusEvent,
-    writable: true,
-    configurable: true
-  });
-}
diff --git a/src/polyfills/list/dom.event.keyboard.ts b/src/polyfills/list/dom.event.keyboard.ts
deleted file mode 100644
index ae16d6b55..000000000
--- a/src/polyfills/list/dom.event.keyboard.ts
+++ /dev/null
@@ -1,43 +0,0 @@
-/**
- * Group: DOM API shims
- * Target Browsers: `IE11`, `Edge < 18`
- * KeyboardEvent polyfill: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/KeyboardEvent
- */
-try {
-  new window.KeyboardEvent('event', {bubbles: true, cancelable: true});
-} catch (error) {
-  const KeyboardEventOriginal = window.KeyboardEvent || window.Event;
-  // eslint-disable-next-line sonarjs/cognitive-complexity
-  const KeyboardEvent = function (eventName: string, params: KeyboardEventInit): KeyboardEvent {
-    params = params || {};
-    const event: any = document.createEvent('KeyboardEvent');
-
-    // https://msdn.microsoft.com/en-us/library/ff975297(v=vs.85).aspx
-    event.initKeyboardEvent(
-      eventName,
-      (params.bubbles === void 0) ? false : params.bubbles,
-      (params.cancelable === void 0) ? false : params.cancelable,
-      (params.view === void 0) ? window : params.view,
-      (params.key === void 0) ? '' : params.key,
-      (params.location === void 0) ? 0 : params.location,
-      params.ctrlKey === true,
-      params.altKey === true,
-      params.shiftKey === true,
-      params.metaKey === true
-    );
-
-    event.keyCode   = (params.keyCode === void 0) ? 0 : params.keyCode;
-    event.code      = (params.code === void 0) ? '' : params.code;
-    event.charCode  = (params.charCode === void 0) ? 0 : params.charCode;
-    event.char      = (params.charCode === void 0) ? '' : params.charCode;
-    event.which     = (params.which === void 0) ? 0 : params.which;
-
-    return event;
-  };
-  KeyboardEvent.prototype = KeyboardEventOriginal.prototype;
-  Object.defineProperty(window, 'KeyboardEvent', {
-    value: KeyboardEvent,
-    writable: true,
-    configurable: true
-  });
-}
diff --git a/src/polyfills/list/dom.event.mouse.ts b/src/polyfills/list/dom.event.mouse.ts
deleted file mode 100644
index fc2c5ec39..000000000
--- a/src/polyfills/list/dom.event.mouse.ts
+++ /dev/null
@@ -1,45 +0,0 @@
-/**
- * Group: DOM API shims
- * Target Browsers: `IE11`, `Edge < 18`
- * MouseEvent polyfill: https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/MouseEvent
- */
-try {
-  new window.MouseEvent('event', {bubbles: true, cancelable: true});
-} catch (error) {
-  const MouseEventOriginal = window.MouseEvent || window.Event;
-  // eslint-disable-next-line sonarjs/cognitive-complexity
-  const MouseEvent = function (eventName: string, params: MouseEventInit): MouseEvent {
-    params = params || {};
-    const event: any = document.createEvent('MouseEvent');
-
-    // https://msdn.microsoft.com/en-us/library/ff975292(v=vs.85).aspx
-    event.initMouseEvent(
-      eventName,
-      (params.bubbles === void 0) ? false : params.bubbles,
-      (params.cancelable === void 0) ? false : params.cancelable,
-      (params.view === void 0) ? window : params.view!,
-      (params.detail === void 0) ? 0 : params.detail,
-      (params.screenX === void 0) ? 0 : params.screenX,
-      (params.screenY === void 0) ? 0 : params.screenY,
-      (params.clientX === void 0) ? 0 : params.clientX,
-      (params.clientY === void 0) ? 0 : params.clientY,
-      (params.ctrlKey === void 0) ? false : params.ctrlKey,
-      (params.altKey === void 0) ? false : params.altKey,
-      (params.shiftKey === void 0) ? false : params.shiftKey,
-      (params.metaKey === void 0) ? false : params.metaKey,
-      (params.button === void 0) ? 0 : params.button,
-      (params.relatedTarget === void 0) ? null : params.relatedTarget
-    );
-
-    event.buttons = (params.buttons === void 0) ? 0 : params.buttons;
-    event.region  = ((params as any).region === void 0) ? null : (params as any).region;
-
-    return event;
-  };
-  MouseEvent.prototype = MouseEventOriginal.prototype;
-  Object.defineProperty(window, 'MouseEvent', {
-    value: MouseEvent,
-    writable: true,
-    configurable: true
-  });
-}
diff --git a/src/polyfills/list/dom.keyboard.key.ts b/src/polyfills/list/dom.keyboard.key.ts
deleted file mode 100644
index 984846913..000000000
--- a/src/polyfills/list/dom.keyboard.key.ts
+++ /dev/null
@@ -1,38 +0,0 @@
-/**
- * Group: DOM API shims
- * Target Browsers: `IE11`, `Edge < 18`
- * KeyboardEvent.prototype.key shim to normalize key values to W3C spec
- * Based on published shim https://www.npmjs.com/package/shim-keyboard-event-key
- */
-(function (KeyboardEventProto): void {
-  const desc = Object.getOwnPropertyDescriptor(KeyboardEventProto, 'key');
-  if (!desc) return;
-
-  const keyMap: Record<string, string> = {
-    Win: 'Meta',
-    Scroll: 'ScrollLock',
-    Spacebar: ' ',
-
-    Down: 'ArrowDown',
-    Left: 'ArrowLeft',
-    Right: 'ArrowRight',
-    Up: 'ArrowUp',
-
-    Del: 'Delete',
-    Apps: 'ContextMenu',
-    Esc: 'Escape',
-
-    Multiply: '*',
-    Add: '+',
-    Subtract: '-',
-    Decimal: '.',
-    Divide: '/',
-  };
-
-  Object.defineProperty(KeyboardEventProto, 'key', {
-    get() {
-      const key = desc.get!.call(this);
-      return Object.prototype.hasOwnProperty.call(keyMap, key) ? keyMap[key] : key;
-    },
-  });
-})(KeyboardEvent.prototype);
diff --git a/src/polyfills/list/dom.node.isConnected.ts b/src/polyfills/list/dom.node.isConnected.ts
deleted file mode 100644
index 3044301e6..000000000
--- a/src/polyfills/list/dom.node.isConnected.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-/**
- * Group: DOM polyfills
- * Target browsers: `IE11`, `Edge <= 18`
- * Node.isConnected polyfill
- */
-if (!('isConnected' in Node.prototype)) {
-  Object.defineProperty(Node.prototype, 'isConnected', {
-    get() {
-      return !this.ownerDocument ||
-        // eslint-disable-next-line no-bitwise
-        !(this.ownerDocument.compareDocumentPosition(this) & this.DOCUMENT_POSITION_DISCONNECTED);
-    }
-  });
-}
diff --git a/src/polyfills/list/dom.tokenList.toggle.ts b/src/polyfills/list/dom.tokenList.toggle.ts
deleted file mode 100644
index 67257dcab..000000000
--- a/src/polyfills/list/dom.tokenList.toggle.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-/**
- * Group: ES6 shims
- * Target Browsers: `IE11`
- * DOMTokenList.toggle polyfill to support force option
- */
-(function (DOMTokenListProto): void {
-  const nativeToggle = DOMTokenListProto.toggle;
-  const testTokenList = window.document.createElement('div').classList;
-
-  if (!DOMTokenListProto.toggle) return;
-  // eslint-disable-next-line
-  if (testTokenList.toggle('a', false) === false) return;
-
-  DOMTokenListProto.toggle = function toggle(val: string, force?: boolean): boolean {
-    if (arguments.length > 1) {
-      this[force ? 'add' : 'remove'](val);
-      return !!force;
-    }
-    return nativeToggle.call(this, val);
-  };
-})(window.DOMTokenList.prototype);
diff --git a/src/polyfills/list/es6.array.find.ts b/src/polyfills/list/es6.array.find.ts
deleted file mode 100644
index 62a6d79fc..000000000
--- a/src/polyfills/list/es6.array.find.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-type FindPredicate<T> = (T: any, index: number, array: T[]) => boolean;
-
-/**
- * Group: ES6 shim
- * Target Browsers: `IE11`, `Edge < 14`
- * Array.prototype.findIndex && Array.prototype.findIndex
- */
-if (typeof Array.prototype.findIndex !== 'function') {
-  const findHelper = function <T> (array: T[], predicate: FindPredicate<T>, context: any): {item: T | null, index: number} {
-    if (typeof predicate !== 'function') throw new TypeError('predicate must be a function');
-
-    const list = Object(array);
-    // eslint-disable-next-line no-bitwise
-    const length = list.length >>> 0;
-
-    for (let i = 0; i < length; i++) {
-      if (predicate.call(context, list[i], i, list)) {
-        return {item: list[i], index: i};
-      }
-    }
-    return {item: null, index: -1};
-  };
-
-  Array.prototype.find = function <T> (predicate: FindPredicate<T>, context: any = window): T | null {
-    if (!this) throw new TypeError('Array.prototype.find called on null or undefined');
-    return findHelper(this, predicate, context).item;
-  };
-  Array.prototype.findIndex = function <T> (predicate: FindPredicate<T>, context: any = window): number {
-    if (!this) throw new TypeError('Array.prototype.findIndex called on null or undefined');
-    return findHelper(this, predicate, context).index;
-  };
-}
diff --git a/src/polyfills/list/es6.array.from.ts b/src/polyfills/list/es6.array.from.ts
deleted file mode 100644
index 05ac0684c..000000000
--- a/src/polyfills/list/es6.array.from.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-/**
- * Group: ES6 shim
- * Target Browsers: `IE11`
- * Array.from polyfill
- * Note: does not support Iterables, consider polyfill from https://github.com/rousan/symbol-es6
- */
-if (!Array.from) {
-  Array.from = function <T>(object: ArrayLike<T>): T[] {
-    return [].slice.call(object);
-  };
-}
diff --git a/src/polyfills/list/es6.array.includes.ts b/src/polyfills/list/es6.array.includes.ts
deleted file mode 100644
index 1a7475b1e..000000000
--- a/src/polyfills/list/es6.array.includes.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-/**
- * Group: ES6 shim
- * Target Browsers: `IE11`, `Edge < 14`
- * Array.prototype.includes polyfill
- */
-if (!Array.prototype.includes) {
-  Array.prototype.includes = function <T> (item: T, fromIndex?: number): boolean {
-    if (item !== item) {
-      // NaN case
-      return Array.prototype.slice.call(this, fromIndex).some((i: T) => i !== i);
-    }
-    return Array.prototype.indexOf.call(this, item, fromIndex) !== -1;
-  };
-}
diff --git a/src/polyfills/list/es6.object.assign.ts b/src/polyfills/list/es6.object.assign.ts
deleted file mode 100644
index 61aaa5576..000000000
--- a/src/polyfills/list/es6.object.assign.ts
+++ /dev/null
@@ -1,38 +0,0 @@
-/**
- * Group: ES6 shim
- * Target Browsers: `IE11`
- * Object.assign polyfill
- */
-function toObject(value: any): any {
-  if (value === undefined || value === null) throw new TypeError('Cannot convert undefined or null to object');
-  if (typeof value === 'boolean') return new Boolean(value);
-  if (typeof value === 'number') return new Number(value);
-  if (typeof value === 'string') return new String(value);
-  if (typeof value === 'symbol') return Object(value);
-  return value;
-}
-
-function copyProperty(from: any, to: any, name: string): void {
-  const desc = Object.getOwnPropertyDescriptor(from, name);
-  if (desc !== undefined && desc.enumerable) to[name] = from[name];
-}
-
-if (!Object.assign) {
-  Object.assign = function assign(target: any, ...sources: any[]): any {
-    if (this instanceof Object.assign) throw new TypeError('Invoked as a constructor');
-    const to = toObject(target);
-    for (let i = 0; i < sources.length; i++) {
-      const nextSource = sources[i];
-      if (nextSource !== undefined && nextSource !== null) {
-        const from = toObject(nextSource);
-        const keys = Object.getOwnPropertyNames(from);
-        keys.forEach(copyProperty.bind(null, from, to));
-        if (Object.getOwnPropertySymbols) {
-          const symbols = Object.getOwnPropertySymbols(from);
-          symbols.forEach(copyProperty.bind(null, from, to));
-        }
-      }
-    }
-    return to;
-  };
-}
diff --git a/src/polyfills/list/es6.object.is.ts b/src/polyfills/list/es6.object.is.ts
deleted file mode 100644
index e34fba24c..000000000
--- a/src/polyfills/list/es6.object.is.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-/**
- * Group: ES6 shims
- * Target Browsers: `IE11`
- * Object.is support
- * Blatantly stolen from ES6 specs. Kept here for reference.
- * http://wiki.ecmascript.org/doku.php?id=harmony:egal
- */
-if (!Object.is) {
-  Object.defineProperty(Object, 'is', {
-    value(x: any, y: any) {
-      return (x === y) ? (x !== 0 || 1 / x === 1 / y) : (x !== x && y !== y);
-    },
-    enumerable: false
-  });
-}
diff --git a/src/polyfills/polyfills.es5.ts b/src/polyfills/polyfills.es5.ts
deleted file mode 100644
index 8256d1c08..000000000
--- a/src/polyfills/polyfills.es5.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import './list/es6.object.is';
-import './list/es6.object.assign';
-
-import './list/es6.array.from';
-import './list/es6.array.find';
-import './list/es6.array.includes';
-
-import './list/dom.event.base';
-import './list/dom.event.custom';
-import './list/dom.event.mouse';
-import './list/dom.event.keyboard';
-import './list/dom.event.focus';
-
-import './list/dom.element.closest';
-import './list/dom.tokenList.toggle';
diff --git a/src/polyfills/polyfills.es6.ts b/src/polyfills/polyfills.es6.ts
deleted file mode 100644
index 075c9002f..000000000
--- a/src/polyfills/polyfills.es6.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-import './list/dom.node.isConnected';
-import './list/dom.keyboard.key';
-import './list/dom.element.toggleAttribute';


Test patch:
diff --git a/src/modules/esl-base-element/test/element.listener.test.ts b/src/modules/esl-base-element/test/element.listener.test.ts
index 3daa05818..259834c44 100644
--- a/src/modules/esl-base-element/test/element.listener.test.ts
+++ b/src/modules/esl-base-element/test/element.listener.test.ts
@@ -1,6 +1,7 @@
 import '../../../polyfills/es5-target-shim';
 
-import {ESLBaseElement, listen} from '../core';
+import {ESLBaseElement} from '../core';
+import {listen} from '../../esl-utils/decorators/listen';
 import {randUID} from '../../esl-utils/misc/uid';
 import {ESLEventUtils} from '../../esl-utils/dom/events';
 
diff --git a/src/modules/esl-media/test/esl-media.condition.test.ts b/src/modules/esl-media/test/esl-media.condition.test.ts
index 769eabc55..f5d4c0dec 100644
--- a/src/modules/esl-media/test/esl-media.condition.test.ts
+++ b/src/modules/esl-media/test/esl-media.condition.test.ts
@@ -44,67 +44,6 @@ describe('esl-media: lazy loading unit tests', () => {
     expect($media.querySelectorAll('*')).not.toEqual(expect.objectContaining({length: 0}));
   });
 
-  describe('ESLMedia legacy load condition classes', () => {
-    test('ESLMedia declining condition leads to "declined" class on the target', async () => {
-      const $media = createMedia({
-        loadCondition: 'not all',
-        loadClsTarget: 'body',
-        loadClsAccepted: 'accepted',
-        loadClsDeclined: 'declined'
-      });
-      document.body.appendChild($media);
-
-      await promisifyTimeout(100);
-      expect(document.body.classList.contains('accepted')).toBe(false);
-      expect(document.body.classList.contains('declined')).toBe(true);
-    });
-
-    test('ESLMedia accepting condition leads to "accepted" class on the target', async () => {
-      const $media = createMedia({
-        loadCondition: 'all',
-        loadClsTarget: 'body',
-        loadClsAccepted: 'accepted',
-        loadClsDeclined: 'declined'
-      });
-      document.body.appendChild($media);
-
-      await promisifyTimeout(100);
-      expect(document.body.classList.contains('accepted')).toBe(true);
-      expect(document.body.classList.contains('declined')).toBe(false);
-    });
-
-    test('ESLMedia load condition does not clock "declined" class on the target', async () => {
-      const $media = createMedia({
-        lazy: 'manual',
-        loadCondition: 'not all',
-        loadClsTarget: 'body',
-        loadClsAccepted: 'accepted',
-        loadClsDeclined: 'declined'
-      });
-      document.body.appendChild($media);
-
-      await promisifyTimeout(100);
-      expect(document.body.classList.contains('accepted')).toBe(false);
-      expect(document.body.classList.contains('declined')).toBe(true);
-    });
-
-    test('ESLMedia load condition does not clock "accepted" class on the target', async () => {
-      const $media = createMedia({
-        lazy: 'manual',
-        loadCondition: 'all',
-        loadClsTarget: 'body',
-        loadClsAccepted: 'accepted',
-        loadClsDeclined: 'declined'
-      });
-      document.body.appendChild($media);
-
-      await promisifyTimeout(100);
-      expect(document.body.classList.contains('accepted')).toBe(true);
-      expect(document.body.classList.contains('declined')).toBe(false);
-    });
-  });
-
-
   describe('ESLMedia load condition classes', () => {
     test('ESLMedia declining condition leads to "declined" class on the target', async () => {
       const $media = createMedia({
diff --git a/src/modules/esl-mixin-element/test/mixin.listener.test.ts b/src/modules/esl-mixin-element/test/mixin.listener.test.ts
index 8e197f5a7..95792e911 100644
--- a/src/modules/esl-mixin-element/test/mixin.listener.test.ts
+++ b/src/modules/esl-mixin-element/test/mixin.listener.test.ts
@@ -1,4 +1,5 @@
-import {ESLMixinElement, listen} from '../core';
+import {ESLMixinElement} from '../core';
+import {listen} from '../../esl-utils/decorators/listen';
 import {ESLEventUtils} from '../../esl-utils/dom/events';
 
 describe('ESLMixinElement: listeners', () => {
diff --git a/src/modules/esl-utils/dom/events/test/target.test.ts b/src/modules/esl-utils/dom/events/test/target.test.ts
index 918a32ba7..faecbc7eb 100644
--- a/src/modules/esl-utils/dom/events/test/target.test.ts
+++ b/src/modules/esl-utils/dom/events/test/target.test.ts
@@ -30,18 +30,6 @@ describe('dom/events: SyntheticEventTarget', () => {
         expect(listener).toHaveBeenLastCalledWith(event2);
       });
 
-      test('listener should be stored', () => {
-        expect(et.hasEventListener('change', 0)).toBe(true);
-      });
-
-      test('target shouldn`t have more listeners than it actually has', () => {
-        expect(et.hasEventListener('change', 1)).toBe(false);
-      });
-
-      test('listener shouldn`t be stored for wrong event type', () => {
-        expect(et.hasEventListener('change2', 0)).toBe(false);
-      });
-
       test('listener shouldn`t be called third time', () => {
         et.removeEventListener('change', listener);
         et.dispatchEvent(event3);
@@ -81,10 +69,6 @@ describe('dom/events: SyntheticEventTarget', () => {
         expect(et.hasEventListener('change2')).toBe(false);
       });
 
-      test('target shouldn`t have more listeners than it actually has', () => {
-        expect(et.hasEventListener(1)).toBe(false);
-      });
-
       test('listener shouldn`t be called second time', () => {
         et.removeEventListener(listener);
         et.dispatchEvent(event2);
@@ -94,28 +78,6 @@ describe('dom/events: SyntheticEventTarget', () => {
       afterAll(() => jest.clearAllMocks());
     });
 
-    describe('Legacy functionality', () => {
-      const et = new SyntheticEventTarget();
-
-      const event1 = new CustomEvent('change');
-      const event2 = new CustomEvent('change');
-      const listener = jest.fn();
-      et.addListener(listener);
-
-      test('listener shoudn`t be called', () => expect(listener).toBeCalledTimes(0));
-
-      test('listener should be called once', () => {
-        et.dispatchEvent(event1);
-        expect(listener).toBeCalledWith(event1);
-      });
-
-      test('listener shouldn`t be called second time', () => {
-        et.removeListener(listener);
-        et.dispatchEvent(event2);
-        expect(listener).toBeCalledTimes(1);
-      });
-    });
-
     describe('API restriction', () => {
       const et = new SyntheticEventTarget();
       // @ts-ignore
@@ -208,19 +170,4 @@ describe('dom/events: SyntheticEventTarget', () => {
       });
     });
   });
-
-  describe('Listener duplicate subscription', () => {
-    const et = new SyntheticEventTarget();
-    const listener = jest.fn(() => {});
-
-    test('event should be subscribed', () => {
-      et.addEventListener('change', listener);
-      expect(et.hasEventListener(1)).toBe(false);
-    });
-
-    test('event shouldn`t be subscribed again', () => {
-      et.addEventListener('change', listener);
-      expect(et.hasEventListener(1)).toBe(false);
-    });
-  });
 });
diff --git a/src/modules/esl-utils/misc/test/array.test.ts b/src/modules/esl-utils/misc/test/array.test.ts
index 4739782f6..bec218a58 100644
--- a/src/modules/esl-utils/misc/test/array.test.ts
+++ b/src/modules/esl-utils/misc/test/array.test.ts
@@ -13,12 +13,13 @@ describe('misc/array helper tests', () => {
 
   test('flat', () => {
     expect(flat([])).toEqual([]);
+    expect(flat([0])).toEqual([0]);
     expect(flat([1])).toEqual([1]);
     expect(flat([1, 2])).toEqual([1, 2]);
     expect(flat([1, [2, 3]])).toEqual([1, 2, 3]);
     expect(flat([[1, 2], [3, 4]])).toEqual([1, 2, 3, 4]);
     expect(flat([[1], [2, 3, 4], [], [5]])).toEqual([1, 2, 3, 4, 5]);
-    expect(flat([null, 1, 2, 3, [4, 5], null, [6]])).toEqual([1, 2, 3, 4, 5, 6]);
+    expect(flat([null, 1, 2, 3, [4, 5], null, [6]])).toEqual([null, 1, 2, 3, 4, 5, null, 6]);
   });
 
   test('wrap', () => {
@@ -43,6 +44,7 @@ describe('misc/array helper tests', () => {
 
   test('range', () => {
     expect(range(3)).toEqual([0, 1, 2]);
+    // @ts-ignore
     expect(range(9, (x) => x / 8)).toEqual([...Array(9).keys()].map((x) => x / 8));
   });
 
diff --git a/src/polyfills/test/es6.object.assign.test.ts b/src/polyfills/test/es6.object.assign.test.ts
deleted file mode 100644
index 58d1aa741..000000000
--- a/src/polyfills/test/es6.object.assign.test.ts
+++ /dev/null
@@ -1,394 +0,0 @@
-const originalAssignFn = Object.assign;
-Object.defineProperty(Object, 'assign', {writable: true, value: undefined});
-import '../list/es6.object.assign';
-
-class TestError extends Error {}
-
-afterAll(() => {
-  Object.assign = originalAssignFn;
-});
-
-describe('Object.assign() polyfill', () => {
-  test('object override sameproperty', () => {
-    // Object properties are assigned to target in ascending index order,
-    // i.e. a later assignment to the same property overrides an earlier assignment.
-    const target = {a: 1};
-    const result = Object.assign(target, {a: 2}, {a: 'c'});
-    expect(result.a).toBe('c');
-  });
-
-  test('only one argument', () => {
-    // Should return ToObject(target)
-    const target = 'a';
-    const result = Object.assign(target as any);
-    expect(typeof result).toBe('object');
-    expect(result.valueOf()).toEqual('a');
-  });
-
-  test('override notstringtarget', () => {
-    // Every string from sources will be wrapped to objects, and override from the first letter(result[0]) all the time
-    const target = 12;
-    const result = Object.assign(target, 'aaa', 'bb2b', '1c');
-    expect(Object.getOwnPropertyNames(result).length).toBe(4);
-    expect(result[0]).toBe('1');
-    expect(result[1]).toBe('c');
-    expect(result[2]).toBe('2');
-    expect(result[3]).toBe('b');
-  });
-
-  test('override', () => {
-    const target = {a: 1};
-    /*
-    '1a2c3' have own enumerable properties, so it Should be wrapped to objects;
-    {b:6} is an object,should be assigned to final object.
-    undefined and null should be ignored;
-    125 is a number,it cannot has own enumerable properties;
-    {a:'c'},{a:5} will override property a, the value should be 5.
-    */
-    const result = Object.assign(target, '1a2c3', {a: 'c'}, undefined, {b: 6}, null, 125, {a: 5});
-    expect(Object.getOwnPropertyNames(result).length).toBe(7);
-    expect(result.a).toBe(5);
-    expect(result[0]).toBe('1');
-    expect(result[1]).toBe('a');
-    expect(result[2]).toBe('2');
-    expect(result[3]).toBe('c');
-    expect(result[4]).toBe('3');
-    expect(result.b).toBe(6);
-  });
-
-  test('source is Null Undefined', () => {
-    const target = new Object();
-    // null and undefined source should be ignored, result should be original object.
-    const result = Object.assign(target, undefined, null);
-    expect(result).toEqual(target);
-  });
-
-  test('source is Number Boolean Symbol', () => {
-    const target = new Object();
-    /*
-    Number, Boolean, and Symbol cannot have their own enumerable properties,
-    so cannot be Assigned. Here result should be the original object.
-    */
-    const result = Object.assign(target, 123, true, Symbol('foo'));
-    expect(result).toEqual(target);
-  });
-
-  test('source is String', () => {
-    const target = new Object();
-    // string have own enumerable properties, so it can be wrapped to objects.
-    const result = Object.assign(target, '123');
-    expect(result[0]).toBe('1');
-    expect(result[1]).toBe('2');
-    expect(result[2]).toBe('3');
-  });
-
-  test('target is Boolean', () => {
-    // if target is Boolean,the return value should be a new object whose value is target.
-    const result = Object.assign(true, {a: 2});
-    expect(typeof result).toBe('object');
-    expect(result.valueOf()).toEqual(true);
-  });
-
-  test('target is Null', () => {
-    // if target is null, should throw a TypeError exception.
-    // @ts-ignore
-    expect(() => Object.assign(null, {a: 1})).toThrow(TypeError);
-  });
-
-  test('target is Number', () => {
-    // if target is Number, the return value should be a new object whose value is target.
-    const result = Object.assign(1, {a: 2});
-    expect(typeof result).toBe('object');
-    expect(result.valueOf()).toEqual(1);
-  });
-
-  test('target is Object', () => {
-    const target = {foo: 1};
-    // if target is Object, its properties will be the properties of new object.
-    const result = Object.assign(target, {a: 2});
-    expect(typeof result).toBe('object');
-    expect(result.a).toEqual(2);
-    expect(result.foo).toEqual(1);
-  });
-
-  test('target is String', () => {
-    // if target is String, the return value should be a new object whose value is target.
-    const result = Object.assign('1', {a: 2});
-    expect(typeof result).toBe('object');
-    expect(result.valueOf()).toEqual('1');
-  });
-
-  test('target is Symbol', () => {
-    // if target is Symbol, the return value should be a new Symbol object whose [[SymbolData]] value is target.
-    const target = Symbol('foo');
-    const result = Object.assign(target, {a: 1});
-    expect(typeof result).toBe('object');
-    expect(result.toString()).toEqual('Symbol(foo)');
-  });
-
-  test('target is Undefined', () => {
-    // if target is Undefined, should throw a TypeError exception.
-    // @ts-ignore
-    expect(() => Object.assign(undefined, {a: 1})).toThrow(TypeError);
-  });
-
-  test('assign descriptor', () => {
-    const descriptor = Object.getOwnPropertyDescriptor(Object, 'assign');
-    expect(descriptor).toHaveProperty('writable', true);
-    expect(descriptor).toHaveProperty('enumerable', false);
-    expect(descriptor).toHaveProperty('configurable', true);
-  });
-
-  test('assignment to readonly property of target must throw a typeerror exception', () => {
-    // Assignment to readonly property of target must throw a TypeError exception
-    expect(() => Object.assign('a', [1])).toThrow(TypeError);
-  });
-
-  test('invoked as constructor', () => {
-    /*
-    Built-in function objects that are not identified as constructors do not
-    implement the [[Construct]] internal method unless otherwise specified in
-    the description of a particular function.
-    */
-    expect(() => {
-      // @ts-ignore
-      new Object.assign({});
-    }).toThrow(TypeError);
-  });
-
-  test('assign name', () => {
-    /*
-    ES6 Section 17:
-    Every built-in Function object, including constructors, that is not
-    identified as an anonymous function has a name property whose value is a
-    String. Unless otherwise specified, this value is the name that is given to
-    the function in this specification.
-    [...]
-    Unless otherwise specified, the name property of a built-in Function
-    object, if it exists, has the attributes { [[Writable]]: false,
-    [[Enumerable]]: false, [[Configurable]]: true }.
-    */
-    expect(Object.assign.name).toBe('assign');
-    const descriptor = Object.getOwnPropertyDescriptor(Object.assign, 'name');
-    expect(descriptor).toHaveProperty('writable', false);
-    expect(descriptor).toHaveProperty('enumerable', false);
-    expect(descriptor).toHaveProperty('configurable', true);
-  });
-
-  test('source - get attributes error', () => {
-    // Errors thrown during retrieval of source object attributes
-    const source = {
-      get attr() {throw new TestError();}
-    };
-    expect(() => Object.assign({}, source)).toThrow(TestError);
-  });
-
-  test('source - non-enumerable', () => {
-    // Does not assign non-enumerable source properties
-    const target = {};
-    const source = Object.defineProperty({}, 'attr', {value: 1, enumerable: false});
-    const result = Object.assign(target, source);
-
-    expect(Object.hasOwnProperty.call(target, 'attr')).toBe(false);
-    expect(result).toEqual(target);
-  });
-
-  test('source - own property error', () => {
-    // Invoked with a source whose own property descriptor cannot be retrieved
-    const source = new Proxy({
-      attr: null
-    }, {
-      getOwnPropertyDescriptor: () => {
-        throw new TestError();
-      }
-    });
-    expect(() => Object.assign({}, source)).toThrow(TestError);
-  });
-
-  test('source - own property keys error', () => {
-    // Invoked with a source whose own property keys cannot be retrieved
-    const source = new Proxy({}, {
-      ownKeys: () => { throw new TestError(); }
-    });
-    expect(() => Object.assign({}, source)).toThrow(TestError);
-  });
-
-  test('strings and symbol order', () => {
-    // Symbol-valued properties are copied after String-valued properties.
-    const log: string[] = [];
-    const sym1 = Symbol('x');
-    const sym2 = Symbol('y');
-    const source = {};
-
-    Object.defineProperty(source, sym1, {
-      get: () => log.push('get sym(x)'),
-      enumerable: true, configurable: true,
-    });
-    Object.defineProperty(source, 'a', {
-      get: () => log.push('get a'),
-      enumerable: true, configurable: true,
-    });
-    Object.defineProperty(source, sym2, {
-      get: () => log.push('get sym(y)'),
-      enumerable: true, configurable: true,
-    });
-    Object.defineProperty(source, 'b', {
-      get: () => log.push('get b'),
-      enumerable: true, configurable: true,
-    });
-
-    Object.assign({}, source);
-    expect(log).toEqual(['get a', 'get b', 'get sym(x)', 'get sym(y)']);
-  });
-
-  test('target is frozen accessor property set succeeds', () => {
-    // [[Set]] to accessor property of frozen `target` succeeds.
-    let value1 = 1;
-    const target1 = {
-      set foo(val: any) { value1 = val; },
-    };
-    Object.freeze(target1);
-    Object.assign(target1, {foo: 2});
-    expect(value1).toBe(2);
-
-    const sym = Symbol();
-    let value2 = 1;
-    const target2 = Object.freeze({
-      set [sym](val: any) { value2 = val; },
-    });
-    Object.freeze(target2);
-    Object.assign(target2, {[sym]: 2});
-    expect(value2).toBe(2);
-  });
-
-  test('target is frozen data property set throws', () => {
-    // [[Set]] to data property of frozen `target` fails with TypeError.
-    const sym = Symbol();
-    const target1 = {[sym]: 1};
-    Object.freeze(target1);
-    expect(() => Object.assign(target1, {[sym]: 1})).toThrow(TypeError);
-
-    const target2 = Object.freeze({foo: 1});
-    expect(() => Object.assign(target2, {foo: 1})).toThrow(TypeError);
-  });
-
-  test('target is non extensible existing accessor property', () => {
-    // [[Set]] to existing accessor property of non-extensible `target` is successful.
-    let value1 = 1;
-    const target1 = Object.preventExtensions({
-      set foo(val: any) { value1 = val; },
-    });
-    Object.assign(target1, {foo: 2});
-    expect(value1).toBe(2);
-
-    const sym = Symbol();
-    let value2 = 1;
-    const target2 = {
-      set [sym](val: any) { value2 = val; },
-    };
-    Object.preventExtensions(target2);
-    Object.assign(target2, {[sym]: 2});
-    expect(value2).toBe(2);
-  });
-
-  test('target is non extensible existing data property', () => {
-    // [[Set]] to existing data property of non-extensible `target` is successful.
-    const target1 = Object.preventExtensions({foo: 1});
-    Object.assign(target1, {foo: 2});
-    expect(target1.foo).toBe(2);
-
-    const sym = Symbol();
-    const target2 = {[sym]: 1};
-    Object.preventExtensions(target2);
-    Object.assign(target2, {[sym]: 2});
-    expect(target2[sym]).toBe(2);
-  });
-
-  test('target is non-extensible property creation throws', () => {
-    // [[Set]] to non-existing property of non-extensible `target` fails with TypeError.
-    const target1 = Object.preventExtensions({foo: 1});
-    expect(() => Object.assign(target1, {get bar() {return;}})).toThrow(TypeError);
-
-    const target2 = {};
-    Object.preventExtensions(target2);
-    expect(() => Object.assign(target2, {[Symbol()]: 1})).toThrow(TypeError);
-  });
-
-  test('target is sealed existing accessor property', () => {
-    // [[Set]] to existing accessor property of sealed `target` is successful.
-    let value1 = 1;
-    const target1 = Object.seal({
-      set foo(val: any) { value1 = val; }
-    });
-    Object.assign(target1, {foo: 2});
-    expect(value1).toBe(2);
-
-    const sym = Symbol();
-    let value2 = 1;
-    const target2 = {
-      set [sym](val: any) { value2 = val; }
-    };
-    Object.seal(target2);
-    Object.assign(target2, {[sym]: 2});
-    expect(value2).toBe(2);
-  });
-
-  test('target is sealed existing data property', () => {
-    // [[Set]] to existing data property of sealed `target` is successful.
-    const target1 = Object.seal({foo: 1});
-    Object.assign(target1, {foo: 2});
-    expect(target1.foo).toBe(2);
-
-    const sym = Symbol();
-    const target2 = {[sym]: 1};
-    Object.seal(target2);
-    Object.assign(target2, {[sym]: 2});
-    expect(target2[sym]).toBe(2);
-  });
-
-  test('target is sealed property creation throws', () => {
-    // [[Set]] to non-existing property of sealed `target` fails with TypeError.
-    const target1 = Object.seal({foo: 1});
-    expect(() => Object.assign(target1, {get bar() {return;}})).toThrow(TypeError);
-
-    const target2 = {};
-    Object.seal(target2);
-    expect(() => Object.assign(target2, {[Symbol()]: 1})).toThrow(TypeError);
-  });
-
-  test('target set not-writable', () => {
-    // Errors thrown during definition of target object attributes
-    const target = {};
-    Object.defineProperty(target, 'attr', {writable: false});
-    expect(() => Object.assign(target, {attr: 1})).toThrow(TypeError);
-  });
-
-  test('target set user error', () => {
-    // Errors thrown during definition of target object attributes
-    const target = {};
-    Object.defineProperty(target, 'attr', {
-      set: (_) => { throw new TestError(); }
-    });
-    expect(() => Object.assign(target, {attr: 1})).toThrow(TestError);
-  });
-
-  test('prototype pollution via __proto__', () => {
-    const maliciousPayload = '{"a": 1, "__proto__": {"polluted": true}}';
-    const a: any = {};
-    const result = Object.assign({}, JSON.parse(maliciousPayload));
-
-    expect(({} as any).polluted).not.toBe(true); // safe Plain Old Javascript Object
-    expect(a.polluted).not.toBe(true); // safe a input
-    expect(result.polluted).toBe(true);
-  });
-
-  test('prototype pollution via constructor', () => {
-    const maliciousPayload = '{"a": 1, "constructor": {"prototype": {"polluted": true}}}';
-    const a: any = {};
-    const result = Object.assign({}, JSON.parse(maliciousPayload));
-
-    expect(result.constructor.prototype.polluted).toBe(true);
-    expect(a.constructor.prototype.polluted).toBe(undefined);
-    expect(({} as any).constructor.prototype.polluted).toBe(undefined);
-  });
-});