blob: 30153a064aab45c9925bb4abf7858076c0ea60fd [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export class DomUtils {
/**
* JSF's component separator constant
*/
static readonly COMPONENT_SEP = ":";
/**
* Tobago's sub-component separator constant
*/
static readonly SUB_COMPONENT_SEP = "::";
/**
* Find all elements (and also self) which have the class "className".
* @param element Starting element in DOM to collect.
* @param className Class of elements to find.
*/
static selfOrElementsByClassName(element: HTMLElement, className: string): Array<HTMLElement> {
const result: Array<HTMLElement> = new Array<HTMLElement>();
if (element.classList.contains(className)) {
result.push(element);
}
const list = element.getElementsByClassName(className);
for (let i = 0; i < list.length; i++) {
result.push(list.item(i) as HTMLElement);
}
return result;
}
/**
* Find all elements (and also self) which have the attribute "attributeName".
* @param element Starting element in DOM to collect.
* @param selectors Name of the attribute of the elements to find.
*/
// todo: may return NodeListOf<HTMLElementTagNameMap[K]> or something like that.
static selfOrQuerySelectorAll(element: HTMLElement, selectors: string): Array<HTMLElement> {
const result: Array<HTMLElement> = new Array<HTMLElement>();
if (element.matches(selectors)) {
result.push(element);
}
for (const found of element.querySelectorAll(selectors)) {
result.push(found as HTMLElement);
}
return result;
}
/**
* Get the previous sibling element (without <style> elements).
*/
static previousElementSibling(element: HTMLElement): HTMLElement {
let sibling = element.previousElementSibling as HTMLElement;
while (sibling != null) {
if (sibling.tagName !== "STYLE") {
return sibling;
}
sibling = sibling.previousElementSibling as HTMLElement;
}
return null;
}
/**
* Get the next sibling element (without <style> elements).
*/
static nextElementSibling(element: HTMLElement): HTMLElement {
let sibling = element.nextElementSibling as HTMLElement;
while (sibling !== null) {
if (sibling.tagName !== "STYLE") {
return sibling;
}
sibling = sibling.nextElementSibling as HTMLElement;
}
return null;
}
static outerWidthWithMargin(element: HTMLElement): number {
const style = window.getComputedStyle(element);
return element.offsetWidth + parseInt(style.marginLeft) + parseInt(style.marginRight);
}
static outerHeightWithMargin(element: HTMLElement): number {
const style = window.getComputedStyle(element);
return element.offsetHeight + parseInt(style.marginTop) + parseInt(style.marginBottom);
}
static offset(element: HTMLElement): { top, left } {
let top = 0;
let left = 0;
let currentElement = element;
while (currentElement) {
top += (currentElement.offsetTop - currentElement.scrollTop + currentElement.clientTop);
left += (currentElement.offsetLeft - currentElement.scrollLeft + currentElement.clientLeft);
currentElement = currentElement.offsetParent as HTMLElement;
}
return {top: top, left: left};
}
static isVisible(element: HTMLElement): boolean {
return element.offsetWidth > 0 || element.offsetHeight > 0 || element.getClientRects().length > 0;
}
/**
*
* @param id A JSF client id, type=string. Example: escapeClientId("page:input") -> "#page\\:input"
* @return A string which can be used as a jQuery selector.
*/
static escapeClientId(id: string): string {
return "#" + id.replace(/([:\.])/g, "\\$1");
}
/**
* "a:b" -> "a"
* "a:b:c" -> "a:b"
* "a" -> null
* null -> null
* "a:b::sub-component" -> "a"
* "a::sub-component:b" -> "a::sub-component" // should currently not happen in Tobago
*
* @param clientId The clientId of a component.
* @return The clientId of the naming container.
*/
static getNamingContainerId(clientId: string): string {
if (clientId == null || clientId.lastIndexOf(DomUtils.COMPONENT_SEP) === -1) {
return null;
}
let id = clientId;
while (true) {
const sub = id.lastIndexOf(DomUtils.SUB_COMPONENT_SEP);
if (sub == -1) {
break;
}
if (sub + 1 == id.lastIndexOf(DomUtils.COMPONENT_SEP)) {
id = id.substring(0, sub);
} else {
break;
}
}
return id.substring(0, id.lastIndexOf(DomUtils.COMPONENT_SEP));
}
}