bunnyquery 1.8.16 → 1.8.18
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/bunnyquery.js +68 -170
- package/dist/engine.cjs +49 -132
- package/dist/engine.cjs.map +1 -1
- package/dist/engine.d.mts +42 -60
- package/dist/engine.d.ts +42 -60
- package/dist/engine.mjs +49 -132
- package/dist/engine.mjs.map +1 -1
- package/package.json +1 -1
- package/src/engine/image_preview.ts +0 -0
- package/src/engine/scroll_anchor.ts +171 -356
package/dist/engine.d.mts
CHANGED
|
@@ -1815,32 +1815,54 @@ declare function shouldRescueInFlightMessage(m: ChatMessage, ctx: RescueDecision
|
|
|
1815
1815
|
* prepends, a poll resolves, an indexing row splices in or changes label, a link
|
|
1816
1816
|
* chip goes grey, an image preview finishes decoding, the "Fetching history..."
|
|
1817
1817
|
* bar appears and disappears. Every one of those changes the height of something
|
|
1818
|
-
* that may sit ABOVE the viewport, and the browser answers by keeping scrollTop
|
|
1819
|
-
* which slides the sentence the
|
|
1820
|
-
*
|
|
1821
|
-
*
|
|
1822
|
-
*
|
|
1823
|
-
*
|
|
1824
|
-
*
|
|
1825
|
-
*
|
|
1818
|
+
* that may sit ABOVE the viewport, and the browser answers by keeping scrollTop,
|
|
1819
|
+
* which slides the sentence the reader was on out from under them.
|
|
1820
|
+
*
|
|
1821
|
+
* THE ONE RULE: nothing here stores a position to be applied later. Every method
|
|
1822
|
+
* acts at the moment it is called, against the box as it is at that moment, and
|
|
1823
|
+
* is finished when it returns.
|
|
1824
|
+
*
|
|
1825
|
+
* That rule is the whole design, and it was learned the hard way. An earlier
|
|
1826
|
+
* version of this module also had a "park the place they left, put them back when
|
|
1827
|
+
* they return" half — parked/parkedStuck/returning/returnBudget/sawFrozen, a
|
|
1828
|
+
* frozen mode, a retry budget. Every one of those was a stored instruction that
|
|
1829
|
+
* some later event would carry out, and the reader's experience of a stored
|
|
1830
|
+
* instruction is that the chat throws them somewhere for no reason they can see:
|
|
1831
|
+
* they came back from another app, tapped the composer, the on-screen keyboard
|
|
1832
|
+
* fired a resize, the resize triggered a fetch, the fetch settled, and the settle
|
|
1833
|
+
* dutifully executed an instruction recorded before they ever left. Compensating
|
|
1834
|
+
* at the moment of the change needs no such instruction, and a return then needs
|
|
1835
|
+
* no handling at all, because nothing moved the reader in the first place.
|
|
1836
|
+
*
|
|
1837
|
+
* Two shapes, both immediate:
|
|
1826
1838
|
*
|
|
1827
1839
|
* preserve(fn) / capture() + restore(a)
|
|
1828
1840
|
* A mutation you can bracket. Measures immediately before and immediately
|
|
1829
1841
|
* after, so it is exact even when the mutation tears the list down.
|
|
1830
1842
|
*
|
|
1831
1843
|
* remember() + hold()
|
|
1832
|
-
* A layout change you CANNOT bracket
|
|
1833
|
-
*
|
|
1834
|
-
*
|
|
1835
|
-
*
|
|
1836
|
-
*
|
|
1837
|
-
*
|
|
1838
|
-
*
|
|
1839
|
-
*
|
|
1840
|
-
*
|
|
1841
|
-
*
|
|
1842
|
-
*
|
|
1843
|
-
*
|
|
1844
|
+
* A layout change you CANNOT bracket: an image decoding, a font arriving, a
|
|
1845
|
+
* re-parse from a promise. remember() runs from the view's scroll handler,
|
|
1846
|
+
* so the anchor is always the reader's own last position.
|
|
1847
|
+
*
|
|
1848
|
+
* hold() is not an exception to the rule, and the staleness check is why. A height
|
|
1849
|
+
* change above the viewport does NOT change scrollTop; the browser preserves it,
|
|
1850
|
+
* which is precisely why the content appears to jump. So the remembered anchor is
|
|
1851
|
+
* valid exactly while `box.scrollTop` still equals the value it was captured at,
|
|
1852
|
+
* i.e. while nothing at all has moved the box. The instant that stops being true
|
|
1853
|
+
* it is re-measured, never replayed. hold() can therefore only ever undo a height
|
|
1854
|
+
* change that just happened, and can never carry out an old intention.
|
|
1855
|
+
*
|
|
1856
|
+
* Two more consequences worth stating, because both were once done the other way:
|
|
1857
|
+
*
|
|
1858
|
+
* NO FREEZE. Compensation runs whether or not the tab is visible. Layout and
|
|
1859
|
+
* getBoundingClientRect are live in a hidden tab; only painting and rAF stop.
|
|
1860
|
+
* Suspending compensation while hidden is what created the need to restore
|
|
1861
|
+
* something afterwards.
|
|
1862
|
+
*
|
|
1863
|
+
* A CLAMP IS ACCEPTED. If the content below the reader shrank, their line is
|
|
1864
|
+
* genuinely unreachable and the browser truncates the correction. Retrying that
|
|
1865
|
+
* later is how a correction turns into an ambush.
|
|
1844
1866
|
*
|
|
1845
1867
|
* DOM-free like the rest of the engine: the element shapes below are structural,
|
|
1846
1868
|
* so real DOM nodes satisfy them while this file imports nothing from lib.dom.
|
|
@@ -1914,21 +1936,6 @@ interface ScrollAnchorOptions {
|
|
|
1914
1936
|
* scrollToBottom* paths own the position, so every method here no-ops.
|
|
1915
1937
|
*/
|
|
1916
1938
|
isStuck: () => boolean;
|
|
1917
|
-
/**
|
|
1918
|
-
* The reader cannot see this box right now (the tab is hidden), so FREEZE:
|
|
1919
|
-
* remember where they were and refuse to move them.
|
|
1920
|
-
*
|
|
1921
|
-
* A hidden tab still runs everything that mutates the list — a resumed poll, a
|
|
1922
|
-
* head refresh and its deferred background batch, a settling request — and each
|
|
1923
|
-
* of those would otherwise write scrollTop against a layout nobody is looking at
|
|
1924
|
-
* and re-stamp the remembered position on the way through. The reader then comes
|
|
1925
|
-
* back to wherever the last of those writes happened to land, which is the
|
|
1926
|
-
* "somehow placed in the middle" they see, and only the NEXT correction puts
|
|
1927
|
-
* them right. So while frozen, reads still happen but nothing writes and nothing
|
|
1928
|
-
* re-stamps: the anchor holds the last position the reader actually had, and one
|
|
1929
|
-
* hold() on return puts them back on it.
|
|
1930
|
-
*/
|
|
1931
|
-
isFrozen?: () => boolean;
|
|
1932
1939
|
/**
|
|
1933
1940
|
* Fall back to the raw scrollTop when the anchored row cannot be found again.
|
|
1934
1941
|
*
|
|
@@ -1951,33 +1958,8 @@ interface ScrollAnchor {
|
|
|
1951
1958
|
remember: () => void;
|
|
1952
1959
|
/** Put the remembered place back, if it is still the reader's own. */
|
|
1953
1960
|
hold: () => void;
|
|
1954
|
-
/** The reader is going away: park the exact place they are leaving. */
|
|
1955
|
-
park: () => void;
|
|
1956
|
-
/**
|
|
1957
|
-
* They are back. Puts them on the parked place, and STAYS ARMED until it has
|
|
1958
|
-
* actually landed. Returns true when the host must pin to the bottom instead
|
|
1959
|
-
* (the reader left pinned), which only the host can do meaningfully.
|
|
1960
|
-
*/
|
|
1961
|
-
settleReturn: () => boolean;
|
|
1962
|
-
/** A return is armed: its position, not the host's, decides scrollTop. */
|
|
1963
|
-
isReturning: () => boolean;
|
|
1964
|
-
/**
|
|
1965
|
-
* The reader is driving. Retire any armed return, at once.
|
|
1966
|
-
*
|
|
1967
|
-
* Call from the raw gesture handlers (wheel, touchstart, touchmove, keydown),
|
|
1968
|
-
* which fire synchronously on a real user action and never for a programmatic
|
|
1969
|
-
* scroll. Waiting for the scroll EVENT is not good enough: a background refresh
|
|
1970
|
-
* can settle in between, and a settle with a return still armed puts the reader
|
|
1971
|
-
* back where they were before they left, which lands as "I touched the scroll
|
|
1972
|
-
* and it threw me somewhere else".
|
|
1973
|
-
*/
|
|
1974
|
-
release: () => void;
|
|
1975
1961
|
/** Pin to the bottom, instantly, recording the write. The ONLY way to pin. */
|
|
1976
1962
|
pinBottom: () => void;
|
|
1977
|
-
/** The box is not being painted (hidden tab). Shared so hosts agree. */
|
|
1978
|
-
isFrozen: () => boolean;
|
|
1979
|
-
/** Deprecated alias of settleReturn, kept so a stale dist does not break. */
|
|
1980
|
-
thaw: () => void;
|
|
1981
1963
|
/** Absorb one element's own resize. See below. */
|
|
1982
1964
|
absorb: (el: AnchorGrowableEl | null | undefined) => void;
|
|
1983
1965
|
/** Drop the remembered place (chat switch, unmount). */
|
package/dist/engine.d.ts
CHANGED
|
@@ -1815,32 +1815,54 @@ declare function shouldRescueInFlightMessage(m: ChatMessage, ctx: RescueDecision
|
|
|
1815
1815
|
* prepends, a poll resolves, an indexing row splices in or changes label, a link
|
|
1816
1816
|
* chip goes grey, an image preview finishes decoding, the "Fetching history..."
|
|
1817
1817
|
* bar appears and disappears. Every one of those changes the height of something
|
|
1818
|
-
* that may sit ABOVE the viewport, and the browser answers by keeping scrollTop
|
|
1819
|
-
* which slides the sentence the
|
|
1820
|
-
*
|
|
1821
|
-
*
|
|
1822
|
-
*
|
|
1823
|
-
*
|
|
1824
|
-
*
|
|
1825
|
-
*
|
|
1818
|
+
* that may sit ABOVE the viewport, and the browser answers by keeping scrollTop,
|
|
1819
|
+
* which slides the sentence the reader was on out from under them.
|
|
1820
|
+
*
|
|
1821
|
+
* THE ONE RULE: nothing here stores a position to be applied later. Every method
|
|
1822
|
+
* acts at the moment it is called, against the box as it is at that moment, and
|
|
1823
|
+
* is finished when it returns.
|
|
1824
|
+
*
|
|
1825
|
+
* That rule is the whole design, and it was learned the hard way. An earlier
|
|
1826
|
+
* version of this module also had a "park the place they left, put them back when
|
|
1827
|
+
* they return" half — parked/parkedStuck/returning/returnBudget/sawFrozen, a
|
|
1828
|
+
* frozen mode, a retry budget. Every one of those was a stored instruction that
|
|
1829
|
+
* some later event would carry out, and the reader's experience of a stored
|
|
1830
|
+
* instruction is that the chat throws them somewhere for no reason they can see:
|
|
1831
|
+
* they came back from another app, tapped the composer, the on-screen keyboard
|
|
1832
|
+
* fired a resize, the resize triggered a fetch, the fetch settled, and the settle
|
|
1833
|
+
* dutifully executed an instruction recorded before they ever left. Compensating
|
|
1834
|
+
* at the moment of the change needs no such instruction, and a return then needs
|
|
1835
|
+
* no handling at all, because nothing moved the reader in the first place.
|
|
1836
|
+
*
|
|
1837
|
+
* Two shapes, both immediate:
|
|
1826
1838
|
*
|
|
1827
1839
|
* preserve(fn) / capture() + restore(a)
|
|
1828
1840
|
* A mutation you can bracket. Measures immediately before and immediately
|
|
1829
1841
|
* after, so it is exact even when the mutation tears the list down.
|
|
1830
1842
|
*
|
|
1831
1843
|
* remember() + hold()
|
|
1832
|
-
* A layout change you CANNOT bracket
|
|
1833
|
-
*
|
|
1834
|
-
*
|
|
1835
|
-
*
|
|
1836
|
-
*
|
|
1837
|
-
*
|
|
1838
|
-
*
|
|
1839
|
-
*
|
|
1840
|
-
*
|
|
1841
|
-
*
|
|
1842
|
-
*
|
|
1843
|
-
*
|
|
1844
|
+
* A layout change you CANNOT bracket: an image decoding, a font arriving, a
|
|
1845
|
+
* re-parse from a promise. remember() runs from the view's scroll handler,
|
|
1846
|
+
* so the anchor is always the reader's own last position.
|
|
1847
|
+
*
|
|
1848
|
+
* hold() is not an exception to the rule, and the staleness check is why. A height
|
|
1849
|
+
* change above the viewport does NOT change scrollTop; the browser preserves it,
|
|
1850
|
+
* which is precisely why the content appears to jump. So the remembered anchor is
|
|
1851
|
+
* valid exactly while `box.scrollTop` still equals the value it was captured at,
|
|
1852
|
+
* i.e. while nothing at all has moved the box. The instant that stops being true
|
|
1853
|
+
* it is re-measured, never replayed. hold() can therefore only ever undo a height
|
|
1854
|
+
* change that just happened, and can never carry out an old intention.
|
|
1855
|
+
*
|
|
1856
|
+
* Two more consequences worth stating, because both were once done the other way:
|
|
1857
|
+
*
|
|
1858
|
+
* NO FREEZE. Compensation runs whether or not the tab is visible. Layout and
|
|
1859
|
+
* getBoundingClientRect are live in a hidden tab; only painting and rAF stop.
|
|
1860
|
+
* Suspending compensation while hidden is what created the need to restore
|
|
1861
|
+
* something afterwards.
|
|
1862
|
+
*
|
|
1863
|
+
* A CLAMP IS ACCEPTED. If the content below the reader shrank, their line is
|
|
1864
|
+
* genuinely unreachable and the browser truncates the correction. Retrying that
|
|
1865
|
+
* later is how a correction turns into an ambush.
|
|
1844
1866
|
*
|
|
1845
1867
|
* DOM-free like the rest of the engine: the element shapes below are structural,
|
|
1846
1868
|
* so real DOM nodes satisfy them while this file imports nothing from lib.dom.
|
|
@@ -1914,21 +1936,6 @@ interface ScrollAnchorOptions {
|
|
|
1914
1936
|
* scrollToBottom* paths own the position, so every method here no-ops.
|
|
1915
1937
|
*/
|
|
1916
1938
|
isStuck: () => boolean;
|
|
1917
|
-
/**
|
|
1918
|
-
* The reader cannot see this box right now (the tab is hidden), so FREEZE:
|
|
1919
|
-
* remember where they were and refuse to move them.
|
|
1920
|
-
*
|
|
1921
|
-
* A hidden tab still runs everything that mutates the list — a resumed poll, a
|
|
1922
|
-
* head refresh and its deferred background batch, a settling request — and each
|
|
1923
|
-
* of those would otherwise write scrollTop against a layout nobody is looking at
|
|
1924
|
-
* and re-stamp the remembered position on the way through. The reader then comes
|
|
1925
|
-
* back to wherever the last of those writes happened to land, which is the
|
|
1926
|
-
* "somehow placed in the middle" they see, and only the NEXT correction puts
|
|
1927
|
-
* them right. So while frozen, reads still happen but nothing writes and nothing
|
|
1928
|
-
* re-stamps: the anchor holds the last position the reader actually had, and one
|
|
1929
|
-
* hold() on return puts them back on it.
|
|
1930
|
-
*/
|
|
1931
|
-
isFrozen?: () => boolean;
|
|
1932
1939
|
/**
|
|
1933
1940
|
* Fall back to the raw scrollTop when the anchored row cannot be found again.
|
|
1934
1941
|
*
|
|
@@ -1951,33 +1958,8 @@ interface ScrollAnchor {
|
|
|
1951
1958
|
remember: () => void;
|
|
1952
1959
|
/** Put the remembered place back, if it is still the reader's own. */
|
|
1953
1960
|
hold: () => void;
|
|
1954
|
-
/** The reader is going away: park the exact place they are leaving. */
|
|
1955
|
-
park: () => void;
|
|
1956
|
-
/**
|
|
1957
|
-
* They are back. Puts them on the parked place, and STAYS ARMED until it has
|
|
1958
|
-
* actually landed. Returns true when the host must pin to the bottom instead
|
|
1959
|
-
* (the reader left pinned), which only the host can do meaningfully.
|
|
1960
|
-
*/
|
|
1961
|
-
settleReturn: () => boolean;
|
|
1962
|
-
/** A return is armed: its position, not the host's, decides scrollTop. */
|
|
1963
|
-
isReturning: () => boolean;
|
|
1964
|
-
/**
|
|
1965
|
-
* The reader is driving. Retire any armed return, at once.
|
|
1966
|
-
*
|
|
1967
|
-
* Call from the raw gesture handlers (wheel, touchstart, touchmove, keydown),
|
|
1968
|
-
* which fire synchronously on a real user action and never for a programmatic
|
|
1969
|
-
* scroll. Waiting for the scroll EVENT is not good enough: a background refresh
|
|
1970
|
-
* can settle in between, and a settle with a return still armed puts the reader
|
|
1971
|
-
* back where they were before they left, which lands as "I touched the scroll
|
|
1972
|
-
* and it threw me somewhere else".
|
|
1973
|
-
*/
|
|
1974
|
-
release: () => void;
|
|
1975
1961
|
/** Pin to the bottom, instantly, recording the write. The ONLY way to pin. */
|
|
1976
1962
|
pinBottom: () => void;
|
|
1977
|
-
/** The box is not being painted (hidden tab). Shared so hosts agree. */
|
|
1978
|
-
isFrozen: () => boolean;
|
|
1979
|
-
/** Deprecated alias of settleReturn, kept so a stale dist does not break. */
|
|
1980
|
-
thaw: () => void;
|
|
1981
1963
|
/** Absorb one element's own resize. See below. */
|
|
1982
1964
|
absorb: (el: AnchorGrowableEl | null | undefined) => void;
|
|
1983
1965
|
/** Drop the remembered place (chat switch, unmount). */
|
package/dist/engine.mjs
CHANGED
|
@@ -2580,7 +2580,6 @@ var ROW_KEY_ATTR = "data-row-key";
|
|
|
2580
2580
|
var ROW_POS_ATTR = "data-row-pos";
|
|
2581
2581
|
var MAX_ALTS = 2;
|
|
2582
2582
|
var ALT_SCAN_LIMIT = 64;
|
|
2583
|
-
var MAX_RETURN_SETTLES = 3;
|
|
2584
2583
|
var UNKNOWN_ROW_POS = "\0?";
|
|
2585
2584
|
function createScrollAnchor(options) {
|
|
2586
2585
|
var held = null;
|
|
@@ -2592,13 +2591,24 @@ function createScrollAnchor(options) {
|
|
|
2592
2591
|
var kids = box.children;
|
|
2593
2592
|
var fallback = null;
|
|
2594
2593
|
var fallbackAt = -1;
|
|
2594
|
+
var behind = [];
|
|
2595
2595
|
for (var i = 0; i < kids.length; i++) {
|
|
2596
2596
|
var el = kids[i];
|
|
2597
2597
|
if (!el || typeof el.getAttribute !== "function") continue;
|
|
2598
2598
|
var key = el.getAttribute(ROW_KEY_ATTR);
|
|
2599
2599
|
if (!key) continue;
|
|
2600
2600
|
var top = el.getBoundingClientRect().top - boxTop;
|
|
2601
|
-
if (top + el.offsetHeight <= 0)
|
|
2601
|
+
if (top + el.offsetHeight <= 0) {
|
|
2602
|
+
var bpos = el.getAttribute(ROW_POS_ATTR);
|
|
2603
|
+
behind.push({
|
|
2604
|
+
key,
|
|
2605
|
+
top,
|
|
2606
|
+
pos: bpos === null ? null : bpos || UNKNOWN_ROW_POS,
|
|
2607
|
+
el
|
|
2608
|
+
});
|
|
2609
|
+
if (behind.length > MAX_ALTS) behind.shift();
|
|
2610
|
+
continue;
|
|
2611
|
+
}
|
|
2602
2612
|
if (top >= box.clientHeight) break;
|
|
2603
2613
|
var rawPos = el.getAttribute(ROW_POS_ATTR);
|
|
2604
2614
|
var pos = rawPos === null ? null : rawPos || UNKNOWN_ROW_POS;
|
|
@@ -2611,7 +2621,7 @@ function createScrollAnchor(options) {
|
|
|
2611
2621
|
el
|
|
2612
2622
|
};
|
|
2613
2623
|
if (rawPos === null) {
|
|
2614
|
-
cand.alts = collectAlts(box, boxTop, i + 1);
|
|
2624
|
+
cand.alts = withBehind(collectAlts(box, boxTop, i + 1), behind);
|
|
2615
2625
|
return cand;
|
|
2616
2626
|
}
|
|
2617
2627
|
if (!fallback) {
|
|
@@ -2620,7 +2630,10 @@ function createScrollAnchor(options) {
|
|
|
2620
2630
|
}
|
|
2621
2631
|
}
|
|
2622
2632
|
if (fallback) {
|
|
2623
|
-
fallback.alts =
|
|
2633
|
+
fallback.alts = withBehind(
|
|
2634
|
+
collectAlts(box, boxTop, fallbackAt + 1, true) || collectAlts(box, boxTop, fallbackAt + 1),
|
|
2635
|
+
behind
|
|
2636
|
+
);
|
|
2624
2637
|
return fallback;
|
|
2625
2638
|
}
|
|
2626
2639
|
return {
|
|
@@ -2652,6 +2665,10 @@ function createScrollAnchor(options) {
|
|
|
2652
2665
|
}
|
|
2653
2666
|
return out.length ? out : void 0;
|
|
2654
2667
|
}
|
|
2668
|
+
function withBehind(forward, behind) {
|
|
2669
|
+
var out = (forward || []).concat(behind.slice().reverse());
|
|
2670
|
+
return out.length ? out : void 0;
|
|
2671
|
+
}
|
|
2655
2672
|
function findRow(box, anchor) {
|
|
2656
2673
|
var el = anchor.el;
|
|
2657
2674
|
if (el && el.parentNode === box) return el;
|
|
@@ -2664,21 +2681,7 @@ function createScrollAnchor(options) {
|
|
|
2664
2681
|
}
|
|
2665
2682
|
return null;
|
|
2666
2683
|
}
|
|
2667
|
-
|
|
2668
|
-
var parked = null;
|
|
2669
|
-
var parkedStuck = false;
|
|
2670
|
-
var returning = false;
|
|
2671
|
-
var returnBudget = 0;
|
|
2672
|
-
var wroteTop = -1;
|
|
2673
|
-
var restoreExact = true;
|
|
2674
|
-
var restorePinned = false;
|
|
2675
|
-
function frozen() {
|
|
2676
|
-
var f = !!options.isFrozen && options.isFrozen();
|
|
2677
|
-
if (f) sawFrozen = true;
|
|
2678
|
-
return f;
|
|
2679
|
-
}
|
|
2680
|
-
function restore(anchor, unbounded) {
|
|
2681
|
-
if (frozen()) return;
|
|
2684
|
+
function restore(anchor) {
|
|
2682
2685
|
var box = options.getBox();
|
|
2683
2686
|
if (!box || !anchor || options.isStuck()) return;
|
|
2684
2687
|
var el = findRow(box, anchor);
|
|
@@ -2690,32 +2693,36 @@ function createScrollAnchor(options) {
|
|
|
2690
2693
|
var boxTop = box.getBoundingClientRect().top;
|
|
2691
2694
|
var delta = el.getBoundingClientRect().top - boxTop - anchor.top;
|
|
2692
2695
|
var slack = Math.abs(box.scrollHeight - anchor.scrollHeight) + box.clientHeight;
|
|
2693
|
-
|
|
2694
|
-
|
|
2696
|
+
var moved = delta - (anchor.scrollTop - box.scrollTop);
|
|
2697
|
+
if (moved > slack || moved < -slack) {
|
|
2698
|
+
el = null;
|
|
2699
|
+
} else {
|
|
2700
|
+
if (delta >= 1 || delta <= -1) box.scrollTop += delta;
|
|
2701
|
+
held = {
|
|
2702
|
+
key: anchor.key,
|
|
2703
|
+
top: anchor.top,
|
|
2704
|
+
pos: anchor.pos,
|
|
2705
|
+
scrollTop: box.scrollTop,
|
|
2706
|
+
scrollHeight: box.scrollHeight,
|
|
2707
|
+
el,
|
|
2708
|
+
// Carried, not dropped: one successful restore used to disarm the
|
|
2709
|
+
// standbys for every later hold.
|
|
2710
|
+
alts: anchor.alts
|
|
2711
|
+
};
|
|
2695
2712
|
return;
|
|
2696
2713
|
}
|
|
2697
|
-
var want = box.scrollTop + delta;
|
|
2698
|
-
if (delta >= 1 || delta <= -1) box.scrollTop += delta;
|
|
2699
|
-
wroteTop = box.scrollTop;
|
|
2700
|
-
restorePinned = true;
|
|
2701
|
-
restoreExact = box.scrollTop >= want - 1 && box.scrollTop <= want + 1;
|
|
2702
|
-
held = {
|
|
2703
|
-
key: anchor.key,
|
|
2704
|
-
top: anchor.top,
|
|
2705
|
-
pos: anchor.pos,
|
|
2706
|
-
scrollTop: box.scrollTop,
|
|
2707
|
-
scrollHeight: box.scrollHeight,
|
|
2708
|
-
el,
|
|
2709
|
-
// Carried, not dropped: one successful restore used to disarm the
|
|
2710
|
-
// standbys for every later hold.
|
|
2711
|
-
alts: anchor.alts
|
|
2712
|
-
};
|
|
2713
|
-
return;
|
|
2714
2714
|
}
|
|
2715
2715
|
var alts = anchor.alts;
|
|
2716
2716
|
for (var ai = 0; alts && ai < alts.length; ai++) {
|
|
2717
2717
|
var alt = alts[ai];
|
|
2718
|
-
var ael = findRow(box, {
|
|
2718
|
+
var ael = findRow(box, {
|
|
2719
|
+
key: alt.key,
|
|
2720
|
+
top: alt.top,
|
|
2721
|
+
pos: alt.pos,
|
|
2722
|
+
scrollTop: anchor.scrollTop,
|
|
2723
|
+
scrollHeight: anchor.scrollHeight,
|
|
2724
|
+
el: alt.el
|
|
2725
|
+
});
|
|
2719
2726
|
if (!ael) continue;
|
|
2720
2727
|
if (alt.pos !== null && alt.pos !== UNKNOWN_ROW_POS) {
|
|
2721
2728
|
var altLive = ael.getAttribute(ROW_POS_ATTR) || UNKNOWN_ROW_POS;
|
|
@@ -2724,12 +2731,9 @@ function createScrollAnchor(options) {
|
|
|
2724
2731
|
var aboxTop = box.getBoundingClientRect().top;
|
|
2725
2732
|
var adelta = ael.getBoundingClientRect().top - aboxTop - alt.top;
|
|
2726
2733
|
var aslack = Math.abs(box.scrollHeight - anchor.scrollHeight) + box.clientHeight;
|
|
2727
|
-
|
|
2728
|
-
|
|
2734
|
+
var amoved = adelta - (anchor.scrollTop - box.scrollTop);
|
|
2735
|
+
if (amoved > aslack || amoved < -aslack) continue;
|
|
2729
2736
|
if (adelta >= 1 || adelta <= -1) box.scrollTop += adelta;
|
|
2730
|
-
wroteTop = box.scrollTop;
|
|
2731
|
-
restorePinned = true;
|
|
2732
|
-
restoreExact = box.scrollTop >= awant - 1 && box.scrollTop <= awant + 1;
|
|
2733
2737
|
held = {
|
|
2734
2738
|
key: alt.key,
|
|
2735
2739
|
top: alt.top,
|
|
@@ -2748,13 +2752,9 @@ function createScrollAnchor(options) {
|
|
|
2748
2752
|
var grew = box.scrollHeight - anchor.scrollHeight;
|
|
2749
2753
|
if (grew > 0) {
|
|
2750
2754
|
box.scrollTop = anchor.scrollTop + grew;
|
|
2751
|
-
wroteTop = box.scrollTop;
|
|
2752
2755
|
return;
|
|
2753
2756
|
}
|
|
2754
|
-
if (options.rawFallback)
|
|
2755
|
-
box.scrollTop = anchor.scrollTop;
|
|
2756
|
-
wroteTop = box.scrollTop;
|
|
2757
|
-
}
|
|
2757
|
+
if (options.rawFallback) box.scrollTop = anchor.scrollTop;
|
|
2758
2758
|
}
|
|
2759
2759
|
function preserve(mutate) {
|
|
2760
2760
|
var anchor = capture();
|
|
@@ -2763,17 +2763,9 @@ function createScrollAnchor(options) {
|
|
|
2763
2763
|
return result;
|
|
2764
2764
|
}
|
|
2765
2765
|
function remember() {
|
|
2766
|
-
var b0 = options.getBox();
|
|
2767
|
-
if (returning && b0 && b0.scrollTop !== wroteTop) release();
|
|
2768
|
-
if (frozen()) return;
|
|
2769
2766
|
held = capture();
|
|
2770
2767
|
}
|
|
2771
2768
|
function hold() {
|
|
2772
|
-
if (frozen()) return;
|
|
2773
|
-
if (sawFrozen) {
|
|
2774
|
-
settleReturn();
|
|
2775
|
-
return;
|
|
2776
|
-
}
|
|
2777
2769
|
var box = options.getBox();
|
|
2778
2770
|
if (!box || options.isStuck()) {
|
|
2779
2771
|
held = null;
|
|
@@ -2800,86 +2792,17 @@ function createScrollAnchor(options) {
|
|
|
2800
2792
|
if (prev === void 0) prev = 0;
|
|
2801
2793
|
var delta = h - prev;
|
|
2802
2794
|
if (delta === 0) return;
|
|
2803
|
-
if (frozen()) {
|
|
2804
|
-
foldFrozenGrowth(box, el, delta);
|
|
2805
|
-
return;
|
|
2806
|
-
}
|
|
2807
2795
|
if (el.getBoundingClientRect().top >= box.getBoundingClientRect().top) return;
|
|
2808
2796
|
box.scrollTop += delta;
|
|
2809
|
-
wroteTop = box.scrollTop;
|
|
2810
2797
|
if (held) held = capture();
|
|
2811
2798
|
}
|
|
2812
|
-
function park() {
|
|
2813
|
-
parked = capture();
|
|
2814
|
-
parkedStuck = !!options.isStuck();
|
|
2815
|
-
returning = true;
|
|
2816
|
-
returnBudget = MAX_RETURN_SETTLES;
|
|
2817
|
-
}
|
|
2818
|
-
function release() {
|
|
2819
|
-
parked = null;
|
|
2820
|
-
parkedStuck = false;
|
|
2821
|
-
returning = false;
|
|
2822
|
-
returnBudget = 0;
|
|
2823
|
-
}
|
|
2824
2799
|
function pinBottom() {
|
|
2825
2800
|
var box = options.getBox();
|
|
2826
2801
|
if (!box) return;
|
|
2827
2802
|
box.scrollTop = box.scrollHeight;
|
|
2828
|
-
wroteTop = box.scrollTop;
|
|
2829
|
-
}
|
|
2830
|
-
function settleReturn() {
|
|
2831
|
-
if (frozen()) return false;
|
|
2832
|
-
var wasFrozen = sawFrozen;
|
|
2833
|
-
sawFrozen = false;
|
|
2834
|
-
if (!returning && !wasFrozen) return false;
|
|
2835
|
-
if (returning) {
|
|
2836
|
-
if (returnBudget <= 0) {
|
|
2837
|
-
release();
|
|
2838
|
-
return false;
|
|
2839
|
-
}
|
|
2840
|
-
returnBudget--;
|
|
2841
|
-
}
|
|
2842
|
-
if (parkedStuck) {
|
|
2843
|
-
pinBottom();
|
|
2844
|
-
return true;
|
|
2845
|
-
}
|
|
2846
|
-
var target = parked || held;
|
|
2847
|
-
restoreExact = true;
|
|
2848
|
-
restorePinned = false;
|
|
2849
|
-
restore(target, true);
|
|
2850
|
-
parked = !restorePinned || !restoreExact ? target : null;
|
|
2851
|
-
returning = !!parked && returnBudget > 0;
|
|
2852
|
-
if (!returning) {
|
|
2853
|
-
parked = null;
|
|
2854
|
-
parkedStuck = false;
|
|
2855
|
-
returnBudget = 0;
|
|
2856
|
-
}
|
|
2857
|
-
return false;
|
|
2858
|
-
}
|
|
2859
|
-
function isReturning() {
|
|
2860
|
-
return returning;
|
|
2861
|
-
}
|
|
2862
|
-
function thaw() {
|
|
2863
|
-
settleReturn();
|
|
2864
|
-
}
|
|
2865
|
-
function foldFrozenGrowth(box, el, delta) {
|
|
2866
|
-
var elTop = el.getBoundingClientRect().top;
|
|
2867
|
-
foldInto(box, held, elTop, delta);
|
|
2868
|
-
foldInto(box, parked, elTop, delta);
|
|
2869
|
-
}
|
|
2870
|
-
function foldInto(box, a, elTop, delta) {
|
|
2871
|
-
if (!a || a.top >= 0) return;
|
|
2872
|
-
var rowEl = findRow(box, a);
|
|
2873
|
-
if (!rowEl) return;
|
|
2874
|
-
var within = elTop - rowEl.getBoundingClientRect().top;
|
|
2875
|
-
if (within < 0 || within >= rowEl.offsetHeight) return;
|
|
2876
|
-
if (within >= -a.top) return;
|
|
2877
|
-
a.top -= delta;
|
|
2878
|
-
a.scrollHeight += delta;
|
|
2879
2803
|
}
|
|
2880
2804
|
function forget() {
|
|
2881
2805
|
held = null;
|
|
2882
|
-
release();
|
|
2883
2806
|
}
|
|
2884
2807
|
return {
|
|
2885
2808
|
capture,
|
|
@@ -2887,13 +2810,7 @@ function createScrollAnchor(options) {
|
|
|
2887
2810
|
preserve,
|
|
2888
2811
|
remember,
|
|
2889
2812
|
hold,
|
|
2890
|
-
park,
|
|
2891
|
-
settleReturn,
|
|
2892
|
-
isReturning,
|
|
2893
|
-
release,
|
|
2894
2813
|
pinBottom,
|
|
2895
|
-
isFrozen: frozen,
|
|
2896
|
-
thaw,
|
|
2897
2814
|
absorb,
|
|
2898
2815
|
forget
|
|
2899
2816
|
};
|