OLD | NEW |
(Empty) | |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 /** |
| 6 * @constructor |
| 7 * @param {!WebInspector.StaticViewportControl.Provider} provider |
| 8 */ |
| 9 WebInspector.StaticViewportControl = function(provider) |
| 10 { |
| 11 this.element = createElement("div"); |
| 12 this.element.style.overflow = "auto"; |
| 13 this._innerElement = this.element.createChild("div"); |
| 14 this._innerElement.style.height = "0px"; |
| 15 this._innerElement.style.position = "relative"; |
| 16 this._innerElement.style.overflow = "hidden"; |
| 17 |
| 18 this._provider = provider; |
| 19 this.element.addEventListener("scroll", this._update.bind(this), false); |
| 20 this._itemCount = 0; |
| 21 this._indexSymbol = Symbol("WebInspector.StaticViewportControl._indexSymbol"
); |
| 22 } |
| 23 |
| 24 WebInspector.StaticViewportControl.prototype = { |
| 25 refresh: function() |
| 26 { |
| 27 this._itemCount = this._provider.itemCount(); |
| 28 this._innerElement.removeChildren(); |
| 29 |
| 30 var height = 0; |
| 31 this._cumulativeHeights = new Int32Array(this._itemCount); |
| 32 for (var i = 0; i < this._itemCount; ++i) { |
| 33 height += this._provider.fastItemHeight(i); |
| 34 this._cumulativeHeights[i] = height; |
| 35 } |
| 36 this._innerElement.style.height = height + "px"; |
| 37 |
| 38 this._update(); |
| 39 }, |
| 40 |
| 41 _update: function() |
| 42 { |
| 43 if (!this._cumulativeHeights) { |
| 44 this.refresh(); |
| 45 return; |
| 46 } |
| 47 |
| 48 var visibleHeight = this._visibleHeight(); |
| 49 var visibleFrom = this.element.scrollTop; |
| 50 var activeHeight = visibleHeight * 2; |
| 51 var firstActiveIndex = Math.max(Array.prototype.lowerBound.call(this._cu
mulativeHeights, visibleFrom + 1 - (activeHeight - visibleHeight) / 2), 0); |
| 52 var lastActiveIndex = Math.min(Array.prototype.lowerBound.call(this._cum
ulativeHeights, visibleFrom + visibleHeight + (activeHeight - visibleHeight) / 2
), this._itemCount - 1); |
| 53 |
| 54 var children = this._innerElement.children; |
| 55 for (var i = children.length - 1; i >= 0; --i) { |
| 56 var element = children[i]; |
| 57 if (element[this._indexSymbol] < firstActiveIndex || element[this._i
ndexSymbol] > lastActiveIndex) |
| 58 element.remove(); |
| 59 } |
| 60 |
| 61 for (var i = firstActiveIndex; i <= lastActiveIndex; ++i) |
| 62 this._insertElement(i); |
| 63 }, |
| 64 |
| 65 /** |
| 66 * @param {number} index |
| 67 */ |
| 68 _insertElement: function(index) |
| 69 { |
| 70 var element = this._provider.itemElement(index); |
| 71 if (!element || element.parentElement === this._innerElement) |
| 72 return; |
| 73 |
| 74 element.style.position = "absolute"; |
| 75 element.style.top = (this._cumulativeHeights[index - 1] || 0) + "px"; |
| 76 element.style.left = "0"; |
| 77 element.style.right = "0"; |
| 78 element[this._indexSymbol] = index; |
| 79 this._innerElement.appendChild(element); |
| 80 }, |
| 81 |
| 82 /** |
| 83 * @return {number} |
| 84 */ |
| 85 firstVisibleIndex: function() |
| 86 { |
| 87 return Math.max(Array.prototype.lowerBound.call(this._cumulativeHeights,
this.element.scrollTop + 1), 0); |
| 88 }, |
| 89 |
| 90 /** |
| 91 * @return {number} |
| 92 */ |
| 93 lastVisibleIndex: function() |
| 94 { |
| 95 return Math.min(Array.prototype.lowerBound.call(this._cumulativeHeights,
this.element.scrollTop + this._visibleHeight()), this._itemCount); |
| 96 }, |
| 97 |
| 98 /** |
| 99 * @param {number} index |
| 100 * @param {boolean=} makeLast |
| 101 */ |
| 102 scrollItemIntoView: function(index, makeLast) |
| 103 { |
| 104 var firstVisibleIndex = this.firstVisibleIndex(); |
| 105 var lastVisibleIndex = this.lastVisibleIndex(); |
| 106 if (index > firstVisibleIndex && index < lastVisibleIndex) |
| 107 return; |
| 108 if (makeLast) |
| 109 this.forceScrollItemToBeLast(index); |
| 110 else if (index <= firstVisibleIndex) |
| 111 this.forceScrollItemToBeFirst(index); |
| 112 else if (index >= lastVisibleIndex) |
| 113 this.forceScrollItemToBeLast(index); |
| 114 }, |
| 115 |
| 116 /** |
| 117 * @param {number} index |
| 118 */ |
| 119 forceScrollItemToBeFirst: function(index) |
| 120 { |
| 121 this.element.scrollTop = index > 0 ? this._cumulativeHeights[index - 1]
: 0; |
| 122 this._update(); |
| 123 }, |
| 124 |
| 125 /** |
| 126 * @param {number} index |
| 127 */ |
| 128 forceScrollItemToBeLast: function(index) |
| 129 { |
| 130 this.element.scrollTop = this._cumulativeHeights[index] - this._visibleH
eight(); |
| 131 this._update(); |
| 132 }, |
| 133 |
| 134 /** |
| 135 * @return {number} |
| 136 */ |
| 137 _visibleHeight: function() |
| 138 { |
| 139 return this.element.offsetHeight; |
| 140 } |
| 141 } |
| 142 |
| 143 /** |
| 144 * @interface |
| 145 */ |
| 146 WebInspector.StaticViewportControl.Provider = function() |
| 147 { |
| 148 } |
| 149 |
| 150 WebInspector.StaticViewportControl.Provider.prototype = { |
| 151 /** |
| 152 * @param {number} index |
| 153 * @return {number} |
| 154 */ |
| 155 fastItemHeight: function(index) { return 0; }, |
| 156 |
| 157 /** |
| 158 * @return {number} |
| 159 */ |
| 160 itemCount: function() { return 0; }, |
| 161 |
| 162 /** |
| 163 * @param {number} index |
| 164 * @return {?Element} |
| 165 */ |
| 166 itemElement: function(index) { return null; } |
| 167 } |
OLD | NEW |