mirror of
https://github.com/ansible/awx.git
synced 2026-01-12 10:30:03 -03:30
moving render/record keeping and scroll functionality out of pagers
This commit is contained in:
parent
ee348b7169
commit
138f8a45ae
@ -17,12 +17,12 @@ let scroll;
|
||||
let status;
|
||||
let slide;
|
||||
let stream;
|
||||
let page;
|
||||
|
||||
let vm;
|
||||
|
||||
const listeners = [];
|
||||
let lockFrames = false;
|
||||
|
||||
let lockFrames;
|
||||
function onFrames (events) {
|
||||
events = slide.pushFrames(events);
|
||||
|
||||
@ -30,7 +30,7 @@ function onFrames (events) {
|
||||
return $q.resolve();
|
||||
}
|
||||
|
||||
const popCount = events.length - slide.getCapacity();
|
||||
const popCount = events.length - render.getCapacity();
|
||||
const isAttached = events.length > 0;
|
||||
|
||||
if (!isAttached) {
|
||||
@ -52,13 +52,13 @@ function onFrames (events) {
|
||||
scroll.scrollToBottom();
|
||||
}
|
||||
|
||||
return slide.popBack(popCount)
|
||||
return render.popBack(popCount)
|
||||
.then(() => {
|
||||
if (vm.isFollowing) {
|
||||
scroll.scrollToBottom();
|
||||
}
|
||||
|
||||
return slide.pushFront(events);
|
||||
return render.pushFrames(events);
|
||||
})
|
||||
.then(() => {
|
||||
if (vm.isFollowing) {
|
||||
@ -71,7 +71,11 @@ function onFrames (events) {
|
||||
});
|
||||
}
|
||||
|
||||
function first () {
|
||||
//
|
||||
// Menu Controls (Running)
|
||||
//
|
||||
|
||||
function firstRange () {
|
||||
if (scroll.isPaused()) {
|
||||
return $q.resolve();
|
||||
}
|
||||
@ -81,9 +85,15 @@ function first () {
|
||||
|
||||
stopFollowing();
|
||||
|
||||
return slide.getFirst()
|
||||
.then(() => {
|
||||
scroll.resetScrollPosition();
|
||||
return render.clear()
|
||||
.then(() => slide.getFirst())
|
||||
.then(results => render.pushFront(results))
|
||||
.then(() => slide.getNext())
|
||||
.then(results => {
|
||||
const popCount = results.length - render.getCapacity();
|
||||
|
||||
return render.popBack(popCount)
|
||||
.then(() => render.pushFront(results));
|
||||
})
|
||||
.finally(() => {
|
||||
scroll.resume();
|
||||
@ -91,7 +101,7 @@ function first () {
|
||||
});
|
||||
}
|
||||
|
||||
function next () {
|
||||
function nextRange () {
|
||||
if (vm.isFollowing) {
|
||||
scroll.scrollToBottom();
|
||||
|
||||
@ -110,26 +120,43 @@ function next () {
|
||||
lockFrames = true;
|
||||
|
||||
return slide.getNext()
|
||||
.then(results => {
|
||||
const popCount = results.length - render.getCapacity();
|
||||
|
||||
return render.popBack(popCount)
|
||||
.then(() => render.pushFront(results));
|
||||
})
|
||||
.finally(() => {
|
||||
scroll.resume();
|
||||
lockFrames = false;
|
||||
});
|
||||
}
|
||||
|
||||
function previous () {
|
||||
function previousRange () {
|
||||
if (scroll.isPaused()) {
|
||||
return $q.resolve();
|
||||
}
|
||||
|
||||
scroll.pause();
|
||||
lockFrames = true;
|
||||
|
||||
stopFollowing();
|
||||
|
||||
const initialPosition = scroll.getScrollPosition();
|
||||
let initialPosition;
|
||||
let popHeight;
|
||||
|
||||
return slide.getPrevious()
|
||||
.then(popHeight => {
|
||||
.then(results => {
|
||||
const popCount = results.length - render.getCapacity();
|
||||
initialPosition = scroll.getScrollPosition();
|
||||
|
||||
return render.popFront(popCount)
|
||||
.then(() => {
|
||||
popHeight = scroll.getScrollHeight();
|
||||
|
||||
return render.pushBack(results);
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
const currentHeight = scroll.getScrollHeight();
|
||||
scroll.setScrollPosition(currentHeight - popHeight + initialPosition);
|
||||
|
||||
@ -138,10 +165,12 @@ function previous () {
|
||||
.finally(() => {
|
||||
scroll.resume();
|
||||
lockFrames = false;
|
||||
|
||||
return $q.resolve();
|
||||
});
|
||||
}
|
||||
|
||||
function last () {
|
||||
function lastRange () {
|
||||
if (scroll.isPaused()) {
|
||||
return $q.resolve();
|
||||
}
|
||||
@ -149,9 +178,10 @@ function last () {
|
||||
scroll.pause();
|
||||
lockFrames = true;
|
||||
|
||||
return slide.getLast()
|
||||
return render.clear()
|
||||
.then(() => slide.getLast())
|
||||
.then(results => render.pushFront(results))
|
||||
.then(() => {
|
||||
stream.setMissingCounterThreshold(slide.getTailCounter() + 1);
|
||||
scroll.scrollToBottom();
|
||||
|
||||
return $q.resolve();
|
||||
@ -159,9 +189,30 @@ function last () {
|
||||
.finally(() => {
|
||||
scroll.resume();
|
||||
lockFrames = false;
|
||||
|
||||
return $q.resolve();
|
||||
});
|
||||
}
|
||||
|
||||
function menuLastRange () {
|
||||
if (vm.isFollowing) {
|
||||
lockFollow = true;
|
||||
stopFollowing();
|
||||
|
||||
return $q.resolve();
|
||||
}
|
||||
|
||||
lockFollow = false;
|
||||
|
||||
if (slide.isOnLastPage()) {
|
||||
scroll.scrollToBottom();
|
||||
|
||||
return $q.resolve();
|
||||
}
|
||||
|
||||
return last();
|
||||
}
|
||||
|
||||
let followOnce;
|
||||
let lockFollow;
|
||||
function canStartFollowing () {
|
||||
@ -207,23 +258,159 @@ function stopFollowing () {
|
||||
vm.followTooltip = vm.strings.get('tooltips.MENU_LAST');
|
||||
}
|
||||
|
||||
//
|
||||
// Menu Controls (Page Mode)
|
||||
//
|
||||
|
||||
function firstPage () {
|
||||
if (scroll.isPaused()) {
|
||||
return $q.resolve();
|
||||
}
|
||||
|
||||
scroll.pause();
|
||||
|
||||
return render.clear()
|
||||
.then(() => page.getFirst())
|
||||
.then(results => render.pushFront(results))
|
||||
.then(() => page.getNext())
|
||||
.then(results => {
|
||||
const popCount = page.trimHead();
|
||||
|
||||
return render.popBack(popCount)
|
||||
.then(() => render.pushFront(results));
|
||||
})
|
||||
.finally(() => {
|
||||
scroll.resume();
|
||||
|
||||
return $q.resolve();
|
||||
});
|
||||
}
|
||||
|
||||
function lastPage () {
|
||||
if (scroll.isPaused()) {
|
||||
return $q.resolve();
|
||||
}
|
||||
|
||||
scroll.pause();
|
||||
|
||||
return render.clear()
|
||||
.then(() => page.getLast())
|
||||
.then(results => render.pushBack(results))
|
||||
.then(() => page.getPrevious())
|
||||
.then(results => {
|
||||
const popCount = page.trimTail();
|
||||
|
||||
return render.popFront(popCount)
|
||||
.then(() => render.pushBack(results));
|
||||
})
|
||||
.then(() => {
|
||||
scroll.scrollToBottom();
|
||||
|
||||
return $q.resolve();
|
||||
})
|
||||
.finally(() => {
|
||||
scroll.resume();
|
||||
|
||||
return $q.resolve();
|
||||
});
|
||||
}
|
||||
|
||||
function nextPage () {
|
||||
if (scroll.isPaused()) {
|
||||
return $q.resolve();
|
||||
}
|
||||
|
||||
scroll.pause();
|
||||
|
||||
return page.getNext()
|
||||
.then(results => {
|
||||
const popCount = page.trimHead();
|
||||
|
||||
return render.popBack(popCount)
|
||||
.then(() => render.pushFront(results));
|
||||
})
|
||||
.finally(() => {
|
||||
scroll.resume();
|
||||
});
|
||||
}
|
||||
|
||||
function previousPage () {
|
||||
if (scroll.isPaused()) {
|
||||
return $q.resolve();
|
||||
}
|
||||
|
||||
scroll.pause();
|
||||
|
||||
let initialPosition;
|
||||
let popHeight;
|
||||
|
||||
return page.getPrevious()
|
||||
.then(results => {
|
||||
const popCount = page.trimTail();
|
||||
initialPosition = scroll.getScrollPosition();
|
||||
|
||||
return render.popFront(popCount)
|
||||
.then(() => {
|
||||
popHeight = scroll.getScrollHeight();
|
||||
|
||||
return render.pushBack(results);
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
const currentHeight = scroll.getScrollHeight();
|
||||
scroll.setScrollPosition(currentHeight - popHeight + initialPosition);
|
||||
|
||||
return $q.resolve();
|
||||
})
|
||||
.finally(() => {
|
||||
scroll.resume();
|
||||
|
||||
return $q.resolve();
|
||||
});
|
||||
}
|
||||
|
||||
//
|
||||
// Menu Controls
|
||||
//
|
||||
|
||||
function first () {
|
||||
if (vm.isProcessingFinished) {
|
||||
return firstPage();
|
||||
}
|
||||
|
||||
return firstRange();
|
||||
}
|
||||
|
||||
function last () {
|
||||
if (vm.isProcessingFinished) {
|
||||
return lastPage();
|
||||
}
|
||||
|
||||
return lastRange();
|
||||
}
|
||||
|
||||
function next () {
|
||||
if (vm.isProcessingFinished) {
|
||||
return nextPage();
|
||||
}
|
||||
|
||||
return nextRange();
|
||||
}
|
||||
|
||||
function previous () {
|
||||
if (vm.isProcessingFinished) {
|
||||
return previousPage();
|
||||
}
|
||||
|
||||
return previousRange();
|
||||
}
|
||||
|
||||
function menuLast () {
|
||||
if (vm.isFollowing) {
|
||||
lockFollow = true;
|
||||
stopFollowing();
|
||||
|
||||
return $q.resolve();
|
||||
if (vm.isProcessingFinished) {
|
||||
return lastPage();
|
||||
}
|
||||
|
||||
lockFollow = false;
|
||||
|
||||
if (slide.isOnLastPage()) {
|
||||
scroll.scrollToBottom();
|
||||
|
||||
return $q.resolve();
|
||||
}
|
||||
|
||||
return last();
|
||||
return menuLastRange();
|
||||
}
|
||||
|
||||
function down () {
|
||||
@ -238,6 +425,10 @@ function togglePanelExpand () {
|
||||
vm.isPanelExpanded = !vm.isPanelExpanded;
|
||||
}
|
||||
|
||||
//
|
||||
// Line Interaction
|
||||
//
|
||||
|
||||
const iconCollapsed = 'fa-angle-right';
|
||||
const iconExpanded = 'fa-angle-down';
|
||||
const iconSelector = '.at-Stdout-toggle > i';
|
||||
@ -246,7 +437,7 @@ const lineCollapsed = 'hidden';
|
||||
function toggleCollapseAll () {
|
||||
if (scroll.isPaused()) return;
|
||||
|
||||
const records = Object.keys(render.record).map(key => render.record[key]);
|
||||
const records = Object.keys(render.records).map(key => render.records[key]);
|
||||
const plays = records.filter(({ name }) => name === EVENT_START_PLAY);
|
||||
const tasks = records.filter(({ name }) => name === EVENT_START_TASK);
|
||||
|
||||
@ -286,7 +477,7 @@ function toggleCollapseAll () {
|
||||
function toggleCollapse (uuid) {
|
||||
if (scroll.isPaused()) return;
|
||||
|
||||
const record = render.record[uuid];
|
||||
const record = render.records[uuid];
|
||||
|
||||
if (record.name === EVENT_START_PLAY) {
|
||||
togglePlayCollapse(uuid);
|
||||
@ -298,7 +489,7 @@ function toggleCollapse (uuid) {
|
||||
}
|
||||
|
||||
function togglePlayCollapse (uuid) {
|
||||
const record = render.record[uuid];
|
||||
const record = render.records[uuid];
|
||||
const descendants = record.children || [];
|
||||
|
||||
const icon = $(`#${uuid} ${iconSelector}`);
|
||||
@ -329,11 +520,11 @@ function togglePlayCollapse (uuid) {
|
||||
}
|
||||
|
||||
descendants
|
||||
.map(item => render.record[item])
|
||||
.map(item => render.records[item])
|
||||
.filter(({ name }) => name === EVENT_START_TASK)
|
||||
.forEach(rec => { render.record[rec.uuid].isCollapsed = true; });
|
||||
.forEach(rec => { render.records[rec.uuid].isCollapsed = true; });
|
||||
|
||||
render.record[uuid].isCollapsed = !isCollapsed;
|
||||
render.records[uuid].isCollapsed = !isCollapsed;
|
||||
}
|
||||
|
||||
function toggleTaskCollapse (uuid) {
|
||||
@ -352,7 +543,7 @@ function toggleTaskCollapse (uuid) {
|
||||
lines.addClass(lineCollapsed);
|
||||
}
|
||||
|
||||
render.record[uuid].isCollapsed = !isCollapsed;
|
||||
render.records[uuid].isCollapsed = !isCollapsed;
|
||||
}
|
||||
|
||||
function compile (html) {
|
||||
@ -363,6 +554,14 @@ function showHostDetails (id, uuid) {
|
||||
$state.go('output.host-event.json', { eventId: id, taskUuid: uuid });
|
||||
}
|
||||
|
||||
function showMissingEvents (uuid) {
|
||||
console.log(`expandMissingEvents: ${uuid}`);
|
||||
}
|
||||
|
||||
//
|
||||
// Event Handling
|
||||
//
|
||||
|
||||
let streaming;
|
||||
function stopListening () {
|
||||
streaming = null;
|
||||
@ -433,12 +632,20 @@ function handleSummaryEvent (data) {
|
||||
stream.setFinalCounter(data.final_counter);
|
||||
}
|
||||
|
||||
//
|
||||
// Search
|
||||
//
|
||||
|
||||
function reloadState (params) {
|
||||
params.isPanelExpanded = vm.isPanelExpanded;
|
||||
|
||||
return $state.transitionTo($state.current, params, { inherit: false, location: 'replace' });
|
||||
}
|
||||
|
||||
//
|
||||
// Debug Mode
|
||||
//
|
||||
|
||||
function clear () {
|
||||
stopListening();
|
||||
render.clear();
|
||||
@ -449,7 +656,7 @@ function clear () {
|
||||
|
||||
stream.bufferInit();
|
||||
status.init(resource);
|
||||
slide.init(render, resource.events, scroll);
|
||||
slide.init(resource.events, render);
|
||||
status.subscribe(data => { vm.status = data.status; });
|
||||
|
||||
startListening();
|
||||
@ -484,7 +691,8 @@ function OutputIndexController (
|
||||
render = _render_;
|
||||
status = _status_;
|
||||
stream = _stream_;
|
||||
slide = isProcessingFinished ? _page_ : _slide_;
|
||||
slide = _slide_;
|
||||
page = _page_;
|
||||
|
||||
vm = this || {};
|
||||
|
||||
@ -495,6 +703,7 @@ function OutputIndexController (
|
||||
vm.resource = resource;
|
||||
vm.reloadState = reloadState;
|
||||
vm.isPanelExpanded = isPanelExpanded;
|
||||
vm.isProcessingFinished = isProcessingFinished;
|
||||
vm.togglePanelExpand = togglePanelExpand;
|
||||
|
||||
// Stdout Navigation
|
||||
@ -504,15 +713,18 @@ function OutputIndexController (
|
||||
vm.toggleCollapseAll = toggleCollapseAll;
|
||||
vm.toggleCollapse = toggleCollapse;
|
||||
vm.showHostDetails = showHostDetails;
|
||||
vm.showMissingEvents = showMissingEvents;
|
||||
vm.toggleLineEnabled = resource.model.get('type') === 'job';
|
||||
vm.followTooltip = vm.strings.get('tooltips.MENU_LAST');
|
||||
vm.debug = _debug;
|
||||
|
||||
render.requestAnimationFrame(() => {
|
||||
status.init(resource);
|
||||
slide.init(render, resource.events, scroll);
|
||||
render.init({ compile, toggles: vm.toggleLineEnabled });
|
||||
|
||||
status.init(resource);
|
||||
page.init(resource.events);
|
||||
slide.init(resource.events, render);
|
||||
|
||||
scroll.init({
|
||||
next,
|
||||
previous,
|
||||
@ -600,4 +812,3 @@ OutputIndexController.$inject = [
|
||||
];
|
||||
|
||||
module.exports = OutputIndexController;
|
||||
|
||||
|
||||
@ -2,244 +2,153 @@
|
||||
import { OUTPUT_PAGE_LIMIT } from './constants';
|
||||
|
||||
function PageService ($q) {
|
||||
this.init = (storage, api, { getScrollHeight }) => {
|
||||
const { prepend, append, shift, pop, deleteRecord } = storage;
|
||||
const { getPage, getFirst, getLast, getLastPageNumber, getMaxCounter } = api;
|
||||
|
||||
this.init = ({ getPage, getFirst, getLast, getLastPageNumber }) => {
|
||||
this.api = {
|
||||
getPage,
|
||||
getFirst,
|
||||
getLast,
|
||||
getLastPageNumber,
|
||||
getMaxCounter,
|
||||
};
|
||||
|
||||
this.storage = {
|
||||
prepend,
|
||||
append,
|
||||
shift,
|
||||
pop,
|
||||
deleteRecord,
|
||||
};
|
||||
|
||||
this.hooks = {
|
||||
getScrollHeight,
|
||||
};
|
||||
|
||||
this.records = {};
|
||||
this.uuids = {};
|
||||
|
||||
this.state = {
|
||||
head: 0,
|
||||
tail: 0,
|
||||
};
|
||||
|
||||
this.chain = $q.resolve();
|
||||
};
|
||||
|
||||
this.pushFront = (results, key) => {
|
||||
if (!results) {
|
||||
return $q.resolve();
|
||||
}
|
||||
|
||||
return this.storage.append(results)
|
||||
.then(() => {
|
||||
const tail = key || ++this.state.tail;
|
||||
|
||||
this.records[tail] = {};
|
||||
results.forEach(({ counter, start_line, end_line, uuid }) => {
|
||||
this.records[tail][counter] = { start_line, end_line };
|
||||
this.uuids[counter] = uuid;
|
||||
});
|
||||
|
||||
return $q.resolve();
|
||||
});
|
||||
};
|
||||
|
||||
this.pushBack = (results, key) => {
|
||||
if (!results) {
|
||||
return $q.resolve();
|
||||
}
|
||||
|
||||
return this.storage.prepend(results)
|
||||
.then(() => {
|
||||
const head = key || --this.state.head;
|
||||
|
||||
this.records[head] = {};
|
||||
results.forEach(({ counter, start_line, end_line, uuid }) => {
|
||||
this.records[head][counter] = { start_line, end_line };
|
||||
this.uuids[counter] = uuid;
|
||||
});
|
||||
|
||||
return $q.resolve();
|
||||
});
|
||||
};
|
||||
|
||||
this.popBack = () => {
|
||||
if (this.getRecordCount() === 0) {
|
||||
return $q.resolve();
|
||||
}
|
||||
|
||||
const pageRecord = this.records[this.state.head] || {};
|
||||
|
||||
let lines = 0;
|
||||
const counters = [];
|
||||
|
||||
Object.keys(pageRecord)
|
||||
.forEach(counter => {
|
||||
lines += pageRecord[counter].end_line - pageRecord[counter].start_line;
|
||||
counters.push(counter);
|
||||
});
|
||||
|
||||
return this.storage.shift(lines)
|
||||
.then(() => {
|
||||
counters.forEach(counter => {
|
||||
this.storage.deleteRecord(this.uuids[counter]);
|
||||
delete this.uuids[counter];
|
||||
});
|
||||
|
||||
delete this.records[this.state.head++];
|
||||
|
||||
return $q.resolve();
|
||||
});
|
||||
};
|
||||
|
||||
this.popFront = () => {
|
||||
if (this.getRecordCount() === 0) {
|
||||
return $q.resolve();
|
||||
}
|
||||
|
||||
const pageRecord = this.records[this.state.tail] || {};
|
||||
|
||||
let lines = 0;
|
||||
const counters = [];
|
||||
|
||||
Object.keys(pageRecord)
|
||||
.forEach(counter => {
|
||||
lines += pageRecord[counter].end_line - pageRecord[counter].start_line;
|
||||
counters.push(counter);
|
||||
});
|
||||
|
||||
return this.storage.pop(lines)
|
||||
.then(() => {
|
||||
counters.forEach(counter => {
|
||||
this.storage.deleteRecord(this.uuids[counter]);
|
||||
delete this.uuids[counter];
|
||||
});
|
||||
|
||||
delete this.records[this.state.tail--];
|
||||
|
||||
return $q.resolve();
|
||||
});
|
||||
this.pages = {};
|
||||
this.state = { head: 0, tail: 0 };
|
||||
};
|
||||
|
||||
this.getNext = () => {
|
||||
const lastPageNumber = this.api.getLastPageNumber();
|
||||
const number = Math.min(this.state.tail + 1, lastPageNumber);
|
||||
|
||||
const isLoaded = (number >= this.state.head && number <= this.state.tail);
|
||||
const isValid = (number >= 1 && number <= lastPageNumber);
|
||||
|
||||
let popHeight = this.hooks.getScrollHeight();
|
||||
|
||||
if (!isValid || isLoaded) {
|
||||
this.chain = this.chain
|
||||
.then(() => $q.resolve(popHeight));
|
||||
|
||||
return this.chain;
|
||||
if (number < 1) {
|
||||
return $q.resolve([]);
|
||||
}
|
||||
|
||||
const pageCount = this.state.head - this.state.tail;
|
||||
|
||||
if (pageCount >= OUTPUT_PAGE_LIMIT) {
|
||||
this.chain = this.chain
|
||||
.then(() => this.popBack())
|
||||
.then(() => {
|
||||
popHeight = this.hooks.getScrollHeight();
|
||||
|
||||
return $q.resolve();
|
||||
});
|
||||
if (number > lastPageNumber) {
|
||||
return $q.resolve([]);
|
||||
}
|
||||
|
||||
this.chain = this.chain
|
||||
.then(() => this.api.getPage(number))
|
||||
.then(events => this.pushFront(events))
|
||||
.then(() => $q.resolve(popHeight));
|
||||
let promise;
|
||||
|
||||
return this.chain;
|
||||
if (this.pages[number]) {
|
||||
promise = $q.resolve(this.pages[number]);
|
||||
} else {
|
||||
promise = this.api.getPage(number);
|
||||
}
|
||||
|
||||
return promise
|
||||
.then(results => {
|
||||
if (results.length <= 0) {
|
||||
return $q.resolve([]);
|
||||
}
|
||||
|
||||
this.state.tail = number;
|
||||
this.pages[number] = results;
|
||||
|
||||
return $q.resolve(results);
|
||||
});
|
||||
};
|
||||
|
||||
this.getPrevious = () => {
|
||||
const number = Math.max(this.state.head - 1, 1);
|
||||
|
||||
const isLoaded = (number >= this.state.head && number <= this.state.tail);
|
||||
const isValid = (number >= 1 && number <= this.api.getLastPageNumber());
|
||||
|
||||
let popHeight = this.hooks.getScrollHeight();
|
||||
|
||||
if (!isValid || isLoaded) {
|
||||
this.chain = this.chain
|
||||
.then(() => $q.resolve(popHeight));
|
||||
|
||||
return this.chain;
|
||||
if (number < 1) {
|
||||
return $q.resolve([]);
|
||||
}
|
||||
|
||||
const pageCount = this.state.head - this.state.tail;
|
||||
|
||||
if (pageCount >= OUTPUT_PAGE_LIMIT) {
|
||||
this.chain = this.chain
|
||||
.then(() => this.popFront())
|
||||
.then(() => {
|
||||
popHeight = this.hooks.getScrollHeight();
|
||||
|
||||
return $q.resolve();
|
||||
});
|
||||
if (number > this.api.getLastPageNumber()) {
|
||||
return $q.resolve([]);
|
||||
}
|
||||
|
||||
this.chain = this.chain
|
||||
.then(() => this.api.getPage(number))
|
||||
.then(events => this.pushBack(events))
|
||||
.then(() => $q.resolve(popHeight));
|
||||
let promise;
|
||||
|
||||
return this.chain;
|
||||
if (this.pages[number]) {
|
||||
promise = $q.resolve(this.pages[number]);
|
||||
} else {
|
||||
promise = this.api.getPage(number);
|
||||
}
|
||||
|
||||
return promise
|
||||
.then(results => {
|
||||
if (results.length <= 0) {
|
||||
return $q.resolve([]);
|
||||
}
|
||||
|
||||
this.state.head = number;
|
||||
this.pages[number] = results;
|
||||
|
||||
return $q.resolve(results);
|
||||
});
|
||||
};
|
||||
|
||||
this.clear = () => {
|
||||
const count = this.getRecordCount();
|
||||
this.getLast = () => this.api.getLast()
|
||||
.then(results => {
|
||||
if (results.length <= 0) {
|
||||
return $q.resolve([]);
|
||||
}
|
||||
|
||||
for (let i = 0; i <= count; ++i) {
|
||||
this.chain = this.chain.then(() => this.popBack());
|
||||
}
|
||||
const number = this.api.getLastPageNumber();
|
||||
|
||||
return this.chain;
|
||||
};
|
||||
this.state.head = number;
|
||||
this.state.tail = number;
|
||||
this.pages[number] = results;
|
||||
|
||||
this.getLast = () => this.clear()
|
||||
.then(() => this.api.getLast())
|
||||
.then(events => {
|
||||
const lastPage = this.api.getLastPageNumber();
|
||||
return $q.resolve(results);
|
||||
});
|
||||
|
||||
this.state.head = lastPage;
|
||||
this.state.tail = lastPage;
|
||||
this.getFirst = () => this.api.getFirst()
|
||||
.then(results => {
|
||||
if (results.length <= 0) {
|
||||
return $q.resolve([]);
|
||||
}
|
||||
|
||||
return this.pushBack(events, lastPage);
|
||||
})
|
||||
.then(() => this.getPrevious());
|
||||
|
||||
this.getFirst = () => this.clear()
|
||||
.then(() => this.api.getFirst())
|
||||
.then(events => {
|
||||
this.state.head = 1;
|
||||
this.state.tail = 1;
|
||||
this.pages[1] = results;
|
||||
|
||||
return this.pushBack(events, 1);
|
||||
})
|
||||
.then(() => this.getNext());
|
||||
return $q.resolve(results);
|
||||
});
|
||||
|
||||
this.isOnLastPage = () => this.api.getLastPageNumber() === this.state.tail;
|
||||
this.getRecordCount = () => Object.keys(this.records).length;
|
||||
this.getTailCounter = () => this.state.tail;
|
||||
this.getMaxCounter = () => this.api.getMaxCounter();
|
||||
this.trimTail = () => {
|
||||
const { tail, head } = this.state;
|
||||
let popCount = 0;
|
||||
|
||||
for (let i = tail; i > head; i--) {
|
||||
if (!this.isOverCapacity()) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (this.pages[i]) {
|
||||
popCount += this.pages[i].length;
|
||||
}
|
||||
|
||||
delete this.pages[i];
|
||||
|
||||
this.state.tail--;
|
||||
}
|
||||
|
||||
return popCount;
|
||||
};
|
||||
|
||||
this.trimHead = () => {
|
||||
const { head, tail } = this.state;
|
||||
let popCount = 0;
|
||||
|
||||
for (let i = head; i < tail; i++) {
|
||||
if (!this.isOverCapacity()) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (this.pages[i]) {
|
||||
popCount += this.pages[i].length;
|
||||
}
|
||||
|
||||
delete this.pages[i];
|
||||
|
||||
this.state.head++;
|
||||
}
|
||||
|
||||
return popCount;
|
||||
};
|
||||
|
||||
this.isOverCapacity = () => this.state.tail - this.state.head > OUTPUT_PAGE_LIMIT;
|
||||
}
|
||||
|
||||
PageService.$inject = ['$q'];
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
/* eslint camelcase: 0 */
|
||||
import {
|
||||
API_MAX_PAGE_SIZE,
|
||||
OUTPUT_EVENT_LIMIT,
|
||||
OUTPUT_PAGE_SIZE,
|
||||
} from './constants';
|
||||
@ -34,9 +33,8 @@ function getContinuous (events, reverse = false) {
|
||||
}
|
||||
|
||||
function SlidingWindowService ($q) {
|
||||
this.init = (storage, api, { getScrollHeight }) => {
|
||||
const { prepend, append, shift, pop, getRecord, deleteRecord, clear } = storage;
|
||||
const { getRange, getFirst, getLast, getMaxCounter } = api;
|
||||
this.init = ({ getRange, getFirst, getLast, getMaxCounter }, storage) => {
|
||||
const { getHeadCounter, getTailCounter } = storage;
|
||||
|
||||
this.api = {
|
||||
getRange,
|
||||
@ -46,36 +44,24 @@ function SlidingWindowService ($q) {
|
||||
};
|
||||
|
||||
this.storage = {
|
||||
clear,
|
||||
prepend,
|
||||
append,
|
||||
shift,
|
||||
pop,
|
||||
getRecord,
|
||||
deleteRecord,
|
||||
getHeadCounter,
|
||||
getTailCounter,
|
||||
};
|
||||
|
||||
this.hooks = {
|
||||
getScrollHeight,
|
||||
};
|
||||
|
||||
this.lines = {};
|
||||
this.uuids = {};
|
||||
this.chain = $q.resolve();
|
||||
|
||||
this.state = { head: null, tail: null };
|
||||
this.cache = { first: null };
|
||||
|
||||
this.buffer = {
|
||||
events: [],
|
||||
min: 0,
|
||||
max: 0,
|
||||
count: 0,
|
||||
};
|
||||
|
||||
this.cache = {
|
||||
first: null
|
||||
};
|
||||
};
|
||||
|
||||
this.getBoundedRange = range => {
|
||||
const bounds = [0, this.getMaxCounter()];
|
||||
const bounds = [1, this.getMaxCounter()];
|
||||
|
||||
return [Math.max(range[0], bounds[0]), Math.min(range[1], bounds[1])];
|
||||
};
|
||||
@ -92,273 +78,48 @@ function SlidingWindowService ($q) {
|
||||
return this.getBoundedRange([head - 1 - displacement, head - 1]);
|
||||
};
|
||||
|
||||
this.createRecord = ({ counter, uuid, start_line, end_line }) => {
|
||||
this.lines[counter] = end_line - start_line;
|
||||
this.uuids[counter] = uuid;
|
||||
|
||||
if (this.state.tail === null) {
|
||||
this.state.tail = counter;
|
||||
}
|
||||
|
||||
if (counter > this.state.tail) {
|
||||
this.state.tail = counter;
|
||||
}
|
||||
|
||||
if (this.state.head === null) {
|
||||
this.state.head = counter;
|
||||
}
|
||||
|
||||
if (counter < this.state.head) {
|
||||
this.state.head = counter;
|
||||
}
|
||||
};
|
||||
|
||||
this.deleteRecord = counter => {
|
||||
this.storage.deleteRecord(this.uuids[counter]);
|
||||
|
||||
delete this.uuids[counter];
|
||||
delete this.lines[counter];
|
||||
};
|
||||
|
||||
this.getLineCount = counter => {
|
||||
const record = this.storage.getRecord(counter);
|
||||
|
||||
if (record && record.lineCount) {
|
||||
return record.lineCount;
|
||||
}
|
||||
|
||||
if (this.lines[counter]) {
|
||||
return this.lines[counter];
|
||||
}
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
this.pushFront = events => {
|
||||
const tail = this.getTailCounter();
|
||||
const newEvents = events.filter(({ counter }) => counter > tail);
|
||||
|
||||
return this.storage.append(newEvents)
|
||||
.then(() => {
|
||||
newEvents.forEach(event => this.createRecord(event));
|
||||
|
||||
return $q.resolve();
|
||||
});
|
||||
};
|
||||
|
||||
this.pushBack = events => {
|
||||
const [head, tail] = this.getRange();
|
||||
const newEvents = events
|
||||
.filter(({ counter }) => counter < head || counter > tail);
|
||||
|
||||
return this.storage.prepend(newEvents)
|
||||
.then(() => {
|
||||
newEvents.forEach(event => this.createRecord(event));
|
||||
|
||||
return $q.resolve();
|
||||
});
|
||||
};
|
||||
|
||||
this.popFront = count => {
|
||||
if (!count || count <= 0) {
|
||||
return $q.resolve();
|
||||
}
|
||||
|
||||
const max = this.getTailCounter();
|
||||
const min = max - count;
|
||||
|
||||
let lines = 0;
|
||||
|
||||
for (let i = max; i >= min; --i) {
|
||||
lines += this.getLineCount(i);
|
||||
}
|
||||
|
||||
return this.storage.pop(lines)
|
||||
.then(() => {
|
||||
for (let i = max; i >= min; --i) {
|
||||
this.deleteRecord(i);
|
||||
this.state.tail--;
|
||||
}
|
||||
|
||||
return $q.resolve();
|
||||
});
|
||||
};
|
||||
|
||||
this.popBack = count => {
|
||||
if (!count || count <= 0) {
|
||||
return $q.resolve();
|
||||
}
|
||||
|
||||
const min = this.getHeadCounter();
|
||||
const max = min + count;
|
||||
|
||||
let lines = 0;
|
||||
|
||||
for (let i = min; i <= max; ++i) {
|
||||
lines += this.getLineCount(i);
|
||||
}
|
||||
|
||||
return this.storage.shift(lines)
|
||||
.then(() => {
|
||||
for (let i = min; i <= max; ++i) {
|
||||
this.deleteRecord(i);
|
||||
this.state.head++;
|
||||
}
|
||||
|
||||
return $q.resolve();
|
||||
});
|
||||
};
|
||||
|
||||
this.clear = () => this.storage.clear()
|
||||
.then(() => {
|
||||
const [head, tail] = this.getRange();
|
||||
|
||||
for (let i = head; i <= tail; ++i) {
|
||||
this.deleteRecord(i);
|
||||
}
|
||||
|
||||
this.state.head = null;
|
||||
this.state.tail = null;
|
||||
|
||||
return $q.resolve();
|
||||
});
|
||||
|
||||
this.getNext = (displacement = OUTPUT_PAGE_SIZE) => {
|
||||
const next = this.getNextRange(displacement);
|
||||
const [head, tail] = this.getRange();
|
||||
|
||||
this.chain = this.chain
|
||||
.then(() => this.api.getRange(next))
|
||||
.then(events => {
|
||||
const results = getContinuous(events);
|
||||
const min = Math.min(...results.map(({ counter }) => counter));
|
||||
|
||||
if (min > tail + 1) {
|
||||
return $q.resolve([]);
|
||||
}
|
||||
|
||||
return $q.resolve(results);
|
||||
})
|
||||
.then(results => {
|
||||
const count = (tail - head + results.length);
|
||||
const excess = count - OUTPUT_EVENT_LIMIT;
|
||||
|
||||
return this.popBack(excess)
|
||||
.then(() => {
|
||||
const popHeight = this.hooks.getScrollHeight();
|
||||
|
||||
return this.pushFront(results).then(() => $q.resolve(popHeight));
|
||||
});
|
||||
});
|
||||
|
||||
return this.chain;
|
||||
return this.api.getRange(next)
|
||||
.then(results => getContinuous(results));
|
||||
};
|
||||
|
||||
this.getPrevious = (displacement = OUTPUT_PAGE_SIZE) => {
|
||||
const previous = this.getPreviousRange(displacement);
|
||||
const [head, tail] = this.getRange();
|
||||
|
||||
this.chain = this.chain
|
||||
.then(() => this.api.getRange(previous))
|
||||
.then(events => {
|
||||
const results = getContinuous(events, true);
|
||||
const max = Math.max(...results.map(({ counter }) => counter));
|
||||
|
||||
if (head > max + 1) {
|
||||
return $q.resolve([]);
|
||||
}
|
||||
|
||||
return $q.resolve(results);
|
||||
})
|
||||
.then(results => {
|
||||
const count = (tail - head + results.length);
|
||||
const excess = count - OUTPUT_EVENT_LIMIT;
|
||||
|
||||
return this.popFront(excess)
|
||||
.then(() => {
|
||||
const popHeight = this.hooks.getScrollHeight();
|
||||
|
||||
return this.pushBack(results).then(() => $q.resolve(popHeight));
|
||||
});
|
||||
});
|
||||
|
||||
return this.chain;
|
||||
return this.api.getRange(previous)
|
||||
.then(results => getContinuous(results, true));
|
||||
};
|
||||
|
||||
this.getFirst = () => {
|
||||
this.chain = this.chain
|
||||
.then(() => this.clear())
|
||||
.then(() => {
|
||||
if (this.cache.first) {
|
||||
return $q.resolve(this.cache.first);
|
||||
}
|
||||
if (this.cache.first) {
|
||||
return $q.resolve(this.cache.first);
|
||||
}
|
||||
|
||||
return this.api.getFirst();
|
||||
})
|
||||
return this.api.getFirst()
|
||||
.then(events => {
|
||||
if (events.length === OUTPUT_PAGE_SIZE) {
|
||||
this.cache.first = events;
|
||||
}
|
||||
|
||||
return this.pushFront(events);
|
||||
return $q.resolve(events);
|
||||
});
|
||||
|
||||
return this.chain
|
||||
.then(() => this.getNext());
|
||||
};
|
||||
|
||||
this.getLast = () => {
|
||||
this.chain = this.chain
|
||||
.then(() => this.getFrames())
|
||||
.then(frames => {
|
||||
if (frames.length > 0) {
|
||||
return $q.resolve(frames);
|
||||
}
|
||||
this.getLast = () => this.getFrames()
|
||||
.then(frames => {
|
||||
if (frames.length > 0) {
|
||||
return $q.resolve(frames);
|
||||
}
|
||||
|
||||
return this.api.getLast();
|
||||
})
|
||||
.then(events => {
|
||||
const min = Math.min(...events.map(({ counter }) => counter));
|
||||
|
||||
if (min <= this.getTailCounter() + 1) {
|
||||
return this.pushFront(events);
|
||||
}
|
||||
|
||||
return this.clear()
|
||||
.then(() => this.pushBack(events));
|
||||
});
|
||||
|
||||
return this.chain
|
||||
.then(() => this.getPrevious());
|
||||
};
|
||||
|
||||
this.getTailCounter = () => {
|
||||
if (this.state.tail === null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (this.state.tail < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return this.state.tail;
|
||||
};
|
||||
|
||||
this.getHeadCounter = () => {
|
||||
if (this.state.head === null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (this.state.head < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return this.state.head;
|
||||
};
|
||||
return this.api.getLast();
|
||||
});
|
||||
|
||||
this.pushFrames = events => {
|
||||
const head = this.getHeadCounter();
|
||||
const tail = this.getTailCounter();
|
||||
const frames = this.buffer.events.concat(events);
|
||||
const [head, tail] = this.getRange();
|
||||
|
||||
let min;
|
||||
let max;
|
||||
@ -367,7 +128,7 @@ function SlidingWindowService ($q) {
|
||||
for (let i = frames.length - 1; i >= 0; i--) {
|
||||
count++;
|
||||
|
||||
if (count > API_MAX_PAGE_SIZE) {
|
||||
if (count > OUTPUT_EVENT_LIMIT) {
|
||||
frames.splice(i, 1);
|
||||
|
||||
count--;
|
||||
@ -417,9 +178,9 @@ function SlidingWindowService ($q) {
|
||||
return this.getTailCounter() >= (this.getMaxCounter() - OUTPUT_PAGE_SIZE);
|
||||
};
|
||||
|
||||
this.getRange = () => [this.getHeadCounter(), this.getTailCounter()];
|
||||
this.getRecordCount = () => Object.keys(this.lines).length;
|
||||
this.getCapacity = () => OUTPUT_EVENT_LIMIT - this.getRecordCount();
|
||||
this.isOnFirstPage = () => this.getHeadCounter() === 1;
|
||||
this.getTailCounter = () => this.storage.getTailCounter();
|
||||
this.getHeadCounter = () => this.storage.getHeadCounter();
|
||||
}
|
||||
|
||||
SlidingWindowService.$inject = ['$q'];
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user