UNPKG

@w11k/ngx-present

Version:

Angular based Presentation Tool

1,920 lines (1,909 loc) 118 kB
import { CommonModule } from '@angular/common'; import { InjectionToken, Injectable, Injector, ɵɵdefineInjectable, ɵɵinject, INJECTOR, Directive, ElementRef, Optional, Input, Pipe, Component, ViewContainerRef, ComponentFactoryResolver, ViewChild, HostListener, ViewEncapsulation, TemplateRef, NgModule, isDevMode } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MatCardModule } from '@angular/material/card'; import { MatDialog, MatDialogModule } from '@angular/material/dialog'; import { MatIconModule } from '@angular/material/icon'; import { MatListModule } from '@angular/material/list'; import { MatSidenav, MatSidenavModule } from '@angular/material/sidenav'; import { MatSlideToggleModule } from '@angular/material/slide-toggle'; import { MatToolbarModule } from '@angular/material/toolbar'; import { Router, RouterLinkWithHref, ActivatedRoute, RouterModule } from '@angular/router'; import { TyduxModule } from '@w11k/tydux-angular'; import { Title } from '@angular/platform-browser'; import { Commands, Facade } from '@w11k/tydux'; import { Observable, Subject, combineLatest, ReplaySubject, fromEvent, of, BehaviorSubject } from 'rxjs'; import { OnDestroyMixin, untilComponentDestroyed } from '@w11k/ngx-componentdestroyed'; import { skipNil, skipPropertyNil } from '@w11k/rx-ninja'; import { filter, takeUntil, take, map, withLatestFrom, delay, first, switchMap } from 'rxjs/operators'; /** * @fileoverview added by tsickle * Generated from: lib/core/utils.ts * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * @template T * @param {?} list * @return {?} */ function maxDepth(list) { if (list.length === 0) { return 0; } /** * @param {?} value * @param {?} currentDepth * @return {?} */ function recursive(value, currentDepth) { return value.reduce((/** * @param {?} depth * @param {?} val * @return {?} */ (depth, val) => { if (Array.isArray(val)) { /** @type {?} */ const newDepth = recursive(val, currentDepth + 1); return max(newDepth, depth); } return depth; }), currentDepth + 1); } return recursive(list, 0); } /** * @param {?} a * @param {?} b * @param {...?} c * @return {?} */ function min(a, b, ...c) { /** @type {?} */ const values = [a, b, ...c]; return values.reduce((/** * @param {?} x * @param {?} y * @return {?} */ (x, y) => x > y ? y : x)); } /** * @param {?} a * @param {?} b * @param {...?} c * @return {?} */ function max(a, b, ...c) { /** @type {?} */ const values = [a, b, ...c]; return values.reduce((/** * @param {?} x * @param {?} y * @return {?} */ (x, y) => x > y ? x : y)); } /** * @template T * @param {?} value * @param {?=} result * @return {?} */ function flattenDeep(value, result = []) { for (const element of value) { if (Array.isArray(element)) { flattenDeep(element, result); } else { result.push(element); } } return result; } /** * @template T, U * @param {?} level0 * @param {?} mapper * @return {?} */ function mapDeep(level0, mapper) { return level0.map((/** * @param {?} level1 * @return {?} */ level1 => { if (Array.isArray(level1)) { return mapDeep(level1, mapper); } else { return mapper(level1); } })); } /** * @template T * @param {?} list * @param {?} predicate * @return {?} */ function filterDeep(list, predicate) { /** @type {?} */ const filtered = []; list.forEach((/** * @param {?} entry * @return {?} */ entry => { if (Array.isArray(entry)) { /** @type {?} */ const nestedFiltered = filterDeep(entry, predicate); if (nestedFiltered.length > 0) { filtered.push(nestedFiltered); } } else if (predicate(entry)) { filtered.push(entry); } })); return filtered; } /** * @template S1, S2 * @param {?} s1 * @param {...?} sources * @return {?} */ function mergeDeep(s1, ...sources) { /** @type {?} */ const target = {}; /** @type {?} */ const s1AndSources = [s1, ...sources]; for (const source of s1AndSources) { if (source === undefined) { continue; } /** @type {?} */ const keys = Object.keys(source); for (const key of keys) { /** @type {?} */ const sourceVal = source[key]; /** @type {?} */ const targetVal = target[key]; /** @type {?} */ const targetIsObj = typeof targetVal === 'object' && Array.isArray(targetVal) === false; /** @type {?} */ const sourceIsObj = typeof sourceVal === 'object' && Array.isArray(sourceVal) === false; if (targetIsObj && sourceIsObj) { /** @type {?} */ const merged = mergeDeep(targetVal, sourceVal); target[key] = merged; } else if (source.hasOwnProperty(key)) { target[key] = sourceVal; } } } return target; } /** * @template T * @param {?} list * @return {?} */ function flattenDelayedWithAnimationFrame(list) { /** @type {?} */ const flatList = flattenDeep(list); /** @type {?} */ const observable = new Observable((/** * @param {?} subscriber * @return {?} */ (subscriber) => { /** @type {?} */ const k = 5; /** @type {?} */ let i = k; /** @type {?} */ const next = (/** * @return {?} */ () => { subscriber.next(flatList.slice(0, i)); i = i + k; if (i > flatList.length) { cancelAnimationFrame(frame); subscriber.complete(); } else { frame = requestAnimationFrame(next); } }); /** @type {?} */ let frame = requestAnimationFrame(next); })); return observable; } /** * @template T * @param {?} list * @param {?} depth * @return {?} */ function limitDepth(list, depth) { if (depth === undefined || depth < 0) { return list; } return recursive(list, depth); /** * @param {?} value * @param {?} depth_ * @return {?} */ function recursive(value, depth_) { if (depth_ === 0) { return []; } return value .map((/** * @param {?} x * @return {?} */ x => { if (Array.isArray(x)) { return recursive(x, depth_ - 1); } return x; })) .filter((/** * @param {?} x * @return {?} */ x => (Array.isArray(x) && x.length === 0) === false)); } } /** * @fileoverview added by tsickle * Generated from: lib/core/presentation.types.ts * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ class Slide { /** * @param {?} component * @param {?} coordinates * @param {?} index */ constructor(component, coordinates, index) { this.component = component; this.coordinates = coordinates; this.index = index; } } if (false) { /** @type {?} */ Slide.prototype.component; /** @type {?} */ Slide.prototype.coordinates; /** @type {?} */ Slide.prototype.index; } /** @type {?} */ const ngxPresentDefaultConfig = { sidebar: { tableOfContent: { enabled: true, showCoordinates: (/** @type {?} */ (undefined)), separator: (/** @type {?} */ (undefined)), depth: (/** @type {?} */ (undefined)) } }, tableOfContent: { showCoordinates: false, separator: ')', depth: (/** @type {?} */ (undefined)), }, coordinates: { separator: '.' }, title: { separator: ' / ' }, navigation: { overview: { component: (/** @type {?} */ (undefined)) } }, presenter: { preview1: { move: 1, coordinatesToKeep: (/** @type {?} */ (undefined)) }, preview2: { move: 2, coordinatesToKeep: (/** @type {?} */ (undefined)) } }, code: { theme: (/** @type {?} */ ('dark')) } }; class PresentationState { constructor() { this.config = ngxPresentDefaultConfig; this.slides = []; this.sideBar = { open: false, expert: false, settings: false }; /** @type {?} */ const id = Math.random().toString(36).substr(2, 9); /** @type {?} */ const chunks = id.match(/.{1,3}/g); if (!chunks) { throw new Error(`ID generation failed. Couldn't generate chunks from random string`); } this.id = chunks.join('-'); } } if (false) { /** @type {?} */ PresentationState.prototype.id; /** @type {?} */ PresentationState.prototype.config; /** @type {?} */ PresentationState.prototype.slides; /** @type {?} */ PresentationState.prototype.sideBar; } class PresentationCommands extends Commands { constructor() { super(); } /** * @param {?} slides * @return {?} */ setSlides(slides) { this.state.slides = slides; } /** * @param {?} config * @return {?} */ mergeConfig(config) { this.state.config = mergeDeep(this.state.config, config); } /** * @return {?} */ toggleSideBar() { this.state.sideBar = Object.assign(Object.assign({}, this.state.sideBar), { open: !this.state.sideBar.open }); } /** * @return {?} */ closeSideBar() { this.state.sideBar = Object.assign(Object.assign({}, this.state.sideBar), { open: false }); } /** * @return {?} */ openSideBar() { this.state.sideBar = Object.assign(Object.assign({}, this.state.sideBar), { open: true }); } /** * @return {?} */ enableSideBarExpertMode() { this.state.sideBar = Object.assign(Object.assign({}, this.state.sideBar), { expert: true, settings: true }); } /** * @param {?} id * @return {?} */ setId(id) { this.state.id = id; } /** * @param {?} theme * @return {?} */ setCodeTheme(theme) { this.state.config = Object.assign(Object.assign({}, this.state.config), { code: Object.assign(Object.assign({}, this.state.config.code), { theme: theme }) }); } } /** * @fileoverview added by tsickle * Generated from: lib/core/presentation.functions.ts * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * @param {?} component * @param {?} coordinates * @param {?} index * @return {?} */ function componentToSlide(component, coordinates, index) { return new Slide(component, coordinates, index); } /** * @param {?} components * @param {?} coordinates * @param {?} counter * @return {?} */ function componentsToSlidesRecursive(components, coordinates, counter) { if (Array.isArray(components)) { return components.map((/** * @param {?} x * @param {?} i * @return {?} */ (x, i) => { /** @type {?} */ const newCoordinates = coordinates.slice(); newCoordinates.push(i + 1); return componentsToSlidesRecursive(x, newCoordinates, counter); })); } counter.index++; return componentToSlide(components, coordinates, counter.index); } /** * @param {?} slideComponents * @return {?} */ function componentsToSlideTree(slideComponents) { /** @type {?} */ const counter = { index: -1 }; return slideComponents.map((/** * @param {?} x * @param {?} i * @return {?} */ (x, i) => componentsToSlidesRecursive(x, [i + 1], counter))); } /** * @fileoverview added by tsickle * Generated from: lib/core/presentation.service.ts * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** @type {?} */ const NGX_PRESENT_CONFIG = new InjectionToken('NgxPresentConfig'); /** @type {?} */ const SLIDES = new InjectionToken('SLIDES'); class PresentationService extends Facade { /** * @param {?} injector */ constructor(injector) { super('Presentation', new PresentationState(), new PresentationCommands()); // make mutate public this.dispatch = this.commands; /** @type {?} */ const slideComponents = injector.get(SLIDES); /** @type {?} */ const config = injector.get(NGX_PRESENT_CONFIG); /** @type {?} */ const slides = componentsToSlideTree(slideComponents); this.commands.setSlides(slides); this.commands.mergeConfig(config); } /** * @param {?} event * @return {?} */ toggleSideBar(event) { if (event.altKey) { this.commands.enableSideBarExpertMode(); } this.commands.toggleSideBar(); } } PresentationService.decorators = [ { type: Injectable, args: [{ providedIn: 'root' },] } ]; /** @nocollapse */ PresentationService.ctorParameters = () => [ { type: Injector } ]; /** @nocollapse */ PresentationService.ɵprov = ɵɵdefineInjectable({ factory: function PresentationService_Factory() { return new PresentationService(ɵɵinject(INJECTOR)); }, token: PresentationService, providedIn: "root" }); if (false) { /** @type {?} */ PresentationService.prototype.dispatch; } /** * @fileoverview added by tsickle * Generated from: lib/core/title.service.ts * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ class AdvancedTitleService { /** * @param {?} title * @param {?} presentation */ constructor(title, presentation) { this.title = title; this.presentation = presentation; this.original = title.getTitle(); } /** * @param {?} newTitle * @return {?} */ setTitle(newTitle) { /** @type {?} */ const oldTitle = this.title.getTitle(); this.title.setTitle(newTitle); this.lastReturnedUnset = ((/** * @return {?} */ () => { /** @type {?} */ const unset = (/** * @return {?} */ () => { if (this.lastReturnedUnset === unset) { this.title.setTitle(oldTitle); } }); return unset; }))(); return this.lastReturnedUnset; } /** * @param {?} prefix * @param {?=} separator * @return {?} */ prefixTitle(prefix, separator = this.presentation.state.config.title.separator) { return this.setTitle(prefix + separator + this.original); } } AdvancedTitleService.decorators = [ { type: Injectable, args: [{ providedIn: 'root' },] } ]; /** @nocollapse */ AdvancedTitleService.ctorParameters = () => [ { type: Title }, { type: PresentationService } ]; /** @nocollapse */ AdvancedTitleService.ɵprov = ɵɵdefineInjectable({ factory: function AdvancedTitleService_Factory() { return new AdvancedTitleService(ɵɵinject(Title), ɵɵinject(PresentationService)); }, token: AdvancedTitleService, providedIn: "root" }); if (false) { /** * @type {?} * @private */ AdvancedTitleService.prototype.lastReturnedUnset; /** * @type {?} * @private */ AdvancedTitleService.prototype.original; /** * @type {?} * @private */ AdvancedTitleService.prototype.title; /** * @type {?} * @private */ AdvancedTitleService.prototype.presentation; } /** * @fileoverview added by tsickle * Generated from: lib/core/page-title.directive.ts * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ class PageTitleDirective { /** * @param {?} title * @param {?} element */ constructor(title, element) { this.title = title; this.element = element; } /** * @return {?} */ ngAfterViewInit() { /** @type {?} */ const pageTitle = this.element.nativeElement.innerText; this.unsetTitle = this.title.prefixTitle(pageTitle); this.element.nativeElement.hidden = true; } /** * @return {?} */ ngOnDestroy() { if (this.unsetTitle) { this.unsetTitle(); } } } PageTitleDirective.decorators = [ { type: Directive, args: [{ selector: '[ngxPresentPageTitle]' },] } ]; /** @nocollapse */ PageTitleDirective.ctorParameters = () => [ { type: AdvancedTitleService }, { type: ElementRef } ]; if (false) { /** @type {?} */ PageTitleDirective.prototype.unsetTitle; /** * @type {?} * @private */ PageTitleDirective.prototype.title; /** * @type {?} * @private */ PageTitleDirective.prototype.element; } /** * @fileoverview added by tsickle * Generated from: lib/core/event.service.ts * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * @param {?} event * @return {?} */ function nonNavigationEvent(event) { return noModifierPressed(event) && isNotEditable(event); } /** * @param {?} event * @return {?} */ function isNotEditable(event) { /** @type {?} */ const srcElement = event.srcElement; if (srcElement instanceof HTMLElement) { /** @type {?} */ const tagName = srcElement.tagName; if (tagName === 'INPUT' || tagName === 'TEXTAREA' || srcElement.isContentEditable) { return false; } } return true; } /** * @param {?} event * @return {?} */ function noModifierPressed(event) { if (event.altKey || event.ctrlKey || event.shiftKey || event.metaKey) { return false; } return true; } /** * @abstract */ class KeyboardEventProcessor { } if (false) { /** * @abstract * @param {?} events$ * @return {?} */ KeyboardEventProcessor.prototype.init = function (events$) { }; } class ToggleSideNav { /** * @param {?} service */ constructor(service) { this.service = service; } /** * @param {?} events$ * @return {?} */ init(events$) { events$ .pipe(filter(isNotEditable), filter((/** * @param {?} event * @return {?} */ event => !(event.ctrlKey || event.metaKey || event.shiftKey))), // letter m filter((/** * @param {?} event * @return {?} */ event => event.keyCode === 77))) .subscribe((/** * @param {?} event * @return {?} */ event => { event.preventDefault(); this.service.toggleSideBar(event); })); } } ToggleSideNav.decorators = [ { type: Injectable } ]; /** @nocollapse */ ToggleSideNav.ctorParameters = () => [ { type: PresentationService } ]; if (false) { /** * @type {?} * @private */ ToggleSideNav.prototype.service; } /** @type {?} */ const KEYBOARD_EVENT_PROCESSOR_TOKEN = new InjectionToken('KEYBOARD_EVENT_PROCESSORS'); class EventService { /** * @param {?} injector */ constructor(injector) { this.keyboardEvents$ = new Subject(); /** @type {?} */ const keyboardEventProcessors = injector.get(KEYBOARD_EVENT_PROCESSOR_TOKEN); for (const processor of keyboardEventProcessors) { processor.init(this.keyboardEvents$); } } /** * @param {?} event * @return {?} */ processKeyboardEvent(event) { // console.debug('EventService: emit keyboard event'); this.keyboardEvents$.next(event); } } EventService.decorators = [ { type: Injectable, args: [{ providedIn: 'root' },] } ]; /** @nocollapse */ EventService.ctorParameters = () => [ { type: Injector } ]; /** @nocollapse */ EventService.ɵprov = ɵɵdefineInjectable({ factory: function EventService_Factory() { return new EventService(ɵɵinject(INJECTOR)); }, token: EventService, providedIn: "root" }); if (false) { /** * @type {?} * @private */ EventService.prototype.keyboardEvents$; } /** * @fileoverview added by tsickle * Generated from: lib/theming/table-of-content.ts * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** @type {?} */ const tableOfContentMetadataKey = Symbol('TableOfContentEntry'); /** * @record */ function DecoratorMetadata() { } if (false) { /** @type {?} */ DecoratorMetadata.prototype.linkName; } // Decorator /** * @param {?} config * @return {?} */ function TableOfContentEntry(config) { return (/** * @param {?} constructor * @return {?} */ function (constructor) { // TODO: get rid of cast to any, include proper Reflect typings ((/** @type {?} */ (Reflect))).defineMetadata(tableOfContentMetadataKey, config, constructor); }); } /** * @param {?} slide * @return {?} */ function tableOfContentMetadata(slide) { /** @type {?} */ const decoratorMetadata = ((/** @type {?} */ (Reflect))).getMetadata(tableOfContentMetadataKey, slide.component); return decoratorMetadata; } /** * @param {?} slides * @return {?} */ function tableOfContentSlides(slides) { return slides.filter((/** * @param {?} slide * @return {?} */ slide => tableOfContentMetadata(slide) !== undefined)); } /** * @fileoverview added by tsickle * Generated from: lib/slide-by-slide/slide-by-slide.functions.ts * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * @param {?} slides * @param {?} currentSlide * @param {?} move * @param {?} coordinatesToKeep * @param {?} coordinatesMaxDepth * @return {?} */ function calculateCoordinates(slides, currentSlide, move, coordinatesToKeep, coordinatesMaxDepth) { if (move === 0) { return currentSlide; } if (coordinatesToKeep === undefined) { return slides[currentSlide.index + move]; } /** @type {?} */ const currentCoordinates = currentSlide.coordinates; /** @type {?} */ const coordinatesToKeepAbs = coordinatesToKeepAbsolute(currentCoordinates, coordinatesToKeep); /** @type {?} */ const newCoordinates = currentCoordinates.slice(0, coordinatesToKeepAbs); if (move > 0 && coordinatesToKeepAbs < currentCoordinates.length) { newCoordinates.push(currentCoordinates[coordinatesToKeepAbs] + move); } else if (move > 0) { newCoordinates.push(move); } else if (move < 0 && currentCoordinates[coordinatesToKeepAbs + 1] !== undefined && currentCoordinates[coordinatesToKeepAbs + 1] > 1) { newCoordinates.push(currentCoordinates[coordinatesToKeepAbs] + move + 1); } else if (move < 0 && coordinatesToKeepAbs < currentCoordinates.length) { newCoordinates.push(max(1, currentCoordinates[coordinatesToKeepAbs] + move)); } else if (move < 0) { newCoordinates.push(1); } if (move < 0) { while (newCoordinates.length <= coordinatesMaxDepth) { newCoordinates.push(1); } } /** @type {?} */ let arrayToSearchIn; if (move >= 0) { arrayToSearchIn = slides.slice(currentSlide.index + 1); } else { arrayToSearchIn = slides.slice(0, currentSlide.index).reverse(); } /** @type {?} */ const nextSlide = arrayToSearchIn.find((/** * @param {?} slide * @return {?} */ slide => { /** @type {?} */ const compared = compareCoordinates(slide.coordinates, newCoordinates); if (move < 0) { return compared <= 0; } else { return compared >= 0; } })); return nextSlide; } /** * @param {?} coordinates * @param {?} coordinatesToKeepRelative * @return {?} */ function coordinatesToKeepAbsolute(coordinates, coordinatesToKeepRelative) { if (coordinatesToKeepRelative >= 0) { return min(coordinatesToKeepRelative, coordinates.length); } return max(coordinates.length + coordinatesToKeepRelative, 0); } /** @type {?} */ const routerParamsCoordinatePrefix = 'coordinate-'; /** * @param {?} routeParams * @return {?} */ function routeParamsToCoordinate(routeParams) { /** @type {?} */ const keys = Object.keys(routeParams); /** @type {?} */ const coordinates = keys .filter((/** * @param {?} key * @return {?} */ key => key.startsWith(routerParamsCoordinatePrefix))) .map((/** * @param {?} key * @return {?} */ key => { /** @type {?} */ const keyNumber = parseInt(key.substr(routerParamsCoordinatePrefix.length), 10); return { key, keyNumber }; })) .sort((/** * @param {?} a * @param {?} b * @return {?} */ (a, b) => compareNumber(a.keyNumber, b.keyNumber))) .map((/** * @param {?} key * @return {?} */ key => parseInt(routeParams[key.key], 10))); return coordinates; } /** * @param {?} a * @param {?} b * @return {?} */ function compareNumber(a, b) { if (a !== undefined && b === undefined) { return 1; } if (a === undefined && b !== undefined) { return -1; } if (a === undefined && b === undefined) { return 0; } if (a !== undefined && b !== undefined && a > b) { return 1; } if (a !== undefined && b !== undefined && a < b) { return -1; } return 0; } /** * @param {?} c1 * @param {?} c2 * @return {?} */ function equalCoordinates(c1, c2) { return compareCoordinates(c1, c2) === 0; } /** * @param {?} c1 * @param {?} c2 * @return {?} */ function compareCoordinates(c1, c2) { if (c1 === c2) { return 0; } if (c1 === null || c1 === undefined) { return -1; } if (c2 === null || c2 === undefined) { return 1; } /** @type {?} */ const maxLength = max(c1.length, c2.length); for (let i = 0; i < maxLength; i++) { /** @type {?} */ const c1i = c1[i]; /** @type {?} */ const c2i = c2[i]; /** @type {?} */ const iResult = compareNumber(c1i, c2i); if (iResult !== 0) { return iResult; } } return 0; } /** * @param {?} slides * @param {?} coordinates * @return {?} */ function isValidCoordinate(slides, coordinates) { if (coordinates.length === 0) { return false; } /** @type {?} */ let current = slides; for (const coordinate of coordinates) { /** @type {?} */ const tooLow = coordinate < 1; /** @type {?} */ const tooHigh = Array.isArray(current) && coordinate - 1 >= current.length; if (tooLow || tooHigh) { return false; } /** @type {?} */ const next = current[coordinate - 1]; if (Array.isArray(next)) { current = next; } else { current = []; } } return true; } /** * @param {?} coordinates * @param {?} separator * @param {?=} length * @return {?} */ function coordinatesToString(coordinates, separator, length) { return coordinates.slice(0, length).join(separator); } /** * @param {?} slides * @return {?} */ function coordinateToSlideMap(slides) { /** @type {?} */ const map = {}; for (const slide of slides) { /** @type {?} */ const index = coordinatesToString(slide.coordinates, '.'); map[index] = slide; } return map; } /** * @fileoverview added by tsickle * Generated from: lib/slide-by-slide/slide-by-slide.service.ts * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ class SlideBySlideState { constructor() { this.coordinatesMaxDepth = 0; this.slides = []; this.slideMap = {}; } } if (false) { /** @type {?} */ SlideBySlideState.prototype.coordinatesMaxDepth; /** @type {?} */ SlideBySlideState.prototype.slides; /** @type {?} */ SlideBySlideState.prototype.slideMap; /** @type {?} */ SlideBySlideState.prototype.currentSlide; /** @type {?} */ SlideBySlideState.prototype.currentMode; } class SlideBySlideMutator extends Commands { constructor() { super(); } /** * @param {?} slide * @return {?} */ setCurrentSlide(slide) { this.state.currentSlide = slide; } /** * @param {?} mode * @return {?} */ setCurrentMode(mode) { this.state.currentMode = mode; } /** * @param {?} slides * @return {?} */ setSlides(slides) { this.state.coordinatesMaxDepth = maxDepth(slides); this.state.slides = flattenDeep(slides); this.state.slideMap = coordinateToSlideMap(this.state.slides); } } class SlideBySlideService extends Facade { /** * @param {?} injector * @param {?} presentation * @param {?} router */ constructor(injector, presentation, router) { super('SlideBySlide', new SlideBySlideState(), new SlideBySlideMutator()); this.presentation = presentation; this.router = router; this.onDestroy$ = new Subject(); this.presentation.select((/** * @param {?} state * @return {?} */ state => state.slides)) .pipe(takeUntil(this.onDestroy$)) .subscribe((/** * @param {?} slides * @return {?} */ slides => this.commands.setSlides(slides))); } /** * @param {?} coordinatesToKeep * @param {?=} mode * @return {?} */ navigateToNext(coordinatesToKeep, mode) { this.nextSlide(coordinatesToKeep) .pipe(take(1)) .subscribe((/** * @param {?} slide * @return {?} */ slide => this.navigateAbsolute(slide, mode))); } /** * @param {?} coordinatesToKeep * @param {?=} mode * @return {?} */ navigateToPrevious(coordinatesToKeep, mode) { this.previousSlide(coordinatesToKeep) .pipe(take(1)) .subscribe((/** * @param {?} slide * @return {?} */ slide => this.navigateAbsolute(slide, mode))); } /** * @param {?} coordinatesToKeep * @param {?=} prefix * @return {?} */ previousSlide(coordinatesToKeep, prefix) { return this.navigateRelative(-1, coordinatesToKeep); } /** * @param {?} coordinatesToKeep * @return {?} */ nextSlide(coordinatesToKeep) { return this.navigateRelative(1, coordinatesToKeep); } /** * @param {?=} mode * @return {?} */ navigateToNextToc(mode) { this.nextToc('forward') .pipe(take(1)) .subscribe((/** * @param {?} slide * @return {?} */ slide => this.navigateAbsolute(slide, mode))); } /** * @param {?=} mode * @return {?} */ navigateToPreviousToc(mode) { this.nextToc('backward') .pipe(take(1)) .subscribe((/** * @param {?} slide * @return {?} */ slide => this.navigateAbsolute(slide, mode))); } /** * @param {?} direction * @return {?} */ nextToc(direction) { /** @type {?} */ const currentSlide$ = this.select((/** * @param {?} state * @return {?} */ state => state.currentSlide)) .pipe(skipNil()); /** @type {?} */ const tocSlides$ = this.select((/** * @param {?} state * @return {?} */ state => state.slides)) .pipe(skipNil(), filter((/** * @param {?} x * @return {?} */ x => x.length !== 0)), map(tableOfContentSlides)); return combineLatest(currentSlide$, tocSlides$) .pipe(map((/** * @param {?} __0 * @return {?} */ ([currentSlide, tocSlides]) => { if (direction === 'forward') { return tocSlides.find((/** * @param {?} tocSlide * @return {?} */ tocSlide => { return compareCoordinates(tocSlide.coordinates, currentSlide.coordinates) === 1; })); } else { return tocSlides.slice().reverse().find((/** * @param {?} tocSlide * @return {?} */ tocSlide => { return compareCoordinates(tocSlide.coordinates, currentSlide.coordinates) === -1; })); } }))); } /** * @param {?} move * @param {?} coordinatesToKeep * @return {?} */ navigateRelative(move, coordinatesToKeep) { /** @type {?} */ const currentSlide$ = this.select((/** * @param {?} state * @return {?} */ state => state.currentSlide)) .pipe(skipNil()); /** @type {?} */ const slides$ = this.select((/** * @param {?} state * @return {?} */ state => state.slides)) .pipe(skipNil(), filter((/** * @param {?} x * @return {?} */ x => x.length !== 0))); /** @type {?} */ const depth$ = this.select((/** * @param {?} state * @return {?} */ state => state.coordinatesMaxDepth)) .pipe(skipNil()); return combineLatest(slides$, currentSlide$, depth$) .pipe(map((/** * @param {?} __0 * @return {?} */ ([slides, current, depth]) => calculateCoordinates(slides, current, move, coordinatesToKeep, depth)))); } /** * @param {?} target * @param {?=} mode * @return {?} */ navigateAbsolute(target, mode) { /** @type {?} */ let slide; if (target instanceof Slide) { slide = target; } else { slide = this.state.slides.find((/** * @param {?} x * @return {?} */ x => equalCoordinates(target, x.coordinates))); } if (slide === undefined) { return; } /** @type {?} */ let modeWithFallback; if (mode !== undefined) { modeWithFallback = mode; } else if (this.state.currentMode !== undefined) { modeWithFallback = this.state.currentMode; } else { modeWithFallback = 'slide'; } /** @type {?} */ const link = [`/${modeWithFallback}`, ...slide.coordinates]; return this.router.navigate(link, { queryParamsHandling: 'merge' }); } /** * @param {?=} prefix * @return {?} */ navigateToFirst(prefix) { this.firstSlide() .pipe(take(1), takeUntil(this.onDestroy$)) .subscribe((/** * @param {?} slide * @return {?} */ slide => this.navigateAbsolute(slide, prefix))); } /** * @return {?} */ firstSlide() { return this.select((/** * @param {?} state * @return {?} */ state => state.slides)) .pipe(skipNil(), filter((/** * @param {?} slides * @return {?} */ slides => slides.length > 0)), map((/** * @param {?} slides * @return {?} */ slides => slides[0]))); } /** * @param {?} coordinates * @return {?} */ isValidCoordinate(coordinates) { return this.presentation.select((/** * @param {?} state * @return {?} */ state => state.slides)) .pipe(skipNil(), filter((/** * @param {?} slides * @return {?} */ slides => slides.length > 0)), map((/** * @param {?} slides * @return {?} */ slides => isValidCoordinate(slides, coordinates)))); } /** * @private * @param {?} coordinates * @return {?} */ coordinatesToSlide(coordinates) { return this.state.slideMap[coordinates.join('.')]; } /** * @param {?} route * @return {?} */ setCurrentModeAndSlide(route) { /** @type {?} */ const coordinates = routeParamsToCoordinate(route.params); /** @type {?} */ const mode = (/** @type {?} */ (route.url[0].path)); /** @type {?} */ const slide = this.coordinatesToSlide(coordinates); this.commands.setCurrentSlide(slide); this.commands.setCurrentMode(mode); } /** * @return {?} */ ngOnDestroy() { this.onDestroy$.next(); } } SlideBySlideService.decorators = [ { type: Injectable, args: [{ providedIn: 'root' },] } ]; /** @nocollapse */ SlideBySlideService.ctorParameters = () => [ { type: Injector }, { type: PresentationService }, { type: Router } ]; /** @nocollapse */ SlideBySlideService.ɵprov = ɵɵdefineInjectable({ factory: function SlideBySlideService_Factory() { return new SlideBySlideService(ɵɵinject(INJECTOR), ɵɵinject(PresentationService), ɵɵinject(Router)); }, token: SlideBySlideService, providedIn: "root" }); if (false) { /** * @type {?} * @private */ SlideBySlideService.prototype.onDestroy$; /** * @type {?} * @private */ SlideBySlideService.prototype.presentation; /** * @type {?} * @private */ SlideBySlideService.prototype.router; } class NavigateSectionForward { /** * @param {?} service */ constructor(service) { this.service = service; } /** * @param {?} events$ * @return {?} */ init(events$) { events$ .pipe(filter(isNotEditable), // arrow down + alt || arrow right + alt filter((/** * @param {?} event * @return {?} */ event => { if (event.keyCode === 40 && event.altKey) { return true; } else if (event.keyCode === 39 && event.altKey) { return true; } return false; }))) .subscribe((/** * @return {?} */ () => { this.service.navigateToNextToc(); })); } } NavigateSectionForward.decorators = [ { type: Injectable } ]; /** @nocollapse */ NavigateSectionForward.ctorParameters = () => [ { type: SlideBySlideService } ]; if (false) { /** * @type {?} * @private */ NavigateSectionForward.prototype.service; } class NavigateSlideForward { /** * @param {?} service */ constructor(service) { this.service = service; } /** * @param {?} events$ * @return {?} */ init(events$) { events$ .pipe(filter(nonNavigationEvent), // arrow down, arrow right, or page down filter((/** * @param {?} event * @return {?} */ event => event.keyCode === 40 || event.keyCode === 39 || event.keyCode === 34))) .subscribe((/** * @return {?} */ () => { this.service.navigateToNext(-1); })); } } NavigateSlideForward.decorators = [ { type: Injectable } ]; /** @nocollapse */ NavigateSlideForward.ctorParameters = () => [ { type: SlideBySlideService } ]; if (false) { /** * @type {?} * @private */ NavigateSlideForward.prototype.service; } class NavigateSectionBackward { /** * @param {?} service */ constructor(service) { this.service = service; } /** * @param {?} events$ * @return {?} */ init(events$) { events$ .pipe(filter(isNotEditable), // arrow up + alt || arrow left + alt filter((/** * @param {?} event * @return {?} */ event => { if (event.keyCode === 38 && event.altKey) { return true; } else if (event.keyCode === 37 && event.altKey) { return true; } return false; }))) .subscribe((/** * @return {?} */ () => { this.service.navigateToPreviousToc(); })); } } NavigateSectionBackward.decorators = [ { type: Injectable } ]; /** @nocollapse */ NavigateSectionBackward.ctorParameters = () => [ { type: SlideBySlideService } ]; if (false) { /** * @type {?} * @private */ NavigateSectionBackward.prototype.service; } class NavigateSlideBackward { /** * @param {?} service */ constructor(service) { this.service = service; } /** * @param {?} events$ * @return {?} */ init(events$) { events$ .pipe(filter(nonNavigationEvent), // arrow up, arrow left or page up filter((/** * @param {?} event * @return {?} */ event => event.keyCode === 38 || event.keyCode === 37 || event.keyCode === 33))) .subscribe((/** * @return {?} */ () => { this.service.navigateToPrevious(-1); })); } } NavigateSlideBackward.decorators = [ { type: Injectable } ]; /** @nocollapse */ NavigateSlideBackward.ctorParameters = () => [ { type: SlideBySlideService } ]; if (false) { /** * @type {?} * @private */ NavigateSlideBackward.prototype.service; } class NavigateToFirstSlide { /** * @param {?} service */ constructor(service) { this.service = service; } /** * @param {?} events$ * @return {?} */ init(events$) { events$ .pipe(filter(nonNavigationEvent), // pos 1 filter((/** * @param {?} event * @return {?} */ event => event.keyCode === 36))) .subscribe((/** * @return {?} */ () => { this.service.navigateToFirst(); })); } } NavigateToFirstSlide.decorators = [ { type: Injectable } ]; /** @nocollapse */ NavigateToFirstSlide.ctorParameters = () => [ { type: SlideBySlideService } ]; if (false) { /** * @type {?} * @private */ NavigateToFirstSlide.prototype.service; } class NavigateToOverview { /** * @param {?} service * @param {?} presentation */ constructor(service, presentation) { this.service = service; this.presentation = presentation; } /** * @param {?} events$ * @return {?} */ init(events$) { /** @type {?} */ const config$ = this.presentation.select((/** * @param {?} state * @return {?} */ state => state.config.navigation.overview)) .pipe(skipPropertyNil('component')); /** @type {?} */ const slide$ = this.service.select() .pipe(withLatestFrom(config$), map((/** * @param {?} __0 * @return {?} */ ([state, config]) => state.slides.find((/** * @param {?} slide * @return {?} */ slide => slide.component === config.component))))); events$ .pipe(filter(nonNavigationEvent), // pos 1 filter((/** * @param {?} event * @return {?} */ event => { // o return event.keyCode === 79; })), withLatestFrom(slide$)) .subscribe((/** * @param {?} __0 * @return {?} */ ([event, slide]) => { this.service.navigateAbsolute(slide); })); } } NavigateToOverview.decorators = [ { type: Injectable } ]; /** @nocollapse */ NavigateToOverview.ctorParameters = () => [ { type: SlideBySlideService }, { type: PresentationService } ]; if (false) { /** * @type {?} * @private */ NavigateToOverview.prototype.service; /** * @type {?} * @private */ NavigateToOverview.prototype.presentation; } class TogglePresenter { /** * @param {?} service * @param {?} router */ constructor(service, router) { this.service = service; this.router = router; } /** * @param {?} events$ * @return {?} */ init(events$) { /** @type {?} */ const slide$ = this.service.select(); events$ .pipe(filter(isNotEditable), // letter p filter((/** * @param {?} event * @return {?} */ event => event.keyCode === 80 && event.altKey)), withLatestFrom(slide$)) .subscribe((/** * @param {?} __0 * @return {?} */ ([event, state]) => { /** @type {?} */ let mode; if (state.currentMode === 'presenter') { mode = 'slide'; } else { mode = 'presenter'; } /** @type {?} */ let coordinates = []; if (state.currentSlide !== undefined) { coordinates = state.currentSlide.coordinates; } /** @type {?} */ const link = [mode, ...coordinates]; this.router.navigate(link, { queryParamsHandling: 'merge' }); })); } } TogglePresenter.decorators = [ { type: Injectable } ]; /** @nocollapse */ TogglePresenter.ctorParameters = () => [ { type: SlideBySlideService }, { type: Router } ]; if (false) { /** * @type {?} * @private */ TogglePresenter.prototype.service; /** * @type {?} * @private */ TogglePresenter.prototype.router; } /** * @fileoverview added by tsickle * Generated from: lib/core/slide-link.directive.ts * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * @param {?} event * @return {?} */ function isMouseEvent(event) { return event instanceof MouseEvent; } /** * Use in conjunction with routerLink on an anchor tag for real links. * Use on any tag for click event handling. */ class SlideLinkDirective extends OnDestroyMixin { /** * @param {?} service * @param {?} router * @param {?} element * @param {?} routerLinkDirective */ constructor(service, router, element, routerLinkDirective) { super(); this.service = service; this.router = router; this.element = element; this.routerLinkDirective = routerLinkDirective; this.slide$ = new ReplaySubject(1); if (routerLinkDirective) { this.slide$ .pipe(untilComponentDestroyed(this)) .subscribe((/** * @param {?} slide * @return {?} */ slide => { /** @type {?} */ let link; if (slide) { /** @type {?} */ const mode = `/${this.service.state.currentMode}`; link = ([mode, ...slide.coordinates]); } else { link = []; } this.routerLinkDirective.routerLink = link; // update href on link this.routerLinkDirective.ngOnChanges({}); })); } fromEvent(element.nativeElement, 'click') .pipe(filter(isMouseEvent), filter((/** * @param {?} event * @return {?} */ event => event.altKey)), withLatestFrom(this.slide$), untilComponentDestroyed(this)) .subscribe((/** * @param {?} __0 * @return {?} */ ([event, slide]) => { event.preventDefault(); /** @type {?} */ const link = (['/presenter', ...slide.coordinates]); this.router.navigate(link, { queryParamsHandling: 'merge' }); })); } /** * @param {?} component * @return {?} */ set ngxPresentSlideLink(component) { this.service.select(((/** * @param {?} state * @return {?} */ state => state.slides))) .pipe(skipNil(), untilComponentDestroyed(this)) .pipe(map((/** * @param {?} slides * @return {?} */ slide