dev@170: /** dev@170: * Created by lucast on 21/03/2017. dev@170: */ dev@231: import { dev@231: ChangeDetectionStrategy, dev@231: Component, dev@231: Input, dev@408: OnDestroy, dev@456: OnInit, dev@456: Output, dev@456: EventEmitter dev@236: } from '@angular/core'; dev@348: import {naivePagingMapper} from '../visualisations/WavesJunk'; dev@408: import {OnSeekHandler} from '../playhead/PlayHeadHelpers'; dev@361: import { dev@381: defaultColourGenerator, dev@460: HigherLevelFeatureShape dev@361: } from '../visualisations/FeatureUtilities'; dev@408: import { dev@408: RenderLoopService, dev@408: TaskRemover dev@408: } from '../services/render-loop/render-loop.service'; dev@456: import {DomSanitizer} from '@angular/platform-browser'; dev@460: import { dev@460: isExtractedAnalysisItem, dev@460: isLoadedRootAudioItem, dev@460: isPendingAnalysisItem, dev@460: isPendingRootAudioItem, dev@460: Item, dev@460: RootAudioItem dev@460: } from './AnalysisItem'; dev@170: dev@170: @Component({ dev@170: selector: 'ugly-analysis-item', dev@170: templateUrl: './analysis-item.component.html', dev@231: styleUrls: ['./analysis-item.component.css'], dev@231: changeDetection: ChangeDetectionStrategy.OnPush dev@170: }) dev@408: export class AnalysisItemComponent implements OnInit, OnDestroy { dev@224: dev@408: // TODO should be TimelineTimeContext? dev@408: @Input() set timeline(timeline: Timeline) { dev@408: this.mTimeline = timeline; dev@408: this.resetRemoveAnimation(); dev@408: } dev@408: dev@408: get timeline(): Timeline { dev@408: return this.mTimeline; dev@408: } dev@408: dev@408: @Input() set isActive(isActive: boolean) { dev@408: this.removeAnimation(); dev@408: this.mIsActive = isActive; dev@408: if (isActive) { dev@408: this.resetRemoveAnimation(); dev@408: } dev@408: } dev@408: dev@408: get isActive() { dev@408: return this.mIsActive; dev@408: } dev@408: dev@350: @Input() item: Item; dev@285: @Input() contentWidth: number; dev@348: @Input() onSeek: OnSeekHandler; dev@456: @Output() remove: EventEmitter; dev@408: // TODO move / re-think - naivePagingMapper feels like a big ol' bodge dev@408: private removeAnimation: TaskRemover; dev@224: private hasProgressOnInit = false; dev@408: private mIsActive: boolean; dev@408: private mTimeline: Timeline; dev@224: dev@456: constructor(private renderLoop: RenderLoopService, dev@456: private sanitizer: DomSanitizer) { dev@456: this.remove = new EventEmitter(); dev@456: } dev@348: dev@224: ngOnInit(): void { dev@408: this.resetRemoveAnimation(); dev@231: this.hasProgressOnInit = this.item.progress != null; dev@224: } dev@224: dev@224: isLoading(): boolean { dev@231: return this.hasProgressOnInit && this.item.progress < 100; dev@224: } dev@348: dev@348: isAudioItem(): boolean { dev@460: return this.item && isLoadedRootAudioItem(this.item); dev@348: } dev@361: dev@410: isPending(): boolean { dev@410: return this.item && dev@460: !isLoadedRootAudioItem(this.item) && !isExtractedAnalysisItem(this.item) && dev@410: (isPendingAnalysisItem(this.item) || isPendingRootAudioItem(this.item)); dev@410: } dev@410: dev@361: getFeatureShape(): HigherLevelFeatureShape | null { dev@361: return !isPendingRootAudioItem(this.item) && dev@460: isExtractedAnalysisItem(this.item) ? this.item.shape : null; dev@361: } dev@381: dev@412: getDuration(): number | null { dev@460: if (isLoadedRootAudioItem(this.item)) { dev@412: return this.item.audioData.duration; dev@412: } dev@460: if (isExtractedAnalysisItem(this.item)) { dev@412: return this.item.parent.audioData.duration; dev@412: } dev@412: } dev@412: dev@381: getNextColour(): string { dev@381: return defaultColourGenerator.next().value; dev@381: } dev@408: dev@408: ngOnDestroy(): void { dev@408: this.removeAnimation(); dev@408: } dev@408: dev@456: private sanitize(url: string) { dev@456: return this.sanitizer.bypassSecurityTrustUrl(url); dev@456: } dev@456: dev@460: private generateFilename(item: RootAudioItem): string { dev@456: // TODO this is too brittle, and will often produce the wrong result dev@456: // i.e. audio/mpeg results in .mpeg, when .mp3 is likely desired dev@456: const mimeParts = item.mimeType ? item.mimeType.split('/') : []; dev@456: const extension = mimeParts.length === 2 ? mimeParts[1] : ''; dev@456: return `${item.title}.${extension}`; dev@456: } dev@456: dev@408: private resetRemoveAnimation(): void { dev@408: if (this.removeAnimation) { dev@408: this.removeAnimation(); dev@408: } dev@408: const createPagingTask = () => { dev@408: const pagingMapper = naivePagingMapper(this.timeline); dev@408: return this.renderLoop.addPlayingTask(currentTime => { dev@408: pagingMapper(currentTime); dev@408: }); dev@408: }; dev@408: // only add a pager to audio items, it can drive the feature items dev@408: const remover = this.timeline && this.isAudioItem() ? dev@408: createPagingTask() : () => {}; dev@408: this.removeAnimation = () => { dev@408: remover(); dev@408: this.removeAnimation = () => {}; dev@408: }; dev@408: } dev@170: }