Implement UI Website (#2087)
* Implement UI Website system * Add UIWebsite documentation * Implement review fixes * Add getAll function on UIWebsite Co-authored-by: Alexis Faizeau <a.faizeau@workadventu.re>
This commit is contained in:
parent
d47aad2ead
commit
9c5fcd2fd8
@ -34,7 +34,7 @@ Please note that `openPopup` returns an object of the `Popup` class. Also, the c
|
||||
|
||||
The `Popup` class that represents an open popup contains a single method: `close()`. This will obviously close the popup when called.
|
||||
|
||||
```javascript
|
||||
```ts
|
||||
class Popup {
|
||||
/**
|
||||
* Closes the popup
|
||||
@ -45,7 +45,7 @@ class Popup {
|
||||
|
||||
Example:
|
||||
|
||||
```javascript
|
||||
```ts
|
||||
let helloWorldPopup;
|
||||
|
||||
// Open the popup when we enter a given zone
|
||||
@ -90,8 +90,8 @@ Custom menu exist only until the map is unloaded, or you leave the iframe zone o
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Example:
|
||||
```javascript
|
||||
Example:
|
||||
```ts
|
||||
const menu = WA.ui.registerMenuCommand('menu test',
|
||||
{
|
||||
callback: () => {
|
||||
@ -107,7 +107,7 @@ Please note that `registerMenuCommand` returns an object of the `Menu` class.
|
||||
|
||||
The `Menu` class contains a single method: `remove(): void`. This will obviously remove the menu when called.
|
||||
|
||||
```javascript
|
||||
```ts
|
||||
class Menu {
|
||||
/**
|
||||
* Remove the menu
|
||||
@ -120,7 +120,7 @@ class Menu {
|
||||
|
||||
### Awaiting User Confirmation (with space bar)
|
||||
|
||||
```
|
||||
```ts
|
||||
WA.ui.displayActionMessage({
|
||||
message: string,
|
||||
callback: () => void,
|
||||
@ -136,7 +136,7 @@ Displays a message at the bottom of the screen (that will disappear when space b
|
||||
|
||||
Example:
|
||||
|
||||
```javascript
|
||||
```ts
|
||||
const triggerMessage = WA.ui.displayActionMessage({
|
||||
message: "press 'space' to confirm",
|
||||
callback: () => {
|
||||
@ -154,7 +154,7 @@ Please note that `displayActionMessage` returns an object of the `ActionMessage`
|
||||
|
||||
The `ActionMessage` class contains a single method: `remove(): Promise<void>`. This will obviously remove the message when called.
|
||||
|
||||
```javascript
|
||||
```ts
|
||||
class ActionMessage {
|
||||
/**
|
||||
* Hides the message
|
||||
@ -173,7 +173,7 @@ When clicking on other player's WOKA, the contextual menu (we call it ActionsMen
|
||||
|
||||
To do that, we need to listen for the `onRemotePlayerClicked` stream and make use of the `remotePlayer` object that is passed by as a payload.
|
||||
|
||||
```javascript
|
||||
```ts
|
||||
WA.ui.onRemotePlayerClicked.subscribe((remotePlayer) => {
|
||||
remotePlayer.addAction('Ask to tell a joke', () => {
|
||||
console.log('I am NOT telling you a joke!');
|
||||
@ -182,7 +182,7 @@ WA.ui.onRemotePlayerClicked.subscribe((remotePlayer) => {
|
||||
```
|
||||
|
||||
`remotePlayer.addAction(actionName, callback)` returns an Action object, which can remove itself from ActionsMenu:
|
||||
```javascript
|
||||
```ts
|
||||
const action = remotePlayer.addAction('This will disappear!', () => {
|
||||
console.log('You managed to click me!');
|
||||
});
|
||||
@ -193,3 +193,95 @@ setTimeout(
|
||||
1000,
|
||||
);
|
||||
```
|
||||
|
||||
# Open fixed iframes
|
||||
|
||||
You can use the scripting API to display an iframe (so any HTML element) above the game. The iframe is positionned relative to the browser window (so unlike [embedded websites](website-in-map.md), the position of the iframe does not move when someone walks on the map).
|
||||
|
||||
<div class="col">
|
||||
<img src="images/ui-website.png" class="figure-img img-fluid rounded" alt="" />
|
||||
</div>
|
||||
|
||||
This functonnality creates an iframe positionned on the viewport.
|
||||
|
||||
## Display a UI website
|
||||
|
||||
```ts
|
||||
WA.ui.website.open(website: CreateUIWebsiteEvent): Promise<UIWebsite>
|
||||
|
||||
interface CreateUIWebsiteEvent {
|
||||
url: string, // Website URL
|
||||
visible?: boolean, // The website is visible or not
|
||||
allowApi?: boolean, // Allow scripting API on the website
|
||||
allowPolicy?: string, // The list of feature policies allowed
|
||||
position: {
|
||||
vertical: "top"|"middle"|"bottom",,
|
||||
horizontal: "left","middle","right",
|
||||
},
|
||||
size: { // Size on the UI (available units: px|em|%|cm|in|pc|pt|mm|ex|vw|vh|rem and others values auto|inherit)
|
||||
height: string,
|
||||
width: string,
|
||||
},
|
||||
margin?: { // Website margin (available units: px|em|%|cm|in|pc|pt|mm|ex|vw|vh|rem and others values auto|inherit)
|
||||
top?: string,
|
||||
bottom?: string,
|
||||
left?: string,
|
||||
right?: string,
|
||||
},
|
||||
}
|
||||
|
||||
interface UIWebsite {
|
||||
readonly id: string, // Unique ID
|
||||
url: string, // Website URL
|
||||
visible: boolean, // The website is visible or not
|
||||
readonly allowApi: boolean, // Allow scripting API on the website
|
||||
readonly allowPolicy: string, // The list of feature policies allowed
|
||||
position: {
|
||||
vertical: string, // Vertical position (top, middle, bottom)
|
||||
horizontal: string, // Horizontal position (left, middle, right)
|
||||
},
|
||||
size: { // Size on the UI (available units: px|em|%|cm|in|pc|pt|mm|ex|vw|vh|rem and others values auto|inherit)
|
||||
height: string,
|
||||
width: string,
|
||||
},
|
||||
margin?: { // Website margin (available units: px|em|%|cm|in|pc|pt|mm|ex|vw|vh|rem and others values auto|inherit)
|
||||
top?: string,
|
||||
bottom?: string,
|
||||
left?: string,
|
||||
right?: string,
|
||||
},
|
||||
close(): Promise<void>, // Close the current website instance
|
||||
}
|
||||
```
|
||||
|
||||
You can open a website with the `WA.ui.website.open()` method. It returns an `Promise<UIWebsite>` instance.
|
||||
|
||||
```ts
|
||||
const myWebsite = await WA.ui.website.open({
|
||||
url: "https://wikipedia.org",
|
||||
position: {
|
||||
vertical: "middle",
|
||||
horizontal: "middle",
|
||||
},
|
||||
size: {
|
||||
height: "50vh",
|
||||
width: "50vw",
|
||||
},
|
||||
});
|
||||
|
||||
myWebsite.position.vertical = "top";
|
||||
```
|
||||
|
||||
## Close a UI website
|
||||
You can close a website with the close function on the `UIWebsite` object
|
||||
|
||||
```ts
|
||||
myWebsite.close();
|
||||
```
|
||||
|
||||
## Get all UI websites
|
||||
You can get all websites with the `WA.ui.website.getAll()` method. It returns an `Promise<UIWebsite[]>` instance.
|
||||
|
||||
```ts
|
||||
WA.ui.website.getAll();
|
||||
```
|
||||
|
BIN
docs/maps/images/ui-website.png
Normal file
BIN
docs/maps/images/ui-website.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 454 KiB |
1
front/.gitignore
vendored
1
front/.gitignore
vendored
@ -5,3 +5,4 @@ dist/
|
||||
*.sh
|
||||
!templater.sh
|
||||
/public/iframe_api.js
|
||||
/public/es.js
|
||||
|
@ -21,7 +21,7 @@
|
||||
"lint-staged": "^12.3.7",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "^2.3.1",
|
||||
"prettier-plugin-svelte": "^2.5.0",
|
||||
"prettier-plugin-svelte": "^2.7.0",
|
||||
"sass": "^1.49.7",
|
||||
"svelte": "^3.46.3",
|
||||
"svelte-check": "^2.1.0",
|
||||
@ -95,4 +95,4 @@
|
||||
"yarn run pretty"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
@ -39,6 +39,7 @@ import type { RemotePlayerClickedEvent } from "./RemotePlayerClickedEvent";
|
||||
import { isAddActionsMenuKeyToRemotePlayerEvent } from "./AddActionsMenuKeyToRemotePlayerEvent";
|
||||
import type { ActionsMenuActionClickedEvent } from "./ActionsMenuActionClickedEvent";
|
||||
import { isRemoveActionsMenuKeyFromRemotePlayerEvent } from "./RemoveActionsMenuKeyFromRemotePlayerEvent";
|
||||
import { isCreateUIWebsiteEvent, isModifyUIWebsiteEvent, isUIWebsite } from "./ui/UIWebsite";
|
||||
|
||||
export interface TypedMessageEvent<T> extends MessageEvent {
|
||||
data: T;
|
||||
@ -152,6 +153,10 @@ export const isIframeEventWrapper = z.union([
|
||||
type: z.literal("modifyEmbeddedWebsite"),
|
||||
data: isEmbeddedWebsiteEvent,
|
||||
}),
|
||||
z.object({
|
||||
type: z.literal("modifyUIWebsite"),
|
||||
data: isModifyUIWebsiteEvent,
|
||||
}),
|
||||
]);
|
||||
|
||||
export type IframeEvent = z.infer<typeof isIframeEventWrapper>;
|
||||
@ -261,6 +266,18 @@ export const iframeQueryMapTypeGuards = {
|
||||
query: isMovePlayerToEventConfig,
|
||||
answer: isMovePlayerToEventAnswer,
|
||||
},
|
||||
openUIWebsite: {
|
||||
query: isCreateUIWebsiteEvent,
|
||||
answer: isUIWebsite,
|
||||
},
|
||||
closeUIWebsite: {
|
||||
query: z.string(),
|
||||
answer: z.undefined(),
|
||||
},
|
||||
getUIWebsites: {
|
||||
query: z.undefined(),
|
||||
answer: z.array(isUIWebsite),
|
||||
},
|
||||
};
|
||||
|
||||
type IframeQueryMapTypeGuardsType = typeof iframeQueryMapTypeGuards;
|
||||
|
75
front/src/Api/Events/ui/UIWebsite.ts
Normal file
75
front/src/Api/Events/ui/UIWebsite.ts
Normal file
@ -0,0 +1,75 @@
|
||||
import { z } from "zod";
|
||||
|
||||
const regexUnit = /-*\d+(px|em|%|cm|in|pc|pt|mm|ex|vw|vh|rem)|auto|inherit/;
|
||||
|
||||
// Parse the string to check if is a valid CSS unit (px,%,vw,vh...)
|
||||
export const isUIWebsiteCSSValue = z.string().regex(regexUnit);
|
||||
|
||||
export type UIWebsiteCSSValue = z.infer<typeof isUIWebsiteCSSValue>;
|
||||
|
||||
export const isUIWebsiteMargin = z.object({
|
||||
top: isUIWebsiteCSSValue.optional(),
|
||||
bottom: isUIWebsiteCSSValue.optional(),
|
||||
left: isUIWebsiteCSSValue.optional(),
|
||||
right: isUIWebsiteCSSValue.optional(),
|
||||
});
|
||||
|
||||
export type UIWebsiteMargin = z.infer<typeof isUIWebsiteMargin>;
|
||||
|
||||
export const isViewportPositionVertical = z.enum(["top", "middle", "bottom"]);
|
||||
|
||||
export type ViewportPositionVertical = z.infer<typeof isViewportPositionVertical>;
|
||||
|
||||
export const isViewportPositionHorizontal = z.enum(["left", "middle", "right"]);
|
||||
|
||||
export type ViewportPositionHorizontal = z.infer<typeof isViewportPositionHorizontal>;
|
||||
|
||||
export const isUIWebsitePosition = z.object({
|
||||
vertical: isViewportPositionVertical,
|
||||
horizontal: isViewportPositionHorizontal,
|
||||
});
|
||||
|
||||
export type UIWebsitePosition = z.infer<typeof isUIWebsitePosition>;
|
||||
|
||||
export const isUIWebsiteSize = z.object({
|
||||
height: isUIWebsiteCSSValue,
|
||||
width: isUIWebsiteCSSValue,
|
||||
});
|
||||
|
||||
export type UIWebsiteSize = z.infer<typeof isUIWebsiteSize>;
|
||||
|
||||
export const isCreateUIWebsiteEvent = z.object({
|
||||
url: z.string(),
|
||||
visible: z.optional(z.boolean()),
|
||||
allowApi: z.optional(z.boolean()),
|
||||
allowPolicy: z.optional(z.string()),
|
||||
position: isUIWebsitePosition,
|
||||
size: isUIWebsiteSize,
|
||||
margin: isUIWebsiteMargin.optional(),
|
||||
});
|
||||
|
||||
export type CreateUIWebsiteEvent = z.infer<typeof isCreateUIWebsiteEvent>;
|
||||
|
||||
export const isModifyUIWebsiteEvent = z.object({
|
||||
id: z.string(),
|
||||
url: z.string().optional(),
|
||||
visible: z.boolean().optional(),
|
||||
position: isUIWebsitePosition.optional(),
|
||||
size: isUIWebsiteSize.optional(),
|
||||
margin: isUIWebsiteMargin.optional(),
|
||||
});
|
||||
|
||||
export type ModifyUIWebsiteEvent = z.infer<typeof isModifyUIWebsiteEvent>;
|
||||
|
||||
export const isUIWebsite = z.object({
|
||||
id: z.string(),
|
||||
url: z.string(),
|
||||
visible: z.boolean(),
|
||||
allowApi: z.boolean(),
|
||||
allowPolicy: z.string(),
|
||||
position: isUIWebsitePosition,
|
||||
size: isUIWebsiteSize,
|
||||
margin: isUIWebsiteMargin.optional(),
|
||||
});
|
||||
|
||||
export type UIWebsite = z.infer<typeof isUIWebsite>;
|
@ -35,6 +35,7 @@ import type { RemotePlayerClickedEvent } from "./Events/RemotePlayerClickedEvent
|
||||
import { AddActionsMenuKeyToRemotePlayerEvent } from "./Events/AddActionsMenuKeyToRemotePlayerEvent";
|
||||
import type { ActionsMenuActionClickedEvent } from "./Events/ActionsMenuActionClickedEvent";
|
||||
import { RemoveActionsMenuKeyFromRemotePlayerEvent } from "./Events/RemoveActionsMenuKeyFromRemotePlayerEvent";
|
||||
import { ModifyUIWebsiteEvent } from "./Events/ui/UIWebsite";
|
||||
|
||||
type AnswererCallback<T extends keyof IframeQueryMap> = (
|
||||
query: IframeQueryMap[T]["query"],
|
||||
@ -112,6 +113,9 @@ class IframeListener {
|
||||
private readonly _modifyEmbeddedWebsiteStream: Subject<ModifyEmbeddedWebsiteEvent> = new Subject();
|
||||
public readonly modifyEmbeddedWebsiteStream = this._modifyEmbeddedWebsiteStream.asObservable();
|
||||
|
||||
private readonly _modifyUIWebsiteStream: Subject<ModifyUIWebsiteEvent> = new Subject();
|
||||
public readonly modifyUIWebsiteStream = this._modifyUIWebsiteStream.asObservable();
|
||||
|
||||
private readonly iframes = new Set<HTMLIFrameElement>();
|
||||
private readonly iframeCloseCallbacks = new Map<HTMLIFrameElement, (() => void)[]>();
|
||||
private readonly scripts = new Map<string, HTMLIFrameElement>();
|
||||
@ -276,6 +280,8 @@ class IframeListener {
|
||||
this._setTilesStream.next(iframeEvent.data);
|
||||
} else if (iframeEvent.type == "modifyEmbeddedWebsite") {
|
||||
this._modifyEmbeddedWebsiteStream.next(iframeEvent.data);
|
||||
} else if (iframeEvent.type == "modifyUIWebsite") {
|
||||
this._modifyUIWebsiteStream.next(iframeEvent.data);
|
||||
} else if (iframeEvent.type == "registerMenu") {
|
||||
const dataName = iframeEvent.data.name;
|
||||
this.iframeCloseCallbacks.get(iframe)?.push(() => {
|
||||
|
338
front/src/Api/iframe/Ui/UIWebsite.ts
Normal file
338
front/src/Api/iframe/Ui/UIWebsite.ts
Normal file
@ -0,0 +1,338 @@
|
||||
import {
|
||||
CreateUIWebsiteEvent,
|
||||
UIWebsiteCSSValue,
|
||||
UIWebsiteMargin,
|
||||
UIWebsitePosition,
|
||||
UIWebsiteSize,
|
||||
ViewportPositionHorizontal,
|
||||
ViewportPositionVertical,
|
||||
UIWebsite as UIWebsiteCore,
|
||||
} from "../../Events/ui/UIWebsite";
|
||||
import { IframeApiContribution, queryWorkadventure, sendToWorkadventure } from "../IframeApiContribution";
|
||||
|
||||
class UIWebsitePositionInternal {
|
||||
private readonly website: UIWebsite;
|
||||
private _vertical: ViewportPositionVertical;
|
||||
private _horizontal: ViewportPositionHorizontal;
|
||||
|
||||
constructor(uiWebsite: UIWebsite, position: UIWebsitePosition) {
|
||||
this.website = uiWebsite;
|
||||
this._vertical = position.vertical;
|
||||
this._horizontal = position.horizontal;
|
||||
}
|
||||
|
||||
public get vertical() {
|
||||
return this._vertical;
|
||||
}
|
||||
|
||||
public set vertical(vertical: ViewportPositionVertical) {
|
||||
this._vertical = vertical;
|
||||
sendToWorkadventure({
|
||||
type: "modifyUIWebsite",
|
||||
data: {
|
||||
id: this.website.id,
|
||||
position: {
|
||||
vertical: this._vertical,
|
||||
horizontal: this._horizontal,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
public get horizontal() {
|
||||
return this._horizontal;
|
||||
}
|
||||
|
||||
public set horizontal(horizontal: ViewportPositionHorizontal) {
|
||||
this._horizontal = horizontal;
|
||||
sendToWorkadventure({
|
||||
type: "modifyUIWebsite",
|
||||
data: {
|
||||
id: this.website.id,
|
||||
position: {
|
||||
vertical: this._vertical,
|
||||
horizontal: this._horizontal,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class UIWebsiteSizeInternal {
|
||||
private readonly website: UIWebsite;
|
||||
private _height: UIWebsiteCSSValue;
|
||||
private _width: UIWebsiteCSSValue;
|
||||
|
||||
constructor(uiWebsite: UIWebsite, size: UIWebsiteSize) {
|
||||
this.website = uiWebsite;
|
||||
this._height = size.height;
|
||||
this._width = size.width;
|
||||
}
|
||||
|
||||
public get height() {
|
||||
return this._height;
|
||||
}
|
||||
|
||||
public set height(height: UIWebsiteCSSValue) {
|
||||
this._height = height;
|
||||
sendToWorkadventure({
|
||||
type: "modifyUIWebsite",
|
||||
data: {
|
||||
id: this.website.id,
|
||||
size: {
|
||||
height: this._height,
|
||||
width: this._width,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
public get width() {
|
||||
return this._height;
|
||||
}
|
||||
|
||||
public set width(width: UIWebsiteCSSValue) {
|
||||
this._width = width;
|
||||
sendToWorkadventure({
|
||||
type: "modifyUIWebsite",
|
||||
data: {
|
||||
id: this.website.id,
|
||||
size: {
|
||||
height: this._height,
|
||||
width: this._width,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class UIWebsiteMarginInternal {
|
||||
private readonly website: UIWebsite;
|
||||
private _top?: UIWebsiteCSSValue;
|
||||
private _bottom?: UIWebsiteCSSValue;
|
||||
private _left?: UIWebsiteCSSValue;
|
||||
private _right?: UIWebsiteCSSValue;
|
||||
|
||||
constructor(uiWebsite: UIWebsite, margin: UIWebsiteMargin) {
|
||||
this.website = uiWebsite;
|
||||
this._top = margin.top;
|
||||
this._bottom = margin.bottom;
|
||||
this._left = margin.left;
|
||||
this._right = margin.right;
|
||||
}
|
||||
|
||||
public get top() {
|
||||
return this._top;
|
||||
}
|
||||
|
||||
public set top(top: UIWebsiteCSSValue | undefined) {
|
||||
this._top = top;
|
||||
sendToWorkadventure({
|
||||
type: "modifyUIWebsite",
|
||||
data: {
|
||||
id: this.website.id,
|
||||
margin: {
|
||||
top: this._top,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
public get bottom() {
|
||||
return this._bottom;
|
||||
}
|
||||
|
||||
public set bottom(bottom: UIWebsiteCSSValue | undefined) {
|
||||
this._bottom = bottom;
|
||||
sendToWorkadventure({
|
||||
type: "modifyUIWebsite",
|
||||
data: {
|
||||
id: this.website.id,
|
||||
margin: {
|
||||
bottom: this._bottom,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
public get left() {
|
||||
return this._left;
|
||||
}
|
||||
|
||||
public set left(left: UIWebsiteCSSValue | undefined) {
|
||||
this._left = left;
|
||||
sendToWorkadventure({
|
||||
type: "modifyUIWebsite",
|
||||
data: {
|
||||
id: this.website.id,
|
||||
margin: {
|
||||
left: this._left,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
public get right() {
|
||||
return this._right;
|
||||
}
|
||||
|
||||
public set right(right: UIWebsiteCSSValue | undefined) {
|
||||
this._right = right;
|
||||
sendToWorkadventure({
|
||||
type: "modifyUIWebsite",
|
||||
data: {
|
||||
id: this.website.id,
|
||||
margin: {
|
||||
right: this._right,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class UIWebsite {
|
||||
public readonly id: string;
|
||||
private _url: string;
|
||||
private _visible: boolean;
|
||||
private readonly _allowPolicy: string;
|
||||
private readonly _allowApi: boolean;
|
||||
private _position: UIWebsitePositionInternal;
|
||||
private _size: UIWebsiteSizeInternal;
|
||||
private _margin: UIWebsiteMarginInternal;
|
||||
|
||||
constructor(config: UIWebsiteCore) {
|
||||
this.id = config.id;
|
||||
this._url = config.url;
|
||||
this._visible = config.visible ?? true;
|
||||
this._allowPolicy = config.allowPolicy ?? "";
|
||||
this._allowApi = config.allowApi ?? false;
|
||||
this._position = new UIWebsitePositionInternal(this, config.position);
|
||||
this._size = new UIWebsiteSizeInternal(this, config.size);
|
||||
this._margin = new UIWebsiteMarginInternal(this, config.margin ?? {});
|
||||
}
|
||||
|
||||
public get url() {
|
||||
return this._url;
|
||||
}
|
||||
|
||||
public set url(url: string) {
|
||||
this._url = url;
|
||||
sendToWorkadventure({
|
||||
type: "modifyUIWebsite",
|
||||
data: {
|
||||
id: this.id,
|
||||
url: this._url,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
public get visible() {
|
||||
return this._visible;
|
||||
}
|
||||
|
||||
public set visible(visible: boolean) {
|
||||
this._visible = visible;
|
||||
sendToWorkadventure({
|
||||
type: "modifyUIWebsite",
|
||||
data: {
|
||||
id: this.id,
|
||||
visible: this._visible,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
public get allowPolicy() {
|
||||
return this._allowPolicy;
|
||||
}
|
||||
|
||||
public get allowApi() {
|
||||
return this._allowApi;
|
||||
}
|
||||
|
||||
public get position() {
|
||||
return this._position;
|
||||
}
|
||||
|
||||
public set position(position: UIWebsitePosition) {
|
||||
this._position = new UIWebsitePositionInternal(this, position);
|
||||
sendToWorkadventure({
|
||||
type: "modifyUIWebsite",
|
||||
data: {
|
||||
id: this.id,
|
||||
position: {
|
||||
vertical: this._position.vertical,
|
||||
horizontal: this._position.horizontal,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
public get size() {
|
||||
return this._size;
|
||||
}
|
||||
|
||||
public set size(size: UIWebsiteSize) {
|
||||
this._size = new UIWebsiteSizeInternal(this, size);
|
||||
sendToWorkadventure({
|
||||
type: "modifyUIWebsite",
|
||||
data: {
|
||||
id: this.id,
|
||||
size: {
|
||||
height: this._size.height,
|
||||
width: this._size.width,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
public get margin() {
|
||||
return this._margin;
|
||||
}
|
||||
|
||||
public set margin(margin: UIWebsiteMargin) {
|
||||
this._margin = new UIWebsiteMarginInternal(this, margin);
|
||||
sendToWorkadventure({
|
||||
type: "modifyUIWebsite",
|
||||
data: {
|
||||
id: this.id,
|
||||
margin: {
|
||||
top: this._margin.top,
|
||||
bottom: this._margin.bottom,
|
||||
left: this._margin.left,
|
||||
right: this._margin.right,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
close() {
|
||||
return queryWorkadventure({
|
||||
type: "closeUIWebsite",
|
||||
data: this.id,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class UIWebsiteCommands extends IframeApiContribution<UIWebsiteCommands> {
|
||||
callbacks = [];
|
||||
|
||||
async open(createUIWebsite: CreateUIWebsiteEvent): Promise<UIWebsite> {
|
||||
const result = await queryWorkadventure({
|
||||
type: "openUIWebsite",
|
||||
data: createUIWebsite,
|
||||
});
|
||||
|
||||
return new UIWebsite(result);
|
||||
}
|
||||
|
||||
async getAll(): Promise<UIWebsite[]> {
|
||||
const result = await queryWorkadventure({
|
||||
type: "getUIWebsites",
|
||||
data: undefined,
|
||||
});
|
||||
|
||||
return result.map((current) => new UIWebsite(current));
|
||||
}
|
||||
}
|
||||
|
||||
export default new UIWebsiteCommands();
|
@ -14,6 +14,8 @@ import {
|
||||
isActionsMenuActionClickedEvent,
|
||||
} from "../Events/ActionsMenuActionClickedEvent";
|
||||
import { Observable, Subject } from "rxjs";
|
||||
import type { UIWebsiteCommands } from "./Ui/UIWebsite";
|
||||
import website from "./Ui/UIWebsite";
|
||||
|
||||
let popupId = 0;
|
||||
const popups: Map<number, Popup> = new Map<number, Popup>();
|
||||
@ -283,6 +285,10 @@ export class WorkAdventureUiCommands extends IframeApiContribution<WorkAdventure
|
||||
actionMessages.set(actionMessage.uuid, actionMessage);
|
||||
return actionMessage;
|
||||
}
|
||||
|
||||
get website(): UIWebsiteCommands {
|
||||
return website;
|
||||
}
|
||||
}
|
||||
|
||||
export default new WorkAdventureUiCommands();
|
||||
|
@ -39,6 +39,8 @@
|
||||
import ActionsMenu from "./ActionsMenu/ActionsMenu.svelte";
|
||||
import Lazy from "./Lazy.svelte";
|
||||
import { showDesktopCapturerSourcePicker } from "../Stores/ScreenSharingStore";
|
||||
import UiWebsiteContainer from "./UI/Website/UIWebsiteContainer.svelte";
|
||||
import { uiWebsitesStore } from "../Stores/UIWebsiteStore";
|
||||
|
||||
let mainLayout: HTMLDivElement;
|
||||
|
||||
@ -128,6 +130,10 @@
|
||||
{#if hasEmbedScreen}
|
||||
<EmbedScreensContainer />
|
||||
{/if}
|
||||
|
||||
{#if $uiWebsitesStore}
|
||||
<UiWebsiteContainer />
|
||||
{/if}
|
||||
</section>
|
||||
|
||||
<section id="main-layout-baseline">
|
||||
|
21
front/src/Components/UI/Website/UIWebsiteContainer.svelte
Normal file
21
front/src/Components/UI/Website/UIWebsiteContainer.svelte
Normal file
@ -0,0 +1,21 @@
|
||||
<script lang="ts">
|
||||
import { uiWebsitesStore } from "../../../Stores/UIWebsiteStore";
|
||||
import UiWebsiteLayer from "./UIWebsiteLayer.svelte";
|
||||
</script>
|
||||
|
||||
<div id="ui-website-container">
|
||||
{#each $uiWebsitesStore.reverse() as uiWebsite (uiWebsite.id)}
|
||||
<UiWebsiteLayer {uiWebsite} />
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
#ui-website-container {
|
||||
position: absolute;
|
||||
z-index: 180;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
</style>
|
66
front/src/Components/UI/Website/UIWebsiteLayer.svelte
Normal file
66
front/src/Components/UI/Website/UIWebsiteLayer.svelte
Normal file
@ -0,0 +1,66 @@
|
||||
<script lang="ts">
|
||||
import { onDestroy, onMount } from "svelte";
|
||||
import { UIWebsite } from "../../../Api/Events/ui/UIWebsite";
|
||||
import { iframeListener } from "../../../Api/IframeListener";
|
||||
|
||||
export let uiWebsite: UIWebsite;
|
||||
|
||||
let main: HTMLDivElement;
|
||||
const iframe = document.createElement("iframe");
|
||||
|
||||
$: {
|
||||
iframe.id = `ui-website-${uiWebsite.id}`;
|
||||
iframe.src = uiWebsite.url;
|
||||
iframe.title = uiWebsite.url;
|
||||
iframe.style.border = "0";
|
||||
iframe.allow = uiWebsite.allowPolicy ?? "";
|
||||
iframe.style.height = uiWebsite.size.height;
|
||||
iframe.style.width = uiWebsite.size.width;
|
||||
iframe.style.visibility = uiWebsite.visible ? "visible" : "hidden";
|
||||
iframe.style.margin = uiWebsite.margin
|
||||
? `${uiWebsite.margin.top ? uiWebsite.margin.top : "O"} ${
|
||||
uiWebsite.margin.right ? uiWebsite.margin.right : "O"
|
||||
} ${uiWebsite.margin.bottom ? uiWebsite.margin.bottom : "O"} ${
|
||||
uiWebsite.margin.left ? uiWebsite.margin.left : "O"
|
||||
}`
|
||||
: "0";
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
main.appendChild(iframe);
|
||||
|
||||
if (uiWebsite.allowApi) {
|
||||
iframeListener.registerIframe(iframe);
|
||||
}
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
if (uiWebsite.allowApi) {
|
||||
iframeListener.unregisterIframe(iframe);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<div
|
||||
bind:this={main}
|
||||
class="layer"
|
||||
style:justify-content={uiWebsite.position.horizontal === "middle"
|
||||
? "center"
|
||||
: uiWebsite.position.horizontal === "right"
|
||||
? "end"
|
||||
: "start"}
|
||||
style:align-items={uiWebsite.position.vertical === "middle"
|
||||
? "center"
|
||||
: uiWebsite.position.vertical === "bottom"
|
||||
? "end"
|
||||
: "top"}
|
||||
/>
|
||||
|
||||
<style lang="scss">
|
||||
.layer {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
position: absolute;
|
||||
}
|
||||
</style>
|
@ -102,6 +102,7 @@ import CancelablePromise from "cancelable-promise";
|
||||
import { Deferred } from "ts-deferred";
|
||||
import { SuperLoaderPlugin } from "../Services/SuperLoaderPlugin";
|
||||
import { ErrorScreenMessage, PlayerDetailsUpdatedMessage } from "../../Messages/ts-proto-generated/protos/messages";
|
||||
import { uiWebsiteManager } from "./UI/UIWebsiteManager";
|
||||
export interface GameSceneInitInterface {
|
||||
initPosition: PointInterface | null;
|
||||
reconnecting: boolean;
|
||||
@ -1298,6 +1299,18 @@ ${escapedMessage}
|
||||
return coWebsiteManager.closeCoWebsites();
|
||||
});
|
||||
|
||||
iframeListener.registerAnswerer("openUIWebsite", (websiteConfig) => {
|
||||
return uiWebsiteManager.open(websiteConfig);
|
||||
});
|
||||
|
||||
iframeListener.registerAnswerer("getUIWebsites", () => {
|
||||
return uiWebsiteManager.getAll();
|
||||
});
|
||||
|
||||
iframeListener.registerAnswerer("closeUIWebsite", (websiteId) => {
|
||||
return uiWebsiteManager.close(websiteId);
|
||||
});
|
||||
|
||||
iframeListener.registerAnswerer("getMapData", () => {
|
||||
return {
|
||||
data: this.gameMap.getMap(),
|
||||
@ -1594,6 +1607,9 @@ ${escapedMessage}
|
||||
iframeListener.unregisterAnswerer("getCoWebsites");
|
||||
iframeListener.unregisterAnswerer("setPlayerOutline");
|
||||
iframeListener.unregisterAnswerer("setVariable");
|
||||
iframeListener.unregisterAnswerer("openUIWebsite");
|
||||
iframeListener.unregisterAnswerer("getUIWebsites");
|
||||
iframeListener.unregisterAnswerer("closeUIWebsite");
|
||||
this.sharedVariablesManager?.close();
|
||||
this.embeddedWebsiteManager?.close();
|
||||
|
||||
|
94
front/src/Phaser/Game/UI/UIWebsiteManager.ts
Normal file
94
front/src/Phaser/Game/UI/UIWebsiteManager.ts
Normal file
@ -0,0 +1,94 @@
|
||||
import { get } from "svelte/store";
|
||||
import { CreateUIWebsiteEvent, ModifyUIWebsiteEvent, UIWebsite } from "../../../Api/Events/ui/UIWebsite";
|
||||
import { iframeListener } from "../../../Api/IframeListener";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { uiWebsitesStore } from "../../../Stores/UIWebsiteStore";
|
||||
|
||||
class UIWebsiteManager {
|
||||
constructor() {
|
||||
iframeListener.modifyUIWebsiteStream.subscribe((websiteEvent: ModifyUIWebsiteEvent) => {
|
||||
const website = get(uiWebsitesStore).find((currentWebsite) => currentWebsite.id === websiteEvent.id);
|
||||
if (!website) {
|
||||
throw new Error(`Could not find ui website with the id "${websiteEvent.id}" in your map`);
|
||||
}
|
||||
|
||||
if (websiteEvent.url) {
|
||||
website.url = websiteEvent.url;
|
||||
}
|
||||
|
||||
if (websiteEvent.visible !== undefined) {
|
||||
website.visible = websiteEvent.visible;
|
||||
}
|
||||
|
||||
if (websiteEvent.position) {
|
||||
if (websiteEvent.position.horizontal) {
|
||||
website.position.horizontal = websiteEvent.position.horizontal;
|
||||
}
|
||||
|
||||
if (websiteEvent.position.vertical) {
|
||||
website.position.vertical = websiteEvent.position.vertical;
|
||||
}
|
||||
}
|
||||
|
||||
if (websiteEvent.size) {
|
||||
if (websiteEvent.size.height) {
|
||||
website.size.height = websiteEvent.size.height;
|
||||
}
|
||||
|
||||
if (websiteEvent.size.width) {
|
||||
website.size.width = websiteEvent.size.width;
|
||||
}
|
||||
}
|
||||
|
||||
if (websiteEvent.margin) {
|
||||
website.margin = {};
|
||||
|
||||
if (websiteEvent.margin.top !== undefined) {
|
||||
website.margin.top = websiteEvent.margin.top;
|
||||
}
|
||||
|
||||
if (websiteEvent.margin.bottom !== undefined) {
|
||||
website.margin.bottom = websiteEvent.margin.bottom;
|
||||
}
|
||||
|
||||
if (websiteEvent.margin.left !== undefined) {
|
||||
website.margin.left = websiteEvent.margin.left;
|
||||
}
|
||||
|
||||
if (websiteEvent.margin.right !== undefined) {
|
||||
website.margin.right = websiteEvent.margin.right;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public open(websiteConfig: CreateUIWebsiteEvent): UIWebsite {
|
||||
const newWebsite: UIWebsite = {
|
||||
...websiteConfig,
|
||||
id: uuidv4(),
|
||||
visible: websiteConfig.visible ?? true,
|
||||
allowPolicy: websiteConfig.allowPolicy ?? "",
|
||||
allowApi: websiteConfig.allowApi ?? false,
|
||||
};
|
||||
|
||||
uiWebsitesStore.add(newWebsite);
|
||||
|
||||
return newWebsite;
|
||||
}
|
||||
|
||||
public getAll(): UIWebsite[] {
|
||||
return get(uiWebsitesStore);
|
||||
}
|
||||
|
||||
public close(websiteId: string) {
|
||||
const uiWebsite = get(uiWebsitesStore).find((currentWebsite) => currentWebsite.id === websiteId);
|
||||
|
||||
if (!uiWebsite) {
|
||||
return;
|
||||
}
|
||||
|
||||
uiWebsitesStore.remove(uiWebsite);
|
||||
}
|
||||
}
|
||||
|
||||
export const uiWebsiteManager = new UIWebsiteManager();
|
20
front/src/Stores/UIWebsiteStore.ts
Normal file
20
front/src/Stores/UIWebsiteStore.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import { writable } from "svelte/store";
|
||||
import { UIWebsite } from "../Api/Events/ui/UIWebsite";
|
||||
|
||||
function createUIWebsiteStore() {
|
||||
const { subscribe, update, set } = writable(Array<UIWebsite>());
|
||||
|
||||
set(Array<UIWebsite>());
|
||||
|
||||
return {
|
||||
subscribe,
|
||||
add: (uiWebsite: UIWebsite) => {
|
||||
update((currentArray) => [...currentArray, uiWebsite]);
|
||||
},
|
||||
remove: (uiWebsite: UIWebsite) => {
|
||||
update((currentArray) => currentArray.filter((currentWebsite) => currentWebsite.id !== uiWebsite.id));
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export const uiWebsitesStore = createUIWebsiteStore();
|
@ -2239,10 +2239,10 @@ prelude-ls@^1.2.1:
|
||||
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
|
||||
integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==
|
||||
|
||||
prettier-plugin-svelte@^2.5.0:
|
||||
version "2.5.0"
|
||||
resolved "https://registry.yarnpkg.com/prettier-plugin-svelte/-/prettier-plugin-svelte-2.5.0.tgz#7922534729f7febe59b4c56c3f5360539f0d8ab1"
|
||||
integrity sha512-+iHY2uGChOngrgKielJUnqo74gIL/EO5oeWm8MftFWjEi213lq9QYTOwm1pv4lI1nA61tdgf80CF2i5zMcu1kw==
|
||||
prettier-plugin-svelte@^2.7.0:
|
||||
version "2.7.0"
|
||||
resolved "https://registry.yarnpkg.com/prettier-plugin-svelte/-/prettier-plugin-svelte-2.7.0.tgz#ecfa4fe824238a4466a3497df1a96d15cf43cabb"
|
||||
integrity sha512-fQhhZICprZot2IqEyoiUYLTRdumULGRvw0o4dzl5jt0jfzVWdGqeYW27QTWAeXhoupEZJULmNoH3ueJwUWFLIA==
|
||||
|
||||
prettier@^2.0.2:
|
||||
version "2.5.1"
|
||||
|
5
maps/tests/UIWebsite/index.html
Normal file
5
maps/tests/UIWebsite/index.html
Normal file
@ -0,0 +1,5 @@
|
||||
<html>
|
||||
<body style="background-color: white;">
|
||||
This is test page
|
||||
</body>
|
||||
</html>
|
48
maps/tests/UIWebsite/script.js
Normal file
48
maps/tests/UIWebsite/script.js
Normal file
@ -0,0 +1,48 @@
|
||||
WA.onInit().then(() => {
|
||||
initListeners();
|
||||
});
|
||||
|
||||
function initListeners() {
|
||||
let first_website = undefined;
|
||||
let second_website = undefined;
|
||||
|
||||
WA.room.onEnterLayer('first_website').subscribe(async () => {
|
||||
first_website = await WA.ui.website.open({
|
||||
url: "http://maps.workadventure.localhost/tests/UIWebsite/index.html",
|
||||
position: {
|
||||
vertical: "middle",
|
||||
horizontal: "middle",
|
||||
},
|
||||
size: {
|
||||
height: "50vh",
|
||||
width: "50vw",
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
WA.room.onLeaveLayer('first_website').subscribe(() => {
|
||||
if (first_website) {
|
||||
first_website.close();
|
||||
}
|
||||
});
|
||||
|
||||
WA.room.onEnterLayer('second_website').subscribe(async () => {
|
||||
second_website = await WA.ui.website.open({
|
||||
url: "https://www.wikipedia.org/",
|
||||
position: {
|
||||
vertical: "top",
|
||||
horizontal: "right",
|
||||
},
|
||||
size: {
|
||||
height: "20vh",
|
||||
width: "50vw",
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
WA.room.onLeaveLayer('second_website').subscribe(() => {
|
||||
if (second_website) {
|
||||
second_website.close();
|
||||
}
|
||||
});
|
||||
}
|
679
maps/tests/UIWebsite/uiwebsite.json
Normal file
679
maps/tests/UIWebsite/uiwebsite.json
Normal file
@ -0,0 +1,679 @@
|
||||
{ "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_website",
|
||||
"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_website",
|
||||
"opacity":1,
|
||||
"type":"tilelayer",
|
||||
"visible":true,
|
||||
"width":10,
|
||||
"x":0,
|
||||
"y":0
|
||||
},
|
||||
{
|
||||
"draworder":"topdown",
|
||||
"id":3,
|
||||
"name":"floorLayer",
|
||||
"objects":[
|
||||
{
|
||||
"height":116.924156284309,
|
||||
"id":1,
|
||||
"name":"Tests",
|
||||
"rotation":0,
|
||||
"text":
|
||||
{
|
||||
"fontfamily":"Sans Serif",
|
||||
"pixelsize":8,
|
||||
"text":"Test 1:\nMove on the white carpet to display a UIWebsite.\n\nTest 2:\nMove on the blue carpet to display an other UIWebsite above the first.",
|
||||
"wrap":true
|
||||
},
|
||||
"type":"",
|
||||
"visible":true,
|
||||
"width":158.381128664136,
|
||||
"x":1.64026713939023,
|
||||
"y":201.037039933902
|
||||
}],
|
||||
"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.8.4",
|
||||
"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.8",
|
||||
"width":10
|
||||
}
|
@ -363,6 +363,14 @@
|
||||
<a href="#" class="testLink" data-testmap="Modules/without_modules.json" target="_blank">Testing scripts with modules mode disabled</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<input type="radio" name="test-uiwebsite"> Success <input type="radio" name="test-uiwebsite"> Failure <input type="radio" name="test-uiwebsite" checked> Pending
|
||||
</td>
|
||||
<td>
|
||||
<a href="#" class="testLink" data-testmap="UIWebsite/uiwebsite.json" target="_blank">Testing UIWebsites</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<h2>CoWebsite</h2>
|
||||
<table class="table">
|
||||
|
Loading…
Reference in New Issue
Block a user