jquery.dgtable 0.6.15 → 0.6.16
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/jquery.dgtable.cjs.js +2 -1459
- package/dist/jquery.dgtable.cjs.js.map +1 -1
- package/dist/jquery.dgtable.cjs.min.js +2 -2
- package/dist/jquery.dgtable.cjs.min.js.map +1 -1
- package/dist/jquery.dgtable.es6.js +2 -1459
- package/dist/jquery.dgtable.es6.js.map +1 -1
- package/dist/jquery.dgtable.es6.min.js +2 -2
- package/dist/jquery.dgtable.es6.min.js.map +1 -1
- package/dist/jquery.dgtable.umd.js +5 -1462
- package/dist/jquery.dgtable.umd.js.map +1 -1
- package/dist/jquery.dgtable.umd.min.js +2 -2
- package/dist/jquery.dgtable.umd.min.js.map +1 -1
- package/package.json +2 -2
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* jquery.dgtable 0.6.
|
|
2
|
+
* jquery.dgtable 0.6.16
|
|
3
3
|
* git://github.com/danielgindi/jquery.dgtable.git
|
|
4
4
|
*/
|
|
5
5
|
'use strict';
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
var jQuery = require('jquery');
|
|
8
8
|
var ScrollHelper_js = require('@danielgindi/dom-utils/lib/ScrollHelper.js');
|
|
9
9
|
var Css_js = require('@danielgindi/dom-utils/lib/Css.js');
|
|
10
|
-
var
|
|
10
|
+
var VirtualListHelper = require('@danielgindi/virtual-list-helper');
|
|
11
11
|
|
|
12
12
|
var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
|
|
13
13
|
|
|
@@ -2348,1463 +2348,6 @@ class SelectionHelper {
|
|
|
2348
2348
|
}
|
|
2349
2349
|
}
|
|
2350
2350
|
|
|
2351
|
-
/**
|
|
2352
|
-
* @typedef {function(index: number):(number|undefined)} VirtualListHelper~ItemHeightEstimatorFunction
|
|
2353
|
-
*/
|
|
2354
|
-
|
|
2355
|
-
/**
|
|
2356
|
-
* @typedef {function():Element} VirtualListHelper~ItemElementCreatorFunction
|
|
2357
|
-
*/
|
|
2358
|
-
|
|
2359
|
-
/**
|
|
2360
|
-
* @typedef {function(itemEl: Element, index: number)} VirtualListHelper~ItemRenderFunction
|
|
2361
|
-
*/
|
|
2362
|
-
|
|
2363
|
-
/**
|
|
2364
|
-
* @typedef {function(itemEl: Element)} VirtualListHelper~ItemUnrenderFunction
|
|
2365
|
-
*/
|
|
2366
|
-
|
|
2367
|
-
/**
|
|
2368
|
-
* @typedef {Object} VirtualListHelper~Options
|
|
2369
|
-
* @property {Element} list - the main element to operate inside of
|
|
2370
|
-
* @property {Element?} [itemsParent] - the element to use as parent for the items (automatically created in virtual mode, uses parent by default in non-virtual mode)
|
|
2371
|
-
* @property {boolean} [autoVirtualWrapperWidth=true] automatically set the width of the virtual wrapper
|
|
2372
|
-
* @property {boolean} [hookScrollEvent=true] automatically hook scroll event as needed
|
|
2373
|
-
* @property {number} [count=0] the item count
|
|
2374
|
-
* @property {boolean} [virtual=true] is virtual mode on?
|
|
2375
|
-
* @property {number} [estimatedItemHeight=20] estimated item height
|
|
2376
|
-
* @property {number} [buffer=5] the amount of buffer items to keep on each end of the list
|
|
2377
|
-
* @property {VirtualListHelper~ItemHeightEstimatorFunction} [itemHeightEstimatorFn] an optional function for providing item height estimations
|
|
2378
|
-
* @property {VirtualListHelper~ItemElementCreatorFunction} [itemElementCreatorFn] an optional function for providing fresh item elements (default creates `<li />`s)
|
|
2379
|
-
* @property {VirtualListHelper~ItemRenderFunction} [onItemRender] a function for rendering element content based on item index
|
|
2380
|
-
* @property {VirtualListHelper~ItemUnrenderFunction} [onItemUnrender] a function for freeing resources in an item element
|
|
2381
|
-
* @property {function(height: number)} [onScrollHeightChange] a function to be notified when scroll height changes
|
|
2382
|
-
*
|
|
2383
|
-
*/
|
|
2384
|
-
|
|
2385
|
-
/** */
|
|
2386
|
-
|
|
2387
|
-
const hasOwnProperty$1 = Object.prototype.hasOwnProperty;
|
|
2388
|
-
|
|
2389
|
-
const hasInsertAdjacentElement = Element.prototype.insertAdjacentElement !== undefined;
|
|
2390
|
-
|
|
2391
|
-
function insertBefore(el, before, parent) {
|
|
2392
|
-
if (!before)
|
|
2393
|
-
parent.appendChild(el);else
|
|
2394
|
-
if (hasInsertAdjacentElement === false || el instanceof DocumentFragment)
|
|
2395
|
-
parent.insertBefore(el, before);else
|
|
2396
|
-
before.insertAdjacentElement('beforebegin', el);
|
|
2397
|
-
}
|
|
2398
|
-
|
|
2399
|
-
/**
|
|
2400
|
-
*
|
|
2401
|
-
* @param {Element} itemEl
|
|
2402
|
-
* @param {DocumentFragment|null} fragment
|
|
2403
|
-
* @param {Node|undefined} before
|
|
2404
|
-
* @param {Element} itemParent
|
|
2405
|
-
* @returns {DocumentFragment|null}
|
|
2406
|
-
*/
|
|
2407
|
-
function insertBeforeWithFragment(itemEl, fragment, before, itemParent) {
|
|
2408
|
-
if (itemEl.parentNode !== itemParent) {
|
|
2409
|
-
if (!fragment)
|
|
2410
|
-
fragment = document.createDocumentFragment();
|
|
2411
|
-
fragment.appendChild(itemEl);
|
|
2412
|
-
} else {
|
|
2413
|
-
// insert fragment
|
|
2414
|
-
if (fragment && fragment.childNodes.length > 0) {
|
|
2415
|
-
insertBefore(fragment, before, itemParent);
|
|
2416
|
-
fragment = null;
|
|
2417
|
-
}
|
|
2418
|
-
|
|
2419
|
-
// insert element
|
|
2420
|
-
if (itemEl.nextSibling !== before) {
|
|
2421
|
-
insertBefore(itemEl, before, itemParent);
|
|
2422
|
-
}
|
|
2423
|
-
}
|
|
2424
|
-
|
|
2425
|
-
return fragment;
|
|
2426
|
-
}
|
|
2427
|
-
|
|
2428
|
-
class VirtualListHelper {
|
|
2429
|
-
/**
|
|
2430
|
-
* @param {VirtualListHelper~Options} opts
|
|
2431
|
-
*/
|
|
2432
|
-
constructor(opts) {var _opts$autoVirtualWrap;
|
|
2433
|
-
/** @private */
|
|
2434
|
-
const p = this._p = {
|
|
2435
|
-
// these come from options:
|
|
2436
|
-
|
|
2437
|
-
list: opts.list || null,
|
|
2438
|
-
hookScrollEvent: opts.hookScrollEvent === undefined ? true : !!opts.hookScrollEvent,
|
|
2439
|
-
count: opts.count || 0,
|
|
2440
|
-
virtual: opts.virtual === undefined ? true : !!opts.virtual,
|
|
2441
|
-
userItemsParent: opts.itemsParent || null,
|
|
2442
|
-
setVirtualWrapperWidth: (_opts$autoVirtualWrap = opts.autoVirtualWrapperWidth) !== null && _opts$autoVirtualWrap !== void 0 ? _opts$autoVirtualWrap : true,
|
|
2443
|
-
estimatedItemHeight: 20,
|
|
2444
|
-
buffer: 5,
|
|
2445
|
-
|
|
2446
|
-
/** @type VirtualListHelper~ItemHeightEstimatorFunction|null */
|
|
2447
|
-
itemHeightEstimatorFn: null,
|
|
2448
|
-
|
|
2449
|
-
/** @type VirtualListHelper~ItemElementCreatorFunction|null */
|
|
2450
|
-
itemElementCreatorFn: defaultElementCreator,
|
|
2451
|
-
|
|
2452
|
-
/** @type VirtualListHelper~ItemRenderFunction|null */
|
|
2453
|
-
onItemRender: null,
|
|
2454
|
-
|
|
2455
|
-
/** @type VirtualListHelper~ItemUnrenderFunction|null */
|
|
2456
|
-
onItemUnrender: null,
|
|
2457
|
-
|
|
2458
|
-
/** @type funct(height: number)|null */
|
|
2459
|
-
onScrollHeightChange: null,
|
|
2460
|
-
|
|
2461
|
-
// internal:
|
|
2462
|
-
|
|
2463
|
-
/** @type Element|null */
|
|
2464
|
-
virtualWrapper: null,
|
|
2465
|
-
|
|
2466
|
-
/** @type Element|null */
|
|
2467
|
-
currentItemsParent: null,
|
|
2468
|
-
|
|
2469
|
-
/** @type (number|undefined)[] */
|
|
2470
|
-
cachedItemHeights: [],
|
|
2471
|
-
|
|
2472
|
-
/** @type (number|undefined)[] */
|
|
2473
|
-
cachedItemEstimatedHeights: [],
|
|
2474
|
-
|
|
2475
|
-
/** @type (number|undefined)[] */
|
|
2476
|
-
cachedItemPositions: [],
|
|
2477
|
-
|
|
2478
|
-
/** @type number */
|
|
2479
|
-
itemPositionsNeedsUpdate: 0,
|
|
2480
|
-
|
|
2481
|
-
/** @type function */
|
|
2482
|
-
boundRender: this.render.bind(this),
|
|
2483
|
-
|
|
2484
|
-
/** @type Element[] */
|
|
2485
|
-
existingEls: []
|
|
2486
|
-
};
|
|
2487
|
-
|
|
2488
|
-
p.currentItemsParent = p.userItemsParent || p.list;
|
|
2489
|
-
|
|
2490
|
-
if (typeof opts.hookScrollEvent === 'boolean')
|
|
2491
|
-
this.setHookScrollEvent(opts.hookScrollEvent);else
|
|
2492
|
-
this._hookEvents();
|
|
2493
|
-
|
|
2494
|
-
if (typeof opts.count === 'number')
|
|
2495
|
-
this.setCount(opts.count);
|
|
2496
|
-
|
|
2497
|
-
if (typeof opts.virtual === 'boolean')
|
|
2498
|
-
this.setVirtual(opts.virtual);
|
|
2499
|
-
|
|
2500
|
-
if (typeof opts.estimatedItemHeight === 'number')
|
|
2501
|
-
this.setEstimatedItemHeight(opts.estimatedItemHeight);
|
|
2502
|
-
|
|
2503
|
-
if (typeof opts.buffer === 'number')
|
|
2504
|
-
this.setBuffer(opts.buffer);
|
|
2505
|
-
|
|
2506
|
-
if (typeof opts.itemHeightEstimatorFn === 'function')
|
|
2507
|
-
this.setItemHeightEstimatorFn(opts.itemHeightEstimatorFn);
|
|
2508
|
-
|
|
2509
|
-
if (typeof opts.itemElementCreatorFn === 'function')
|
|
2510
|
-
this.setItemElementCreatorFn(opts.itemElementCreatorFn);
|
|
2511
|
-
|
|
2512
|
-
if (typeof opts.onItemRender === 'function')
|
|
2513
|
-
this.setOnItemRender(opts.onItemRender);
|
|
2514
|
-
|
|
2515
|
-
if (typeof opts.onItemUnrender === 'function')
|
|
2516
|
-
this.setOnItemUnrender(opts.onItemUnrender);
|
|
2517
|
-
|
|
2518
|
-
if (typeof opts.onScrollHeightChange === 'function')
|
|
2519
|
-
this.setOnScrollHeightChange(opts.onScrollHeightChange);
|
|
2520
|
-
}
|
|
2521
|
-
|
|
2522
|
-
/**
|
|
2523
|
-
* Clean up and free up all resources.
|
|
2524
|
-
*/
|
|
2525
|
-
destroy() {
|
|
2526
|
-
this._unhookEvents().invalidate()._destroyElements();
|
|
2527
|
-
}
|
|
2528
|
-
|
|
2529
|
-
/**
|
|
2530
|
-
* Sets whether 'scroll' event on the list should be hooked automatically.
|
|
2531
|
-
* @param {boolean} enabled
|
|
2532
|
-
* @returns {VirtualListHelper}
|
|
2533
|
-
*/
|
|
2534
|
-
setHookScrollEvent(enabled) {
|
|
2535
|
-
const p = this._p;
|
|
2536
|
-
enabled = enabled === undefined ? true : !!enabled;
|
|
2537
|
-
|
|
2538
|
-
if (p.hookScrollEvent === enabled)
|
|
2539
|
-
return this;
|
|
2540
|
-
|
|
2541
|
-
p.hookScrollEvent = enabled;
|
|
2542
|
-
|
|
2543
|
-
this._unhookEvents()._hookEvents();
|
|
2544
|
-
|
|
2545
|
-
return this;
|
|
2546
|
-
}
|
|
2547
|
-
|
|
2548
|
-
/**
|
|
2549
|
-
* @returns {boolean} whether 'scroll' event on the list should be hooked automatically
|
|
2550
|
-
*/
|
|
2551
|
-
isHookScrollEventEnabled() {
|
|
2552
|
-
const p = this._p;
|
|
2553
|
-
return p.hookScrollEvent;
|
|
2554
|
-
}
|
|
2555
|
-
|
|
2556
|
-
/**
|
|
2557
|
-
* Sets the list item count. <br />
|
|
2558
|
-
* You should probably call `render()` after this.
|
|
2559
|
-
* @param {number} count
|
|
2560
|
-
* @returns {VirtualListHelper}
|
|
2561
|
-
*/
|
|
2562
|
-
setCount(count) {
|
|
2563
|
-
const p = this._p;
|
|
2564
|
-
p.count = count;
|
|
2565
|
-
|
|
2566
|
-
return this.invalidate();
|
|
2567
|
-
}
|
|
2568
|
-
|
|
2569
|
-
/**
|
|
2570
|
-
* @returns {number} current item count
|
|
2571
|
-
*/
|
|
2572
|
-
getCount() {
|
|
2573
|
-
const p = this._p;
|
|
2574
|
-
return p.count;
|
|
2575
|
-
}
|
|
2576
|
-
|
|
2577
|
-
/**
|
|
2578
|
-
* Switches between virtual and non-virtual mode. <br />
|
|
2579
|
-
* The list is invalidated automatically. <br />
|
|
2580
|
-
* You should call `render()` to update the view.
|
|
2581
|
-
* @param {boolean} enabled
|
|
2582
|
-
* @returns {VirtualListHelper}
|
|
2583
|
-
*/
|
|
2584
|
-
setVirtual(enabled) {
|
|
2585
|
-
const p = this._p;
|
|
2586
|
-
enabled = enabled === undefined ? true : !!enabled;
|
|
2587
|
-
|
|
2588
|
-
if (p.virtual === enabled)
|
|
2589
|
-
return this;
|
|
2590
|
-
|
|
2591
|
-
p.virtual = enabled;
|
|
2592
|
-
|
|
2593
|
-
this._hookEvents().invalidate()._destroyElements();
|
|
2594
|
-
|
|
2595
|
-
return this;
|
|
2596
|
-
}
|
|
2597
|
-
|
|
2598
|
-
/**
|
|
2599
|
-
* @returns {boolean} virtual mode
|
|
2600
|
-
*/
|
|
2601
|
-
isVirtual() {
|
|
2602
|
-
const p = this._p;
|
|
2603
|
-
return p.virtual;
|
|
2604
|
-
}
|
|
2605
|
-
|
|
2606
|
-
/**
|
|
2607
|
-
* Sets estimated item height. <br />
|
|
2608
|
-
* No need to be accurate. <br />
|
|
2609
|
-
* The better the estimation - the better the scrollbar behavior will be. <br />
|
|
2610
|
-
* Applicable for virtual-mode only. <br />
|
|
2611
|
-
* You should `invalidate` if you want this to take effect on the existing rendering.
|
|
2612
|
-
* @param {number} height - a positive number representing estimated item height.
|
|
2613
|
-
* @returns {VirtualListHelper}
|
|
2614
|
-
*/
|
|
2615
|
-
setEstimatedItemHeight(height) {
|
|
2616
|
-
const p = this._p;
|
|
2617
|
-
p.estimatedItemHeight = Math.abs((typeof height === 'number' ? height : Number(height)) || 20);
|
|
2618
|
-
return this;
|
|
2619
|
-
}
|
|
2620
|
-
|
|
2621
|
-
/**
|
|
2622
|
-
* @returns {number} current item height estimation
|
|
2623
|
-
*/
|
|
2624
|
-
getEstimatedItemHeight() {
|
|
2625
|
-
const p = this._p;
|
|
2626
|
-
return p.estimatedItemHeight;
|
|
2627
|
-
}
|
|
2628
|
-
|
|
2629
|
-
/**
|
|
2630
|
-
* Sets whether the virtual wrapper width should be set automatically. <br />
|
|
2631
|
-
* @param {boolean} enabled
|
|
2632
|
-
* @returns {VirtualListHelper}
|
|
2633
|
-
*/
|
|
2634
|
-
setAutoVirtualWrapperWidth(enabled) {
|
|
2635
|
-
const p = this._p;
|
|
2636
|
-
p.autoVirtualWrapperWidth = enabled === undefined ? true : !!enabled;
|
|
2637
|
-
|
|
2638
|
-
if (p.virtualWrapper) {
|
|
2639
|
-
if (p.autoVirtualWrapperWidth !== p.virtualWrapperWidthWasSet) {
|
|
2640
|
-
p.virtualWrapper.style.width = p.autoVirtualWrapperWidth ? '100%' : '';
|
|
2641
|
-
p.virtualWrapperWidthWasSet = p.autoVirtualWrapperWidth;
|
|
2642
|
-
}
|
|
2643
|
-
}
|
|
2644
|
-
|
|
2645
|
-
return this;
|
|
2646
|
-
}
|
|
2647
|
-
|
|
2648
|
-
/**
|
|
2649
|
-
* @returns {boolean} whether the virtual wrapper width should be set automatically
|
|
2650
|
-
*/
|
|
2651
|
-
isAutoVirtualWrapperWidth() {
|
|
2652
|
-
const p = this._p;
|
|
2653
|
-
return p.autoVirtualWrapperWidth;
|
|
2654
|
-
}
|
|
2655
|
-
|
|
2656
|
-
/**
|
|
2657
|
-
* Sets the amount of buffer items to keep on each end of the list. <br />
|
|
2658
|
-
* Applicable for virtual-mode only.
|
|
2659
|
-
* @param {number} buffer - a positive value representing the count of buffer items for each end.
|
|
2660
|
-
* @returns {VirtualListHelper}
|
|
2661
|
-
*/
|
|
2662
|
-
setBuffer(buffer) {
|
|
2663
|
-
const p = this._p;
|
|
2664
|
-
p.buffer = Math.abs(typeof buffer === 'number' ? buffer : Number(buffer) || 5);
|
|
2665
|
-
return this;
|
|
2666
|
-
}
|
|
2667
|
-
|
|
2668
|
-
/**
|
|
2669
|
-
* @returns {number} current buffer value
|
|
2670
|
-
*/
|
|
2671
|
-
getBuffer() {
|
|
2672
|
-
const p = this._p;
|
|
2673
|
-
return p.buffer;
|
|
2674
|
-
}
|
|
2675
|
-
|
|
2676
|
-
/**
|
|
2677
|
-
* The `itemHeightEstimatorFn` is an alternative to `estimatedItemHeight` to give better estimations for specific item. <br/>
|
|
2678
|
-
* It's optional, and if it's present - it should return either a numeric height estimation,
|
|
2679
|
-
* or `undefined` to fall back to the default estimation. <br />
|
|
2680
|
-
* You should `invalidate` if you want this to take effect on the existing rendering.
|
|
2681
|
-
* @param {VirtualListHelper~ItemHeightEstimatorFunction} fn
|
|
2682
|
-
* @returns {VirtualListHelper}
|
|
2683
|
-
*/
|
|
2684
|
-
setItemHeightEstimatorFn(fn) {
|
|
2685
|
-
const p = this._p;
|
|
2686
|
-
p.itemHeightEstimatorFn = fn;
|
|
2687
|
-
return this;
|
|
2688
|
-
}
|
|
2689
|
-
|
|
2690
|
-
/**
|
|
2691
|
-
* The `itemElementCreatorFn` is a function creating a basic item element, that will be possibly reused later. <br />
|
|
2692
|
-
* It has no association with a specific item index. <br />
|
|
2693
|
-
* You should `invalidate` if you want this to take effect on the existing rendering.
|
|
2694
|
-
* @param {VirtualListHelper~ItemElementCreatorFunction} fn
|
|
2695
|
-
* @returns {VirtualListHelper}
|
|
2696
|
-
*/
|
|
2697
|
-
setItemElementCreatorFn(fn) {
|
|
2698
|
-
const p = this._p;
|
|
2699
|
-
p.itemElementCreatorFn = fn || defaultElementCreator;
|
|
2700
|
-
return this;
|
|
2701
|
-
}
|
|
2702
|
-
|
|
2703
|
-
/**
|
|
2704
|
-
* The `onItemRender` is a function called for rendering the contents of an item. <br />
|
|
2705
|
-
* It's passed an `Element` and an item index. <br />
|
|
2706
|
-
* You should `invalidate` if you want this to take effect on the existing rendering.
|
|
2707
|
-
* @param {VirtualListHelper~ItemRenderFunction} fn
|
|
2708
|
-
* @returns {VirtualListHelper}
|
|
2709
|
-
*/
|
|
2710
|
-
setOnItemRender(fn) {
|
|
2711
|
-
const p = this._p;
|
|
2712
|
-
p.onItemRender = fn;
|
|
2713
|
-
return this;
|
|
2714
|
-
}
|
|
2715
|
-
|
|
2716
|
-
/**
|
|
2717
|
-
* The `onItemUnrender` is a function called for freeing resources in an item element,
|
|
2718
|
-
* if you've attached something that needs to be explicitly freed. <br />
|
|
2719
|
-
* It's passed an `Element` only, and has no association with a specific index,
|
|
2720
|
-
* as by the time it's called - the indexes are probably not valid anymore.
|
|
2721
|
-
* @param {VirtualListHelper~ItemUnrenderFunction} fn
|
|
2722
|
-
* @returns {VirtualListHelper}
|
|
2723
|
-
*/
|
|
2724
|
-
setOnItemUnrender(fn) {
|
|
2725
|
-
const p = this._p;
|
|
2726
|
-
p.onItemUnrender = fn;
|
|
2727
|
-
return this;
|
|
2728
|
-
}
|
|
2729
|
-
|
|
2730
|
-
/**
|
|
2731
|
-
* The `onScrollHeightChange` is a function called when the scroll height changes.
|
|
2732
|
-
* @param {function(height: number)} fn
|
|
2733
|
-
* @returns {VirtualListHelper}
|
|
2734
|
-
*/
|
|
2735
|
-
setOnScrollHeightChange(fn) {
|
|
2736
|
-
const p = this._p;
|
|
2737
|
-
p.onScrollHeightChange = fn;
|
|
2738
|
-
return this;
|
|
2739
|
-
}
|
|
2740
|
-
|
|
2741
|
-
/**
|
|
2742
|
-
* Estimates the full scroll height. This gets better as more renderings occur.
|
|
2743
|
-
* @returns {number}
|
|
2744
|
-
*/
|
|
2745
|
-
estimateFullHeight() {
|
|
2746
|
-
const p = this._p;
|
|
2747
|
-
|
|
2748
|
-
if (p.count === 0)
|
|
2749
|
-
return 0;
|
|
2750
|
-
|
|
2751
|
-
if (p.virtual) {
|
|
2752
|
-
return this._calculateItemPosition(p.count) || 0;
|
|
2753
|
-
} else {
|
|
2754
|
-
const existingEls = p.existingEls;
|
|
2755
|
-
if (p.count === existingEls.length) {
|
|
2756
|
-
let rect1 = existingEls[0].getBoundingClientRect();
|
|
2757
|
-
let rect2 = existingEls[existingEls.length - 1].getBoundingClientRect();
|
|
2758
|
-
return rect2.top - rect1.top + rect2.height;
|
|
2759
|
-
}
|
|
2760
|
-
|
|
2761
|
-
return this._calculateItemPosition(p.count) || 0;
|
|
2762
|
-
}
|
|
2763
|
-
}
|
|
2764
|
-
|
|
2765
|
-
/**
|
|
2766
|
-
* States that the cached positions/heights are invalid,
|
|
2767
|
-
* and needs to be completely re-calculated.<br />
|
|
2768
|
-
* You should probably call `render()` after this.
|
|
2769
|
-
* @returns {VirtualListHelper}
|
|
2770
|
-
*/
|
|
2771
|
-
invalidatePositions() {
|
|
2772
|
-
const p = this._p;
|
|
2773
|
-
|
|
2774
|
-
p.itemPositionsNeedsUpdate = 0;
|
|
2775
|
-
p.cachedItemHeights = [];
|
|
2776
|
-
p.cachedItemEstimatedHeights = [];
|
|
2777
|
-
p.cachedItemPositions = [];
|
|
2778
|
-
p.cachedItemHeights.length = p.count;
|
|
2779
|
-
p.cachedItemEstimatedHeights.length = p.count;
|
|
2780
|
-
p.cachedItemPositions.length = p.count;
|
|
2781
|
-
|
|
2782
|
-
return this;
|
|
2783
|
-
}
|
|
2784
|
-
|
|
2785
|
-
/**
|
|
2786
|
-
* States that the indexes/item count/rendered content are invalid,
|
|
2787
|
-
* and needs to be completely re-calculated and re-rendered. <br />
|
|
2788
|
-
* You should probably call `render()` after this.
|
|
2789
|
-
* @returns {VirtualListHelper}
|
|
2790
|
-
*/
|
|
2791
|
-
invalidate() {
|
|
2792
|
-
const p = this._p;
|
|
2793
|
-
|
|
2794
|
-
this.invalidatePositions();
|
|
2795
|
-
|
|
2796
|
-
if (!p.virtual) {
|
|
2797
|
-
this._destroyElements();
|
|
2798
|
-
}
|
|
2799
|
-
|
|
2800
|
-
return this;
|
|
2801
|
-
}
|
|
2802
|
-
|
|
2803
|
-
/**
|
|
2804
|
-
* Renders the current viewport. <br />
|
|
2805
|
-
* Call this after making changes to the list. <br />
|
|
2806
|
-
* In virtual mode, this is called automatically for every scroll event.
|
|
2807
|
-
*/
|
|
2808
|
-
render() {
|
|
2809
|
-
const p = this._p;
|
|
2810
|
-
const list = p.list;
|
|
2811
|
-
const virtual = p.virtual;
|
|
2812
|
-
let virtualWrapper = p.virtualWrapper;
|
|
2813
|
-
let itemParent = p.currentItemsParent;
|
|
2814
|
-
let scrollTop = list.scrollTop;
|
|
2815
|
-
let visibleHeight = list.clientHeight;
|
|
2816
|
-
let visibleBottom = scrollTop + visibleHeight;
|
|
2817
|
-
let count = p.count;
|
|
2818
|
-
let buffer = p.buffer;
|
|
2819
|
-
let onItemUnrender = p.onItemUnrender;
|
|
2820
|
-
let existingEls = p.existingEls;
|
|
2821
|
-
let existingCount = existingEls.length;
|
|
2822
|
-
|
|
2823
|
-
if (virtual) {
|
|
2824
|
-
const originalWidth = list.clientWidth;
|
|
2825
|
-
|
|
2826
|
-
if (!virtualWrapper) {
|
|
2827
|
-
virtualWrapper = p.virtualWrapper = p.userItemsParent;
|
|
2828
|
-
if (!virtualWrapper) {
|
|
2829
|
-
virtualWrapper = p.virtualWrapper = document.createElement('div');
|
|
2830
|
-
list.appendChild(virtualWrapper);
|
|
2831
|
-
}
|
|
2832
|
-
|
|
2833
|
-
this._resetCurrentItemsParent();
|
|
2834
|
-
itemParent = p.currentItemsParent;
|
|
2835
|
-
|
|
2836
|
-
if (p.autoVirtualWrapperWidth) {
|
|
2837
|
-
virtualWrapper.style.width = '100%';
|
|
2838
|
-
p.virtualWrapperWidthWasSet = true;
|
|
2839
|
-
} else {
|
|
2840
|
-
p.virtualWrapperWidthWasSet = false;
|
|
2841
|
-
}
|
|
2842
|
-
}
|
|
2843
|
-
|
|
2844
|
-
// Mark all of them for potential reuse
|
|
2845
|
-
for (let i = 0; i < existingCount; i++) {
|
|
2846
|
-
existingEls[i][ReuseElSymbol] = true;
|
|
2847
|
-
}
|
|
2848
|
-
|
|
2849
|
-
// Make sure we have at least estimated positions for all items so we can translate scroll position
|
|
2850
|
-
this._calculateItemPosition(p.count - 1);
|
|
2851
|
-
|
|
2852
|
-
// Find existing elements index range
|
|
2853
|
-
let existingRange = this._getExistingElsRange();
|
|
2854
|
-
|
|
2855
|
-
// Find first visible element
|
|
2856
|
-
let firstVisibleIndex = binarySearchPosition(p.cachedItemPositions, scrollTop);
|
|
2857
|
-
let firstRenderIndex = Math.max(0, firstVisibleIndex - buffer);
|
|
2858
|
-
|
|
2859
|
-
// Iterate over viewport
|
|
2860
|
-
let index = firstRenderIndex;
|
|
2861
|
-
let renderPos = this._calculateItemPosition(index);
|
|
2862
|
-
let bufferEnd = buffer;
|
|
2863
|
-
|
|
2864
|
-
// we want to render until viewport's bottom + buffer items
|
|
2865
|
-
let maxIndexToRender = Math.max(index, binarySearchPosition(p.cachedItemPositions, visibleBottom - 1) + 1 + buffer);
|
|
2866
|
-
|
|
2867
|
-
let insertedItems = [];
|
|
2868
|
-
|
|
2869
|
-
/** @type DocumentFragment|null */
|
|
2870
|
-
let fragment = null;
|
|
2871
|
-
|
|
2872
|
-
// Find the element to insert before
|
|
2873
|
-
let before = virtualWrapper.childNodes[0];
|
|
2874
|
-
|
|
2875
|
-
const findElementToReuse = function (index) {
|
|
2876
|
-
// Find existing element to reuse
|
|
2877
|
-
/** @type Element|undefined */
|
|
2878
|
-
let existingEl = undefined;
|
|
2879
|
-
|
|
2880
|
-
if (existingRange.firstIndex !== -1 && index >= existingRange.firstIndex && index <= existingRange.lastIndex) {
|
|
2881
|
-
existingEl = existingEls.find((x) => x[ItemIndexSymbol] === index && x[ReuseElSymbol] === true);
|
|
2882
|
-
}
|
|
2883
|
-
|
|
2884
|
-
if (existingEl === undefined) {
|
|
2885
|
-
existingEl = (existingRange.firstIndex < firstRenderIndex || existingRange.firstValidArrayIndex > 0 ?
|
|
2886
|
-
existingEls.find((x) =>
|
|
2887
|
-
(x[ItemIndexSymbol] < firstRenderIndex || false === hasOwnProperty$1.call(x, ItemIndexSymbol)) &&
|
|
2888
|
-
x[ReuseElSymbol] === true) :
|
|
2889
|
-
undefined) ||
|
|
2890
|
-
findLast(existingEls, (x) => x[ReuseElSymbol] === true);
|
|
2891
|
-
}
|
|
2892
|
-
|
|
2893
|
-
if (existingEl !== undefined) {
|
|
2894
|
-
delete existingEl[ReuseElSymbol];
|
|
2895
|
-
}
|
|
2896
|
-
|
|
2897
|
-
return existingEl;
|
|
2898
|
-
};
|
|
2899
|
-
|
|
2900
|
-
// First we iterate and try to add all at once in a fragment, as much as we can.
|
|
2901
|
-
// And then reflow the at once.
|
|
2902
|
-
for (; index < count && index < maxIndexToRender; index++) {
|
|
2903
|
-
let existingEl = findElementToReuse(index);
|
|
2904
|
-
|
|
2905
|
-
if (before && before === existingEl)
|
|
2906
|
-
before = before.nextSibling;
|
|
2907
|
-
|
|
2908
|
-
// Dequeue the element by reusing or creating a new one
|
|
2909
|
-
const itemEl = this._dequeueElementForIndex(existingEl, index, before, true);
|
|
2910
|
-
insertedItems.push([itemEl, index]);
|
|
2911
|
-
|
|
2912
|
-
fragment = insertBeforeWithFragment(itemEl, fragment, before, itemParent);
|
|
2913
|
-
}
|
|
2914
|
-
|
|
2915
|
-
// Insert any remaining fragment
|
|
2916
|
-
if (fragment && fragment.childNodes.length > 0) {
|
|
2917
|
-
insertBefore(fragment, before, itemParent);
|
|
2918
|
-
}
|
|
2919
|
-
|
|
2920
|
-
// Iterate on inserted items and reflow them
|
|
2921
|
-
for (let item of insertedItems) {
|
|
2922
|
-
const index = item[1];
|
|
2923
|
-
this._insertItemAndFlow(item[0], index, false /* inserted already */);
|
|
2924
|
-
renderPos = p.cachedItemPositions[index] + p.cachedItemHeights[index];
|
|
2925
|
-
}
|
|
2926
|
-
|
|
2927
|
-
// See if we still need to insert more items
|
|
2928
|
-
if (renderPos < visibleBottom) {
|
|
2929
|
-
for (; (renderPos < visibleBottom || bufferEnd-- > 0) && index < count; index++) {
|
|
2930
|
-
let existingEl = findElementToReuse(index);
|
|
2931
|
-
|
|
2932
|
-
if (before && before === existingEl)
|
|
2933
|
-
before = before.nextSibling;
|
|
2934
|
-
|
|
2935
|
-
// Dequeue the element by reusing or creating a new one
|
|
2936
|
-
this._dequeueElementForIndex(existingEl, index, before, false);
|
|
2937
|
-
|
|
2938
|
-
// Increment pointers
|
|
2939
|
-
renderPos = p.cachedItemPositions[index] + p.cachedItemHeights[index];
|
|
2940
|
-
}
|
|
2941
|
-
}
|
|
2942
|
-
|
|
2943
|
-
// Calculate up-to-date scroll height
|
|
2944
|
-
let scrollHeight = this.estimateFullHeight();
|
|
2945
|
-
let scrollHeightPx = scrollHeight + 'px';
|
|
2946
|
-
|
|
2947
|
-
if (virtualWrapper.style.height !== scrollHeightPx) {var _p$onScrollHeightChan;
|
|
2948
|
-
p.virtualWrapper.style.height = scrollHeightPx;
|
|
2949
|
-
(_p$onScrollHeightChan = p.onScrollHeightChange) === null || _p$onScrollHeightChan === void 0 || _p$onScrollHeightChan.call(p, scrollHeight);
|
|
2950
|
-
}
|
|
2951
|
-
|
|
2952
|
-
if (originalWidth !== list.clientWidth)
|
|
2953
|
-
this.render();
|
|
2954
|
-
} else {// non-virtual
|
|
2955
|
-
if (count !== existingEls.length) {
|
|
2956
|
-
for (let i = 0; i < existingCount; i++) {
|
|
2957
|
-
existingEls[i][ReuseElSymbol] = true;
|
|
2958
|
-
}
|
|
2959
|
-
|
|
2960
|
-
// Find the element to insert before
|
|
2961
|
-
let before = itemParent.childNodes[0];
|
|
2962
|
-
|
|
2963
|
-
/** @type DocumentFragment|null */
|
|
2964
|
-
let fragment = null;
|
|
2965
|
-
|
|
2966
|
-
for (let index = 0; index < count; index++) {
|
|
2967
|
-
// Find existing element to reuse
|
|
2968
|
-
let existingEl = existingEls.find((x) => x[ItemIndexSymbol] === index && x[ReuseElSymbol] === true);
|
|
2969
|
-
|
|
2970
|
-
if (existingEl !== undefined) {
|
|
2971
|
-
delete existingEl[ReuseElSymbol];
|
|
2972
|
-
}
|
|
2973
|
-
|
|
2974
|
-
if (before && before === existingEl)
|
|
2975
|
-
before = before.nextSibling;
|
|
2976
|
-
|
|
2977
|
-
// Dequeue the element by reusing or creating a new one
|
|
2978
|
-
const itemEl = this._dequeueElementForIndex(existingEl, index, before, true);
|
|
2979
|
-
|
|
2980
|
-
fragment = insertBeforeWithFragment(itemEl, fragment, before, itemParent);
|
|
2981
|
-
}
|
|
2982
|
-
|
|
2983
|
-
// Insert any remaining fragment
|
|
2984
|
-
if (fragment && fragment.childNodes.length > 0) {
|
|
2985
|
-
insertBefore(fragment, before, itemParent);
|
|
2986
|
-
}
|
|
2987
|
-
}
|
|
2988
|
-
}
|
|
2989
|
-
|
|
2990
|
-
// Cleanup extra unused elements
|
|
2991
|
-
existingCount = existingEls.length; // May have changed
|
|
2992
|
-
for (let i = 0; i < existingCount; i++) {
|
|
2993
|
-
const el = existingEls[i];
|
|
2994
|
-
if (el[ReuseElSymbol] !== true) continue;
|
|
2995
|
-
|
|
2996
|
-
let parent = el.parentNode;
|
|
2997
|
-
if (parent)
|
|
2998
|
-
parent.removeChild(el);
|
|
2999
|
-
if (onItemUnrender && el[ItemIndexSymbol] !== undefined)
|
|
3000
|
-
onItemUnrender(el);
|
|
3001
|
-
existingEls.splice(i, 1);
|
|
3002
|
-
|
|
3003
|
-
i--;
|
|
3004
|
-
existingCount--;
|
|
3005
|
-
}
|
|
3006
|
-
}
|
|
3007
|
-
|
|
3008
|
-
/**
|
|
3009
|
-
* States that items were added at a certain position in the list. <br />
|
|
3010
|
-
* Virtual mode: Call `render()` to update the view after making changes.
|
|
3011
|
-
* @param {number} count
|
|
3012
|
-
* @param {number} [atIndex=-1]
|
|
3013
|
-
* @returns {VirtualListHelper}
|
|
3014
|
-
*/
|
|
3015
|
-
addItemsAt(count, atIndex = -1) {
|
|
3016
|
-
if (typeof count !== 'number' || count <= 0)
|
|
3017
|
-
return this;
|
|
3018
|
-
|
|
3019
|
-
const p = this._p;
|
|
3020
|
-
|
|
3021
|
-
if (atIndex < 0 || atIndex >= p.count)
|
|
3022
|
-
atIndex = p.count;
|
|
3023
|
-
|
|
3024
|
-
p.count += count;
|
|
3025
|
-
|
|
3026
|
-
if (p.virtual) {
|
|
3027
|
-
if (atIndex >= 0 && atIndex < p.count) {
|
|
3028
|
-
this._invalidateItemIndexesAt(atIndex, -1);
|
|
3029
|
-
}
|
|
3030
|
-
} else
|
|
3031
|
-
{// non-virtual
|
|
3032
|
-
let existingEls = p.existingEls;
|
|
3033
|
-
let existingCount = existingEls.length;
|
|
3034
|
-
if (existingCount !== p.count - count)
|
|
3035
|
-
return this;
|
|
3036
|
-
|
|
3037
|
-
let existingRange = this._getExistingElsRange();
|
|
3038
|
-
if (existingRange.firstValidArrayIndex === -1)
|
|
3039
|
-
return this;
|
|
3040
|
-
|
|
3041
|
-
const itemParent = p.currentItemsParent;
|
|
3042
|
-
|
|
3043
|
-
let startIndex = existingRange.firstValidArrayIndex + atIndex - existingRange.firstIndex;
|
|
3044
|
-
|
|
3045
|
-
this._pushItemIndexesAt(atIndex, count);
|
|
3046
|
-
|
|
3047
|
-
/** @type Node|undefined */
|
|
3048
|
-
let before = existingEls[startIndex - 1] ?
|
|
3049
|
-
existingEls[startIndex - 1].nextSibling :
|
|
3050
|
-
existingEls[0];
|
|
3051
|
-
|
|
3052
|
-
/** @type DocumentFragment|null */
|
|
3053
|
-
let fragment = null;
|
|
3054
|
-
|
|
3055
|
-
for (let index = atIndex, end = atIndex + count; index < end; index++) {
|
|
3056
|
-
const itemEl = this._dequeueElementForIndex(undefined, index, before, true);
|
|
3057
|
-
fragment = insertBeforeWithFragment(itemEl, fragment, before, itemParent);
|
|
3058
|
-
}
|
|
3059
|
-
|
|
3060
|
-
// Insert any remaining fragment
|
|
3061
|
-
if (fragment && fragment.childNodes.length > 0) {
|
|
3062
|
-
insertBefore(fragment, before, itemParent);
|
|
3063
|
-
}
|
|
3064
|
-
}
|
|
3065
|
-
|
|
3066
|
-
return this;
|
|
3067
|
-
}
|
|
3068
|
-
|
|
3069
|
-
/**
|
|
3070
|
-
* States that items were removed at a certain position in the list. <br />
|
|
3071
|
-
* Virtual mode: Call `render()` to update the view after making changes.
|
|
3072
|
-
* @param {number} count
|
|
3073
|
-
* @param {number} atIndex
|
|
3074
|
-
* @returns {VirtualListHelper}
|
|
3075
|
-
*/
|
|
3076
|
-
removeItemsAt(count, atIndex) {
|
|
3077
|
-
const p = this._p;
|
|
3078
|
-
|
|
3079
|
-
if (typeof count !== 'number' || typeof atIndex !== 'number' || count <= 0 || atIndex < 0 || atIndex >= p.count)
|
|
3080
|
-
return this;
|
|
3081
|
-
|
|
3082
|
-
p.count -= Math.min(count, p.count - atIndex);
|
|
3083
|
-
|
|
3084
|
-
if (p.virtual) {
|
|
3085
|
-
this._invalidateItemIndexesAt(atIndex, -1);
|
|
3086
|
-
} else
|
|
3087
|
-
{// non-virtual
|
|
3088
|
-
let existingEls = p.existingEls;
|
|
3089
|
-
let existingCount = existingEls.length;
|
|
3090
|
-
if (existingCount !== p.count + count)
|
|
3091
|
-
return this;
|
|
3092
|
-
|
|
3093
|
-
let existingRange = this._getExistingElsRange();
|
|
3094
|
-
if (existingRange.firstValidArrayIndex === -1)
|
|
3095
|
-
return this;
|
|
3096
|
-
|
|
3097
|
-
this._pushItemIndexesAt(atIndex + count, -count);
|
|
3098
|
-
|
|
3099
|
-
const onItemUnrender = p.onItemUnrender;
|
|
3100
|
-
let index = existingRange.firstValidArrayIndex + atIndex - existingRange.firstIndex;
|
|
3101
|
-
if (index < existingEls.length) {
|
|
3102
|
-
for (let i = 0; i < count; i++) {
|
|
3103
|
-
let itemEl = existingEls[index + i];
|
|
3104
|
-
|
|
3105
|
-
let parent = itemEl.parentNode;
|
|
3106
|
-
if (parent)
|
|
3107
|
-
parent.removeChild(itemEl);
|
|
3108
|
-
if (onItemUnrender && itemEl[ItemIndexSymbol] !== undefined)
|
|
3109
|
-
onItemUnrender(itemEl);
|
|
3110
|
-
}
|
|
3111
|
-
existingEls.splice(index, count);
|
|
3112
|
-
}
|
|
3113
|
-
}
|
|
3114
|
-
|
|
3115
|
-
return this;
|
|
3116
|
-
}
|
|
3117
|
-
|
|
3118
|
-
/**
|
|
3119
|
-
* Mark an element for a re-render. <br />
|
|
3120
|
-
* Virtual mode: Call `render()` to update the view after making changes. <br />
|
|
3121
|
-
* Non-virtual mode - the element is re-rendered immediately.
|
|
3122
|
-
* @param {number} index - the index of the element to re-render
|
|
3123
|
-
* @returns {VirtualListHelper}
|
|
3124
|
-
*/
|
|
3125
|
-
refreshItemAt(index) {
|
|
3126
|
-
const p = this._p;
|
|
3127
|
-
|
|
3128
|
-
if (typeof index !== 'number' || index < 0 || index >= p.count)
|
|
3129
|
-
return this;
|
|
3130
|
-
|
|
3131
|
-
if (p.virtual) {
|
|
3132
|
-
this._invalidateItemIndexesAt(index, 1);
|
|
3133
|
-
} else
|
|
3134
|
-
{// non-virtual
|
|
3135
|
-
let existingEls = p.existingEls;
|
|
3136
|
-
let existingCount = existingEls.length;
|
|
3137
|
-
if (existingCount !== p.count)
|
|
3138
|
-
return this;
|
|
3139
|
-
|
|
3140
|
-
let existingRange = this._getExistingElsRange();
|
|
3141
|
-
|
|
3142
|
-
if (index >= existingRange.firstIndex && index <= existingRange.lastIndex) {
|
|
3143
|
-
let itemEl = existingEls[existingRange.firstValidArrayIndex + index - existingRange.firstIndex];
|
|
3144
|
-
delete itemEl[ItemIndexSymbol];
|
|
3145
|
-
this._dequeueElementForIndex(itemEl, index, itemEl.nextSibling, false);
|
|
3146
|
-
}
|
|
3147
|
-
}
|
|
3148
|
-
|
|
3149
|
-
return this;
|
|
3150
|
-
}
|
|
3151
|
-
|
|
3152
|
-
/**
|
|
3153
|
-
* Tests whether an item at the specified index is rendered.
|
|
3154
|
-
* @param {number} index - the index to test
|
|
3155
|
-
* @returns {boolean}
|
|
3156
|
-
*/
|
|
3157
|
-
isItemRendered(index) {
|
|
3158
|
-
const p = this._p;
|
|
3159
|
-
|
|
3160
|
-
if (typeof index !== 'number' || index < 0 || index >= p.count)
|
|
3161
|
-
return false;
|
|
3162
|
-
|
|
3163
|
-
let existingRange = this._getExistingElsRange();
|
|
3164
|
-
|
|
3165
|
-
return index >= existingRange.firstIndex && index <= existingRange.lastIndex;
|
|
3166
|
-
}
|
|
3167
|
-
|
|
3168
|
-
/**
|
|
3169
|
-
* Retrieves DOM element for the item at the specified index - if it's currently rendered.
|
|
3170
|
-
* @param {number} index - the index to retrieve
|
|
3171
|
-
* @returns {Element|undefined}
|
|
3172
|
-
*/
|
|
3173
|
-
getItemElementAt(index) {
|
|
3174
|
-
const p = this._p;
|
|
3175
|
-
|
|
3176
|
-
if (typeof index !== 'number' || index < 0 || index >= p.count)
|
|
3177
|
-
return undefined;
|
|
3178
|
-
|
|
3179
|
-
let existingEls = p.existingEls;
|
|
3180
|
-
let existingRange = this._getExistingElsRange();
|
|
3181
|
-
|
|
3182
|
-
if (index >= existingRange.firstIndex && index <= existingRange.lastIndex) {
|
|
3183
|
-
return existingEls[existingRange.firstValidArrayIndex + index - existingRange.firstIndex];
|
|
3184
|
-
}
|
|
3185
|
-
|
|
3186
|
-
return undefined;
|
|
3187
|
-
}
|
|
3188
|
-
|
|
3189
|
-
/**
|
|
3190
|
-
* Retrieves the position for the specified index. <br />
|
|
3191
|
-
* Can be used to scroll to a specific item.
|
|
3192
|
-
* @param {number} index
|
|
3193
|
-
* @returns {number|undefined}
|
|
3194
|
-
*/
|
|
3195
|
-
getItemPosition(index) {
|
|
3196
|
-
const p = this._p;
|
|
3197
|
-
|
|
3198
|
-
if (typeof index !== 'number' || index < 0 || index >= p.count)
|
|
3199
|
-
return undefined;
|
|
3200
|
-
|
|
3201
|
-
if (p.virtual) {
|
|
3202
|
-
return this._calculateItemPosition(index);
|
|
3203
|
-
} else {
|
|
3204
|
-
let itemEl = this.getItemElementAt(index);
|
|
3205
|
-
if (itemEl === undefined)
|
|
3206
|
-
return undefined;
|
|
3207
|
-
|
|
3208
|
-
const list = p.list;
|
|
3209
|
-
return Css.getElementOffset(itemEl).top - Css.getElementOffset(list).top + list.scrollTop;
|
|
3210
|
-
}
|
|
3211
|
-
}
|
|
3212
|
-
|
|
3213
|
-
/**
|
|
3214
|
-
* Retrieves the item index for the specified element
|
|
3215
|
-
* @param {Element} el
|
|
3216
|
-
* @returns {number|undefined}
|
|
3217
|
-
*/
|
|
3218
|
-
getItemIndexFromElement(el) {
|
|
3219
|
-
return el ? el[ItemIndexSymbol] : undefined;
|
|
3220
|
-
}
|
|
3221
|
-
|
|
3222
|
-
/**
|
|
3223
|
-
* Retrieves the size (or estimated size, if unknown) for the specified index. <br />
|
|
3224
|
-
* @param {number} index
|
|
3225
|
-
* @returns {number|undefined}
|
|
3226
|
-
*/
|
|
3227
|
-
getItemSize(index) {
|
|
3228
|
-
const p = this._p;
|
|
3229
|
-
|
|
3230
|
-
if (typeof index !== 'number' || index < 0 || index >= p.count)
|
|
3231
|
-
return undefined;
|
|
3232
|
-
|
|
3233
|
-
let height = p.cachedItemHeights[index - 1]; // already calculated
|
|
3234
|
-
|
|
3235
|
-
if (height === undefined) {
|
|
3236
|
-
height = p.itemHeightEstimatorFn ? p.itemHeightEstimatorFn(index - 1) : null; // estimated per item
|
|
3237
|
-
|
|
3238
|
-
if (typeof height !== 'number')
|
|
3239
|
-
height = p.estimatedItemHeight; // estimated
|
|
3240
|
-
|
|
3241
|
-
p.cachedItemEstimatedHeights[index - 1] = height;
|
|
3242
|
-
}
|
|
3243
|
-
|
|
3244
|
-
return height;
|
|
3245
|
-
}
|
|
3246
|
-
|
|
3247
|
-
/**
|
|
3248
|
-
* Retrieves the number of items that fit into the current viewport.
|
|
3249
|
-
* @returns {number}
|
|
3250
|
-
*/
|
|
3251
|
-
getVisibleItemCount() {
|
|
3252
|
-
const p = this._p,list = p.list;
|
|
3253
|
-
|
|
3254
|
-
let scrollTop = list.scrollTop;
|
|
3255
|
-
let visibleHeight = list.clientHeight;
|
|
3256
|
-
let firstVisibleIndex, lastVisibleIndex;
|
|
3257
|
-
|
|
3258
|
-
if (p.virtual) {
|
|
3259
|
-
firstVisibleIndex = binarySearchPosition(p.cachedItemPositions, scrollTop);
|
|
3260
|
-
lastVisibleIndex = binarySearchPosition(p.cachedItemPositions, scrollTop + visibleHeight, firstVisibleIndex);
|
|
3261
|
-
} else
|
|
3262
|
-
{
|
|
3263
|
-
const retriever = (i) => {
|
|
3264
|
-
let pos = this.getItemPosition(i);
|
|
3265
|
-
if (pos === undefined)
|
|
3266
|
-
pos = Infinity;
|
|
3267
|
-
return pos;
|
|
3268
|
-
};
|
|
3269
|
-
|
|
3270
|
-
firstVisibleIndex = binarySearchPositionByFn(p.count, retriever, scrollTop);
|
|
3271
|
-
lastVisibleIndex = binarySearchPositionByFn(p.count, retriever, scrollTop + visibleHeight, firstVisibleIndex);
|
|
3272
|
-
}
|
|
3273
|
-
|
|
3274
|
-
if (this.getItemPosition(lastVisibleIndex) === scrollTop + visibleHeight)
|
|
3275
|
-
lastVisibleIndex--;
|
|
3276
|
-
return lastVisibleIndex - firstVisibleIndex + 1;
|
|
3277
|
-
}
|
|
3278
|
-
|
|
3279
|
-
/**
|
|
3280
|
-
* Renders a temporary ghost item. Can be used for testings several aspects of a proposed element, i.e measurements.
|
|
3281
|
-
* @param {*} ghostIndex - the value to pass as the index for the renderer function
|
|
3282
|
-
* @param {boolean} append - whether to append the item element to the DOM
|
|
3283
|
-
* @param {function(itemEl: Element)} ghostTester - the function that will receive the element, called synchronously.
|
|
3284
|
-
*/
|
|
3285
|
-
createGhostItemElement(ghostIndex, append, ghostTester) {
|
|
3286
|
-
const p = this._p;
|
|
3287
|
-
|
|
3288
|
-
let itemEl = this._dequeueElementForIndex(null, ghostIndex, false, true);
|
|
3289
|
-
try {
|
|
3290
|
-
if (append) {
|
|
3291
|
-
p.currentItemsParent.appendChild(itemEl);
|
|
3292
|
-
}
|
|
3293
|
-
ghostTester(itemEl);
|
|
3294
|
-
} finally {
|
|
3295
|
-
if (append) {
|
|
3296
|
-
let parent = itemEl.parentNode;
|
|
3297
|
-
if (parent)
|
|
3298
|
-
parent.removeChild(itemEl);
|
|
3299
|
-
}
|
|
3300
|
-
if (p.onItemUnrender)
|
|
3301
|
-
p.onItemUnrender(itemEl);
|
|
3302
|
-
}
|
|
3303
|
-
}
|
|
3304
|
-
|
|
3305
|
-
/**
|
|
3306
|
-
* Reset the pointer to the current items wrapper
|
|
3307
|
-
* @private
|
|
3308
|
-
*/
|
|
3309
|
-
_resetCurrentItemsParent() {var _ref, _p$virtualWrapper;
|
|
3310
|
-
const p = this._p;
|
|
3311
|
-
p.currentItemsParent = (_ref = (_p$virtualWrapper = p.virtualWrapper) !== null && _p$virtualWrapper !== void 0 ? _p$virtualWrapper : p.userItemsParent) !== null && _ref !== void 0 ? _ref : p.list;
|
|
3312
|
-
}
|
|
3313
|
-
|
|
3314
|
-
/**
|
|
3315
|
-
* Destroy all created elements, for cleanup
|
|
3316
|
-
* @returns {VirtualListHelper}
|
|
3317
|
-
* @private
|
|
3318
|
-
*/
|
|
3319
|
-
_destroyElements() {
|
|
3320
|
-
const p = this._p;
|
|
3321
|
-
const onItemUnrender = p.onItemUnrender;
|
|
3322
|
-
const existingEls = p.existingEls;
|
|
3323
|
-
|
|
3324
|
-
for (let i = 0; i < existingEls.length; i++) {
|
|
3325
|
-
const el = existingEls[i];
|
|
3326
|
-
|
|
3327
|
-
let parent = el.parentNode;
|
|
3328
|
-
if (parent)
|
|
3329
|
-
parent.removeChild(el);
|
|
3330
|
-
if (onItemUnrender && el[ItemIndexSymbol] !== undefined)
|
|
3331
|
-
onItemUnrender(el);
|
|
3332
|
-
}
|
|
3333
|
-
|
|
3334
|
-
existingEls.length = 0;
|
|
3335
|
-
|
|
3336
|
-
if (p.virtualWrapper) {
|
|
3337
|
-
if (p.virtualWrapper !== p.userItemsParent) {
|
|
3338
|
-
if (p.virtualWrapper.parentNode) {
|
|
3339
|
-
p.virtualWrapper.parentNode.removeChild(p.virtualWrapper);
|
|
3340
|
-
}
|
|
3341
|
-
}
|
|
3342
|
-
p.virtualWrapper = null;
|
|
3343
|
-
this._resetCurrentItemsParent();
|
|
3344
|
-
}
|
|
3345
|
-
|
|
3346
|
-
return this;
|
|
3347
|
-
}
|
|
3348
|
-
|
|
3349
|
-
/**
|
|
3350
|
-
* Marks (an) item(s) at specific index(es) as to be re-rendered. <br />
|
|
3351
|
-
* Applicable for virtual mode only.
|
|
3352
|
-
* @param {number} index
|
|
3353
|
-
* @param {number} count
|
|
3354
|
-
* @private
|
|
3355
|
-
*/
|
|
3356
|
-
_invalidateItemIndexesAt(index, count) {
|
|
3357
|
-
const p = this._p;
|
|
3358
|
-
|
|
3359
|
-
this._setItemPositionsNeedsUpdate(index);
|
|
3360
|
-
|
|
3361
|
-
let existingEls = p.existingEls;
|
|
3362
|
-
let existingCount = existingEls.length;
|
|
3363
|
-
let existingRange = this._getExistingElsRange();
|
|
3364
|
-
|
|
3365
|
-
if (existingRange.firstValidArrayIndex === -1)
|
|
3366
|
-
return;
|
|
3367
|
-
|
|
3368
|
-
if (count === -1)
|
|
3369
|
-
count = existingEls.length;
|
|
3370
|
-
|
|
3371
|
-
// Clean
|
|
3372
|
-
if (index >= existingRange.firstIndex && index <= existingRange.lastIndex) {
|
|
3373
|
-
for (let i = existingRange.firstValidArrayIndex + index - existingRange.firstIndex,
|
|
3374
|
-
c = 0;
|
|
3375
|
-
i < existingCount && c < count;
|
|
3376
|
-
i++, c++)
|
|
3377
|
-
delete existingEls[i][ItemIndexSymbol];
|
|
3378
|
-
}
|
|
3379
|
-
}
|
|
3380
|
-
|
|
3381
|
-
/**
|
|
3382
|
-
* In/decrement the item-index marker for specific item(s). <br />
|
|
3383
|
-
* Used for inserting/removing items in the middle of the list, without re-rendering everything. <br />
|
|
3384
|
-
* Applicable for non-virtual mode only.
|
|
3385
|
-
* @param {number} index
|
|
3386
|
-
* @param {number} count
|
|
3387
|
-
* @private
|
|
3388
|
-
*/
|
|
3389
|
-
_pushItemIndexesAt(index, count) {
|
|
3390
|
-
const p = this._p;
|
|
3391
|
-
|
|
3392
|
-
let existingEls = p.existingEls;
|
|
3393
|
-
let existingCount = existingEls.length;
|
|
3394
|
-
let existingRange = this._getExistingElsRange();
|
|
3395
|
-
|
|
3396
|
-
if (existingRange.firstValidArrayIndex === -1)
|
|
3397
|
-
return;
|
|
3398
|
-
|
|
3399
|
-
// Clean
|
|
3400
|
-
if (index >= existingRange.firstIndex && index <= existingRange.lastIndex) {
|
|
3401
|
-
for (let i = existingRange.firstValidArrayIndex + index - existingRange.firstIndex;
|
|
3402
|
-
i < existingCount;
|
|
3403
|
-
i++)
|
|
3404
|
-
existingEls[i][ItemIndexSymbol] += count;
|
|
3405
|
-
}
|
|
3406
|
-
}
|
|
3407
|
-
|
|
3408
|
-
/**
|
|
3409
|
-
* Hook relevant events
|
|
3410
|
-
* @returns {VirtualListHelper}
|
|
3411
|
-
* @private
|
|
3412
|
-
*/
|
|
3413
|
-
_hookEvents() {
|
|
3414
|
-
const p = this._p;
|
|
3415
|
-
|
|
3416
|
-
this._unhookEvents();
|
|
3417
|
-
|
|
3418
|
-
if (p.virtual && p.hookScrollEvent) {
|
|
3419
|
-
p.list && p.list.addEventListener('scroll', /**@type Function*/p.boundRender);
|
|
3420
|
-
}
|
|
3421
|
-
|
|
3422
|
-
return this;
|
|
3423
|
-
}
|
|
3424
|
-
|
|
3425
|
-
/**
|
|
3426
|
-
* Unhook previously hooked events
|
|
3427
|
-
* @returns {VirtualListHelper}
|
|
3428
|
-
* @private
|
|
3429
|
-
*/
|
|
3430
|
-
_unhookEvents() {
|
|
3431
|
-
const p = this._p;
|
|
3432
|
-
|
|
3433
|
-
p.list && p.list.removeEventListener('scroll', /**@type Function*/p.boundRender);
|
|
3434
|
-
|
|
3435
|
-
return this;
|
|
3436
|
-
}
|
|
3437
|
-
|
|
3438
|
-
/**
|
|
3439
|
-
* Mark item index from which the positions are not considered valid anymore. <br />
|
|
3440
|
-
* Applicable for virtual mode only.
|
|
3441
|
-
* @param {number} value
|
|
3442
|
-
* @private
|
|
3443
|
-
*/
|
|
3444
|
-
_setItemPositionsNeedsUpdate(value) {
|
|
3445
|
-
const p = this._p;
|
|
3446
|
-
|
|
3447
|
-
if (value < p.itemPositionsNeedsUpdate) {
|
|
3448
|
-
p.itemPositionsNeedsUpdate = value;
|
|
3449
|
-
}
|
|
3450
|
-
}
|
|
3451
|
-
|
|
3452
|
-
/**
|
|
3453
|
-
* Calculates an item's top position (and stores in the private `cachedItemPositions` array). <br />
|
|
3454
|
-
* Allows calculating last+1 index too, to get the bottom-most position. <br />
|
|
3455
|
-
* Applicable for non-virtual mode only.
|
|
3456
|
-
* @param {number} index
|
|
3457
|
-
* @returns {number|undefined}
|
|
3458
|
-
* @private
|
|
3459
|
-
*/
|
|
3460
|
-
_calculateItemPosition(index) {
|
|
3461
|
-
const p = this._p;
|
|
3462
|
-
|
|
3463
|
-
const cachedItemPositions = p.cachedItemPositions;
|
|
3464
|
-
|
|
3465
|
-
if (index >= p.itemPositionsNeedsUpdate) {
|
|
3466
|
-
const count = p.count;
|
|
3467
|
-
const cachedItemHeights = p.cachedItemHeights;
|
|
3468
|
-
const cachedItemEstimatedHeights = p.cachedItemEstimatedHeights;
|
|
3469
|
-
const estimatedItemHeight = p.estimatedItemHeight;
|
|
3470
|
-
const itemHeightEstimatorFn = p.itemHeightEstimatorFn;
|
|
3471
|
-
|
|
3472
|
-
if (cachedItemHeights.length !== count) {
|
|
3473
|
-
cachedItemHeights.length = count;
|
|
3474
|
-
cachedItemEstimatedHeights.length = count;
|
|
3475
|
-
cachedItemPositions.length = count;
|
|
3476
|
-
}
|
|
3477
|
-
|
|
3478
|
-
let fromIndex = p.itemPositionsNeedsUpdate;
|
|
3479
|
-
let toIndex = Math.min(index, count);
|
|
3480
|
-
|
|
3481
|
-
let pos = 0;
|
|
3482
|
-
|
|
3483
|
-
if (fromIndex > 0) {
|
|
3484
|
-
pos = cachedItemPositions[fromIndex - 1];
|
|
3485
|
-
}
|
|
3486
|
-
|
|
3487
|
-
for (let i = fromIndex; i <= toIndex; i++) {
|
|
3488
|
-
if (i === 0) {
|
|
3489
|
-
cachedItemPositions[i] = pos;
|
|
3490
|
-
continue;
|
|
3491
|
-
}
|
|
3492
|
-
|
|
3493
|
-
const prevIndex = i - 1;
|
|
3494
|
-
|
|
3495
|
-
let height = cachedItemHeights[prevIndex]; // already calculated
|
|
3496
|
-
|
|
3497
|
-
if (height === undefined) {
|
|
3498
|
-
height = itemHeightEstimatorFn ? itemHeightEstimatorFn(prevIndex) : null; // estimated per item
|
|
3499
|
-
|
|
3500
|
-
if (typeof height !== 'number')
|
|
3501
|
-
height = estimatedItemHeight; // estimated
|
|
3502
|
-
|
|
3503
|
-
cachedItemEstimatedHeights[prevIndex] = height;
|
|
3504
|
-
}
|
|
3505
|
-
|
|
3506
|
-
pos += height;
|
|
3507
|
-
cachedItemPositions[i] = pos;
|
|
3508
|
-
}
|
|
3509
|
-
|
|
3510
|
-
p.itemPositionsNeedsUpdate = toIndex + 1;
|
|
3511
|
-
}
|
|
3512
|
-
|
|
3513
|
-
// item after the last (calculate full height)
|
|
3514
|
-
if (index > 0 && index === p.count) {
|
|
3515
|
-
let height = p.cachedItemHeights[index - 1]; // already calculated
|
|
3516
|
-
|
|
3517
|
-
if (height === undefined) {
|
|
3518
|
-
height = p.itemHeightEstimatorFn ? p.itemHeightEstimatorFn(index - 1) : null; // estimated per item
|
|
3519
|
-
|
|
3520
|
-
if (typeof height !== 'number')
|
|
3521
|
-
height = p.estimatedItemHeight; // estimated
|
|
3522
|
-
|
|
3523
|
-
p.cachedItemEstimatedHeights[index - 1] = height;
|
|
3524
|
-
}
|
|
3525
|
-
|
|
3526
|
-
return cachedItemPositions[index - 1] + height;
|
|
3527
|
-
}
|
|
3528
|
-
|
|
3529
|
-
return cachedItemPositions[index];
|
|
3530
|
-
}
|
|
3531
|
-
|
|
3532
|
-
/**
|
|
3533
|
-
* Create (or reuse an existing) element for an item at the specified index,
|
|
3534
|
-
* and insert physically at specified position. <br />
|
|
3535
|
-
* This will also update the element's position in the `existingEls` array.
|
|
3536
|
-
* @param {Element|undefined} itemEl
|
|
3537
|
-
* @param {number} index
|
|
3538
|
-
* @param {Node|boolean|undefined} insertBefore
|
|
3539
|
-
* @param {boolean|undefined} avoidDomReflow
|
|
3540
|
-
* @returns {Element}
|
|
3541
|
-
* @private
|
|
3542
|
-
*/
|
|
3543
|
-
_dequeueElementForIndex(itemEl, index, insertBefore, avoidDomReflow) {
|
|
3544
|
-
const p = this._p;
|
|
3545
|
-
const virtualWrapper = p.virtualWrapper;
|
|
3546
|
-
p.currentItemsParent;
|
|
3547
|
-
const existingEls = p.existingEls;
|
|
3548
|
-
const onItemRender = p.onItemRender;
|
|
3549
|
-
const onItemUnrender = p.onItemUnrender;
|
|
3550
|
-
const isNew = !itemEl;
|
|
3551
|
-
const shouldReRender = isNew || index !== itemEl[ItemIndexSymbol];
|
|
3552
|
-
|
|
3553
|
-
if (itemEl) {
|
|
3554
|
-
if (onItemUnrender && shouldReRender) {
|
|
3555
|
-
onItemUnrender(itemEl);
|
|
3556
|
-
}
|
|
3557
|
-
} else {
|
|
3558
|
-
itemEl = p.itemElementCreatorFn();
|
|
3559
|
-
|
|
3560
|
-
if (virtualWrapper && insertBefore !== false) {
|
|
3561
|
-
(/**@type ElementCSSInlineStyle*/itemEl).style.position = 'absolute';
|
|
3562
|
-
(/**@type ElementCSSInlineStyle*/itemEl).style.top = '0';
|
|
3563
|
-
(/**@type ElementCSSInlineStyle*/itemEl).style.left = '0';
|
|
3564
|
-
(/**@type ElementCSSInlineStyle*/itemEl).style.right = '0';
|
|
3565
|
-
}
|
|
3566
|
-
}
|
|
3567
|
-
|
|
3568
|
-
// Render only if it's a new item element
|
|
3569
|
-
// OR the index of the existing element is not the same of the index to render
|
|
3570
|
-
if (shouldReRender) {
|
|
3571
|
-
itemEl.innerHTML = ''; // Basic cleanup
|
|
3572
|
-
|
|
3573
|
-
if (onItemRender)
|
|
3574
|
-
onItemRender(itemEl, index);
|
|
3575
|
-
}
|
|
3576
|
-
|
|
3577
|
-
if (insertBefore !== false) {
|
|
3578
|
-
if (!(insertBefore instanceof Node))
|
|
3579
|
-
insertBefore = null;
|
|
3580
|
-
|
|
3581
|
-
// Remove from existing list
|
|
3582
|
-
if (!isNew) {
|
|
3583
|
-
let i = existingEls.indexOf(itemEl);
|
|
3584
|
-
if (i !== -1)
|
|
3585
|
-
existingEls.splice(i, 1);
|
|
3586
|
-
}
|
|
3587
|
-
|
|
3588
|
-
// Insert into existing list
|
|
3589
|
-
let beforeIndex = insertBefore ? existingEls.indexOf(/**@type Element*/insertBefore) : -1;
|
|
3590
|
-
if (beforeIndex === -1) {
|
|
3591
|
-
existingEls.push(itemEl);
|
|
3592
|
-
} else {
|
|
3593
|
-
existingEls.splice(beforeIndex, 0, itemEl);
|
|
3594
|
-
}
|
|
3595
|
-
|
|
3596
|
-
if (!avoidDomReflow) {
|
|
3597
|
-
this._insertItemAndFlow(itemEl, index, insertBefore);
|
|
3598
|
-
}
|
|
3599
|
-
}
|
|
3600
|
-
|
|
3601
|
-
// Add index metadata to item
|
|
3602
|
-
itemEl[ItemIndexSymbol] = index;
|
|
3603
|
-
|
|
3604
|
-
return itemEl;
|
|
3605
|
-
}
|
|
3606
|
-
|
|
3607
|
-
/**
|
|
3608
|
-
* Insert item element into the DOM, set it's flow in the DOM, and update the item's position. <br />
|
|
3609
|
-
* @param {Element|undefined} itemEl
|
|
3610
|
-
* @param {number} index
|
|
3611
|
-
* @param {Node|boolean|undefined} before
|
|
3612
|
-
* @private
|
|
3613
|
-
*/
|
|
3614
|
-
_insertItemAndFlow(itemEl, index, before) {
|
|
3615
|
-
const p = this._p;
|
|
3616
|
-
const virtualWrapper = p.virtualWrapper;
|
|
3617
|
-
const itemParent = p.currentItemsParent;
|
|
3618
|
-
|
|
3619
|
-
if (before !== false) {
|
|
3620
|
-
if (!(before instanceof Node))
|
|
3621
|
-
before = null;
|
|
3622
|
-
|
|
3623
|
-
// Insert into DOM
|
|
3624
|
-
if (itemEl.parentNode !== itemParent ||
|
|
3625
|
-
itemEl.nextSibling !== before) {
|
|
3626
|
-
insertBefore(itemEl, before, itemParent);
|
|
3627
|
-
}
|
|
3628
|
-
}
|
|
3629
|
-
|
|
3630
|
-
if (virtualWrapper) {
|
|
3631
|
-
// Calculate height
|
|
3632
|
-
let itemHeight = itemEl.getBoundingClientRect().height;
|
|
3633
|
-
|
|
3634
|
-
// Put calculated height into cache, and invalidate positions if it's different
|
|
3635
|
-
let cachedItemHeight = p.cachedItemHeights[index];
|
|
3636
|
-
if (cachedItemHeight !== itemHeight) {
|
|
3637
|
-
p.cachedItemHeights[index] = itemHeight;
|
|
3638
|
-
}
|
|
3639
|
-
|
|
3640
|
-
if (cachedItemHeight !== undefined && itemHeight !== cachedItemHeight ||
|
|
3641
|
-
cachedItemHeight === undefined && itemHeight !== p.cachedItemEstimatedHeights[index]) {
|
|
3642
|
-
this._setItemPositionsNeedsUpdate(index + 1);
|
|
3643
|
-
}
|
|
3644
|
-
|
|
3645
|
-
// Set item top position
|
|
3646
|
-
let pos = this._calculateItemPosition(index);
|
|
3647
|
-
const supportedTransform = getSupportedTransform();
|
|
3648
|
-
|
|
3649
|
-
if (supportedTransform === false) {
|
|
3650
|
-
(/**@type ElementCSSInlineStyle*/itemEl).style.top = `${pos}px`;
|
|
3651
|
-
} else {
|
|
3652
|
-
(/**@type ElementCSSInlineStyle*/itemEl).style[supportedTransform] = `translateY(${pos}px)`;
|
|
3653
|
-
}
|
|
3654
|
-
}
|
|
3655
|
-
}
|
|
3656
|
-
|
|
3657
|
-
/**
|
|
3658
|
-
* Fetches valid range of existingEls
|
|
3659
|
-
* @returns {{firstIndex: (*|number), firstValidArrayIndex: number, lastValidArrayIndex: number, lastIndex: (*|number)}}
|
|
3660
|
-
* @private
|
|
3661
|
-
*/
|
|
3662
|
-
_getExistingElsRange() {
|
|
3663
|
-
const p = this._p,existingEls = p.existingEls;
|
|
3664
|
-
|
|
3665
|
-
let firstValidArrayIndex = -1,lastValidArrayIndex = -1;
|
|
3666
|
-
|
|
3667
|
-
for (let i = 0, len = existingEls.length; i < len; i++) {
|
|
3668
|
-
if (false === hasOwnProperty$1.call(existingEls[i], ItemIndexSymbol))
|
|
3669
|
-
continue;
|
|
3670
|
-
firstValidArrayIndex = i;
|
|
3671
|
-
break;
|
|
3672
|
-
}
|
|
3673
|
-
|
|
3674
|
-
for (let i = existingEls.length - 1; i >= 0; i--) {
|
|
3675
|
-
if (false === hasOwnProperty$1.call(existingEls[i], ItemIndexSymbol))
|
|
3676
|
-
continue;
|
|
3677
|
-
lastValidArrayIndex = i;
|
|
3678
|
-
break;
|
|
3679
|
-
}
|
|
3680
|
-
|
|
3681
|
-
let firstIndex = firstValidArrayIndex !== -1 ? existingEls[firstValidArrayIndex][ItemIndexSymbol] : -1;
|
|
3682
|
-
let lastIndex = lastValidArrayIndex !== -1 ? existingEls[lastValidArrayIndex][ItemIndexSymbol] : -1;
|
|
3683
|
-
|
|
3684
|
-
return {
|
|
3685
|
-
firstValidArrayIndex: firstValidArrayIndex,
|
|
3686
|
-
lastValidArrayIndex: lastValidArrayIndex,
|
|
3687
|
-
firstIndex: firstIndex,
|
|
3688
|
-
lastIndex: lastIndex
|
|
3689
|
-
};
|
|
3690
|
-
}
|
|
3691
|
-
}
|
|
3692
|
-
|
|
3693
|
-
/** Marks the item index associated with an item element */
|
|
3694
|
-
const ItemIndexSymbol = Symbol('index');
|
|
3695
|
-
|
|
3696
|
-
/** Marks an element for reuse */
|
|
3697
|
-
const ReuseElSymbol = Symbol('reuse');
|
|
3698
|
-
|
|
3699
|
-
/**
|
|
3700
|
-
* The default element creator
|
|
3701
|
-
* @returns {HTMLLIElement}
|
|
3702
|
-
*/
|
|
3703
|
-
const defaultElementCreator = () => {
|
|
3704
|
-
return document.createElement('li');
|
|
3705
|
-
};
|
|
3706
|
-
|
|
3707
|
-
/**
|
|
3708
|
-
* Will look for the index in the `positions` array closest to the specified `pos` value (<= pos).
|
|
3709
|
-
* @param {number[]} positions
|
|
3710
|
-
* @param {number} pos
|
|
3711
|
-
* @param {number} [start=0]
|
|
3712
|
-
* @param {number} [end=-1]
|
|
3713
|
-
* @returns {number}
|
|
3714
|
-
*/
|
|
3715
|
-
const binarySearchPosition = (positions, pos, start = 0, end = -1) => {
|
|
3716
|
-
let total = positions.length;
|
|
3717
|
-
if (end < 0)
|
|
3718
|
-
end += total;
|
|
3719
|
-
if (end <= start) return end; // 0 or 1 length array
|
|
3720
|
-
|
|
3721
|
-
while (start <= end) {
|
|
3722
|
-
let mid = Math.floor(start + (end - start) / 2);
|
|
3723
|
-
let midPos = positions[mid];
|
|
3724
|
-
|
|
3725
|
-
if (midPos === pos || midPos <= pos && mid < total && positions[mid + 1] > pos) {
|
|
3726
|
-
while (mid > 0 && positions[mid - 1] === midPos) // avoid bugs on 0-height items
|
|
3727
|
-
mid--;
|
|
3728
|
-
|
|
3729
|
-
return mid;
|
|
3730
|
-
}
|
|
3731
|
-
|
|
3732
|
-
if (midPos < pos)
|
|
3733
|
-
start = mid + 1;else
|
|
3734
|
-
|
|
3735
|
-
end = mid - 1;
|
|
3736
|
-
}
|
|
3737
|
-
|
|
3738
|
-
return end === -1 ? 0 : total - 1;
|
|
3739
|
-
};
|
|
3740
|
-
|
|
3741
|
-
/**
|
|
3742
|
-
* Will look for the index in a virtual list of positions supplied by `total` and `fn`,
|
|
3743
|
-
* closest to the specified `pos` value (<= pos).
|
|
3744
|
-
* @param {number} total
|
|
3745
|
-
* @param {function(index: number):number} fn
|
|
3746
|
-
* @param {number} pos
|
|
3747
|
-
* @param {number} [start=0]
|
|
3748
|
-
* @param {number} [end=-1]
|
|
3749
|
-
* @returns {number}
|
|
3750
|
-
*/
|
|
3751
|
-
const binarySearchPositionByFn = (total, fn, pos, start = 0, end = -1) => {
|
|
3752
|
-
if (end < 0)
|
|
3753
|
-
end += total;
|
|
3754
|
-
if (end <= start) return end; // 0 or 1 length array
|
|
3755
|
-
|
|
3756
|
-
while (start <= end) {
|
|
3757
|
-
let mid = Math.floor(start + (end - start) / 2);
|
|
3758
|
-
let midPos = fn(mid);
|
|
3759
|
-
|
|
3760
|
-
if (midPos === pos || midPos <= pos && mid < total && fn(mid + 1) > pos) {
|
|
3761
|
-
while (mid > 0 && fn(mid - 1) === midPos) // avoid bugs on 0-height items
|
|
3762
|
-
mid--;
|
|
3763
|
-
|
|
3764
|
-
return mid;
|
|
3765
|
-
}
|
|
3766
|
-
|
|
3767
|
-
if (midPos < pos)
|
|
3768
|
-
start = mid + 1;else
|
|
3769
|
-
|
|
3770
|
-
end = mid - 1;
|
|
3771
|
-
}
|
|
3772
|
-
|
|
3773
|
-
return end === -1 ? 0 : fn(total - 1);
|
|
3774
|
-
};
|
|
3775
|
-
|
|
3776
|
-
/**
|
|
3777
|
-
* Finds the last item in the array for which `fn` returns a truthy value
|
|
3778
|
-
* @param {Array} array
|
|
3779
|
-
* @param {Function} fn
|
|
3780
|
-
* @returns {undefined|*}
|
|
3781
|
-
*/
|
|
3782
|
-
const findLast = (array, fn) => {
|
|
3783
|
-
for (let i = array.length - 1; i >= 0; i--) {
|
|
3784
|
-
if (fn(array[i])) {
|
|
3785
|
-
return array[i];
|
|
3786
|
-
}
|
|
3787
|
-
}
|
|
3788
|
-
return undefined;
|
|
3789
|
-
};
|
|
3790
|
-
|
|
3791
|
-
let _isTransformSupported = null;
|
|
3792
|
-
|
|
3793
|
-
const getSupportedTransform = () => {
|
|
3794
|
-
if (_isTransformSupported === null) {
|
|
3795
|
-
let prefixes = ['transform', 'WebkitTransform', 'MozTransform', 'OTransform', 'msTransform'];
|
|
3796
|
-
let div = document.createElement('div');
|
|
3797
|
-
_isTransformSupported = false;
|
|
3798
|
-
for (let item of prefixes) {
|
|
3799
|
-
if (div && div.style[item] !== undefined) {
|
|
3800
|
-
_isTransformSupported = item;
|
|
3801
|
-
break;
|
|
3802
|
-
}
|
|
3803
|
-
}
|
|
3804
|
-
}
|
|
3805
|
-
return _isTransformSupported;
|
|
3806
|
-
};
|
|
3807
|
-
|
|
3808
2351
|
function ByColumnFilter(row, args) {
|
|
3809
2352
|
|
|
3810
2353
|
let column = args.column;
|