* slideshow start changed to slideshowService call
* builtin slide switching added to the team forms * parts of team form made conditional * fixed keyboard slide switching when not in slideshow * slideshow slide management totally reworked into linked list * Date.getDay() -> Date.getDate() as it was originally meant to be
This commit is contained in:
parent
6af8ccbf7a
commit
41ad9d9a28
@ -2,7 +2,7 @@
|
|||||||
<h1 class="ui dividing header">Dashboard</h1>
|
<h1 class="ui dividing header">Dashboard</h1>
|
||||||
|
|
||||||
<div class="ui four cards">
|
<div class="ui four cards">
|
||||||
<a class="ui raised yellow card" [routerLink]="['/kanban']" *ngIf="hasTeamSelected">
|
<a class="ui raised yellow card" (click)="startSlideShow()" *ngIf="hasTeamSelected">
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="header">Start slideshow</div>
|
<div class="header">Start slideshow</div>
|
||||||
<div class="meta">
|
<div class="meta">
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
import { Title } from '@angular/platform-browser';
|
import { Title } from '@angular/platform-browser';
|
||||||
import {SettingsService} from '../../shared/service/settings.service';
|
import {SettingsService} from '../../shared/service/settings.service';
|
||||||
|
import {SlideShowService} from '../../display/slide-show.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-dashboard',
|
selector: 'app-dashboard',
|
||||||
@ -10,7 +11,8 @@ import {SettingsService} from '../../shared/service/settings.service';
|
|||||||
export class DashboardComponent implements OnInit {
|
export class DashboardComponent implements OnInit {
|
||||||
|
|
||||||
constructor(private titleService: Title,
|
constructor(private titleService: Title,
|
||||||
private settingService: SettingsService) { }
|
private settingService: SettingsService,
|
||||||
|
private slideShowService: SlideShowService) { }
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.titleService.setTitle('Dashboard : MTAStv');
|
this.titleService.setTitle('Dashboard : MTAStv');
|
||||||
@ -20,4 +22,7 @@ export class DashboardComponent implements OnInit {
|
|||||||
return this.settingService.team.id !== null;
|
return this.settingService.team.id !== null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public startSlideShow() {
|
||||||
|
this.slideShowService.startWithFirstSlide();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,29 +1,59 @@
|
|||||||
<div class="ui main container">
|
<div class="ui main container">
|
||||||
<h1 class="ui dividing header">Team editor</h1>
|
<h1 class="ui dividing header">Team editor</h1>
|
||||||
<form class="ui form" #teamEditorForm (ngSubmit)="saveTeam(f)" #f="ngForm">
|
<form class="ui form" #teamEditorForm (ngSubmit)="saveTeam(f)" #f="ngForm">
|
||||||
<div class="two fields">
|
<div class="two inline fields">
|
||||||
<div class="six wide field" [class.error]="checkError(teamName)">
|
<div class="six wide field" [class.error]="checkError(teamName)">
|
||||||
<label for="team_name">Team name</label>
|
<label for="team_name">Team name</label>
|
||||||
<input id="team_name" type="text" name="team_name"
|
<input id="team_name" type="text" name="team_name"
|
||||||
required [(ngModel)]="team.name" #teamName="ngModel">
|
required [(ngModel)]="team.name" #teamName="ngModel">
|
||||||
</div>
|
</div>
|
||||||
<div class="six wide field" [class.error]="checkError(filterId)">
|
|
||||||
<label for="filter_id">Jira filter id</label>
|
|
||||||
<input id="filter_id" type="number" name="filter_id"
|
|
||||||
required minlength="4" min="1" [(ngModel)]="team.filterId" #filterId="ngModel">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="six wide field">
|
<div class="six wide field">
|
||||||
<label for="team_name"> </label>
|
<label> </label>
|
||||||
<div class="ui checkbox">
|
<div class="ui checkbox">
|
||||||
<input type="checkbox" id="team_is_active" name="team_is_active"
|
<input type="checkbox" id="team_is_active" name="team_is_active"
|
||||||
[(ngModel)]="team.isActive">
|
[(ngModel)]="team.isActive">
|
||||||
<label for="team_is_active">Active</label>
|
<label for="team_is_active">Active</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h4 class="ui dividing header">Built-in slides</h4>
|
||||||
|
<div class="three inline fields">
|
||||||
|
<div class="three wide field">
|
||||||
|
<label for="team_name"> </label>
|
||||||
|
<div class="ui checkbox">
|
||||||
|
<input type="checkbox" id="kanban_enabled" name="kanban_enabled"
|
||||||
|
[(ngModel)]="team.kanbanEnabled">
|
||||||
|
<label for="kanban_enabled">Kanban board</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="three wide field">
|
||||||
|
<label for="team_name"> </label>
|
||||||
|
<div class="ui checkbox">
|
||||||
|
<input type="checkbox" id="commit_tracker_enabled" name="commit_tracker_enabled"
|
||||||
|
[(ngModel)]="team.commitTrackerEnabled">
|
||||||
|
<label for="commit_tracker_enabled">Commit-tracker</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="three wide field">
|
||||||
|
<label for="team_name"> </label>
|
||||||
|
<div class="ui checkbox">
|
||||||
|
<input type="checkbox" id="watched_enabled" name="watched_enabled"
|
||||||
|
[(ngModel)]="team.watchedEnabled">
|
||||||
|
<label for="watched_enabled">Watched</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ng-container *ngIf="team.kanbanEnabled">
|
||||||
<h3 class="ui dividing header">Kanban configuration</h3>
|
<h3 class="ui dividing header">Kanban configuration</h3>
|
||||||
<h4 class="ui dividing header">Daily standup timer</h4>
|
<div class="six wide field" [class.error]="checkError(filterId)">
|
||||||
|
<label for="filter_id">Jira filter id</label>
|
||||||
|
<input id="filter_id" type="number" name="filter_id"
|
||||||
|
required minlength="4" min="1" [required]="team.kanbanEnabled"
|
||||||
|
[(ngModel)]="team.filterId" #filterId="ngModel">
|
||||||
|
</div>
|
||||||
|
<h5 class="ui dividing header">Daily standup timer</h5>
|
||||||
<div class="three inline fields">
|
<div class="three inline fields">
|
||||||
<div class="two wide field">
|
<div class="two wide field">
|
||||||
<div class="ui checkbox">
|
<div class="ui checkbox">
|
||||||
@ -36,7 +66,7 @@
|
|||||||
<label for="daily_start">Starts</label>
|
<label for="daily_start">Starts</label>
|
||||||
<div class="ui left icon input">
|
<div class="ui left icon input">
|
||||||
<input type="time" id="daily_start" name="daily_start"
|
<input type="time" id="daily_start" name="daily_start"
|
||||||
min="9:00" max="15:00" [required]="team.dailyLockEnabled"
|
min="9:00" max="15:00" [required]="team.dailyLockEnabled && team.kanbanEnabled"
|
||||||
[(ngModel)]="team.dailyStartTime" #startTime="ngModel">
|
[(ngModel)]="team.dailyStartTime" #startTime="ngModel">
|
||||||
<i class="time icon"></i>
|
<i class="time icon"></i>
|
||||||
</div>
|
</div>
|
||||||
@ -45,7 +75,7 @@
|
|||||||
<label for="daily_end">Ends</label>
|
<label for="daily_end">Ends</label>
|
||||||
<div class="ui left icon input">
|
<div class="ui left icon input">
|
||||||
<input type="time" id="daily_end" name="daily_end"
|
<input type="time" id="daily_end" name="daily_end"
|
||||||
min="9:00" max="15:00" [required]="team.dailyLockEnabled"
|
min="9:00" max="15:00" [required]="team.dailyLockEnabled && team.kanbanEnabled"
|
||||||
[(ngModel)]="team.dailyEndTime" #endTime="ngModel">
|
[(ngModel)]="team.dailyEndTime" #endTime="ngModel">
|
||||||
<i class="time icon"></i>
|
<i class="time icon"></i>
|
||||||
</div>
|
</div>
|
||||||
@ -148,6 +178,7 @@
|
|||||||
class="ui medium {{label.color}} label">{{label.name}}<i class="large delete icon"
|
class="ui medium {{label.color}} label">{{label.name}}<i class="large delete icon"
|
||||||
(click)="removeLabel(label)"></i></span>
|
(click)="removeLabel(label)"></i></span>
|
||||||
</div>
|
</div>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
<h3 class="ui dividing header">Team members</h3>
|
<h3 class="ui dividing header">Team members</h3>
|
||||||
<div class="three inline fields">
|
<div class="three inline fields">
|
||||||
|
|||||||
@ -15,6 +15,7 @@ export class AppComponent implements OnInit {
|
|||||||
|
|
||||||
@HostListener('document:keyup', ['$event.key'])
|
@HostListener('document:keyup', ['$event.key'])
|
||||||
private keyPressed(key: string) {
|
private keyPressed(key: string) {
|
||||||
|
if (this.timerService.autoSwitch) {
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case ' ':
|
case ' ':
|
||||||
this.timerService.togglePause();
|
this.timerService.togglePause();
|
||||||
@ -29,6 +30,7 @@ export class AppComponent implements OnInit {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public get paused(): boolean {
|
public get paused(): boolean {
|
||||||
return this.timerService.paused;
|
return this.timerService.paused;
|
||||||
|
|||||||
@ -1,76 +1,86 @@
|
|||||||
import {Injectable} from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import {Slide, SlideVisibility} from '../shared/slide';
|
import { Slide, SlideVisibility } from '../shared/slide';
|
||||||
import {SlideService} from '../shared/service/slide.service';
|
import { SlideService } from '../shared/service/slide.service';
|
||||||
import {Router} from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
import {AnimationDirection, SettingsService} from '../shared/service/settings.service';
|
import { AnimationDirection, SettingsService } from '../shared/service/settings.service';
|
||||||
|
import { TwoWayLinkedList } from '../shared/two-way-linked-list';
|
||||||
|
import { SlideWrapper, WrappedType } from '../shared/slide-wrapper';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class SlideShowService {
|
export class SlideShowService {
|
||||||
|
|
||||||
private oddEven = false;
|
private oddEven = false;
|
||||||
private currentSlideIndex = -1;
|
private cachedSlides: TwoWayLinkedList<SlideWrapper> = new TwoWayLinkedList<SlideWrapper>();
|
||||||
private slides: Array<Slide> = [];
|
|
||||||
|
|
||||||
constructor(private slideService: SlideService,
|
constructor(private slideService: SlideService,
|
||||||
private settingsService: SettingsService,
|
private settings: SettingsService,
|
||||||
private router: Router) {
|
private router: Router) {
|
||||||
this.reloadSlides();
|
this.reloadSlides();
|
||||||
}
|
}
|
||||||
|
|
||||||
public prevSlide() {
|
public startWithFirstSlide() {
|
||||||
this.settingsService.animationDirection = AnimationDirection.LEFT;
|
this.settings.animationDirection = AnimationDirection.RIGHT;
|
||||||
console.log('prev-in', this.slides.length, this.currentSlideIndex);
|
this.reloadSlides(() => this.switchToWrappedSlide(this.cachedSlides.first));
|
||||||
if (this.currentSlideIndex > this.slides.length) {
|
}
|
||||||
this.currentSlideIndex = this.slides.length;
|
|
||||||
this.router.navigate(['/watchers']);
|
public prevSlide() {
|
||||||
} else if (this.currentSlideIndex === this.slides.length) {
|
this.settings.animationDirection = AnimationDirection.LEFT;
|
||||||
this.currentSlideIndex--;
|
if (this.cachedSlides.isFirst()) {
|
||||||
this.router.navigate(['/commit-tracker']);
|
this.reloadSlides(() => this.switchToWrappedSlide(this.cachedSlides.last));
|
||||||
} else if (this.currentSlideIndex < 0) {
|
} else {
|
||||||
this.currentSlideIndex = this.slides.length + 1;
|
const prevSlide = this.cachedSlides.prev();
|
||||||
this.reloadSlides();
|
this.switchToWrappedSlide(prevSlide);
|
||||||
this.router.navigate(['/kanban']);
|
|
||||||
} else {
|
|
||||||
this.oddEven = !this.oddEven;
|
|
||||||
this.router.navigate([
|
|
||||||
this.oddEven ? '/slideshow-odd' : '/slideshow-even',
|
|
||||||
this.slides[this.currentSlideIndex].id
|
|
||||||
]);
|
|
||||||
this.currentSlideIndex--;
|
|
||||||
}
|
}
|
||||||
console.log('prev-out', this.slides.length, this.currentSlideIndex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public nextSlide() {
|
public nextSlide() {
|
||||||
this.settingsService.animationDirection = AnimationDirection.RIGHT;
|
this.settings.animationDirection = AnimationDirection.RIGHT;
|
||||||
console.log('next-in', this.slides.length, this.currentSlideIndex);
|
if (this.cachedSlides.isLast()) {
|
||||||
if (this.currentSlideIndex < 0) {
|
this.reloadSlides(() => this.switchToWrappedSlide(this.cachedSlides.first));
|
||||||
this.currentSlideIndex++;
|
|
||||||
this.router.navigate(['/kanban']);
|
|
||||||
} else if (this.currentSlideIndex === this.slides.length) {
|
|
||||||
this.currentSlideIndex++;
|
|
||||||
this.router.navigate(['/commit-tracker']);
|
|
||||||
} else if (this.currentSlideIndex > this.slides.length) {
|
|
||||||
this.currentSlideIndex = -1;
|
|
||||||
this.reloadSlides();
|
|
||||||
this.router.navigate(['/watchers']);
|
|
||||||
} else {
|
} else {
|
||||||
|
const nextSlide = this.cachedSlides.next();
|
||||||
|
this.switchToWrappedSlide(nextSlide);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private switchToWrappedSlide(wrappedSlide: SlideWrapper) {
|
||||||
|
if (WrappedType.BUILTIN === wrappedSlide.type) {
|
||||||
|
this.router.navigate([wrappedSlide.slideRoute]);
|
||||||
|
} else if (WrappedType.USER === wrappedSlide.type) {
|
||||||
this.oddEven = !this.oddEven;
|
this.oddEven = !this.oddEven;
|
||||||
this.router.navigate([
|
this.router.navigate([
|
||||||
this.oddEven ? '/slideshow-odd' : '/slideshow-even',
|
this.oddEven ? '/slideshow-odd' : '/slideshow-even',
|
||||||
this.slides[this.currentSlideIndex].id
|
wrappedSlide.slideData.id
|
||||||
]);
|
]);
|
||||||
this.currentSlideIndex++;
|
} else {
|
||||||
|
throw Error(`Unknown slide type: ${wrappedSlide.type}`);
|
||||||
}
|
}
|
||||||
console.log('next-out', this.slides.length, this.currentSlideIndex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private reloadSlides() {
|
private reloadSlides(onReloadFinish: () => void = null) {
|
||||||
const team = this.settingsService.team;
|
|
||||||
this.slideService.list().subscribe(
|
this.slideService.list().subscribe(
|
||||||
slides => this.slides = slides.filter(
|
slides => {
|
||||||
slide => slide.isVisible && (slide.visibility === SlideVisibility.Public || slide.teams.some(s => s.id === team.id))
|
this.cachedSlides.clear();
|
||||||
)
|
slides.filter(
|
||||||
|
(slide: Slide) => slide.isVisible && (slide.visibility === SlideVisibility.Public || slide.teams.some(
|
||||||
|
s => s.id === this.settings.team.id
|
||||||
|
))
|
||||||
|
).map(slide => this.cachedSlides.push(new SlideWrapper(WrappedType.USER, slide)));
|
||||||
|
|
||||||
|
if (this.settings.team.kanbanEnabled) {
|
||||||
|
this.cachedSlides.push(new SlideWrapper(WrappedType.BUILTIN, '/kanban'));
|
||||||
|
}
|
||||||
|
if (this.settings.team.commitTrackerEnabled) {
|
||||||
|
this.cachedSlides.push(new SlideWrapper(WrappedType.BUILTIN, '/commit-tracker'));
|
||||||
|
}
|
||||||
|
if (this.settings.team.watchedEnabled) {
|
||||||
|
this.cachedSlides.push(new SlideWrapper(WrappedType.BUILTIN, '/watchers'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null !== onReloadFinish) {
|
||||||
|
onReloadFinish();
|
||||||
|
}
|
||||||
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,6 +5,9 @@ import { Observable } from 'rxjs';
|
|||||||
|
|
||||||
import { environment } from '../../../environments/environment';
|
import { environment } from '../../../environments/environment';
|
||||||
import { Slide } from '../slide';
|
import { Slide } from '../slide';
|
||||||
|
import { TeamService } from './team.service';
|
||||||
|
import { SettingsService } from './settings.service';
|
||||||
|
import { flatMap } from 'rxjs/operators';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class SlideService implements Resolve<Array<Slide>>{
|
export class SlideService implements Resolve<Array<Slide>>{
|
||||||
@ -13,7 +16,9 @@ export class SlideService implements Resolve<Array<Slide>>{
|
|||||||
private apiEndPointPosition = environment.apiUrl + '/api/slide-position';
|
private apiEndPointPosition = environment.apiUrl + '/api/slide-position';
|
||||||
private cachedSlides: Array<Slide> = [];
|
private cachedSlides: Array<Slide> = [];
|
||||||
|
|
||||||
constructor(private httpClient: HttpClient) {}
|
constructor(private httpClient: HttpClient,
|
||||||
|
private teamService: TeamService,
|
||||||
|
private settings: SettingsService) {}
|
||||||
|
|
||||||
private static prepareSlideData(slide: Slide) {
|
private static prepareSlideData(slide: Slide) {
|
||||||
const slideToSave = <any>Object.assign({}, slide);
|
const slideToSave = <any>Object.assign({}, slide);
|
||||||
@ -28,6 +33,11 @@ export class SlideService implements Resolve<Array<Slide>>{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public list(): Observable<Array<Slide>> {
|
public list(): Observable<Array<Slide>> {
|
||||||
|
if (this.settings.team.id) {
|
||||||
|
return this.teamService.get(this.settings.team.id).pipe(
|
||||||
|
flatMap( () => this.httpClient.get<Array<Slide>>(this.apiEndPoint))
|
||||||
|
);
|
||||||
|
}
|
||||||
return this.httpClient.get<Array<Slide>>(this.apiEndPoint);
|
return this.httpClient.get<Array<Slide>>(this.apiEndPoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -15,7 +15,7 @@ const TIME_SEPARATOR = ':';
|
|||||||
@Injectable()
|
@Injectable()
|
||||||
export class TimerService implements OnDestroy {
|
export class TimerService implements OnDestroy {
|
||||||
public paused = false;
|
public paused = false;
|
||||||
private autoSwitch = false;
|
public autoSwitch = false;
|
||||||
private slideShowTimer: Subscription;
|
private slideShowTimer: Subscription;
|
||||||
private selfUpdateCheckerTimer: Subscription;
|
private selfUpdateCheckerTimer: Subscription;
|
||||||
private slideTimerSubject: Subject<number> = new Subject<number>();
|
private slideTimerSubject: Subject<number> = new Subject<number>();
|
||||||
@ -85,8 +85,8 @@ export class TimerService implements OnDestroy {
|
|||||||
const startsParts = this.settings.team.dailyStartTime.split(TIME_SEPARATOR).map(part => +part);
|
const startsParts = this.settings.team.dailyStartTime.split(TIME_SEPARATOR).map(part => +part);
|
||||||
const endsParts = this.settings.team.dailyEndTime.split(TIME_SEPARATOR).map(part => +part);
|
const endsParts = this.settings.team.dailyEndTime.split(TIME_SEPARATOR).map(part => +part);
|
||||||
const times = [
|
const times = [
|
||||||
new Date(now.getFullYear(), now.getMonth(), now.getDay(), startsParts[0], startsParts[1]),
|
new Date(now.getFullYear(), now.getMonth(), now.getDate(), startsParts[0], startsParts[1]),
|
||||||
new Date(now.getFullYear(), now.getMonth(), now.getDay(), endsParts[0], endsParts[1])
|
new Date(now.getFullYear(), now.getMonth(), now.getDate(), endsParts[0], endsParts[1])
|
||||||
];
|
];
|
||||||
const startsAt = min(...times);
|
const startsAt = min(...times);
|
||||||
const endsAt = max(...times);
|
const endsAt = max(...times);
|
||||||
|
|||||||
23
src/app/shared/slide-wrapper.ts
Executable file
23
src/app/shared/slide-wrapper.ts
Executable file
@ -0,0 +1,23 @@
|
|||||||
|
import { Slide } from './slide';
|
||||||
|
|
||||||
|
export class SlideWrapper {
|
||||||
|
public type: WrappedType = null;
|
||||||
|
public slideData: Slide = null;
|
||||||
|
public slideRoute: string = null;
|
||||||
|
|
||||||
|
public constructor(type: WrappedType, data: string|Slide) {
|
||||||
|
this.type = type;
|
||||||
|
if (type === WrappedType.USER) {
|
||||||
|
this.slideData = data as Slide;
|
||||||
|
} else if (type === WrappedType.BUILTIN) {
|
||||||
|
this.slideRoute = data as string;
|
||||||
|
} else {
|
||||||
|
throw new Error(`Unknown slide type: ${type}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum WrappedType {
|
||||||
|
BUILTIN,
|
||||||
|
USER
|
||||||
|
}
|
||||||
@ -7,7 +7,10 @@ export class Team {
|
|||||||
name: String = '';
|
name: String = '';
|
||||||
members: Array<Member> = [];
|
members: Array<Member> = [];
|
||||||
filterId = 0;
|
filterId = 0;
|
||||||
dailyLockEnabled: false;
|
kanbanEnabled = true;
|
||||||
|
commitTrackerEnabled = true;
|
||||||
|
watchedEnabled = true;
|
||||||
|
dailyLockEnabled = false;
|
||||||
dailyStartTime: string;
|
dailyStartTime: string;
|
||||||
dailyEndTime: string;
|
dailyEndTime: string;
|
||||||
backlogColumn: KanbanColumn = new KanbanColumn();
|
backlogColumn: KanbanColumn = new KanbanColumn();
|
||||||
|
|||||||
116
src/app/shared/two-way-linked-list.ts
Executable file
116
src/app/shared/two-way-linked-list.ts
Executable file
@ -0,0 +1,116 @@
|
|||||||
|
|
||||||
|
export class TwoWayLinkedList<T> {
|
||||||
|
private count = 0;
|
||||||
|
private entryNode: Node<T> = null;
|
||||||
|
private lastNode: Node<T> = null;
|
||||||
|
private nodePtr: Node<T> = null;
|
||||||
|
|
||||||
|
public push(newNode: T): TwoWayLinkedList<T> {
|
||||||
|
if (null === this.lastNode) {
|
||||||
|
this.entryNode = new Node<T>(newNode);
|
||||||
|
this.lastNode = this.entryNode;
|
||||||
|
this.nodePtr = this.entryNode;
|
||||||
|
this.entryNode
|
||||||
|
.setPrev(this.entryNode)
|
||||||
|
.setNext(this.entryNode);
|
||||||
|
} else {
|
||||||
|
const prevPtr = this.lastNode;
|
||||||
|
const nodeToAdd = new Node(newNode);
|
||||||
|
nodeToAdd.setNext(this.entryNode).setPrev(prevPtr);
|
||||||
|
prevPtr.setNext(nodeToAdd);
|
||||||
|
this.lastNode = nodeToAdd;
|
||||||
|
}
|
||||||
|
this.count++;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public pop(): T {
|
||||||
|
if (null === this.lastNode) {
|
||||||
|
throw new Error('No items in list');
|
||||||
|
}
|
||||||
|
|
||||||
|
let returnData: T;
|
||||||
|
if (this.entryNode === this.lastNode) {
|
||||||
|
returnData = this.lastNode.getData();
|
||||||
|
this.lastNode.setPrev(null).setNext(null);
|
||||||
|
this.entryNode = null;
|
||||||
|
this.lastNode = null;
|
||||||
|
this.nodePtr = null;
|
||||||
|
} else {
|
||||||
|
const newLast = this.lastNode.getPrev();
|
||||||
|
if (this.nodePtr === this.lastNode) {
|
||||||
|
this.nodePtr = newLast;
|
||||||
|
}
|
||||||
|
newLast.setNext(this.entryNode);
|
||||||
|
returnData = this.lastNode.getData();
|
||||||
|
this.lastNode.setPrev(null).setNext(null);
|
||||||
|
this.lastNode = newLast;
|
||||||
|
}
|
||||||
|
this.count--;
|
||||||
|
return returnData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public clear() {
|
||||||
|
while (this.count > 0) {
|
||||||
|
this.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public prev(): T {
|
||||||
|
this.nodePtr = this.nodePtr.getPrev();
|
||||||
|
return this.nodePtr.getData();
|
||||||
|
}
|
||||||
|
|
||||||
|
public next(): T {
|
||||||
|
this.nodePtr = this.nodePtr.getNext();
|
||||||
|
return this.nodePtr.getData();
|
||||||
|
}
|
||||||
|
|
||||||
|
public isFirst(): boolean {
|
||||||
|
return this.nodePtr === this.lastNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public isLast(): boolean {
|
||||||
|
return this.nodePtr === this.lastNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get first(): T {
|
||||||
|
return this.entryNode.getData();
|
||||||
|
}
|
||||||
|
|
||||||
|
public get last(): T {
|
||||||
|
return this.lastNode.getData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Node<T> {
|
||||||
|
private prev: Node<T> = null;
|
||||||
|
private next: Node<T> = null;
|
||||||
|
private readonly data: T = null;
|
||||||
|
|
||||||
|
constructor(data: T) {
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getPrev(): Node<T> {
|
||||||
|
return this.prev;
|
||||||
|
}
|
||||||
|
|
||||||
|
public setPrev(node: Node<T>): Node<T> {
|
||||||
|
this.prev = node;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getNext(): Node<T> {
|
||||||
|
return this.next;
|
||||||
|
}
|
||||||
|
|
||||||
|
public setNext(node: Node<T>): Node<T> {
|
||||||
|
this.next = node;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getData(): T {
|
||||||
|
return this.data;
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user