Skip to content

Commit

Permalink
Use binary search in getVisibleElements()
Browse files Browse the repository at this point in the history
  • Loading branch information
fkaelberer committed Dec 31, 2014
1 parent 50fe7c8 commit 351909a
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 17 deletions.
37 changes: 37 additions & 0 deletions test/unit/ui_utils_spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
/* globals expect, it, describe, binarySearchFirstItem */

'use strict';

describe('ui_utils', function() {

describe('binary search', function() {
function isTrue(boolean) {
return boolean;
}
function isGreater3(number) {
return number > 3;
}

it('empty array', function() {
expect(binarySearchFirstItem([], isTrue)).toEqual(-1);
});
it('single boolean entry', function() {
expect(binarySearchFirstItem([false], isTrue)).toEqual(-1);
expect(binarySearchFirstItem([true], isTrue)).toEqual(0);
});
it('three boolean entries', function() {
expect(binarySearchFirstItem([true, true, true], isTrue)).toEqual(0);
expect(binarySearchFirstItem([false, true, true], isTrue)).toEqual(1);
expect(binarySearchFirstItem([false, false, true], isTrue)).toEqual(2);
expect(binarySearchFirstItem([false, false, false], isTrue)).toEqual(-1);
});
it('three numeric entries', function() {
expect(binarySearchFirstItem([0, 1, 2], isGreater3)).toEqual(-1);
expect(binarySearchFirstItem([2, 3, 4], isGreater3)).toEqual(2);
expect(binarySearchFirstItem([4, 5, 6], isGreater3)).toEqual(0);
});
});
});

2 changes: 2 additions & 0 deletions test/unit/unit_test.html
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
<script src="../../src/core/worker.js"></script>
<script src="../../src/display/metadata.js"></script>
<script src="../../src/core/jpg.js"></script>
<script src="../../web/ui_utils.js"></script>
<script>PDFJS.workerSrc = '../../src/worker_loader.js';</script>

<!-- include spec files here... -->
Expand All @@ -52,6 +53,7 @@
<script src="parser_spec.js"></script>
<script src="api_spec.js"></script>
<script src="metadata_spec.js"></script>
<script src="ui_utils_spec.js"></script>
<script src="util_spec.js"></script>
<script src="cmap_spec.js"></script>
<script>
Expand Down
77 changes: 60 additions & 17 deletions web/ui_utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -161,13 +161,10 @@ function watchScroll(viewAreaElement, callback) {

var currentY = viewAreaElement.scrollTop;
var lastY = state.lastY;
if (currentY > lastY) {
state.down = true;
} else if (currentY < lastY) {
state.down = false;
if (currentY !== lastY) {
state.down = currentY > lastY;
}
state.lastY = currentY;
// else do nothing and use previous value
callback(state);
});
};
Expand All @@ -183,37 +180,83 @@ function watchScroll(viewAreaElement, callback) {
return state;
}

/**
* Use binary search to find the index of the first item in a given array which
* passes a given condition. The items are expected to be sorted in the sense
* that if the condition is true for one item in the array, then it is also true
* for all following items.
*
* @returns {Number} Index of the first array element to pass the test,
* or -1 if no such element exists.
*/
function binarySearchFirstItem(items, condition) {
var minIndex = 0;
var maxIndex = items.length - 1;

if (items.length === 0 || !condition(items[maxIndex])) {
return -1;
}
if (condition(items[minIndex])) {
return minIndex;
}

while (minIndex < maxIndex) {
var currentIndex = (minIndex + maxIndex) >> 1;
var currentItem = items[currentIndex];
if (condition(currentItem)) {
maxIndex = currentIndex;
} else {
minIndex = currentIndex + 1;
}
}
return minIndex; /* === maxIndex */
}

/**
* Generic helper to find out what elements are visible within a scroll pane.
*/
function getVisibleElements(scrollEl, views, sortByVisibility) {
var top = scrollEl.scrollTop, bottom = top + scrollEl.clientHeight;
var left = scrollEl.scrollLeft, right = left + scrollEl.clientWidth;

var visible = [], view;
function isElemBottomBelowViewTop(view) {
var elem = view.el;
var elemBottom = elem.offsetTop + elem.clientTop + elem.clientHeight;
return elemBottom > top;
}

var visible = [], view, elem;
var currentHeight, viewHeight, hiddenHeight, percentHeight;
var currentWidth, viewWidth;
for (var i = 0, ii = views.length; i < ii; ++i) {
var firstVisibleElemInd = binarySearchFirstItem(views,
isElemBottomBelowViewTop);

for (var i = firstVisibleElemInd, ii = views.length; i < ii; i++) {
view = views[i];
currentHeight = view.el.offsetTop + view.el.clientTop;
viewHeight = view.el.clientHeight;
if ((currentHeight + viewHeight) < top) {
continue;
}
elem = view.el;
currentHeight = elem.offsetTop + elem.clientTop;
viewHeight = elem.clientHeight;

if (currentHeight > bottom) {
break;
}
currentWidth = view.el.offsetLeft + view.el.clientLeft;
viewWidth = view.el.clientWidth;
if ((currentWidth + viewWidth) < left || currentWidth > right) {

currentWidth = elem.offsetLeft + elem.clientLeft;
viewWidth = elem.clientWidth;
if (currentWidth + viewWidth < left || currentWidth > right) {
continue;
}
hiddenHeight = Math.max(0, top - currentHeight) +
Math.max(0, currentHeight + viewHeight - bottom);
percentHeight = ((viewHeight - hiddenHeight) * 100 / viewHeight) | 0;

visible.push({ id: view.id, x: currentWidth, y: currentHeight,
view: view, percent: percentHeight });
visible.push({
id: view.id,
x: currentWidth,
y: currentHeight,
view: view,
percent: percentHeight
});
}

var first = visible[0];
Expand Down

0 comments on commit 351909a

Please sign in to comment.