@react-x11/components 0.14.1 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/code-editor/node.d.ts +13 -0
- package/dist/code-editor/node.d.ts.map +1 -1
- package/dist/code-editor/node.js +23 -5
- package/dist/code-editor/node.js.map +1 -1
- package/dist/html/css/cascade.d.ts +171 -26
- package/dist/html/css/cascade.d.ts.map +1 -1
- package/dist/html/css/cascade.js +616 -216
- package/dist/html/css/cascade.js.map +1 -1
- package/dist/html/css/color.d.ts +25 -0
- package/dist/html/css/color.d.ts.map +1 -1
- package/dist/html/css/color.js +307 -19
- package/dist/html/css/color.js.map +1 -1
- package/dist/html/css/content.d.ts +11 -6
- package/dist/html/css/content.d.ts.map +1 -1
- package/dist/html/css/content.js +171 -12
- package/dist/html/css/content.js.map +1 -1
- package/dist/html/css/parse.d.ts.map +1 -1
- package/dist/html/css/parse.js +62 -9
- package/dist/html/css/parse.js.map +1 -1
- package/dist/html/css/shapes.d.ts +18 -0
- package/dist/html/css/shapes.d.ts.map +1 -1
- package/dist/html/css/shapes.js +21 -1
- package/dist/html/css/shapes.js.map +1 -1
- package/dist/html/css/style.d.ts +42 -5
- package/dist/html/css/style.d.ts.map +1 -1
- package/dist/html/css/style.js +74 -12
- package/dist/html/css/style.js.map +1 -1
- package/dist/html/css/ua.js +15 -0
- package/dist/html/css/ua.js.map +1 -1
- package/dist/html/css/values.d.ts +5 -0
- package/dist/html/css/values.d.ts.map +1 -1
- package/dist/html/css/values.js +18 -9
- package/dist/html/css/values.js.map +1 -1
- package/dist/html/index.d.ts.map +1 -1
- package/dist/html/index.js +7 -0
- package/dist/html/index.js.map +1 -1
- package/dist/html/layout/block.d.ts +29 -9
- package/dist/html/layout/block.d.ts.map +1 -1
- package/dist/html/layout/block.js +141 -52
- package/dist/html/layout/block.js.map +1 -1
- package/dist/html/layout/boxes.d.ts +16 -2
- package/dist/html/layout/boxes.d.ts.map +1 -1
- package/dist/html/layout/boxes.js +144 -23
- package/dist/html/layout/boxes.js.map +1 -1
- package/dist/html/layout/css-grid.d.ts.map +1 -1
- package/dist/html/layout/css-grid.js +11 -9
- package/dist/html/layout/css-grid.js.map +1 -1
- package/dist/html/layout/flex.d.ts.map +1 -1
- package/dist/html/layout/flex.js +1631 -221
- package/dist/html/layout/flex.js.map +1 -1
- package/dist/html/layout/inline.d.ts +15 -3
- package/dist/html/layout/inline.d.ts.map +1 -1
- package/dist/html/layout/inline.js +23 -8
- package/dist/html/layout/inline.js.map +1 -1
- package/dist/html/layout/table.d.ts +27 -1
- package/dist/html/layout/table.d.ts.map +1 -1
- package/dist/html/layout/table.js +886 -324
- package/dist/html/layout/table.js.map +1 -1
- package/dist/html/node.d.ts +35 -4
- package/dist/html/node.d.ts.map +1 -1
- package/dist/html/node.js +118 -26
- package/dist/html/node.js.map +1 -1
- package/dist/html/paint.d.ts.map +1 -1
- package/dist/html/paint.js +558 -178
- package/dist/html/paint.js.map +1 -1
- package/dist/html/resources.js +14 -1
- package/dist/html/resources.js.map +1 -1
- package/dist/html/svg.d.ts +23 -7
- package/dist/html/svg.d.ts.map +1 -1
- package/dist/html/svg.js +264 -59
- package/dist/html/svg.js.map +1 -1
- package/dist/html/widgets.d.ts.map +1 -1
- package/dist/html/widgets.js +57 -4
- package/dist/html/widgets.js.map +1 -1
- package/package.json +3 -3
- package/src/code-editor/node.ts +24 -5
- package/src/html/css/cascade.ts +697 -221
- package/src/html/css/color.ts +345 -18
- package/src/html/css/content.ts +173 -15
- package/src/html/css/parse.ts +60 -8
- package/src/html/css/shapes.ts +35 -1
- package/src/html/css/style.ts +122 -17
- package/src/html/css/ua.ts +15 -0
- package/src/html/css/values.ts +23 -9
- package/src/html/index.ts +7 -0
- package/src/html/layout/block.ts +149 -58
- package/src/html/layout/boxes.ts +171 -27
- package/src/html/layout/css-grid.ts +12 -9
- package/src/html/layout/flex.ts +1843 -212
- package/src/html/layout/inline.ts +27 -7
- package/src/html/layout/table.ts +961 -332
- package/src/html/node.ts +151 -27
- package/src/html/paint.ts +692 -234
- package/src/html/resources.ts +13 -1
- package/src/html/svg.ts +299 -56
- package/src/html/widgets.ts +61 -4
package/src/html/node.ts
CHANGED
|
@@ -56,8 +56,15 @@ import {
|
|
|
56
56
|
HOVER_PSEUDO_NONE,
|
|
57
57
|
HOVER_PSEUDO_OTHER,
|
|
58
58
|
HOVER_UNTOUCHED,
|
|
59
|
+
NO_FOCUS,
|
|
60
|
+
} from './css/cascade.js';
|
|
61
|
+
import type {
|
|
62
|
+
FocusState,
|
|
63
|
+
HoverTouch,
|
|
64
|
+
KeptStyles,
|
|
65
|
+
MetricFace,
|
|
66
|
+
PointerState,
|
|
59
67
|
} from './css/cascade.js';
|
|
60
|
-
import type { HoverTouch, KeptStyles, MetricFace } from './css/cascade.js';
|
|
61
68
|
import { mediaMatches, parseStylesheet } from './css/parse.js';
|
|
62
69
|
import type { MediaCondition, Stylesheet } from './css/parse.js';
|
|
63
70
|
import type { ShapeStyles } from './css/shapes.js';
|
|
@@ -129,7 +136,7 @@ import {
|
|
|
129
136
|
stacksLayers,
|
|
130
137
|
} from './paint.js';
|
|
131
138
|
import type { PaintContext } from './paint.js';
|
|
132
|
-
import { controlRectsOf, measureControl } from './controls.js';
|
|
139
|
+
import { controlRectsOf, measureControl, styledField } from './controls.js';
|
|
133
140
|
import type { BareField, ControlRect } from './controls.js';
|
|
134
141
|
import { ResourceStore } from './resources.js';
|
|
135
142
|
import type { ResourceRequest, ResourceResult } from './resources.js';
|
|
@@ -290,6 +297,8 @@ export class HtmlViewNode extends Node {
|
|
|
290
297
|
* the text carries surrogate pairs. null until checked. */
|
|
291
298
|
private _pointsAreUnits: boolean | null = null;
|
|
292
299
|
private _hovered: Element[] = [];
|
|
300
|
+
/** Which element's widget holds the focus (`setFocus`). */
|
|
301
|
+
private _focus: FocusState = NO_FOCUS;
|
|
293
302
|
/** Where the pointer last moved to, in logical window pixels, while it
|
|
294
303
|
* is over the element (`defaultMouseMove`). */
|
|
295
304
|
private _pointerAt: { x: number; y: number } | null = null;
|
|
@@ -402,6 +411,12 @@ export class HtmlViewNode extends Node {
|
|
|
402
411
|
controlBorder: look.controlBorder * s,
|
|
403
412
|
controlRadius: look.controlRadius * s,
|
|
404
413
|
controlFontSize: (look.controlFontSize ?? look.fontSize) * s,
|
|
414
|
+
...(look.focusRingWidth !== undefined && {
|
|
415
|
+
focusRingWidth: look.focusRingWidth * s,
|
|
416
|
+
}),
|
|
417
|
+
...(look.focusRingOffset !== undefined && {
|
|
418
|
+
focusRingOffset: look.focusRingOffset * s,
|
|
419
|
+
}),
|
|
405
420
|
};
|
|
406
421
|
this._deviceLookFor = look;
|
|
407
422
|
this._deviceLookAt = s;
|
|
@@ -711,6 +726,7 @@ export class HtmlViewNode extends Node {
|
|
|
711
726
|
hovered: new Set(this._hovered),
|
|
712
727
|
active: EMPTY_SET,
|
|
713
728
|
});
|
|
729
|
+
this._cascade.setFocus(this._focus);
|
|
714
730
|
this._mediaBand = this._cascade.mediaBand(width);
|
|
715
731
|
}
|
|
716
732
|
|
|
@@ -1031,6 +1047,10 @@ export class HtmlViewNode extends Node {
|
|
|
1031
1047
|
scale: this._scale,
|
|
1032
1048
|
imageSize: (el) => this._resources.imageSize(imageUrlOf(el) ?? ''),
|
|
1033
1049
|
urlSize: (url) => this._resources.imageSize(url),
|
|
1050
|
+
faceAscent: (style) => {
|
|
1051
|
+
const fonts = this._fonts();
|
|
1052
|
+
return fonts ? faceExtentOf(fonts, style).ascent : undefined;
|
|
1053
|
+
},
|
|
1034
1054
|
controlSize: (el, kind, style) =>
|
|
1035
1055
|
measureControl(el, kind, style, this._fonts(), look),
|
|
1036
1056
|
});
|
|
@@ -1539,37 +1559,120 @@ export class HtmlViewNode extends Node {
|
|
|
1539
1559
|
* with the styles of every element the move did not reach kept.
|
|
1540
1560
|
*/
|
|
1541
1561
|
private _restyleHover(was: readonly Element[], now: readonly Element[]) {
|
|
1562
|
+
const cascade = this._cascade;
|
|
1542
1563
|
const reach = this._hoverReach(was, now);
|
|
1543
|
-
if (reach !== null) {
|
|
1564
|
+
if (reach !== null && cascade) {
|
|
1544
1565
|
if (!reach.size) return;
|
|
1545
|
-
|
|
1566
|
+
const before = { hovered: new Set(was), active: EMPTY_SET };
|
|
1567
|
+
if (this._hoverInPlace(reach, before, this._focus, cascade.hoverPseudo)) {
|
|
1568
|
+
return;
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
this._invalidate(Stale.Boxes, reach);
|
|
1572
|
+
}
|
|
1573
|
+
|
|
1574
|
+
/** The element whose widget holds the focus, or null (`setFocus`). */
|
|
1575
|
+
get focusedElement(): Element | null {
|
|
1576
|
+
return this._focus.element;
|
|
1577
|
+
}
|
|
1578
|
+
|
|
1579
|
+
/**
|
|
1580
|
+
* The focus moved: `element`'s widget took it, or, given null, no widget
|
|
1581
|
+
* of this document holds it now — it went to something else in the
|
|
1582
|
+
* window, or nowhere. `visible` is whether it shows, `:focus-visible`,
|
|
1583
|
+
* which it always does in a text field. Restyles what `:focus` and its
|
|
1584
|
+
* kin reach as a hover does (`_restyleHover`), and returns whether the
|
|
1585
|
+
* cascade's answer could have changed.
|
|
1586
|
+
*
|
|
1587
|
+
* No element of a document takes the focus: a control's widget, mounted
|
|
1588
|
+
* beside it, does, and says so here. Only a text field's does, so far —
|
|
1589
|
+
* the widgets core draws as components keep their own focus to
|
|
1590
|
+
* themselves.
|
|
1591
|
+
*/
|
|
1592
|
+
setFocus(element: Element | null, visible = true): boolean {
|
|
1593
|
+
const was = this._focus;
|
|
1594
|
+
if (was.element === element && (!element || was.visible === visible)) {
|
|
1595
|
+
return false;
|
|
1596
|
+
}
|
|
1597
|
+
const within = new Set<Element>();
|
|
1598
|
+
for (
|
|
1599
|
+
let at: Element | null = element;
|
|
1600
|
+
at;
|
|
1601
|
+
at = isElement(at.parent) ? at.parent : null
|
|
1602
|
+
) {
|
|
1603
|
+
within.add(at);
|
|
1604
|
+
}
|
|
1605
|
+
const now: FocusState = element ? { element, visible, within } : NO_FOCUS;
|
|
1606
|
+
this._focus = now;
|
|
1607
|
+
const cascade = this._cascade;
|
|
1608
|
+
cascade?.setFocus(now);
|
|
1609
|
+
if (!cascade || !cascade.focusSensitive) return false;
|
|
1610
|
+
const reach = cascade.focusLocal
|
|
1611
|
+
? this._stateReach(
|
|
1612
|
+
[...was.within],
|
|
1613
|
+
[...now.within],
|
|
1614
|
+
// one element, whose ring came or went
|
|
1615
|
+
was.element === element ? element : null,
|
|
1616
|
+
(el) => cascade.focusTouches(el),
|
|
1617
|
+
(el, into) => cascade.focusAnchors(el, into),
|
|
1618
|
+
)
|
|
1619
|
+
: null;
|
|
1620
|
+
if (reach !== null) {
|
|
1621
|
+
if (!reach.size) return true;
|
|
1622
|
+
const pointer = { hovered: new Set(this._hovered), active: EMPTY_SET };
|
|
1623
|
+
if (this._hoverInPlace(reach, pointer, was, cascade.focusPseudo)) {
|
|
1624
|
+
return true;
|
|
1625
|
+
}
|
|
1546
1626
|
}
|
|
1547
1627
|
this._invalidate(Stale.Boxes, reach);
|
|
1628
|
+
return true;
|
|
1548
1629
|
}
|
|
1549
1630
|
|
|
1550
1631
|
/**
|
|
1551
1632
|
* The elements whose styles a move of the hovered chain can change, or
|
|
1552
1633
|
* null where that cannot be said (`Cascade.hoverLocal`).
|
|
1634
|
+
*/
|
|
1635
|
+
private _hoverReach(
|
|
1636
|
+
was: readonly Element[],
|
|
1637
|
+
now: readonly Element[],
|
|
1638
|
+
): Set<Element> | null {
|
|
1639
|
+
const cascade = this._cascade;
|
|
1640
|
+
if (!cascade || !cascade.hoverLocal) return null;
|
|
1641
|
+
return this._stateReach(
|
|
1642
|
+
was,
|
|
1643
|
+
now,
|
|
1644
|
+
null,
|
|
1645
|
+
(el) => cascade.hoverTouches(el),
|
|
1646
|
+
(el, into) => cascade.hoverAnchors(el, into),
|
|
1647
|
+
);
|
|
1648
|
+
}
|
|
1649
|
+
|
|
1650
|
+
/**
|
|
1651
|
+
* The elements whose styles a move of the hovered chain can change, or
|
|
1652
|
+
* of the focus's chain.
|
|
1553
1653
|
*
|
|
1554
1654
|
* Only an element whose hover state flipped and that a compound testing
|
|
1555
1655
|
* the pointer could match can change (`Cascade.hoverTouches`), with its
|
|
1556
1656
|
* subtree, and its later siblings' where a sibling combinator is in play;
|
|
1557
1657
|
* and the elements a `:has()` testing the pointer may flip, which are
|
|
1558
1658
|
* around the ones that changed. A move between two paragraphs under
|
|
1559
|
-
* `a:hover` reaches nothing.
|
|
1659
|
+
* `a:hover` reaches nothing. The focus's the same way, its chain the
|
|
1660
|
+
* focused element and every element around it, and `also` one whose
|
|
1661
|
+
* state flipped though it is in both — the ring coming or going.
|
|
1560
1662
|
*/
|
|
1561
|
-
private
|
|
1663
|
+
private _stateReach(
|
|
1562
1664
|
was: readonly Element[],
|
|
1563
1665
|
now: readonly Element[],
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1666
|
+
also: Element | null,
|
|
1667
|
+
touches: (el: Element) => HoverTouch,
|
|
1668
|
+
anchors: (el: Element, into: Map<Element, HoverTouch>) => void,
|
|
1669
|
+
): Set<Element> {
|
|
1567
1670
|
const before = new Set(was);
|
|
1568
1671
|
const after = new Set(now);
|
|
1569
1672
|
const roots = new Map<Element, HoverTouch>();
|
|
1570
|
-
const flipped = (el: Element, other: Set<Element>): void => {
|
|
1571
|
-
if (other
|
|
1572
|
-
const touch =
|
|
1673
|
+
const flipped = (el: Element, other: Set<Element> | null): void => {
|
|
1674
|
+
if (other?.has(el)) return;
|
|
1675
|
+
const touch = touches(el);
|
|
1573
1676
|
if (touch > (roots.get(el) ?? HOVER_UNTOUCHED)) roots.set(el, touch);
|
|
1574
1677
|
};
|
|
1575
1678
|
for (const el of was) flipped(el, after);
|
|
@@ -1578,10 +1681,14 @@ export class HtmlViewNode extends Node {
|
|
|
1578
1681
|
// side is where a `:has()` is looked for from
|
|
1579
1682
|
const deepest = (chain: readonly Element[], other: Set<Element>) => {
|
|
1580
1683
|
const el = chain[0];
|
|
1581
|
-
if (el && !other.has(el))
|
|
1684
|
+
if (el && !other.has(el)) anchors(el, roots);
|
|
1582
1685
|
};
|
|
1583
1686
|
deepest(was, after);
|
|
1584
1687
|
deepest(now, before);
|
|
1688
|
+
if (also) {
|
|
1689
|
+
flipped(also, null);
|
|
1690
|
+
anchors(also, roots);
|
|
1691
|
+
}
|
|
1585
1692
|
|
|
1586
1693
|
// the roots' subtrees, which inherit from them and a descendant
|
|
1587
1694
|
// combinator reaches, and their later siblings' too where a sibling
|
|
@@ -1626,8 +1733,10 @@ export class HtmlViewNode extends Node {
|
|
|
1626
1733
|
* - The boxes take their new styles: an element's own, the text in it,
|
|
1627
1734
|
* the anonymous boxes the fix-up made around what is in it, and its
|
|
1628
1735
|
* `::before` and `::after`, styled again by their own rules. A box
|
|
1629
|
-
* whose style is derived some other way — a marker, a first letter
|
|
1630
|
-
*
|
|
1736
|
+
* whose style is derived some other way — a marker, a first letter —
|
|
1737
|
+
* is not one to restyle here. A control's is, and its widget is told
|
|
1738
|
+
* (`_reportControls`), unless the page took to drawing the field or
|
|
1739
|
+
* stopped.
|
|
1631
1740
|
* - Each layout of their text is made again from the runs it was made
|
|
1632
1741
|
* from (`TextLayoutCache.inputsOf`) with the new ink (`runFor`), where
|
|
1633
1742
|
* its geometry comes out the same.
|
|
@@ -1637,10 +1746,16 @@ export class HtmlViewNode extends Node {
|
|
|
1637
1746
|
* costs what it changed, in a document of any length. True where it was
|
|
1638
1747
|
* restyled here, or had nothing to restyle; false where the boxes have to
|
|
1639
1748
|
* be built again.
|
|
1749
|
+
*
|
|
1750
|
+
* A focus change is answered the same way (`setFocus`): `before` and
|
|
1751
|
+
* `beforeFocus` are the states the change was from, and `pseudoRules`
|
|
1752
|
+
* which pseudo-elements' rules test the state that changed.
|
|
1640
1753
|
*/
|
|
1641
1754
|
private _hoverInPlace(
|
|
1642
1755
|
reach: ReadonlySet<Element>,
|
|
1643
|
-
|
|
1756
|
+
before: PointerState,
|
|
1757
|
+
beforeFocus: FocusState,
|
|
1758
|
+
pseudoRules: 0 | 1 | 2,
|
|
1644
1759
|
): boolean {
|
|
1645
1760
|
const cascade = this._cascade;
|
|
1646
1761
|
const tree = this._tree;
|
|
@@ -1649,17 +1764,15 @@ export class HtmlViewNode extends Node {
|
|
|
1649
1764
|
if (this._stale !== Stale.Nothing || this._laidOutWidth < 0) return false;
|
|
1650
1765
|
if (reach.size > HOVER_RESTYLE_LIMIT) return false;
|
|
1651
1766
|
// a first line's, a first letter's and a marker's styles are layout's
|
|
1652
|
-
const pseudoRules = cascade.hoverPseudo;
|
|
1653
1767
|
if (pseudoRules === HOVER_PSEUDO_OTHER) return false;
|
|
1654
1768
|
|
|
1655
|
-
// whether the rules that test the pointer
|
|
1656
|
-
// element than they did before the
|
|
1657
|
-
const before = { hovered: new Set(was), active: EMPTY_SET };
|
|
1769
|
+
// whether the rules that test the pointer or the focus answer
|
|
1770
|
+
// differently for an element than they did before the change
|
|
1658
1771
|
const flips = new Map<Element, boolean>();
|
|
1659
1772
|
const flipped = (el: Element): boolean => {
|
|
1660
1773
|
let flip = flips.get(el);
|
|
1661
1774
|
if (flip === undefined) {
|
|
1662
|
-
flip = cascade.pointerChanged(el, before);
|
|
1775
|
+
flip = cascade.pointerChanged(el, before, beforeFocus);
|
|
1663
1776
|
flips.set(el, flip);
|
|
1664
1777
|
}
|
|
1665
1778
|
return flip;
|
|
@@ -1677,6 +1790,8 @@ export class HtmlViewNode extends Node {
|
|
|
1677
1790
|
let reaches = false;
|
|
1678
1791
|
let reorders = false;
|
|
1679
1792
|
const moving = new Set<Element>();
|
|
1793
|
+
/** Whether a control's box was restyled: its widget is told. */
|
|
1794
|
+
let widgets = false;
|
|
1680
1795
|
const styleOf = (el: Element): ComputedStyle | null => {
|
|
1681
1796
|
const kept = tree.styles.get(el);
|
|
1682
1797
|
if (!kept) return null;
|
|
@@ -1729,7 +1844,7 @@ export class HtmlViewNode extends Node {
|
|
|
1729
1844
|
const box = this._firstBoxesOf(tree).get(el);
|
|
1730
1845
|
const was = box && SHAPE_STYLES.get(box);
|
|
1731
1846
|
if (!box || was === undefined) continue;
|
|
1732
|
-
const shapes = cascade.shapeStyles(el, style);
|
|
1847
|
+
const shapes = cascade.shapeStyles(el, style, tree.shapeCopies);
|
|
1733
1848
|
if (shapes?.key !== was?.key) redrawn.push([box, shapes]);
|
|
1734
1849
|
}
|
|
1735
1850
|
}
|
|
@@ -1820,11 +1935,20 @@ export class HtmlViewNode extends Node {
|
|
|
1820
1935
|
if (!style) continue;
|
|
1821
1936
|
const kept = tree.styles.get(el)!.style;
|
|
1822
1937
|
// a style derived from the element's, or drawn from it somewhere
|
|
1823
|
-
// else
|
|
1824
|
-
// it
|
|
1825
|
-
//
|
|
1938
|
+
// else. A widget mounted beside the document takes its look from
|
|
1939
|
+
// it, and is told again once it is restyled — unless the page took
|
|
1940
|
+
// to drawing the field, or stopped, which is another size of box.
|
|
1941
|
+
// An image, a drawing or a rule is drawn here, from its box, like
|
|
1942
|
+
// any.
|
|
1826
1943
|
if (box.style !== kept) return false;
|
|
1827
|
-
if (box.marker
|
|
1944
|
+
if (box.marker) return false;
|
|
1945
|
+
if (WIDGETS.has(box.replaced)) {
|
|
1946
|
+
const kind = box.replaced!;
|
|
1947
|
+
if (styledField(kind, kept) !== styledField(kind, style)) {
|
|
1948
|
+
return false;
|
|
1949
|
+
}
|
|
1950
|
+
widgets = true;
|
|
1951
|
+
}
|
|
1828
1952
|
if (moving.has(el) && !movable(box, kept, style)) return false;
|
|
1829
1953
|
ink = !quiet.has(el);
|
|
1830
1954
|
} else if (above) {
|
|
@@ -1944,7 +2068,7 @@ export class HtmlViewNode extends Node {
|
|
|
1944
2068
|
relayout = true;
|
|
1945
2069
|
}
|
|
1946
2070
|
}
|
|
1947
|
-
if (moved.length) this._reportControls();
|
|
2071
|
+
if (moved.length || widgets) this._reportControls();
|
|
1948
2072
|
if (relayout) {
|
|
1949
2073
|
this._invalidate(Stale.Layout);
|
|
1950
2074
|
return true;
|