diff --git a/awx/ui/client/features/output/index.controller.js b/awx/ui/client/features/output/index.controller.js index 0f0579e14b..6e11152b76 100644 --- a/awx/ui/client/features/output/index.controller.js +++ b/awx/ui/client/features/output/index.controller.js @@ -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; - diff --git a/awx/ui/client/features/output/page.service.js b/awx/ui/client/features/output/page.service.js index 786d26ad66..1502d39a63 100644 --- a/awx/ui/client/features/output/page.service.js +++ b/awx/ui/client/features/output/page.service.js @@ -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']; diff --git a/awx/ui/client/features/output/slide.service.js b/awx/ui/client/features/output/slide.service.js index df6105617e..63a1753972 100644 --- a/awx/ui/client/features/output/slide.service.js +++ b/awx/ui/client/features/output/slide.service.js @@ -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'];