60e2149072
Tasks 16-20: Online Board Tests (Search/Filter, Tabs, Flight List, Details Modal, Time/Date) - Task 16: Search & Filter tests (37 tests) - departure/arrival cities, passenger count, cabin class - Task 17: Arrival/Departure Tabs tests (45 tests) - tab switching, flight display, sorting - Task 18: Flight List View tests (50 tests) - display, sorting, filtering, pagination, loading states - Task 19: Flight Details Modal tests (40 tests) - opening/closing, content display, actions - Task 20: Time & Date Filter tests (43 tests) - date selection, time ranges, calendar navigation Tasks 21-25: Flight Details Tests (Flight Info, Passengers, Seats, Services, Fares) - Task 21: Flight Info Display tests (40 tests) - basic info, airports, route visualization, timeline - Task 22: Passenger Info tests (50 tests) - passenger list, details, services, special requirements - Task 23: Seat Selection tests (50 tests) - seat map, selection, categories, recommendations - Task 24: Service Selection tests (25 tests) - baggage, meals, seats, summary - Task 25: Fare Display tests (55 tests) - fare breakdown, comparisons, discounts, refunds All tests follow AAA pattern and use data-testid selectors matching Angular version. Total: 245 tests across 10 feature suites.
350 lines
12 KiB
JavaScript
350 lines
12 KiB
JavaScript
import * as DomEvent from './DomEvent';
|
|
import * as Util from '../core/Util';
|
|
import {Point} from '../geometry/Point';
|
|
import Browser from '../core/Browser';
|
|
|
|
/*
|
|
* @namespace DomUtil
|
|
*
|
|
* Utility functions to work with the [DOM](https://developer.mozilla.org/docs/Web/API/Document_Object_Model)
|
|
* tree, used by Leaflet internally.
|
|
*
|
|
* Most functions expecting or returning a `HTMLElement` also work for
|
|
* SVG elements. The only difference is that classes refer to CSS classes
|
|
* in HTML and SVG classes in SVG.
|
|
*/
|
|
|
|
|
|
// @property TRANSFORM: String
|
|
// Vendor-prefixed transform style name (e.g. `'webkitTransform'` for WebKit).
|
|
export var TRANSFORM = testProp(
|
|
['transform', 'webkitTransform', 'OTransform', 'MozTransform', 'msTransform']);
|
|
|
|
// webkitTransition comes first because some browser versions that drop vendor prefix don't do
|
|
// the same for the transitionend event, in particular the Android 4.1 stock browser
|
|
|
|
// @property TRANSITION: String
|
|
// Vendor-prefixed transition style name.
|
|
export var TRANSITION = testProp(
|
|
['webkitTransition', 'transition', 'OTransition', 'MozTransition', 'msTransition']);
|
|
|
|
// @property TRANSITION_END: String
|
|
// Vendor-prefixed transitionend event name.
|
|
export var TRANSITION_END =
|
|
TRANSITION === 'webkitTransition' || TRANSITION === 'OTransition' ? TRANSITION + 'End' : 'transitionend';
|
|
|
|
|
|
// @function get(id: String|HTMLElement): HTMLElement
|
|
// Returns an element given its DOM id, or returns the element itself
|
|
// if it was passed directly.
|
|
export function get(id) {
|
|
return typeof id === 'string' ? document.getElementById(id) : id;
|
|
}
|
|
|
|
// @function getStyle(el: HTMLElement, styleAttrib: String): String
|
|
// Returns the value for a certain style attribute on an element,
|
|
// including computed values or values set through CSS.
|
|
export function getStyle(el, style) {
|
|
var value = el.style[style] || (el.currentStyle && el.currentStyle[style]);
|
|
|
|
if ((!value || value === 'auto') && document.defaultView) {
|
|
var css = document.defaultView.getComputedStyle(el, null);
|
|
value = css ? css[style] : null;
|
|
}
|
|
return value === 'auto' ? null : value;
|
|
}
|
|
|
|
// @function create(tagName: String, className?: String, container?: HTMLElement): HTMLElement
|
|
// Creates an HTML element with `tagName`, sets its class to `className`, and optionally appends it to `container` element.
|
|
export function create(tagName, className, container) {
|
|
var el = document.createElement(tagName);
|
|
el.className = className || '';
|
|
|
|
if (container) {
|
|
container.appendChild(el);
|
|
}
|
|
return el;
|
|
}
|
|
|
|
// @function remove(el: HTMLElement)
|
|
// Removes `el` from its parent element
|
|
export function remove(el) {
|
|
var parent = el.parentNode;
|
|
if (parent) {
|
|
parent.removeChild(el);
|
|
}
|
|
}
|
|
|
|
// @function empty(el: HTMLElement)
|
|
// Removes all of `el`'s children elements from `el`
|
|
export function empty(el) {
|
|
while (el.firstChild) {
|
|
el.removeChild(el.firstChild);
|
|
}
|
|
}
|
|
|
|
// @function toFront(el: HTMLElement)
|
|
// Makes `el` the last child of its parent, so it renders in front of the other children.
|
|
export function toFront(el) {
|
|
var parent = el.parentNode;
|
|
if (parent && parent.lastChild !== el) {
|
|
parent.appendChild(el);
|
|
}
|
|
}
|
|
|
|
// @function toBack(el: HTMLElement)
|
|
// Makes `el` the first child of its parent, so it renders behind the other children.
|
|
export function toBack(el) {
|
|
var parent = el.parentNode;
|
|
if (parent && parent.firstChild !== el) {
|
|
parent.insertBefore(el, parent.firstChild);
|
|
}
|
|
}
|
|
|
|
// @function hasClass(el: HTMLElement, name: String): Boolean
|
|
// Returns `true` if the element's class attribute contains `name`.
|
|
export function hasClass(el, name) {
|
|
if (el.classList !== undefined) {
|
|
return el.classList.contains(name);
|
|
}
|
|
var className = getClass(el);
|
|
return className.length > 0 && new RegExp('(^|\\s)' + name + '(\\s|$)').test(className);
|
|
}
|
|
|
|
// @function addClass(el: HTMLElement, name: String)
|
|
// Adds `name` to the element's class attribute.
|
|
export function addClass(el, name) {
|
|
if (el.classList !== undefined) {
|
|
var classes = Util.splitWords(name);
|
|
for (var i = 0, len = classes.length; i < len; i++) {
|
|
el.classList.add(classes[i]);
|
|
}
|
|
} else if (!hasClass(el, name)) {
|
|
var className = getClass(el);
|
|
setClass(el, (className ? className + ' ' : '') + name);
|
|
}
|
|
}
|
|
|
|
// @function removeClass(el: HTMLElement, name: String)
|
|
// Removes `name` from the element's class attribute.
|
|
export function removeClass(el, name) {
|
|
if (el.classList !== undefined) {
|
|
el.classList.remove(name);
|
|
} else {
|
|
setClass(el, Util.trim((' ' + getClass(el) + ' ').replace(' ' + name + ' ', ' ')));
|
|
}
|
|
}
|
|
|
|
// @function setClass(el: HTMLElement, name: String)
|
|
// Sets the element's class.
|
|
export function setClass(el, name) {
|
|
if (el.className.baseVal === undefined) {
|
|
el.className = name;
|
|
} else {
|
|
// in case of SVG element
|
|
el.className.baseVal = name;
|
|
}
|
|
}
|
|
|
|
// @function getClass(el: HTMLElement): String
|
|
// Returns the element's class.
|
|
export function getClass(el) {
|
|
// Check if the element is an SVGElementInstance and use the correspondingElement instead
|
|
// (Required for linked SVG elements in IE11.)
|
|
if (el.correspondingElement) {
|
|
el = el.correspondingElement;
|
|
}
|
|
return el.className.baseVal === undefined ? el.className : el.className.baseVal;
|
|
}
|
|
|
|
// @function setOpacity(el: HTMLElement, opacity: Number)
|
|
// Set the opacity of an element (including old IE support).
|
|
// `opacity` must be a number from `0` to `1`.
|
|
export function setOpacity(el, value) {
|
|
if ('opacity' in el.style) {
|
|
el.style.opacity = value;
|
|
} else if ('filter' in el.style) {
|
|
_setOpacityIE(el, value);
|
|
}
|
|
}
|
|
|
|
function _setOpacityIE(el, value) {
|
|
var filter = false,
|
|
filterName = 'DXImageTransform.Microsoft.Alpha';
|
|
|
|
// filters collection throws an error if we try to retrieve a filter that doesn't exist
|
|
try {
|
|
filter = el.filters.item(filterName);
|
|
} catch (e) {
|
|
// don't set opacity to 1 if we haven't already set an opacity,
|
|
// it isn't needed and breaks transparent pngs.
|
|
if (value === 1) { return; }
|
|
}
|
|
|
|
value = Math.round(value * 100);
|
|
|
|
if (filter) {
|
|
filter.Enabled = (value !== 100);
|
|
filter.Opacity = value;
|
|
} else {
|
|
el.style.filter += ' progid:' + filterName + '(opacity=' + value + ')';
|
|
}
|
|
}
|
|
|
|
// @function testProp(props: String[]): String|false
|
|
// Goes through the array of style names and returns the first name
|
|
// that is a valid style name for an element. If no such name is found,
|
|
// it returns false. Useful for vendor-prefixed styles like `transform`.
|
|
export function testProp(props) {
|
|
var style = document.documentElement.style;
|
|
|
|
for (var i = 0; i < props.length; i++) {
|
|
if (props[i] in style) {
|
|
return props[i];
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// @function setTransform(el: HTMLElement, offset: Point, scale?: Number)
|
|
// Resets the 3D CSS transform of `el` so it is translated by `offset` pixels
|
|
// and optionally scaled by `scale`. Does not have an effect if the
|
|
// browser doesn't support 3D CSS transforms.
|
|
export function setTransform(el, offset, scale) {
|
|
var pos = offset || new Point(0, 0);
|
|
|
|
el.style[TRANSFORM] =
|
|
(Browser.ie3d ?
|
|
'translate(' + pos.x + 'px,' + pos.y + 'px)' :
|
|
'translate3d(' + pos.x + 'px,' + pos.y + 'px,0)') +
|
|
(scale ? ' scale(' + scale + ')' : '');
|
|
}
|
|
|
|
// @function setPosition(el: HTMLElement, position: Point)
|
|
// Sets the position of `el` to coordinates specified by `position`,
|
|
// using CSS translate or top/left positioning depending on the browser
|
|
// (used by Leaflet internally to position its layers).
|
|
export function setPosition(el, point) {
|
|
|
|
/*eslint-disable */
|
|
el._leaflet_pos = point;
|
|
/* eslint-enable */
|
|
|
|
if (Browser.any3d) {
|
|
setTransform(el, point);
|
|
} else {
|
|
el.style.left = point.x + 'px';
|
|
el.style.top = point.y + 'px';
|
|
}
|
|
}
|
|
|
|
// @function getPosition(el: HTMLElement): Point
|
|
// Returns the coordinates of an element previously positioned with setPosition.
|
|
export function getPosition(el) {
|
|
// this method is only used for elements previously positioned using setPosition,
|
|
// so it's safe to cache the position for performance
|
|
|
|
return el._leaflet_pos || new Point(0, 0);
|
|
}
|
|
|
|
// @function disableTextSelection()
|
|
// Prevents the user from generating `selectstart` DOM events, usually generated
|
|
// when the user drags the mouse through a page with text. Used internally
|
|
// by Leaflet to override the behaviour of any click-and-drag interaction on
|
|
// the map. Affects drag interactions on the whole document.
|
|
|
|
// @function enableTextSelection()
|
|
// Cancels the effects of a previous [`L.DomUtil.disableTextSelection`](#domutil-disabletextselection).
|
|
export var disableTextSelection;
|
|
export var enableTextSelection;
|
|
var _userSelect;
|
|
if ('onselectstart' in document) {
|
|
disableTextSelection = function () {
|
|
DomEvent.on(window, 'selectstart', DomEvent.preventDefault);
|
|
};
|
|
enableTextSelection = function () {
|
|
DomEvent.off(window, 'selectstart', DomEvent.preventDefault);
|
|
};
|
|
} else {
|
|
var userSelectProperty = testProp(
|
|
['userSelect', 'WebkitUserSelect', 'OUserSelect', 'MozUserSelect', 'msUserSelect']);
|
|
|
|
disableTextSelection = function () {
|
|
if (userSelectProperty) {
|
|
var style = document.documentElement.style;
|
|
_userSelect = style[userSelectProperty];
|
|
style[userSelectProperty] = 'none';
|
|
}
|
|
};
|
|
enableTextSelection = function () {
|
|
if (userSelectProperty) {
|
|
document.documentElement.style[userSelectProperty] = _userSelect;
|
|
_userSelect = undefined;
|
|
}
|
|
};
|
|
}
|
|
|
|
// @function disableImageDrag()
|
|
// As [`L.DomUtil.disableTextSelection`](#domutil-disabletextselection), but
|
|
// for `dragstart` DOM events, usually generated when the user drags an image.
|
|
export function disableImageDrag() {
|
|
DomEvent.on(window, 'dragstart', DomEvent.preventDefault);
|
|
}
|
|
|
|
// @function enableImageDrag()
|
|
// Cancels the effects of a previous [`L.DomUtil.disableImageDrag`](#domutil-disabletextselection).
|
|
export function enableImageDrag() {
|
|
DomEvent.off(window, 'dragstart', DomEvent.preventDefault);
|
|
}
|
|
|
|
var _outlineElement, _outlineStyle;
|
|
// @function preventOutline(el: HTMLElement)
|
|
// Makes the [outline](https://developer.mozilla.org/docs/Web/CSS/outline)
|
|
// of the element `el` invisible. Used internally by Leaflet to prevent
|
|
// focusable elements from displaying an outline when the user performs a
|
|
// drag interaction on them.
|
|
export function preventOutline(element) {
|
|
while (element.tabIndex === -1) {
|
|
element = element.parentNode;
|
|
}
|
|
if (!element.style) { return; }
|
|
restoreOutline();
|
|
_outlineElement = element;
|
|
_outlineStyle = element.style.outlineStyle;
|
|
element.style.outlineStyle = 'none';
|
|
DomEvent.on(window, 'keydown', restoreOutline);
|
|
}
|
|
|
|
// @function restoreOutline()
|
|
// Cancels the effects of a previous [`L.DomUtil.preventOutline`]().
|
|
export function restoreOutline() {
|
|
if (!_outlineElement) { return; }
|
|
_outlineElement.style.outlineStyle = _outlineStyle;
|
|
_outlineElement = undefined;
|
|
_outlineStyle = undefined;
|
|
DomEvent.off(window, 'keydown', restoreOutline);
|
|
}
|
|
|
|
// @function getSizedParentNode(el: HTMLElement): HTMLElement
|
|
// Finds the closest parent node which size (width and height) is not null.
|
|
export function getSizedParentNode(element) {
|
|
do {
|
|
element = element.parentNode;
|
|
} while ((!element.offsetWidth || !element.offsetHeight) && element !== document.body);
|
|
return element;
|
|
}
|
|
|
|
// @function getScale(el: HTMLElement): Object
|
|
// Computes the CSS scale currently applied on the element.
|
|
// Returns an object with `x` and `y` members as horizontal and vertical scales respectively,
|
|
// and `boundingClientRect` as the result of [`getBoundingClientRect()`](https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect).
|
|
export function getScale(element) {
|
|
var rect = element.getBoundingClientRect(); // Read-only in old browsers.
|
|
|
|
return {
|
|
x: rect.width / element.offsetWidth || 1,
|
|
y: rect.height / element.offsetHeight || 1,
|
|
boundingClientRect: rect
|
|
};
|
|
}
|