@progress/kendo-angular-common
Version:
Kendo UI for Angular - Utility Package
1,290 lines (1,250 loc) • 86.1 kB
JavaScript
/**-----------------------------------------------------------------------------------------
* Copyright © 2026 Progress Software Corporation. All rights reserved.
* Licensed under commercial license. See LICENSE.md in the project root for more information
*-------------------------------------------------------------------------------------------*/
import * as i0 from '@angular/core';
import { EventEmitter, Output, Input, Directive, Injectable, Component, InjectionToken, HostBinding, ViewChild, Optional, isDevMode } from '@angular/core';
import { detectDesktopBrowser, detectMobileOS } from '@progress/kendo-common';
import { take, auditTime } from 'rxjs/operators';
import { Draggable } from '@progress/kendo-draggable';
import { merge, fromEvent, from, Subscription } from 'rxjs';
import { NgStyle, NgTemplateOutlet } from '@angular/common';
import { registerLicenseMessage, getLicenseStatus } from '@progress/kendo-licensing';
/**
* @hidden
*/
const isDocumentAvailable = () => typeof document !== 'undefined';
/**
* @hidden
*/
const isChanged = (propertyName, changes, skipFirstChange = true) => (typeof changes[propertyName] !== 'undefined' &&
(!changes[propertyName].isFirstChange() || !skipFirstChange) &&
changes[propertyName].previousValue !== changes[propertyName].currentValue);
/**
* @hidden
*/
const anyChanged = (propertyNames, changes, skipFirstChange = true) => propertyNames.some(name => isChanged(name, changes, skipFirstChange));
/**
* @hidden
*/
const hasObservers = (emitter) => emitter && emitter.observers.length > 0;
/**
* @hidden
*/
const guid = () => {
let id = "";
for (let i = 0; i < 32; i++) {
const random = Math.random() * 16 | 0;
if (i === 8 || i === 12 || i === 16 || i === 20) {
id += "-";
}
let charValue;
if (i === 12) {
charValue = 4;
}
else if (i === 16) {
charValue = random & 3 | 8;
}
else {
charValue = random;
}
id += charValue.toString(16);
}
return id;
};
/**
* @hidden
*
* Returns true if the used browser is Safari.
*/
const isSafari = (userAgent) => {
return detectDesktopBrowser(userAgent).safari ||
(detectMobileOS(userAgent) && detectMobileOS(userAgent).browser === 'mobilesafari');
};
/**
* @hidden
*
* Returns true if the used browser is Firefox.
*/
const isFirefox = (userAgent) => {
const desktopBrowser = detectDesktopBrowser(userAgent);
const mobileOS = detectMobileOS(userAgent);
return desktopBrowser?.mozilla || mobileOS?.browser === 'firefox';
};
/**
* @hidden
*/
const firefoxMaxHeight = 17895697;
/**
* @hidden
*/
const isPresent = (value) => value !== null && value !== undefined;
/**
* @hidden
*/
const isObjectPresent = (value) => {
return isObject(value) && Object.keys(value).length > 0;
};
/**
* @hidden
*/
const isString = (value) => value instanceof String || typeof value === 'string';
/**
* @hidden
*/
const isObject = (value) => isPresent(value) && !Array.isArray(value) && typeof value === 'object';
/**
* @hidden
*/
const isSet = (value) => isPresent(value) && value instanceof Set;
/**
* @hidden
*/
const splitStringToArray = (value) => value.trim().replace(/\s+/g, " ").split(' ');
/**
* Receives CSS class declarations either as an object, string, set or array and returns an array of the class names.
*
* @hidden
*/
const parseCSSClassNames = (value) => {
if (Array.isArray(value)) {
return parseArrayClassNames(value);
}
if (isSet(value)) {
return parseArrayClassNames(Array.from(value));
}
if (isObject(value)) {
return parseObjectClassNames(value);
}
if (isString(value)) {
return parseStringClassNames(value);
}
};
const parseObjectClassNames = (value) => {
const classes = [];
Object.keys(value).forEach((className) => {
const currentClassName = splitStringToArray(className);
if (value[className] && currentClassName.length) {
classes.push(...currentClassName);
}
});
return classes;
};
const parseStringClassNames = (value) => {
const classes = [];
const classesArray = splitStringToArray(value);
classesArray.forEach((className) => {
classes.push(className);
});
return classes;
};
const parseArrayClassNames = (value) => {
const classes = [];
value.forEach((className) => {
const current = splitStringToArray(className);
if (current[0]) {
classes.push(...current);
}
});
return classes;
};
/**
* @hidden
*/
const setHTMLAttributes = (attributes, renderer, element, zone) => {
if (zone) {
zone.onStable.pipe(take(1)).subscribe(() => {
applyAttributes(attributes, renderer, element);
});
}
else {
applyAttributes(attributes, renderer, element);
}
};
/**
* @hidden
*/
const removeHTMLAttributes = (attributes, renderer, element) => {
for (const attribute in attributes) {
if (attribute) {
renderer.removeAttribute(element, attribute);
}
}
};
/**
* @hidden
*/
const parseAttributes = (target, source) => {
const targetObj = target;
Object.keys(source).forEach(key => {
delete targetObj[key];
});
return targetObj;
};
/**
* @hidden
*/
const applyAttributes = (attributes, renderer, element) => {
for (const attribute in attributes) {
if (attribute && isPresent(attributes[attribute])) {
renderer.setAttribute(element, attribute, attributes[attribute]);
}
}
};
/**
* @hidden
*/
const isControlRequired = (control) => {
if (!control?.validator) {
return false;
}
return control.validator(control)?.hasOwnProperty('required');
};
const areObjectsEqual = (firstObject, secondObject) => {
if (Object.keys(firstObject).length !== Object.keys(secondObject).length) {
return false;
}
const equalSettings = Object.entries(firstObject)
.filter(([key, value]) => value === secondObject[key.toString()]);
return equalSettings.length === Object.keys(firstObject).length;
};
const processCssValue = (value) => {
if (typeof value === 'number') {
return `${value}px`;
}
else if (typeof value === 'string') {
const trimmedValue = value.trim();
const numValue = parseInt(trimmedValue, 10);
if (!isNaN(numValue) && Number.isFinite(numValue)) {
if (numValue.toString() === trimmedValue) {
return `${numValue}px`;
}
else {
return value;
}
}
return null;
}
return null;
};
class DraggableDirective {
element;
ngZone;
enableDrag = true;
kendoPress = new EventEmitter();
kendoDrag = new EventEmitter();
kendoRelease = new EventEmitter();
draggable;
constructor(element, ngZone) {
this.element = element;
this.ngZone = ngZone;
}
ngOnInit() {
this.toggleDraggable();
}
ngOnChanges(changes) {
if (isChanged('enableDrag', changes)) {
this.toggleDraggable();
}
}
ngOnDestroy() {
this.destroyDraggable();
}
toggleDraggable() {
if (isDocumentAvailable()) {
this.destroyDraggable();
if (this.enableDrag) {
this.draggable = new Draggable({
drag: (e) => this.kendoDrag.next(e),
press: (e) => this.kendoPress.next(e),
release: (e) => this.kendoRelease.next(e)
});
this.ngZone.runOutsideAngular(() => this.draggable?.bindTo(this.element.nativeElement));
}
}
}
destroyDraggable() {
if (this.draggable) {
this.draggable.destroy();
this.draggable = undefined;
}
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: DraggableDirective, deps: [{ token: i0.ElementRef }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Directive });
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.25", type: DraggableDirective, isStandalone: true, selector: "[kendoDraggable]", inputs: { enableDrag: "enableDrag" }, outputs: { kendoPress: "kendoPress", kendoDrag: "kendoDrag", kendoRelease: "kendoRelease" }, usesOnChanges: true, ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: DraggableDirective, decorators: [{
type: Directive,
args: [{
selector: '[kendoDraggable]',
standalone: true
}]
}], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.NgZone }], propDecorators: { enableDrag: [{
type: Input
}], kendoPress: [{
type: Output
}], kendoDrag: [{
type: Output
}], kendoRelease: [{
type: Output
}] } });
const closestInScope = (node, predicate, scope) => {
while (node && node !== scope && !predicate(node)) {
node = node.parentNode;
}
if (node !== scope) {
return node;
}
return undefined;
};
const closest = (node, predicate) => {
while (node && !predicate(node)) {
node = node.parentNode;
}
return node;
};
const contains = (parent, node, matchSelf = false) => {
const outside = !closest(node, (child) => child === parent);
if (outside) {
return false;
}
const el = closest(node, (child) => child === node);
return el && (matchSelf || el !== parent);
};
const findElement = (node, predicate, matchSelf = true) => {
if (!node) {
return;
}
if (matchSelf && predicate(node)) {
return node;
}
node = node.firstChild;
while (node) {
if (node.nodeType === 1) {
const element = findElement(node, predicate);
if (element) {
return element;
}
}
node = node.nextSibling;
}
};
const focusableRegex = /^(?:a|input|select|option|textarea|button|object)$/i;
const isFocusable = (element) => {
if (!element.tagName) {
return false;
}
const tagName = element.tagName.toLowerCase();
const hasTabIndex = Boolean(element.getAttribute('tabIndex'));
const focusable = !element.disabled && focusableRegex.test(tagName);
return focusable || hasTabIndex;
};
const hasFocusableParent = (element, container) => {
let currentElement = element;
let hasFocusableParent = false;
while (currentElement && currentElement !== container) {
if (isFocusable(currentElement)) {
hasFocusableParent = true;
break;
}
currentElement = currentElement.parentElement;
}
return hasFocusableParent;
};
const isVisible = (element) => {
if (!isDocumentAvailable() || !element?.getBoundingClientRect) {
return false;
}
const rect = element.getBoundingClientRect();
const hasSize = rect.width > 0 && rect.height > 0;
const hasPosition = rect.x !== 0 && rect.y !== 0;
// Elements can have zero size due to styling, but they will still count as visible.
// For example, the selection checkbox has no size, but is made visible through styling.
return (hasSize || hasPosition) && window.getComputedStyle(element).visibility !== 'hidden';
};
const isFocusableWithTabKey = (element, checkVisibility = true) => {
if (!isFocusable(element)) {
return false;
}
const tabIndex = element.getAttribute('tabIndex');
const visible = !checkVisibility || isVisible(element);
return visible && tabIndex !== '-1';
};
const findFocusableChild = (element, checkVisibility = true) => {
return findElement(element, (node) => isFocusableWithTabKey(node, checkVisibility), false);
};
const findFocusable = (element, checkVisibility = true) => {
return findElement(element, (node) => isFocusableWithTabKey(node, checkVisibility));
};
const toClassList = (classNames) => String(classNames).trim().split(' ');
const hasClasses = (element, classNames) => {
const namesList = toClassList(classNames);
return Boolean(toClassList(element.className).find((className) => namesList.indexOf(className) >= 0));
};
const matchesClasses = (classNames) => (element) => hasClasses(element, classNames);
const NODE_NAME_PREDICATES = {};
const matchesNodeName = (nodeName) => {
if (!NODE_NAME_PREDICATES[nodeName]) {
NODE_NAME_PREDICATES[nodeName] = (element) => String(element.nodeName).toLowerCase() === nodeName.toLowerCase();
}
return NODE_NAME_PREDICATES[nodeName];
};
/**
* Normalizes a scroll position value in RTL mode.
*/
function rtlScrollPosition(position, element, initial) {
let result = position;
if (initial < 0) {
result = -position;
}
else if (initial > 0) {
result = element.scrollWidth - element.offsetWidth - position;
}
return result;
}
function closestBySelector(element, selector) {
if (element.closest) {
return element.closest(selector);
}
const matches = Element.prototype.matches ?
(el, sel) => el.matches(sel)
: (el, sel) => el.msMatchesSelector(sel);
let node = element;
while (node && !isDocumentNode(node)) {
if (matches(node, selector)) {
return node;
}
node = node.parentNode;
}
}
const isDocumentNode = (container) => container.nodeType === 9;
/**
* @hidden
*/
class EventsOutsideAngularDirective {
element;
ngZone;
renderer;
events = {};
scope;
subscriptions;
constructor(element, ngZone, renderer) {
this.element = element;
this.ngZone = ngZone;
this.renderer = renderer;
}
ngOnInit() {
if (!this.element?.nativeElement) {
return;
}
const events = this.events;
this.subscriptions = [];
this.ngZone.runOutsideAngular(() => {
for (const name in events) {
if (Object.hasOwnProperty.call(events, name)) {
this.subscriptions?.push(this.renderer.listen(this.element.nativeElement, name, this.scope ? events[name].bind(this.scope) : events[name]));
}
}
});
}
ngOnDestroy() {
if (this.subscriptions) {
for (let idx = 0; idx < this.subscriptions.length; idx++) {
this.subscriptions[idx]();
}
this.subscriptions = null;
}
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: EventsOutsideAngularDirective, deps: [{ token: i0.ElementRef }, { token: i0.NgZone }, { token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Directive });
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.25", type: EventsOutsideAngularDirective, isStandalone: true, selector: "[kendoEventsOutsideAngular]", inputs: { events: ["kendoEventsOutsideAngular", "events"], scope: "scope" }, ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: EventsOutsideAngularDirective, decorators: [{
type: Directive,
args: [{
selector: '[kendoEventsOutsideAngular]',
standalone: true
}]
}], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.NgZone }, { type: i0.Renderer2 }], propDecorators: { events: [{
type: Input,
args: ['kendoEventsOutsideAngular']
}], scope: [{
type: Input
}] } });
class ResizeService {
resizeBatchService;
resize = new EventEmitter();
acceptedSize = false;
lastWidth;
lastHeight;
state = 0 /* ServiceState.Initial */;
parentElement;
constructor(resizeBatchService) {
this.resizeBatchService = resizeBatchService;
}
acceptSize(size = this.measure()) {
this.lastWidth = size.width;
this.lastHeight = size.height;
this.acceptedSize = true;
}
checkChanges() {
if (!isDocumentAvailable()) {
return;
}
if (this.state === 0 /* ServiceState.Initial */) {
this.state = 1 /* ServiceState.Initializing */;
// batch initial measure
this.resizeBatchService.schedule(this, this.init);
}
}
destroy() {
this.resizeBatchService.cancel(this);
}
checkSize() {
if (!this.parentElement) {
return false;
}
const { width, height } = this.measure();
const sameSize = width === this.lastWidth && height === this.lastHeight;
if (sameSize) {
return false;
}
this.lastWidth = width;
this.lastHeight = height;
this.acceptedSize = false;
this.resize.emit({ width, height });
return true;
}
initSize() {
const size = this.measure();
this.lastWidth = size.width;
this.lastHeight = size.height;
}
measure() {
let width = 0;
let height = 0;
if (this.parentElement) {
height = this.parentElement.offsetHeight;
width = this.parentElement.offsetWidth;
}
return { height, width };
}
}
// eslint-disable import/no-deprecated
const applyStyles = (el, styles) => {
for (const prop in styles) {
el.style.setProperty(prop, styles[prop]);
}
};
const div = (styles) => {
const el = document.createElement('div');
applyStyles(el, styles);
return el;
};
const computedProp = (elem, prop) => getComputedStyle(elem, null).getPropertyValue(prop);
const WRAP_STYLES = {
'position': 'absolute',
'display': 'block',
'left': '0',
'top': '0',
'right': '0',
'bottom': '0',
'z-index': '-1',
'overflow': 'hidden',
'visibility': 'hidden'
};
const EXPAND_CHILD_STYLES = {
'position': 'absolute',
'left': '0',
'top': '0',
'transition': '0s'
};
const SHRINK_CHILD_STYLES = {
...EXPAND_CHILD_STYLES,
'width': '200%',
'height': '200%'
};
class ResizeCompatService extends ResizeService {
element;
ngZone;
expand;
expandChild;
shrink;
subscription;
constructor(resizeBatchService, element, ngZone) {
super(resizeBatchService);
this.element = element;
this.ngZone = ngZone;
}
checkChanges() {
if (this.state === 2 /* ServiceState.Initialized */) {
if (!this.resizeBatchService.isScheduled(this)) {
this.resizeBatchService.schedule(this, this.checkSize);
}
return;
}
super.checkChanges();
}
destroy() {
super.destroy();
if (this.subscription) {
this.subscription.unsubscribe();
}
if (this.expand) {
const element = this.element?.nativeElement;
if (element) {
element.removeChild(this.expand);
element.removeChild(this.shrink);
}
this.expand.removeChild(this.expandChild);
this.expand = this.expandChild = this.shrink = this.element = null;
}
}
checkSize() {
if (super.checkSize()) {
this.reset();
return true;
}
return false;
}
init() {
const parentElement = this.parentElement = this.element?.nativeElement?.parentElement;
if (!parentElement) {
return;
}
if (computedProp(parentElement, 'position') === 'static') {
parentElement.style.position = 'relative';
}
this.state = 2 /* ServiceState.Initialized */;
this.render();
this.reset();
this.initSize();
this.subscribe();
}
render() {
const element = this.element?.nativeElement;
if (!element) {
return;
}
applyStyles(element, WRAP_STYLES);
element.setAttribute('dir', 'ltr');
this.expand = div(WRAP_STYLES);
this.expandChild = div(EXPAND_CHILD_STYLES);
this.expand.appendChild(this.expandChild);
element.appendChild(this.expand);
this.shrink = div(WRAP_STYLES);
const shrinkChild = div(SHRINK_CHILD_STYLES);
this.shrink.appendChild(shrinkChild);
element.appendChild(this.shrink);
}
reset() {
const expandChild = this.expandChild;
expandChild.style.width = 100000 + 'px';
expandChild.style.height = 100000 + 'px';
const expand = this.expand;
expand.scrollLeft = 100000;
expand.scrollTop = 100000;
const shrink = this.shrink;
shrink.scrollLeft = 100000;
shrink.scrollTop = 100000;
}
subscribe() {
this.ngZone.runOutsideAngular(() => {
this.subscription = merge(fromEvent(this.shrink, 'scroll'), fromEvent(this.expand, 'scroll'))
.subscribe(() => {
this.checkSize();
});
});
}
}
const HAS_OBSERVER = typeof ResizeObserver !== 'undefined';
/**
* @hidden
*/
class ResizeObserverService extends ResizeService {
element;
ngZone;
resizeObserver;
static supported() {
return HAS_OBSERVER;
}
constructor(resizeBatchService, element, ngZone) {
super(resizeBatchService);
this.element = element;
this.ngZone = ngZone;
}
destroy() {
super.destroy();
if (this.resizeObserver) {
this.resizeObserver.disconnect();
this.resizeObserver = null;
}
this.parentElement = null;
}
init() {
this.parentElement = this.element.nativeElement.parentElement;
this.initSize();
this.state = 2 /* ServiceState.Initialized */;
this.ngZone.runOutsideAngular(() => {
this.resizeObserver = new ResizeObserver(() => {
this.checkSize();
});
this.resizeObserver.observe(this.parentElement);
});
}
}
/**
* @hidden
*/
class ResizeBatchService {
ngZone;
scheduled = [];
resolvedPromise = Promise.resolve(null);
subscription;
constructor(ngZone) {
this.ngZone = ngZone;
this.flush = this.flush.bind(this);
}
schedule(instance, method) {
this.scheduled.push({ instance, method });
if (!this.subscription) {
this.ngZone.runOutsideAngular(() => {
this.subscription = from(this.resolvedPromise)
.subscribe(this.flush);
});
}
}
isScheduled(instance) {
return Boolean(this.scheduled.find(item => item.instance === instance));
}
cancel(instance) {
const scheduled = this.scheduled;
const count = scheduled.length;
for (let idx = 0; idx < count; idx++) {
if (scheduled[idx].instance === instance) {
scheduled.splice(idx, 1);
if (!scheduled.length) {
this.unsubscribe();
}
return;
}
}
}
ngOnDestroy() {
this.unsubscribe();
}
unsubscribe() {
if (this.subscription) {
this.subscription.unsubscribe();
this.subscription = null;
}
}
flush() {
this.scheduled.forEach(item => {
item.method.call(item.instance);
});
this.scheduled = [];
this.unsubscribe();
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: ResizeBatchService, deps: [{ token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Injectable });
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: ResizeBatchService, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: ResizeBatchService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root'
}]
}], ctorParameters: () => [{ type: i0.NgZone }] });
/**
* Emit up to 10 resize events per second by default.
* Chosen as a compromise between responsiveness and performance.
*/
const DEFAULT_RATE_LIMIT = 10;
/**
* Resize Sensor Component
*
* Triggers a "resize" event whenever the parent DOM element size changes.
*/
class ResizeSensorComponent {
/**
* The maximum number of resize events to emit per second.
*
* Defaults to 10.
*/
rateLimit = DEFAULT_RATE_LIMIT;
/**
* Fires when the parent DOM element has been resized.
*/
resize = new EventEmitter();
subscription;
resizeService;
constructor(resizeBatchService, element, ngZone) {
const serviceType = ResizeObserverService.supported() ? ResizeObserverService : ResizeCompatService;
this.resizeService = new serviceType(resizeBatchService, element, ngZone);
const throttleTime = 1000 / (this.rateLimit || DEFAULT_RATE_LIMIT);
this.subscription = this.resizeService.resize
.pipe(auditTime(throttleTime))
.subscribe(({ width, height }) => {
if (!this.resizeService.acceptedSize) {
this.resize.emit({ width, height });
}
});
}
ngAfterViewChecked() {
this.resizeService.checkChanges();
}
ngOnDestroy() {
this.subscription.unsubscribe();
this.resizeService.destroy();
}
acceptSize(size) {
this.resizeService.acceptSize(size);
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: ResizeSensorComponent, deps: [{ token: ResizeBatchService }, { token: i0.ElementRef }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.25", type: ResizeSensorComponent, isStandalone: true, selector: "kendo-resize-sensor", inputs: { rateLimit: "rateLimit" }, outputs: { resize: "resize" }, ngImport: i0, template: '', isInline: true });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: ResizeSensorComponent, decorators: [{
type: Component,
args: [{
selector: 'kendo-resize-sensor',
template: '',
standalone: true
}]
}], ctorParameters: () => [{ type: ResizeBatchService }, { type: i0.ElementRef }, { type: i0.NgZone }], propDecorators: { rateLimit: [{
type: Input
}], resize: [{
type: Output
}] } });
class KendoInput {
}
/**
* @hidden
*
* Token used by the kendoWebMcp directive to resolve the host component instance.
* Each supported Kendo component provides itself under this token.
*/
const KENDO_WEBMCP_HOST = new InjectionToken('KendoWebMcpHost');
/**
* Enum with key codes.
*/
var Keys;
(function (Keys) {
Keys["ArrowDown"] = "ArrowDown";
Keys["ArrowLeft"] = "ArrowLeft";
Keys["ArrowRight"] = "ArrowRight";
Keys["ArrowUp"] = "ArrowUp";
Keys["Backspace"] = "Backspace";
Keys["Delete"] = "Delete";
Keys["Digit0"] = "Digit0";
Keys["Digit1"] = "Digit1";
Keys["Digit2"] = "Digit2";
Keys["Digit3"] = "Digit3";
Keys["Digit4"] = "Digit4";
Keys["Digit5"] = "Digit5";
Keys["Digit6"] = "Digit6";
Keys["Digit7"] = "Digit7";
Keys["Digit8"] = "Digit8";
Keys["Digit9"] = "Digit9";
Keys["End"] = "End";
Keys["Enter"] = "Enter";
Keys["Escape"] = "Escape";
Keys["F1"] = "F1";
Keys["F2"] = "F2";
Keys["F10"] = "F10";
Keys["Home"] = "Home";
Keys["KeyA"] = "KeyA";
Keys["KeyB"] = "KeyB";
Keys["KeyC"] = "KeyC";
Keys["KeyD"] = "KeyD";
Keys["KeyE"] = "KeyE";
Keys["KeyF"] = "KeyF";
Keys["KeyG"] = "KeyG";
Keys["KeyH"] = "KeyH";
Keys["KeyI"] = "KeyI";
Keys["KeyJ"] = "KeyJ";
Keys["KeyK"] = "KeyK";
Keys["KeyL"] = "KeyL";
Keys["KeyM"] = "KeyM";
Keys["KeyN"] = "KeyN";
Keys["KeyO"] = "KeyO";
Keys["KeyP"] = "KeyP";
Keys["KeyQ"] = "KeyQ";
Keys["KeyR"] = "KeyR";
Keys["KeyS"] = "KeyS";
Keys["KeyT"] = "KeyT";
Keys["KeyU"] = "KeyU";
Keys["KeyV"] = "KeyV";
Keys["KeyW"] = "KeyW";
Keys["KeyX"] = "KeyX";
Keys["KeyY"] = "KeyY";
Keys["KeyZ"] = "KeyZ";
Keys["Numpad1"] = "Numpad1";
Keys["Numpad2"] = "Numpad2";
Keys["Numpad3"] = "Numpad3";
Keys["Numpad4"] = "Numpad4";
Keys["Numpad5"] = "Numpad5";
Keys["Numpad6"] = "Numpad6";
Keys["Numpad7"] = "Numpad7";
Keys["Numpad8"] = "Numpad8";
Keys["Numpad9"] = "Numpad9";
Keys["Numpad0"] = "Numpad0";
Keys["NumpadEnter"] = "NumpadEnter";
Keys["NumpadDecimal"] = "NumpadDecimal";
Keys["PageDown"] = "PageDown";
Keys["PageUp"] = "PageUp";
Keys["Space"] = "Space";
Keys["Tab"] = "Tab";
})(Keys || (Keys = {}));
/**
* @hidden
*/
const focusableSelector = [
'a[href]:not([tabindex^="-"]):not([disabled])',
'area[href]:not([tabindex^="-"]):not([disabled])',
'input:not([tabindex^="-"]):not([disabled])',
'select:not([tabindex^="-"]):not([disabled])',
'textarea:not([tabindex^="-"]):not([disabled])',
'button:not([tabindex^="-"]):not([disabled])',
'iframe:not([tabindex^="-"]):not([disabled])',
'object:not([tabindex^="-"]):not([disabled])',
'embed:not([tabindex^="-"]):not([disabled])',
'*[tabindex]:not([tabindex^="-"]):not([disabled])',
'*[contenteditable]:not([tabindex^="-"]):not([disabled]):not([contenteditable="false"])'
].join(',');
/**
* @hidden
*
* Maps keyCode values (65-90) to Keys enum values (KeyA-KeyZ).
* Used to handle letter keys correctly across different keyboard layouts.
*/
const keyCodeToKeysMap = {
65: Keys.KeyA,
66: Keys.KeyB,
67: Keys.KeyC,
68: Keys.KeyD,
69: Keys.KeyE,
70: Keys.KeyF,
71: Keys.KeyG,
72: Keys.KeyH,
73: Keys.KeyI,
74: Keys.KeyJ,
75: Keys.KeyK,
76: Keys.KeyL,
77: Keys.KeyM,
78: Keys.KeyN,
79: Keys.KeyO,
80: Keys.KeyP,
81: Keys.KeyQ,
82: Keys.KeyR,
83: Keys.KeyS,
84: Keys.KeyT,
85: Keys.KeyU,
86: Keys.KeyV,
87: Keys.KeyW,
88: Keys.KeyX,
89: Keys.KeyY,
90: Keys.KeyZ
};
/**
* @hidden
*
* Normalizes keyboard events to ensure consistent key handling across different keyboard layouts.
*
* This function addresses the following scenarios:
* 1. On some keyboards, PageUp/Down, Home/End, and arrow keys are mapped to Numpad keys
* 2. For letter keys (KeyA-KeyZ), checks the deprecated keyCode property to handle non-QWERTY layouts
* (e.g., AZERTY, QWERTZ) where event.code may not match the expected letter
*
* @param event - The keyboard event to normalize
* @returns The normalized key code string (e.g., 'KeyA', 'ArrowDown', 'Enter')
*
* @example
* // On an AZERTY layout, pressing Ctrl+A (where 'A' is physically at 'Q' position)
* // event.code = 'KeyQ', event.keyCode = 65
* const code = normalizeKeys(event); // Returns 'KeyA'
*/
const normalizeKeys = (event) => {
const keyCode = event.keyCode;
if (keyCode >= 65 && keyCode <= 90) {
const normalizedKey = keyCodeToKeysMap[keyCode];
if (normalizedKey) {
return normalizedKey;
}
}
// Handle numpad keys that may be mapped to navigation keys
if (event.code === Keys.Numpad1 && event.key === Keys.End) {
return Keys.End;
}
if (event.code === Keys.Numpad2 && event.key === Keys.ArrowDown) {
return Keys.ArrowDown;
}
if (event.code === Keys.Numpad3 && event.key === Keys.PageDown) {
return Keys.PageDown;
}
if (event.code === Keys.Numpad4 && event.key === Keys.ArrowLeft) {
return Keys.ArrowLeft;
}
if (event.code === Keys.Numpad6 && event.key === Keys.ArrowRight) {
return Keys.ArrowRight;
}
if (event.code === Keys.Numpad7 && event.key === Keys.Home) {
return Keys.Home;
}
if (event.code === Keys.Numpad8 && event.key === Keys.ArrowUp) {
return Keys.ArrowUp;
}
if (event.code === Keys.Numpad9 && event.key === Keys.PageUp) {
return Keys.PageUp;
}
if (event.code === Keys.NumpadEnter) {
return Keys.Enter;
}
return event.code;
};
const FIELD_REGEX$1 = /\[(?:(\d+)|['"](.*?)['"])\]|((?:(?!\[.*?\]|\.).)+)/g;
const getterCache = {};
getterCache['undefined'] = () => undefined;
/**
* @hidden
*/
function getter(field) {
if (getterCache[field]) {
return getterCache[field];
}
const fields = [];
field.replace(FIELD_REGEX$1, function (_match, index, indexAccessor, fieldName) {
fields.push(index !== undefined ? index : (indexAccessor || fieldName));
});
getterCache[field] = function (obj) {
let result = obj;
for (let idx = 0; idx < fields.length && result; idx++) {
result = result[fields[idx]];
}
return result;
};
return getterCache[field];
}
const FIELD_REGEX = /\[(?:(\d+)|['"](.*?)['"])\]|((?:(?!\[.*?\]|\.).)+)/g;
const setterCache = {};
setterCache['undefined'] = (obj) => obj;
/**
* @hidden
*/
function setter(field) {
if (setterCache[field]) {
return setterCache[field];
}
const fields = [];
field.replace(FIELD_REGEX, function (_match, index, indexAccessor, fieldName) {
fields.push(index !== undefined ? index : (indexAccessor || fieldName));
});
setterCache[field] = function (obj, value) {
let root = obj;
const depth = fields.length - 1;
for (let idx = 0; idx < depth && root; idx++) {
root = root[fields[idx]] = root[fields[idx]] || {};
}
root[fields[depth]] = value;
};
return setterCache[field];
}
/**
* @hidden
*/
const watermarkStyles = `
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
right: 0;
bottom: 0;
opacity: 0.2;
zIndex: 101;
pointerEvents: none;
backgroundImage: url('data:image/png;base64,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');
`;
/**
* @hidden
*/
const licenseKeyUrl = 'https://www.telerik.com/kendo-angular-ui/components/my-license/?utm_medium=product&utm_source=kendoangular&utm_campaign=kendo-ui-angular-purchase-license-keys-banner';
let bannerPresentOnPage = false;
/**
* @hidden
*/
class WatermarkOverlayComponent {
licenseMessage;
banner;
watermarkStyle = watermarkStyles;
isOpen = true;
isMobile = false;
isNarrow = false;
isCloseHovered = false;
isCTAHovered = false;
bannerMounted = false;
get messages() {
return [this.primaryMessage, ...this.extraMessages];
}
extraMessages = [];
get primaryMessage() {
return (this.licenseMessage || {
severity: 'ERROR',
productName: '',
code: '',
message: '',
notificationTitle: 'License key missing for Kendo UI for Angular.',
notificationBody: `We couldn't verify your <a href="${this.licenseKeyUrl}">license key</a> for Kendo UI for Angular.` +
`Please see the browser console for details and resolution steps.`,
});
}
licenseKeyUrl = licenseKeyUrl;
getContent(msg) {
const iconContent = msg.notificationIcon?.content;
return {
iconSrc: iconContent ? `data:image/svg+xml;base64,${btoa(iconContent)}` : undefined,
title: msg.notificationTitle || '',
description: msg.notificationBody || msg.notificationMessage || '',
buttonText: msg.callToAction?.message || 'Buy Now',
buttonLink: msg.callToAction?.link || 'https://prgress.co/3PyHIoH',
};
}
unsubscribeLicenseMessage = () => {
/* noop */
};
unsubscribeResize = () => {
/* noop */
};
ngOnInit() {
if (bannerPresentOnPage || !this.licenseMessage || !isDocumentAvailable()) {
return;
}
this.subscribeLicenseMessage();
this.subscribeResize();
bannerPresentOnPage = true;
}
ngAfterViewInit() {
if (this.isBannerRendered) {
document.body.appendChild(this.banner.nativeElement);
}
}
ngOnDestroy() {
this.unsubscribeLicenseMessage();
this.unsubscribeResize();
if (this.isBannerRendered) {
document.body.removeChild(this.banner.nativeElement);
}
}
closeBanner() {
this.isOpen = false;
}
get isBannerRendered() {
return isDocumentAvailable() && !!this.banner?.nativeElement;
}
subscribeLicenseMessage() {
this.unsubscribeLicenseMessage = registerLicenseMessage(this.licenseMessage, 'KENDOUIANGULAR', ({ message }) => {
// Add messages from other suites
this.extraMessages.push(message);
}, () => {
// Show our own message dialog
this.bannerMounted = true;
});
}
subscribeResize() {
const handleResize = () => {
this.isMobile = window.innerWidth < 500;
this.isNarrow = window.innerWidth < 768;
};
window.addEventListener('resize', handleResize);
this.unsubscribeResize = () => window.removeEventListener('resize', handleResize);
}
// Used in tests to reset the static presence check flag
resetPresenceCheck() {
bannerPresentOnPage = false;
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: WatermarkOverlayComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.25", type: WatermarkOverlayComponent, isStandalone: true, selector: "div[kendoWatermarkOverlay], kendo-watermark-overlay", inputs: { licenseMessage: "licenseMessage" }, host: { properties: { "style": "this.watermarkStyle" } }, viewQueries: [{ propertyName: "banner", first: true, predicate: ["banner"], descendants: true }], ngImport: i0, template: `
<ng-template #buttonTemplate>
<button
[ngStyle]="{
backgroundColor: isCloseHovered ? '#3d3d3d14' : 'transparent',
border: 'none',
borderRadius: '4px',
cursor: 'pointer',
display: 'flex',
padding: '4px',
position: isMobile ? 'absolute' : 'static',
top: isMobile ? '12px' : 'auto',
right: isMobile ? '12px' : 'auto'
}"
title="Close"
(click)="closeBanner()"
(mouseenter)="isCloseHovered = true"
(mouseleave)="isCloseHovered = false"
>
<svg width="20" height="20" viewBox="0 0 16 16" fill="none">
<path
d="M11.9309 3.1838C12.1754 2.93933 12.5712 2.93937 12.8157 3.1838C13.0601 3.4283 13.0601 3.82407 12.8157 4.06857L8.885 7.99923L12.8166 11.9309C13.0611 12.1754 13.0611 12.5721 12.8166 12.8166C12.5721 13.0611 12.1754 13.0611 11.9309 12.8166L7.99925 8.88497L4.06859 12.8166C3.8241 13.0611 3.42732 13.0611 3.18285 12.8166C2.93862 12.5721 2.93851 12.1753 3.18285 11.9309L7.11449 7.99923L3.18382 4.06857C2.93947 3.82413 2.93955 3.42829 3.18382 3.1838C3.42831 2.9393 3.82508 2.9393 4.06957 3.1838L7.99925 7.11349L11.9309 3.1838Z"
fill="#212529"
/>
</svg>
</button>
</ng-template>
<ng-template #ctaTemplate>
<a
class="k-watermark-trial-button"
[attr.title]="getContent(primaryMessage).buttonText"
[attr.href]="getContent(primaryMessage).buttonLink"
target="_blank"
rel="noopener noreferrer"
[ngStyle]="{
display: 'inline-flex',
border: 'none',
borderRadius: '4px',
backgroundColor: isCTAHovered ? '#b90138' : '#eb0249',
color: '#ffffff',
transition: 'background-color 0.2s ease-in-out',
cursor: 'pointer',
padding: '4px 8px',
whiteSpace: 'nowrap',
textDecoration: 'none',
fontFamily: 'system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif'
}"
(mouseenter)="isCTAHovered = true"
(mouseleave)="isCTAHovered = false"
>{{ getContent(primaryMessage).buttonText }}</a>
</ng-template>
@if (isOpen && bannerMounted) {
<div
#banner
[ngStyle]="{
position: 'fixed',
top: isNarrow ? '0' : '16px',
left: '50%',
transform: 'translateX(-50%)',
display: 'flex',
flexDirection: isMobile ? 'column' : 'row',
justifyContent: 'center',
alignItems: isMobile ? 'flex-start' : 'center',
borderRadius: isNarrow ? '0' : '6px',
borderLeft: '6px solid #FFC000',
borderTop: '1px solid #00000029',
borderRight: '1px solid #00000029',
borderBottom: '1px solid #00000029',
boxSizing: 'border-box',
fontSize: '14px',
lineHeight: '20px',
color: '#1E1E1E',
zIndex: 2000,
boxShadow: '0px 4px 5px 0px #0000000A, 0px 2px 4px 0px #00000008',
maxWidth: isNarrow ? 'none' : '768px',
width: '100%',
backgroundColor: '#fff',
padding: isMobile ? '12px' : '0'
}"
>
@if (isMobile) {
<ng-container *ngTemplateOutlet="buttonTemplate"></ng-container>
} @if (getContent(primaryMessage).iconSrc) {
<span
[ngStyle]="{
display: 'flex',
alignSelf: isMobile ? 'flex-start' : 'center',
padding: isMobile ? '0 0 1