Implement new cowbesite system on API

This commit is contained in:
Alexis Faizeau 2021-10-12 10:38:49 +02:00
parent c29c4bfaa4
commit b81b1ff68b
18 changed files with 1089 additions and 84 deletions

View file

@ -49,19 +49,36 @@ WA.nav.goToRoom('../otherMap/map.json');
WA.nav.goToRoom("/_/global/<path to global map>.json#start-layer-2") WA.nav.goToRoom("/_/global/<path to global map>.json#start-layer-2")
``` ```
### Opening/closing a web page in an iFrame ### Opening/closing web page in Co-Websites
``` ```
WA.nav.openCoWebSite(url: string, allowApi: boolean = false, allowPolicy: string = ""): void WA.nav.openCoWebsite(url: string, allowApi: boolean = false, allowPolicy: string = "", position: number = 0): Promise
WA.nav.closeCoWebSite(): void WA.nav.closeCoWebsite(coWebsiteId: string): Promise
WA.nav.closeCoWebsites(): Promise
``` ```
Opens the webpage at "url" in an iFrame (on the right side of the screen) or close that iFrame. `allowApi` allows the webpage to use the "IFrame API" and execute script (it is equivalent to putting the `openWebsiteAllowApi` property in the map). `allowPolicy` grants additional access rights to the iFrame. The `allowPolicy` parameter is turned into an [`allow` feature policy in the iFrame](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe#attr-allow). Opens the webpage at "url" in an iFrame (on the right side of the screen) or close that iFrame. `allowApi` allows the webpage to use the "IFrame API" and execute script (it is equivalent to putting the `openWebsiteAllowApi` property in the map). `allowPolicy` grants additional access rights to the iFrame. The `allowPolicy` parameter is turned into an [`allow` feature policy in the iFrame](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe#attr-allow), position in whitch slot the web page will be open.
Example: Example:
```javascript ```javascript
WA.nav.openCoWebSite('https://www.wikipedia.org/'); const coWebsite = await WA.nav.openCoWebsite('https://www.wikipedia.org/');
const coWebsiteWorkAdventure = await WA.nav.openCoWebsite('https://workadventu.re/', true, "", 1);
// ... // ...
WA.nav.closeCoWebSite(); WA.nav.closeCoWebsite(coWebsite.id);
WA.nav.closeCoWebsites();
```
### Opening/closing web page in Co-Websites
```
WA.nav.getCoWebsites(): Promise
```
Get all opened co-websites withe their ids and positions.
Example:
```javascript
const coWebsites = await WA.nav.getCowebsites();
``` ```

View file

@ -111,8 +111,7 @@
</div> </div>
<div id="cowebsite-sub-icons"></div> <div id="cowebsite-sub-icons"></div>
</aside> </aside>
<main id="cowebsite-slot-0"> <main id="cowebsite-slot-0"></main>
</main>
</div> </div>
<div id="cowebsite-buffer"></div> <div id="cowebsite-buffer"></div>
</div> </div>

View file

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

View file

@ -5,11 +5,10 @@ import type { ClosePopupEvent } from "./ClosePopupEvent";
import type { EnterLeaveEvent } from "./EnterLeaveEvent"; import type { EnterLeaveEvent } from "./EnterLeaveEvent";
import type { GoToPageEvent } from "./GoToPageEvent"; import type { GoToPageEvent } from "./GoToPageEvent";
import type { LoadPageEvent } from "./LoadPageEvent"; import type { LoadPageEvent } from "./LoadPageEvent";
import type { OpenCoWebSiteEvent } from "./OpenCoWebSiteEvent"; import { coWebsite, isOpenCoWebsiteEvent } from "./OpenCoWebsiteEvent";
import type { OpenPopupEvent } from "./OpenPopupEvent"; import type { OpenPopupEvent } from "./OpenPopupEvent";
import type { OpenTabEvent } from "./OpenTabEvent"; import type { OpenTabEvent } from "./OpenTabEvent";
import type { UserInputChatEvent } from "./UserInputChatEvent"; import type { UserInputChatEvent } from "./UserInputChatEvent";
import type { MapDataEvent } from "./MapDataEvent";
import type { LayerEvent } from "./LayerEvent"; import type { LayerEvent } from "./LayerEvent";
import type { SetPropertyEvent } from "./setPropertyEvent"; import type { SetPropertyEvent } from "./setPropertyEvent";
import type { LoadSoundEvent } from "./LoadSoundEvent"; import type { LoadSoundEvent } from "./LoadSoundEvent";
@ -27,9 +26,6 @@ import type { LoadTilesetEvent } from "./LoadTilesetEvent";
import { isLoadTilesetEvent } from "./LoadTilesetEvent"; import { isLoadTilesetEvent } from "./LoadTilesetEvent";
import type { import type {
MessageReferenceEvent, MessageReferenceEvent,
removeActionMessage,
triggerActionMessage,
TriggerActionMessageEvent,
} from "./ui/TriggerActionMessageEvent"; } from "./ui/TriggerActionMessageEvent";
import { isMessageReferenceEvent, isTriggerActionMessageEvent } from "./ui/TriggerActionMessageEvent"; import { isMessageReferenceEvent, isTriggerActionMessageEvent } from "./ui/TriggerActionMessageEvent";
import type { MenuRegisterEvent, UnregisterMenuEvent } from "./ui/MenuRegisterEvent"; import type { MenuRegisterEvent, UnregisterMenuEvent } from "./ui/MenuRegisterEvent";
@ -48,8 +44,6 @@ export type IframeEventMap = {
closePopup: ClosePopupEvent; closePopup: ClosePopupEvent;
openTab: OpenTabEvent; openTab: OpenTabEvent;
goToPage: GoToPageEvent; goToPage: GoToPageEvent;
openCoWebSite: OpenCoWebSiteEvent;
closeCoWebSite: null;
disablePlayerControls: null; disablePlayerControls: null;
restorePlayerControls: null; restorePlayerControls: null;
displayBubble: null; displayBubble: null;
@ -118,6 +112,22 @@ export const iframeQueryMapTypeGuards = {
query: isLoadTilesetEvent, query: isLoadTilesetEvent,
answer: tg.isNumber, answer: tg.isNumber,
}, },
openCoWebsite: {
query: isOpenCoWebsiteEvent,
answer: coWebsite
},
getCoWebsites: {
query: tg.isUndefined,
answer: tg.isArray(coWebsite)
},
closeCoWebsite: {
query: tg.isString,
answer: tg.isUndefined
},
closeCoWebsites: {
query: tg.isUndefined,
answer: tg.isUndefined
},
triggerActionMessage: { triggerActionMessage: {
query: isTriggerActionMessageEvent, query: isTriggerActionMessageEvent,
answer: tg.isUndefined, answer: tg.isUndefined,

View file

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

View file

@ -0,0 +1,22 @@
import * as tg from "generic-type-guard";
export const isOpenCoWebsiteEvent = new tg.IsInterface()
.withProperties({
url: tg.isString,
allowApi: tg.isOptional(tg.isBoolean),
allowPolicy: tg.isOptional(tg.isString),
position: tg.isOptional(tg.isNumber)
})
.get();
export const coWebsite = new tg.IsInterface()
.withProperties({
id: tg.isString,
position: tg.isNumber,
})
.get();
/**
* A message sent from the iFrame to the game to add a message in the chat.
*/
export type OpenCoWebsiteEvent = tg.GuardedType<typeof isOpenCoWebsiteEvent>;

View file

@ -1,6 +1,5 @@
import { Subject } from "rxjs"; import { Subject } from "rxjs";
import type * as tg from "generic-type-guard"; import { isChatEvent } from "./Events/ChatEvent";
import { ChatEvent, isChatEvent } from "./Events/ChatEvent";
import { HtmlUtils } from "../WebRtc/HtmlUtils"; import { HtmlUtils } from "../WebRtc/HtmlUtils";
import type { EnterLeaveEvent } from "./Events/EnterLeaveEvent"; import type { EnterLeaveEvent } from "./Events/EnterLeaveEvent";
import { isOpenPopupEvent, OpenPopupEvent } from "./Events/OpenPopupEvent"; import { isOpenPopupEvent, OpenPopupEvent } from "./Events/OpenPopupEvent";
@ -8,18 +7,15 @@ import { isOpenTabEvent, OpenTabEvent } from "./Events/OpenTabEvent";
import type { ButtonClickedEvent } from "./Events/ButtonClickedEvent"; import type { ButtonClickedEvent } from "./Events/ButtonClickedEvent";
import { ClosePopupEvent, isClosePopupEvent } from "./Events/ClosePopupEvent"; import { ClosePopupEvent, isClosePopupEvent } from "./Events/ClosePopupEvent";
import { scriptUtils } from "./ScriptUtils"; import { scriptUtils } from "./ScriptUtils";
import { GoToPageEvent, isGoToPageEvent } from "./Events/GoToPageEvent"; import { isGoToPageEvent } from "./Events/GoToPageEvent";
import { isOpenCoWebsite, OpenCoWebSiteEvent } from "./Events/OpenCoWebSiteEvent"; import { isCloseCoWebsite, CloseCoWebsiteEvent } from "./Events/CloseCoWebsiteEvent";
import { import {
IframeErrorAnswerEvent, IframeErrorAnswerEvent,
IframeEvent,
IframeEventMap,
IframeQueryMap, IframeQueryMap,
IframeResponseEvent, IframeResponseEvent,
IframeResponseEventMap, IframeResponseEventMap,
isIframeEventWrapper, isIframeEventWrapper,
isIframeQueryWrapper, isIframeQueryWrapper,
TypedMessageEvent,
} from "./Events/IframeEvent"; } from "./Events/IframeEvent";
import type { UserInputChatEvent } from "./Events/UserInputChatEvent"; import type { UserInputChatEvent } from "./Events/UserInputChatEvent";
import { isPlaySoundEvent, PlaySoundEvent } from "./Events/PlaySoundEvent"; import { isPlaySoundEvent, PlaySoundEvent } from "./Events/PlaySoundEvent";
@ -33,7 +29,6 @@ import { isMenuRegisterEvent, isUnregisterMenuEvent } from "./Events/ui/MenuRegi
import { SetTilesEvent, isSetTilesEvent } from "./Events/SetTilesEvent"; import { SetTilesEvent, isSetTilesEvent } from "./Events/SetTilesEvent";
import type { SetVariableEvent } from "./Events/SetVariableEvent"; import type { SetVariableEvent } from "./Events/SetVariableEvent";
import { ModifyEmbeddedWebsiteEvent, isEmbeddedWebsiteEvent } from "./Events/EmbeddedWebsiteEvent"; import { ModifyEmbeddedWebsiteEvent, isEmbeddedWebsiteEvent } from "./Events/EmbeddedWebsiteEvent";
import { EmbeddedWebsite } from "./iframe/Room/EmbeddedWebsite";
import { handleMenuRegistrationEvent, handleMenuUnregisterEvent } from "../Stores/MenuStore"; import { handleMenuRegistrationEvent, handleMenuUnregisterEvent } from "../Stores/MenuStore";
type AnswererCallback<T extends keyof IframeQueryMap> = ( type AnswererCallback<T extends keyof IframeQueryMap> = (
@ -53,10 +48,7 @@ class IframeListener {
public readonly openTabStream = this._openTabStream.asObservable(); public readonly openTabStream = this._openTabStream.asObservable();
private readonly _loadPageStream: Subject<string> = new Subject(); private readonly _loadPageStream: Subject<string> = new Subject();
public readonly loadPageStream = this._loadPageStream.asObservable(); public readonly loadPageStream = this._loadPageStream.asObservable()
private readonly _openCoWebSiteStream: Subject<OpenCoWebSiteEvent> = new Subject();
public readonly openCoWebSiteStream = this._openCoWebSiteStream.asObservable();
private readonly _disablePlayerControlStream: Subject<void> = new Subject(); private readonly _disablePlayerControlStream: Subject<void> = new Subject();
public readonly disablePlayerControlStream = this._disablePlayerControlStream.asObservable(); public readonly disablePlayerControlStream = this._disablePlayerControlStream.asObservable();
@ -138,8 +130,6 @@ class IframeListener {
return; return;
} }
foundSrc = this.getBaseUrl(foundSrc, message.source);
if (isIframeQueryWrapper(payload)) { if (isIframeQueryWrapper(payload)) {
const queryId = payload.id; const queryId = payload.id;
const query = payload.query; const query = payload.query;
@ -224,15 +214,6 @@ class IframeListener {
this._stopSoundStream.next(payload.data); this._stopSoundStream.next(payload.data);
} else if (payload.type === "loadSound" && isLoadSoundEvent(payload.data)) { } else if (payload.type === "loadSound" && isLoadSoundEvent(payload.data)) {
this._loadSoundStream.next(payload.data); this._loadSoundStream.next(payload.data);
} else if (payload.type === "openCoWebSite" && isOpenCoWebsite(payload.data)) {
scriptUtils.openCoWebsite(
payload.data.url,
foundSrc,
payload.data.allowApi,
payload.data.allowPolicy
);
} else if (payload.type === "closeCoWebSite") {
scriptUtils.closeCoWebSite();
} else if (payload.type === "disablePlayerControls") { } else if (payload.type === "disablePlayerControls") {
this._disablePlayerControlStream.next(); this._disablePlayerControlStream.next();
} else if (payload.type === "restorePlayerControls") { } else if (payload.type === "restorePlayerControls") {
@ -252,6 +233,9 @@ class IframeListener {
this.iframeCloseCallbacks.get(iframe)?.push(() => { this.iframeCloseCallbacks.get(iframe)?.push(() => {
handleMenuUnregisterEvent(dataName); handleMenuUnregisterEvent(dataName);
}); });
foundSrc = this.getBaseUrl(foundSrc, message.source);
handleMenuRegistrationEvent( handleMenuRegistrationEvent(
payload.data.name, payload.data.name,
payload.data.iframe, payload.data.iframe,
@ -354,6 +338,20 @@ class IframeListener {
return src; return src;
} }
public getBaseUrlFromSource(source: MessageEventSource): string {
let foundSrc: string | undefined;
let iframe: HTMLIFrameElement | undefined;
for (iframe of this.iframes) {
if (iframe.contentWindow === source) {
foundSrc = iframe.src;
break;
}
}
return this.getBaseUrl(foundSrc ?? "", source);
}
private static getIFrameId(scriptUrl: string): string { private static getIFrameId(scriptUrl: string): string {
return "script" + btoa(scriptUrl); return "script" + btoa(scriptUrl);
} }

View file

@ -1,4 +1,4 @@
import { coWebsiteManager } from "../WebRtc/CoWebsiteManager"; import { coWebsiteManager, CoWebsite } from "../WebRtc/CoWebsiteManager";
import { playersStore } from "../Stores/PlayersStore"; import { playersStore } from "../Stores/PlayersStore";
import { chatMessagesStore } from "../Stores/ChatStore"; import { chatMessagesStore } from "../Stores/ChatStore";
import type { ChatEvent } from "./Events/ChatEvent"; import type { ChatEvent } from "./Events/ChatEvent";
@ -12,14 +12,6 @@ class ScriptUtils {
window.location.href = url; window.location.href = url;
} }
public openCoWebsite(url: string, base: string, api: boolean, policy: string) {
coWebsiteManager.loadCoWebsite(url, base, api, policy);
}
public closeCoWebSite() {
coWebsiteManager.closeCoWebsite();
}
public sendAnonymousChat(chatEvent: ChatEvent) { public sendAnonymousChat(chatEvent: ChatEvent) {
const userId = playersStore.addFacticePlayer(chatEvent.author); const userId = playersStore.addFacticePlayer(chatEvent.author);
chatMessagesStore.addExternalMessage(userId, chatEvent.message); chatMessagesStore.addExternalMessage(userId, chatEvent.message);

View file

@ -1,8 +1,4 @@
import type { GoToPageEvent } from "../Events/GoToPageEvent"; import { IframeApiContribution, sendToWorkadventure, queryWorkadventure } from "./IframeApiContribution";
import type { OpenTabEvent } from "../Events/OpenTabEvent";
import { IframeApiContribution, sendToWorkadventure } from "./IframeApiContribution";
import type { OpenCoWebSiteEvent } from "../Events/OpenCoWebSiteEvent";
import type { LoadPageEvent } from "../Events/LoadPageEvent";
export class WorkadventureNavigationCommands extends IframeApiContribution<WorkadventureNavigationCommands> { export class WorkadventureNavigationCommands extends IframeApiContribution<WorkadventureNavigationCommands> {
callbacks = []; callbacks = [];
@ -34,21 +30,61 @@ export class WorkadventureNavigationCommands extends IframeApiContribution<Worka
}); });
} }
openCoWebSite(url: string, allowApi: boolean = false, allowPolicy: string = ""): void { /**
sendToWorkadventure({ * @deprecated Use openCoWebsite instead
type: "openCoWebSite", */
openCoWebSite(url: string, allowApi?: boolean, allowPolicy?: string) {
return queryWorkadventure({
type: "openCoWebsite",
data: { data: {
url, url,
allowApi, allowApi,
allowPolicy, allowPolicy,
position: undefined,
}, },
}); });
} }
closeCoWebSite(): void { openCoWebsite(url: string, allowApi?: boolean, allowPolicy?: string, position?: number) {
sendToWorkadventure({ return queryWorkadventure({
type: "closeCoWebSite", type: "openCoWebsite",
data: null, data: {
url,
allowApi,
allowPolicy,
position,
},
});
}
getCoWebsites() {
return queryWorkadventure({
type: "getCoWebsites",
data: undefined
});
}
/**
* @deprecated Use closeCoWebsites instead to close all co-websites
*/
closeCoWebSite() {
return queryWorkadventure({
type: "closeCoWebsites",
data: undefined,
});
}
closeCoWebsite(coWebsiteId: string) {
return queryWorkadventure({
type: "closeCoWebsite",
data: coWebsiteId,
});
}
closeCoWebsites() {
return queryWorkadventure({
type: "closeCoWebsites",
data: undefined,
}); });
} }
} }

View file

@ -25,7 +25,7 @@ import {
TRIGGER_WEBSITE_PROPERTIES, TRIGGER_WEBSITE_PROPERTIES,
WEBSITE_MESSAGE_PROPERTIES, WEBSITE_MESSAGE_PROPERTIES,
} from "../../WebRtc/LayoutManager"; } from "../../WebRtc/LayoutManager";
import { coWebsiteManager } from "../../WebRtc/CoWebsiteManager"; import { CoWebsite, coWebsiteManager } from "../../WebRtc/CoWebsiteManager";
import type { UserMovedMessage } from "../../Messages/generated/messages_pb"; import type { UserMovedMessage } from "../../Messages/generated/messages_pb";
import { ProtobufClientUtils } from "../../Network/ProtobufClientUtils"; import { ProtobufClientUtils } from "../../Network/ProtobufClientUtils";
import type { RoomConnection } from "../../Connexion/RoomConnection"; import type { RoomConnection } from "../../Connexion/RoomConnection";
@ -95,7 +95,6 @@ import { EmbeddedWebsiteManager } from "./EmbeddedWebsiteManager";
import { GameMapPropertiesListener } from "./GameMapPropertiesListener"; import { GameMapPropertiesListener } from "./GameMapPropertiesListener";
import { analyticsClient } from "../../Administration/AnalyticsClient"; import { analyticsClient } from "../../Administration/AnalyticsClient";
import { get } from "svelte/store"; import { get } from "svelte/store";
import type { RadialMenuItem } from "../Components/RadialMenu";
import { contactPageStore } from "../../Stores/MenuStore"; import { contactPageStore } from "../../Stores/MenuStore";
export interface GameSceneInitInterface { export interface GameSceneInitInterface {
@ -1081,6 +1080,58 @@ ${escapedMessage}
}) })
); );
iframeListener.registerAnswerer("openCoWebsite", async (openCoWebsite, source) => {
if (!source) {
throw new Error("Unknown query source");
}
const coWebsite = await coWebsiteManager.loadCoWebsite(
openCoWebsite.url,
iframeListener.getBaseUrlFromSource(source),
openCoWebsite.allowApi,
openCoWebsite.allowPolicy,
openCoWebsite.position
);
if (!coWebsite) {
throw new Error("Error on opening co-website");
}
return {
id: coWebsite.iframe.id,
position: coWebsite.position,
};
});
iframeListener.registerAnswerer("getCoWebsites", () => {
const coWebsites = coWebsiteManager.getCoWebsites();
return coWebsites.map((coWebsite: CoWebsite) => {
return {
id: coWebsite.iframe.id,
position: coWebsite.position,
};
});
});
iframeListener.registerAnswerer("closeCoWebsite", async (coWebsiteId) => {
const coWebsite = coWebsiteManager.getCoWebsiteById(coWebsiteId);
if (!coWebsite) {
throw new Error("Unknown co-website");
}
return coWebsiteManager.closeCoWebsite(coWebsite).catch((error) => {
throw new Error("Error on closing co-website");
});
});
iframeListener.registerAnswerer("closeCoWebsites", async () => {
return await coWebsiteManager.closeCoWebsites().catch((error) => {
throw new Error("Error on closing all co-websites");
});
});
iframeListener.registerAnswerer("getMapData", () => { iframeListener.registerAnswerer("getMapData", () => {
return { return {
data: this.gameMap.getMap(), data: this.gameMap.getMap(),
@ -1310,6 +1361,8 @@ ${escapedMessage}
iframeListener.unregisterAnswerer("getMapData"); iframeListener.unregisterAnswerer("getMapData");
iframeListener.unregisterAnswerer("triggerActionMessage"); iframeListener.unregisterAnswerer("triggerActionMessage");
iframeListener.unregisterAnswerer("removeActionMessage"); iframeListener.unregisterAnswerer("removeActionMessage");
iframeListener.unregisterAnswerer("openCoWebsite");
iframeListener.unregisterAnswerer("getCoWebsites");
this.sharedVariablesManager?.close(); this.sharedVariablesManager?.close();
this.embeddedWebsiteManager?.close(); this.embeddedWebsiteManager?.close();

View file

@ -27,13 +27,13 @@ interface TouchMoveCoordinates {
y: number; y: number;
} }
export interface CoWebsite { export type CoWebsite = {
iframe: HTMLIFrameElement, iframe: HTMLIFrameElement,
icon: HTMLDivElement, icon: HTMLDivElement,
position: number position: number
} }
interface CoWebsiteSlot { type CoWebsiteSlot = {
container: HTMLElement, container: HTMLElement,
position: number position: number
} }
@ -186,6 +186,7 @@ class CoWebsiteManager {
}; };
this.cowebsiteAsideDom.addEventListener(touchMode ? "touchstart" : "mousedown", (event) => { this.cowebsiteAsideDom.addEventListener(touchMode ? "touchstart" : "mousedown", (event) => {
this.cowebsiteMainDom.style.display = "none";
this.resizing = true; this.resizing = true;
if (touchMode) { if (touchMode) {
const touchEvent = (event as TouchEvent).touches[0]; const touchEvent = (event as TouchEvent).touches[0];
@ -203,6 +204,7 @@ class CoWebsiteManager {
document.removeEventListener(touchMode ? "touchmove" : "mousemove", movecallback); document.removeEventListener(touchMode ? "touchmove" : "mousemove", movecallback);
this.cowebsiteMainDom.style.display = "block"; this.cowebsiteMainDom.style.display = "block";
this.resizing = false; this.resizing = false;
this.cowebsiteMainDom.style.display = "flex";
}); });
} }
@ -277,7 +279,11 @@ class CoWebsiteManager {
}); });
} }
private searchCoWebsiteById(coWebsiteId: string): CoWebsite|undefined { public getCoWebsites(): CoWebsite[] {
return this.coWebsites;
}
public getCoWebsiteById(coWebsiteId: string): CoWebsite|undefined {
return this.coWebsites.find((coWebsite: CoWebsite) => coWebsite.iframe.id === coWebsiteId); return this.coWebsites.find((coWebsite: CoWebsite) => coWebsite.iframe.id === coWebsiteId);
} }
@ -449,7 +455,7 @@ class CoWebsiteManager {
do { do {
iframe.id = "cowebsite-iframe-" + (Math.random() + 1).toString(36).substring(7); iframe.id = "cowebsite-iframe-" + (Math.random() + 1).toString(36).substring(7);
} while (iframe.id.toLowerCase().includes('jitsi') || this.searchCoWebsiteById(iframe.id)); } while (iframe.id.toLowerCase().includes('jitsi') || this.getCoWebsiteById(iframe.id));
iframe.src = new URL(url, base).toString() iframe.src = new URL(url, base).toString()

View file

@ -31,8 +31,7 @@
aside { aside {
height: 30px; height: 50px;
min-height: 30px;
cursor: ns-resize; cursor: ns-resize;
flex-direction: row-reverse; flex-direction: row-reverse;
align-items: center; align-items: center;
@ -58,8 +57,8 @@
visibility: visible; visibility: visible;
img { img {
height: 20px; height: 30px;
width: 20px; width: 30px;
cursor: pointer !important; cursor: pointer !important;
border-radius: 50%; border-radius: 50%;
background-color: whitesmoke; background-color: whitesmoke;

View file

@ -4,7 +4,7 @@
position: fixed; position: fixed;
z-index: 200; z-index: 200;
transition: transform 0.5s; transition: transform 0.5s;
background-color: white; background-color: whitesmoke;
display: none; display: none;
&.loading { &.loading {

View file

@ -0,0 +1,629 @@
{ "compressionlevel":-1,
"height":10,
"infinite":false,
"layers":[
{
"data":[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
"height":10,
"id":1,
"name":"floor",
"opacity":1,
"type":"tilelayer",
"visible":true,
"width":10,
"x":0,
"y":0
},
{
"data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
"height":10,
"id":2,
"name":"start",
"opacity":1,
"type":"tilelayer",
"visible":true,
"width":10,
"x":0,
"y":0
},
{
"data":[0, 0, 0, 0, 0, 23, 23, 23, 23, 23, 0, 0, 0, 0, 0, 23, 23, 23, 23, 23, 0, 0, 0, 0, 0, 23, 23, 23, 23, 23, 0, 0, 0, 0, 0, 23, 23, 23, 23, 23, 0, 0, 0, 0, 0, 23, 23, 23, 23, 23, 0, 0, 0, 0, 0, 23, 23, 23, 23, 23, 0, 0, 0, 0, 0, 23, 23, 23, 23, 23, 0, 0, 0, 0, 0, 23, 23, 23, 23, 23, 0, 0, 0, 0, 0, 23, 23, 23, 23, 23, 0, 0, 0, 0, 0, 23, 23, 23, 23, 23],
"height":10,
"id":5,
"name":"first_cowebsite",
"opacity":1,
"type":"tilelayer",
"visible":true,
"width":10,
"x":0,
"y":0
},
{
"data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 12, 12, 12, 0, 0, 0, 0, 0, 12, 12, 12, 12, 12, 0, 0, 0, 0, 0, 12, 12, 12, 12, 12, 0, 0, 0, 0, 0, 12, 12, 12, 12, 12, 0, 0, 0, 0, 0, 12, 12, 12, 12, 12],
"height":10,
"id":7,
"name":"second_cowebsite",
"opacity":1,
"type":"tilelayer",
"visible":true,
"width":10,
"x":0,
"y":0
},
{
"draworder":"topdown",
"id":3,
"name":"floorLayer",
"objects":[
{
"height":157.696496283938,
"id":1,
"name":"Tests",
"rotation":0,
"text":
{
"fontfamily":"Sans Serif",
"pixelsize":8,
"text":"Test 1:\nEnter \/cowebsite open https:\/\/wikipedia.com on the chat\nResult:\nA cowebsite must have been opened\n\nDo the first test 4 more times\n\nTest 2:\nEnter \/cowebsite close 0 on the chat\nResult:\nThe main co-website has been closed\n\nTest 3:\nEnter \/cowebsite close all on the chat\nResult:\nAll co-websites has been closed",
"wrap":true
},
"type":"",
"visible":true,
"width":316.770833333333,
"x":1.64026713939023,
"y":160.264699934273
}],
"opacity":1,
"type":"objectgroup",
"visible":true,
"x":0,
"y":0
},
{
"data":[0, 0, 0, 0, 0, 0, 0, 0, 82, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 27, 0, 0, 0, 0, 0, 0, 0, 0, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
"height":10,
"id":8,
"name":"objects",
"opacity":1,
"type":"tilelayer",
"visible":true,
"width":10,
"x":0,
"y":0
}],
"nextlayerid":9,
"nextobjectid":3,
"orientation":"orthogonal",
"properties":[
{
"name":"script",
"type":"string",
"value":"script.js"
}],
"renderorder":"right-down",
"tiledversion":"1.7.2",
"tileheight":32,
"tilesets":[
{
"columns":11,
"firstgid":1,
"image":"tileset1.png",
"imageheight":352,
"imagewidth":352,
"margin":0,
"name":"tileset1",
"spacing":0,
"tilecount":121,
"tileheight":32,
"tiles":[
{
"id":1,
"properties":[
{
"name":"collides",
"type":"bool",
"value":true
}]
},
{
"id":2,
"properties":[
{
"name":"collides",
"type":"bool",
"value":true
}]
},
{
"id":3,
"properties":[
{
"name":"collides",
"type":"bool",
"value":true
}]
},
{
"id":4,
"properties":[
{
"name":"collides",
"type":"bool",
"value":true
}]
},
{
"id":5,
"properties":[
{
"name":"collides",
"type":"bool",
"value":true
}]
},
{
"id":6,
"properties":[
{
"name":"collides",
"type":"bool",
"value":true
}]
},
{
"id":7,
"properties":[
{
"name":"collides",
"type":"bool",
"value":true
}]
},
{
"id":8,
"properties":[
{
"name":"collides",
"type":"bool",
"value":true
}]
},
{
"id":9,
"properties":[
{
"name":"collides",
"type":"bool",
"value":true
}]
},
{
"id":10,
"properties":[
{
"name":"collides",
"type":"bool",
"value":true
}]
},
{
"id":12,
"properties":[
{
"name":"collides",
"type":"bool",
"value":true
}]
},
{
"id":16,
"properties":[
{
"name":"collides",
"type":"bool",
"value":true
}]
},
{
"id":17,
"properties":[
{
"name":"collides",
"type":"bool",
"value":true
}]
},
{
"id":18,
"properties":[
{
"name":"collides",
"type":"bool",
"value":true
}]
},
{
"id":19,
"properties":[
{
"name":"collides",
"type":"bool",
"value":true
}]
},
{
"id":20,
"properties":[
{
"name":"collides",
"type":"bool",
"value":true
}]
},
{
"id":21,
"properties":[
{
"name":"collides",
"type":"bool",
"value":true
}]
},
{
"id":23,
"properties":[
{
"name":"collides",
"type":"bool",
"value":true
}]
},
{
"id":24,
"properties":[
{
"name":"collides",
"type":"bool",
"value":true
}]
},
{
"id":25,
"properties":[
{
"name":"collides",
"type":"bool",
"value":true
}]
},
{
"id":26,
"properties":[
{
"name":"collides",
"type":"bool",
"value":true
}]
},
{
"id":27,
"properties":[
{
"name":"collides",
"type":"bool",
"value":true
}]
},
{
"id":28,
"properties":[
{
"name":"collides",
"type":"bool",
"value":true
}]
},
{
"id":29,
"properties":[
{
"name":"collides",
"type":"bool",
"value":true
}]
},
{
"id":30,
"properties":[
{
"name":"collides",
"type":"bool",
"value":true
}]
},
{
"id":31,
"properties":[
{
"name":"collides",
"type":"bool",
"value":true
}]
},
{
"id":32,
"properties":[
{
"name":"collides",
"type":"bool",
"value":true
}]
},
{
"id":34,
"properties":[
{
"name":"collides",
"type":"bool",
"value":true
}]
},
{
"id":35,
"properties":[
{
"name":"collides",
"type":"bool",
"value":true
}]
},
{
"id":42,
"properties":[
{
"name":"collides",
"type":"bool",
"value":true
}]
},
{
"id":43,
"properties":[
{
"name":"collides",
"type":"bool",
"value":true
}]
},
{
"id":45,
"properties":[
{
"name":"collides",
"type":"bool",
"value":true
}]
},
{
"id":46,
"properties":[
{
"name":"collides",
"type":"bool",
"value":true
}]
},
{
"id":59,
"properties":[
{
"name":"collides",
"type":"bool",
"value":true
}]
},
{
"id":60,
"properties":[
{
"name":"collides",
"type":"bool",
"value":true
}]
},
{
"id":70,
"properties":[
{
"name":"collides",
"type":"bool",
"value":true
}]
},
{
"id":71,
"properties":[
{
"name":"collides",
"type":"bool",
"value":true
}]
},
{
"id":80,
"properties":[
{
"name":"collides",
"type":"bool",
"value":true
}]
},
{
"id":81,
"properties":[
{
"name":"collides",
"type":"bool",
"value":true
}]
},
{
"id":89,
"properties":[
{
"name":"collides",
"type":"bool",
"value":true
}]
},
{
"id":91,
"properties":[
{
"name":"collides",
"type":"bool",
"value":true
}]
},
{
"id":93,
"properties":[
{
"name":"collides",
"type":"bool",
"value":true
}]
},
{
"id":94,
"properties":[
{
"name":"collides",
"type":"bool",
"value":true
}]
},
{
"id":95,
"properties":[
{
"name":"collides",
"type":"bool",
"value":true
}]
},
{
"id":96,
"properties":[
{
"name":"collides",
"type":"bool",
"value":true
}]
},
{
"id":97,
"properties":[
{
"name":"collides",
"type":"bool",
"value":true
}]
},
{
"id":100,
"properties":[
{
"name":"collides",
"type":"bool",
"value":true
}]
},
{
"id":102,
"properties":[
{
"name":"collides",
"type":"bool",
"value":true
}]
},
{
"id":103,
"properties":[
{
"name":"collides",
"type":"bool",
"value":true
}]
},
{
"id":104,
"properties":[
{
"name":"collides",
"type":"bool",
"value":true
}]
},
{
"id":105,
"properties":[
{
"name":"collides",
"type":"bool",
"value":true
}]
},
{
"id":106,
"properties":[
{
"name":"collides",
"type":"bool",
"value":true
}]
},
{
"id":107,
"properties":[
{
"name":"collides",
"type":"bool",
"value":true
}]
},
{
"id":108,
"properties":[
{
"name":"collides",
"type":"bool",
"value":true
}]
},
{
"id":114,
"properties":[
{
"name":"collides",
"type":"bool",
"value":true
}]
},
{
"id":115,
"properties":[
{
"name":"collides",
"type":"bool",
"value":true
}]
}],
"tilewidth":32
}],
"tilewidth":32,
"type":"map",
"version":"1.6",
"width":10
}

View file

@ -0,0 +1,118 @@
{ "compressionlevel":-1,
"height":10,
"infinite":false,
"layers":[
{
"data":[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
"height":10,
"id":1,
"name":"floor",
"opacity":1,
"type":"tilelayer",
"visible":true,
"width":10,
"x":0,
"y":0
},
{
"data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
"height":10,
"id":2,
"name":"start",
"opacity":1,
"type":"tilelayer",
"visible":true,
"width":10,
"x":0,
"y":0
},
{
"data":[0, 0, 0, 0, 0, 23, 23, 23, 23, 23, 0, 0, 0, 0, 0, 23, 23, 23, 23, 23, 0, 0, 0, 0, 0, 23, 23, 23, 23, 23, 0, 0, 0, 0, 0, 23, 23, 23, 23, 23, 0, 0, 0, 0, 0, 23, 23, 23, 23, 23, 0, 0, 0, 0, 0, 23, 23, 23, 23, 23, 0, 0, 0, 0, 0, 23, 23, 23, 23, 23, 0, 0, 0, 0, 0, 23, 23, 23, 23, 23, 0, 0, 0, 0, 0, 23, 23, 23, 23, 23, 0, 0, 0, 0, 0, 23, 23, 23, 23, 23],
"height":10,
"id":5,
"name":"first_cowebsite",
"opacity":1,
"properties":[
{
"name":"openWebsite",
"type":"string",
"value":"https:\/\/wikipedia.org"
}],
"type":"tilelayer",
"visible":true,
"width":10,
"x":0,
"y":0
},
{
"data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 12, 12, 12, 0, 0, 0, 0, 0, 12, 12, 12, 12, 12, 0, 0, 0, 0, 0, 12, 12, 12, 12, 12, 0, 0, 0, 0, 0, 12, 12, 12, 12, 12, 0, 0, 0, 0, 0, 12, 12, 12, 12, 12],
"height":10,
"id":7,
"name":"second_cowebsite",
"opacity":1,
"properties":[
{
"name":"openWebsite",
"type":"string",
"value":"https:\/\/workadventu.re\/"
}],
"type":"tilelayer",
"visible":true,
"width":10,
"x":0,
"y":0
},
{
"draworder":"topdown",
"id":3,
"name":"floorLayer",
"objects":[
{
"height":141,
"id":1,
"name":"Tests",
"rotation":0,
"text":
{
"fontfamily":"Sans Serif",
"pixelsize":11,
"text":"Test:\nWalk on the blue carpet, an iframe open, walk on the white carpet another one to open another one\nResult:\n2 co-websites must be opened\n\nTest:\nGo outside of carpets\nResult:\nAll co-websites must disapeared",
"wrap":true
},
"type":"",
"visible":true,
"width":316.770833333333,
"x":0.28125,
"y":187.833333333333
}],
"opacity":1,
"type":"objectgroup",
"visible":true,
"x":0,
"y":0
}],
"nextlayerid":8,
"nextobjectid":3,
"orientation":"orthogonal",
"renderorder":"right-down",
"tiledversion":"1.7.1",
"tileheight":32,
"tilesets":[
{
"columns":11,
"firstgid":1,
"image":"tileset1.png",
"imageheight":352,
"imagewidth":352,
"margin":0,
"name":"tileset1",
"spacing":0,
"tilecount":121,
"tileheight":32,
"tilewidth":32
}],
"tilewidth":32,
"type":"map",
"version":"1.6",
"width":10
}

View file

@ -0,0 +1,108 @@
WA.onInit().then(() => {
WA.chat.onChatMessage((message) => {
if (!message.startsWith('/')) {
return;
}
const splitedMessage = message.trim().split(' ');
const command = splitedMessage.shift().substr(1);
executeCommand(command, splitedMessage);
});
});
function wookaSendMessage(message) {
WA.chat.sendChatMessage(message, 'Wooka');
}
function unknownCommand() {
wookaSendMessage('Unknown command');
}
function executeCommand(command, args) {
switch(command) {
case 'cowebsite':
coWebsiteCommand(args);
break;
default:
unknownCommand();
}
}
function coWebsiteCommand(args) {
if (args.length < 1) {
wookaSendMessage('Too few arguments');
return;
}
const subCommand = args.shift();
switch(subCommand) {
case 'open':
openCoWebsite(args);
break;
case 'close':
closeCoWebsite(args);
break;
default:
unknownCommand();
}
}
async function openCoWebsite(args) {
if (args.length < 1) {
wookaSendMessage('Too few arguments');
return;
}
try {
const url = new URL(args[0]);
} catch (exception) {
wookaSendMessage('Parameter is not a valid URL !');
return;
}
await WA.nav.openCoWebsite(args[0]).then(() => {
wookaSendMessage('Co-website has been opened !');
}).catch((error) => {
wookaSendMessage(`Something wrong happen during co-website opening: ${error.message}`);
});
}
async function closeCoWebsite(args) {
if (args.length < 1) {
wookaSendMessage('Too few arguments');
return;
}
// All
if (args[0] === "all" || args[0] === "*") {
WA.nav.closeCoWebsites().then(() => {
wookaSendMessage('All co-websites has been closed !');
}).catch((error) => {
wookaSendMessage(`Something wrong happen during co-websites closing: ${error.message}`);
});
return;
}
const coWebsites = await WA.nav.getCoWebsites();
const position = parseInt(args[0]);
// By ID or Position
const coWebsite =
isNaN(position) ?
coWebsites.find((coWebsite) => coWebsite.id === args[0]) :
coWebsites.find((coWebsite) => coWebsite.position === position);
if (!coWebsite) {
wookaSendMessage('Unknown co-website');
return;
}
await WA.nav.closeCoWebsite(coWebsite.id).then(() => {
wookaSendMessage('This co-websites has been closed !');
}).catch((error) => {
wookaSendMessage(`Something wrong happen during co-website closing: ${error.message}`);
});
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

View file

@ -3,6 +3,7 @@
<head> <head>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-F3w7mX95PdgyTmZZMECAngseQB83DfGTowi0iMjiWaeVhAn4FJkqJByhZMI3AhiU" crossorigin="anonymous"> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-F3w7mX95PdgyTmZZMECAngseQB83DfGTowi0iMjiWaeVhAn4FJkqJByhZMI3AhiU" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.1/dist/js/bootstrap.bundle.min.js" integrity="sha384-/bQdsTh/da6pkI1MST/rWKFNjaCP5gBSY4sEBT38Q/9RBh9AH40zEOg7Hlq2THRZ" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.1/dist/js/bootstrap.bundle.min.js" integrity="sha384-/bQdsTh/da6pkI1MST/rWKFNjaCP5gBSY4sEBT38Q/9RBh9AH40zEOg7Hlq2THRZ" crossorigin="anonymous"></script>
<title>Map Tests</title>
</head> </head>
<body> <body>
<div class="container"> <div class="container">
@ -235,6 +236,25 @@
</td> </td>
</tr> </tr>
</table> </table>
<h2>CoWebsite</h2>
<table class="table">
<tr>
<td>
<input type="radio" name="test-cowebsite-property"> Success <input type="radio" name="test-cowebsite-property"> Failure <input type="radio" name="test-cowebsite-property" checked> Pending
</td>
<td>
<a href="#" class="testLink" data-testmap="cowebsite_property.json" target="_blank">Open co-websites by map property</a>
</td>
</tr>
<tr>
<td>
<input type="radio" name="test-cowebsite-api"> Success <input type="radio" name="test-cowebsite-api"> Failure <input type="radio" name="test-cowebsite-api" checked> Pending
</td>
<td>
<a href="#" class="testLink" data-testmap="CoWebsite/cowebsite_api.json" target="_blank">Open co-websites by scripting api</a>
</td>
</tr>
</table>
<h2>Mobile</h2> <h2>Mobile</h2>
<table class="table"> <table class="table">
<tr> <tr>