UNPKG

bitmovin-player-ui

Version:
489 lines (488 loc) 18.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.DOM = void 0; /** * Simple DOM manipulation and DOM element event handling modeled after jQuery (as replacement for jQuery). * * Like jQuery, DOM operates on single elements and lists of elements. For example: creating an element returns a DOM * instance with a single element, selecting elements returns a DOM instance with zero, one, or many elements. Similar * to jQuery, setters usually affect all elements, while getters operate on only the first element. * Also similar to jQuery, most methods (except getters) return the DOM instance facilitating easy chaining of method * calls. * * Built with the help of: http://youmightnotneedjquery.com/ */ var DOM = /** @class */ (function () { function DOM(something, attributes, component) { if (something instanceof Array) { if (something.length > 0 && something[0] instanceof HTMLElement) { var elements = something; this.elements = elements; } } else if (something instanceof HTMLElement) { var element = something; this.elements = [element]; } else if (something instanceof Document || something instanceof ShadowRoot) { // When a document or the ShadowRoot is passed in, we do not do anything with it, but by setting // this.elements to null we give the event handling method a means to detect if the events should be // registered on the document or ShadowRoot instead of elements. this.documentOrShadowRoot = something; this.elements = null; } else if (attributes) { var tagName = something; var element = document.createElement(tagName); for (var attributeName in attributes) { var attributeValue = attributes[attributeName]; if (attributeValue != null) { element.setAttribute(attributeName, attributeValue); } } if (component) { element.component = component; } this.elements = [element]; } else { var selector = something; this.elements = this.findChildElements(selector); } } Object.defineProperty(DOM.prototype, "length", { /** * Gets the number of elements that this DOM instance currently holds. * @returns {number} the number of elements */ get: function () { return this.elements ? this.elements.length : 0; }, enumerable: false, configurable: true }); DOM.prototype.get = function (index) { if (index === undefined) { return this.elements; } else if (!this.elements || index >= this.elements.length || index < -this.elements.length) { return undefined; } else if (index < 0) { return this.elements[this.elements.length - index]; } else { return this.elements[index]; } }; /** * A shortcut method for iterating all elements. Shorts this.elements.forEach(...) to this.forEach(...). * @param handler the handler to execute an operation on an element */ DOM.prototype.forEach = function (handler) { if (!this.elements) { return; } this.elements.forEach(function (element) { handler(element); }); }; DOM.prototype.findChildElementsOfElement = function (element, selector) { var childElements = element.querySelectorAll(selector); // Convert NodeList to Array // https://toddmotto.com/a-comprehensive-dive-into-nodelists-arrays-converting-nodelists-and-understanding-the-dom/ return [].slice.call(childElements); }; DOM.prototype.findChildElements = function (selector) { var _this = this; var allChildElements = []; if (this.elements) { this.forEach(function (element) { allChildElements = allChildElements.concat(_this.findChildElementsOfElement(element, selector)); }); } else { return this.findChildElementsOfElement(document, selector); } return allChildElements; }; /** * Finds all child elements of all elements matching the supplied selector. * @param selector the selector to match with child elements * @returns {DOM} a new DOM instance representing all matched children */ DOM.prototype.find = function (selector) { var allChildElements = this.findChildElements(selector); return new DOM(allChildElements); }; /** * Focuses to the first input element */ DOM.prototype.focusToFirstInput = function () { var inputElements = this.findChildElements('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'); if (inputElements.length > 0) { inputElements[0].focus(); } }; /** * Focuses to the first input element */ DOM.prototype.scrollTo = function (x, y) { this.elements[0].scrollTo(x, y); }; DOM.prototype.html = function (content) { if (arguments.length > 0) { return this.setHtml(content); } else { return this.getHtml(); } }; DOM.prototype.getHtml = function () { return this.elements[0].innerHTML; }; DOM.prototype.setHtml = function (content) { if (content === undefined || content == null) { // Set to empty string to avoid innerHTML getting set to 'undefined' (all browsers) or 'null' (IE9) content = ''; } this.forEach(function (element) { element.innerHTML = content; }); return this; }; /** * Clears the inner HTML of all elements (deletes all children). * @returns {DOM} */ DOM.prototype.empty = function () { this.forEach(function (element) { element.innerHTML = ''; }); return this; }; /** * Returns the current value of the first form element, e.g. the selected value of a select box or the text if an * input field. * @returns {string} the value of a form element */ DOM.prototype.val = function () { var element = this.elements[0]; if (element instanceof HTMLSelectElement || element instanceof HTMLInputElement) { return element.value; } else { // TODO add support for missing form elements throw new Error("val() not supported for ".concat(typeof element)); } }; DOM.prototype.attr = function (attribute, value) { if (arguments.length > 1) { return this.setAttr(attribute, value); } else { return this.getAttr(attribute); } }; /** * Removes the attribute of the element. * @param attribute */ DOM.prototype.removeAttr = function (attribute) { this.forEach(function (element) { element.removeAttribute(attribute); }); }; DOM.prototype.getAttr = function (attribute) { return this.elements[0].getAttribute(attribute); }; DOM.prototype.setAttr = function (attribute, value) { this.forEach(function (element) { element.setAttribute(attribute, value); }); return this; }; DOM.prototype.data = function (dataAttribute, value) { if (arguments.length > 1) { return this.setData(dataAttribute, value); } else { return this.getData(dataAttribute); } }; DOM.prototype.getData = function (dataAttribute) { return this.elements[0].getAttribute('data-' + dataAttribute); }; DOM.prototype.setData = function (dataAttribute, value) { this.forEach(function (element) { element.setAttribute('data-' + dataAttribute, value); }); return this; }; /** * Appends one or more DOM elements as children to all elements. * @param childElements the child elements to append * @returns {DOM} */ DOM.prototype.append = function () { var childElements = []; for (var _i = 0; _i < arguments.length; _i++) { childElements[_i] = arguments[_i]; } var appendElements = function (node) { childElements.forEach(function (childElement) { childElement.elements.forEach(function (_, index) { node.appendChild(childElement.elements[index]); }); }); }; if (this.elements) { this.forEach(function (element) { return appendElements(element); }); } else { appendElements(this.documentOrShadowRoot); } return this; }; /** * Prepends one or more DOM elements as children to all elements. * @param childElements the child elements to prepend * @returns {DOM} */ DOM.prototype.prepend = function () { var childElements = []; for (var _i = 0; _i < arguments.length; _i++) { childElements[_i] = arguments[_i]; } var insertElements = function (node) { childElements.forEach(function (childElement) { childElement.elements.forEach(function (_, index) { node.insertBefore(childElement.elements[index], node.firstChild); }); }); }; if (this.elements) { this.forEach(function (element) { return insertElements(element); }); } else { insertElements(this.documentOrShadowRoot); } return this; }; /** * Removes all elements from the DOM. */ DOM.prototype.remove = function () { var removeElements = function (node) { var parent = node.parentNode; if (parent) { parent.removeChild(node); } }; if (this.elements) { this.forEach(function (element) { return removeElements(element); }); } else { removeElements(this.documentOrShadowRoot); } }; /** * Returns the offset of the first element from the document's top left corner. * @returns {Offset} */ DOM.prototype.offset = function () { var element = this.elements[0]; var elementRect = element.getBoundingClientRect(); var htmlRect = document.body.parentElement.getBoundingClientRect(); // Virtual viewport scroll handling (e.g. pinch zoomed viewports in mobile browsers or desktop Chrome/Edge) // 'normal' zooms and virtual viewport zooms (aka layout viewport) result in different // element.getBoundingClientRect() results: // - with normal scrolls, the clientRect decreases with an increase in scroll(Top|Left)/page(X|Y)Offset // - with pinch zoom scrolls, the clientRect stays the same while scroll/pageOffset changes // This means, that the combination of clientRect + scroll/pageOffset does not work to calculate the offset // from the document's upper left origin when pinch zoom is used. // To work around this issue, we do not use scroll/pageOffset but get the clientRect of the html element and // subtract it from the element's rect, which always results in the offset from the document origin. // NOTE: the current way of offset calculation was implemented specifically to track event positions on the // seek bar, and it might break compatibility with jQuery's offset() method. If this ever turns out to be a // problem, this method should be reverted to the old version and the offset calculation moved to the seek bar. return { top: elementRect.top - htmlRect.top, left: elementRect.left - htmlRect.left, }; }; /** * Returns the width of the first element. * @returns {number} the width of the first element */ DOM.prototype.width = function () { // TODO check if this is the same as jQuery's width() (probably not) return this.elements[0].offsetWidth; }; /** * Returns the height of the first element. * @returns {number} the height of the first element */ DOM.prototype.height = function () { // TODO check if this is the same as jQuery's height() (probably not) return this.elements[0].offsetHeight; }; /** * Returns the size of the first element. * @return {Size} the size of the first element */ DOM.prototype.size = function () { return { width: this.width(), height: this.height() }; }; /** * Attaches an event handler to one or more events on all elements. * @param eventName the event name (or multiple names separated by space) to listen to * @param eventHandler the event handler to call when the event fires * @param options the options for this event handler * @returns {DOM} */ DOM.prototype.on = function (eventName, eventHandler, options) { var _this = this; var events = eventName.split(' '); events.forEach(function (event) { if (_this.elements == null) { _this.documentOrShadowRoot.addEventListener(event, eventHandler, options); } else { _this.forEach(function (element) { element.addEventListener(event, eventHandler, options); }); } }); return this; }; /** * Removes an event handler from one or more events on all elements. * @param eventName the event name (or multiple names separated by space) to remove the handler from * @param eventHandler the event handler to remove * @param options the options for this event handler * @returns {DOM} */ DOM.prototype.off = function (eventName, eventHandler, options) { var _this = this; var events = eventName.split(' '); events.forEach(function (event) { if (_this.elements == null) { _this.documentOrShadowRoot.removeEventListener(event, eventHandler, options); } else { _this.forEach(function (element) { element.removeEventListener(event, eventHandler, options); }); } }); return this; }; /** * Adds the specified class(es) to all elements. * @param className the class(es) to add, multiple classes separated by space * @returns {DOM} */ DOM.prototype.addClass = function (className) { this.forEach(function (element) { var _a; if (element.classList) { var classNames = className.split(' ').filter(function (className) { return className.length > 0; }); if (classNames.length > 0) { (_a = element.classList).add.apply(_a, classNames); } } else { element.className += ' ' + className; } }); return this; }; /** * Removed the specified class(es) from all elements. * @param className the class(es) to remove, multiple classes separated by space * @returns {DOM} */ DOM.prototype.removeClass = function (className) { this.forEach(function (element) { var _a; if (element.classList) { var classNames = className.split(' ').filter(function (className) { return className.length > 0; }); if (classNames.length > 0) { (_a = element.classList).remove.apply(_a, classNames); } } else { element.className = element.className.replace(new RegExp('(^|\\b)' + className.split(' ').join('|') + '(\\b|$)', 'gi'), ' '); } }); return this; }; /** * Checks if any of the elements has the specified class. * @param className the class name to check * @returns {boolean} true if one of the elements has the class attached, else if no element has it attached */ DOM.prototype.hasClass = function (className) { var hasClass = false; this.forEach(function (element) { if (element.classList) { if (element.classList.contains(className)) { // Since we are inside a handler, we can't just 'return true'. Instead, we save it to a variable // and return it at the end of the function body. hasClass = true; } } else { if (new RegExp('(^| )' + className + '( |$)', 'gi').test(element.className)) { // See comment above hasClass = true; } } }); return hasClass; }; DOM.prototype.css = function (propertyNameOrCollection, value) { if (typeof propertyNameOrCollection === 'string') { var propertyName = propertyNameOrCollection; if (arguments.length === 2) { return this.setCss(propertyName, value); } else { return this.getCss(propertyName); } } else { var propertyValueCollection = propertyNameOrCollection; return this.setCssCollection(propertyValueCollection); } }; /** * Removes an inline CSS property if it exists * @param propertyName name of the property to remove * @param elementIndex index of the element whose CSS property should get removed */ DOM.prototype.removeCss = function (propertyName, elementIndex) { if (elementIndex === void 0) { elementIndex = 0; } return this.elements[elementIndex].style.removeProperty(propertyName); }; DOM.prototype.getCss = function (propertyName) { return getComputedStyle(this.elements[0])[propertyName]; }; DOM.prototype.setCss = function (propertyName, value) { this.forEach(function (element) { // <any> cast to resolve TS7015: http://stackoverflow.com/a/36627114/370252 element.style[propertyName] = value; }); return this; }; DOM.prototype.setCssCollection = function (ruleValueCollection) { this.forEach(function (element) { // http://stackoverflow.com/a/34490573/370252 Object.assign(element.style, ruleValueCollection); }); return this; }; return DOM; }()); exports.DOM = DOM;