@flosportsinc/ng-video-autoplay
Version:
Simple way to ensure videos autoplay even if they need to be muted first.
275 lines (271 loc) • 8.42 kB
JavaScript
import { Directive, ElementRef, Renderer2, Input, ContentChildren, NgModule } from '@angular/core';
import { share, map, takeUntil, tap, take, filter, flatMap } from 'rxjs/operators';
import { Subject, fromEvent } from 'rxjs';
import { maybe, either } from 'typescript-monads';
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
// tslint:disable:no-object-mutation
/** @type {?} */
const filterHalted = (/**
* @param {?} source
* @return {?}
*/
(source) => source.pipe(filter((/**
* @param {?} res
* @return {?}
*/
res => res.halted === true))));
/** @type {?} */
const tryPlayVideoAsIs = (/**
* @param {?} source
* @return {?}
*/
(source) => source.pipe(flatMap((/**
* @param {?} videoElement
* @return {?}
*/
videoElement => videoElement.play()
.then((/**
* @return {?}
*/
() => ({ videoElement, halted: false })))
.catch((/**
* @return {?}
*/
() => ({ videoElement, halted: true }))))), filterHalted));
/** @type {?} */
const tryPlayVideoMuted = (/**
* @param {?} source
* @return {?}
*/
(source) => source.pipe(map((/**
* @param {?} res
* @return {?}
*/
res => {
res.videoElement.muted = true;
res.videoElement.volume = 0;
return res.videoElement;
})), tryPlayVideoAsIs))
// 1) attempt to play as-is (including autoplay + unmuted)
// 2) attempt to play muted, showing unmite button
// 3) show click to play
;
// 1) attempt to play as-is (including autoplay + unmuted)
// 2) attempt to play muted, showing unmite button
// 3) show click to play
class FloVideoAutoplayDirective {
/**
* @param {?} elmRef
* @param {?} rd
*/
constructor(elmRef, rd) {
this.elmRef = elmRef;
this.rd = rd;
this.floVideoAutoplay = true;
this.floVideoAutoplayIndex = 0;
this.onDestroySource = new Subject();
this.onDestroy = this.onDestroySource.pipe(share());
this.canExecute = (/**
* @return {?}
*/
() => this.floVideoAutoplay === true || this.floVideoAutoplay === '');
this.maybeVideoElement = (/**
* @return {?}
*/
() => maybe(this.elmRef.nativeElement).filter((/**
* @param {?} e
* @return {?}
*/
e => e.nodeName === 'VIDEO')));
this.maybeUnmuteActionRef = (/**
* @return {?}
*/
() => maybe(this.floVideoAutoplayClickUnmuteRef).filter((/**
* @param {?} a
* @return {?}
*/
a => (/** @type {?} */ (a)) !== '')));
this.maybePlayActionRef = (/**
* @return {?}
*/
() => maybe(this.floVideoAutoplayClickPlayRef).filter((/**
* @param {?} a
* @return {?}
*/
a => (/** @type {?} */ (a)) !== '')));
this.hideRef = (/**
* @param {?} ref
* @return {?}
*/
(ref) => this.rd.setStyle(ref, 'display', 'none'));
this.showRef = (/**
* @param {?} ref
* @return {?}
*/
(ref) => this.rd.setStyle(ref, 'display', 'block'));
this.volumeChange = (/**
* @param {?} videoElement
* @return {?}
*/
(videoElement) => fromEvent(videoElement, 'volumechange', { passive: true }).pipe(map((/**
* @param {?} evt
* @return {?}
*/
evt => (/** @type {?} */ (evt.target)))), share(), takeUntil(this.onDestroy)));
this.initOnVideo = (/**
* @param {?} videoElement
* @return {?}
*/
(videoElement) => fromEvent(videoElement, 'loadstart').pipe(tap((/**
* @return {?}
*/
() => videoElement.setAttribute('autoplay', 'true'))), map((/**
* @param {?} evt
* @return {?}
*/
evt => (/** @type {?} */ (evt.target)))), tryPlayVideoAsIs, tryPlayVideoMuted, take(1)).subscribe((/**
* @param {?} res
* @return {?}
*/
res => {
res.videoElement.preload = 'none';
res.videoElement.muted = false;
res.videoElement.volume = 1;
this.maybePlayActionRef().tapSome(this.showRef);
})));
this.runUnmuteSequence = (/**
* @param {?} actionRef
* @return {?}
*/
(actionRef) => (/**
* @param {?} runOnce
* @return {?}
*/
(runOnce) => (/**
* @param {?} videoElement
* @return {?}
*/
(videoElement) => {
videoElement.setAttribute('autoplay', 'true');
fromEvent(actionRef, 'click').pipe(takeUntil(this.onDestroy)).subscribe((/**
* @return {?}
*/
() => {
videoElement.muted = false;
videoElement.volume = 1;
}));
this.volumeChange(videoElement).pipe(filter((/**
* @param {?} v
* @return {?}
*/
v => !v.muted || v.volume > 0)), takeUntil(this.onDestroy))
.subscribe((/**
* @return {?}
*/
() => this.hideRef(actionRef)));
/** @type {?} */
const showRef = this.volumeChange(videoElement).pipe(filter((/**
* @param {?} v
* @return {?}
*/
v => v.muted || v.volume <= 0)));
(runOnce ? showRef.pipe(take(1)) : showRef).subscribe((/**
* @return {?}
*/
() => this.showRef(actionRef)));
})));
}
/**
* @return {?}
*/
ngAfterContentInit() {
if (!this.canExecute()) {
return;
}
this.maybeVideoElement().tapSome(this.initOnVideo);
this.videos.map((/**
* @param {?} a
* @return {?}
*/
a => a.nativeElement)).forEach(this.initOnVideo);
this.maybeUnmuteActionRef().tapSome((/**
* @param {?} ref
* @return {?}
*/
ref => {
this.hideRef(ref);
either(this.maybeVideoElement().valueOrUndefined(), maybe(this.videos.toArray()[this.floVideoAutoplayIndex]).map((/**
* @param {?} a
* @return {?}
*/
a => a.nativeElement)).valueOrUndefined())
.tap({
left: this.runUnmuteSequence(ref)(false),
right: this.runUnmuteSequence(ref)(true)
});
}));
this.maybePlayActionRef().tapSome((/**
* @param {?} actionRef
* @return {?}
*/
actionRef => {
this.hideRef(actionRef);
fromEvent(actionRef, 'click').pipe(takeUntil(this.onDestroy)).subscribe((/**
* @return {?}
*/
() => {
this.hideRef(actionRef);
this.maybeVideoElement().tapSome((/**
* @param {?} v
* @return {?}
*/
v => v.play()));
}));
}));
}
/**
* @return {?}
*/
ngOnDestroy() {
this.onDestroySource.next();
this.onDestroySource.complete();
}
}
FloVideoAutoplayDirective.decorators = [
{ type: Directive, args: [{
selector: '[floVideoAutoplay]'
},] }
];
/** @nocollapse */
FloVideoAutoplayDirective.ctorParameters = () => [
{ type: ElementRef },
{ type: Renderer2 }
];
FloVideoAutoplayDirective.propDecorators = {
floVideoAutoplay: [{ type: Input }],
floVideoAutoplayClickUnmuteRef: [{ type: Input }],
floVideoAutoplayClickPlayRef: [{ type: Input }],
floVideoAutoplayIndex: [{ type: Input }],
videos: [{ type: ContentChildren, args: ['floVideoAutoplay', { descendants: true },] }]
};
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
class FloVideoAutoplayModule {
}
FloVideoAutoplayModule.decorators = [
{ type: NgModule, args: [{
declarations: [
FloVideoAutoplayDirective
],
exports: [
FloVideoAutoplayDirective
]
},] }
];
export { FloVideoAutoplayModule, FloVideoAutoplayDirective as ɵa };
//# sourceMappingURL=flosportsinc-ng-video-autoplay.js.map