Implement the new co website system
This commit is contained in:
parent
1ee0b28f66
commit
da8cc661b7
74
front/dist/index.tmpl.html
vendored
74
front/dist/index.tmpl.html
vendored
@ -37,6 +37,54 @@
|
||||
<div class="main-container" id="main-container">
|
||||
<!-- Create the editor container -->
|
||||
<div id="game" class="game">
|
||||
<div id="cowebsite-container">
|
||||
<div id="cowebsite-container-main">
|
||||
<div id="cowebsite-slot-1">
|
||||
<div class="actions">
|
||||
<button type="button" class="nes-btn is-primary expand">></button>
|
||||
<button type="button" class="nes-btn is-error close">×</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="cowebsite-container-sub">
|
||||
<div id="cowebsite-slot-2">
|
||||
<div class="overlay">
|
||||
<div class="actions">
|
||||
<button type="button" title="Close" class="nes-btn is-error close">×</button>
|
||||
</div>
|
||||
<div class="actions-move">
|
||||
<button type="button" title="Expand" class="nes-btn is-primary expand">></button>
|
||||
<button type="button" title="Hightlight" class="nes-btn is-secondary hightlight">Ξ</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="cowebsite-slot-3">
|
||||
<div class="overlay">
|
||||
<div class="actions">
|
||||
<button type="button" title="Close" class="nes-btn is-error close">×</button>
|
||||
</div>
|
||||
<div class="actions-move">
|
||||
<button type="button" title="Expand" class="nes-btn is-primary expand">></button>
|
||||
<button type="button" title="Hightlight" class="nes-btn is-secondary hightlight">Ξ</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="cowebsite-slot-4">
|
||||
<div class="overlay">
|
||||
<div class="actions">
|
||||
<button type="button" title="Close" class="nes-btn is-error close">×</button>
|
||||
</div>
|
||||
<div class="actions-move">
|
||||
<button type="button" title="Expand" class="nes-btn is-primary expand">></button>
|
||||
<button type="button" title="Hightlight" class="nes-btn is-secondary hightlight">Ξ</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="svelte-overlay"></div>
|
||||
<div id="game-overlay" class="game-overlay">
|
||||
<div id="main-section" class="main-section">
|
||||
@ -48,19 +96,25 @@
|
||||
</div>
|
||||
</div>
|
||||
<div id="cowebsite" class="cowebsite hidden">
|
||||
<aside id="cowebsite-aside">
|
||||
<img src="/static/images/menu.svg" alt="hold to resize"/>
|
||||
</aside>
|
||||
<main id="cowebsite-main">
|
||||
</main>
|
||||
<button class="top-right-btn" id="cowebsite-fullscreen" alt="fullscreen mode">
|
||||
<img id="cowebsite-fullscreen-open" src="resources/logos/fullscreen.svg"/>
|
||||
<img id="cowebsite-fullscreen-close" style="display: none;" src="resources/logos/fullscreen-exit.svg"/>
|
||||
<aside id="cowebsite-aside" class="noselect">
|
||||
<div id="cowebsite-aside-buttons">
|
||||
<button class="top-right-btn nes-btn is-error" id="cowebsite-close" alt="close all co-websites">
|
||||
×
|
||||
</button>
|
||||
<button class="top-right-btn" id="cowebsite-close" alt="close the iframe">
|
||||
<img src="resources/logos/close.svg"/>
|
||||
<button class="top-right-btn nes-btn is-primary" id="cowebsite-fullscreen" alt="fullscreen mode">
|
||||
<img id="cowebsite-fullscreen-close" style="display: none;" src="resources/logos/fullscreen-exit.svg"/>
|
||||
<img id="cowebsite-fullscreen-open" src="resources/logos/fullscreen.svg"/>
|
||||
</button>
|
||||
</div>
|
||||
<div id="cowebsite-aside-holder">
|
||||
<img src="/static/images/menu.svg" alt="hold to resize"/>
|
||||
</div>
|
||||
<div id="cowebsite-sub-icons"></div>
|
||||
</aside>
|
||||
<main id="cowebsite-slot-0">
|
||||
</main>
|
||||
</div>
|
||||
<div id="cowebsite-buffer"></div>
|
||||
</div>
|
||||
|
||||
<div id="activeScreenSharing" class="active-screen-sharing active">
|
||||
|
@ -124,6 +124,7 @@
|
||||
top: 10%;
|
||||
|
||||
position: relative;
|
||||
z-index: 80;
|
||||
margin: auto;
|
||||
|
||||
display: grid;
|
||||
|
@ -29,6 +29,8 @@
|
||||
<style lang="scss">
|
||||
.menuIcon {
|
||||
display: inline-grid;
|
||||
z-index: 90;
|
||||
position: relative;
|
||||
margin: 25px;
|
||||
img {
|
||||
pointer-events: auto;
|
||||
|
@ -1284,7 +1284,7 @@ ${escapedMessage}
|
||||
|
||||
public cleanupClosingScene(): void {
|
||||
// stop playing audio, close any open website, stop any open Jitsi
|
||||
coWebsiteManager.closeCoWebsite();
|
||||
coWebsiteManager.closeCoWebsites();
|
||||
// Stop the script, if any
|
||||
const scripts = this.getScriptUrls(this.mapFile);
|
||||
for (const script of scripts) {
|
||||
|
@ -10,9 +10,12 @@ enum iframeStates {
|
||||
opened,
|
||||
}
|
||||
|
||||
const cowebsiteDivId = "cowebsite"; // the id of the whole container.
|
||||
const cowebsiteMainDomId = "cowebsite-main"; // the id of the parent div of the iframe.
|
||||
const cowebsiteDomId = "cowebsite"; // the id of the whole container.
|
||||
const cowebsiteContainerDomId = "cowebsite-container"; // the id of the whole container.
|
||||
const cowebsiteMainDomId = "cowebsite-slot-0"; // the id of the parent div of the iframe.
|
||||
const cowebsiteBufferDomId = "cowebsite-buffer"; // the id of the container who contains cowebsite iframes.
|
||||
const cowebsiteAsideDomId = "cowebsite-aside"; // the id of the parent div of the iframe.
|
||||
const cowebsiteSubIconsDomId = "cowebsite-sub-icons";
|
||||
export const cowebsiteCloseButtonId = "cowebsite-close";
|
||||
const cowebsiteFullScreenButtonId = "cowebsite-fullscreen";
|
||||
const cowebsiteOpenFullScreenImageId = "cowebsite-fullscreen-open";
|
||||
@ -24,8 +27,19 @@ interface TouchMoveCoordinates {
|
||||
y: number;
|
||||
}
|
||||
|
||||
export interface CoWebsite {
|
||||
iframe: HTMLIFrameElement,
|
||||
icon: HTMLDivElement,
|
||||
position: number
|
||||
}
|
||||
|
||||
interface CoWebsiteSlot {
|
||||
container: HTMLElement,
|
||||
position: number
|
||||
}
|
||||
|
||||
class CoWebsiteManager {
|
||||
private opened: iframeStates = iframeStates.closed;
|
||||
private openedMain: iframeStates = iframeStates.closed;
|
||||
|
||||
private _onResize: Subject<void> = new Subject();
|
||||
public onResize = this._onResize.asObservable();
|
||||
@ -34,30 +48,41 @@ class CoWebsiteManager {
|
||||
* So we use this promise to queue up every cowebsite state transition
|
||||
*/
|
||||
private currentOperationPromise: Promise<void> = Promise.resolve();
|
||||
private cowebsiteDiv: HTMLDivElement;
|
||||
private cowebsiteDom: HTMLDivElement;
|
||||
private cowebsiteContainerDom: HTMLDivElement;
|
||||
private resizing: boolean = false;
|
||||
private cowebsiteMainDom: HTMLDivElement;
|
||||
private cowebsiteBufferDom: HTMLDivElement;
|
||||
private cowebsiteAsideDom: HTMLDivElement;
|
||||
private cowebsiteSubIconsDom: HTMLDivElement;
|
||||
private previousTouchMoveCoordinates: TouchMoveCoordinates | null = null; //only use on touchscreens to track touch movement
|
||||
|
||||
private coWebsites: CoWebsite[] = [];
|
||||
|
||||
private slots: CoWebsiteSlot[];
|
||||
|
||||
private resizeObserver = new ResizeObserver(entries => {
|
||||
this.resizeAllIframes();
|
||||
});
|
||||
|
||||
get width(): number {
|
||||
return this.cowebsiteDiv.clientWidth;
|
||||
return this.cowebsiteDom.clientWidth;
|
||||
}
|
||||
|
||||
set width(width: number) {
|
||||
this.cowebsiteDiv.style.width = width + "px";
|
||||
this.cowebsiteDom.style.width = width + "px";
|
||||
}
|
||||
|
||||
set widthPercent(width: number) {
|
||||
this.cowebsiteDiv.style.width = width + "%";
|
||||
this.cowebsiteDom.style.width = width + "%";
|
||||
}
|
||||
|
||||
get height(): number {
|
||||
return this.cowebsiteDiv.clientHeight;
|
||||
return this.cowebsiteDom.clientHeight;
|
||||
}
|
||||
|
||||
set height(height: number) {
|
||||
this.cowebsiteDiv.style.height = height + "px";
|
||||
this.cowebsiteDom.style.height = height + "px";
|
||||
}
|
||||
|
||||
get verticalMode(): boolean {
|
||||
@ -69,19 +94,59 @@ class CoWebsiteManager {
|
||||
}
|
||||
|
||||
constructor() {
|
||||
this.cowebsiteDiv = HtmlUtils.getElementByIdOrFail<HTMLDivElement>(cowebsiteDivId);
|
||||
this.cowebsiteDom = HtmlUtils.getElementByIdOrFail<HTMLDivElement>(cowebsiteDomId);
|
||||
this.cowebsiteContainerDom = HtmlUtils.getElementByIdOrFail<HTMLDivElement>(cowebsiteContainerDomId);
|
||||
this.cowebsiteMainDom = HtmlUtils.getElementByIdOrFail<HTMLDivElement>(cowebsiteMainDomId);
|
||||
this.cowebsiteBufferDom = HtmlUtils.getElementByIdOrFail<HTMLDivElement>(cowebsiteBufferDomId);
|
||||
this.cowebsiteAsideDom = HtmlUtils.getElementByIdOrFail<HTMLDivElement>(cowebsiteAsideDomId);
|
||||
this.cowebsiteSubIconsDom = HtmlUtils.getElementByIdOrFail<HTMLDivElement>(cowebsiteSubIconsDomId);
|
||||
this.initResizeListeners(touchScreenManager.supportTouchScreen);
|
||||
|
||||
if (touchScreenManager.supportTouchScreen) {
|
||||
this.initResizeListeners(true);
|
||||
this.resizeObserver.observe(this.cowebsiteDom);
|
||||
this.resizeObserver.observe(this.cowebsiteContainerDom);
|
||||
|
||||
this.slots = [
|
||||
{
|
||||
container: this.cowebsiteMainDom,
|
||||
position: 0
|
||||
},
|
||||
{
|
||||
container: HtmlUtils.getElementByIdOrFail<HTMLDivElement>('cowebsite-slot-1'),
|
||||
position: 1
|
||||
},
|
||||
{
|
||||
container: HtmlUtils.getElementByIdOrFail<HTMLDivElement>('cowebsite-slot-2'),
|
||||
position: 2
|
||||
},
|
||||
{
|
||||
container: HtmlUtils.getElementByIdOrFail<HTMLDivElement>('cowebsite-slot-3'),
|
||||
position: 3
|
||||
},
|
||||
{
|
||||
container: HtmlUtils.getElementByIdOrFail<HTMLDivElement>('cowebsite-slot-4'),
|
||||
position: 4
|
||||
},
|
||||
];
|
||||
|
||||
this.slots.forEach((slot) => {
|
||||
this.resizeObserver.observe(slot.container);
|
||||
});
|
||||
|
||||
this.initActionsListeners();
|
||||
|
||||
const buttonCloseCoWebsites = HtmlUtils.getElementByIdOrFail(cowebsiteCloseButtonId);
|
||||
buttonCloseCoWebsites.addEventListener("click", () => {
|
||||
if (this.isSmallScreen() && this.coWebsites.length > 1) {
|
||||
const coWebsite = this.getCoWebsiteByPosition(0);
|
||||
|
||||
if (coWebsite) {
|
||||
this.removeCoWebsiteFromStack(coWebsite);
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.initResizeListeners(false);
|
||||
|
||||
const buttonCloseFrame = HtmlUtils.getElementByIdOrFail(cowebsiteCloseButtonId);
|
||||
buttonCloseFrame.addEventListener("click", () => {
|
||||
buttonCloseFrame.blur();
|
||||
this.closeCoWebsite();
|
||||
buttonCloseCoWebsites.blur();
|
||||
this.closeCoWebsites();
|
||||
});
|
||||
|
||||
const buttonFullScreenFrame = HtmlUtils.getElementByIdOrFail(cowebsiteFullScreenButtonId);
|
||||
@ -91,6 +156,16 @@ class CoWebsiteManager {
|
||||
});
|
||||
}
|
||||
|
||||
public getDevicePixelRatio(): number {
|
||||
//on chrome engines, movementX and movementY return global screens coordinates while other browser return pixels
|
||||
//so on chrome-based browser we need to adjust using 'devicePixelRatio'
|
||||
return window.navigator.userAgent.includes("Firefox") ? 1 : window.devicePixelRatio;
|
||||
}
|
||||
|
||||
private isSmallScreen(): boolean {
|
||||
return window.matchMedia("(max-aspect-ratio: 1/1)").matches;
|
||||
}
|
||||
|
||||
private initResizeListeners(touchMode: boolean) {
|
||||
const movecallback = (event: MouseEvent | TouchEvent) => {
|
||||
let x, y;
|
||||
@ -112,7 +187,6 @@ class CoWebsiteManager {
|
||||
|
||||
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 };
|
||||
@ -127,43 +201,231 @@ class CoWebsiteManager {
|
||||
this.previousTouchMoveCoordinates = null;
|
||||
}
|
||||
document.removeEventListener(touchMode ? "touchmove" : "mousemove", movecallback);
|
||||
this.getIframeDom().style.display = "block";
|
||||
this.cowebsiteMainDom.style.display = "block";
|
||||
this.resizing = false;
|
||||
});
|
||||
}
|
||||
|
||||
private getDevicePixelRatio(): number {
|
||||
//on chrome engines, movementX and movementY return global screens coordinates while other browser return pixels
|
||||
//so on chrome-based browser we need to adjust using 'devicePixelRatio'
|
||||
return window.navigator.userAgent.includes("Firefox") ? 1 : window.devicePixelRatio;
|
||||
private closeMain(): void {
|
||||
this.cowebsiteDom.classList.remove("loaded"); //edit the css class to trigger the transition
|
||||
this.cowebsiteDom.classList.add("hidden");
|
||||
this.openedMain = iframeStates.closed;
|
||||
this.resetStyleMain();
|
||||
this.cowebsiteDom.style.display = "none";
|
||||
}
|
||||
private loadMain(): void {
|
||||
this.cowebsiteDom.style.display = "flex";
|
||||
this.cowebsiteDom.classList.remove("hidden"); //edit the css class to trigger the transition
|
||||
this.cowebsiteDom.classList.add("loading");
|
||||
this.openedMain = iframeStates.loading;
|
||||
}
|
||||
private openMain(): void {
|
||||
this.cowebsiteDom.classList.remove("loading", "hidden"); //edit the css class to trigger the transition
|
||||
this.openedMain = iframeStates.opened;
|
||||
this.resetStyleMain();
|
||||
}
|
||||
|
||||
private close(): void {
|
||||
this.cowebsiteDiv.classList.remove("loaded"); //edit the css class to trigger the transition
|
||||
this.cowebsiteDiv.classList.add("hidden");
|
||||
this.opened = iframeStates.closed;
|
||||
this.resetStyle();
|
||||
}
|
||||
private load(): void {
|
||||
this.cowebsiteDiv.classList.remove("hidden"); //edit the css class to trigger the transition
|
||||
this.cowebsiteDiv.classList.add("loading");
|
||||
this.opened = iframeStates.loading;
|
||||
}
|
||||
private open(): void {
|
||||
this.cowebsiteDiv.classList.remove("loading", "hidden"); //edit the css class to trigger the transition
|
||||
this.opened = iframeStates.opened;
|
||||
this.resetStyle();
|
||||
public resetStyleMain() {
|
||||
this.cowebsiteDom.style.width = "";
|
||||
this.cowebsiteDom.style.height = "";
|
||||
}
|
||||
|
||||
public resetStyle() {
|
||||
this.cowebsiteDiv.style.width = "";
|
||||
this.cowebsiteDiv.style.height = "";
|
||||
private initActionsListeners() {
|
||||
this.slots.forEach((slot: CoWebsiteSlot) => {
|
||||
const expandButton = slot.container.querySelector('.expand');
|
||||
const highlightButton = slot.container.querySelector('.hightlight');
|
||||
const closeButton = slot.container.querySelector('.close');
|
||||
|
||||
if (expandButton) {
|
||||
expandButton.addEventListener('click', (event) => {
|
||||
event.preventDefault();
|
||||
const coWebsite = this.getCoWebsiteByPosition(slot.position);
|
||||
|
||||
if (!coWebsite) {
|
||||
return;
|
||||
}
|
||||
|
||||
private getIframeDom(): HTMLIFrameElement {
|
||||
const iframe = HtmlUtils.getElementByIdOrFail<HTMLDivElement>(cowebsiteDivId).querySelector("iframe");
|
||||
if (!iframe) throw new Error("Could not find iframe!");
|
||||
return iframe;
|
||||
this.moveRightPreviousCoWebsite(coWebsite, 0);
|
||||
});
|
||||
}
|
||||
|
||||
if (highlightButton) {
|
||||
highlightButton.addEventListener('click', (event) => {
|
||||
event.preventDefault();
|
||||
const coWebsite = this.getCoWebsiteByPosition(slot.position);
|
||||
|
||||
if (!coWebsite) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.moveRightPreviousCoWebsite(coWebsite, 1);
|
||||
});
|
||||
}
|
||||
|
||||
if (closeButton) {
|
||||
closeButton.addEventListener('click', (event) => {
|
||||
event.preventDefault();
|
||||
const coWebsite = this.getCoWebsiteByPosition(slot.position);
|
||||
|
||||
if (!coWebsite) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.removeCoWebsiteFromStack(coWebsite);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private searchCoWebsiteById(coWebsiteId: string): CoWebsite|undefined {
|
||||
return this.coWebsites.find((coWebsite: CoWebsite) => coWebsite.iframe.id === coWebsiteId);
|
||||
}
|
||||
|
||||
private getSlotByPosition(position: number): CoWebsiteSlot|undefined {
|
||||
return this.slots.find((slot: CoWebsiteSlot) => slot.position === position);
|
||||
}
|
||||
|
||||
private getCoWebsiteByPosition(position: number): CoWebsite|undefined {
|
||||
return this.coWebsites.find((coWebsite: CoWebsite) => coWebsite.position === position);
|
||||
}
|
||||
|
||||
private setIframeOffset(coWebsite: CoWebsite, slot: CoWebsiteSlot) {
|
||||
const bounding = slot.container.getBoundingClientRect();
|
||||
|
||||
coWebsite.iframe.style.top = bounding.top + 'px';
|
||||
coWebsite.iframe.style.left = bounding.left + 'px';
|
||||
coWebsite.iframe.style.width = (bounding.right - bounding.left) + 'px';
|
||||
coWebsite.iframe.style.height = (bounding.bottom - bounding.top) + 'px';
|
||||
}
|
||||
|
||||
private resizeAllIframes() {
|
||||
this.coWebsites.forEach((coWebsite: CoWebsite) => {
|
||||
const slot = this.getSlotByPosition(coWebsite.position);
|
||||
|
||||
if (slot) {
|
||||
this.setIframeOffset(coWebsite, slot);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private moveCoWebsite(coWebsite: CoWebsite, newPosition: number) {
|
||||
const oldSlot = this.getSlotByPosition(coWebsite.position);
|
||||
const newSlot = this.getSlotByPosition(newPosition);
|
||||
|
||||
if (!newSlot) {
|
||||
return;
|
||||
}
|
||||
|
||||
coWebsite.iframe.scrolling = newPosition === 0 || newPosition === 1 ? "yes" : "no";
|
||||
|
||||
if (newPosition === 0) {
|
||||
coWebsite.iframe.classList.add('main');
|
||||
coWebsite.icon.style.display = "none";
|
||||
} else {
|
||||
coWebsite.iframe.classList.remove('main');
|
||||
coWebsite.icon.style.display = "flex";
|
||||
}
|
||||
|
||||
if (newPosition === 1) {
|
||||
coWebsite.iframe.classList.add('sub-main');
|
||||
} else {
|
||||
coWebsite.iframe.classList.remove('sub-main');
|
||||
}
|
||||
|
||||
coWebsite.position = newPosition;
|
||||
|
||||
if (oldSlot && !this.getCoWebsiteByPosition(oldSlot.position)) {
|
||||
oldSlot.container.style.display = 'none';
|
||||
}
|
||||
|
||||
newSlot.container.style.display = 'block';
|
||||
|
||||
coWebsite.iframe.classList.remove('pixel');
|
||||
|
||||
this.resizeAllIframes();
|
||||
}
|
||||
|
||||
private moveLeftPreviousCoWebsite(coWebsite: CoWebsite, newPosition: number) {
|
||||
const nextCoWebsite = this.getCoWebsiteByPosition(coWebsite.position + 1);
|
||||
|
||||
this.moveCoWebsite(coWebsite, newPosition);
|
||||
|
||||
if (nextCoWebsite) {
|
||||
this.moveLeftPreviousCoWebsite(nextCoWebsite, nextCoWebsite.position - 1);
|
||||
}
|
||||
}
|
||||
|
||||
private moveRightPreviousCoWebsite(coWebsite: CoWebsite, newPosition: number) {
|
||||
if (newPosition >= 5) {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentCoWebsite = this.getCoWebsiteByPosition(newPosition);
|
||||
|
||||
this.moveCoWebsite(coWebsite, newPosition);
|
||||
|
||||
if (newPosition === 4 ||
|
||||
!currentCoWebsite ||
|
||||
currentCoWebsite.iframe.id === coWebsite.iframe.id
|
||||
)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!currentCoWebsite) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.moveRightPreviousCoWebsite(currentCoWebsite, currentCoWebsite.position + 1);
|
||||
}
|
||||
|
||||
private removeCoWebsiteFromStack(coWebsite: CoWebsite) {
|
||||
this.coWebsites = this.coWebsites.filter(
|
||||
(coWebsiteToRemove: CoWebsite) => coWebsiteToRemove.iframe.id !== coWebsite.iframe.id
|
||||
);
|
||||
|
||||
if (this.coWebsites.length < 1) {
|
||||
this.closeMain();
|
||||
}
|
||||
|
||||
if (coWebsite.position > 0) {
|
||||
const slot = this.getSlotByPosition(coWebsite.position);
|
||||
if (slot) {
|
||||
slot.container.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
const previousCoWebsite = this.coWebsites.find((coWebsiteToCheck: CoWebsite) =>
|
||||
coWebsite.position + 1 === coWebsiteToCheck.position
|
||||
);
|
||||
|
||||
if (previousCoWebsite) {
|
||||
this.moveLeftPreviousCoWebsite(previousCoWebsite, coWebsite.position);
|
||||
}
|
||||
|
||||
coWebsite.icon.remove();
|
||||
coWebsite.iframe.remove();
|
||||
}
|
||||
|
||||
public searchJitsi(): CoWebsite|undefined {
|
||||
return this.coWebsites.find((coWebsite : CoWebsite) =>
|
||||
coWebsite.iframe.id.toLowerCase().includes('jitsi')
|
||||
);
|
||||
}
|
||||
|
||||
private generateCoWebsiteIcon(iframe: HTMLIFrameElement): HTMLDivElement {
|
||||
const icon = document.createElement("div");
|
||||
icon.id = "cowebsite-icon-" + iframe.id;
|
||||
icon.style.display = "none";
|
||||
|
||||
const iconImage = document.createElement("img");
|
||||
iconImage.src = `http://www.google.com/s2/favicons?sz=64&domain_url=${iframe.src}`;
|
||||
const url = new URL(iframe.src);
|
||||
iconImage.alt = url.hostname;
|
||||
|
||||
icon.appendChild(iconImage);
|
||||
|
||||
return icon;
|
||||
}
|
||||
|
||||
public loadCoWebsite(
|
||||
@ -171,89 +433,183 @@ class CoWebsiteManager {
|
||||
base: string,
|
||||
allowApi?: boolean,
|
||||
allowPolicy?: string,
|
||||
widthPercent?: number
|
||||
): void {
|
||||
this.load();
|
||||
this.cowebsiteMainDom.innerHTML = ``;
|
||||
widthPercent?: number,
|
||||
position?: number
|
||||
): Promise<CoWebsite> {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (this.coWebsites.length < 1) {
|
||||
this.loadMain();
|
||||
} else if (this.coWebsites.length === 5) {
|
||||
return reject();
|
||||
}
|
||||
|
||||
const iframe = document.createElement("iframe");
|
||||
iframe.id = "cowebsite-iframe";
|
||||
iframe.src = new URL(url, base).toString();
|
||||
|
||||
iframe?.classList.add("pixel");
|
||||
|
||||
do {
|
||||
iframe.id = "cowebsite-iframe-" + (Math.random() + 1).toString(36).substring(7);
|
||||
} while (iframe.id.toLowerCase().includes('jitsi') || this.searchCoWebsiteById(iframe.id));
|
||||
|
||||
iframe.src = new URL(url, base).toString()
|
||||
|
||||
if (allowPolicy) {
|
||||
iframe.allow = allowPolicy;
|
||||
}
|
||||
|
||||
const onloadPromise = new Promise<void>((resolve) => {
|
||||
iframe.onload = () => resolve();
|
||||
});
|
||||
|
||||
if (allowApi) {
|
||||
iframeListener.registerIframe(iframe);
|
||||
}
|
||||
this.cowebsiteMainDom.appendChild(iframe);
|
||||
|
||||
const icon = this.generateCoWebsiteIcon(iframe);
|
||||
|
||||
const coWebsite = {
|
||||
iframe,
|
||||
icon,
|
||||
position: position ?? this.coWebsites.length,
|
||||
};
|
||||
|
||||
// Iframe management on mobile
|
||||
icon.addEventListener("click", () => {
|
||||
if (this.isSmallScreen()) {
|
||||
this.moveRightPreviousCoWebsite(coWebsite, 0);
|
||||
}
|
||||
});
|
||||
|
||||
this.coWebsites.push(coWebsite);
|
||||
this.cowebsiteSubIconsDom.appendChild(icon);
|
||||
this.cowebsiteBufferDom.appendChild(coWebsite.iframe);
|
||||
|
||||
const onTimeoutPromise = new Promise<void>((resolve) => {
|
||||
setTimeout(() => resolve(), 2000);
|
||||
});
|
||||
|
||||
this.currentOperationPromise = this.currentOperationPromise
|
||||
.then(() => Promise.race([onloadPromise, onTimeoutPromise]))
|
||||
.then(() => {
|
||||
this.open();
|
||||
if (coWebsite.position === 0) {
|
||||
this.openMain();
|
||||
if (widthPercent) {
|
||||
this.widthPercent = widthPercent;
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
this.fire();
|
||||
position ?
|
||||
this.moveRightPreviousCoWebsite(coWebsite, coWebsite.position) :
|
||||
this.moveCoWebsite(coWebsite, coWebsite.position);
|
||||
}, animationTime);
|
||||
} else {
|
||||
position ?
|
||||
this.moveRightPreviousCoWebsite(coWebsite, coWebsite.position) :
|
||||
this.moveCoWebsite(coWebsite, coWebsite.position);
|
||||
}
|
||||
|
||||
return resolve(coWebsite);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error("Error loadCoWebsite => ", err);
|
||||
this.closeCoWebsite();
|
||||
this.removeCoWebsiteFromStack(coWebsite);
|
||||
return reject();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Just like loadCoWebsite but the div can be filled by the user.
|
||||
*/
|
||||
public insertCoWebsite(callback: (cowebsite: HTMLDivElement) => Promise<void>, widthPercent?: number): void {
|
||||
this.load();
|
||||
this.cowebsiteMainDom.innerHTML = ``;
|
||||
public insertCoWebsite(callback: (jitsiBuffer: HTMLDivElement) => Promise<void>, widthPercent?: number): void {
|
||||
if (this.coWebsites.length < 1) {
|
||||
this.loadMain();
|
||||
} else if (this.coWebsites.length === 5) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.currentOperationPromise = this.currentOperationPromise
|
||||
.then(() => callback(this.cowebsiteMainDom))
|
||||
.then(() => callback(this.cowebsiteBufferDom))
|
||||
.then(() => {
|
||||
this.open();
|
||||
const iframe = this.cowebsiteBufferDom.querySelector<HTMLIFrameElement>('[id*="jitsi" i]');
|
||||
iframe?.classList.add("pixel");
|
||||
|
||||
if (!iframe) {
|
||||
console.error("Error insertCoWebsite => Cannot find Iframe Element on Jitsi DOM");
|
||||
return;
|
||||
}
|
||||
|
||||
const icon = this.generateCoWebsiteIcon(iframe);
|
||||
|
||||
const coWebsite = {
|
||||
iframe,
|
||||
icon,
|
||||
position: 0,
|
||||
};
|
||||
|
||||
this.coWebsites.push(coWebsite);
|
||||
this.cowebsiteSubIconsDom.appendChild(icon);
|
||||
|
||||
if (coWebsite.position === 0) {
|
||||
this.openMain();
|
||||
|
||||
if (widthPercent) {
|
||||
this.widthPercent = widthPercent;
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
this.fire();
|
||||
}, animationTime);
|
||||
}
|
||||
|
||||
this.moveRightPreviousCoWebsite(coWebsite, coWebsite.position);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error("Error insertCoWebsite => ", err);
|
||||
this.closeCoWebsite();
|
||||
});
|
||||
}
|
||||
|
||||
public closeCoWebsite(): Promise<void> {
|
||||
public closeCoWebsite(coWebsite: CoWebsite): Promise<void> {
|
||||
this.currentOperationPromise = this.currentOperationPromise.then(
|
||||
() =>
|
||||
new Promise((resolve, reject) => {
|
||||
if (this.opened === iframeStates.closed) resolve(); //this method may be called twice, in case of iframe error for example
|
||||
this.close();
|
||||
new Promise((resolve) => {
|
||||
if (this.coWebsites.length === 1) {
|
||||
if (this.openedMain === iframeStates.closed) resolve(); //this method may be called twice, in case of iframe error for example
|
||||
this.closeMain();
|
||||
this.fire();
|
||||
const iframe = this.cowebsiteDiv.querySelector("iframe");
|
||||
if (iframe) {
|
||||
iframeListener.unregisterIframe(iframe);
|
||||
}
|
||||
setTimeout(() => {
|
||||
this.cowebsiteMainDom.innerHTML = ``;
|
||||
|
||||
if (coWebsite) {
|
||||
iframeListener.unregisterIframe(coWebsite.iframe);
|
||||
}
|
||||
|
||||
this.removeCoWebsiteFromStack(coWebsite);
|
||||
resolve();
|
||||
}, animationTime);
|
||||
})
|
||||
);
|
||||
return this.currentOperationPromise;
|
||||
}
|
||||
|
||||
public closeJitsi() {
|
||||
const jitsi = this.searchJitsi();
|
||||
if (jitsi) {
|
||||
this.closeCoWebsite(jitsi);
|
||||
}
|
||||
}
|
||||
|
||||
public closeCoWebsites(): Promise<void> {
|
||||
this.currentOperationPromise = this.currentOperationPromise
|
||||
.then(() => {
|
||||
this.coWebsites.forEach((coWebsite: CoWebsite) => {
|
||||
this.closeCoWebsite(coWebsite);
|
||||
});
|
||||
});
|
||||
return this.currentOperationPromise;
|
||||
}
|
||||
|
||||
public getGameSize(): { width: number; height: number } {
|
||||
if (this.opened !== iframeStates.opened) {
|
||||
if (this.openedMain !== iframeStates.opened) {
|
||||
return {
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight,
|
||||
@ -279,7 +635,7 @@ class CoWebsiteManager {
|
||||
|
||||
private fullscreen(): void {
|
||||
if (this.isFullScreen) {
|
||||
this.resetStyle();
|
||||
this.resetStyleMain();
|
||||
this.fire();
|
||||
//we don't trigger a resize of the phaser game since it won't be visible anyway.
|
||||
HtmlUtils.getElementByIdOrFail(cowebsiteOpenFullScreenImageId).style.display = "inline";
|
||||
|
@ -181,11 +181,17 @@ class JitsiFactory {
|
||||
}, jitsiWidth);
|
||||
}
|
||||
|
||||
public async stop(): Promise<void> {
|
||||
public stop() {
|
||||
if (!this.jitsiApi) {
|
||||
return;
|
||||
}
|
||||
await coWebsiteManager.closeCoWebsite();
|
||||
|
||||
const jitsiCoWebsite = coWebsiteManager.searchJitsi();
|
||||
|
||||
if (jitsiCoWebsite) {
|
||||
coWebsiteManager.closeJitsi();
|
||||
}
|
||||
|
||||
this.jitsiApi.removeListener("audioMuteStatusChanged", this.audioCallback);
|
||||
this.jitsiApi.removeListener("videoMuteStatusChanged", this.videoCallback);
|
||||
this.jitsiApi?.dispose();
|
||||
|
@ -141,7 +141,7 @@ const game = new Game(config);
|
||||
waScaleManager.setGame(game);
|
||||
|
||||
window.addEventListener("resize", function (event) {
|
||||
coWebsiteManager.resetStyle();
|
||||
coWebsiteManager.resetStyleMain();
|
||||
|
||||
waScaleManager.applyNewSize();
|
||||
});
|
||||
|
@ -25,26 +25,73 @@
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
&-container {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
aside {
|
||||
height: 30px;
|
||||
min-height: 30px;
|
||||
cursor: ns-resize;
|
||||
flex-direction: column;
|
||||
flex-direction: row-reverse;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
#cowebsite-aside-holder {
|
||||
pointer-events: none;
|
||||
height: 100%;
|
||||
|
||||
img {
|
||||
height: 80%;
|
||||
}
|
||||
}
|
||||
|
||||
#cowebsite-sub-icons {
|
||||
display: inline-flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-top: 0;
|
||||
height: 100%;
|
||||
visibility: visible;
|
||||
|
||||
img {
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
cursor: pointer !important;
|
||||
border-radius: 50%;
|
||||
background-color: whitesmoke;
|
||||
}
|
||||
|
||||
&>div {
|
||||
display: flex;
|
||||
margin-left: 2px;
|
||||
margin-right: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
#cowebsite-aside-buttons {
|
||||
flex-direction: row-reverse;
|
||||
margin-left: auto;
|
||||
margin-bottom: 0;
|
||||
justify-content: end;
|
||||
}
|
||||
|
||||
img {
|
||||
cursor: ns-resize;
|
||||
}
|
||||
|
||||
#cowebsite-fullscreen {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.top-right-btn {
|
||||
&#cowebsite-close {
|
||||
right: 0;
|
||||
}
|
||||
&#cowebsite-fullscreen {
|
||||
left: 0;
|
||||
img {
|
||||
width: 15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,8 +2,10 @@
|
||||
|
||||
#cowebsite {
|
||||
position: fixed;
|
||||
z-index: 200;
|
||||
transition: transform 0.5s;
|
||||
background-color: white;
|
||||
display: none;
|
||||
|
||||
&.loading {
|
||||
background-color: gray;
|
||||
@ -22,30 +24,159 @@
|
||||
background: gray;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
|
||||
img {
|
||||
margin: 3px;
|
||||
#cowebsite-aside-holder {
|
||||
pointer-events: none;
|
||||
height: 20px;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
|
||||
img {
|
||||
width: 80%;
|
||||
}
|
||||
}
|
||||
|
||||
#cowebsite-aside-buttons {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-bottom: auto;
|
||||
flex: 1;
|
||||
justify-content: start;
|
||||
}
|
||||
|
||||
.top-right-btn{
|
||||
position: absolute;
|
||||
background: none;
|
||||
border: none;
|
||||
transform: scale(0.5);
|
||||
cursor: url('./images/cursor_pointer.png'), pointer;
|
||||
|
||||
img {
|
||||
height: 25px;
|
||||
padding: 4px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
img:hover {
|
||||
background-color: rgba(0,0,0,0.4);
|
||||
#cowebsite-sub-icons {
|
||||
display: flex;
|
||||
margin-top: auto;
|
||||
visibility: hidden;
|
||||
justify-content: end;
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
&-container {
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
&-main {
|
||||
padding: 2% 5%;
|
||||
height: 50%;
|
||||
}
|
||||
|
||||
&-sub {
|
||||
position: absolute !important;
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
bottom: 23%;
|
||||
height: 20% !important;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
&-slot-0 {
|
||||
z-index: 70 !important;
|
||||
background-color: whitesmoke;
|
||||
}
|
||||
|
||||
@for $i from 1 through 4 {
|
||||
&-slot-#{$i} {
|
||||
transition: transform 0.5s;
|
||||
position: relative;
|
||||
height: 100%;
|
||||
display: none;
|
||||
background-color: #333333;
|
||||
|
||||
@if $i == 1 {
|
||||
width: 100%;
|
||||
} @else {
|
||||
width: 33%;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.overlay {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 50;
|
||||
position: absolute;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.actions-move {
|
||||
display: none;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
gap: 10%;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: rgba($color: #333333, $alpha: 0.6);
|
||||
|
||||
.actions-move {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.actions {
|
||||
pointer-events: all !important;
|
||||
margin: 3% 2%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: end;
|
||||
position: relative;
|
||||
z-index: 50;
|
||||
|
||||
button {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
margin: 8px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-buffer {
|
||||
iframe {
|
||||
z-index: 45 !important;
|
||||
pointer-events: none !important;
|
||||
overflow: hidden;
|
||||
border: 0;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.main {
|
||||
pointer-events: all !important;
|
||||
z-index: 205 !important;
|
||||
}
|
||||
|
||||
.sub-main {
|
||||
pointer-events: all !important;
|
||||
}
|
||||
}
|
||||
|
||||
.pixel {
|
||||
visibility: hidden;
|
||||
height: 1px;
|
||||
width: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-aspect-ratio: 1/1) {
|
||||
@ -54,7 +185,7 @@
|
||||
top: 0;
|
||||
width: 50%;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
display: none;
|
||||
|
||||
&.loading {
|
||||
transform: translateX(90%);
|
||||
@ -77,15 +208,5 @@
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
}
|
||||
|
||||
.top-right-btn{
|
||||
left: -6px;
|
||||
&#cowebsite-close {
|
||||
top: 0px;
|
||||
}
|
||||
&#cowebsite-fullscreen {
|
||||
top: 25px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1111,3 +1111,18 @@ div.emoji-picker {
|
||||
border-radius: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
.noselect {
|
||||
-webkit-touch-callout: none; /* iOS Safari */
|
||||
-webkit-user-select: none; /* Safari */
|
||||
-khtml-user-select: none; /* Konqueror HTML */
|
||||
-moz-user-select: none; /* Old versions of Firefox */
|
||||
-ms-user-select: none; /* Internet Explorer/Edge */
|
||||
user-select: none; /* Non-prefixed version, currently
|
||||
supported by Chrome, Edge, Opera and Firefox */
|
||||
}
|
||||
|
||||
.pixel {
|
||||
height: 1px !important;
|
||||
width: 1px !important;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user