From eea2a18c3f5466fdf0169edcfaa4ec8212ed9fbb Mon Sep 17 00:00:00 2001 From: alecmerdler Date: Sat, 21 Jan 2017 00:14:37 -0800 Subject: [PATCH] move window.__config to an Angular constant; refactor AngularViewArray -> ViewArray + ViewArrayFactory --- static/js/constants/quay-config.constant.ts | 10 ++ static/js/directives/ui/build-logs-view.js | 4 +- static/js/directives/ui/image-feature-view.js | 2 +- .../directives/ui/image-vulnerability-view.js | 2 +- static/js/quay.config.ts | 8 +- static/js/quay.module.ts | 16 +- static/js/quay.run.ts | 6 +- .../angular-view-array.spec.ts | 152 ------------------ .../angular-view-array/angular-view-array.ts | 24 --- .../services/angular-view-array/view-array.ts | 12 -- static/js/services/table-service.js | 4 +- .../view-array/view-array.factory.spec.ts | 27 ++++ .../services/view-array/view-array.factory.ts | 29 ++++ .../view-array/view-array.impl.spec.ts | 140 ++++++++++++++++ .../view-array.impl.ts | 49 +++--- static/js/services/view-array/view-array.ts | 62 +++++++ 16 files changed, 316 insertions(+), 231 deletions(-) create mode 100644 static/js/constants/quay-config.constant.ts delete mode 100644 static/js/services/angular-view-array/angular-view-array.spec.ts delete mode 100644 static/js/services/angular-view-array/angular-view-array.ts delete mode 100644 static/js/services/angular-view-array/view-array.ts create mode 100644 static/js/services/view-array/view-array.factory.spec.ts create mode 100644 static/js/services/view-array/view-array.factory.ts create mode 100644 static/js/services/view-array/view-array.impl.spec.ts rename static/js/services/{angular-view-array => view-array}/view-array.impl.ts (55%) create mode 100644 static/js/services/view-array/view-array.ts diff --git a/static/js/constants/quay-config.constant.ts b/static/js/constants/quay-config.constant.ts new file mode 100644 index 000000000..400207b75 --- /dev/null +++ b/static/js/constants/quay-config.constant.ts @@ -0,0 +1,10 @@ +/** + * Configuration data set. + */ +export const CONFIG: any = (window).__config; + + +/** + * REST API route information. + */ +export const ENDPOINTS: any = (window).__endpoints; \ No newline at end of file diff --git a/static/js/directives/ui/build-logs-view.js b/static/js/directives/ui/build-logs-view.js index 9d1dd7569..e26d10b2b 100644 --- a/static/js/directives/ui/build-logs-view.js +++ b/static/js/directives/ui/build-logs-view.js @@ -13,7 +13,7 @@ angular.module('quay').directive('buildLogsView', function () { 'useTimestamps': '=useTimestamps', 'buildUpdated': '&buildUpdated' }, - controller: function($scope, $element, $interval, $sanitize, ansi2html, AngularViewArray, + controller: function($scope, $element, $interval, $sanitize, ansi2html, ViewArrayFactory, AngularPollChannel, ApiService, Restangular, UtilService) { var result = $element.find('#copyButton').clipboardCopy(); @@ -53,7 +53,7 @@ angular.module('quay').directive('buildLogsView', function () { var entry = logs[i]; var type = entry['type'] || 'entry'; if (type == 'command' || type == 'phase' || type == 'error') { - entry['logs'] = AngularViewArray.create(); + entry['logs'] = ViewArrayFactory.create(); entry['index'] = $scope.logStartIndex + i; $scope.logEntries.push(entry); diff --git a/static/js/directives/ui/image-feature-view.js b/static/js/directives/ui/image-feature-view.js index 175089318..484bfe8f9 100644 --- a/static/js/directives/ui/image-feature-view.js +++ b/static/js/directives/ui/image-feature-view.js @@ -13,7 +13,7 @@ angular.module('quay').directive('imageFeatureView', function () { 'image': '=image', 'isEnabled': '=isEnabled' }, - controller: function($scope, $element, Config, ApiService, VulnerabilityService, AngularViewArray, ImageMetadataService, TableService) { + controller: function($scope, $element, Config, ApiService, VulnerabilityService, ViewArrayFactory, ImageMetadataService, TableService) { $scope.options = { 'filter': null, 'predicate': 'fixableScore', diff --git a/static/js/directives/ui/image-vulnerability-view.js b/static/js/directives/ui/image-vulnerability-view.js index afbf9671e..25d49bb63 100644 --- a/static/js/directives/ui/image-vulnerability-view.js +++ b/static/js/directives/ui/image-vulnerability-view.js @@ -13,7 +13,7 @@ angular.module('quay').directive('imageVulnerabilityView', function () { 'image': '=image', 'isEnabled': '=isEnabled' }, - controller: function($scope, $element, Config, ApiService, VulnerabilityService, AngularViewArray, ImageMetadataService, TableService) { + controller: function($scope, $element, Config, ApiService, VulnerabilityService, ViewArrayFactory, ImageMetadataService, TableService) { $scope.options = { 'filter': null, 'fixableVulns': false, diff --git a/static/js/quay.config.ts b/static/js/quay.config.ts index 3a9e90551..f7ba10686 100644 --- a/static/js/quay.config.ts +++ b/static/js/quay.config.ts @@ -3,6 +3,7 @@ import * as Raven from 'raven-js'; quayConfig.$inject = [ '$provide', + 'CONFIG', 'cfpLoadingBarProvider', '$tooltipProvider', '$compileProvider', @@ -12,6 +13,7 @@ quayConfig.$inject = [ export function quayConfig( $provide, + CONFIG, cfpLoadingBarProvider, $tooltipProvider, $compileProvider, @@ -38,7 +40,7 @@ export function quayConfig( return tooltipFactory.apply(this, arguments); }; - if (!(window).__config['DEBUG']) { + if (!CONFIG['DEBUG']) { $compileProvider.debugInfoEnabled(false); } @@ -50,12 +52,12 @@ export function quayConfig( RestangularProvider.setBaseUrl('/api/v1/'); // Configure analytics. - if ((window).__config && (window).__config.MIXPANEL_KEY) { + if (CONFIG && CONFIG.MIXPANEL_KEY) { $analyticsProvider.virtualPageviews(true); } // Configure sentry. - if ((window).__config && (window).__config.SENTRY_PUBLIC_DSN) { + if (CONFIG && CONFIG.SENTRY_PUBLIC_DSN) { $provide.decorator("$exceptionHandler", function($delegate) { return function(ex, cause) { $delegate(ex, cause); diff --git a/static/js/quay.module.ts b/static/js/quay.module.ts index 257c70f91..b61c0a4d4 100644 --- a/static/js/quay.module.ts +++ b/static/js/quay.module.ts @@ -2,10 +2,11 @@ import * as angular from 'angular'; import { quayConfig } from './quay.config'; import quayPages from './quay-pages.module'; import quayRun from './quay.run'; -import { angularViewArrayFactory } from './services/angular-view-array/angular-view-array'; +import { ViewArrayFactory } from './services/view-array/view-array.factory'; import { routeBuilderFactory } from './route-builder/route-builder.service.impl'; import NAME_PATTERNS from './constants/name-patterns.constant'; import { routeConfig } from './quay.routes'; +import { CONFIG } from './constants/quay-config.constant'; var quayDependencies: string[] = [ @@ -28,19 +29,19 @@ var quayDependencies: string[] = [ 'react' ]; -if ((window).__config && ((window).__config.MIXPANEL_KEY || (window).__config.MUNCHKIN_KEY || (window).__config.GOOGLE_ANALYTICS_KEY)) { +if (CONFIG && (CONFIG.MIXPANEL_KEY || CONFIG.MUNCHKIN_KEY || CONFIG.GOOGLE_ANALYTICS_KEY)) { quayDependencies.push('angulartics'); } -if ((window).__config && (window).__config.MIXPANEL_KEY) { +if (CONFIG && CONFIG.MIXPANEL_KEY) { quayDependencies.push('angulartics.mixpanel'); } -if ((window).__config && (window).__config.MUNCHKIN_KEY) { +if (CONFIG && CONFIG.MUNCHKIN_KEY) { quayDependencies.push('angulartics.marketo'); } -if ((window).__config && (window).__config.GOOGLE_ANALYTICS_KEY) { +if (CONFIG && CONFIG.GOOGLE_ANALYTICS_KEY) { quayDependencies.push('angulartics.google.analytics'); } -if ((window).__config && (window).__config.RECAPTCHA_SITE_KEY) { +if (CONFIG && CONFIG.RECAPTCHA_SITE_KEY) { quayDependencies.push('vcRecaptcha'); } @@ -49,7 +50,8 @@ export default angular .config(quayConfig) .config(routeConfig) .constant('NAME_PATTERNS', NAME_PATTERNS) + .constant('CONFIG', CONFIG) .factory('RouteBuilder', routeBuilderFactory) - .factory('AngularViewArray', angularViewArrayFactory) + .service('ViewArrayFactory', ViewArrayFactory) .run(quayRun) .name; \ No newline at end of file diff --git a/static/js/quay.run.ts b/static/js/quay.run.ts index a8a2a139f..e7513c560 100644 --- a/static/js/quay.run.ts +++ b/static/js/quay.run.ts @@ -14,6 +14,7 @@ quayRun.$inject = [ '$anchorScroll', 'UtilService', 'MetaService', + 'CONFIG', ]; export default function quayRun( @@ -28,8 +29,9 @@ export default function quayRun( Features, $anchorScroll, UtilService, - MetaService) { - var defaultTitle = (window).__config['REGISTRY_TITLE'] || 'Quay Container Registry'; + MetaService, + CONFIG) { + var defaultTitle = CONFIG['REGISTRY_TITLE'] || 'Quay Container Registry'; // Handle session security. Restangular.setDefaultRequestParams(['post', 'put', 'remove', 'delete'], {'_csrf_token': (window).__token || ''}); diff --git a/static/js/services/angular-view-array/angular-view-array.spec.ts b/static/js/services/angular-view-array/angular-view-array.spec.ts deleted file mode 100644 index 8a02d35b1..000000000 --- a/static/js/services/angular-view-array/angular-view-array.spec.ts +++ /dev/null @@ -1,152 +0,0 @@ -import { angularViewArrayFactory } from './angular-view-array'; -import { ViewArrayImpl } from './view-array.impl'; - - -describe("Service: AngularViewArray", () => { - var angularViewArray: any; - var $interval: ng.IIntervalService; - - beforeEach(inject(($injector: ng.auto.IInjectorService) => { - $interval = $injector.get('$interval'); - angularViewArray = angularViewArrayFactory($interval); - })); - - describe("create", () => { - - it("sanity", () => { - expect(angularViewArrayFactory).toBeDefined(); - }); - - it("returns a ViewArray object", () => { - var viewArray: ViewArrayImpl = angularViewArray.create(); - - expect(viewArray).toBeDefined(); - }); - - describe("returned ViewArray object", () => { - var viewArray: ViewArrayImpl; - - beforeEach(() => { - viewArray = angularViewArray.create(); - }); - - describe("constructor", () => { - // TODO - }); - - describe("length", () => { - - it("returns the number of entries", () => { - viewArray.entries = [{}, {}, {}]; - - expect(viewArray.length()).toEqual(viewArray.entries.length); - }); - }); - - describe("get", () => { - - it("returns the entry at a given index", () => { - var index: number = 8; - viewArray.entries = new Array(10); - viewArray.entries[index] = 3; - - expect(viewArray.get(index)).toEqual(viewArray.entries[index]); - }); - }); - - describe("push", () => { - - it("adds given element to the end of entries", () => { - var element: number = 3; - var originalLength: number = viewArray.length(); - viewArray.push(element); - - expect(viewArray.entries.length).toEqual(originalLength + 1); - expect(viewArray.get(originalLength)).toEqual(element); - }); - - it("sets 'hasEntries' to true", () => { - viewArray.push(2); - - expect(viewArray.hasEntries).toBe(true); - }); - - it("starts timer if 'isVisible' is true", () => { - spyOn(viewArray, "startTimer_").and.returnValue(null); - viewArray.isVisible = true; - viewArray.push(2); - - expect(viewArray.startTimer_).toHaveBeenCalled(); - }); - - it("does not start timer if 'isVisible' is false", () => { - spyOn(viewArray, "startTimer_").and.returnValue(null); - viewArray.isVisible = false; - viewArray.push(2); - - expect(viewArray.startTimer_).not.toHaveBeenCalled(); - }); - }); - - describe("toggle", () => { - - it("sets 'isVisible' to false if currently true", () => { - viewArray.isVisible = true; - viewArray.toggle(); - - expect(viewArray.isVisible).toBe(false); - }); - - it("sets 'isVisible' to true if currently false", () => { - viewArray.isVisible = false; - viewArray.toggle(); - - expect(viewArray.isVisible).toBe(true); - }); - }); - - describe("setVisible", () => { - - it("sets 'isVisible' to false if given false", () => { - viewArray.setVisible(false); - - expect(viewArray.isVisible).toBe(false); - }); - - it("sets 'visibleEntries' to empty array if given false", () => { - viewArray.setVisible(false); - - expect(viewArray.visibleEntries.length).toEqual(0); - }); - - it("shows additional entries if given true", () => { - spyOn(viewArray, "showAdditionalEntries_").and.returnValue(null); - viewArray.setVisible(true); - - expect(viewArray.showAdditionalEntries_).toHaveBeenCalled(); - }); - - it("does not show additional entries if given false", () => { - spyOn(viewArray, "showAdditionalEntries_").and.returnValue(null); - viewArray.setVisible(false); - - expect(viewArray.showAdditionalEntries_).not.toHaveBeenCalled(); - }); - - it("starts timer if given true", () => { - spyOn(viewArray, "startTimer_").and.returnValue(null); - viewArray.setVisible(true); - - expect(viewArray.startTimer_).toHaveBeenCalled(); - }); - - it("stops timer if given false", () => { - spyOn(viewArray, "stopTimer_").and.returnValue(null); - viewArray.setVisible(true); - - expect(viewArray.stopTimer_).toHaveBeenCalled(); - }); - }); - }); - }); -}); \ No newline at end of file diff --git a/static/js/services/angular-view-array/angular-view-array.ts b/static/js/services/angular-view-array/angular-view-array.ts deleted file mode 100644 index 0de564392..000000000 --- a/static/js/services/angular-view-array/angular-view-array.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { ViewArray } from './view-array'; -import { ViewArrayImpl } from './view-array.impl'; - - -/** - * Specialized wrapper around array which provides a toggle() method for viewing the contents of the - * array in a manner that is asynchronously filled in over a short time period. This prevents long - * pauses in the UI for ngRepeat's when the array is significant in size. - */ - -angularViewArrayFactory.$inject = [ - '$interval' -]; - -export function angularViewArrayFactory($interval): any { - const ADDITIONAL_ENTRIES: number = 20; - - return { - create: function(): ViewArray { - return new ViewArrayImpl($interval, ADDITIONAL_ENTRIES); - } - }; -} - diff --git a/static/js/services/angular-view-array/view-array.ts b/static/js/services/angular-view-array/view-array.ts deleted file mode 100644 index 343ca1efb..000000000 --- a/static/js/services/angular-view-array/view-array.ts +++ /dev/null @@ -1,12 +0,0 @@ -export abstract class ViewArray { - - public abstract length(): number; - - public abstract get(index: number): any; - - public abstract push(elem: any): void; - - public abstract toggle(): void; - - public abstract setVisible(newState: boolean): void; -} \ No newline at end of file diff --git a/static/js/services/table-service.js b/static/js/services/table-service.js index 180cf648a..76968e6c6 100644 --- a/static/js/services/table-service.js +++ b/static/js/services/table-service.js @@ -1,7 +1,7 @@ /** * Service which provides helper methods for constructing and managing tabular data. */ -angular.module('quay').factory('TableService', ['AngularViewArray', function(AngularViewArray) { +angular.module('quay').factory('TableService', ['ViewArrayFactory', function(ViewArrayFactory) { var tableService = {}; tableService.tablePredicateClass = function(name, predicate, reverse) { @@ -31,7 +31,7 @@ angular.module('quay').factory('TableService', ['AngularViewArray', function(Ang }; tableService.buildOrderedItems = function(items, options, filterFields, numericFields, opt_extrafilter) { - var orderedItems = AngularViewArray.create(); + var orderedItems = ViewArrayFactory.create(); items.forEach(function(item) { var filter = options.filter; diff --git a/static/js/services/view-array/view-array.factory.spec.ts b/static/js/services/view-array/view-array.factory.spec.ts new file mode 100644 index 000000000..5e0b11134 --- /dev/null +++ b/static/js/services/view-array/view-array.factory.spec.ts @@ -0,0 +1,27 @@ +import { ViewArrayFactory } from './view-array.factory'; +import { ViewArray } from './view-array'; + + +describe("Factory: ViewArrayFactory", () => { + var viewArrayFactory: ViewArrayFactory; + var $intervalMock: any; + + beforeEach(() => { + $intervalMock = jasmine.createSpy('$intervalSpy'); + $intervalMock.cancel = jasmine.createSpy('cancelSpy'); + viewArrayFactory = new ViewArrayFactory($intervalMock) + }); + + describe("constructor", () => { + + }); + + describe("create", () => { + + it("returns a ViewArray object", () => { + var viewArray: ViewArray = viewArrayFactory.create(); + + expect(viewArray).toBeDefined(); + }); + }); +}); \ No newline at end of file diff --git a/static/js/services/view-array/view-array.factory.ts b/static/js/services/view-array/view-array.factory.ts new file mode 100644 index 000000000..0a4a4de2d --- /dev/null +++ b/static/js/services/view-array/view-array.factory.ts @@ -0,0 +1,29 @@ +import { ViewArray } from './view-array'; +import { ViewArrayImpl } from './view-array.impl'; + + +/** + * Factory for creating ViewArray instances. + */ +export class ViewArrayFactory { + + private ADDITIONAL_ENTRIES: number = 20; + + /** + * @param $interval Angular $interval service. + * @return viewArrayFactory A factory for creating ViewArray objects. + */ + static $inject = ['$interval']; + constructor(private $interval: ng.IIntervalService) { + + } + + /** + * Create a new ViewArray object. + * @return viewArray New ViewArrayImpl instance. + */ + public create(): ViewArray { + return new ViewArrayImpl(this.$interval, this.ADDITIONAL_ENTRIES); + } +} + diff --git a/static/js/services/view-array/view-array.impl.spec.ts b/static/js/services/view-array/view-array.impl.spec.ts new file mode 100644 index 000000000..82128e5bf --- /dev/null +++ b/static/js/services/view-array/view-array.impl.spec.ts @@ -0,0 +1,140 @@ +import { ViewArrayImpl } from './view-array.impl'; + + +describe("ViewArrayImplImpl", () => { + var viewArrayImpl: ViewArrayImpl; + var $intervalMock: any; + var additionalCount: number; + + beforeEach(inject(($injector: ng.auto.IInjectorService) => { + $intervalMock = jasmine.createSpy('$intervalSpy'); + $intervalMock.and.returnValue({}); + $intervalMock.cancel = jasmine.createSpy('cancelSpy'); + additionalCount = 20; + viewArrayImpl = new ViewArrayImpl($intervalMock, additionalCount); + })); + + + describe("constructor", () => { + + it("initializes values", () => { + expect(viewArrayImpl.isVisible).toBe(false); + expect(viewArrayImpl.visibleEntries).toBe(null); + expect(viewArrayImpl.entries.length).toEqual(0); + expect(viewArrayImpl.hasEntries).toBe(false); + expect(viewArrayImpl.hasHiddenEntries).toBe(false); + }); + }); + + describe("length", () => { + + it("returns the number of entries", () => { + viewArrayImpl.entries = [{}, {}, {}]; + + expect(viewArrayImpl.length()).toEqual(viewArrayImpl.entries.length); + }); + }); + + describe("get", () => { + + it("returns the entry at a given index", () => { + var index: number = 8; + viewArrayImpl.entries = new Array(10); + viewArrayImpl.entries[index] = 3; + + expect(viewArrayImpl.get(index)).toEqual(viewArrayImpl.entries[index]); + }); + }); + + describe("push", () => { + + it("adds given element to the end of entries", () => { + var element: number = 3; + var originalLength: number = viewArrayImpl.length(); + viewArrayImpl.push(element); + + expect(viewArrayImpl.entries.length).toEqual(originalLength + 1); + expect(viewArrayImpl.get(originalLength)).toEqual(element); + }); + + it("sets 'hasEntries' to true", () => { + viewArrayImpl.push(2); + + expect(viewArrayImpl.hasEntries).toBe(true); + }); + + it("starts timer if 'isVisible' is true", () => { + viewArrayImpl.isVisible = true; + viewArrayImpl.push(2); + + expect($intervalMock).toHaveBeenCalled(); + }); + + it("does not start timer if 'isVisible' is false", () => { + viewArrayImpl.isVisible = false; + viewArrayImpl.push(2); + + expect($intervalMock).not.toHaveBeenCalled(); + }); + }); + + describe("toggle", () => { + + it("sets 'isVisible' to false if currently true", () => { + viewArrayImpl.isVisible = true; + viewArrayImpl.toggle(); + + expect(viewArrayImpl.isVisible).toBe(false); + }); + + it("sets 'isVisible' to true if currently false", () => { + viewArrayImpl.isVisible = false; + viewArrayImpl.toggle(); + + expect(viewArrayImpl.isVisible).toBe(true); + }); + }); + + describe("setVisible", () => { + + it("sets 'isVisible' to false if given false", () => { + viewArrayImpl.setVisible(false); + + expect(viewArrayImpl.isVisible).toBe(false); + }); + + it("sets 'visibleEntries' to empty array if given false", () => { + viewArrayImpl.setVisible(false); + + expect(viewArrayImpl.visibleEntries.length).toEqual(0); + }); + + it("shows additional entries if given true", () => { + viewArrayImpl.setVisible(true); + }); + + it("does not show additional entries if given false", () => { + viewArrayImpl.setVisible(false); + }); + + it("starts timer if given true", () => { + viewArrayImpl.setVisible(true); + + expect($intervalMock).toHaveBeenCalled(); + }); + + it("does not stop timer if given false and timer is not active", () => { + viewArrayImpl.setVisible(false); + + expect($intervalMock.cancel).not.toHaveBeenCalled(); + }); + + it("stops timer if given false and timer is active", () => { + viewArrayImpl.isVisible = true; + viewArrayImpl.push(2); + viewArrayImpl.setVisible(false); + + expect($intervalMock.cancel).toHaveBeenCalled(); + }); + }); +}); \ No newline at end of file diff --git a/static/js/services/angular-view-array/view-array.impl.ts b/static/js/services/view-array/view-array.impl.ts similarity index 55% rename from static/js/services/angular-view-array/view-array.impl.ts rename to static/js/services/view-array/view-array.impl.ts index cca978307..f0ed9a445 100644 --- a/static/js/services/angular-view-array/view-array.impl.ts +++ b/static/js/services/view-array/view-array.impl.ts @@ -3,13 +3,13 @@ import { ViewArray } from './view-array'; export class ViewArrayImpl implements ViewArray { + public entries: any[]; public isVisible: boolean; public visibleEntries: any[]; public hasEntries: boolean; - public entries: any[]; public hasHiddenEntries: boolean; - public timerRef_: any; - public currentIndex_: number; + private timerRef: any; + private currentIndex: number; constructor(private interval: any, private additionalCount: number) { this.isVisible = false; @@ -17,8 +17,8 @@ export class ViewArrayImpl implements ViewArray { this.hasEntries = false; this.entries = []; this.hasHiddenEntries = false; - this.timerRef_ = null; - this.currentIndex_ = 0; + this.timerRef = null; + this.currentIndex = 0; } public length(): number { @@ -34,7 +34,7 @@ export class ViewArrayImpl implements ViewArray { this.hasEntries = true; if (this.isVisible) { - this.startTimer_(); + this.startTimer(); } } @@ -46,45 +46,44 @@ export class ViewArrayImpl implements ViewArray { this.isVisible = newState; this.visibleEntries = []; - this.currentIndex_ = 0; + this.currentIndex = 0; if (newState) { - this.showAdditionalEntries_(); - this.startTimer_(); + this.showAdditionalEntries(); + this.startTimer(); } else { - this.stopTimer_(); + this.stopTimer(); } } - public showAdditionalEntries_(): void { + private showAdditionalEntries(): void { var i: number = 0; - for (i = this.currentIndex_; i < (this.currentIndex_ + this.additionalCount) && i < this.entries.length; ++i) { + for (i = this.currentIndex; i < (this.currentIndex + this.additionalCount) && i < this.entries.length; ++i) { this.visibleEntries.push(this.entries[i]); } - this.currentIndex_ = i; - this.hasHiddenEntries = this.currentIndex_ < this.entries.length; - if (this.currentIndex_ >= this.entries.length) { - this.stopTimer_(); + this.currentIndex = i; + this.hasHiddenEntries = this.currentIndex < this.entries.length; + if (this.currentIndex >= this.entries.length) { + this.stopTimer(); } } - public startTimer_(): void { - if (this.timerRef_) { + private startTimer(): void { + if (this.timerRef) { return; } - var that = this; - this.timerRef_ = this.interval(function() { - that.showAdditionalEntries_(); + this.timerRef = this.interval(() => { + this.showAdditionalEntries(); }, 10); } - public stopTimer_(): void { - if (this.timerRef_) { - this.interval.cancel(this.timerRef_); - this.timerRef_ = null; + private stopTimer(): void { + if (this.timerRef) { + this.interval.cancel(this.timerRef); + this.timerRef = null; } } } \ No newline at end of file diff --git a/static/js/services/view-array/view-array.ts b/static/js/services/view-array/view-array.ts new file mode 100644 index 000000000..fdc89e64a --- /dev/null +++ b/static/js/services/view-array/view-array.ts @@ -0,0 +1,62 @@ +/** + * Specialized wrapper around array which provides a toggle() method for viewing the contents of the + * array in a manner that is asynchronously filled in over a short time period. This prevents long + * pauses in the UI for ngRepeat's when the array is significant in size. + */ +export abstract class ViewArray { + + /** + * The stored entries. + */ + public abstract entries: any; + + /** + * If the entries are displayed. + */ + public abstract isVisible: boolean; + + /** + * The displayed entries. + */ + public abstract visibleEntries: any[]; + + /** + * If there are stored entries. + */ + public abstract hasEntries: boolean; + + /** + * If there are entries not visible. + */ + public abstract hasHiddenEntries: boolean; + + /** + * Get the number of entries stored. + * @return number The number of entries. + */ + public abstract length(): number; + + /** + * Get a specific entry. + * @param index The index of the entry. + * @return element The element at the given index. + */ + public abstract get(index: number): any; + + /** + * Add a new element. + * @param elem The new element. + */ + public abstract push(elem: any): void; + + /** + * Toggle whether the elements are visible. + */ + public abstract toggle(): void; + + /** + * Set whether the elements are visible. + * @param newState True/False if the contents are visible. + */ + public abstract setVisible(newState: boolean): void; +} \ No newline at end of file