Merge branch 'develop' of github.com:thecodingmachine/workadventure into adminSvelte

This commit is contained in:
GRL 2021-06-22 10:36:16 +02:00
commit 0cc7ed1647
60 changed files with 1525 additions and 1305 deletions

View file

@ -20,9 +20,11 @@
"jasmine": "^3.5.0",
"mini-css-extract-plugin": "^1.6.0",
"node-polyfill-webpack-plugin": "^1.1.2",
"npm-run-all": "^4.1.5",
"sass": "^1.32.12",
"sass-loader": "^11.1.0",
"svelte": "^3.38.2",
"svelte-check": "^2.1.0",
"svelte-loader": "^3.1.1",
"svelte-preprocess": "^4.7.3",
"ts-loader": "^9.1.2",
@ -38,6 +40,7 @@
"@types/simple-peer": "^9.6.0",
"@types/socket.io-client": "^1.4.32",
"axios": "^0.21.1",
"cross-env": "^7.0.3",
"generic-type-guard": "^3.2.0",
"google-protobuf": "^3.13.0",
"phaser": "^3.54.0",
@ -50,10 +53,13 @@
"standardized-audio-context": "^25.2.4"
},
"scripts": {
"start": "TS_NODE_PROJECT=\"tsconfig-for-webpack.json\" webpack serve --open",
"build": "TS_NODE_PROJECT=\"tsconfig-for-webpack.json\" NODE_ENV=production webpack",
"start": "run-p serve svelte-check-watch",
"serve": "cross-env TS_NODE_PROJECT=\"tsconfig-for-webpack.json\" webpack serve --open",
"build": "cross-env TS_NODE_PROJECT=\"tsconfig-for-webpack.json\" NODE_ENV=production webpack",
"test": "TS_NODE_PROJECT=\"tsconfig-for-jasmine.json\" ts-node node_modules/jasmine/bin/jasmine --config=jasmine.json",
"lint": "node_modules/.bin/eslint src/ . --ext .ts",
"fix": "node_modules/.bin/eslint --fix src/ . --ext .ts"
"fix": "node_modules/.bin/eslint --fix src/ . --ext .ts",
"svelte-check-watch": "svelte-check --fail-on-warnings --fail-on-hints --compiler-warnings \"a11y-no-onchange:ignore,a11y-autofocus:ignore\" --watch",
"svelte-check": "svelte-check --fail-on-warnings --fail-on-hints --compiler-warnings \"a11y-no-onchange:ignore,a11y-autofocus:ignore\""
}
}

View file

