@progress/kendo-ui
Version:
This package is part of the [Kendo UI for jQuery](http://www.telerik.com/kendo-ui) suite.
1,767 lines (1,752 loc) • 213 kB
JavaScript
const require_kendo_licensing = require('./kendo.licensing-DQkab2ZD.js');
//#region ../src/utils/mediaquery.js
let breakpoints;
const EVENT = "change";
const defaultBreakpoints = {
small: "(max-width: 500px)",
medium: "(min-width: 500.1px) and (max-width: 768px)",
large: "(min-width: 768.1px)"
};
function createMediaQuery(query) {
let mediaQueryList = window.matchMedia(query);
let onEnterCallbacks = [];
let onLeaveCallbacks = [];
let onChangeHandlers = [];
let kendoMediaQuery = { mediaQueryList };
const onChangeHandler = (ev) => {
onChangeHandlers.forEach((cb) => cb(ev));
if (ev.matches) {
onEnterCallbacks.forEach((cb) => cb(ev));
} else {
onLeaveCallbacks.forEach((cb) => cb(ev));
}
};
mediaQueryList.addEventListener(EVENT, onChangeHandler);
const onChange = (cb) => {
onChangeHandlers.push(cb);
return kendoMediaQuery;
};
const onEnter = (cb) => {
onEnterCallbacks.push(cb);
if (mediaQueryList.matches) {
const media = mediaQueryList.media;
const matches = true;
const ev = new MediaQueryListEvent(EVENT, {
media,
matches
});
cb(ev);
}
return kendoMediaQuery;
};
const onLeave = (cb) => {
onLeaveCallbacks.push(cb);
return kendoMediaQuery;
};
const destroy = () => {
if (mediaQueryList) {
mediaQueryList.removeEventListener(EVENT, onChangeHandler);
}
onEnterCallbacks = null;
onLeaveCallbacks = null;
onChangeHandlers = null;
mediaQueryList = null;
kendoMediaQuery = null;
};
kendoMediaQuery.onChange = onChange;
kendoMediaQuery.onEnter = onEnter;
kendoMediaQuery.onLeave = onLeave;
kendoMediaQuery.destroy = destroy;
return kendoMediaQuery;
}
function mediaQuery(query) {
if (!query) {
return;
}
breakpoints = breakpoints || Object.assign({}, defaultBreakpoints, kendo.defaults.breakpoints);
if (query in breakpoints) {
query = breakpoints[query];
}
return createMediaQuery(query);
}
//#endregion
//#region ../src/utils/convert-class.js
function fromESClass(ESClass) {
class ExtendedClass extends ESClass {
static extend(proto) {
const subclass = class extends ExtendedClass {
constructor() {
super();
if (proto && proto.init) {
proto.init.apply(this, arguments);
}
}
};
Object.assign(subclass.prototype, proto);
addInstanceGetter(subclass.prototype);
subclass.fn = subclass.prototype;
return subclass;
}
}
addInstanceGetter(ExtendedClass.prototype);
ExtendedClass.fn = ExtendedClass.prototype;
return ExtendedClass;
}
function addInstanceGetter(proto) {
Object.defineProperty(proto, "_instance", { get: function() {
return this;
} });
}
//#endregion
//#region ../src/core/models/date-utils.ts
/**
* Date utility type definitions
*/
/**
* Date field mapping for format parsing
*/
const DATE_FIELD_MAP = {
"G": "era",
"y": "year",
"q": "quarter",
"Q": "quarter",
"M": "month",
"L": "month",
"d": "day",
"E": "weekday",
"c": "weekday",
"e": "weekday",
"h": "hour",
"H": "hour",
"k": "hour",
"K": "hour",
"m": "minute",
"s": "second",
"a": "dayperiod",
"t": "dayperiod",
"x": "zone",
"X": "zone",
"z": "zone",
"Z": "zone"
};
/**
* Name types for date formatting
*/
const NAME_TYPES = {
month: {
type: "months",
minLength: 3,
standAlone: "L"
},
quarter: {
type: "quarters",
minLength: 3,
standAlone: "q"
},
weekday: {
type: "days",
minLength: {
E: 0,
c: 3,
e: 3
},
standAlone: "c"
},
dayperiod: {
type: "dayPeriods",
minLength: 0
},
era: {
type: "eras",
minLength: 0
}
};
//#endregion
//#region ../src/core/models/dom-utils.ts
/**
* Animation directions
*/
const DIRECTIONS = {
left: { reverse: "right" },
right: { reverse: "left" },
down: { reverse: "up" },
up: { reverse: "down" },
top: { reverse: "bottom" },
bottom: { reverse: "top" },
"in": { reverse: "out" },
out: { reverse: "in" }
};
//#endregion
//#region ../src/core/services/support.service.ts
const UNDEFINED$2 = "undefined";
/**
* Test a value against a set of regex patterns
*/
function testRx(agent, rxs, dflt) {
for (const rx in rxs) {
if (rxs.hasOwnProperty(rx) && rxs[rx].test(agent)) {
return rx;
}
}
return dflt !== undefined ? dflt : agent;
}
/**
* Support detection service for browser, OS, and feature detection
*/
var SupportService = class {
constructor() {
this._scrollbar = undefined;
this.scrollbar = this.scrollbar.bind(this);
this.isRtl = this.isRtl.bind(this);
this.detectOS = this.detectOS.bind(this);
this.detectBrowser = this.detectBrowser.bind(this);
this.detectClipboardAccess = this.detectClipboardAccess.bind(this);
this.zoomLevel = this.zoomLevel.bind(this);
this.delayedClick = this.delayedClick.bind(this);
this.initialize();
}
initialize() {
const win = window;
const doc = document;
const nav = navigator;
const table = doc.createElement("table");
try {
table.innerHTML = "<tr><td></td></tr>";
this.tbodyInnerHtml = true;
} catch (e) {
this.tbodyInnerHtml = false;
}
this.touch = "ontouchstart" in win;
const docStyle = doc.documentElement.style;
const elementProto = "HTMLElement" in win ? HTMLElement.prototype : [];
this.transforms = this.transitions = {
css: "",
prefix: "",
event: "transitionend"
};
this.hasHW3D = "WebKitCSSMatrix" in win && "m11" in new win.WebKitCSSMatrix() || "MozPerspective" in docStyle || "msPerspective" in docStyle;
this.cssFlexbox = "flexWrap" in docStyle || "WebkitFlexWrap" in docStyle || "msFlexWrap" in docStyle;
this.devicePixelRatio = win.devicePixelRatio === undefined ? 1 : win.devicePixelRatio;
try {
this.screenWidth = win.outerWidth || win.screen ? win.screen.availWidth : win.innerWidth;
this.screenHeight = win.outerHeight || win.screen ? win.screen.availHeight : win.innerHeight;
} catch (e) {
this.screenWidth = win.screen.availWidth;
this.screenHeight = win.screen.availHeight;
}
let mobileOS = this.detectOS(nav.userAgent);
this.mobileOS = mobileOS;
this.wpDevicePixelRatio = mobileOS && mobileOS.wp ? screen.width / 320 : 0;
this.hasNativeScrolling = false;
if (mobileOS && (mobileOS.ios || mobileOS.android && parseInt(mobileOS.majorVersion) > 2 || mobileOS.wp)) {
this.hasNativeScrolling = mobileOS;
}
this.mouseAndTouchPresent = this.touch && !(mobileOS && (mobileOS.ios || mobileOS.android));
this.browser = this.detectBrowser(nav.userAgent);
if (!mobileOS && this.touch && this.browser.safari) {
mobileOS = this.mobileOS = {
ios: true,
tablet: "tablet",
device: "ipad",
majorVersion: "13",
minorVersion: "0",
flatVersion: "1300",
cordova: false,
appMode: false,
name: "ios",
browser: "mobilesafari"
};
}
this.clipboard = this.detectClipboardAccess();
this.eventCapture = !!doc.documentElement.addEventListener;
const input = doc.createElement("input");
this.placeholder = "placeholder" in input;
this.propertyChangeEvent = "onpropertychange" in input;
this.input = this.detectInputTypes(input);
input.style.cssText = "float:left;";
this.cssFloat = !!input.style.cssFloat;
this.stableSort = this.detectStableSort();
this.matchesSelector = elementProto.webkitMatchesSelector || elementProto.mozMatchesSelector || elementProto.msMatchesSelector || elementProto.oMatchesSelector || elementProto.matchesSelector || elementProto.matches || function(selector) {
const nodeList = doc.querySelectorAll ? (this.parentNode || doc).querySelectorAll(selector) || [] : $(selector);
let i = nodeList.length;
while (i--) {
if (nodeList[i] === this) {
return true;
}
}
return false;
};
this.matchMedia = "matchMedia" in win;
this.pushState = !!(win.history && win.history.pushState);
this.hashChange = "onhashchange" in win;
this.customElements = "registerElement" in doc;
const chrome = this.browser.chrome;
const mobileChrome = this.browser.crios;
const mozilla = this.browser.mozilla;
const safari = this.browser.safari;
this.msPointers = !chrome && win.MSPointerEvent;
this.pointers = !chrome && !mobileChrome && !mozilla && !safari && win.PointerEvent;
this.kineticScrollNeeded = !!(mobileOS && (mobileOS.device !== "ipad" || parseInt(mobileOS.majorVersion) < 13) && (this.touch || this.msPointers || this.pointers));
if (this.touch) {
if (!this.mobileOS) {
this.mousedown = "mousedown touchstart";
this.mouseup = "mouseup touchend";
this.mousemove = "mousemove touchmove";
this.mousecancel = "mouseleave touchcancel";
this.click = "click";
this.resize = "resize";
} else {
this.mousedown = "touchstart";
this.mouseup = "touchend";
this.mousemove = "touchmove";
this.mousecancel = "touchcancel";
this.click = "touchend";
this.resize = "orientationchange";
}
} else if (this.pointers) {
this.mousemove = "pointermove";
this.mousedown = "pointerdown";
this.mouseup = "pointerup";
this.mousecancel = "pointercancel";
this.click = "pointerup";
this.resize = "orientationchange resize";
} else if (this.msPointers) {
this.mousemove = "MSPointerMove";
this.mousedown = "MSPointerDown";
this.mouseup = "MSPointerUp";
this.mousecancel = "MSPointerCancel";
this.click = "MSPointerUp";
this.resize = "orientationchange resize";
} else {
this.mousemove = "mousemove";
this.mousedown = "mousedown";
this.mouseup = "mouseup";
this.mousecancel = "mouseleave";
this.click = "click";
this.resize = "resize";
}
this.addBrowserCssClasses($);
}
/**
* Get or calculate scrollbar width
*/
scrollbar(refresh) {
if (!isNaN(this._scrollbar) && !refresh) {
return this._scrollbar;
}
const div = document.createElement("div");
div.style.cssText = "overflow:scroll;overflow-x:hidden;zoom:1;clear:both;display:block";
div.innerHTML = " ";
document.body.appendChild(div);
this._scrollbar = div.offsetWidth - div.scrollWidth;
document.body.removeChild(div);
return this._scrollbar;
}
/**
* Check if element is in RTL context
*/
isRtl(element) {
return $(element).closest(".k-rtl").length > 0;
}
/**
* Detect mobile operating system from user agent
*/
detectOS(ua) {
let os = false;
let minorVersion;
let match = null;
const notAndroidPhone = !/mobile safari/i.test(ua);
const agentRxs = {
wp: /(Windows Phone(?: OS)?)\s(\d+)\.(\d+(\.\d+)?)/,
fire: /(Silk)\/(\d+)\.(\d+(\.\d+)?)/,
android: /(Android|Android.*(?:Opera|Firefox).*?\/)\s*(\d+)\.?(\d+(\.\d+)?)?/,
iphone: /(iPhone|iPod).*OS\s+(\d+)[\._]([\d\._]+)/,
ipad: /(iPad).*OS\s+(\d+)[\._]([\d_]+)/,
meego: /(MeeGo).+NokiaBrowser\/(\d+)\.([\d\._]+)/,
webos: /(webOS)\/(\d+)\.(\d+(\.\d+)?)/,
blackberry: /(BlackBerry|BB10).*?Version\/(\d+)\.(\d+(\.\d+)?)/,
playbook: /(PlayBook).*?Tablet\s*OS\s*(\d+)\.(\d+(\.\d+)?)/,
windows: /(MSIE)\s+(\d+)\.(\d+(\.\d+)?)/,
tizen: /(tizen).*?Version\/(\d+)\.(\d+(\.\d+)?)/i,
sailfish: /(sailfish).*rv:(\d+)\.(\d+(\.\d+)?).*firefox/i,
ffos: /(Mobile).*rv:(\d+)\.(\d+(\.\d+)?).*Firefox/
};
const osRxs = {
ios: /^i(phone|pad|pod)$/i,
android: /^android|fire$/i,
blackberry: /^blackberry|playbook/i,
windows: /windows/,
wp: /wp/,
flat: /sailfish|ffos|tizen/i,
meego: /meego/
};
const formFactorRxs = { tablet: /playbook|ipad|fire/i };
const browserRxs = {
omini: /Opera\sMini/i,
omobile: /Opera\sMobi/i,
firefox: /Firefox|Fennec/i,
mobilesafari: /version\/.*safari/i,
ie: /MSIE|Windows\sPhone/i,
chrome: /chrome|crios/i,
webkit: /webkit/i,
edge: /edge|edg|edgios|edga/i
};
for (const agent in agentRxs) {
if (agentRxs.hasOwnProperty(agent)) {
match = ua.match(agentRxs[agent]);
if (match) {
if (agent === "windows" && "plugins" in navigator) {
return false;
}
os = {};
os.device = agent;
os.tablet = testRx(agent, formFactorRxs, false);
os.browser = testRx(ua, browserRxs, "default");
os.name = testRx(agent, osRxs);
os[os.name] = true;
os.majorVersion = match[2];
os.minorVersion = (match[3] || "0").replace("_", ".");
minorVersion = os.minorVersion.replace(".", "").substr(0, 2);
os.flatVersion = os.majorVersion + minorVersion + new Array(3 - (minorVersion.length < 3 ? minorVersion.length : 2)).join("0");
os.cordova = typeof window.PhoneGap !== UNDEFINED$2 || typeof window.cordova !== UNDEFINED$2;
os.appMode = !!navigator.standalone || /file|local|wmapp/.test(window.location.protocol) || os.cordova;
if (os.android && (this.devicePixelRatio < 1.5 && parseInt(os.flatVersion) < 400 || notAndroidPhone) && (this.screenWidth > 800 || this.screenHeight > 800)) {
os.tablet = agent;
}
break;
}
}
}
return os;
}
/**
* Detect browser from user agent
*/
detectBrowser(ua) {
let browser = false;
let match;
let chromiumEdgeMatch;
const browserRxs = {
edge: /(edge)[ \/]([\w.]+)/i,
webkit: /(chrome|crios)[ \/]([\w.]+)/i,
safari: /(webkit)[ \/]([\w.]+)/i,
opera: /(opera)(?:.*version|)[ \/]([\w.]+)/i,
msie: /(msie\s|trident.*? rv:)([\w.]+)/i,
mozilla: /(mozilla)(?:.*? rv:([\w.]+)|)/i
};
for (const agent in browserRxs) {
if (browserRxs.hasOwnProperty(agent)) {
match = ua.match(browserRxs[agent]);
if (match) {
browser = {};
browser[agent] = true;
browser[match[1].toLowerCase().split(" ")[0].split("/")[0]] = true;
browser.version = parseInt(document.documentMode || match[2], 10);
if (browser.chrome) {
chromiumEdgeMatch = ua.match(/(edg)[ \/]([\w.]+)/i);
if (chromiumEdgeMatch) {
browser.chromiumEdge = true;
}
}
break;
}
}
}
return browser || { version: 0 };
}
/**
* Detect clipboard command support
*/
detectClipboardAccess() {
const doc = document;
const commands = {
copy: doc.queryCommandSupported ? doc.queryCommandSupported("copy") : false,
cut: doc.queryCommandSupported ? doc.queryCommandSupported("cut") : false,
paste: doc.queryCommandSupported ? doc.queryCommandSupported("paste") : false
};
if (this.browser.chrome) {
commands.paste = false;
if (this.browser.version >= 43) {
commands.copy = true;
commands.cut = true;
}
}
return commands;
}
/**
* Get current zoom level
*/
zoomLevel() {
try {
const browser = this.browser;
let ie11WidthCorrection = 0;
const docEl = document.documentElement;
if (browser.msie && browser.version === 11 && docEl.scrollHeight > docEl.clientHeight && !this.touch) {
ie11WidthCorrection = this.scrollbar();
}
return this.touch ? docEl.clientWidth / window.innerWidth : browser.msie && browser.version >= 10 ? ((top || window).document.documentElement.offsetWidth + ie11WidthCorrection) / (top || window).innerWidth : 1;
} catch (e) {
return 1;
}
}
/**
* Check if device has delayed click behavior
*/
delayedClick() {
if (this.touch) {
const mobileOS = this.mobileOS;
if (mobileOS && mobileOS.ios) {
return true;
}
if (mobileOS && mobileOS.android) {
if (!this.browser.chrome) {
return true;
}
if (this.browser.version < 32) {
return false;
}
return !($("meta[name=viewport]").attr("content") || "").match(/user-scalable=no/i);
}
}
return false;
}
/**
* Detect native input type support
*/
detectInputTypes(input) {
const types = [
"number",
"date",
"time",
"month",
"week",
"datetime",
"datetime-local"
];
const value = "test";
const result = {};
for (const type of types) {
input.setAttribute("type", type);
input.value = value;
result[type.replace("-", "")] = input.type !== "text" && input.value !== value;
}
return result;
}
/**
* Detect if sort is stable
*/
detectStableSort() {
const threshold = 513;
const sorted = [{
index: 0,
field: "b"
}];
for (let i = 1; i < threshold; i++) {
sorted.push({
index: i,
field: "a"
});
}
sorted.sort((a, b) => {
return a.field > b.field ? 1 : a.field < b.field ? -1 : 0;
});
return sorted[0].index === 1;
}
/**
* Add browser-specific CSS classes to document element
*/
addBrowserCssClasses($) {
const browser = this.browser;
let cssClass = "";
const docElement = $(document.documentElement);
const majorVersion = parseInt(String(browser.version), 10);
if (browser.msie) {
cssClass = "ie";
} else if (browser.mozilla) {
cssClass = "ff";
} else if (browser.safari) {
cssClass = "safari";
} else if (browser.webkit) {
cssClass = "webkit";
} else if (browser.opera) {
cssClass = "opera";
} else if (browser.edge) {
cssClass = "edge";
}
if (cssClass) {
cssClass = "k-" + cssClass + " k-" + cssClass + majorVersion;
}
if (this.mobileOS) {
cssClass += " k-mobile";
}
if (!this.cssFlexbox) {
cssClass += " k-no-flexbox";
}
docElement.addClass(cssClass);
}
/**
* Convert Bootstrap breakpoint name to CSS media query
*/
bootstrapToMedia(bootstrapMedia) {
const bootstrapBreakpoints = {
"xs": "(max-width: 576px)",
"sm": "(min-width: 576px)",
"md": "(min-width: 768px)",
"lg": "(min-width: 992px)",
"xl": "(min-width: 1200px)"
};
return bootstrapBreakpoints[bootstrapMedia];
}
/**
* Check if a media query matches
* Supports both CSS media queries and Bootstrap breakpoint names
*/
matchesMedia(mediaQuery) {
const media = this.bootstrapToMedia(mediaQuery) || mediaQuery;
return this.matchMedia && window.matchMedia(media).matches;
}
};
const supportService = new SupportService();
//#endregion
//#region ../src/core/services/mouse-event-normalizer.service.ts
/**
* Mouse Event Normalizer Service
* Handles mouse event capturing and muting for touch/pointer event normalization.
*/
/**
* Mouse event normalizer service for handling touch/mouse event conflicts.
* Prevents ghost clicks and normalizes touch events to mouse events.
*/
var MouseEventNormalizerService = class {
constructor() {
this.mouseTrap = false;
this.bustClick = false;
this.captureMouse = false;
this.MOUSE_EVENTS = [
"mousedown",
"mousemove",
"mouseenter",
"mouseleave",
"mouseover",
"mouseout",
"mouseup",
"click"
];
this.EXCLUDE_BUST_CLICK_SELECTOR = "label, input, [data-rel=external]";
}
/**
* Set up mouse event capturing to prevent ghost clicks from touch events.
* This sets up event listeners on document.documentElement to intercept
* and optionally stop mouse events when touch events are active.
*/
setupMouseMute() {
let idx = 0;
const length = this.MOUSE_EVENTS.length;
const element = document.documentElement;
if (this.mouseTrap || !supportService.eventCapture) {
return;
}
this.mouseTrap = true;
this.bustClick = false;
this.captureMouse = false;
const self = this;
const handler = function(e) {
if (self.captureMouse) {
if (e.type === "click") {
if (self.bustClick && !$(e.target).is(self.EXCLUDE_BUST_CLICK_SELECTOR)) {
e.preventDefault();
e.stopPropagation();
}
} else {
e.stopPropagation();
}
}
};
for (; idx < length; idx++) {
element.addEventListener(this.MOUSE_EVENTS[idx], handler, true);
}
}
/**
* Mute mouse events. Called on touchstart to prevent ghost clicks.
* @param e - The jQuery event with bustClick data
*/
muteMouse(e) {
this.captureMouse = true;
if (e.data?.bustClick) {
this.bustClick = true;
}
clearTimeout(this.mouseTrapTimeoutID);
}
/**
* Unmute mouse events. Called on touchend after a delay to allow
* legitimate mouse events through again.
*/
unMuteMouse() {
clearTimeout(this.mouseTrapTimeoutID);
this.mouseTrapTimeoutID = setTimeout(() => {
this.captureMouse = false;
this.bustClick = false;
}, 400);
}
};
const mouseEventNormalizerService = new MouseEventNormalizerService();
//#endregion
//#region ../src/core/services/event-map.service.ts
/**
* Event Map Service
* Provides cross-browser event mapping for touch, pointer, and mouse events.
*/
/**
* Event map service for cross-browser event handling.
* Maps abstract event names (down, move, up, cancel) to the appropriate
* browser-specific events based on touch/pointer/mouse support.
*/
var EventMapService = class {
constructor() {
this.eventRegEx = /([^ ]+)/g;
this.eventMap = this.buildEventMap();
this.setupMSPointerEvents();
}
/**
* Build the event map based on browser capabilities
*/
buildEventMap() {
let map = {
down: "touchstart mousedown",
move: "mousemove touchmove",
up: "mouseup touchend touchcancel",
cancel: "mouseleave touchcancel"
};
if (supportService.touch && supportService.mobileOS && (supportService.mobileOS.ios || supportService.mobileOS.android)) {
map = {
down: "touchstart",
move: "touchmove",
up: "touchend touchcancel",
cancel: "touchcancel"
};
} else if (supportService.pointers) {
map = {
down: "pointerdown",
move: "pointermove",
up: "pointerup",
cancel: "pointercancel pointerleave"
};
} else if (supportService.msPointers) {
map = {
down: "MSPointerDown",
move: "MSPointerMove",
up: "MSPointerUp",
cancel: "MSPointerCancel MSPointerLeave"
};
}
return map;
}
/**
* Setup MSPointerEnter/MSPointerLeave events for IE10
* Creates these events using mouseover/out and event-time checks
*/
setupMSPointerEvents() {
if (supportService.msPointers && !("onmspointerenter" in window)) {
$.each({
MSPointerEnter: "MSPointerOver",
MSPointerLeave: "MSPointerOut"
}, (orig, fix) => {
$.event.special[orig] = {
delegateType: fix,
bindType: fix,
handle: function(event) {
let ret;
const target = this;
const related = event.relatedTarget;
const handleObj = event.handleObj;
if (!related || related !== target && !this.$.contains(target, related)) {
event.type = handleObj.origType;
ret = handleObj.handler.apply(this, arguments);
event.type = fix;
}
return ret;
}
};
});
}
}
/**
* Get the mapped event for an abstract event name
* @param eventName - The abstract event name (down, move, up, cancel) or specific event
* @returns The browser-specific event(s) or the original event if no mapping exists
*/
getEventMap(eventName) {
return this.eventMap[eventName] || eventName;
}
/**
* Get the full event map object
*/
getFullEventMap() {
return { ...this.eventMap };
}
/**
* Apply event mapping to a space-separated list of events
* @param events - Space-separated event names to map
* @param ns - Optional namespace to append to each event
* @returns The mapped and namespaced events
*/
applyEventMap(events, ns) {
events = events.replace(this.eventRegEx, (e) => this.getEventMap(e));
if (ns) {
events = events.replace(this.eventRegEx, "$1." + ns);
}
return events;
}
};
const eventMapService = new EventMapService();
//#endregion
//#region ../src/core/services/utils.service.ts
const OBJECT = "object";
const UNDEFINED$1 = "undefined";
/**
* Utility service providing general helper functions.
*/
var UtilsService = class {
constructor() {
this.keys = {
INSERT: 45,
DELETE: 46,
BACKSPACE: 8,
TAB: 9,
ENTER: 13,
ESC: 27,
LEFT: 37,
UP: 38,
RIGHT: 39,
DOWN: 40,
END: 35,
HOME: 36,
SPACEBAR: 32,
PAGEUP: 33,
PAGEDOWN: 34,
F2: 113,
F10: 121,
F12: 123,
SHIFT: 16,
NUMPAD_PLUS: 107,
NUMPAD_MINUS: 109,
NUMPAD_DOT: 110
};
this.days = {
Sunday: 0,
Monday: 1,
Tuesday: 2,
Wednesday: 3,
Thursday: 4,
Friday: 5,
Saturday: 6
};
}
/**
* Get kendo.data namespace (accessed at call time for lazy loading)
*/
get kendoData() {
return window.kendo?.data || {};
}
/**
* Convert camelCase to hyphen-case
*/
toHyphens(str) {
return str.replace(/([a-z][A-Z])/g, (g) => {
return g.charAt(0) + "-" + g.charAt(1).toLowerCase();
});
}
/**
* Convert hyphen-case to camelCase
*/
toCamelCase(str) {
return str.replace(/\-(\w)/g, (_strMatch, g1) => {
return g1.toUpperCase();
});
}
/**
* Count properties in an object (excluding toJSON for IE7 compat)
*/
size(obj) {
let result = 0;
for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key) && key !== "toJSON") {
result++;
}
}
return result;
}
/**
* Deep extend an object with one or more source objects.
*/
deepExtend(destination, ...sources) {
for (let i = 0; i < sources.length; i++) {
this.deepExtendOne(destination, sources[i]);
}
return destination;
}
/**
* Deep extend destination with a single source object.
* This is the core implementation that handles all the special cases.
*/
deepExtendOne(destination, source) {
const ObservableArray = this.kendoData.ObservableArray;
const LazyObservableArray = this.kendoData.LazyObservableArray;
const DataSource = this.kendoData.DataSource;
const HierarchicalDataSource = this.kendoData.HierarchicalDataSource;
for (const property in source) {
if (property === "__proto__" || property === "constructor" || property === "prototype") {
continue;
}
const propValue = source[property];
const propType = typeof propValue;
let propInit = null;
if (propType === OBJECT && propValue !== null) {
propInit = propValue.constructor;
}
let isRegExp = propInit?.name === "RegExp";
let isArrayBuffer = propInit?.name === "ArrayBuffer";
let isDate = propInit?.name === "Date";
if (propInit && !Array.isArray(propValue) && propInit !== ObservableArray && propInit !== LazyObservableArray && propInit !== DataSource && propInit !== HierarchicalDataSource && !isRegExp && (!this.isFunction(window.ArrayBuffer) || !isArrayBuffer) && !(propValue instanceof HTMLElement)) {
if (isDate) {
destination[property] = new Date(propValue.getTime());
} else if (this.isCloneable(propValue)) {
destination[property] = propValue.clone();
} else {
const destProp = destination[property];
if (typeof destProp === OBJECT) {
destination[property] = destProp || {};
} else {
destination[property] = {};
}
this.deepExtendOne(destination[property], propValue);
}
} else if (propType !== UNDEFINED$1) {
destination[property] = propValue;
}
}
return destination;
}
/**
* Check if an object has a clone method
*/
isCloneable(obj) {
return typeof obj.clone === "function";
}
/**
* Create a throttled version of a function.
* The throttled function will only execute at most once per delay period.
* Includes a cancel() method to clear any pending execution.
*
* If delay is falsy (0, null, undefined), returns the original function unchanged.
*/
throttle(fn, delay) {
if (!delay || delay <= 0) {
return fn;
}
let timeout;
let lastExecTime = 0;
const throttled = function(...args) {
const that = this;
const elapsed = +new Date() - lastExecTime;
function exec() {
const result = fn.apply(that, args);
lastExecTime = +new Date();
return result;
}
if (!lastExecTime) {
return exec();
}
if (timeout) {
clearTimeout(timeout);
}
if (elapsed > delay) {
return exec();
} else {
timeout = setTimeout(exec, delay - elapsed);
}
};
throttled.cancel = function() {
if (timeout) {
clearTimeout(timeout);
timeout = undefined;
}
};
return throttled;
}
/**
* Generate a UUID.
* Uses crypto.randomUUID() when available (HTTPS only),
* falls back to crypto.getRandomValues().
*/
guid() {
const cryptoObj = window.crypto;
try {
return cryptoObj.randomUUID();
} catch (e) {
const randomValues = cryptoObj.getRandomValues(new Uint8Array(16));
return randomValues.reduce((acc, curr, i) => {
if (i === 4 || i === 6 || i === 8 || i === 10) {
acc += "-";
}
acc += curr.toString(16).padStart(2, "0");
return acc;
}, "");
}
}
/**
* Trim whitespace from a value.
* Converts to string first, returns empty string for falsy values.
*/
trim(value) {
if (value) {
return value.toString().trim();
}
return "";
}
/**
* Check if a value is present (not null and not undefined)
*/
isPresent(value) {
return value !== null && value !== undefined;
}
/**
* Check if a value is blank (null or undefined)
*/
isBlank(value) {
return value === null || value === undefined;
}
/**
* Check if a value is empty (has length 0)
*/
isEmpty(value) {
return value.length === 0;
}
/**
* Check if a value is a string
*/
isString(value) {
return typeof value === "string";
}
/**
* Check if a value is an integer
*/
isInteger(value) {
return Number.isInteger(value);
}
/**
* Check if a value is numeric
*/
isNumeric(value) {
return !isNaN(value - parseFloat(value));
}
/**
* Check if a value is a Date object
*/
isDate(value) {
return value && value.getTime;
}
/**
* Check if a value is a function
*/
isFunction(value) {
return typeof value === "function";
}
/**
* Check if a value is an object (and not null)
*/
isObject(value) {
return value !== null && typeof value === OBJECT;
}
/**
* Log a message to the console.
* Respects kendo.suppressLog setting.
* @param message - The message to log
* @param type - Console method to use ("log", "warn", "error", etc.). Defaults to "log"
*/
logToConsole(message, type) {
const console = window.console;
const kendo = window.kendo;
if (!kendo.suppressLog && typeof console !== "undefined" && console.log) {
console[type || "log"](message);
}
}
/**
* Wait for all promises to resolve, similar to Promise.all but for jQuery Deferreds.
* Unlike $.when(), this handles failures gracefully and reports all results.
*
* Influenced from: https://gist.github.com/fearphage/4341799
*
* @param array - Array of deferreds/promises, or multiple arguments
* @returns A jQuery Promise that resolves when all inputs resolve, or rejects if any fail
*/
whenAll(array) {
const $ = window.jQuery;
const resolveValues = arguments.length === 1 && Array.isArray(array) ? array : Array.prototype.slice.call(arguments);
const length = resolveValues.length;
let remaining = length;
const deferred = $.Deferred();
let i = 0;
let failed = 0;
const rejectContexts = new Array(length);
const rejectValues = new Array(length);
const resolveContexts = new Array(length);
let value;
const updateFunc = (index, contexts, values) => {
return function() {
if (values !== resolveValues) {
failed++;
}
deferred.notifyWith(contexts[index] = this, values[index] = Array.prototype.slice.call(arguments));
if (!--remaining) {
deferred[(!failed ? "resolve" : "reject") + "With"](contexts, values);
}
};
};
for (; i < length; i++) {
value = resolveValues[i];
if (value && this.isFunction(value.promise)) {
value.promise().done(updateFunc(i, resolveContexts, resolveValues)).fail(updateFunc(i, rejectContexts, rejectValues));
} else {
deferred.notifyWith(this, value);
--remaining;
}
}
if (!remaining) {
deferred.resolveWith(resolveContexts, resolveValues);
}
return deferred.promise();
}
/**
* Check if a URL is local (doesn't start with a protocol)
* @param url - URL to check
* @returns True if the URL is local
*/
isLocalUrl(url) {
return url && !/^([a-z]+:)?\/\//i.test(url);
}
/**
* Get all method names (static and instance) from a class
* @param targetClass - The class to inspect
* @returns Array of method names
*/
getAllMethods(targetClass) {
const allStatic = Object.getOwnPropertyNames(targetClass).filter((prop) => typeof targetClass[prop] === "function");
const allNonStatic = Object.getOwnPropertyNames(Object.getPrototypeOf(new targetClass({}))).filter((prop) => prop !== "constructor");
return allStatic.concat(allNonStatic);
}
/**
* Get the base class (parent class) of a given class
* @param targetClass - The class to get the parent of
* @returns The parent class or null if none
*/
getBaseClass(targetClass) {
if (targetClass instanceof Function) {
const baseClass = targetClass;
const newBaseClass = Object.getPrototypeOf(baseClass);
if (newBaseClass && newBaseClass !== Object && newBaseClass.name) {
return newBaseClass;
}
}
return null;
}
/**
* Create a proxy member on a prototype that delegates to an instance
* @param proto - The prototype object to add the member to
* @param name - The name of the member to create
*/
createProxyMember(proto, name) {
proto.fn[name] = function() {
const instance = this._instance;
if (instance) {
return instance[name].apply(instance, arguments);
}
};
}
/**
* Convert a native Promise to a jQuery Deferred
* @param promise - The native Promise to convert
* @returns A jQuery Promise
*/
convertPromiseToDeferred(promise) {
const deferred = $.Deferred();
promise.finally(deferred.always).then(deferred.resolve).catch(deferred.reject);
return deferred.promise();
}
};
const utilsService = new UtilsService();
//#endregion
//#region ../src/core/services/kendo-jquery.service.ts
/**
* KendoJQuery Service
*
* Provides a jQuery wrapper with Kendo-specific functionality:
* - Event namespacing with automatic cleanup
* - Handler context binding
* - Touch/mouse event normalization
* - Keyboard event handling
*/
const STRING$5 = "string";
const UNDEFINED = "undefined";
/**
* KendoJQuery Service
* Creates and manages the KendoJQuery wrapper around jQuery
*/
var KendoJQueryService = class {
constructor() {
this.originalOn = $.fn.on;
this.kendoJQuery = this.createKendoJQuery();
this.rootjQuery = this.kendoJQuery(document);
}
/**
* Extend objects while avoiding jQuery deprecated properties
*/
noDeprecateExtend(deep, target, ...sources) {
let src, copyIsArray, copy, name, options, clone;
if (typeof target !== "object" && typeof target !== "function") {
target = {};
}
for (const source of sources) {
if (source != null) {
for (name in source) {
if (name === "filters" || name === "concat" || name === ":" || name === "cssNumber") {
continue;
}
src = target[name];
copy = source[name];
if (target === copy) {
continue;
}
if (deep && copy && ($.isPlainObject(copy) || (copyIsArray = Array.isArray(copy)))) {
if (copyIsArray) {
copyIsArray = false;
clone = src && Array.isArray(src) ? src : [];
} else {
clone = src && $.isPlainObject(src) ? src : {};
}
target[name] = this.noDeprecateExtend(deep, clone, copy);
} else if (copy !== undefined) {
target[name] = copy;
}
}
}
}
return target;
}
/**
* Create the KendoJQuery constructor and prototype
*/
createKendoJQuery() {
const self = this;
const kendoJQuery = function(selector, context) {
return new kendoJQuery.fn.init(selector, context);
};
this.noDeprecateExtend(true, kendoJQuery, $);
kendoJQuery.fn = kendoJQuery.prototype = new $();
kendoJQuery.fn.constructor = kendoJQuery;
kendoJQuery.fn.init = function(selector, context) {
if (context && context instanceof $ && !(context instanceof kendoJQuery)) {
context = kendoJQuery(context);
}
return $.fn.init.call(this, selector, context, self.rootjQuery);
};
kendoJQuery.fn.init.prototype = kendoJQuery.fn;
$.extend(kendoJQuery.fn, {
handler: function(handler) {
this.data("handler", handler);
return this;
},
autoApplyNS: function(ns) {
this.data("kendoNS", ns || utilsService.guid());
return this;
},
on: function(...args) {
const that = this;
const ns = that.data("kendoNS");
const on = self.originalOn;
if (args.length === 1) {
return on.call(that, args[0]);
}
let context = that;
const argsCopy = args.slice();
if (typeof argsCopy[argsCopy.length - 1] === UNDEFINED) {
argsCopy.pop();
}
const callback = argsCopy[argsCopy.length - 1];
const events = eventMapService.applyEventMap(argsCopy[0], ns);
if (supportService.mouseAndTouchPresent && events.search(/mouse|click/) > -1 && this[0] !== document.documentElement) {
mouseEventNormalizerService.setupMouseMute();
const selector = argsCopy.length === 2 ? null : argsCopy[1];
const bustClick = events.indexOf("click") > -1 && events.indexOf("touchend") > -1;
on.call(this, {
touchstart: (e) => mouseEventNormalizerService.muteMouse(e),
touchend: () => mouseEventNormalizerService.unMuteMouse()
}, selector, { bustClick });
}
if (argsCopy[0].indexOf("keydown") !== -1 && argsCopy[1] && argsCopy[1].options) {
argsCopy[0] = events;
const widget = argsCopy[1];
const keyDownCallback = argsCopy[argsCopy.length - 1];
argsCopy[argsCopy.length - 1] = function(e) {
if (self.keyDownHandler(e, widget)) {
return keyDownCallback.apply(this, [e]);
}
};
on.apply(that, argsCopy);
return that;
}
if (typeof callback === STRING$5) {
context = that.data("handler");
const callbackFn = context[callback];
argsCopy[argsCopy.length - 1] = function(e) {
callbackFn.call(context, e);
};
}
argsCopy[0] = events;
on.apply(that, argsCopy);
return that;
},
kendoDestroy: function(ns) {
ns = ns || this.data("kendoNS");
if (ns) {
this.off("." + ns);
}
return this;
}
});
return kendoJQuery;
}
/**
* Get the KendoJQuery constructor function
*/
getConstructor() {
return this.kendoJQuery;
}
/**
* Create a KendoJQuery wrapper
*/
create(selector, context) {
return this.kendoJQuery(selector, context);
}
/**
* Handle keydown events for a widget
* Executes all kendoKeydown handlers and checks if event should be prevented
*/
keyDownHandler(e, widget) {
const events = widget._events.kendoKeydown;
if (!events) {
return true;
}
const eventsCopy = events.slice();
e.sender = widget;
e.preventKendoKeydown = false;
for (let idx = 0, length = eventsCopy.length; idx < length; idx++) {
eventsCopy[idx].call(widget, e);
}
return !e.preventKendoKeydown;
}
};
const kendoJQueryService = new KendoJQueryService();
//#endregion
//#region ../src/core/base/class.ts
/**
* Base Class for Kendo UI.
* All Kendo classes inherit from this base class.
*/
var Class = class {
/**
* Creates a subclass with the given prototype.
* This is the legacy extend pattern used throughout Kendo UI.
*
* This method:
* 1. Creates a new constructor function that extends the current class
* 2. Copies all properties from proto to the new prototype
* 3. For plain object members, performs deep merge with base class members using $.extend
* 4. Sets up the extend method on the subclass for further inheritance
*
* @param proto - Object containing methods and properties for the subclass
* @returns New constructor function for the subclass
*
* @example
* var MyClass = Class.extend({
* init: function(options) {
* this.options = options;
* },
* myMethod: function() {
* return this.options;
* }
* });
*
* var instance = new MyClass({ foo: 'bar' });
*/
static extend(proto) {
const that = this;
const base = function() {};
base.prototype = that.prototype;
const subclass = proto && proto.init ? proto.init : function(...args) {
if (that.prototype.init) {
that.prototype.init.apply(this, args);
}
};
const fn = subclass.fn = subclass.prototype = new base();
if (proto) {
for (const member in proto) {
if (proto[member] != null && proto[member].constructor === Object) {
fn[member] = kendoJQueryService.getConstructor().extend(true, {}, base.prototype[member], proto[member]);
} else {
fn[member] = proto[member];
}
}
}
fn.constructor = subclass;
subclass.extend = that.extend;
return subclass;
}
};
Class.fn = Class.prototype;
/**
* Define init and _initOptions on the prototype as ENUMERABLE properties.
*
* ES6 class methods are non-enumerable by default, which breaks the
* legacy extend() pattern because when we do:
* base.prototype = that.prototype;
* fn = new base();
*
* The inherited methods from the ES6 class prototype don't appear in
* for...in loops or when the object is logged/inspected. This causes
* issues when deepExtend copies class instances - the non-enumerable
* methods are not properly inherited through the prototype chain.
*
* By defining these methods directly on the prototype (like the original
* Kendo Class did), we ensure they're enumerable and properly inherited.
*/
Class.prototype.init = function(..._args) {};
Class.prototype._initOptions = function(options) {
this.options = kendoJQueryService.getConstructor().extend(true, {}, this.options, options);
};
//#endregion
//#region ../src/core/base/observable.ts
/**
* Kendo UI Observable Class
*
* Provides event binding, unbinding, and triggering functionality.
* This is the foundation for all event-driven components in Kendo UI.
*
* Extends Class and supports both ES6 and legacy extend() patterns.
*
*
* @example
* // ES6 class inheritance
* class MyComponent extends Observable {
* doSomething() {
* this.trigger("change", { value: 42 });
* }
* }
*
* @example
* // Legacy extend pattern
* const MyComponent = Observable.extend({
* init: function() {
* Observable.fn.init.call(this);
* this.value = 0;
* },
* setValue: function(val) {
* this.value = val;
* this.trigger("change", { value: val });
* }
* });
*
* const component = new MyComponent();
* component.bind("change", function(e) {
* console.log("Value changed to:", e.value);
* });
* component.setValue(42);
*/
const STRING$4 = "string";
const FUNCTION$2 = "function";
/**
* preventDefault helper function.
* Called on event object to prevent default action.
* Uses regular function to preserve `this` context (the event object).
*/
function preventDefault() {
this._defaultPrevented = true;
}
/**
* isDefaultPrevented helper function.
* Called on event object to check if default was prevented.
* Uses regular function to preserve `this` context (the event object).
*/
function isDefaultPrevented() {
return this._defaultPrevented === true;
}
/**
* Observable class for event handling.
* All Kendo UI widgets and data components inherit from this class.
*/
var Observable = class extends Class {
/**
* Constructor - initializes the Observable's _events storage.
*/
constructor() {
super();
this._events = {};
}
/**
* Initialize the Observable instance.
* Sets up the internal _events storage.
* Accepts any arguments to allow subclasses to override with different signatures.
*
* Note: For direct Observable usage, this is called by the constructor.
* For subclasses, they should call Observable.fn.init.call(this) in their init.
*/
init(..._args) {
this._events = {};
}
/**
* Binds one or more event handlers to the observable.
*
* Supports multiple calling patterns:
* - bind("event", handler)
* - bind(["event1", "event2"], handler)
* - bind({ event1: handler1, event2: handler2 })
* - bind("event", handler, true) // one-time binding
*
* @param eventName - Event name, array of names, or object map
* @param handlers - Handler function or map of handlers
* @param one - If true, handler is removed after first invocation
* @returns this for chaining
*/
bind(eventName, handlers, one) {
const that = this;
let idx;
let length;
let original;
let handler;
const handlersIsFunction = typeof handlers === FUNCTION$2;
let events;
if (handlers === undefined) {
const eventMap = eventName;
for (idx in eventMap) {
that.bind(idx, eventMap[idx]);
}
return that;
}
const eventNames = typeof eventName === STRING$4 ? [eventName] : eventName;
for (idx = 0, length = eventNames.length; idx < length; idx++) {
const currentEventName = eventNames[idx];
handler = handlersIsFunction ? handlers : handlers[currentEventName];
if (handler) {
if (one) {
original = handler;
handler = (function(evtName, originalHandler) {
const wrappedHandler = function() {
that.unbind(evtName, wrappedHandler);
originalHandler.apply(that, arguments);
};
wrappedHandler.original = originalHandler;
return wrappedHandler;
})(currentEventName, original);
}
events = that._events[currentEventName] = that._events[currentEventName] || [];
events.push(handler);
}
}
return that;
}
/**
* Binds an event handler that will be removed after first invocation.
*
* @param eventNames - Event name or array of names
* @param handlers - Handler function or map of handlers
* @returns this for chaining
*/
one(eventNames, handlers) {
return this.bind(eventNames, handlers, true);
}
/**
* Binds an event handler at the beginning of the handler list.
* The handler will be invoked before other handlers.
*
* @param eventName - Event name or array of names
* @param handlers - Handler function or map of handlers
* @returns this for chaining
*/
first(eventName, handlers) {
const that = this;
let idx;
const eventNames = typeof eventName === STRING$4 ? [eventName] : eventName;
const length = eventNames.length;
let handler;
const handlersIsFunction = typeof handlers === FUNCTION$2;
let events;
for (idx = 0; idx < length; idx++) {
const currentEventName = eventNames[idx];
handler = handlersIsFunction ? handlers : handlers[currentEventName];
if (handler) {
events = that._events[currentEventName] = that._events[currentEventName] || [];
events.unshift(handler);
}
}
return that;
}
/**
* Triggers an event, invoking all bound handlers.
*
* @param eventName - Name of the event to trigger
* @param e - Optional event data object
* @returns true if preventDefault() was called, false otherwise
*/
trigger(eventName, e) {
const that = this;
let events = that._events[eventName];
let idx;
const length = events ? events.length : 0;
if (events) {
const eventObj = e || {};
eventObj.sender = that;
eventObj._defaultPrevented = false;
eventObj.preventDefault = preventDefault;
eventObj.isDefaultPrevented = isDefaultPrevented;
events = events.slice();
for (idx = 0; idx < length; idx++) {
events[idx].call(that, eventObj);
}
return eventObj._defaultPrevented === true;
}
return false;
}
/**
* Unbinds event handlers.
*
* - unbind() - removes all handlers for all events
* - unbind("event") - removes all handlers for specific event
* - unbind("event", handler) - removes specific handler
*
* @param eventName - Optional event name
* @param handler - Optional specific handler to remove
* @returns this for chaining
*/
unbind(eventName, handler) {
const that = this;
const events = eventName ? that._events[eventName] : undefined;
let idx;
if (eventName === undefined) {
that._events = {};
} else if (events) {
if (handler) {
for (idx = events.length - 1; idx >= 0; idx--) {
if (events[idx] === handler || events[idx].original === handler) {
events.splice(idx, 1);
}
}
} else {
that._events[eventName] = [];
}
}
return that;
}
};
/**
* Make Observable methods ENUMERABLE by deleting and reassigning them.
*
* ES6 class methods are non-enumerable by default, which breaks legacy patterns
* like `$.extend({}, observableInstance, ...)` which copies properties using
* for...in loops. By deleting and reassigning, we create enumerable properties.
*/
const proto = Observable.prototype;
const methods = [
"init",
"bind",
"one",
"first",
"trigger",
"unbind"
];
methods.forEach((method) => {
const fn = proto[method];
Object.defineProperty(proto, method, {
value: fn,
writable: true,
configurable: true,
enumerable: true
});
});
Observable.fn = Observable.prototype;
//#endregion
//#region ../src/core/services/namespace.service.ts
var NamespaceService = class {
constructor() {
this._ns = "";
}
/**
* Get the current namespace prefix
*/
get ns() {
return this._ns;
}
/**
* Set the namespace prefix
* @param value - The namespace prefix (e.g., "kendo-")
*/
setNs(value) {
this._ns = value;
}
};
const namespaceService = new NamespaceService();
//#endregion
//#region ../src/core/services/dom-utils.service.ts
const PERCENT_REGEXP = /%/;
const BOX_SHADOW_REGEXP = /(\d+(?:\.?)\d*)px\s*(\d+(?:\.?)\d*)px\s*(\d+(?:\.?)\d*)px\s*(\d+)?/i;
/**
* Service providing DOM utility functions
*/
var DomUtilsService = class {
constructor() {
this.animationQueue = [];
const win = window;
this.animationFrameFn = win.requestAnimationFrame || win.webkitRequestAnimationFrame || win.mozRequestAnimationFrame || win.oRequestAnimationFrame || win.msRequestAnimationFrame || ((callback) => {
setTimeout(callback, 1e3 / 60);
});
}
isElement(element) {
return element instanceof Element || element instanceof HTMLDocument;
}
/**
* Get outer width of element
*/
outerWidth(element, includeMargin, calculateFromHidden) {
const $element = $(element);
if (calculateFromHidden) {
return this.getHiddenDimensions($element, includeMargin).width;
}
return $element.outerWidth(includeMargin || false) || 0;
}
/**
* Get outer height of element
*/
outerHeight(element, includeMargin, calculateFromHidden) {
const $element = $(element);
if (calculateFromHidden) {
return this.getHiddenDimensions($element, includeMargin).height;
}
return $element.outerHeight(includeMargin || false) || 0;
}
/**
* Get computed styles for an element
*/
getComputedStyles(element, properties) {
const styles = {};
let computedStyle;
if (document.defaultView && document.defaultView.getComputedStyle) {
computedStyle = document.defaultView.getComputedStyle(element, "");
if (properties) {
$.each(properties, (_idx, value) => {
styles[value] = computedStyle.getPropertyValue(value);
});
}
} else {
computedStyle = element.currentStyle;
if (properties) {
$.each(properties, (_idx, value) => {
styles[value] = computedStyle[utilsService.toCamelCase(valu