diff --git a/CHANGELOG.md b/CHANGELOG.md
index d6dd944..7fa2486 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,15 @@
# Changelog for apl-viewhost-web
+## [2023.1]
+This release adds support for version 2023.1 of the APL specification. Please also see APL Core Library for changes: [apl-core-library CHANGELOG](https://github.com/alexa/apl-core-library/blob/master/CHANGELOG.md)
+
+### Added
+- SRT support for APL Video textTrack
+- Support for new Porter-Duff blend modes
+
+### Changed
+- Bug fixes
+
## [2022.2]
This release adds support for version 2022.2 of the APL specification. Please also see APL Core Library for changes: [apl-core-library CHANGELOG](https://github.com/alexa/apl-core-library/blob/master/CHANGELOG.md)
diff --git a/README.md b/README.md
index 2a48cdf..57fe159 100644
--- a/README.md
+++ b/README.md
@@ -1,10 +1,10 @@
# Alexa Presentation Language (APL) Viewhost Web
-
-
-
-
+
+
+
+
## Introduction
@@ -16,7 +16,7 @@ platform or framework for which the view host was designed by leveraging the fun
### Prerequisites
-* [NodeJS](https://nodejs.org/en/) - version 10.x or higher
+* [NodeJS](https://nodejs.org/en/) - version 16.x or higher
* [cmake](https://cmake.org/install/) - the easiest way to install on Mac is using `brew install cmake`
* [Yarn](https://yarnpkg.com/getting-started/install)
diff --git a/js/apl-html/package.json b/js/apl-html/package.json
index ef407ac..b2d3296 100644
--- a/js/apl-html/package.json
+++ b/js/apl-html/package.json
@@ -1,5 +1,5 @@
{
- "name": "apl-html",
+ "name": "@amzn/apl-html",
"version": "1.0.0",
"license": "SEE LICENSE IN LICENSE.txt",
"main": "lib/index.js",
@@ -52,5 +52,8 @@
"webpack-cli": "^3.3.12",
"webpack-merge": "^4.2.1",
"xregexp": "4.2.4"
+ },
+ "npm-pretty-much": {
+ "legacyPackageNameAlias": "apl-html"
}
}
diff --git a/js/apl-html/src/CommandFactory.ts b/js/apl-html/src/CommandFactory.ts
new file mode 100644
index 0000000..fb0543a
--- /dev/null
+++ b/js/apl-html/src/CommandFactory.ts
@@ -0,0 +1,83 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import APLRenderer from './APLRenderer';
+import { EventType } from './enums/EventType';
+import { DataSourceFetchRequest } from './events/DataSourceFetchRequest';
+import { ExtensionEvent } from './events/ExtensionEvent';
+import { Finish } from './events/Finish';
+import { Focus } from './events/Focus';
+import { LineHighlight } from './events/LineHighlight';
+import { MediaRequest } from './events/MediaRequest';
+import { OpenUrl } from './events/OpenUrl';
+import { ReInflate} from './events/ReInflate';
+import { RequestLineBounds } from './events/RequestLineBounds';
+import { SendEvent } from './events/SendEvent';
+
+/**
+ * Creates and executes a command
+ * @param event The core engine event
+ * @param renderer A reference to the renderer instance
+ * @internal
+ */
+export const commandFactory = (event: APL.Event, renderer: APLRenderer) => {
+ if (factoryMap[event.getType()]) {
+ return factoryMap[event.getType()](event, renderer);
+ }
+ throw new Error(`Cannot create command with type ${event.getType()}`);
+};
+
+const factoryMap = {
+ [EventType.kEventTypeSendEvent]: (event: APL.Event, renderer: APLRenderer) => {
+ const command = new SendEvent(event, renderer);
+ command.execute();
+ return command;
+ },
+ [EventType.kEventTypeRequestLineBounds]: (event: APL.Event, renderer: APLRenderer) => {
+ const command = new RequestLineBounds(event, renderer);
+ command.execute();
+ return command;
+ },
+ [EventType.kEventTypeLineHighlight]: (event: APL.Event, renderer: APLRenderer) => {
+ const command = new LineHighlight(event, renderer);
+ command.execute();
+ return command;
+ },
+ [EventType.kEventTypeReinflate]: (event: APL.Event, renderer: APLRenderer) => {
+ const command = new ReInflate(event, renderer);
+ command.execute();
+ return command;
+ },
+ [EventType.kEventTypeFinish]: (event: APL.Event, renderer: APLRenderer) => {
+ const command = new Finish(event, renderer);
+ command.execute();
+ return command;
+ },
+ [EventType.kEventTypeFocus]: (event: APL.Event, renderer: APLRenderer) => {
+ const command = new Focus(event, renderer);
+ command.execute();
+ return command;
+ },
+ [EventType.kEventTypeOpenURL]: (event: APL.Event, renderer: APLRenderer) => {
+ const command = new OpenUrl(event, renderer);
+ command.execute();
+ return command;
+ },
+ [EventType.kEventTypeDataSourceFetchRequest]: (event: APL.Event, renderer: APLRenderer) => {
+ const command = new DataSourceFetchRequest(event, renderer);
+ command.execute();
+ return command;
+ },
+ [EventType.kEventTypeExtension]: (event: APL.Event, renderer: APLRenderer) => {
+ const command = new ExtensionEvent(event, renderer);
+ command.execute();
+ return command;
+ },
+ [EventType.kEventTypeMediaRequest]: (event: APL.Event, renderer: APLRenderer) => {
+ const command = new MediaRequest(event, renderer);
+ command.execute();
+ return command;
+ }
+};
diff --git a/js/apl-html/src/ComponentFactory.ts b/js/apl-html/src/ComponentFactory.ts
index ebab33a..2a625d5 100644
--- a/js/apl-html/src/ComponentFactory.ts
+++ b/js/apl-html/src/ComponentFactory.ts
@@ -24,8 +24,9 @@ export const componentFactory = (renderer: APLRenderer, component: APL.Component
parent?: Component, ensureLayout: boolean = false,
insertAt: number = -1 ): Component => {
const id = component.getUniqueId();
+ let comp;
if (renderer.componentMap[id]) {
- const comp = renderer.componentMap[id];
+ comp = renderer.componentMap[id];
comp.parent = parent;
if (ensureLayout && comp instanceof Text) {
comp.setDimensions();
@@ -40,25 +41,29 @@ export const componentFactory = (renderer: APLRenderer, component: APL.Component
}
return comp;
} else if (factoryMap[component.getType()]) {
- const item = factoryMap[component.getType()](renderer, component, parent);
- if (ensureLayout) {
- if (item instanceof Text) {
- item.init();
- }
- item.component.ensureLayout();
- item.init();
- if (parent) {
- if (insertAt >= 0 && parent.container.children.length > 0 &&
- insertAt < parent.container.children.length) {
- parent.container.insertBefore(item.container, parent.container.children.item(insertAt));
- } else {
- parent.container.appendChild(item.container);
- }
+ comp = factoryMap[component.getType()](renderer, component, parent);
+ } else {
+ // Any unknown component is effectively container
+ comp = new Container(renderer, component, componentFactory, parent);
+ }
+
+ if (ensureLayout) {
+ if (comp instanceof Text) {
+ comp.init();
+ }
+ comp.component.ensureLayout();
+ comp.init();
+ if (parent) {
+ if (insertAt >= 0 && parent.container.children.length > 0 &&
+ insertAt < parent.container.children.length) {
+ parent.container.insertBefore(comp.container, parent.container.children.item(insertAt));
+ } else {
+ parent.container.appendChild(comp.container);
}
}
- return item;
}
- throw new Error(`Cannot create component with type ${component.getType()}`);
+
+ return comp;
};
// tslint:disable:max-line-length
diff --git a/js/apl-html/src/components/Component.ts b/js/apl-html/src/components/Component.ts
index 0e2ad37..b0d1fb2 100644
--- a/js/apl-html/src/components/Component.ts
+++ b/js/apl-html/src/components/Component.ts
@@ -741,7 +741,7 @@ export abstract class Component extends EventEmitt
const isLegacyComponentType: boolean = LEGACY_CLIPPING_COMPONENTS_SET.has(componentType);
const isLegacyAplVersion: boolean = this.renderer && this.renderer.getLegacyClippingEnabled();
- if (isLegacyComponentType || isParentLegacy || !isLegacyAplVersion) {
+ if (!this.parent || isLegacyComponentType || isParentLegacy || !isLegacyAplVersion) {
this.enableClipping();
}
}
diff --git a/js/apl-html/src/components/filters/Blend.ts b/js/apl-html/src/components/filters/Blend.ts
index 1891e08..394c8ef 100644
--- a/js/apl-html/src/components/filters/Blend.ts
+++ b/js/apl-html/src/components/filters/Blend.ts
@@ -26,11 +26,21 @@ export interface IBlend extends IBaseFilter {
* https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feBlend
*/
+enum BlendType {
+ Blend = 'feBlend',
+ Composite = 'feComposite'
+}
+
export function getBlendFilter(filter: Filter, imageSrcArray: string[]): IImageFilterElement | undefined {
const blendId: string = uuidv4().toString();
let filterImageArray: SVGFEImageElement[] = [];
- const blend: SVGElement = document.createElementNS(SVG_NS, 'feBlend');
- blend.setAttributeNS('', 'mode', getBlendMode((filter as IBlend).mode));
+ const [elementType, operator] = getBlendMode((filter as IBlend).mode);
+ const blend: SVGElement = document.createElementNS(SVG_NS, elementType);
+ if (elementType === BlendType.Composite) {
+ blend.setAttributeNS('', 'operator', operator);
+ } else {
+ blend.setAttributeNS('', 'mode', operator);
+ }
blend.setAttributeNS('', 'result', blendId);
/*
@@ -78,41 +88,47 @@ export function getBlendFilter(filter: Filter, imageSrcArray: string[]): IImageF
* Return Blend Mode
* https://codepen.io/yoksel/pen/BiExv
*/
-export function getBlendMode(mode: BlendMode): string {
+function getBlendMode(mode: BlendMode): [BlendType, string] {
switch (mode) {
case BlendMode.kBlendModeNormal:
- return 'normal';
+ return [BlendType.Blend, 'normal'];
case BlendMode.kBlendModeMultiply:
- return 'multiply';
+ return [BlendType.Blend, 'multiply'];
case BlendMode.kBlendModeScreen:
- return 'screen';
+ return [BlendType.Blend, 'screen'];
case BlendMode.kBlendModeOverlay:
- return 'overlay';
+ return [BlendType.Blend, 'overlay'];
case BlendMode.kBlendModeDarken:
- return 'darken';
+ return [BlendType.Blend, 'darken'];
case BlendMode.kBlendModeLighten:
- return 'lighten';
+ return [BlendType.Blend, 'lighten'];
case BlendMode.kBlendModeColorDodge:
- return 'color-dodge';
+ return [BlendType.Blend, 'color-dodge'];
case BlendMode.kBlendModeColorBurn:
- return 'color-burn';
+ return [BlendType.Blend, 'color-burn'];
case BlendMode.kBlendModeHardLight:
- return 'hard-light';
+ return [BlendType.Blend, 'hard-light'];
case BlendMode.kBlendModeSoftLight:
- return 'soft-light';
+ return [BlendType.Blend, 'soft-light'];
case BlendMode.kBlendModeDifference:
- return 'difference';
+ return [BlendType.Blend, 'difference'];
case BlendMode.kBlendModeExclusion:
- return 'exclusion';
+ return [BlendType.Blend, 'exclusion'];
case BlendMode.kBlendModeHue:
- return 'hue';
+ return [BlendType.Blend, 'hue'];
case BlendMode.kBlendModeSaturation:
- return 'saturation';
+ return [BlendType.Blend, 'saturation'];
case BlendMode.kBlendModeColor:
- return 'color';
+ return [BlendType.Blend, 'color'];
case BlendMode.kBlendModeLuminosity:
- return 'luminosity';
+ return [BlendType.Blend, 'luminosity'];
+ case BlendMode.kBlendModeSourceIn:
+ return [BlendType.Composite, 'in'];
+ case BlendMode.kBlendModeSourceAtop:
+ return [BlendType.Composite, 'atop'];
+ case BlendMode.kBlendModeSourceOut:
+ return [BlendType.Composite, 'out'];
default:
- return 'normal';
+ return [BlendType.Blend, 'normal'];
}
}
diff --git a/js/apl-html/src/components/filters/Blur.ts b/js/apl-html/src/components/filters/Blur.ts
new file mode 100644
index 0000000..b89b837
--- /dev/null
+++ b/js/apl-html/src/components/filters/Blur.ts
@@ -0,0 +1,52 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+'use strict';
+
+import { Filter, generateSVGFeImage, isIndexOutOfBound } from '../../utils/FilterUtils';
+import { SVG_NS, uuidv4 } from '../Component';
+import { BITMAP_IMAGE_REGEX_CHECK, IBaseFilter, IImageFilterElement } from './ImageFilter';
+
+/**
+ * @ignore
+ */
+export interface IBlur extends IBaseFilter {
+ radius: number;
+ source: number;
+}
+
+/*
+ * Apply a Gaussian blur with a specified radius. The new image is appended to the end of the array.
+ * Specs: https://developer.amazon.com/en-US/docs/alexa/alexa-presentation-language/apl-filters.html#blur
+ * Utilize svg filter
+ * https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feGaussianBlur
+ */
+export function getBlurFilter(filter: Filter, imageSrcArray: string[]): IImageFilterElement | undefined {
+ const blurId: string = uuidv4().toString();
+ let filterImageArray: SVGFEImageElement[] = [];
+ const blur: SVGElement = document.createElementNS(SVG_NS, 'feGaussianBlur');
+ blur.setAttributeNS('', 'stdDeviation', (filter as IBlur).radius.toString());
+ blur.setAttributeNS('', 'result', blurId);
+ /*
+ * All filters that operate on a single image have a default image source property of -1;
+ * that is, by default they take as input the last image in the image array.
+ */
+ let index: number = (filter as IBlur).source;
+
+ // Negative case : index outside source array bounds. return undefined
+ if (isIndexOutOfBound(index, imageSrcArray.length)) {
+ return undefined;
+ }
+ if (index < 0) {
+ index += imageSrcArray.length;
+ }
+ const imageId: string = imageSrcArray[index];
+ if (imageId.match(BITMAP_IMAGE_REGEX_CHECK)) {
+ filterImageArray = generateSVGFeImage(imageId, blur);
+ } else {
+ blur.setAttributeNS('', 'in', imageId);
+ }
+ return { filterId: blurId, filterElement: blur, filterImageArray };
+}
diff --git a/js/apl-html/src/components/filters/Saturate.ts b/js/apl-html/src/components/filters/Saturate.ts
new file mode 100644
index 0000000..98733f6
--- /dev/null
+++ b/js/apl-html/src/components/filters/Saturate.ts
@@ -0,0 +1,57 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+'use strict';
+
+import { Filter, generateSVGFeImage, isIndexOutOfBound } from '../../utils/FilterUtils';
+import { SVG_NS, uuidv4 } from '../Component';
+import { BITMAP_IMAGE_REGEX_CHECK, IBaseFilter, IImageFilterElement } from './ImageFilter';
+
+/**
+ * @ignore
+ */
+export interface ISaturate extends IBaseFilter {
+ amount: number;
+ source?: number;
+}
+
+/*
+ * Change the color saturation of the image and appends the new image to the end of the array.
+ * Specs: https://developer.amazon.com/en-US/docs/alexa/alexa-presentation-language/apl-filters.html#saturate
+ * Utilize svg filter
+ * https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feColorMatrix
+ */
+
+export function getSaturateFilter(filter: Filter, imageSrcArray: string[]): IImageFilterElement | undefined {
+ const saturateId: string = uuidv4().toString();
+ let filterImageArray: SVGFEImageElement[] = [];
+ const saturate: SVGElement = document.createElementNS(SVG_NS, 'feColorMatrix');
+ saturate.setAttribute('type', 'saturate');
+ saturate.setAttribute('values', (filter as ISaturate).amount.toString());
+ saturate.setAttributeNS('', 'result', saturateId);
+
+ /*
+ * An amount of 0% completed unsaturates the image.
+ * An amount of 100% leaves the image the same.
+ * Values greater than 100% produce super-saturation.
+ * The source image must be a bitmap
+ */
+ let index: number = (filter as ISaturate).source;
+
+ // Negative case : index outside source array bounds. return undefined
+ if (isIndexOutOfBound(index, imageSrcArray.length)) {
+ return undefined;
+ }
+ if (index < 0) {
+ index += imageSrcArray.length;
+ }
+ const imageId: string = imageSrcArray[index];
+ if (imageId.match(BITMAP_IMAGE_REGEX_CHECK)) {
+ filterImageArray = generateSVGFeImage(imageId, saturate);
+ } else {
+ saturate.setAttributeNS('', 'in', imageId);
+ }
+ return { filterId : saturateId, filterElement : saturate, filterImageArray };
+}
diff --git a/js/apl-html/src/index.ts b/js/apl-html/src/index.ts
index ace852b..52c15bc 100644
--- a/js/apl-html/src/index.ts
+++ b/js/apl-html/src/index.ts
@@ -100,6 +100,7 @@ export * from './utils/EventUtils';
export * from './utils/FilterUtils';
export * from './utils/FontUtils';
export * from './utils/SoftRandom';
+export * from './utils/TextTrackUtils';
export * from './utils/LocaleMethods';
export * from './logging/ILogger';
export * from './logging/LoggerFactory';
diff --git a/js/apl-html/src/media/IMediaSource.ts b/js/apl-html/src/media/IMediaSource.ts
index 1d27c24..1e2a760 100644
--- a/js/apl-html/src/media/IMediaSource.ts
+++ b/js/apl-html/src/media/IMediaSource.ts
@@ -4,7 +4,7 @@
*/
/**
- * MediaTrack defines the source and playback parameters
+ * IMediaSource defines the source and playback parameters
* @ignore
*/
export interface IMediaSource {
@@ -13,11 +13,6 @@ export interface IMediaSource {
*/
url: string;
- /**
- * Optional description of this source
- */
- description: string;
-
/**
* Duration of the track in milliseconds
*/
@@ -32,4 +27,30 @@ export interface IMediaSource {
* Milliseconds from the start of the track to play from
*/
offset: number;
+
+ /**
+ * Text tracks of the media
+ */
+ textTracks: Array;
}
+
+/**
+ * ITextTrackSource defines the text track attached to a media source
+ * @ignore
+ */
+export interface ITextTrackSource {
+ /**
+ * The kind of the text track
+ */
+ kind: string;
+
+ /**
+ * The actual URL to load the text track
+ */
+ url: string;
+
+ /**
+ * Optional description of this source
+ */
+ description: string;
+}
\ No newline at end of file
diff --git a/js/apl-html/src/media/IVideoPlayer.ts b/js/apl-html/src/media/IVideoPlayer.ts
index cdbe92d..17a6060 100644
--- a/js/apl-html/src/media/IVideoPlayer.ts
+++ b/js/apl-html/src/media/IVideoPlayer.ts
@@ -2,6 +2,7 @@
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*/
+import { ITextTrackSource } from './IMediaSource';
import {IPlayer} from './IPlayer';
export interface IVideoPlayer extends IPlayer {
@@ -28,4 +29,6 @@ export interface IVideoPlayer extends IPlayer {
reset(): void;
destroy(): void;
+
+ loadTextTracks(textTracks: ITextTrackSource[]): void;
}
diff --git a/js/apl-html/src/media/MediaEventProcessor.ts b/js/apl-html/src/media/MediaEventProcessor.ts
index e76fdde..b207ba2 100644
--- a/js/apl-html/src/media/MediaEventProcessor.ts
+++ b/js/apl-html/src/media/MediaEventProcessor.ts
@@ -12,6 +12,7 @@ import { IMediaEventListener } from './IMediaEventListener';
import { IMediaPlayerHandle } from './IMediaPlayerHandle';
import { IVideoPlayer } from './IVideoPlayer';
import { MediaErrorCode } from './MediaErrorCode';
+import { MediaPlayerHandle } from './MediaPlayerHandle';
import { MediaState } from './MediaState';
import { IMediaResource, PlaybackManager } from './PlaybackManager';
import { PlaybackState } from './Resource';
@@ -36,7 +37,6 @@ export function createMediaEventProcessor(mediaEventProcessorArgs: MediaEventPro
// Private Variables
let videoState: PlaybackState = PlaybackState.IDLE;
- const endEventPromiseListeners: PromiseCallback[] = [];
// Private Functions
async function ensureLoaded(fromEvent: boolean, isSettingSource: boolean = false) {
@@ -44,6 +44,7 @@ export function createMediaEventProcessor(mediaEventProcessorArgs: MediaEventPro
if (!currentMediaResource.loaded) {
await this.player.load(currentMediaResource.id, currentMediaResource.url);
currentMediaResource.loaded = true;
+ this.player.loadTextTracks(currentMediaResource.textTracks);
} else {
this.updateMediaState(fromEvent, isSettingSource);
}
@@ -58,14 +59,14 @@ export function createMediaEventProcessor(mediaEventProcessorArgs: MediaEventPro
if (!this.loaded) {
logger.info('First loaded, refresh video');
this.setTrackIndex({trackIndex: 0, fromEvent: false});
- this.rewind();
+ this.rewind({fromEvent: false});
this.loaded = true;
}
if (this.shouldStartPlayAfterPlayerInit) {
logger.info('Player ready, starting playback');
this.play({
waitForFinish: false,
- fromEvent: true,
+ fromEvent: false,
isSettingSource: false
});
}
@@ -117,12 +118,6 @@ export function createMediaEventProcessor(mediaEventProcessorArgs: MediaEventPro
this.currentMediaState.paused = true;
mediaPlayerEventType = MediaPlayerEventType.kMediaPlayerEventEnd;
}
- endEventPromiseListeners.forEach((resolvePromise) => {
- try {
- resolvePromise();
- } catch (e) {
- }
- });
break;
case PlaybackState.ERROR:
logger.error('Playback error.');
@@ -182,12 +177,6 @@ export function createMediaEventProcessor(mediaEventProcessorArgs: MediaEventPro
}
}
);
-
- if (waitForFinish) {
- await new Promise((resolve) => {
- endEventPromiseListeners.push(resolve);
- });
- }
},
async pause(): Promise {
if (videoState === PlaybackState.PLAYING || videoState === PlaybackState.BUFFERING) {
@@ -231,8 +220,10 @@ export function createMediaEventProcessor(mediaEventProcessorArgs: MediaEventPro
this.updateMediaState(fromEvent);
},
- async rewind(): Promise {
- await this.pause();
+ async rewind({ fromEvent }): Promise {
+ if (fromEvent) {
+ await this.pause();
+ }
const currentMediaResource = this.playbackManager.getCurrent();
this.player.setCurrentTimeInSeconds(
toSecondsFromMilliseconds(currentMediaResource.offset)
@@ -240,16 +231,19 @@ export function createMediaEventProcessor(mediaEventProcessorArgs: MediaEventPro
},
async previous({ fromEvent }): Promise {
if (!this.playbackManager.hasPrevious()) {
- await this.delegate.rewind();
+ await this.delegate.rewind(fromEvent);
} else {
- await this.pause();
+ if (fromEvent) {
+ await this.pause();
+ }
this.playbackManager.previous();
}
await ensureLoaded.call(this, fromEvent);
},
async next({ fromEvent }): Promise {
- await this.pause();
-
+ if (fromEvent) {
+ await this.pause();
+ }
if (!this.playbackManager.hasNext()) {
this.player.setCurrentTimeInSeconds(this.player.getDurationInSeconds() - 0.001);
} else {
@@ -258,7 +252,9 @@ export function createMediaEventProcessor(mediaEventProcessorArgs: MediaEventPro
}
},
async setTrackIndex({ trackIndex, fromEvent }): Promise {
- await this.pause();
+ if (fromEvent) {
+ await this.pause();
+ }
this.playbackManager.setCurrent(trackIndex);
this.updateMediaState(fromEvent, true);
await ensureLoaded.call(this, fromEvent);
@@ -268,14 +264,16 @@ export function createMediaEventProcessor(mediaEventProcessorArgs: MediaEventPro
this.audioTrack = audioTrack;
},
setMuted({ muted, fromEvent }) {
- this.muted = muted;
+ if (this.audioTrack !== AudioTrack.kAudioTrackNone) {
+ this.muted = muted;
- if (this.muted) {
- this.player.mute();
- } else {
- this.player.unmute();
+ if (this.muted) {
+ this.player.mute();
+ } else {
+ this.player.unmute();
+ }
+ this.updateMediaState(fromEvent);
}
- this.updateMediaState(fromEvent);
},
async setTrackList({ trackArray }): Promise {
// Configure Player and Playback
@@ -411,7 +409,7 @@ export function createMediaEventProcessor(mediaEventProcessorArgs: MediaEventPro
}
});
- return Object.setPrototypeOf(mediaEventProcessor, mediaPlayerHandle);
+ return Object.setPrototypeOf(mediaEventProcessor, mediaPlayerHandle) as MediaPlayerHandle;
}
function toSecondsFromMilliseconds(milliseconds: number) {
diff --git a/js/apl-html/src/media/MediaPlayerHandle.ts b/js/apl-html/src/media/MediaPlayerHandle.ts
index a10548b..c5c77d5 100644
--- a/js/apl-html/src/media/MediaPlayerHandle.ts
+++ b/js/apl-html/src/media/MediaPlayerHandle.ts
@@ -128,17 +128,17 @@ export class MediaPlayerHandle implements IMediaPlayerHandle, IMediaEventListene
}
public onEvent(event: PlaybackState): void {
- this.eventProcessor.onEvent({
- event,
- fromEvent: false,
- isSettingSource: false,
- aplMediaPlayer: this.mediaPlayer
- });
if (this.videoComponent) {
this.videoComponent.onEvent(event);
} else {
this.lastPlaybackState = event;
}
+ this.eventSequencer.enqueueForProcessing(VideoInterface.ON_EVENT, {
+ event,
+ fromEvent: false,
+ isSettingSource: false,
+ aplMediaPlayer: this.mediaPlayer
+ });
}
public onPlayerReady(): void {
diff --git a/js/apl-html/src/media/PlaybackManager.ts b/js/apl-html/src/media/PlaybackManager.ts
index 4648c61..1bc87a8 100644
--- a/js/apl-html/src/media/PlaybackManager.ts
+++ b/js/apl-html/src/media/PlaybackManager.ts
@@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
const uuidv4 = require('uuid/v4');
-import { IMediaSource } from './IMediaSource';
+import { IMediaSource, ITextTrackSource } from './IMediaSource';
/**
* Media resource state
@@ -18,6 +18,7 @@ export interface IMediaResource {
id: string;
loaded: boolean;
duration: number;
+ textTracks: ITextTrackSource[];
}
/**
@@ -117,7 +118,8 @@ export class PlaybackManager {
trackIndex : index,
url : track.url,
loaded : false,
- duration : track.duration
+ duration : track.duration,
+ textTracks: track.textTracks
};
this.resources.set(index, mediaResource);
diff --git a/js/apl-html/src/media/audio/SpeechMarks.ts b/js/apl-html/src/media/audio/SpeechMarks.ts
index c0c3717..5839534 100644
--- a/js/apl-html/src/media/audio/SpeechMarks.ts
+++ b/js/apl-html/src/media/audio/SpeechMarks.ts
@@ -9,7 +9,7 @@
* The type of speech mark
* @ignore
*/
-export type SpeechMarkType = 'visime' | 'word' | 'sentence' | 'ssml';
+export type SpeechMarkType = 'viseme' | 'word' | 'sentence' | 'ssml';
/**
* Base type
diff --git a/js/apl-html/src/media/video/PlayerNetworkRetryManager.ts b/js/apl-html/src/media/video/PlayerNetworkRetryManager.ts
new file mode 100644
index 0000000..41fcc4c
--- /dev/null
+++ b/js/apl-html/src/media/video/PlayerNetworkRetryManager.ts
@@ -0,0 +1,54 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+const hls = require('hls.js/dist/hls.light.min.js');
+
+export interface PlayerNetworkRetryManagerArgs {
+ player: HTMLVideoElement | any;
+ numberOfRetries?: number;
+ errorCallback?: () => void;
+}
+
+export interface PlayerNetworkRetryManager {
+ fail: () => void;
+ shouldRetry: () => boolean;
+ retry: (url: string, errorDetails: string) => void;
+}
+
+export function createPlayerNetworkRetryManager(args: PlayerNetworkRetryManagerArgs): PlayerNetworkRetryManager {
+ const defaultArgs = {
+ numberOfRetries: 3,
+ errorCallback: () => {}
+ };
+
+ args = Object.assign(defaultArgs, args);
+
+ const {
+ player,
+ errorCallback
+ } = args;
+
+ let remainingTries = args.numberOfRetries;
+
+ return {
+ fail(): void {
+ errorCallback();
+ },
+ retry(url, errorDetails): void {
+ remainingTries -= 1;
+
+ if (errorDetails === hls.ErrorDetails.MANIFEST_LOAD_ERROR ||
+ errorDetails === hls.ErrorDetails.MANIFEST_LOAD_TIMEOUT ||
+ errorDetails === hls.ErrorDetails.MANIFEST_PARSING_ERROR) {
+ player.loadSource(url);
+ } else {
+ player.startLoad();
+ }
+ },
+ shouldRetry(): boolean {
+ return remainingTries > 0;
+ }
+ };
+}
diff --git a/js/apl-html/src/media/video/VideoPlayer.ts b/js/apl-html/src/media/video/VideoPlayer.ts
new file mode 100644
index 0000000..9044586
--- /dev/null
+++ b/js/apl-html/src/media/video/VideoPlayer.ts
@@ -0,0 +1,253 @@
+/**
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { clearAllTextTracks, loadDynamicTextTrack, TextTrackKind } from '../../utils/TextTrackUtils';
+import { IMediaEventListener } from '../IMediaEventListener';
+import { ITextTrackSource } from '../IMediaSource';
+import { IVideoPlayer } from '../IVideoPlayer';
+import { PlaybackState } from '../Resource';
+import { EmitBehavior, IPlaybackStateHandler, PlaybackStateHandler } from './PlaybackStateHandler';
+
+export interface PlayerInitializationArgs {
+ player: HTMLVideoElement;
+ parent: HTMLElement;
+ scale: 'contain' | 'cover';
+}
+
+export enum PlaybackFailure {
+ UNINITIALIZED = 'UNINITIALIZED',
+ GENERIC = 'GENERIC'
+}
+
+export function createVideoPlayer(eventListener: IMediaEventListener): IVideoPlayer {
+ // Private Functions
+ function initializePlayer(args: PlayerInitializationArgs) {
+ const {
+ player,
+ parent,
+ scale
+ } = args;
+ parent.appendChild(player);
+ Object.assign(player.style, {
+ 'height': '100%',
+ 'object-fit': scale,
+ 'width': '100%'
+ });
+ }
+
+ function positionRelativeToCurrentTime(time: number): Comparison {
+ if (time > this.player.currentTime) {
+ return Comparison.GT;
+ }
+ if (time < this.player.currentTime) {
+ return Comparison.LT;
+ }
+ return Comparison.EQ;
+ }
+
+ function updateCurrentTimeTo(time: number): void {
+ this.player.currentTime = time / 1000;
+ }
+
+ // Video LifeCycle Callbacks
+ function attachOnPlayCallback() {
+ function onPlayCallback(): void {
+ this.playbackStateHandler.transitionToState(PlaybackState.PLAYING);
+ }
+
+ this.player.onplay = onPlayCallback.bind(this);
+ }
+
+ function attachOnPlayingCallback() {
+ function onPlayingCallback(): void {
+ this.playbackStateHandler.transitionToState(PlaybackState.PLAYING);
+ }
+
+ this.player.onplaying = onPlayingCallback.bind(this);
+ }
+
+ function attachOnEndedCallback() {
+ function onEndedCallback(): void {
+ this.playbackStateHandler.transitionToState(PlaybackState.ENDED);
+ }
+
+ this.player.onended = onEndedCallback.bind(this);
+ }
+
+ function attachOnPauseCallback() {
+ function onPauseCallback() {
+ this.playbackStateHandler.transitionToState(PlaybackState.PAUSED, EmitBehavior.AlwaysEmit);
+ }
+
+ this.player.onpause = onPauseCallback.bind(this);
+ }
+
+ function attachOnErrorCallback() {
+ function onErrorCallback() {
+ this.playbackStateHandler.transitionToState(PlaybackState.ERROR);
+ }
+ this.player.onerror = onErrorCallback.bind(this);
+ }
+
+ function attachOnLoadedDataCallback() {
+ function onLoadedDataCallback() {
+ this.playbackStateHandler.transitionToState(PlaybackState.LOADED);
+ }
+
+ this.player.onloadeddata = onLoadedDataCallback.bind(this);
+ }
+
+ function attachOnTimeUpdateCallback() {
+ function onTimeUpdateCallback() {
+ const currentPlaybackState = this.playbackStateHandler.currentPlaybackState;
+ if (currentPlaybackState === PlaybackState.PLAYING) {
+ this.playbackStateHandler.transitionToState(currentPlaybackState, EmitBehavior.AlwaysEmit);
+ }
+ }
+
+ this.player.ontimeupdate = onTimeUpdateCallback.bind(this);
+ }
+
+ // Public Interface
+ const videoPlayer: IVideoPlayer = {
+ // IVideoPlayer Interface
+ init(): void {
+ this.playbackStateHandler.transitionToState(PlaybackState.IDLE);
+ attachOnPlayCallback.call(this);
+ attachOnPlayingCallback.call(this);
+ attachOnEndedCallback.call(this);
+ attachOnPauseCallback.call(this);
+ attachOnErrorCallback.call(this);
+ attachOnLoadedDataCallback.call(this);
+ attachOnTimeUpdateCallback.call(this);
+
+ this.playerIsInitialized = false;
+ this.shouldStartPlayAfterInit = false;
+ },
+ configure(parent: HTMLElement, scale: 'contain' | 'cover'): void {
+ initializePlayer({
+ player: this.player,
+ parent,
+ scale
+ });
+ this.playerIsInitialized = true;
+ this.eventListener.onPlayerReady();
+ },
+ applyCssShadow(shadowParams: string): void {
+ this.player.style.boxShadow = shadowParams;
+ },
+ setCurrentTimeInSeconds(offsetInSeconds: number): void {
+ this.player.currentTime = offsetInSeconds;
+ },
+ getCurrentPlaybackPositionInSeconds(): number {
+ return this.player.currentTime;
+ },
+ getDurationInSeconds(): number {
+ return this.player.duration;
+ },
+ mute(): void {
+ this.player.muted = true;
+ },
+ unmute(): void {
+ this.player.muted = false;
+ },
+ // IPlayer Interface
+ load(id: string, url: string): Promise {
+ attachOnLoadedDataCallback.call(this);
+ this.player.id = id;
+ this.playbackStateHandler.transitionToState(PlaybackState.IDLE);
+ this.player.src = url;
+ this.player.load();
+
+ return Promise.resolve(undefined);
+ },
+ loadTextTracks(textTracks: ITextTrackSource[]) {
+ clearAllTextTracks(this.player);
+ if (textTracks && textTracks.length !== 0) {
+ loadDynamicTextTrack(this.player, textTracks[0].url, textTracks[0].kind as TextTrackKind);
+ }
+ },
+ play(id: string, url: string, offset: number): Promise {
+ if (!this.playerIsInitialized) {
+ return Promise.reject(PlaybackFailure.UNINITIALIZED);
+ }
+ const playbackIsNotPaused = !this.playbackStateHandler.isState(PlaybackState.PAUSED);
+ const offsetIsAhead = positionRelativeToCurrentTime.call(this, offset) === Comparison.GT;
+ if (playbackIsNotPaused && offsetIsAhead) {
+ updateCurrentTimeTo.call(this, offset);
+ }
+ return new Promise((resolve, reject) => {
+ this.player.play()
+ .then(resolve)
+ .catch(reject);
+ });
+ },
+ pause() {
+ this.playbackStateHandler.transitionToState(PlaybackState.PAUSED);
+ return this.player.pause();
+ },
+ end() {
+ this.playbackStateHandler.transitionToState(PlaybackState.ENDED);
+ return this.player.pause();
+ },
+ setVolume(volume: number): void {
+ this.player.volume = volume;
+ },
+ flush(): void {
+ this.pause();
+ },
+ getMediaId(): string {
+ return this.player.id;
+ },
+ getMediaState(): PlaybackState {
+ return this.playbackStateHandler.currentPlaybackState;
+ },
+ reset() {
+ // Pause any existing playback
+ this.player.pause();
+ // Empty out the source
+ this.player.removeAttribute('src');
+ // Reload the player to refresh its state
+ this.player.load();
+ },
+ destroy() {
+ this.reset();
+ // Remove from DOM
+ this.player.remove();
+ }
+ };
+
+ const videoElement = document.createElement('video');
+ const playbackStateHandler = PlaybackStateHandler({
+ eventListener,
+ initialPlaybackState: PlaybackState.IDLE
+ });
+
+ // Object Properties
+ return Object.defineProperties(videoPlayer, {
+ player: {
+ value: videoElement as HTMLVideoElement,
+ writable: false,
+ configurable: false
+ },
+ playbackStateHandler: {
+ value: playbackStateHandler as IPlaybackStateHandler,
+ writable: false,
+ configurable: false
+ },
+ eventListener: {
+ value: eventListener as IMediaEventListener,
+ writable: false,
+ configurable: false
+ }
+ });
+}
+
+// Helper Enums
+enum Comparison {
+ LT = 'LT',
+ GT = 'GT',
+ EQ = 'EQ'
+}
diff --git a/js/apl-html/src/utils/AplVersionUtils.ts b/js/apl-html/src/utils/AplVersionUtils.ts
index efbb5a3..93a91e8 100644
--- a/js/apl-html/src/utils/AplVersionUtils.ts
+++ b/js/apl-html/src/utils/AplVersionUtils.ts
@@ -16,6 +16,7 @@ export const APL_1_8 = 8;
export const APL_1_9 = 9;
export const APL_2022_1 = 10;
export const APL_2022_2 = 11;
+export const APL_2023_1 = 12;
export const APL_LATEST = Number.MAX_VALUE;
export interface AplVersionUtils {
@@ -36,7 +37,8 @@ export function createAplVersionUtils(): AplVersionUtils {
['1.8', APL_1_8],
['1.9', APL_1_9],
['2022.1', APL_2022_1],
- ['2022.2', APL_2022_2]
+ ['2022.2', APL_2022_2],
+ ['2023.1', APL_2023_1]
]);
return {
diff --git a/js/apl-html/src/utils/TextTrackUtils.ts b/js/apl-html/src/utils/TextTrackUtils.ts
new file mode 100644
index 0000000..3125c79
--- /dev/null
+++ b/js/apl-html/src/utils/TextTrackUtils.ts
@@ -0,0 +1,123 @@
+/**
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+// We don't want to block the main thread too long. So subtitle format conversion we do by chunks
+// This number is small enough that we don't block the main thread too long to miss a frame
+// This number is large enough that we don't get too many segments
+const DEFAULT_CHUNK_SZIE = 50;
+
+declare var VTTCue: {
+ prototype: TextTrackCue;
+ new(startTime: number, endTime: number, text: string): TextTrackCue;
+};
+
+export type TextTrackKind = 'captions' | 'chapters' | 'descriptions' | 'metadata' | 'subtitles';
+
+export function clearAllTextTracks(video: HTMLVideoElement) {
+ const tracks = video.querySelectorAll('track');
+ for (let i = 0; i < tracks.length; i++) {
+ video.textTracks[i].mode = 'disabled';
+ video.removeChild(tracks[i]);
+ }
+}
+
+export function loadDynamicTextTrack(video: HTMLVideoElement, url: string, kind: TextTrackKind,
+ chunkSize: number = DEFAULT_CHUNK_SZIE) {
+ const track = document.createElement('track');
+ track.kind = kind;
+ video.appendChild(track);
+ const textTrack = video.textTracks[0];
+
+ fetch(url, {mode: 'cors'})
+ .then((response) => response.text())
+ .then((data) => {
+ textTrack.mode = 'showing';
+
+ if (getType(data) === 'vtt') {
+ // HTML text track component doesn't like cors, so we need to create a blob inside the memory
+ const blob = new Blob([data], {
+ type: 'text/plain'
+ });
+ track.src = URL.createObjectURL(blob);
+ } else {
+ setTimeout(() => {
+ addDynamicSRTTrack(data, 0, textTrack, chunkSize);
+ }, 0);
+ }
+ });
+}
+
+function getType(data: string): string {
+ // VTT has text WEBVTT as the first line
+ if (data.split('\n', 1)[0].toLowerCase() === 'webvtt') {
+ // We just ignore WebVTT for now as other viewhost doesn't support it.
+ return 'unsupported';
+ }
+
+ // SRT has no indicator in its file, so we try to match the timestamp line in the first 100 chars
+ const findLineNumber = /\d+(?:\r\n|\r|\n)(?=(?:\d\d:\d\d:\d\d,\d\d\d)\s-->\s(?:\d\d:\d\d:\d\d,\d\d\d))/g;
+ if (data.substring(0, 100).match(findLineNumber)) {
+ return 'srt';
+ }
+
+ return 'unsupported';
+}
+
+// SRT to VTT
+
+function toTime(timeString: string) {
+ const t = timeString.match(/(\d+):(\d+):(\d+)(?:,(\d+))/);
+ // the same regex is used to find the string, so 't' won't be null in any case
+ const time = Number(t![1]) * 3600 + Number(t![2]) * 60 + Number(t![3]) + Number(t![4]) / 1000;
+ return time;
+}
+
+function toVTTCue(srtCue: string) {
+ // match timestamp and the rest of text
+ // webVtt doesn't support pixel based positioning anyway. So we just ingore the X1,X2,Y1,Y2 in the timestamp line
+ const matchTimestamp = /(\d\d:\d\d:\d\d,\d\d\d)/g;
+ const matches = srtCue.match(matchTimestamp);
+ if (!matches) {
+ return null;
+ }
+ const start = matches[0];
+ const end = matches[1];
+ let text = srtCue.slice(srtCue.indexOf('\n')).trim();
+ // remove the ASS tags.
+ text = text.replace(/\{\\.*?\}/g, '');
+
+ const vtt = new VTTCue(toTime(start), toTime(end), text);
+ return vtt;
+}
+
+function addDynamicSRTTrack(srt: string, cursor: number, textTrack: TextTrack, chunkSize: number) {
+ if (textTrack.mode !== 'showing') {
+ return;
+ }
+ // find digits followed by a single line break and timestamps
+ const findLineNumber = /\d+(?:\r\n|\r|\n)(?=(?:\d\d:\d\d:\d\d,\d\d\d)\s-->\s(?:\d\d:\d\d:\d\d,\d\d\d))/g;
+ const srtSeg = srt.substring(cursor);
+
+ // we don't want to block the main thread too long. So only process chunkSize lines once
+ const srtArray = srtSeg.split(findLineNumber, chunkSize);
+
+ // remember the parser position for the next iteration
+ const endSeg = cursor + srtSeg.indexOf(srtArray[srtArray.length - 1]) + srtArray[srtArray.length - 1].length;
+ if (endSeg === cursor) {
+ return;
+ }
+
+ // parser one line of subtitle and convert to vtt
+ srtArray.forEach((srtCue) => {
+ const vtt = toVTTCue(srtCue);
+ if (vtt) {
+ textTrack.addCue(vtt);
+ }
+ });
+
+ setTimeout(() => {
+ addDynamicSRTTrack(srt, endSeg, textTrack, chunkSize);
+ }, 0);
+}
diff --git a/js/apl-wasm/package.json b/js/apl-wasm/package.json
index a838479..92b606c 100644
--- a/js/apl-wasm/package.json
+++ b/js/apl-wasm/package.json
@@ -1,5 +1,5 @@
{
- "name": "apl-wasm",
+ "name": "@amzn/apl-wasm",
"version": "1.0.0",
"private": true,
"license": "SEE LICENSE IN LICENSE.txt",
@@ -58,5 +58,8 @@
"webpack-bundle-analyzer": "^4.6.1",
"webpack-cli": "^3.3.12",
"webpack-merge": "^4.2.1"
+ },
+ "npm-pretty-much": {
+ "legacyPackageNameAlias": "apl-wasm"
}
}
diff --git a/js/dts-packer/package.json b/js/dts-packer/package.json
index 2e90bca..c8215ff 100644
--- a/js/dts-packer/package.json
+++ b/js/dts-packer/package.json
@@ -1,5 +1,5 @@
{
- "name": "dts-packer",
+ "name": "@amzn/dts-packer",
"version": "1.0.0",
"license": "SEE LICENSE IN LICENSE.txt",
"main": "lib/index.js",
@@ -20,5 +20,8 @@
"webpack": "^4.26.1",
"webpack-cli": "^3.3.12",
"webpack-merge": "^4.2.1"
+ },
+ "npm-pretty-much": {
+ "legacyPackageNameAlias": "dts-packer"
}
}
diff --git a/js/dts-packer/src/index.ts b/js/dts-packer/src/index.ts
index 431d6cf..370531d 100644
--- a/js/dts-packer/src/index.ts
+++ b/js/dts-packer/src/index.ts
@@ -221,7 +221,7 @@ export default class DtsPackerPlugin {
compiler.plugin('done', () => {
const name = path.basename(compiler.context);
const m = new NodeModules(this.options.require, compiler.outputPath);
- m.addModule(name, true);
+ m.addModule('@amzn/'+name, true);
m.transformAll();
});
}
diff --git a/js/package.json b/js/package.json
index 81193e6..85ec936 100644
--- a/js/package.json
+++ b/js/package.json
@@ -1,5 +1,5 @@
{
- "name": "apl-workspace",
+ "name": "@amzn/apl-workspace",
"version": "1.0.0",
"private": true,
"workspaces": [
@@ -25,5 +25,8 @@
"clean:html": "cd apl-html && yarn clean",
"clean:wasm": "cd apl-wasm && yarn clean",
"lint": "yarn workspaces run lint"
+ },
+ "npm-pretty-much": {
+ "legacyPackageNameAlias": "apl-workspace"
}
}
diff --git a/js/yarn.lock b/js/yarn.lock
index b6f3ceb..a40b5c9 100644
--- a/js/yarn.lock
+++ b/js/yarn.lock
@@ -447,9 +447,9 @@ ansi-regex@^3.0.0:
integrity sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==
ansi-regex@^4.1.0:
- version "4.1.0"
- resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997"
- integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.1.tgz#164daac87ab2d6f6db3a29875e2d1766582dabed"
+ integrity sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==
ansi-regex@^5.0.0:
version "5.0.0"
@@ -563,14 +563,14 @@ array-unique@^0.3.2:
resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428"
integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=
-array.prototype.reduce@^1.0.4:
- version "1.0.4"
- resolved "https://registry.yarnpkg.com/array.prototype.reduce/-/array.prototype.reduce-1.0.4.tgz#8167e80089f78bff70a99e20bd4201d4663b0a6f"
- integrity sha512-WnM+AjG/DvLRLo4DDl+r+SvCzYtD2Jd9oeBYMcEaI7t3fFrHY9M53/wdLcTvmZNQ70IU6Htj0emFkZ5TS+lrdw==
+array.prototype.reduce@^1.0.5:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/array.prototype.reduce/-/array.prototype.reduce-1.0.5.tgz#6b20b0daa9d9734dd6bc7ea66b5bbce395471eac"
+ integrity sha512-kDdugMl7id9COE8R7MHF5jWk7Dqt/fs4Pv+JXoICnYwqpjjjbUurz6w5fT5IG6brLdJhv6/VoHB0H7oyIBXd+Q==
dependencies:
call-bind "^1.0.2"
- define-properties "^1.1.3"
- es-abstract "^1.19.2"
+ define-properties "^1.1.4"
+ es-abstract "^1.20.4"
es-array-method-boxes-properly "^1.0.0"
is-string "^1.0.7"
@@ -1248,7 +1248,7 @@ color-convert@^2.0.1:
color-name@1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
- integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=
+ integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==
color-name@~1.1.4:
version "1.1.4"
@@ -1608,9 +1608,9 @@ decamelize@^1.1.2, decamelize@^1.2.0:
integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=
decode-uri-component@^0.2.0:
- version "0.2.0"
- resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"
- integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=
+ version "0.2.2"
+ resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.2.tgz#e69dbe25d37941171dd540e024c444cd5188e1e9"
+ integrity sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==
deep-eql@^3.0.1:
version "3.0.1"
@@ -1851,22 +1851,23 @@ error-ex@^1.2.0:
dependencies:
is-arrayish "^0.2.1"
-es-abstract@^1.19.0, es-abstract@^1.19.2, es-abstract@^1.19.5, es-abstract@^1.20.1:
- version "1.20.2"
- resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.2.tgz#8495a07bc56d342a3b8ea3ab01bd986700c2ccb3"
- integrity sha512-XxXQuVNrySBNlEkTYJoDNFe5+s2yIOpzq80sUHEdPdQr0S5nTLz4ZPPPswNIpKseDDUS5yghX1gfLIHQZ1iNuQ==
+es-abstract@^1.19.0, es-abstract@^1.20.4:
+ version "1.20.5"
+ resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.5.tgz#e6dc99177be37cacda5988e692c3fa8b218e95d2"
+ integrity sha512-7h8MM2EQhsCA7pU/Nv78qOXFpD8Rhqd12gYiSJVkrH9+e8VuA8JlPJK/hQjjlLv6pJvx/z1iRFKzYb0XT/RuAQ==
dependencies:
call-bind "^1.0.2"
es-to-primitive "^1.2.1"
function-bind "^1.1.1"
function.prototype.name "^1.1.5"
- get-intrinsic "^1.1.2"
+ get-intrinsic "^1.1.3"
get-symbol-description "^1.0.0"
+ gopd "^1.0.1"
has "^1.0.3"
has-property-descriptors "^1.0.0"
has-symbols "^1.0.3"
internal-slot "^1.0.3"
- is-callable "^1.2.4"
+ is-callable "^1.2.7"
is-negative-zero "^2.0.2"
is-regex "^1.1.4"
is-shared-array-buffer "^1.0.2"
@@ -1876,8 +1877,9 @@ es-abstract@^1.19.0, es-abstract@^1.19.2, es-abstract@^1.19.5, es-abstract@^1.20
object-keys "^1.1.1"
object.assign "^4.1.4"
regexp.prototype.flags "^1.4.3"
- string.prototype.trimend "^1.0.5"
- string.prototype.trimstart "^1.0.5"
+ safe-regex-test "^1.0.0"
+ string.prototype.trimend "^1.0.6"
+ string.prototype.trimstart "^1.0.6"
unbox-primitive "^1.0.2"
es-array-method-boxes-properly@^1.0.0:
@@ -2337,7 +2339,7 @@ get-func-name@^2.0.0:
resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41"
integrity sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=
-get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1, get-intrinsic@^1.1.2:
+get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.3.tgz#063c84329ad93e83893c7f4f243ef63ffa351385"
integrity sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==
@@ -2519,6 +2521,13 @@ globby@^7.1.1:
pify "^3.0.0"
slash "^1.0.0"
+gopd@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c"
+ integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==
+ dependencies:
+ get-intrinsic "^1.1.3"
+
graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0:
version "4.2.4"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb"
@@ -2581,7 +2590,7 @@ has-flag@^1.0.0:
has-flag@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
- integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0=
+ integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==
has-flag@^4.0.0:
version "4.0.0"
@@ -2894,10 +2903,10 @@ is-buffer@~2.0.3:
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191"
integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==
-is-callable@^1.1.4, is-callable@^1.2.4:
- version "1.2.5"
- resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.5.tgz#6123e0b1fef5d7591514b371bb018204892f1a2b"
- integrity sha512-ZIWRujF6MvYGkEuHMYtFRkL2wAtFw89EHfKlXrkPkjQZZRWeh9L1q3SV13NIfHnqxugjLvAOkEHx9mb1zcMnEw==
+is-callable@^1.1.4, is-callable@^1.2.7:
+ version "1.2.7"
+ resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055"
+ integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==
is-ci@^2.0.0:
version "2.0.0"
@@ -2989,7 +2998,7 @@ is-fullwidth-code-point@^1.0.0:
is-fullwidth-code-point@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f"
- integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=
+ integrity sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==
is-fullwidth-code-point@^3.0.0:
version "3.0.0"
@@ -3145,7 +3154,7 @@ isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0:
isexe@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
- integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=
+ integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==
isobject@^2.0.0:
version "2.1.0"
@@ -3431,7 +3440,7 @@ loader-runner@^2.4.0:
resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.4.0.tgz#ed47066bfe534d7e84c4c7b9998c2a75607d9357"
integrity sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==
-loader-utils@^1.0.2, loader-utils@^1.1.0, loader-utils@^1.2.3, loader-utils@^1.4.0:
+loader-utils@^1.0.2, loader-utils@^1.1.0, loader-utils@^1.2.3:
version "1.4.0"
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.0.tgz#c579b5e34cb34b1a74edc6c1fb36bfa371d5a613"
integrity sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==
@@ -3440,6 +3449,15 @@ loader-utils@^1.0.2, loader-utils@^1.1.0, loader-utils@^1.2.3, loader-utils@^1.4
emojis-list "^3.0.0"
json5 "^1.0.1"
+loader-utils@^1.4.0:
+ version "1.4.2"
+ resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.2.tgz#29a957f3a63973883eb684f10ffd3d151fec01a3"
+ integrity sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==
+ dependencies:
+ big.js "^5.2.2"
+ emojis-list "^3.0.0"
+ json5 "^1.0.1"
+
locate-path@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e"
@@ -3704,11 +3722,16 @@ minimist@0.0.8:
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=
-minimist@^1.1.0, minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5:
+minimist@^1.1.0, minimist@^1.1.3:
version "1.2.5"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
+minimist@^1.2.0, minimist@^1.2.5:
+ version "1.2.7"
+ resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18"
+ integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==
+
mississippi@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/mississippi/-/mississippi-3.0.0.tgz#ea0a3291f97e0b5e8776b363d5f0a12d94c67022"
@@ -3837,11 +3860,16 @@ ms@2.1.1:
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a"
integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==
-ms@2.1.2, ms@^2.1.1:
+ms@2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
+ms@^2.1.1:
+ version "2.1.3"
+ resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
+ integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
+
nan@^2.12.1:
version "2.14.1"
resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.1.tgz#d7be34dfa3105b91494c3147089315eff8874b01"
@@ -4038,14 +4066,14 @@ object.assign@^4.1.4:
object-keys "^1.1.1"
object.getownpropertydescriptors@^2.0.3:
- version "2.1.4"
- resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.4.tgz#7965e6437a57278b587383831a9b829455a4bc37"
- integrity sha512-sccv3L/pMModT6dJAYF3fzGMVcb38ysQ0tEE6ixv2yXJDtEIPph268OlAdJj5/qZMZDq2g/jqvwppt36uS/uQQ==
+ version "2.1.5"
+ resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.5.tgz#db5a9002489b64eef903df81d6623c07e5b4b4d3"
+ integrity sha512-yDNzckpM6ntyQiGTik1fKV1DcVDRS+w8bvpWNCBanvH5LfRX9O8WTHqQzG4RZwRAM4I0oU7TV11Lj5v0g20ibw==
dependencies:
- array.prototype.reduce "^1.0.4"
+ array.prototype.reduce "^1.0.5"
call-bind "^1.0.2"
define-properties "^1.1.4"
- es-abstract "^1.20.1"
+ es-abstract "^1.20.4"
object.pick@^1.3.0:
version "1.3.0"
@@ -4194,7 +4222,7 @@ path-exists@^2.0.0:
path-exists@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515"
- integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=
+ integrity sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==
path-is-absolute@^1.0.0:
version "1.0.1"
@@ -4694,7 +4722,7 @@ request@^2.45.0:
require-directory@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
- integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I=
+ integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==
require-main-filename@^2.0.0:
version "2.0.0"
@@ -4814,6 +4842,15 @@ safe-buffer@~5.1.0, safe-buffer@~5.1.1:
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
+safe-regex-test@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.0.tgz#793b874d524eb3640d1873aad03596db2d4f2295"
+ integrity sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==
+ dependencies:
+ call-bind "^1.0.2"
+ get-intrinsic "^1.1.3"
+ is-regex "^1.1.4"
+
safe-regex@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e"
@@ -4881,7 +4918,7 @@ serialize-javascript@^3.1.0:
set-blocking@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
- integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc=
+ integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==
set-value@^2.0.0, set-value@^2.0.1:
version "2.0.1"
@@ -5117,7 +5154,7 @@ split-string@^3.0.1, split-string@^3.0.2:
sprintf-js@~1.0.2:
version "1.0.3"
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
- integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=
+ integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==
sshpk@^1.7.0:
version "1.16.1"
@@ -5216,23 +5253,23 @@ string-width@^4.2.0:
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.0"
-string.prototype.trimend@^1.0.5:
- version "1.0.5"
- resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz#914a65baaab25fbdd4ee291ca7dde57e869cb8d0"
- integrity sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==
+string.prototype.trimend@^1.0.6:
+ version "1.0.6"
+ resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz#c4a27fa026d979d79c04f17397f250a462944533"
+ integrity sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==
dependencies:
call-bind "^1.0.2"
define-properties "^1.1.4"
- es-abstract "^1.19.5"
+ es-abstract "^1.20.4"
-string.prototype.trimstart@^1.0.5:
- version "1.0.5"
- resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz#5466d93ba58cfa2134839f81d7f42437e8c01fef"
- integrity sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==
+string.prototype.trimstart@^1.0.6:
+ version "1.0.6"
+ resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz#e90ab66aa8e4007d92ef591bbf3cd422c56bdcf4"
+ integrity sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==
dependencies:
call-bind "^1.0.2"
define-properties "^1.1.4"
- es-abstract "^1.19.5"
+ es-abstract "^1.20.4"
string_decoder@^1.0.0, string_decoder@^1.1.1:
version "1.3.0"
@@ -5263,7 +5300,7 @@ strip-ansi@^3.0.0, strip-ansi@^3.0.1:
strip-ansi@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f"
- integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8=
+ integrity sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==
dependencies:
ansi-regex "^3.0.0"
@@ -5966,7 +6003,7 @@ which-boxed-primitive@^1.0.2:
which-module@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
- integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=
+ integrity sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==
which@1.3.1, which@^1.1.1, which@^1.2.14, which@^1.2.9, which@^1.3.1:
version "1.3.1"
diff --git a/package.json b/package.json
index ed5857e..723f587 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "apl-viewhost-web",
- "version": "2022.2.0",
+ "version": "2023.1.0",
"description": "This is a Web-assembly version (WASM) of apl viewhost web.",
"license": "Apache 2.0",
"repository": {
diff --git a/scripts/fetch.js b/scripts/fetch.js
index 6b10f4c..5b69509 100644
--- a/scripts/fetch.js
+++ b/scripts/fetch.js
@@ -3,7 +3,7 @@
const https = require('https');
const fs = require('fs');
-const artifactUrl = 'https://d1gkjrhppbyzyh.cloudfront.net/apl-viewhost-web/6dcd129a-c82d-46fe-ab8e-93b6f3ed1870/index.js';
+const artifactUrl = 'https://d1gkjrhppbyzyh.cloudfront.net/apl-viewhost-web/92777dcb-9ef0-4824-ba45-18b1505eb190/index.js';
const outputFilePath = 'index.js';
const outputFile = fs.createWriteStream(outputFilePath);
diff --git a/wasm/CMakeLists.txt b/wasm/CMakeLists.txt
index 174fc16..82c7c65 100644
--- a/wasm/CMakeLists.txt
+++ b/wasm/CMakeLists.txt
@@ -115,6 +115,7 @@ add_custom_target(generate-wasm-enums ALL
-f "SubmitKeyType"
-f "TextAlign"
-f "TextAlignVertical"
+ -f "TextTrackType"
-f "TrackState"
-f "UpdateType"
-f "VectorGraphicScale"
diff --git a/wasm/config.cmake b/wasm/config.cmake
index f249301..42ddf84 100644
--- a/wasm/config.cmake
+++ b/wasm/config.cmake
@@ -44,5 +44,5 @@ if(WASM_PROFILING)
endif()
#set compiler flags
-set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WASM_FLAGS} --bind")
-set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WASM_FLAGS} --bind")
+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WASM_FLAGS} --bind -O1")
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WASM_FLAGS} --bind -O1")
diff --git a/wasm/include/wasm/graphicpattern.h b/wasm/include/wasm/graphicpattern.h
new file mode 100644
index 0000000..f7138af
--- /dev/null
+++ b/wasm/include/wasm/graphicpattern.h
@@ -0,0 +1,29 @@
+/**
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef APL_WASM_GRAPHIC_PATTERN_H
+#define APL_WASM_GRAPHIC_PATTERN_H
+
+#include "apl/apl.h"
+#include
+
+namespace apl {
+namespace wasm {
+
+namespace internal {
+struct GraphicPatternMethods {
+ static std::string getId(const GraphicPatternPtr& graphicPattern);
+ static std::string getDescription(const GraphicPatternPtr& graphicPattern);
+ static double getHeight(const GraphicPatternPtr& graphicPattern);
+ static double getWidth(const GraphicPatternPtr& graphicPattern);
+ static size_t getItemCount(const GraphicPatternPtr& graphicPattern);
+ static GraphicElementPtr getItemAt(const GraphicPatternPtr& graphicPattern, size_t index);
+};
+} // namespace internal
+
+} // namespace wasm
+} // namespace apl
+
+#endif // APL_WASM_GRAPHIC_PATTERN_H
\ No newline at end of file
diff --git a/wasm/src/audioplayer.cpp b/wasm/src/audioplayer.cpp
index 92238f7..b71db7d 100644
--- a/wasm/src/audioplayer.cpp
+++ b/wasm/src/audioplayer.cpp
@@ -81,7 +81,7 @@ static inline apl::SpeechMarkType stringToMarkType(const std::string& type) {
auto result = apl::SpeechMarkType::kSpeechMarkUnknown;
if (type == "word") {
result = apl::SpeechMarkType::kSpeechMarkWord;
- } else if (type == "visime") {
+ } else if (type == "viseme") {
result = apl::SpeechMarkType::kSpeechMarkViseme;
} else if (type == "sentence") {
result = apl::SpeechMarkType::kSpeechMarkSentence;
diff --git a/wasm/src/context.cpp b/wasm/src/context.cpp
index 88cc5d3..b9eb684 100644
--- a/wasm/src/context.cpp
+++ b/wasm/src/context.cpp
@@ -268,10 +268,10 @@ ContextMethods::create(emscripten::val options, emscripten::val text, emscripten
// get document background, color or gradient
background.set("color", emscripten::val(Color().asString())); // Transparent
background.set("gradient", emscripten::val::null());
- if (contentPtr->getBackground(coreMetrics, rootConfig).isColor()) {
+ if (contentPtr->getBackground(coreMetrics, rootConfig).is()) {
background.set("color", emscripten::val(contentPtr->getBackground(coreMetrics, rootConfig).asColor().asString()));
- } else if (contentPtr->getBackground(coreMetrics, rootConfig).isGradient()) {
- background.set("gradient", emscripten::getValFromObject(contentPtr->getBackground(coreMetrics, rootConfig).getGradient(), m));
+ } else if (contentPtr->getBackground(coreMetrics, rootConfig).is()) {
+ background.set("gradient", emscripten::getValFromObject(contentPtr->getBackground(coreMetrics, rootConfig).get(), m));
}
// add metrics to the root context so we can connect our viewport to any events, components,
diff --git a/wasm/src/embindutils.cpp b/wasm/src/embindutils.cpp
index b72824e..dd5ef7a 100644
--- a/wasm/src/embindutils.cpp
+++ b/wasm/src/embindutils.cpp
@@ -21,59 +21,58 @@ iterateProps(const apl::CalculatedPropertyMap& calculated, emscripten::val& map,
emscripten::val
getValFromObject(const apl::Object& prop, WASMMetrics* m) {
- double scale;
- switch (prop.getType()) {
- case apl::Object::ObjectType::kNumberType:
- return emscripten::val(prop.asNumber());
- case apl::Object::ObjectType::kStringType:
- return emscripten::val(prop.asString());
- case apl::Object::ObjectType::kBoolType:
- return emscripten::val(prop.asBoolean());
- case apl::Object::ObjectType::kColorType:
- return emscripten::val(prop.asColor().get());
- case apl::Object::ObjectType::kAbsoluteDimensionType:
- scale = m->toViewhost(1.0f);
- return emscripten::val(prop.getAbsoluteDimension() * scale);
- case apl::Object::ObjectType::kFilterType:
- return getValFromObject(prop.getFilter(), m);
- case apl::Object::ObjectType::kRadiiType:
- return getValFromObject(prop.getRadii(), m);
- case apl::Object::ObjectType::kRectType:
- return getValFromObject(prop.getRect(), m);
- case apl::Object::ObjectType::kGradientType:
- return getValFromObject(prop.getGradient(), m);
- case apl::Object::ObjectType::kGraphicFilterType:
- return getValFromObject(prop.getGraphicFilter(), m);
- case apl::Object::ObjectType::kGraphicPatternType:
- return emscripten::val(prop.getGraphicPattern());
- case apl::Object::ObjectType::kMediaSourceType:
- return getValFromObject(prop.getMediaSource(), m);
- case apl::Object::ObjectType::kMapType:
- return getValFromObject(prop.getMap(), m);
- case apl::Object::ObjectType::kArrayType:
- return getValFromObject(prop.getArray(), m);
- case apl::Object::ObjectType::kStyledTextType:
- return getValFromObject(prop.getStyledText(), m);
- case apl::Object::ObjectType::kGraphicType: {
- // set the metrics here because we need them for scaling
- // a graphic element, which is derived from a graphic.
- auto graphic = prop.getGraphic();
- graphic->setUserData(m);
- return emscripten::val(graphic);
- }
- case apl::Object::ObjectType::kTransform2DType: {
- auto transform = prop.getTransform2D().get();
- auto mat = "matrix(" + std::to_string(transform[0]) + "," +
- std::to_string(transform[1]) + "," + std::to_string(transform[2]) + "," +
- std::to_string(transform[3]) + "," + std::to_string(transform[4]) +
- "," + std::to_string(transform[5]) + ")";
- return emscripten::val(mat);
- }
- case apl::Object::ObjectType::kURLRequestType:
- return getValFromObject(prop.getURLRequest(), m);
- default:
- return emscripten::val::undefined();
+ if (prop.isNumber())
+ return emscripten::val(prop.getDouble());
+ else if (prop.isBoolean())
+ return emscripten::val(prop.getBoolean());
+ else if (prop.isString())
+ return emscripten::val(prop.getString());
+ else if (prop.is())
+ return emscripten::val(prop.getColor());
+ else if (prop.isAbsoluteDimension()) {
+ auto scale = m->toViewhost(1.0f);
+ return emscripten::val(prop.getAbsoluteDimension() * scale);
}
+ else if (prop.is())
+ return getValFromObject(prop.get(), m);
+ else if (prop.is())
+ return getValFromObject(prop.get(), m);
+ else if (prop.is())
+ return getValFromObject(prop.get(), m);
+ else if (prop.is())
+ return getValFromObject(prop.get(), m);
+ else if (prop.is())
+ return getValFromObject(prop.get(), m);
+ else if (prop.is())
+ return emscripten::val(prop.get());
+ else if (prop.is())
+ return getValFromObject(prop.get(), m);
+ else if (prop.isMap())
+ return getValFromObject(prop.getMap(), m);
+ else if (prop.isArray())
+ return getValFromObject(prop.getArray(), m);
+ else if (prop.is())
+ return getValFromObject(prop.get(), m);
+ else if (prop.is()) {
+ // set the metrics here because we need them for scaling
+ // a graphic element, which is derived from a graphic.
+ auto graphic = prop.get();
+ graphic->setUserData(m);
+ return emscripten::val(graphic);
+ }
+ else if (prop.is()) {
+ auto transform = prop.get().get();
+ auto mat = "matrix(" + std::to_string(transform[0]) + "," +
+ std::to_string(transform[1]) + "," + std::to_string(transform[2]) + "," +
+ std::to_string(transform[3]) + "," + std::to_string(transform[4]) +
+ "," + std::to_string(transform[5]) + ")";
+ return emscripten::val(mat);
+ }
+ else if (prop.is())
+ return getValFromObject(prop.get(), m);
+
+ return emscripten::val::undefined();
+
}
emscripten::val
diff --git a/wasm/src/mediaplayer.cpp b/wasm/src/mediaplayer.cpp
index 21e69ab..25e385b 100644
--- a/wasm/src/mediaplayer.cpp
+++ b/wasm/src/mediaplayer.cpp
@@ -11,6 +11,15 @@
namespace apl {
namespace wasm {
+static std::string textTrackTypeToKind(TextTrackType type) {
+ switch (type) {
+ case TextTrackType::kTextTrackTypeCaption:
+ return "captions";
+ default:
+ return "unsupported";
+ }
+}
+
std::shared_ptr
MediaPlayer::create(apl::MediaPlayerCallback&& playerCallback,
emscripten::val MediaPlayerFactory)
@@ -58,16 +67,27 @@ MediaPlayer::setTrackList(std::vector tracks)
emscripten::val trackArray = emscripten::val::array();
- int i = 0;
- for (auto& track : tracks) {
+ for (int i = 0; i < tracks.size(); i++) {
+ auto& track = tracks[i];
emscripten::val trackObj = emscripten::val::object();
trackObj.set("url", track.url);
trackObj.set("offset", track.offset);
trackObj.set("duration", track.duration);
trackObj.set("repeatCount", track.repeatCount);
+ emscripten::val textTrackArray = emscripten::val::array();
+ for (int j = 0; j < track.textTracks.size(); j++) {
+ auto& textTrack = track.textTracks[j];
+ emscripten::val textTrackObj = emscripten::val::object();
+ textTrackObj.set("kind", textTrackTypeToKind(textTrack.type));
+ textTrackObj.set("url", textTrack.url);
+ textTrackObj.set("description", textTrack.description);
+
+ textTrackArray.set(j, textTrackObj);
+ }
+ trackObj.set("textTracks", textTrackArray);
+
trackArray.set(i, trackObj);
- i++;
}
mPlayer.call("setTrackList", trackArray);
@@ -207,7 +227,8 @@ void
MediaPlayer::doCallback(int eventType) {
if (!isActive()) return;
apl::MediaPlayerEventType mediaPlayerEventType = static_cast(eventType);
- if (mediaPlayerEventType == apl::MediaPlayerEventType::kMediaPlayerEventEnd) {
+ if (mediaPlayerEventType == apl::MediaPlayerEventType::kMediaPlayerEventEnd
+ || eventType == apl::MediaPlayerEventType::kMediaPlayerEventTrackFail) {
resolveExistingAction();
}
auto callback = mCallback;