Files
awx/awx/ui/client/features/output/scroll.service.js
gconsidine 033314e4f6 Add fixes to results
- Handle out of order events by batching lines until all lines
  are present
  - In static mode, fetch pages of results until container is full
  and scroll bar appears (for scroll events related to pagination)
2018-04-05 02:46:59 -04:00

168 lines
4.0 KiB
JavaScript

const ELEMENT_CONTAINER = '.at-Stdout-container';
const ELEMENT_TBODY = '#atStdoutResultTable';
const DELAY = 100;
const THRESHOLD = 0.1;
function JobScrollService ($q, $timeout) {
this.init = (hooks) => {
this.el = $(ELEMENT_CONTAINER);
this.timer = null;
this.position = {
previous: 0,
current: 0
};
this.hooks = {
isAtRest: hooks.isAtRest,
next: hooks.next,
previous: hooks.previous
};
this.state = {
locked: false,
paused: false,
top: true
};
this.el.scroll(this.listen);
};
this.listen = () => {
if (this.isPaused()) {
return;
}
if (this.timer) {
$timeout.cancel(this.timer);
}
this.timer = $timeout(this.register, DELAY);
};
this.register = () => {
this.pause();
const current = this.getScrollPosition();
const downward = current > this.position.previous;
let promise;
if (downward && this.isBeyondThreshold(downward, current)) {
promise = this.hooks.next;
} else if (!downward && this.isBeyondThreshold(downward, current)) {
promise = this.hooks.previous;
}
if (!promise) {
this.setScrollPosition(current);
this.isAtRest();
this.resume();
return $q.resolve();
}
return promise()
.then(() => {
this.setScrollPosition(this.getScrollPosition());
this.isAtRest();
this.resume();
});
};
this.isBeyondThreshold = (downward, current) => {
const height = this.getScrollHeight();
if (downward) {
current += this.getViewableHeight();
if (current >= height || ((height - current) / height) < THRESHOLD) {
return true;
}
} else if (current <= 0 || (current / height) < THRESHOLD) {
return true;
}
return false;
};
this.pageUp = () => {
if (this.isPaused()) {
return;
}
const top = this.getScrollPosition();
const height = this.getViewableHeight();
this.setScrollPosition(top - height);
};
this.pageDown = () => {
if (this.isPaused()) {
return;
}
const top = this.getScrollPosition();
const height = this.getViewableHeight();
this.setScrollPosition(top + height);
};
this.getScrollHeight = () => this.el[0].scrollHeight;
this.getViewableHeight = () => this.el[0].offsetHeight;
this.getScrollPosition = () => this.el[0].scrollTop;
this.setScrollPosition = position => {
this.position.previous = this.position.current;
this.position.current = position;
this.el[0].scrollTop = position;
this.isAtRest();
};
this.resetScrollPosition = () => {
this.position.previous = 0;
this.position.current = 0;
this.el[0].scrollTop = 0;
this.isAtRest();
};
this.scrollToBottom = () => {
this.setScrollPosition(this.getScrollHeight());
};
this.isAtRest = () => {
if (this.position.current === 0 && !this.state.top) {
this.state.top = true;
this.hooks.isAtRest(true);
} else if (this.position.current > 0 && this.state.top) {
this.state.top = false;
this.hooks.isAtRest(false);
}
};
this.resume = () => {
this.state.paused = false;
};
this.pause = () => {
this.state.paused = true;
};
this.isPaused = () => this.state.paused;
this.lock = () => {
this.state.locked = true;
};
this.unlock = () => {
this.state.locked = false;
};
this.isLocked = () => this.state.locked;
this.isMissing = () => $(ELEMENT_TBODY)[0].clientHeight < this.getViewableHeight();
}
JobScrollService.$inject = ['$q', '$timeout'];
export default JobScrollService;