@ -5,11 +5,12 @@ import type { ChatEvent } from './ChatEvent';
import type { ClosePopupEvent } from './ClosePopupEvent';
import type { EnterLeaveEvent } from './EnterLeaveEvent';
import type { GoToPageEvent } from './GoToPageEvent';
import type { LoadPageEvent } from './LoadPageEvent';
import type { OpenCoWebSiteEvent } from './OpenCoWebSiteEvent';
import type { OpenPopupEvent } from './OpenPopupEvent';
import type { OpenTabEvent } from './OpenTabEvent';
import type { UserInputChatEvent } from './UserInputChatEvent';
import type {LoadSoundEvent} from "./LoadSoundEvent";
import type { LoadSoundEvent} from "./LoadSoundEvent";
import type {PlaySoundEvent} from "./PlaySoundEvent";
@ -20,6 +21,7 @@ export interface TypedMessageEvent<T> extends MessageEvent {
export type IframeEventMap = {
//getState: GameStateEvent,
// updateTile: UpdateTileEvent
loadPage: LoadPageEvent
chat: ChatEvent,
openPopup: OpenPopupEvent
closePopup: ClosePopupEvent
@ -33,7 +35,7 @@ export type IframeEventMap = {
removeBubble: null
loadSound: LoadSoundEvent
playSound: PlaySoundEvent
stopSound: null
stopSound: null,
}
export interface IframeEvent<T extends keyof IframeEventMap> {
type: T;

View file

@ -0,0 +1,13 @@
import * as tg from "generic-type-guard";
export const isLoadPageEvent =
new tg.IsInterface().withProperties({
url: tg.isString,
}).get();
/**
* A message sent from the iFrame to the game to add a message in the chat.
*/
export type LoadPageEvent = tg.GuardedType<typeof isLoadPageEvent>;

View file

@ -12,6 +12,7 @@ import { GoToPageEvent, isGoToPageEvent } from "./Events/GoToPageEvent";
import { isOpenCoWebsite, OpenCoWebSiteEvent } from "./Events/OpenCoWebSiteEvent";
import { IframeEventMap, IframeEvent, IframeResponseEvent, IframeResponseEventMap, isIframeEventWrapper, TypedMessageEvent } from "./Events/IframeEvent";
import type { UserInputChatEvent } from "./Events/UserInputChatEvent";
import { isLoadPageEvent } from './Events/LoadPageEvent';
import {isPlaySoundEvent, PlaySoundEvent} from "./Events/PlaySoundEvent";
import {isStopSoundEvent, StopSoundEvent} from "./Events/StopSoundEvent";
import {isLoadSoundEvent, LoadSoundEvent} from "./Events/LoadSoundEvent";
@ -32,6 +33,10 @@ class IframeListener {
private readonly _goToPageStream: Subject<GoToPageEvent> = new Subject();
public readonly goToPageStream = this._goToPageStream.asObservable();
private readonly _loadPageStream: Subject<string> = new Subject();
public readonly loadPageStream = this._loadPageStream.asObservable();
private readonly _openCoWebSiteStream: Subject<OpenCoWebSiteEvent> = new Subject();
public readonly openCoWebSiteStream = this._openCoWebSiteStream.asObservable();
@ -132,6 +137,8 @@ class IframeListener {
}
else if (payload.type === 'removeBubble') {
this._removeBubbleStream.next();
}else if (payload.type === 'loadPage' && isLoadPageEvent(payload.data)){
this._loadPageStream.next(payload.data.url);
}
}

View file

@ -0,0 +1,31 @@
import type * as tg from "generic-type-guard";
import type { IframeEvent, IframeEventMap, IframeResponseEventMap } from '../Events/IframeEvent';
export function sendToWorkadventure(content: IframeEvent<keyof IframeEventMap>) {
window.parent.postMessage(content, "*")
}
type GuardedType<Guard extends tg.TypeGuard<unknown>> = Guard extends tg.TypeGuard<infer T> ? T : never
export interface IframeCallback<Key extends keyof IframeResponseEventMap, T = IframeResponseEventMap[Key], Guard = tg.TypeGuard<T>> {
typeChecker: Guard,
callback: (payloadData: T) => void
}
export interface IframeCallbackContribution<Key extends keyof IframeResponseEventMap> extends IframeCallback<Key> {
type: Key
}
/**
* !! be aware that the implemented attributes (addMethodsAtRoot and subObjectIdentifier) must be readonly
*
*
*/
export abstract class IframeApiContribution<T extends {
callbacks: Array<IframeCallbackContribution<keyof IframeResponseEventMap>>,
}> {
abstract callbacks: T["callbacks"]
}

View file

@ -0,0 +1,39 @@
import {sendToWorkadventure} from "../IframeApiContribution";
import type {LoadSoundEvent} from "../../Events/LoadSoundEvent";
import type {PlaySoundEvent} from "../../Events/PlaySoundEvent";
import type {StopSoundEvent} from "../../Events/StopSoundEvent";
import SoundConfig = Phaser.Types.Sound.SoundConfig;
export class Sound {
constructor(private url: string) {
sendToWorkadventure({
"type": 'loadSound',
"data": {
url: this.url,
} as LoadSoundEvent
});
}
public play(config: SoundConfig) {
sendToWorkadventure({
"type": 'playSound',
"data": {
url: this.url,
config
} as PlaySoundEvent
});
return this.url;
}
public stop() {
sendToWorkadventure({
"type": 'stopSound',
"data": {
url: this.url,
} as StopSoundEvent
});
return this.url;
}
}

View file

@ -0,0 +1,18 @@
import type {Popup} from "./Popup";
export type ButtonClickedCallback = (popup: Popup) => void;
export interface ButtonDescriptor {
/**
* The label of the button
*/
label: string,
/**
* The type of the button. Can be one of "normal", "primary", "success", "warning", "error", "disabled"
*/
className?: "normal" | "primary" | "success" | "warning" | "error" | "disabled",
/**
* Callback called if the button is pressed
*/
callback: ButtonClickedCallback,
}

View file

@ -0,0 +1,19 @@
import {sendToWorkadventure} from "../IframeApiContribution";
import type {ClosePopupEvent} from "../../Events/ClosePopupEvent";
export class Popup {
constructor(private id: number) {
}
/**
* Closes the popup
*/
public close(): void {
sendToWorkadventure({
'type': 'closePopup',
'data': {
'popupId': this.id,
} as ClosePopupEvent
});
}
}

View file

@ -0,0 +1,38 @@
import type { ChatEvent } from '../Events/ChatEvent'
import { isUserInputChatEvent, UserInputChatEvent } from '../Events/UserInputChatEvent'
import { IframeApiContribution, sendToWorkadventure } from './IframeApiContribution'
import { apiCallback } from "./registeredCallbacks";
import {Subject} from "rxjs";
const chatStream = new Subject<string>();
class WorkadventureChatCommands extends IframeApiContribution<WorkadventureChatCommands> {
callbacks = [apiCallback({
callback: (event: UserInputChatEvent) => {
chatStream.next(event.message);
},
type: "userInputChat",
typeChecker: isUserInputChatEvent
})]
sendChatMessage(message: string, author: string) {
sendToWorkadventure({
type: 'chat',
data: {
'message': message,
'author': author
} as ChatEvent
})
}
/**
* Listen to messages sent by the local user, in the chat.
*/
onChatMessage(callback: (message: string) => void) {
chatStream.subscribe(callback);
}
}
export default new WorkadventureChatCommands()

View file

@ -0,0 +1,16 @@
import { IframeApiContribution, sendToWorkadventure } from './IframeApiContribution';
class WorkadventureControlsCommands extends IframeApiContribution<WorkadventureControlsCommands> {
callbacks = []
disablePlayerControls(): void {
sendToWorkadventure({ 'type': 'disablePlayerControls', data: null });
}
restorePlayerControls(): void {
sendToWorkadventure({ 'type': 'restorePlayerControls', data: null });
}
}
export default new WorkadventureControlsCommands();

View file

@ -0,0 +1,56 @@
import type { GoToPageEvent } from '../Events/GoToPageEvent';
import type { OpenTabEvent } from '../Events/OpenTabEvent';
import { IframeApiContribution, sendToWorkadventure } from './IframeApiContribution';
import type {OpenCoWebSiteEvent} from "../Events/OpenCoWebSiteEvent";
class WorkadventureNavigationCommands extends IframeApiContribution<WorkadventureNavigationCommands> {
callbacks = []
openTab(url: string): void {
sendToWorkadventure({
"type": 'openTab',
"data": {
url
} as OpenTabEvent
});
}
goToPage(url: string): void {
sendToWorkadventure({
"type": 'goToPage',
"data": {
url
} as GoToPageEvent
});
}
goToRoom(url: string): void {
sendToWorkadventure({
"type": 'loadPage',
"data": {
url
}
});
}
openCoWebSite(url: string): void {
sendToWorkadventure({
"type": 'openCoWebSite',
"data": {
url
} as OpenCoWebSiteEvent
});
}
closeCoWebSite(): void {
sendToWorkadventure({
"type": 'closeCoWebSite',
data: null
});
}
}
export default new WorkadventureNavigationCommands();

View file

@ -0,0 +1,16 @@
import type {IframeResponseEventMap} from "../../Api/Events/IframeEvent";
import type {IframeCallback} from "../../Api/iframe/IframeApiContribution";
import type {IframeCallbackContribution} from "../../Api/iframe/IframeApiContribution";
export const registeredCallbacks: { [K in keyof IframeResponseEventMap]?: IframeCallback<K> } = {}
export function apiCallback<T extends keyof IframeResponseEventMap>(callbackData: IframeCallbackContribution<T>): IframeCallbackContribution<keyof IframeResponseEventMap> {
const iframeCallback = {
typeChecker: callbackData.typeChecker,
callback: callbackData.callback
} as IframeCallback<T>;
const newCallback = { [callbackData.type]: iframeCallback };
Object.assign(registeredCallbacks, newCallback)
return callbackData as unknown as IframeCallbackContribution<keyof IframeResponseEventMap>;
}

View file

@ -0,0 +1,50 @@
import { Subject } from "rxjs";
import { EnterLeaveEvent, isEnterLeaveEvent } from '../Events/EnterLeaveEvent';
import { IframeApiContribution } from './IframeApiContribution';
import { apiCallback } from "./registeredCallbacks";
const enterStreams: Map<string, Subject<EnterLeaveEvent>> = new Map<string, Subject<EnterLeaveEvent>>();
const leaveStreams: Map<string, Subject<EnterLeaveEvent>> = new Map<string, Subject<EnterLeaveEvent>>();
class WorkadventureRoomCommands extends IframeApiContribution<WorkadventureRoomCommands> {
callbacks = [
apiCallback({
callback: (payloadData: EnterLeaveEvent) => {
enterStreams.get(payloadData.name)?.next();
},
type: "enterEvent",
typeChecker: isEnterLeaveEvent
}),
apiCallback({
type: "leaveEvent",
typeChecker: isEnterLeaveEvent,
callback: (payloadData) => {
leaveStreams.get(payloadData.name)?.next();
}
})
]
onEnterZone(name: string, callback: () => void): void {
let subject = enterStreams.get(name);
if (subject === undefined) {
subject = new Subject<EnterLeaveEvent>();
enterStreams.set(name, subject);
}
subject.subscribe(callback);
}
onLeaveZone(name: string, callback: () => void): void {
let subject = leaveStreams.get(name);
if (subject === undefined) {
subject = new Subject<EnterLeaveEvent>();
leaveStreams.set(name, subject);
}
subject.subscribe(callback);
}
}
export default new WorkadventureRoomCommands();

View file

@ -0,0 +1,17 @@
import type { LoadSoundEvent } from '../Events/LoadSoundEvent';
import type { PlaySoundEvent } from '../Events/PlaySoundEvent';
import type { StopSoundEvent } from '../Events/StopSoundEvent';
import { IframeApiContribution, sendToWorkadventure } from './IframeApiContribution';
import {Sound} from "./Sound/Sound";
class WorkadventureSoundCommands extends IframeApiContribution<WorkadventureSoundCommands> {
callbacks = []
loadSound(url: string): Sound {
return new Sound(url);
}
}
export default new WorkadventureSoundCommands();

View file

@ -0,0 +1,83 @@
import { isButtonClickedEvent } from '../Events/ButtonClickedEvent';
import type { ClosePopupEvent } from '../Events/ClosePopupEvent';
import { IframeApiContribution, sendToWorkadventure } from './IframeApiContribution';
import { apiCallback } from "./registeredCallbacks";
import {Popup} from "./Ui/Popup";
import type {ButtonClickedCallback, ButtonDescriptor} from "./Ui/ButtonDescriptor";
let popupId = 0;
const popups: Map<number, Popup> = new Map<number, Popup>();
const popupCallbacks: Map<number, Map<number, ButtonClickedCallback>> = new Map<number, Map<number, ButtonClickedCallback>>();
interface ZonedPopupOptions {
zone: string
objectLayerName?: string,
popupText: string,
delay?: number
popupOptions: Array<ButtonDescriptor>
}
class WorkAdventureUiCommands extends IframeApiContribution<WorkAdventureUiCommands> {
callbacks = [apiCallback({
type: "buttonClickedEvent",
typeChecker: isButtonClickedEvent,
callback: (payloadData) => {
const callback = popupCallbacks.get(payloadData.popupId)?.get(payloadData.buttonId);
const popup = popups.get(payloadData.popupId);
if (popup === undefined) {
throw new Error('Could not find popup with ID "' + payloadData.popupId + '"');
}
if (callback) {
callback(popup);
}
}
})];
openPopup(targetObject: string, message: string, buttons: ButtonDescriptor[]): Popup {
popupId++;
const popup = new Popup(popupId);
const btnMap = new Map<number, () => void>();
popupCallbacks.set(popupId, btnMap);
let id = 0;
for (const button of buttons) {
const callback = button.callback;
if (callback) {
btnMap.set(id, () => {
callback(popup);
});
}
id++;
}
sendToWorkadventure({
'type': 'openPopup',
'data': {
popupId,
targetObject,
message,
buttons: buttons.map((button) => {
return {
label: button.label,
className: button.className
};
})
}
});
popups.set(popupId, popup)
return popup;
}
displayBubble(): void {
sendToWorkadventure({ 'type': 'displayBubble', data: null });
}
removeBubble(): void {
sendToWorkadventure({ 'type': 'removeBubble', data: null });
}
}
export default new WorkAdventureUiCommands();

View file

@ -1,6 +1,4 @@
<script lang="typescript">
import MenuIcon from "./Menu/MenuIcon.svelte";
import {menuIconVisible} from "../Stores/MenuStore";
import {enableCameraSceneVisibilityStore, gameOverlayVisibilityStore} from "../Stores/MediaStore";
import CameraControls from "./CameraControls.svelte";
import MyCamera from "./MyCamera.svelte";
@ -17,7 +15,7 @@
import VisitCard from "./VisitCard/VisitCard.svelte";
import {requestVisitCardsStore} from "../Stores/GameStore";
import {Game} from "../Phaser/Game/Game";
import type {Game} from "../Phaser/Game/Game";
import {helpCameraSettingsVisibleStore} from "../Stores/HelpCameraSettingsStore";
import HelpCameraSettingsPopup from "./HelpCameraSettings/HelpCameraSettingsPopup.svelte";
import AudioPlaying from "./UI/AudioPlaying.svelte";
@ -81,7 +79,7 @@
{/if}
{#if $helpCameraSettingsVisibleStore}
<div>
<HelpCameraSettingsPopup game={ game }></HelpCameraSettingsPopup>
<HelpCameraSettingsPopup></HelpCameraSettingsPopup>
</div>
{/if}
{#if $requestVisitCardsStore}

View file

@ -1,10 +1,10 @@
<script lang="typescript">
import { Game } from "../../Phaser/Game/Game";
import { CustomizeSceneName } from "../../Phaser/Login/CustomizeScene";
import type { Game } from "../../Phaser/Game/Game";
import {CustomizeScene, CustomizeSceneName} from "../../Phaser/Login/CustomizeScene";
export let game: Game;
const customCharacterScene = game.scene.getScene(CustomizeSceneName);
const customCharacterScene = game.scene.getScene(CustomizeSceneName) as CustomizeScene;
let activeRow = customCharacterScene.activeRow;
function selectLeft() {
@ -116,4 +116,4 @@
}
</style>
</style>

View file

@ -1,5 +1,5 @@
<script lang="typescript">
import {Game} from "../../Phaser/Game/Game";
import type {Game} from "../../Phaser/Game/Game";
import {EnableCameraScene, EnableCameraSceneName} from "../../Phaser/Login/EnableCameraScene";
import {
audioConstraintStore,
@ -15,8 +15,8 @@
import microphoneImg from "../images/microphone.svg";
export let game: Game;
let selectedCamera : string|null = null;
let selectedMicrophone : string|null = null;
let selectedCamera : string|undefined = undefined;
let selectedMicrophone : string|undefined = undefined;
const enableCameraScene = game.scene.getScene(EnableCameraSceneName) as EnableCameraScene;
@ -24,10 +24,10 @@
enableCameraScene.login();
}
function srcObject(node, stream) {
function srcObject(node: HTMLVideoElement, stream: MediaStream) {
node.srcObject = stream;
return {
update(newStream) {
update(newStream: MediaStream) {
if (node.srcObject != newStream) {
node.srcObject = newStream
}
@ -53,8 +53,8 @@
}
} else {
stream = null;
selectedCamera = null;
selectedMicrophone = null;
selectedCamera = undefined;
selectedMicrophone = undefined;
}
});
@ -79,7 +79,7 @@
<section class="text-center">
<h2>Turn on your camera and microphone</h2>
</section>
{#if $localStreamStore.stream}
{#if $localStreamStore.type === 'success' && $localStreamStore.stream}
<video class="myCamVideoSetup" use:srcObject={$localStreamStore.stream} autoplay muted playsinline></video>
{:else }
<div class="webrtcsetup">

View file

@ -8,7 +8,7 @@
const NB_BARS = 20;
let timeout;
let timeout: ReturnType<typeof setTimeout>;
const soundMeter = new SoundMeter();
let display = false;

View file

@ -1,14 +1,13 @@
<script lang="typescript">
import {Game} from "../../Phaser/Game/Game";
import {LoginSceneName} from "../../Phaser/Login/LoginScene";
import type {Game} from "../../Phaser/Game/Game";
import {LoginScene, LoginSceneName} from "../../Phaser/Login/LoginScene";
import {DISPLAY_TERMS_OF_USE, MAX_USERNAME_LENGTH} from "../../Enum/EnvironmentVariable";
import logoImg from "../images/logo.png";
import {gameManager} from "../../Phaser/Game/GameManager";
import {maxUserNameLength} from "../../Connexion/LocalUser";
export let game: Game;
const loginScene = game.scene.getScene(LoginSceneName);
const loginScene = game.scene.getScene(LoginSceneName) as LoginScene;
let name = gameManager.getPlayerName() || '';
let startValidating = false;

View file

@ -3,10 +3,10 @@
import SoundMeterWidget from "./SoundMeterWidget.svelte";
import {onDestroy} from "svelte";
function srcObject(node, stream) {
function srcObject(node: HTMLVideoElement, stream: MediaStream) {
node.srcObject = stream;
return {
update(newStream) {
update(newStream: MediaStream) {
if (node.srcObject != newStream) {
node.srcObject = newStream
}
@ -38,9 +38,9 @@
<div>
<div class="video-container div-myCamVideo" class:hide={!$localStreamStore.constraints.video}>
{#if $localStreamStore.type === "success" && $localStreamStore.stream }
<video class="myCamVideo" use:srcObject={$localStreamStore.stream} autoplay muted playsinline></video>
<!-- {#if stream}
<SoundMeterWidget stream={stream}></SoundMeterWidget>
{/if} -->
{/if}
</div>
</div>

View file

@ -1,10 +1,10 @@
<script lang="typescript">
import {Game} from "../../Phaser/Game/Game";
import {SelectCompanionSceneName} from "../../Phaser/Login/SelectCompanionScene";
import type {Game} from "../../Phaser/Game/Game";
import {SelectCompanionScene, SelectCompanionSceneName} from "../../Phaser/Login/SelectCompanionScene";
export let game: Game;
const selectCompanionScene = game.scene.getScene(SelectCompanionSceneName);
const selectCompanionScene = game.scene.getScene(SelectCompanionSceneName) as SelectCompanionScene;
function selectLeft() {
selectCompanionScene.moveToLeft();

View file

@ -8,7 +8,7 @@
const NB_BARS = 5;
let timeout;
let timeout: ReturnType<typeof setTimeout>;
const soundMeter = new SoundMeter();
let display = false;

View file

@ -2,7 +2,7 @@
import { fly } from 'svelte/transition';
import megaphoneImg from "./images/megaphone.svg";
import {soundPlayingStore} from "../../Stores/SoundPlayingStore";
import {afterUpdate, onMount} from "svelte";
import {afterUpdate} from "svelte";
export let url: string;
let audio: HTMLAudioElement;

View file

@ -7,7 +7,7 @@
let w = '500px';
let h = '250px';
let hidden = true;
let cvIframe;
let cvIframe: HTMLIFrameElement;
function closeCard() {
requestVisitCardsStore.set(null);

View file

@ -1,10 +1,10 @@
<script lang="typescript">
import { Game } from "../../Phaser/Game/Game";
import { SelectCharacterSceneName } from "../../Phaser/Login/SelectCharacterScene";
import type { Game } from "../../Phaser/Game/Game";
import {SelectCharacterScene, SelectCharacterSceneName} from "../../Phaser/Login/SelectCharacterScene";
export let game: Game;
const selectCharacterScene = game.scene.getScene(SelectCharacterSceneName);
const selectCharacterScene = game.scene.getScene(SelectCharacterSceneName) as SelectCharacterScene;
function selectLeft() {
selectCharacterScene.moveToLeft();
@ -89,4 +89,4 @@
}
</style>
</style>

View file

@ -10,7 +10,7 @@ export interface CharacterTexture {
export const maxUserNameLength: number = MAX_USERNAME_LENGTH;
export function isUserNameValid(value: unknown): boolean {
return typeof value === "string" && value.length > 0 && value.length <= maxUserNameLength && value.indexOf(' ') === -1;
return typeof value === "string" && value.length > 0 && value.length <= maxUserNameLength && /\S/.test(value);
}
export function areCharacterLayersValid(value: string[] | null): boolean {

View file

@ -79,6 +79,7 @@ import CanvasTexture = Phaser.Textures.CanvasTexture;
import GameObject = Phaser.GameObjects.GameObject;
import FILE_LOAD_ERROR = Phaser.Loader.Events.FILE_LOAD_ERROR;
import DOMElement = Phaser.GameObjects.DOMElement;
import EVENT_TYPE =Phaser.Scenes.Events
import type {Subscription} from "rxjs";
import {worldFullMessageStream} from "../../Connexion/WorldFullMessageStream";
import { lazyLoadCompanionResource } from "../Companion/CompanionTexturesLoadingManager";
@ -917,7 +918,13 @@ ${escapedMessage}
this.iframeSubscriptionList.push(iframeListener.enablePlayerControlStream.subscribe(()=>{
this.userInputManager.restoreControls();
}));
this.iframeSubscriptionList.push(iframeListener.loadPageStream.subscribe((url:string)=>{
this.loadNextGame(url).then(()=>{
this.events.once(EVENT_TYPE.POST_UPDATE,()=>{
this.onMapExit(url);
})
})
}));
let scriptedBubbleSprite : Sprite;
this.iframeSubscriptionList.push(iframeListener.displayBubbleStream.subscribe(()=>{
scriptedBubbleSprite = new Sprite(this,this.CurrentPlayer.x + 25,this.CurrentPlayer.y,'circleSprite-white');
@ -1092,10 +1099,10 @@ ${escapedMessage}
}
//todo: push that into the gameManager
private loadNextGame(exitSceneIdentifier: string): void {
private loadNextGame(exitSceneIdentifier: string): Promise<void> {
const {roomId, hash} = Room.getIdFromIdentifier(exitSceneIdentifier, this.MapUrlFile, this.instance);
const room = new Room(roomId);
gameManager.loadMap(room, this.scene).catch(() => {});
return gameManager.loadMap(room, this.scene).catch(() => {});
}
private startUser(layer: ITiledMapTileLayer): PositionInterface {

View file

@ -2,26 +2,20 @@ import {EnableCameraSceneName} from "./EnableCameraScene";
import Rectangle = Phaser.GameObjects.Rectangle;
import {loadAllLayers} from "../Entity/PlayerTexturesLoadingManager";
import Sprite = Phaser.GameObjects.Sprite;
import Container = Phaser.GameObjects.Container;
import {gameManager} from "../Game/GameManager";
import {localUserStore} from "../../Connexion/LocalUserStore";
import {addLoader} from "../Components/Loader";
import type {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures";
import {AbstractCharacterScene} from "./AbstractCharacterScene";
import {areCharacterLayersValid} from "../../Connexion/LocalUser";
import { MenuScene } from "../Menu/MenuScene";
import { SelectCharacterSceneName } from "./SelectCharacterScene";
import {customCharacterSceneVisibleStore} from "../../Stores/CustomCharacterStore";
import {selectCharacterSceneVisibleStore} from "../../Stores/SelectCharacterStore";
import {waScaleManager} from "../Services/WaScaleManager";
import {isMobile} from "../../Enum/EnvironmentVariable";
import {CustomizedCharacter} from "../Entity/CustomizedCharacter";
export const CustomizeSceneName = "CustomizeScene";
export const CustomizeSceneKey = "CustomizeScene";
const customizeSceneKey = 'customizeScene';
export class CustomizeScene extends AbstractCharacterScene {
private Rectangle!: Rectangle;
@ -42,7 +36,6 @@ export class CustomizeScene extends AbstractCharacterScene {
}
preload() {
this.load.html(customizeSceneKey, 'resources/html/CustomCharacterScene.html');
this.loadCustomSceneSelectCharacters().then((bodyResourceDescriptions) => {
bodyResourceDescriptions.forEach((bodyResourceDescription) => {
@ -111,7 +104,15 @@ export class CustomizeScene extends AbstractCharacterScene {
this.onResize();
}
public doMoveCursorHorizontally(index: number): void {
public moveCursorHorizontally(index: number): void {
this.moveHorizontally = index;
}
public moveCursorVertically(index: number): void {
this.moveVertically = index;
}
private doMoveCursorHorizontally(index: number): void {
this.selectedLayers[this.activeRow] += index;
if (this.selectedLayers[this.activeRow] < 0) {
this.selectedLayers[this.activeRow] = 0
@ -123,7 +124,7 @@ export class CustomizeScene extends AbstractCharacterScene {
this.saveInLocalStorage();
}
public doMoveCursorVertically(index:number): void {
private doMoveCursorVertically(index:number): void {
this.activeRow += index;
if (this.activeRow < 0) {
@ -262,7 +263,7 @@ export class CustomizeScene extends AbstractCharacterScene {
this.Rectangle.y = this.cameras.main.worldView.y + this.cameras.main.height / 3;
}
private nextSceneToCamera(){
public nextSceneToCamera(){
const layers: string[] = [];
let i = 0;
for (const layerItem of this.selectedLayers) {
@ -283,7 +284,7 @@ export class CustomizeScene extends AbstractCharacterScene {
customCharacterSceneVisibleStore.set(false);
}
private backToPreviousScene(){
public backToPreviousScene(){
this.scene.sleep(CustomizeSceneName);
waScaleManager.restoreZoom();
this.scene.run(SelectCharacterSceneName);

View file

@ -10,17 +10,13 @@ import {AbstractCharacterScene} from "./AbstractCharacterScene";
import {areCharacterLayersValid} from "../../Connexion/LocalUser";
import {touchScreenManager} from "../../Touch/TouchScreenManager";
import {PinchManager} from "../UserInput/PinchManager";
import {MenuScene} from "../Menu/MenuScene";
import {selectCharacterSceneVisibleStore} from "../../Stores/SelectCharacterStore";
import {customCharacterSceneVisibleStore} from "../../Stores/CustomCharacterStore";
import {waScaleManager} from "../Services/WaScaleManager";
import {isMobile} from "../../Enum/EnvironmentVariable";
//todo: put this constants in a dedicated file
export const SelectCharacterSceneName = "SelectCharacterScene";
const selectCharacterKey = 'selectCharacterScene';
export class SelectCharacterScene extends AbstractCharacterScene {
protected readonly nbCharactersPerRow = 6;
protected selectedPlayer!: Phaser.Physics.Arcade.Sprite|null; // null if we are selecting the "customize" option
@ -29,7 +25,6 @@ export class SelectCharacterScene extends AbstractCharacterScene {
protected selectedRectangle!: Rectangle;
protected selectCharacterSceneElement!: Phaser.GameObjects.DOMElement;
protected currentSelectUser = 0;
protected pointerClicked: boolean = false;
protected pointerTimer: number = 0;
@ -43,7 +38,6 @@ export class SelectCharacterScene extends AbstractCharacterScene {
}
preload() {
this.load.html(selectCharacterKey, 'resources/html/selectCharacterScene.html');
this.loadSelectSceneCharacters().then((bodyResourceDescriptions) => {
bodyResourceDescriptions.forEach((bodyResourceDescription) => {
@ -98,7 +92,7 @@ export class SelectCharacterScene extends AbstractCharacterScene {
});
}
protected nextSceneToCameraScene(): void {
public nextSceneToCameraScene(): void {
if (this.selectedPlayer !== null && !areCharacterLayersValid([this.selectedPlayer.texture.key])) {
return;
}
@ -114,7 +108,7 @@ export class SelectCharacterScene extends AbstractCharacterScene {
this.events.removeListener('wake');
}
protected nextSceneToCustomizeScene(): void {
public nextSceneToCustomizeScene(): void {
if (this.selectedPlayer !== null && !areCharacterLayersValid([this.selectedPlayer.texture.key])) {
return;
}
@ -124,7 +118,7 @@ export class SelectCharacterScene extends AbstractCharacterScene {
selectCharacterSceneVisibleStore.set(false);
}
createCurrentPlayer(): void {
createCurrentPlayer(): void {
for (let i = 0; i <this.playerModels.length; i++) {
const playerResource = this.playerModels[i];
@ -171,7 +165,7 @@ export class SelectCharacterScene extends AbstractCharacterScene {
this.updateSelectedPlayer();
}
protected moveToLeft(){
public moveToLeft(){
if(this.currentSelectUser === 0){
return;
}
@ -179,7 +173,7 @@ export class SelectCharacterScene extends AbstractCharacterScene {
this.moveUser();
}
protected moveToRight(){
public moveToRight(){
if(this.currentSelectUser === (this.players.length - 1)){
return;
}

View file

@ -1,6 +1,7 @@
import {HtmlUtils} from "./HtmlUtils";
import {Subject} from "rxjs";
import {iframeListener} from "../Api/IframeListener";
import {touchScreenManager} from "../Touch/TouchScreenManager";
enum iframeStates {
closed = 1,
@ -17,6 +18,11 @@ const cowebsiteOpenFullScreenImageId = 'cowebsite-fullscreen-open';
const cowebsiteCloseFullScreenImageId = 'cowebsite-fullscreen-close';
const animationTime = 500; //time used by the css transitions, in ms.
interface TouchMoveCoordinates {
x: number;
y: number;
}
class CoWebsiteManager {
private opened: iframeStates = iframeStates.closed;
@ -32,6 +38,7 @@ class CoWebsiteManager {
private resizing: boolean = false;
private cowebsiteMainDom: HTMLDivElement;
private cowebsiteAsideDom: HTMLDivElement;
private previousTouchMoveCoordinates: TouchMoveCoordinates|null = null; //only use on touchscreens to track touch movement
get width(): number {
return this.cowebsiteDiv.clientWidth;
@ -62,7 +69,10 @@ class CoWebsiteManager {
this.cowebsiteMainDom = HtmlUtils.getElementByIdOrFail<HTMLDivElement>(cowebsiteMainDomId);
this.cowebsiteAsideDom = HtmlUtils.getElementByIdOrFail<HTMLDivElement>(cowebsiteAsideDomId);
this.initResizeListeners();
if (touchScreenManager.supportTouchScreen) {
this.initResizeListeners(true);
}
this.initResizeListeners(false);
const buttonCloseFrame = HtmlUtils.getElementByIdOrFail(cowebsiteCloseButtonId);
buttonCloseFrame.addEventListener('click', () => {
@ -77,22 +87,43 @@ class CoWebsiteManager {
});
}
private initResizeListeners() {
const movecallback = (event:MouseEvent) => {
this.verticalMode ? this.height += event.movementY / this.getDevicePixelRatio() : this.width -= event.movementX / this.getDevicePixelRatio();
private initResizeListeners(touchMode:boolean) {
const movecallback = (event:MouseEvent|TouchEvent) => {
let x, y;
if (event.type === 'mousemove') {
x = (event as MouseEvent).movementX / this.getDevicePixelRatio();
y = (event as MouseEvent).movementY / this.getDevicePixelRatio();
} else {
const touchEvent = (event as TouchEvent).touches[0];
const last = {x: touchEvent.pageX, y: touchEvent.pageY};
const previous = this.previousTouchMoveCoordinates as TouchMoveCoordinates;
this.previousTouchMoveCoordinates = last;
x = last.x - previous.x;
y = last.y - previous.y;
}
this.verticalMode ? this.height += y : this.width -= x;
this.fire();
}
this.cowebsiteAsideDom.addEventListener('mousedown', (event) => {
this.cowebsiteAsideDom.addEventListener( touchMode ? 'touchstart' : 'mousedown', (event) => {
this.resizing = true;
this.getIframeDom().style.display = 'none';
if (touchMode) {
const touchEvent = (event as TouchEvent).touches[0];
this.previousTouchMoveCoordinates = {x: touchEvent.pageX, y: touchEvent.pageY};
}
document.addEventListener('mousemove', movecallback);
document.addEventListener(touchMode ? 'touchmove' : 'mousemove', movecallback);
});
document.addEventListener('mouseup', (event) => {
document.addEventListener(touchMode ? 'touchend' : 'mouseup', (event) => {
if (!this.resizing) return;
document.removeEventListener('mousemove', movecallback);
if (touchMode) {
this.previousTouchMoveCoordinates = null;
}
document.removeEventListener(touchMode ? 'touchmove' : 'mousemove', movecallback);
this.getIframeDom().style.display = 'block';
this.resizing = false;
});

38
front/src/ambient.d.ts vendored Normal file
View file

@ -0,0 +1,38 @@
/**
* These declarations tell TypeScript that we allow import of images, e.g.
* ```
<script lang='ts'>
import successkid from 'images/successkid.jpg';
</script>
<img src="{successkid}">
```
*/
declare module "*.gif" {
const value: string;
export = value;
}
declare module "*.jpg" {
const value: string;
export = value;
}
declare module "*.jpeg" {
const value: string;
export = value;
}
declare module "*.png" {
const value: string;
export = value;
}
declare module "*.svg" {
const value: string;
export = value;
}
declare module "*.webp" {
const value: string;
export = value;
}

View file

@ -1,275 +1,175 @@
import type { ChatEvent } from "./Api/Events/ChatEvent";
import { isIframeResponseEventWrapper } from "./Api/Events/IframeEvent";
import { isUserInputChatEvent, UserInputChatEvent } from "./Api/Events/UserInputChatEvent";
import { Subject } from "rxjs";
import { EnterLeaveEvent, isEnterLeaveEvent } from "./Api/Events/EnterLeaveEvent";
import type { OpenPopupEvent } from "./Api/Events/OpenPopupEvent";
import { isButtonClickedEvent } from "./Api/Events/ButtonClickedEvent";
import type { ClosePopupEvent } from "./Api/Events/ClosePopupEvent";
import type { OpenTabEvent } from "./Api/Events/OpenTabEvent";
import type { GoToPageEvent } from "./Api/Events/GoToPageEvent";
import type { OpenCoWebSiteEvent } from "./Api/Events/OpenCoWebSiteEvent";
import type {PlaySoundEvent} from "./Api/Events/PlaySoundEvent";
import type {StopSoundEvent} from "./Api/Events/StopSoundEvent";
import type {LoadSoundEvent} from "./Api/Events/LoadSoundEvent";
import SoundConfig = Phaser.Types.Sound.SoundConfig;
import {registeredCallbacks} from "./Api/iframe/registeredCallbacks";
import {
IframeResponseEvent,
IframeResponseEventMap,
isIframeResponseEventWrapper,
TypedMessageEvent
} from "./Api/Events/IframeEvent";
import chat from "./Api/iframe/chat";
import type {IframeCallback} from './Api/iframe/IframeApiContribution';
import nav from "./Api/iframe/nav";
import controls from "./Api/iframe/controls";
import ui from "./Api/iframe/ui";
import sound from "./Api/iframe/sound";
import room from "./Api/iframe/room";
import type {ButtonDescriptor} from "./Api/iframe/Ui/ButtonDescriptor";
import type {Popup} from "./Api/iframe/Ui/Popup";
import type {Sound} from "./Api/iframe/Sound/Sound";
interface WorkAdventureApi {
sendChatMessage(message: string, author: string): void;
onChatMessage(callback: (message: string) => void): void;
onEnterZone(name: string, callback: () => void): void;
onLeaveZone(name: string, callback: () => void): void;
openPopup(targetObject: string, message: string, buttons: ButtonDescriptor[]): Popup;
openTab(url : string): void;
goToPage(url : string): void;
openCoWebSite(url : string): void;
closeCoWebSite(): void;
disablePlayerControls(): void;
restorePlayerControls(): void;
displayBubble(): void;
removeBubble(): void;
loadSound(url : string): Sound;
}
const wa = {
ui,
nav,
controls,
chat,
sound,
room,
// All methods below are deprecated and should not be used anymore.
// They are kept here for backward compatibility.
/**
* @deprecated Use WA.chat.sendChatMessage instead
*/
sendChatMessage(message: string, author: string): void {
console.warn('Method WA.sendChatMessage is deprecated. Please use WA.chat.sendChatMessage instead');
chat.sendChatMessage(message, author);
},
/**
* @deprecated Use WA.chat.disablePlayerControls instead
*/
disablePlayerControls(): void {
console.warn('Method WA.disablePlayerControls is deprecated. Please use WA.controls.disablePlayerControls instead');
controls.disablePlayerControls();
},
/**
* @deprecated Use WA.controls.restorePlayerControls instead
*/
restorePlayerControls(): void {
console.warn('Method WA.restorePlayerControls is deprecated. Please use WA.controls.restorePlayerControls instead');
controls.restorePlayerControls();
},
/**
* @deprecated Use WA.ui.displayBubble instead
*/
displayBubble(): void {
console.warn('Method WA.displayBubble is deprecated. Please use WA.ui.displayBubble instead');
ui.displayBubble();
},
/**
* @deprecated Use WA.ui.removeBubble instead
*/
removeBubble(): void {
console.warn('Method WA.removeBubble is deprecated. Please use WA.ui.removeBubble instead');
ui.removeBubble();
},
/**
* @deprecated Use WA.nav.openTab instead
*/
openTab(url: string): void {
console.warn('Method WA.openTab is deprecated. Please use WA.nav.openTab instead');
nav.openTab(url);
},
/**
* @deprecated Use WA.sound.loadSound instead
*/
loadSound(url: string) : Sound {
console.warn('Method WA.loadSound is deprecated. Please use WA.sound.loadSound instead');
return sound.loadSound(url);
},
/**
* @deprecated Use WA.nav.goToPage instead
*/
goToPage(url : string) : void {
console.warn('Method WA.goToPage is deprecated. Please use WA.nav.goToPage instead');
nav.goToPage(url);
},
/**
* @deprecated Use WA.nav.goToRoom instead
*/
goToRoom(url: string): void {
console.warn('Method WA.goToRoom is deprecated. Please use WA.nav.goToRoom instead');
nav.goToRoom(url);
},
/**
* @deprecated Use WA.nav.openCoWebSite instead
*/
openCoWebSite(url : string) : void{
console.warn('Method WA.openCoWebSite is deprecated. Please use WA.nav.openCoWebSite instead');
nav.openCoWebSite(url);
},
/**
* @deprecated Use WA.nav.closeCoWebSite instead
*/
closeCoWebSite(): void {
console.warn('Method WA.closeCoWebSite is deprecated. Please use WA.nav.closeCoWebSite instead');
nav.closeCoWebSite();
},
/**
* @deprecated Use WA.controls.restorePlayerControls instead
*/
openPopup(targetObject: string, message: string, buttons: ButtonDescriptor[]): Popup {
console.warn('Method WA.openPopup is deprecated. Please use WA.ui.openPopup instead');
return ui.openPopup(targetObject, message, buttons);
},
/**
* @deprecated Use WA.chat.onChatMessage instead
*/
onChatMessage(callback: (message: string) => void): void {
console.warn('Method WA.onChatMessage is deprecated. Please use WA.chat.onChatMessage instead');
chat.onChatMessage(callback);
},
/**
* @deprecated Use WA.room.onEnterZone instead
*/
onEnterZone(name: string, callback: () => void): void {
console.warn('Method WA.onEnterZone is deprecated. Please use WA.room.onEnterZone instead');
room.onEnterZone(name, callback);
},
/**
* @deprecated Use WA.room.onLeaveZone instead
*/
onLeaveZone(name: string, callback: () => void): void {
console.warn('Method WA.onLeaveZone is deprecated. Please use WA.room.onLeaveZone instead');
room.onLeaveZone(name, callback);
},
};
export type WorkAdventureApi = typeof wa;
declare global {
// eslint-disable-next-line no-var
var WA: WorkAdventureApi
interface Window {
WA: WorkAdventureApi
}
let WA: WorkAdventureApi
}
type ChatMessageCallback = (message: string) => void;
type ButtonClickedCallback = (popup: Popup) => void;
window.WA = wa;
const userInputChatStream: Subject<UserInputChatEvent> = new Subject();
const enterStreams: Map<string, Subject<EnterLeaveEvent>> = new Map<string, Subject<EnterLeaveEvent>>();
const leaveStreams: Map<string, Subject<EnterLeaveEvent>> = new Map<string, Subject<EnterLeaveEvent>>();
const popups: Map<number, Popup> = new Map<number, Popup>();
const popupCallbacks: Map<number, Map<number, ButtonClickedCallback>> = new Map<number, Map<number, ButtonClickedCallback>>();
let popupId = 0;
interface ButtonDescriptor {
/**
* The label of the button
*/
label: string,
/**
* The type of the button. Can be one of "normal", "primary", "success", "warning", "error", "disabled"
*/
className?: "normal" | "primary" | "success" | "warning" | "error" | "disabled",
/**
* Callback called if the button is pressed
*/
callback: ButtonClickedCallback,
}
export class Popup {
constructor(private id: number) {
}
/**
* Closes the popup
*/
public close(): void {
window.parent.postMessage({
'type': 'closePopup',
'data': {
'popupId': this.id,
} as ClosePopupEvent
}, '*');
}
}
export class Sound {
constructor(private url: string) {
window.parent.postMessage({
"type" : 'loadSound',
"data": {
url: this.url,
} as LoadSoundEvent
},'*');
}
public play(config : SoundConfig) {
window.parent.postMessage({
"type" : 'playSound',
"data": {
url: this.url,
config
} as PlaySoundEvent
},'*');
return this.url;
}
public stop() {
window.parent.postMessage({
"type" : 'stopSound',
"data": {
url: this.url,
} as StopSoundEvent
},'*');
return this.url;
}
}
window.WA = {
/**
* Send a message in the chat.
* Only the local user will receive this message.
*/
sendChatMessage(message: string, author: string) {
window.parent.postMessage({
'type': 'chat',
'data': {
'message': message,
'author': author
} as ChatEvent
}, '*');
},
disablePlayerControls(): void {
window.parent.postMessage({ 'type': 'disablePlayerControls' }, '*');
},
restorePlayerControls(): void {
window.parent.postMessage({ 'type': 'restorePlayerControls' }, '*');
},
displayBubble(): void {
window.parent.postMessage({ 'type': 'displayBubble' }, '*');
},
removeBubble(): void {
window.parent.postMessage({ 'type': 'removeBubble' }, '*');
},
openTab(url: string): void {
window.parent.postMessage({
"type": 'openTab',
"data": {
url
} as OpenTabEvent
}, '*');
},
loadSound(url: string) : Sound {
return new Sound(url);
},
goToPage(url : string) : void{
window.parent.postMessage({
"type": 'goToPage',
"data": {
url
} as GoToPageEvent
}, '*');
},
openCoWebSite(url : string) : void{
window.parent.postMessage({
"type" : 'openCoWebSite',
"data" : {
url
} as OpenCoWebSiteEvent
}, '*');
},
closeCoWebSite(): void {
window.parent.postMessage({
"type": 'closeCoWebSite'
}, '*');
},
openPopup(targetObject: string, message: string, buttons: ButtonDescriptor[]): Popup {
popupId++;
const popup = new Popup(popupId);
const btnMap = new Map<number, () => void>();
popupCallbacks.set(popupId, btnMap);
let id = 0;
for (const button of buttons) {
const callback = button.callback;
if (callback) {
btnMap.set(id, () => {
callback(popup);
});
}
id++;
}
window.parent.postMessage({
'type': 'openPopup',
'data': {
popupId,
targetObject,
message,
buttons: buttons.map((button) => {
return {
label: button.label,
className: button.className
};
})
} as OpenPopupEvent
}, '*');
popups.set(popupId, popup)
return popup;
},
/**
* Listen to messages sent by the local user, in the chat.
*/
onChatMessage(callback: ChatMessageCallback): void {
userInputChatStream.subscribe((userInputChatEvent) => {
callback(userInputChatEvent.message);
});
},
onEnterZone(name: string, callback: () => void): void {
let subject = enterStreams.get(name);
if (subject === undefined) {
subject = new Subject<EnterLeaveEvent>();
enterStreams.set(name, subject);
}
subject.subscribe(callback);
},
onLeaveZone(name: string, callback: () => void): void {
let subject = leaveStreams.get(name);
if (subject === undefined) {
subject = new Subject<EnterLeaveEvent>();
leaveStreams.set(name, subject);
}
subject.subscribe(callback);
},
}
window.addEventListener('message', message => {
window.addEventListener('message', <T extends keyof IframeResponseEventMap>(message: TypedMessageEvent<IframeResponseEvent<T>>) => {
if (message.source !== window.parent) {
return; // Skip message in this event listener
}
const payload = message.data;
console.debug(payload);
if (isIframeResponseEventWrapper(payload)) {
const payloadData = payload.data;
if (payload.type === 'userInputChat' && isUserInputChatEvent(payloadData)) {
userInputChatStream.next(payloadData);
} else if (payload.type === 'enterEvent' && isEnterLeaveEvent(payloadData)) {
enterStreams.get(payloadData.name)?.next();
} else if (payload.type === 'leaveEvent' && isEnterLeaveEvent(payloadData)) {
leaveStreams.get(payloadData.name)?.next();
} else if (payload.type === 'buttonClickedEvent' && isButtonClickedEvent(payloadData)) {
const callback = popupCallbacks.get(payloadData.popupId)?.get(payloadData.buttonId);
const popup = popups.get(payloadData.popupId);
if (popup === undefined) {
throw new Error('Could not find popup with ID "' + payloadData.popupId + '"');
}
if (callback) {
callback(popup);
}
}
const callback = registeredCallbacks[payload.type] as IframeCallback<T> | undefined
if (callback?.typeChecker(payloadData)) {
callback?.callback(payloadData)
}
}
// ...

View file

@ -28,6 +28,7 @@
aside {
height: 30px;
min-height: 30px;
cursor: ns-resize;
flex-direction: column;

View file

@ -13,6 +13,8 @@
iframe {
width: 100%;
height: 100%;
max-width: 100vw;
max-height: 100vh;
}
}

View file

@ -651,7 +651,7 @@ anymatch@^2.0.0:
micromatch "^3.1.4"
normalize-path "^2.1.1"
anymatch@~3.1.1:
anymatch@~3.1.1, anymatch@~3.1.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716"
integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==
@ -1112,7 +1112,7 @@ caniuse-lite@^1.0.30001219:
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001228.tgz#bfdc5942cd3326fa51ee0b42fbef4da9d492a7fa"
integrity sha512-QQmLOGJ3DEgokHbMSA8cj2a+geXqmnpyOFT0lhQV6P3/YOJvGDEwoedcwxEQ30gJIwIIunHIicunJ2rzK5gB2A==
chalk@^2.0.0:
chalk@^2.0.0, chalk@^2.4.1:
version "2.4.2"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
@ -1163,6 +1163,21 @@ chokidar@^2.1.8:
optionalDependencies:
fsevents "^1.2.7"
chokidar@^3.4.1:
version "3.5.2"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.2.tgz#dba3976fcadb016f66fd365021d91600d01c1e75"
integrity sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==
dependencies:
anymatch "~3.1.2"
braces "~3.0.2"
glob-parent "~5.1.2"
is-binary-path "~2.1.0"
is-glob "~4.0.1"
normalize-path "~3.0.0"
readdirp "~3.6.0"
optionalDependencies:
fsevents "~2.3.2"
chrome-trace-event@^1.0.2:
version "1.0.3"
resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac"
@ -1402,7 +1417,7 @@ create-require@^1.1.0:
resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333"
integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==
cross-spawn@^6.0.0:
cross-spawn@^6.0.0, cross-spawn@^6.0.5:
version "6.0.5"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4"
integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==
@ -2365,7 +2380,7 @@ fsevents@^1.2.7:
bindings "^1.5.0"
nan "^2.12.1"
fsevents@~2.3.1:
fsevents@~2.3.1, fsevents@~2.3.2:
version "2.3.2"
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==
@ -2429,7 +2444,7 @@ glob-parent@^3.1.0:
is-glob "^3.1.0"
path-dirname "^1.0.0"
glob-parent@^5.0.0, glob-parent@^5.1.0, glob-parent@~5.1.0:
glob-parent@^5.0.0, glob-parent@^5.1.0, glob-parent@~5.1.0, glob-parent@~5.1.2:
version "5.1.2"
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
@ -2606,6 +2621,11 @@ hmac-drbg@^1.0.1:
minimalistic-assert "^1.0.0"
minimalistic-crypto-utils "^1.0.1"
hosted-git-info@^2.1.4:
version "2.8.9"
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9"
integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==
hpack.js@^2.1.6:
version "2.1.6"
resolved "https://registry.yarnpkg.com/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2"
@ -3177,7 +3197,7 @@ js-yaml@^3.13.1:
argparse "^1.0.7"
esprima "^4.0.0"
json-parse-better-errors@^1.0.2:
json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9"
integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==
@ -3282,6 +3302,16 @@ linked-list-typescript@^1.0.11:
resolved "https://registry.yarnpkg.com/linked-list-typescript/-/linked-list-typescript-1.0.15.tgz#faeed93cf9203f102e2158c29edcddda320abe82"
integrity sha512-RIyUu9lnJIyIaMe63O7/aFv/T2v3KsMFuXMBbUQCHX+cgtGro86ETDj5ed0a8gQL2+DFjzYYsgVG4I36/cUwgw==
load-json-file@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b"
integrity sha1-L19Fq5HjMhYjT9U62rZo607AmTs=
dependencies:
graceful-fs "^4.1.2"
parse-json "^4.0.0"
pify "^3.0.0"
strip-bom "^3.0.0"
loader-runner@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.2.0.tgz#d7022380d66d14c5fb1d496b89864ebcfd478384"
@ -3396,6 +3426,11 @@ memory-fs@^0.4.1:
errno "^0.1.3"
readable-stream "^2.0.1"
memorystream@^0.3.1:
version "0.3.1"
resolved "https://registry.yarnpkg.com/memorystream/-/memorystream-0.3.1.tgz#86d7090b30ce455d63fbae12dda51a47ddcaf9b2"
integrity sha1-htcJCzDORV1j+64S3aUaR93K+bI=
merge-descriptors@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
@ -3529,6 +3564,11 @@ mkdirp@^0.5.1, mkdirp@^0.5.5:
dependencies:
minimist "^1.2.5"
mri@^1.1.0:
version "1.1.6"
resolved "https://registry.yarnpkg.com/mri/-/mri-1.1.6.tgz#49952e1044db21dbf90f6cd92bc9c9a777d415a6"
integrity sha512-oi1b3MfbyGa7FJMP9GmLTttni5JoICpYBRlq+x5V16fZbLsnL9N3wFqqIm/nIG43FjUFkFh9Epzp/kzUGUnJxQ==
ms@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
@ -3657,6 +3697,16 @@ node-releases@^1.1.71:
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.72.tgz#14802ab6b1039a79a0c7d662b610a5bbd76eacbe"
integrity sha512-LLUo+PpH3dU6XizX3iVoubUNheF/owjXCZZ5yACDxNnPtgFuludV1ZL3ayK1kVep42Rmm0+R9/Y60NQbZ2bifw==
normalize-package-data@^2.3.2:
version "2.5.0"
resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8"
integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==
dependencies:
hosted-git-info "^2.1.4"
resolve "^1.10.0"
semver "2 || 3 || 4 || 5"
validate-npm-package-license "^3.0.1"
normalize-path@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9"
@ -3669,6 +3719,21 @@ normalize-path@^3.0.0, normalize-path@~3.0.0:
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
npm-run-all@^4.1.5:
version "4.1.5"
resolved "https://registry.yarnpkg.com/npm-run-all/-/npm-run-all-4.1.5.tgz#04476202a15ee0e2e214080861bff12a51d98fba"
integrity sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==
dependencies:
ansi-styles "^3.2.1"
chalk "^2.4.1"
cross-spawn "^6.0.5"
memorystream "^0.3.1"
minimatch "^3.0.4"
pidtree "^0.3.0"
read-pkg "^3.0.0"
shell-quote "^1.6.1"
string.prototype.padend "^3.0.0"
npm-run-path@^2.0.0:
version "2.0.2"
resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f"
@ -3899,6 +3964,14 @@ parse-asn1@^5.0.0, parse-asn1@^5.1.5:
pbkdf2 "^3.0.3"
safe-buffer "^5.1.1"
parse-json@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0"
integrity sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=
dependencies:
error-ex "^1.3.1"
json-parse-better-errors "^1.0.1"
parse-json@^5.0.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd"
@ -3987,6 +4060,13 @@ path-to-regexp@0.1.7:
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=
path-type@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f"
integrity sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==
dependencies:
pify "^3.0.0"
path-type@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
@ -4034,11 +4114,21 @@ picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3:
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.3.tgz#465547f359ccc206d3c48e46a1bcb89bf7ee619d"
integrity sha512-KpELjfwcCDUb9PeigTs2mBJzXUPzAuP2oPcA989He8Rte0+YUAjw1JVedDhuTKPkHjSYzMN3npC9luThGYEKdg==
pidtree@^0.3.0:
version "0.3.1"
resolved "https://registry.yarnpkg.com/pidtree/-/pidtree-0.3.1.tgz#ef09ac2cc0533df1f3250ccf2c4d366b0d12114a"
integrity sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==
pify@^2.0.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw=
pify@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176"
integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=
pify@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231"
@ -4288,6 +4378,15 @@ raw-body@2.4.0:
iconv-lite "0.4.24"
unpipe "1.0.0"
read-pkg@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389"
integrity sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=
dependencies:
load-json-file "^4.0.0"
normalize-package-data "^2.3.2"
path-type "^3.0.0"
readable-stream@^2.0.1, readable-stream@^2.0.2:
version "2.3.7"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
@ -4326,6 +4425,13 @@ readdirp@~3.5.0:
dependencies:
picomatch "^2.2.1"
readdirp@~3.6.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7"
integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==
dependencies:
picomatch "^2.2.1"
rechoir@^0.7.0:
version "0.7.0"
resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.7.0.tgz#32650fd52c21ab252aa5d65b19310441c7e03aca"
@ -4444,7 +4550,7 @@ resolve-url@^0.2.1:
resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a"
integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=
resolve@^1.9.0:
resolve@^1.10.0, resolve@^1.9.0:
version "1.20.0"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975"
integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==
@ -4503,6 +4609,13 @@ rxjs@^6.6.3:
dependencies:
tslib "^1.9.0"
sade@^1.7.4:
version "1.7.4"
resolved "https://registry.yarnpkg.com/sade/-/sade-1.7.4.tgz#ea681e0c65d248d2095c90578c03ca0bb1b54691"
integrity sha512-y5yauMD93rX840MwUJr7C1ysLFBgMspsdTo4UVrDg3fXDvtwOyIqykhVAAm6fk/3au77773itJStObgK+LKaiA==
dependencies:
mri "^1.1.0"
safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
version "5.1.2"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
@ -4579,7 +4692,7 @@ selfsigned@^1.10.8:
dependencies:
node-forge "^0.10.0"
semver@^5.5.0:
"semver@2 || 3 || 4 || 5", semver@^5.5.0:
version "5.7.1"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
@ -4714,6 +4827,11 @@ shebang-regex@^3.0.0:
resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172"
integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
shell-quote@^1.6.1:
version "1.7.2"
resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.2.tgz#67a7d02c76c9da24f99d20808fcaded0e0e04be2"
integrity sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==
signal-exit@^3.0.0, signal-exit@^3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c"
@ -4867,6 +4985,32 @@ source-map@^0.7.3, source-map@~0.7.2:
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383"
integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==
spdx-correct@^3.0.0:
version "3.1.1"
resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9"
integrity sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==
dependencies:
spdx-expression-parse "^3.0.0"
spdx-license-ids "^3.0.0"
spdx-exceptions@^2.1.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d"
integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==
spdx-expression-parse@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679"
integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==
dependencies:
spdx-exceptions "^2.1.0"
spdx-license-ids "^3.0.0"
spdx-license-ids@^3.0.0:
version "3.0.9"
resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.9.tgz#8a595135def9592bda69709474f1cbeea7c2467f"
integrity sha512-Ki212dKK4ogX+xDo4CtOZBVIwhsKBEfsEEcwmJfLQzirgc2jIWdzg40Unxz/HzEUqM1WFzVlQSMF9kZZ2HboLQ==
spdy-transport@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/spdy-transport/-/spdy-transport-3.0.0.tgz#00d4863a6400ad75df93361a1608605e5dcdcf31"
@ -4960,6 +5104,15 @@ string-width@^4.2.0:
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.0"
string.prototype.padend@^3.0.0:
version "3.1.2"
resolved "https://registry.yarnpkg.com/string.prototype.padend/-/string.prototype.padend-3.1.2.tgz#6858ca4f35c5268ebd5e8615e1327d55f59ee311"
integrity sha512-/AQFLdYvePENU3W5rgurfWSMU6n+Ww8n/3cUt7E+vPBB/D7YDG8x+qjoFs4M/alR2bW7Qg6xMjVwWUOvuQ0XpQ==
dependencies:
call-bind "^1.0.2"
define-properties "^1.1.3"
es-abstract "^1.18.0-next.2"
string.prototype.trimend@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz#e75ae90c2942c63504686c18b287b4a0b1a45f80"
@ -5059,6 +5212,21 @@ supports-color@^7.0.0, supports-color@^7.1.0:
dependencies:
has-flag "^4.0.0"
svelte-check@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/svelte-check/-/svelte-check-2.1.0.tgz#3ee8ad86068256346ebca862bbee8417a757fc53"
integrity sha512-kKLXkFt0XZTn+O1fnilGTQ1SFLsOFF+lXp1YjPfeN9nX+Y3ZpELtZSQCkbuK6HMkWugFvsOM17FCOSa1mfrEFA==
dependencies:
chalk "^4.0.0"
chokidar "^3.4.1"
glob "^7.1.6"
import-fresh "^3.2.1"
minimist "^1.2.5"
sade "^1.7.4"
source-map "^0.7.3"
svelte-preprocess "^4.0.0"
typescript "*"
svelte-dev-helper@^1.1.9:
version "1.1.9"
resolved "https://registry.yarnpkg.com/svelte-dev-helper/-/svelte-dev-helper-1.1.9.tgz#7d187db5c6cdbbd64d75a32f91b8998bde3273c3"
@ -5078,7 +5246,7 @@ svelte-loader@^3.1.1:
svelte-dev-helper "^1.1.9"
svelte-hmr "^0.12.3"
svelte-preprocess@^4.7.3:
svelte-preprocess@^4.0.0, svelte-preprocess@^4.7.3:
version "4.7.3"
resolved "https://registry.yarnpkg.com/svelte-preprocess/-/svelte-preprocess-4.7.3.tgz#454fa059c2400b15e7a3caeca18993cff9df0e96"
integrity sha512-Zx1/xLeGOIBlZMGPRCaXtlMe4ZA0faato5Dc3CosEqwu75MIEPuOstdkH6cy+RYTUYynoxzNaDxkPX4DbrPwRA==
@ -5283,6 +5451,11 @@ type-is@~1.6.17, type-is@~1.6.18:
media-typer "0.3.0"
mime-types "~2.1.24"
typescript@*:
version "4.3.2"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.2.tgz#399ab18aac45802d6f2498de5054fcbbe716a805"
integrity sha512-zZ4hShnmnoVnAHpVHWpTcxdv7dWP60S2FsydQLV8V5PbS3FifjWFFRiHSWpDJahly88PRyV5teTSLoq4eG7mKw==
typescript@^4.2.4:
version "4.2.4"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.2.4.tgz#8610b59747de028fda898a8aef0e103f156d0961"
@ -5408,6 +5581,14 @@ v8-compile-cache@^2.0.3, v8-compile-cache@^2.2.0:
resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee"
integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==
validate-npm-package-license@^3.0.1:
version "3.0.4"
resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a"
integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==
dependencies:
spdx-correct "^3.0.0"
spdx-expression-parse "^3.0.0"
vary@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"