mirror of
https://github.com/pierre42100/ComunicWeb
synced 2025-07-13 14:38:09 +00:00
Compare commits
53 Commits
personnal-
...
25-02-2019
Author | SHA1 | Date | |
---|---|---|---|
650c761c51 | |||
2e07945994 | |||
d9fe86d160 | |||
bbaaf3a5c2 | |||
0f4d3042c3 | |||
be0306b309 | |||
c819aaf716 | |||
b6e818dc00 | |||
a3f8e2f243 | |||
9f3311978d | |||
817ef1069c | |||
faba9b36cb | |||
5276790afe | |||
8d613d4e57 | |||
5a067001e9 | |||
1a3117a603 | |||
cbfe141c32 | |||
35c4597017 | |||
289a66e55b | |||
8c6e04abd0 | |||
79dfa0511b | |||
982c40788c | |||
a46a7154ea | |||
fe2a6c2dfe | |||
c1053b8041 | |||
194b6c60de | |||
0b806d5bb2 | |||
b8865b96f0 | |||
b62fefc258 | |||
590e1d6794 | |||
ece7a97d42 | |||
99fffb839f | |||
154f1b98ef | |||
45a4f552e8 | |||
ca39a08d07 | |||
5eb1c8b274 | |||
29b5499b85 | |||
b08255cd18 | |||
be03249f11 | |||
0a52bd5fe3 | |||
9eab6c7e2e | |||
118cfeee41 | |||
f79ef55e3b | |||
5640580397 | |||
e90c20c262 | |||
cd4e6ddcb1 | |||
feb17e3f13 | |||
e6f4159e53 | |||
2d88a71b80 | |||
6c090b4967 | |||
f67670dc0a | |||
a03fc1a745 | |||
32c484b41a |
2
LICENSE
2
LICENSE
@ -1,5 +1,5 @@
|
||||
MIT License
|
||||
Copyright (c) 2017-2018 Pierre HUBERT
|
||||
Copyright (c) 2017-2019 Pierre HUBERT
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
|
@ -29,3 +29,4 @@ ComunicWeb would not exists without the following technologies developped by the
|
||||
- SCEditor (BBC WYIWYG editor) (https://github.com/samclarke/SCEditor) (MIT License)
|
||||
- JavaScript BBCode Parser (https://github.com/Frug/js-bbcode-parser) (MIT License)
|
||||
- Pacman (https://github.com/daleharvey/pacman) (WTFPL License)
|
||||
- SimplePeer (https://github.com/feross/simple-peer) (MIT License)
|
262
assets/3rdparty/SignalExchangerClient/SignalExchangerClient.js
vendored
Normal file
262
assets/3rdparty/SignalExchangerClient/SignalExchangerClient.js
vendored
Normal file
@ -0,0 +1,262 @@
|
||||
/**
|
||||
* Signal exchanger web client
|
||||
*
|
||||
* @author Pierre HUBERT
|
||||
*/
|
||||
|
||||
class SignalExchangerClient {
|
||||
|
||||
/**
|
||||
* Server domain
|
||||
*
|
||||
* @type {String}
|
||||
*/
|
||||
//domain;
|
||||
|
||||
/**
|
||||
* Server port
|
||||
*
|
||||
* @type {Number}
|
||||
*/
|
||||
//port;
|
||||
|
||||
/**
|
||||
* Current client ID
|
||||
*
|
||||
* @type {String}
|
||||
*/
|
||||
//clientID;
|
||||
|
||||
/**
|
||||
* Socket connection to the server
|
||||
*
|
||||
* @type {WebSocket}
|
||||
*/
|
||||
//socket;
|
||||
|
||||
/**
|
||||
* Function called in case of error
|
||||
*
|
||||
* @type {Function}
|
||||
*/
|
||||
//onError = null;
|
||||
|
||||
/**
|
||||
* Function called when the connection is etablished
|
||||
*
|
||||
* @type {Function}
|
||||
*/
|
||||
//onConnected = null;
|
||||
|
||||
/**
|
||||
* Function called when the connection to the socket is closed
|
||||
*
|
||||
* @type {Function}
|
||||
*/
|
||||
//onClosed = null;
|
||||
|
||||
/**
|
||||
* Function called when we get a new signal information
|
||||
*
|
||||
* @type {Function}
|
||||
*/
|
||||
//onSignal = null;
|
||||
|
||||
/**
|
||||
* Function called when we get a ready message notice
|
||||
*
|
||||
* @type {Function}
|
||||
*/
|
||||
//onReadyMessage = null;
|
||||
|
||||
/**
|
||||
* Construct a client instance
|
||||
*
|
||||
* @param {String} domain The name of the signal server
|
||||
* @param {Number} port The port of the server to use
|
||||
* @param {String} clientID The ID of current client
|
||||
* @param {Boolean} secure Specify whether connection to the socket should be secure or not
|
||||
*/
|
||||
constructor(domain, port, clientID, secure) {
|
||||
|
||||
//Save information
|
||||
this.domain = domain,
|
||||
this.port = port;
|
||||
this.clientID = clientID;
|
||||
|
||||
this.socket = new WebSocket((secure ? "wss" : "ws") + "://" + this.domain + ":" + this.port + "/socket");
|
||||
|
||||
//Add a few events listeners
|
||||
this.socket.addEventListener("open", () => {
|
||||
this.serverConnected();
|
||||
|
||||
if(this.onConnected != null)
|
||||
setTimeout(this.onConnected, 10);
|
||||
});
|
||||
|
||||
this.socket.addEventListener("message", message => {
|
||||
|
||||
let data;
|
||||
try {
|
||||
data = JSON.parse(message.data);
|
||||
} catch(e){
|
||||
console.error("Could not parse message from server!");
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("New message from socket", data);
|
||||
|
||||
this.serverMessage(data);
|
||||
});
|
||||
|
||||
this.socket.addEventListener("error", () => {
|
||||
if(this.onError != null)
|
||||
setTimeout(this.onError, 0);
|
||||
});
|
||||
|
||||
this.socket.addEventListener("close", () => {
|
||||
if(this.onClosed != null)
|
||||
setTimeout(this.onClosed, 0);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this method to get the current connection status to the server
|
||||
*
|
||||
* @return {Boolean} TRUE if the client is connected to the server / FALSE else
|
||||
*/
|
||||
isConnected() {
|
||||
return this.socket.readyState == WebSocket.OPEN;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the connection to the server (if connected)
|
||||
*/
|
||||
close() {
|
||||
if(this.isConnected())
|
||||
this.socket.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Method called once the client is successfully
|
||||
* connected to the client
|
||||
*/
|
||||
serverConnected(){
|
||||
|
||||
//Send data to the server to identificate client
|
||||
this.sendData({
|
||||
client_id: this.clientID
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Send ready message to a peer
|
||||
*
|
||||
* @param {String} peerID The ID of the target peer for the message
|
||||
*/
|
||||
sendReadyMessage(peerID){
|
||||
|
||||
this.sendData({
|
||||
ready_msg: true,
|
||||
target_id: peerID
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a signal to the server
|
||||
*
|
||||
* @param target_id The ID of the target for the signal
|
||||
* @param content Signal to send to the target
|
||||
*/
|
||||
sendSignal(target_id, content){
|
||||
|
||||
//Send directly the message to the server
|
||||
this.sendData({
|
||||
signal: content,
|
||||
target_id: target_id
|
||||
});
|
||||
|
||||
//Save the current signal being sent to be able to send
|
||||
//it again in case of failure
|
||||
this.pending_signal = content;
|
||||
this.pending_signal_target = target_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop to try to send the current signal message in queue
|
||||
*
|
||||
* This does not cancel the sending of messages already sent through
|
||||
* socket
|
||||
*/
|
||||
cancelCurrentSignal() {
|
||||
this.pending_signal = undefined;
|
||||
this.pending_signal_target = undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send data to the server
|
||||
*
|
||||
* @param {Object} data The data to send to the server
|
||||
*/
|
||||
sendData(data){
|
||||
console.log("Sending data to server", data);
|
||||
this.socket.send(JSON.stringify(data));
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called when the server has sent a new message to this client
|
||||
*
|
||||
* @param {Object} message The message sent by the server, as a JSON object
|
||||
*/
|
||||
serverMessage(message){
|
||||
|
||||
//Check if it is a callback for a pending message
|
||||
if(message.signal_sent){
|
||||
if(message.number_of_targets < 1 && this.pending_signal && this.pending_signal_target){
|
||||
|
||||
//We have to send the message again
|
||||
setTimeout(() => {
|
||||
this.sendSignal(this.pending_signal, this.pending_signal_target);
|
||||
}, 1000);
|
||||
|
||||
}
|
||||
|
||||
else {
|
||||
|
||||
//Else we can remove from this class information about the signal being sent
|
||||
this.cancelCurrentSignal();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//Check if message is a callback for a ready notice
|
||||
else if(message.ready_message_sent){
|
||||
|
||||
if(message.number_of_targets < 1){
|
||||
|
||||
//Try to send message again
|
||||
setTimeout(() => {
|
||||
this.sendReadyMessage(message.target_id);
|
||||
}, 1000);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Check if message is a ready notice
|
||||
else if(message.ready_msg){
|
||||
if(this.onReadyMessage != null)
|
||||
this.onReadyMessage(message.source_id);
|
||||
}
|
||||
|
||||
// Check if the message is a signal
|
||||
else if(message.signal){
|
||||
if(this.onSignal != null)
|
||||
this.onSignal(message.signal, message.source_id);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
11
assets/3rdparty/simplepeer/simplepeer.min.js
vendored
Normal file
11
assets/3rdparty/simplepeer/simplepeer.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
BIN
assets/audio/phone_ring.mp3
Normal file
BIN
assets/audio/phone_ring.mp3
Normal file
Binary file not shown.
BIN
assets/audio/phone_ring.ogg
Normal file
BIN
assets/audio/phone_ring.ogg
Normal file
Binary file not shown.
@ -25,6 +25,10 @@ a {
|
||||
color: #72afd2;
|
||||
}
|
||||
|
||||
.cursor-pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sceditor iframe
|
||||
*/
|
||||
|
158
assets/css/components/calls/callWindow.css
Normal file
158
assets/css/components/calls/callWindow.css
Normal file
@ -0,0 +1,158 @@
|
||||
/**
|
||||
* Calls window stylesheet
|
||||
*
|
||||
* @author Pierre HUBERT
|
||||
*/
|
||||
|
||||
#callsTarget {
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0px;
|
||||
visibility: hidden;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
|
||||
.call-window {
|
||||
width: 300px;
|
||||
min-width: 300px;
|
||||
min-height: 174px;
|
||||
position: absolute;
|
||||
top: 100px;
|
||||
right: 10px;
|
||||
z-index: 100;
|
||||
border: 1px black solid;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: #000000b3;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.call-window .call-window-body{
|
||||
flex: 1;
|
||||
display: flex;
|
||||
max-height: calc(100% - 62px);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Toolbar
|
||||
*/
|
||||
.call-toolbar {
|
||||
height: 20px;
|
||||
background-color: #000000b3;
|
||||
color: white;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
.call-toolbar > i:first-child {
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
.call-title {
|
||||
flex: 1;
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Loading message
|
||||
*/
|
||||
.loading-message-container {
|
||||
position: absolute;
|
||||
min-height: 112px;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
background: 1px #0009;
|
||||
flex-direction: column;
|
||||
justify-content: space-around;
|
||||
text-align: center;
|
||||
font-size: 150%;
|
||||
color: white;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Stream target
|
||||
*/
|
||||
.call-window .streams-target {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
flex-direction: row;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.call-window .streams-target video {
|
||||
flex: 1;
|
||||
flex-shrink: 1;
|
||||
max-height: 100%;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Footer
|
||||
*/
|
||||
.call-window .call-footer {
|
||||
height: 40px;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.call-window .call-footer .call-option-button {
|
||||
color: #fff6;
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.call-window .call-footer .call-option-button.selected {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.call-window .call-footer .call-option-button:hover {
|
||||
background-color: #ffffff4d;
|
||||
}
|
||||
|
||||
.call-window .call-footer .call-option-button:active {
|
||||
background-color: #fff3;
|
||||
}
|
||||
|
||||
.call-window .call-footer .call-option-button.hang-up-button {
|
||||
color: #dd4b39;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Responsive mode
|
||||
*/
|
||||
@media screen and (max-width: 730px) {
|
||||
|
||||
#callsTarget {
|
||||
z-index: 1030;
|
||||
}
|
||||
|
||||
.streams-target {
|
||||
flex-direction: column !important;
|
||||
}
|
||||
|
||||
.streams-target video {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.call-window {
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0px !important;
|
||||
left: 0px !important;
|
||||
}
|
||||
|
||||
}
|
33
assets/css/components/calls/ringScreen.css
Normal file
33
assets/css/components/calls/ringScreen.css
Normal file
@ -0,0 +1,33 @@
|
||||
/**
|
||||
* Ring screen stylesheet
|
||||
*
|
||||
* @author Pierre HUBERT
|
||||
*/
|
||||
|
||||
.ring-call-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: fixed;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
background-color: #011932e6;
|
||||
z-index: 1030;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.call-message-box {
|
||||
background-color: #fff6;
|
||||
color: white;
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
min-width: 200px;
|
||||
text-align: center;
|
||||
font-size: 150%;
|
||||
}
|
||||
|
||||
.ring-call-container .response-buttons {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
}
|
@ -4,6 +4,10 @@
|
||||
* @author Pierre HUBERT
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Main definition
|
||||
*/
|
||||
#conversationsElem .box {
|
||||
width: 250px;
|
||||
height: 350px;
|
||||
@ -15,6 +19,10 @@
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
#conversationsElem .chat-window .box-title {
|
||||
font-size: 15px !important;
|
||||
}
|
||||
|
||||
/**
|
||||
* Conversations create message form
|
||||
*/
|
||||
|
@ -63,6 +63,20 @@
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.post .post-youtube.post-youtube-placeholder {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
background-color: #3c8dbc;
|
||||
color: white;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.post .post-youtube.post-youtube-placeholder .title {
|
||||
font-size: 150%;
|
||||
}
|
||||
|
||||
.post .post-youtube {
|
||||
width: 100%;
|
||||
height: 300px;
|
||||
|
@ -58,6 +58,14 @@ p, h1, h2, h3, h4, h5, h6 {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Callouts
|
||||
*/
|
||||
.callout.callout-info {
|
||||
background-color: var(--black-5) !important;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Forms
|
||||
*/
|
||||
@ -346,11 +354,16 @@ div.sceditor-dropdown input {
|
||||
color: var(--black5);
|
||||
}
|
||||
|
||||
.select2-dropdown {
|
||||
background-color: var(--black5);
|
||||
.select2-dropdown, .select2-selection,
|
||||
.select2-search__field {
|
||||
background-color: var(--black5) !important;
|
||||
color: var(--white);
|
||||
}
|
||||
|
||||
.select2-selection__rendered {
|
||||
color: var(--white) !important;
|
||||
}
|
||||
|
||||
.select2-container--default .select2-results__option--highlighted[aria-selected] {
|
||||
background-color: var(--black4);
|
||||
color: var(--white);
|
||||
@ -437,6 +450,10 @@ div.sceditor-dropdown input {
|
||||
filter: brightness(100%);
|
||||
}
|
||||
|
||||
.post-youtube-placeholder {
|
||||
background-color: var(--black6) !important;
|
||||
}
|
||||
|
||||
.box-comments .username,
|
||||
.box-comments .comment-content {
|
||||
color: var(--white);
|
||||
@ -557,3 +574,53 @@ img[src$="groups_logo/default.png"] {
|
||||
.friends-list-ro .friend a:hover .friends-name {
|
||||
color: var(--black4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Account created page
|
||||
*/
|
||||
.page_account_created .message_container .message {
|
||||
background-color: var(--black5);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call system
|
||||
*/
|
||||
.call-window {
|
||||
border: 1px var(--black4) solid;
|
||||
}
|
||||
|
||||
.ring-call-container {
|
||||
background-color: var(--black5);
|
||||
}
|
||||
|
||||
.call-message-box {
|
||||
background-color: var(--black6);
|
||||
}
|
||||
|
||||
/**
|
||||
* Groups list page
|
||||
*/
|
||||
.groups-main-page {
|
||||
color: var(--white);
|
||||
}
|
||||
|
||||
/**
|
||||
* Groups members page
|
||||
*/
|
||||
.group-members-page {
|
||||
color: var(--white);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pick a movie modal
|
||||
*/
|
||||
.pick-movie-modal {
|
||||
color: var(--white);
|
||||
}
|
||||
|
||||
/**
|
||||
* Group settings page
|
||||
*/
|
||||
.group-settings-container label {
|
||||
color: var(--white);
|
||||
}
|
35
assets/custom_ts/Utils.d.ts
vendored
Normal file
35
assets/custom_ts/Utils.d.ts
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
/**
|
||||
* Typescript typing rules for Utils
|
||||
*
|
||||
* @author Pierre HUBERT
|
||||
*/
|
||||
|
||||
declare interface CreateElem2Args {
|
||||
type : string,
|
||||
appendTo ?: HTMLElement,
|
||||
insertBefore ?: HTMLElement,
|
||||
insertAsFirstChild ?: HTMLElement,
|
||||
class ?: string,
|
||||
id ?: string,
|
||||
title ?: string,
|
||||
src ?: string,
|
||||
href ?: string,
|
||||
name ?: string,
|
||||
elemType ?: string,
|
||||
value ?: string,
|
||||
placeholder ?: string,
|
||||
innerHTML ?: string,
|
||||
innerLang ?: string,
|
||||
innerHTMLprefix ?: string,
|
||||
disabled ?: boolean,
|
||||
}
|
||||
|
||||
declare function createElem(nodeType : string, appendTo : string) : HTMLElement;
|
||||
|
||||
declare function createElem2(infos : CreateElem2Args) : HTMLElement;
|
||||
|
||||
declare function byId(id : string) : HTMLElement;
|
||||
|
||||
declare function emptyElem(target : HTMLElement) : void;
|
||||
|
||||
declare function checkMail(emailAddress : string) : boolean;
|
@ -21,6 +21,11 @@ ComunicWeb.common.date = {
|
||||
* @return {String} The generated date
|
||||
*/
|
||||
diffToStr: function(difference){
|
||||
|
||||
//Check if difference is less than one second
|
||||
if(difference < 0)
|
||||
difference = 0;
|
||||
|
||||
//Calculate seconds
|
||||
var seconds = difference-Math.floor(difference/60)*60;
|
||||
var difference = (difference - seconds)/60;
|
||||
|
@ -1144,6 +1144,66 @@ var ComunicWeb = {
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Calls component
|
||||
*/
|
||||
calls: {
|
||||
|
||||
/**
|
||||
* Calls configuration
|
||||
*/
|
||||
__config: undefined,
|
||||
|
||||
/**
|
||||
* Calls interface
|
||||
*/
|
||||
interface: {
|
||||
//TODO : implement
|
||||
},
|
||||
|
||||
/**
|
||||
* Calls controller
|
||||
*/
|
||||
controller: {
|
||||
//TODO : implement
|
||||
},
|
||||
|
||||
/**
|
||||
* Call window
|
||||
*/
|
||||
callWindow: {
|
||||
//TODO : implement
|
||||
},
|
||||
|
||||
/**
|
||||
* Current calls list
|
||||
*/
|
||||
currentList: {
|
||||
//TODO : implement
|
||||
},
|
||||
|
||||
/**
|
||||
* User media getter
|
||||
*/
|
||||
userMedia: {
|
||||
//TODO : implement
|
||||
},
|
||||
|
||||
/**
|
||||
* Ring screen
|
||||
*/
|
||||
ringScreen: {
|
||||
//TODO : implement
|
||||
},
|
||||
|
||||
/**
|
||||
* Calls utilities
|
||||
*/
|
||||
utils: {
|
||||
//TODO : implement
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* Easter egg : pacman
|
||||
*/
|
||||
|
51
assets/js/common/songPlayer.js
Normal file
51
assets/js/common/songPlayer.js
Normal file
@ -0,0 +1,51 @@
|
||||
/**
|
||||
* Song player
|
||||
*
|
||||
* @author Pierre HUBERT
|
||||
*/
|
||||
|
||||
class SongPlayer {
|
||||
|
||||
/**
|
||||
* Initialize a new SongPlayer instance
|
||||
*
|
||||
* @param {String[]} sources The list of sources to exploit for the song
|
||||
*/
|
||||
constructor(sources){
|
||||
|
||||
this.songElem = document.createElement("audio");
|
||||
|
||||
//Process the list of sources
|
||||
for (var index = 0; index < sources.length; index++) {
|
||||
var url = sources[index];
|
||||
|
||||
var source = document.createElement("source");
|
||||
source.src = url;
|
||||
this.songElem.appendChild(source);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Play audio just once
|
||||
*/
|
||||
playOnce(){
|
||||
this.songElem.loop = false;
|
||||
this.songElem.play();
|
||||
}
|
||||
|
||||
/**
|
||||
* Play song forever
|
||||
*/
|
||||
playForever(){
|
||||
this.songElem.loop = true;
|
||||
this.songElem.play();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop song
|
||||
*/
|
||||
stop(){
|
||||
this.songElem.pause();
|
||||
this.songElem.currentTime = 0;
|
||||
}
|
||||
}
|
@ -58,6 +58,11 @@ ComunicWeb.common.system = {
|
||||
*/
|
||||
ComunicWeb.components.darkTheme.refresh();
|
||||
|
||||
/**
|
||||
* Initialize call system
|
||||
*/
|
||||
ComunicWeb.components.calls.controller.init();
|
||||
|
||||
/**
|
||||
* What to do after login refresh
|
||||
*/
|
||||
|
@ -421,7 +421,7 @@ function checkString(value){
|
||||
function removeHtmlTags(input){
|
||||
|
||||
//Check if input string is empty
|
||||
if(input == null)
|
||||
if(input == null || typeof input !== "string")
|
||||
return "";
|
||||
|
||||
//Prepare update
|
||||
@ -701,3 +701,56 @@ function SendEvent(name, details){
|
||||
document.dispatchEvent(event);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Request full screen for an HTML element
|
||||
*
|
||||
* Note : this function must be called inside of an event of
|
||||
* the user like a click, otherwise it will not work
|
||||
*
|
||||
* This function source code is based on this StackOverFlow Answer
|
||||
* https://stackoverflow.com/a/32100295/3781411
|
||||
*
|
||||
* @param {HTMLElement} elem The element for which we want
|
||||
* full screen
|
||||
*/
|
||||
function RequestFullScreen(element){
|
||||
if (
|
||||
document.fullscreenElement ||
|
||||
document.webkitFullscreenElement ||
|
||||
document.mozFullScreenElement ||
|
||||
document.msFullscreenElement
|
||||
) {
|
||||
if (document.exitFullscreen) {
|
||||
document.exitFullscreen();
|
||||
} else if (document.mozCancelFullScreen) {
|
||||
document.mozCancelFullScreen();
|
||||
} else if (document.webkitExitFullscreen) {
|
||||
document.webkitExitFullscreen();
|
||||
} else if (document.msExitFullscreen) {
|
||||
document.msExitFullscreen();
|
||||
}
|
||||
} else {
|
||||
if (element.requestFullscreen) {
|
||||
element.requestFullscreen();
|
||||
} else if (element.mozRequestFullScreen) {
|
||||
element.mozRequestFullScreen();
|
||||
} else if (element.webkitRequestFullscreen) {
|
||||
element.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT);
|
||||
} else if (element.msRequestFullscreen) {
|
||||
element.msRequestFullscreen();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check out whether Comunic has grabbed full screen or not
|
||||
*
|
||||
* @return {Boolean} TRUE if the window is in full screen / FALSE else
|
||||
*/
|
||||
function IsFullScreen(){
|
||||
return document.fullscreenElement ||
|
||||
document.webkitFullscreenElement ||
|
||||
document.mozFullScreenElement ||
|
||||
document.msFullscreenElement;
|
||||
}
|
@ -25,18 +25,51 @@ ComunicWeb.components.account.export.worker = {
|
||||
ComunicWeb.components.account.export.ui.updateMessage("Got text data");
|
||||
ComunicWeb.components.account.export.ui.updateProgress(10);
|
||||
|
||||
//Parse data
|
||||
ComunicWeb.components.account.export.worker.parse(result);
|
||||
//Get explorer
|
||||
ComunicWeb.components.account.export.worker.getExplorer(result);
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Second step for export : Get and open personnal data explorer
|
||||
*
|
||||
* @param {Object} data Text data about the account (data not modified at this stage)
|
||||
*/
|
||||
getExplorer: function(data){
|
||||
|
||||
ComunicWeb.components.account.export.ui.updateMessage("Getting data explorer");
|
||||
ComunicWeb.components.account.export.ui.updateProgress(15);
|
||||
|
||||
JSZipUtils.getBinaryContent(ComunicWeb.__config.assetsURL+"zip/personnal-data-export-navigator.zip", function(err, file){
|
||||
|
||||
if(err != null){
|
||||
ComunicWeb.debug.logMessage("Could not get personnal data export navigator!");
|
||||
ComunicWeb.components.account.export.ui.exportFatalError(e);
|
||||
return;
|
||||
}
|
||||
|
||||
JSZip.loadAsync(file).then(function(zip){
|
||||
|
||||
//Ready to parse data
|
||||
ComunicWeb.components.account.export.worker.parse(data, zip);
|
||||
|
||||
}).catch(function(){
|
||||
ComunicWeb.debug.logMessage("Could not parse personnal data export navigator!");
|
||||
ComunicWeb.components.account.export.ui.exportFatalError(e);
|
||||
return;
|
||||
});
|
||||
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Parse account text data into ZIP file
|
||||
*
|
||||
* @param {Object} data Text data about the account
|
||||
* @param {ZIP} zip The ZIP object to fill
|
||||
*/
|
||||
parse: function(data){
|
||||
parse: function(data, zip){
|
||||
|
||||
//Get UI shorcut
|
||||
var ui = ComunicWeb.components.account.export.ui;
|
||||
@ -77,9 +110,6 @@ ComunicWeb.components.account.export.worker = {
|
||||
//Determine the list of files to download
|
||||
var files_list = this._generate_files_list(data);
|
||||
|
||||
//Create zip file
|
||||
var zip = new JSZip();
|
||||
|
||||
//Add raw json file
|
||||
zip.file("source.json", JSON.stringify(data));
|
||||
|
||||
|
838
assets/js/components/calls/callWindow.js
Normal file
838
assets/js/components/calls/callWindow.js
Normal file
@ -0,0 +1,838 @@
|
||||
/**
|
||||
* Single call window management
|
||||
*
|
||||
* @author Pierre HUBERT
|
||||
*/
|
||||
|
||||
ComunicWeb.components.calls.callWindow = {
|
||||
|
||||
/**
|
||||
* Initialize a call
|
||||
*
|
||||
* @param {Object} info Information about the call to initialize
|
||||
*/
|
||||
initCall: function(info){
|
||||
|
||||
/**
|
||||
* Initialize call object
|
||||
*/
|
||||
var call = {
|
||||
info: info,
|
||||
|
||||
/**
|
||||
* @type {String}
|
||||
*/
|
||||
localPeerID: undefined,
|
||||
|
||||
/**
|
||||
* @type {Boolean}
|
||||
*/
|
||||
open: true,
|
||||
|
||||
/**
|
||||
* @type {Boolean}
|
||||
*/
|
||||
stopped: false,
|
||||
|
||||
window: {},
|
||||
streams: {},
|
||||
|
||||
/**
|
||||
* @type {MediaStream}
|
||||
*/
|
||||
localStream: undefined,
|
||||
|
||||
/**
|
||||
* @type {HTMLVideoElement}
|
||||
*/
|
||||
localStreamVideo: undefined,
|
||||
|
||||
/**
|
||||
* @type {SignalExchangerClient}
|
||||
*/
|
||||
signalClient: undefined
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Initialize utilities
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Get member information based on call ID
|
||||
*
|
||||
* @param {String} id The ID of the peer to process
|
||||
* @return Information about the peer / empty object
|
||||
* if no peer found
|
||||
*/
|
||||
call.getMemberByCallID = function(id){
|
||||
var memberInfo = undefined;
|
||||
call.info.members.forEach(function(member){
|
||||
|
||||
if(member.user_call_id == id)
|
||||
memberInfo = member;
|
||||
|
||||
});
|
||||
return memberInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check out whether local stream audio is enabled or not
|
||||
*
|
||||
* @return {Boolean} TRUE if local stream is enabled / FALSE else
|
||||
*/
|
||||
call.isLocalAudioEnabled = function(){
|
||||
|
||||
//Default behaviour : local stream not enabled
|
||||
if(!call.localStream)
|
||||
return true;
|
||||
|
||||
return call.localStream.getAudioTracks()[0].enabled;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Set audio mute status of local stream
|
||||
*
|
||||
* @param {Boolean} enabled New enabled status
|
||||
*/
|
||||
call.setLocalAudioEnabled = function(enabled){
|
||||
if(call.localStream)
|
||||
call.localStream.getAudioTracks()[0].enabled = enabled;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if the local video is enabled or not
|
||||
*
|
||||
* @return {Boolean} TRUE to mute video / FALSE else
|
||||
*/
|
||||
call.isLocalVideoEnabled = function(){
|
||||
|
||||
//Default behaviour : video not enabled
|
||||
if(!call.localStream)
|
||||
return true;
|
||||
|
||||
return call.localStream.getVideoTracks()[0].enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update mute status of local video stream
|
||||
*
|
||||
* @param {Boolean} enabled New mute status
|
||||
*/
|
||||
call.setLocalVideoEnabled = function(enabled){
|
||||
if(call.localStream)
|
||||
call.localStream.getVideoTracks()[0].enabled = enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set local stream video visibility
|
||||
*
|
||||
* @param {Boolean} visible TRUE to make it visible / FALSE else
|
||||
*/
|
||||
call.setLocalStreamVisibility = function(visible){
|
||||
if(call.localStreamVideo)
|
||||
call.localStreamVideo.style.display = visible ? "block" : "none";
|
||||
}
|
||||
|
||||
/**
|
||||
* Get local stream visibility
|
||||
*
|
||||
* @return {Boolean} TRUE if local stream is visible / FALSE else
|
||||
*/
|
||||
call.isLocalStreamVisible = function(){
|
||||
if(!call.localStreamVideo)
|
||||
return true;
|
||||
|
||||
return call.localStreamVideo.style.display !== "none";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* We have to begin to draw conversation UI
|
||||
*/
|
||||
var callContainer = createElem2({
|
||||
appendTo: byId("callsTarget") ? byId("callsTarget") : byId("wrapper"), //If call target is not found, add call in page wrapper
|
||||
type: "div",
|
||||
class: "call-window"
|
||||
});
|
||||
call.window.container = callContainer;
|
||||
|
||||
|
||||
//Add toolbar
|
||||
call.window.toolbar = createElem2({
|
||||
appendTo: callContainer,
|
||||
type: "div",
|
||||
class: "call-toolbar",
|
||||
innerHTML: "<i class='fa fa-phone'>"
|
||||
});
|
||||
|
||||
//Call title
|
||||
call.window.title = createElem2({
|
||||
appendTo: call.window.toolbar,
|
||||
type: "div",
|
||||
class: "call-title",
|
||||
innerHTML: "Loading..."
|
||||
});
|
||||
|
||||
/**
|
||||
* Update the title of the call
|
||||
*/
|
||||
call.setTitle = function(title){
|
||||
call.window.title.innerHTML = title;
|
||||
}
|
||||
|
||||
//Add close button
|
||||
call.window.closeButton = createElem2({
|
||||
appendTo: call.window.toolbar,
|
||||
type: "button",
|
||||
class: "btn btn-box-tool close-btn",
|
||||
innerHTML: "<i class='fa fa-times'></i>"
|
||||
});
|
||||
|
||||
//Make close button lives
|
||||
call.close = function(){
|
||||
|
||||
//Avoid to call this several times
|
||||
if(call.stopped)
|
||||
return;
|
||||
|
||||
call.open = false;
|
||||
call.stopped = true;
|
||||
callContainer.remove();
|
||||
|
||||
//Close sockets connections too
|
||||
ComunicWeb.components.calls.callWindow.stop(call);
|
||||
}
|
||||
|
||||
call.window.closeButton.addEventListener("click", function(){
|
||||
|
||||
//Check if the call is in full screen mode
|
||||
if(IsFullScreen())
|
||||
RequestFullScreen(null)
|
||||
else
|
||||
call.close();
|
||||
|
||||
});
|
||||
|
||||
|
||||
//Get information about related conversation to get the name of the call
|
||||
ComunicWeb.components.conversations.utils.getNameForID(info.conversation_id, function(name){
|
||||
|
||||
if(!name)
|
||||
return notify("Could not get information about related conversation!", "danger");
|
||||
|
||||
call.setTitle(name);
|
||||
|
||||
});
|
||||
|
||||
|
||||
//Call box body
|
||||
call.window.body = createElem2({
|
||||
appendTo: callContainer,
|
||||
type: "div",
|
||||
class: "call-window-body"
|
||||
});
|
||||
|
||||
|
||||
//Call videos target
|
||||
call.window.videosTarget = createElem2({
|
||||
appendTo: call.window.body,
|
||||
type: "div",
|
||||
class: "streams-target"
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Create loading message area
|
||||
*/
|
||||
call.window.loadingMessageContainer = createElem2({
|
||||
insertBefore: call.window.body.firstChild,
|
||||
type: "div",
|
||||
class: "loading-message-container",
|
||||
innerHTML: "<i class='fa fa-clock-o'></i>"
|
||||
});
|
||||
|
||||
call.window.loadingMessageContent = createElem2({
|
||||
appendTo: call.window.loadingMessageContainer,
|
||||
type: "div",
|
||||
class: "message",
|
||||
innerHTML: "Loading..."
|
||||
});
|
||||
|
||||
/**
|
||||
* Set loading message visiblity
|
||||
*
|
||||
* @param {Boolean} visible TRUE to make it visible / FALSE else
|
||||
*/
|
||||
call.setLoadingMessageVisibility = function(visible){
|
||||
call.window.loadingMessageContainer.style.display = visible ? "flex" : "none";
|
||||
}
|
||||
|
||||
/**
|
||||
* Update call loading message
|
||||
*
|
||||
* @param {String} message The new message to show to the
|
||||
* users
|
||||
*/
|
||||
call.setLoadingMessage = function(message){
|
||||
call.setLoadingMessageVisibility(true);
|
||||
call.window.loadingMessageContent.innerHTML = message;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//Call footer
|
||||
call.window.footer = createElem2({
|
||||
appendTo: callContainer,
|
||||
type: "div",
|
||||
class: "call-footer"
|
||||
});
|
||||
|
||||
/**
|
||||
* This function is used to toggle selection state
|
||||
* of one of the call toolbar button
|
||||
*
|
||||
* @param {HTMLElement} btn Target button
|
||||
* @param {Boolean} selected Selection state of the button
|
||||
*/
|
||||
var togglButtonSelectedStatus = function(btn, selected){
|
||||
|
||||
if(!selected){
|
||||
while(btn.className.includes(" selected"))
|
||||
btn.className = btn.className.replace(" selected", "");
|
||||
}
|
||||
|
||||
else if(selected && !btn.className.includes(" selected"))
|
||||
btn.className += " selected";
|
||||
}
|
||||
|
||||
var buttonsList = [
|
||||
|
||||
//Show current user camera
|
||||
{
|
||||
icon: "fa-eye",
|
||||
selected: true,
|
||||
onclick: function(btn){
|
||||
call.setLocalStreamVisibility(!call.isLocalStreamVisible());
|
||||
togglButtonSelectedStatus(btn, call.isLocalStreamVisible());
|
||||
}
|
||||
},
|
||||
|
||||
//Mute button
|
||||
{
|
||||
icon: "fa-microphone",
|
||||
selected: true,
|
||||
onclick: function(btn){
|
||||
call.setLocalAudioEnabled(!call.isLocalAudioEnabled());
|
||||
togglButtonSelectedStatus(btn, call.isLocalAudioEnabled());
|
||||
}
|
||||
},
|
||||
|
||||
//Hang up button
|
||||
{
|
||||
icon: "fa-phone",
|
||||
class: "hang-up-button",
|
||||
selected: false,
|
||||
onclick: function(){
|
||||
call.close();
|
||||
}
|
||||
},
|
||||
|
||||
//Stop video button
|
||||
{
|
||||
icon: "fa-video-camera",
|
||||
selected: true,
|
||||
onclick: function(btn){
|
||||
call.setLocalVideoEnabled(!call.isLocalVideoEnabled());
|
||||
togglButtonSelectedStatus(btn, call.isLocalVideoEnabled());
|
||||
console.log(call);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
//Full screen button
|
||||
{
|
||||
icon: "fa-expand",
|
||||
selected: false,
|
||||
onclick: function(btn){
|
||||
RequestFullScreen(callContainer);
|
||||
setTimeout(function(){
|
||||
togglButtonSelectedStatus(btn, IsFullScreen());
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
//Add buttons
|
||||
buttonsList.forEach(function(button){
|
||||
|
||||
var buttonEl = createElem2({
|
||||
appendTo: call.window.footer,
|
||||
type: "div",
|
||||
class: "call-option-button",
|
||||
innerHTML: "<i class='fa " + button.icon + "'></i>"
|
||||
});
|
||||
|
||||
//Add button optionnal class
|
||||
if(button.class)
|
||||
buttonEl.className += " " + button.class;
|
||||
|
||||
buttonEl.addEventListener("click", function(){
|
||||
button.onclick(buttonEl);
|
||||
});
|
||||
|
||||
togglButtonSelectedStatus(buttonEl, button.selected);
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Make the call window draggable
|
||||
*/
|
||||
|
||||
function checkWindowMinPosition(){
|
||||
|
||||
if(window.innerHeight < callContainer.style.top.replace("px", ""))
|
||||
callContainer.style.top = "0px";
|
||||
|
||||
if(window.innerWidth < callContainer.style.left.replace("px", ""))
|
||||
callContainer.style.left = "0px";
|
||||
|
||||
if(callContainer.style.left.replace("px", "") < 0)
|
||||
callContainer.style.left = "0px";
|
||||
|
||||
if(callContainer.style.top.replace("px", "") < 49)
|
||||
callContainer.style.top = "50px";
|
||||
}
|
||||
|
||||
//Enable dragging
|
||||
{
|
||||
var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
|
||||
|
||||
call.window.title.onmousedown = function(e){
|
||||
e = e || window.event;
|
||||
e.preventDefault();
|
||||
|
||||
//Check if the window is currently in full screen mode
|
||||
if(IsFullScreen())
|
||||
return;
|
||||
|
||||
//get the mouse cursor position at startup
|
||||
pos3 = e.clientX;
|
||||
pos4 = e.clientY;
|
||||
document.onmouseup = closeDragElement;
|
||||
document.onmousemove = elementDrag;
|
||||
}
|
||||
|
||||
function elementDrag(e){
|
||||
e = e || window.event;
|
||||
e.preventDefault();
|
||||
|
||||
//Calculate new cursor position
|
||||
pos1 = pos3 - e.clientX;
|
||||
pos2 = pos4 - e.clientY;
|
||||
pos3 = e.clientX;
|
||||
pos4 = e.clientY;
|
||||
|
||||
//Set element new position
|
||||
callContainer.style.top = (callContainer.offsetTop - pos2) + "px";
|
||||
callContainer.style.left = (callContainer.offsetLeft - pos1) + "px";
|
||||
|
||||
checkWindowMinPosition();
|
||||
}
|
||||
|
||||
function closeDragElement(){
|
||||
|
||||
//Stop moving when mouse button is released
|
||||
document.onmouseup = null;
|
||||
document.onmousemove = null;
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener("resize", function(){
|
||||
checkWindowMinPosition();
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
//Load user media
|
||||
call.setLoadingMessage("Waiting for your microphone and camera...");
|
||||
|
||||
ComunicWeb.components.calls.userMedia.get().then(function(stream){
|
||||
|
||||
//Check if connection has already been closed
|
||||
if(!call.open)
|
||||
return;
|
||||
|
||||
call.localStream = stream;
|
||||
|
||||
//Initialize signaling server connection
|
||||
ComunicWeb.components.calls.callWindow.initializeConnectionToSignalingServer(call);
|
||||
|
||||
//Add local stream to the list of visible stream
|
||||
call.localStreamVideo = ComunicWeb.components.calls.callWindow.addVideoStream(call, true, stream);
|
||||
|
||||
//Mark as connecting
|
||||
call.setLoadingMessage("Connecting...");
|
||||
|
||||
return true;
|
||||
|
||||
}).catch(function(e){
|
||||
console.error("Get user media error: ", e);
|
||||
call.setLoadingMessageVisibility(false);
|
||||
return notify("Could not get your microphone and camera!", "danger");
|
||||
});
|
||||
|
||||
/**
|
||||
* Start to automaticaly refresh information about the call
|
||||
*/
|
||||
var interval = setInterval(function(){
|
||||
|
||||
//Check if call is not visible anymore
|
||||
if(!callContainer.isConnected){
|
||||
call.close();
|
||||
return;
|
||||
}
|
||||
|
||||
if(!call.open)
|
||||
return clearInterval(interval);
|
||||
|
||||
ComunicWeb.components.calls.callWindow.refreshInfo(call);
|
||||
|
||||
}, 4000);
|
||||
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Initialize connection to signaling server
|
||||
*
|
||||
* @param {Object} call Information about the call
|
||||
*/
|
||||
initializeConnectionToSignalingServer: function(call) {
|
||||
|
||||
//Get current user call ID
|
||||
call.info.members.forEach(function(member){
|
||||
|
||||
if(member.userID == userID())
|
||||
call.localPeerID = member.user_call_id;
|
||||
});
|
||||
|
||||
|
||||
//Create client instance and connect to server
|
||||
var config = ComunicWeb.components.calls.controller.getConfig();
|
||||
call.signalClient = new SignalExchangerClient(
|
||||
config.signal_server_name,
|
||||
config.signal_server_port,
|
||||
call.localPeerID,
|
||||
config.is_signal_server_secure
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Error when connecting to signaling server
|
||||
*/
|
||||
call.signalClient.onError = function(){
|
||||
call.setLoadingMessage("Could not connect to signaling server!");
|
||||
call.open = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Connection to signaling server is not supposed to close
|
||||
*/
|
||||
call.signalClient.onClosed = function(){
|
||||
call.setLoadingMessage("Connection to signaling server closed!");
|
||||
call.open = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* A remote peer sent a ready notice
|
||||
*/
|
||||
call.signalClient.onReadyMessage = function(peerID){
|
||||
ComunicWeb.components.calls.callWindow.readyToInitialize(call, peerID);
|
||||
}
|
||||
|
||||
/**
|
||||
* A remote peer sent a signal
|
||||
*/
|
||||
call.signalClient.onSignal = function(signal, peerID){
|
||||
ComunicWeb.components.calls.callWindow.receivedSignal(call, peerID, signal);
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Refresh at a regular interval information about the call
|
||||
*
|
||||
* @param {Object} call Call Root object
|
||||
*/
|
||||
refreshInfo: function(call){
|
||||
|
||||
ComunicWeb.components.calls.interface.getInfo(call.info.id, function(result){
|
||||
|
||||
if(result.error)
|
||||
return notify("Could not get information about the call!", "danger");
|
||||
|
||||
call.info = result;
|
||||
|
||||
ComunicWeb.components.calls.callWindow.gotNewCallInfo(call);
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* This method get called each time information about the call
|
||||
* are refreshed on the server
|
||||
*
|
||||
* @param {Object} call Information about the call
|
||||
*/
|
||||
gotNewCallInfo: function(call) {
|
||||
|
||||
//Check if we are connected to signaling server and we have got local
|
||||
//streams
|
||||
if(!call.signalClient || !call.signalClient.isConnected() || !call.localStream)
|
||||
return;
|
||||
|
||||
//Check if all other members rejected call
|
||||
var allDisconnected = ComunicWeb.components.calls.utils.hasEveryoneLeft(call.info);
|
||||
|
||||
//Check if all call peer rejected the call
|
||||
if(allDisconnected){
|
||||
call.setLoadingMessage("Conversation terminated.");
|
||||
|
||||
setTimeout(function(){
|
||||
call.close();
|
||||
}, 5000);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//Process the connection to each accepted member
|
||||
call.info.members.forEach(function(member){
|
||||
|
||||
//Ignores all not accepted connection
|
||||
if(member.userID == userID() || member.status !== "accepted")
|
||||
return;
|
||||
|
||||
//Check if we have not peer information
|
||||
if(!call.streams.hasOwnProperty("peer-" + member.userID)){
|
||||
|
||||
//If the ID of the current user is bigger than the remote
|
||||
//peer user ID, we wait a ready signal from him
|
||||
if(member.userID < userID())
|
||||
return;
|
||||
|
||||
//Else we have to create peer
|
||||
ComunicWeb.components.calls.callWindow.createPeerConnection(call, member, false);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Create a peer connection
|
||||
*
|
||||
* @param {Object} call Information about the peer
|
||||
* @param {Object} member Information about the member
|
||||
* @param {Boolean} isInitiator Specify whether current user is the
|
||||
* initiator of the connection or not
|
||||
*/
|
||||
createPeerConnection: function(call, member, isInitiator){
|
||||
|
||||
var peerConnection = {
|
||||
peer: undefined
|
||||
};
|
||||
call.streams["peer-" + member.userID] = peerConnection;
|
||||
|
||||
//Get call configuration
|
||||
var config = ComunicWeb.components.calls.controller.getConfig();
|
||||
|
||||
//Create peer
|
||||
var peer = new SimplePeer({
|
||||
initiator: isInitiator,
|
||||
stream: call.localStream,
|
||||
trickle: true,
|
||||
config: {
|
||||
'iceServers': [
|
||||
{ urls: config.stun_server },
|
||||
{"url": config.turn_server,
|
||||
"credential": config.turn_username,
|
||||
"username": config.turn_password}
|
||||
]
|
||||
}
|
||||
});
|
||||
peerConnection.peer = peer;
|
||||
|
||||
//Add a function to remove connection
|
||||
peerConnection.removePeerConnection = function(){
|
||||
peer.destroy();
|
||||
delete call.streams["peer-" + member.userID];
|
||||
|
||||
if(peerConnection.video)
|
||||
peerConnection.video.remove();
|
||||
}
|
||||
|
||||
|
||||
peer.on("error", function(err){
|
||||
console.error("Peer error !", err, member);
|
||||
peerConnection.removePeerConnection();
|
||||
});
|
||||
|
||||
peer.on("signal", function(data){
|
||||
console.log('SIGNAL', JSON.stringify(data));
|
||||
call.signalClient.sendSignal(member.user_call_id, JSON.stringify(data));
|
||||
});
|
||||
|
||||
peer.on("message", function(message){
|
||||
console.log("Message from remote peer: " + message);
|
||||
});
|
||||
|
||||
|
||||
peer.on("close", function(){
|
||||
peerConnection.removePeerConnection();
|
||||
});
|
||||
|
||||
peer.on("stream", function(stream){
|
||||
ComunicWeb.components.calls.callWindow.streamAvailable(call, member, stream);
|
||||
});
|
||||
|
||||
|
||||
//If this peer does not initialize connection, inform other peer we are ready
|
||||
if(!isInitiator)
|
||||
call.signalClient.sendReadyMessage(member.user_call_id);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* This method is called when a remote peers notify it is ready to
|
||||
* establish connection
|
||||
*
|
||||
* @param {Object} call Information about the call
|
||||
* @param {String} peerID Remote peer ID
|
||||
*/
|
||||
readyToInitialize: function(call, peerID){
|
||||
|
||||
var member = call.getMemberByCallID(peerID);
|
||||
if(member == undefined)
|
||||
return;
|
||||
|
||||
//It the user with the smallest ID who send the ready message
|
||||
//else it would mess everything up
|
||||
if(member.userID > userID())
|
||||
return;
|
||||
|
||||
this.createPeerConnection(call, member, true);
|
||||
},
|
||||
|
||||
/**
|
||||
* This method is called when we received a remote signal
|
||||
*
|
||||
* @param {Object} call Information about the call
|
||||
* @param {String} peerID Remote peer ID
|
||||
* @param {String} signal Received signal
|
||||
*/
|
||||
receivedSignal: function(call, peerID, signal){
|
||||
|
||||
console.log("Received signal from " + peerID, signal);
|
||||
|
||||
var member = call.getMemberByCallID(peerID);
|
||||
if(member == undefined)
|
||||
return;
|
||||
|
||||
//Check we have got peer information
|
||||
if(!call.streams.hasOwnProperty("peer-" + member.userID))
|
||||
return;
|
||||
|
||||
call.streams["peer-" + member.userID].peer.signal(JSON.parse(signal));
|
||||
},
|
||||
|
||||
/**
|
||||
* This method is called when a remote stream becomes available
|
||||
*
|
||||
* @param {Object} call Information about remote call
|
||||
* @param {String} member Information about target member
|
||||
* @param {MediaStream} stream Remote stream available
|
||||
*/
|
||||
streamAvailable: function(call, member, stream){
|
||||
|
||||
call.setLoadingMessageVisibility(false);
|
||||
|
||||
call.streams["peer-" + member.userID].stream = stream;
|
||||
|
||||
call.streams["peer-" + member.userID].video = this.addVideoStream(call, false, stream);
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Create and set a video object for a stream
|
||||
*
|
||||
* @param {Object} call Target call
|
||||
* @param {Boolean} muted Specify whether audio should be muted
|
||||
* or not
|
||||
* @param {MediaStream} stream Target stream
|
||||
* @return {HTMLVideoElement} Generated video element
|
||||
*/
|
||||
addVideoStream: function(call, muted, stream){
|
||||
|
||||
/**
|
||||
* @type {HTMLVideoElement}
|
||||
*/
|
||||
var video = createElem2({
|
||||
appendTo: call.window.videosTarget,
|
||||
type: "video"
|
||||
});
|
||||
|
||||
video.muted = muted;
|
||||
|
||||
//Set target video object and play it
|
||||
video.srcObject = stream;
|
||||
video.play();
|
||||
|
||||
return video;
|
||||
},
|
||||
|
||||
/**
|
||||
* Stop the ongoing call
|
||||
*
|
||||
* @param {Object} call Information about the call
|
||||
*/
|
||||
stop: function(call){
|
||||
|
||||
//Remove the call from the opened list
|
||||
ComunicWeb.components.calls.currentList.removeCallFromList(call.info.id);
|
||||
|
||||
//Close the connection to the server
|
||||
if(call.signalClient && call.signalClient.isConnected())
|
||||
call.signalClient.close();
|
||||
|
||||
//Close all socket connections
|
||||
for (var key in call.streams) {
|
||||
if (call.streams.hasOwnProperty(key)) {
|
||||
var element = call.streams[key];
|
||||
element.removePeerConnection();
|
||||
}
|
||||
}
|
||||
|
||||
//Close local stream
|
||||
if(call.localStream){
|
||||
call.localStream.getTracks().forEach(function(track){
|
||||
track.stop();
|
||||
});
|
||||
}
|
||||
|
||||
//Notify server
|
||||
ComunicWeb.components.calls.interface.hangUp(call.info.id, function(){});
|
||||
}
|
||||
}
|
293
assets/js/components/calls/controller.js
Normal file
293
assets/js/components/calls/controller.js
Normal file
@ -0,0 +1,293 @@
|
||||
/**
|
||||
* Calls controller
|
||||
*
|
||||
* @author Pierre HUBERT
|
||||
*/
|
||||
|
||||
ComunicWeb.components.calls.controller = {
|
||||
|
||||
/**
|
||||
* This variable contains the initialization state
|
||||
* of the call component
|
||||
*/
|
||||
_is_init: false,
|
||||
|
||||
/**
|
||||
* This variable contains whether the user is being
|
||||
* notified of a call or not
|
||||
*/
|
||||
_is_processing_call: false,
|
||||
|
||||
/**
|
||||
* Initialize calls component
|
||||
*/
|
||||
init: function(){
|
||||
|
||||
//We init this component just once
|
||||
if(this._is_init)
|
||||
return;
|
||||
|
||||
ComunicWeb.debug.logMessage("Initialize calls component");
|
||||
|
||||
//Initialize call container
|
||||
var initializeCallContainer = function(){
|
||||
|
||||
//Signed out users can not make calls
|
||||
if(!signed_in()){
|
||||
ComunicWeb.components.calls.controller.userSignedOut();
|
||||
return;
|
||||
}
|
||||
|
||||
//Need a wrapper to continue
|
||||
if(!byId("wrapper"))
|
||||
return;
|
||||
|
||||
//Check if calls target already exists
|
||||
if(byId("callsTarget"))
|
||||
return;
|
||||
|
||||
//Call system must be available
|
||||
if(!ComunicWeb.components.calls.controller.isAvailable())
|
||||
return;
|
||||
|
||||
//Create call target
|
||||
createElem2({
|
||||
appendTo: byId("wrapper"),
|
||||
type: "div",
|
||||
id: "callsTarget"
|
||||
});
|
||||
|
||||
//Now we have to reopen current calls
|
||||
ComunicWeb.components.calls.controller.reopenCurrentCalls();
|
||||
}
|
||||
|
||||
//We wait the user to be connected before trying to get
|
||||
// call configuration
|
||||
document.addEventListener("got_user_id", function(){
|
||||
|
||||
//Check if we have already the call configuration
|
||||
if(ComunicWeb.components.calls.__config !== undefined)
|
||||
return;
|
||||
|
||||
ComunicWeb.components.calls.interface.getConfig(function(config){
|
||||
|
||||
//Check if we could not get calls configuration
|
||||
if(config.error)
|
||||
return;
|
||||
|
||||
//Save calls configuration
|
||||
ComunicWeb.components.calls.__config = config;
|
||||
|
||||
initializeCallContainer();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
// Each time a page is opened, wec check if we have to create calls target
|
||||
document.addEventListener("openPage", function(){
|
||||
initializeCallContainer();
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Access calls configuration
|
||||
*
|
||||
* @return Cached calls configuration
|
||||
*/
|
||||
getConfig: function() {
|
||||
return ComunicWeb.components.calls.__config;
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if the call feature is available or not
|
||||
*/
|
||||
isAvailable: function(){
|
||||
|
||||
//First, check if this browser support webrtc
|
||||
if(!SimplePeer.WEBRTC_SUPPORT)
|
||||
return false;
|
||||
|
||||
//If do not have any call configuration, it is not possible to
|
||||
//make calls
|
||||
if(this.getConfig() == null)
|
||||
return false;
|
||||
|
||||
//Read configuration
|
||||
return this.getConfig().enabled;
|
||||
},
|
||||
|
||||
/**
|
||||
* Initiate a call for a conversation
|
||||
*
|
||||
* @param {Number} conversationID The ID of the target conversation
|
||||
*/
|
||||
call: function(conversationID){
|
||||
|
||||
//Create / Get call information for the conversation
|
||||
ComunicWeb.components.calls.interface.createForConversation(conversationID, function(call){
|
||||
|
||||
if(call.error)
|
||||
return notify("Could not get a call for this conversation!", "danger");
|
||||
|
||||
ComunicWeb.components.calls.controller.open(call);
|
||||
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Call this method to initialize a call for a call we have information about
|
||||
*
|
||||
* @param {Object} call Information about the call
|
||||
*/
|
||||
open: function(call){
|
||||
|
||||
//Check if the call is already in the list of calls
|
||||
if(ComunicWeb.components.calls.currentList.isCallInList(call.id))
|
||||
return;
|
||||
|
||||
//Add the call to the list of opened calls
|
||||
ComunicWeb.components.calls.currentList.addCallToList(call.id);
|
||||
|
||||
//Initialize call
|
||||
ComunicWeb.components.calls.callWindow.initCall(call);
|
||||
},
|
||||
|
||||
/**
|
||||
* This method is called each time the notification service
|
||||
* detect that the number of pending calls has increased. It
|
||||
* must in fact be "thread-safe" to avoid to do twice things
|
||||
* that should be one only once
|
||||
*
|
||||
* @param {number} number The number of pending calls
|
||||
*/
|
||||
newCallsAvailable: function(number){
|
||||
|
||||
//Check if user is already processing a call
|
||||
if(this._is_processing_call)
|
||||
return;
|
||||
this._is_processing_call = true;
|
||||
|
||||
/**
|
||||
* Switch processing call to false
|
||||
*/
|
||||
var undoIsProcessing = function(){
|
||||
ComunicWeb.components.calls.controller._is_processing_call = false;
|
||||
}
|
||||
|
||||
//Get information about the next pending call
|
||||
ComunicWeb.components.calls.interface.getNextPendingCall(function(call){
|
||||
|
||||
//Check if there is no pending call
|
||||
if(call.notice)
|
||||
return undoIsProcessing();
|
||||
|
||||
ComunicWeb.components.conversations.utils.getNameForID(call.conversation_id, function(name){
|
||||
|
||||
//Check for errors
|
||||
if(!name){
|
||||
ComunicWeb.debug.logMessage("Could not get the name of the conversation for a call, cannot process it!");
|
||||
undoIsProcessing();
|
||||
return;
|
||||
}
|
||||
|
||||
//Show ring screen
|
||||
var prompting = true;
|
||||
var ringScreenInfo = ComunicWeb.components.calls.ringScreen.show(name, 30, function(accept){
|
||||
|
||||
prompting = false;
|
||||
|
||||
undoIsProcessing();
|
||||
|
||||
ComunicWeb.components.calls.controller.applyReponseForCall(call, accept);
|
||||
|
||||
});
|
||||
|
||||
//Regulary check if the call is still valid
|
||||
var interval = setInterval(function(){
|
||||
|
||||
if(!prompting)
|
||||
return clearInterval(interval);
|
||||
|
||||
ComunicWeb.components.calls.interface.getInfo(call.id, function(info){
|
||||
|
||||
//Check for errors
|
||||
if(info.error)
|
||||
return;
|
||||
|
||||
//Refuse the call if everyone has left it
|
||||
if(ComunicWeb.components.calls.utils.hasEveryoneLeft(info))
|
||||
ringScreenInfo.respond(false);
|
||||
|
||||
//Close ring screen if user responded to the call on another Comunic client
|
||||
if(ComunicWeb.components.calls.utils.getCurrentUserState(info) != "unknown")
|
||||
ringScreenInfo.close();
|
||||
|
||||
});
|
||||
}, 2000);
|
||||
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Apply a response for the call
|
||||
*
|
||||
* @param {Object} call Information about the target call
|
||||
* @param {Boolean} accept TRUE to accept call / FALSE else
|
||||
*/
|
||||
applyReponseForCall: function(call, accept){
|
||||
|
||||
//Send response to server
|
||||
ComunicWeb.components.calls.interface.respondToCall(call.id, accept, function(r){
|
||||
|
||||
//Check for error
|
||||
if(r.error)
|
||||
return notify("Could not send response to call to server!", "danger");
|
||||
|
||||
if(!accept)
|
||||
return;
|
||||
|
||||
//We may start the call now
|
||||
ComunicWeb.components.calls.controller.open(call);
|
||||
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Reopen all current calls
|
||||
*/
|
||||
reopenCurrentCalls: function(){
|
||||
|
||||
//Process each call to open it
|
||||
ComunicWeb.components.calls.currentList.getCurrentCallsList().forEach(function(entry){
|
||||
|
||||
ComunicWeb.components.calls.interface.getInfo(entry, function(call){
|
||||
|
||||
if(call.error){
|
||||
ComunicWeb.components.calls.currentList.removeCallFromList(entry);
|
||||
return notify("Could not get information about a call!", "danger");
|
||||
}
|
||||
|
||||
ComunicWeb.components.calls.controller.open(call);
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Call this method only if the system is sure that
|
||||
* nobody is signed in the current tab
|
||||
*/
|
||||
userSignedOut: function(){
|
||||
|
||||
//Remove all the current calls from the list
|
||||
ComunicWeb.components.calls.currentList.removeAllCalls();
|
||||
|
||||
}
|
||||
}
|
84
assets/js/components/calls/currentList.js
Normal file
84
assets/js/components/calls/currentList.js
Normal file
@ -0,0 +1,84 @@
|
||||
/**
|
||||
* Currents calls list management
|
||||
*
|
||||
* @author Pierre HUBERT
|
||||
*/
|
||||
|
||||
ComunicWeb.components.calls.currentList = {
|
||||
|
||||
/**
|
||||
* This variable contains the name of the session storage
|
||||
* variable that contains active calls
|
||||
*/
|
||||
_local_storage_list_calls_name: "current-calls",
|
||||
|
||||
|
||||
/**
|
||||
* Get the list of active calls
|
||||
*
|
||||
* @return {number[]} The list of calls
|
||||
*/
|
||||
getCurrentCallsList: function(){
|
||||
var string = localStorage.getItem(this._local_storage_list_calls_name);
|
||||
|
||||
if(string === null || string == "")
|
||||
return [];
|
||||
else
|
||||
return string.split(",");
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if a call ID is in the list of opened calls
|
||||
*
|
||||
* @param {Number} id The ID of the call to check
|
||||
*/
|
||||
isCallInList: function(id){
|
||||
return this.getCurrentCallsList().includes(""+id);
|
||||
},
|
||||
|
||||
/**
|
||||
* Save a new list of calls
|
||||
*
|
||||
* @param {number[]} list The new list of calls to save
|
||||
*/
|
||||
saveNewCallsList: function(list){
|
||||
localStorage.setItem(this._local_storage_list_calls_name, list.join(","));
|
||||
},
|
||||
|
||||
/**
|
||||
* Add a call to the list of opened call
|
||||
*
|
||||
* @param {number} id The ID of the call to add
|
||||
*/
|
||||
addCallToList: function(id){
|
||||
var list = this.getCurrentCallsList();
|
||||
|
||||
if(!list.includes(""+id))
|
||||
list.push(id);
|
||||
|
||||
this.saveNewCallsList(list);
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove a call from the list of calls
|
||||
*
|
||||
* @param {Number} id The ID of the call to remove
|
||||
*/
|
||||
removeCallFromList: function(id){
|
||||
|
||||
var list = this.getCurrentCallsList();
|
||||
|
||||
while(list.includes(""+id))
|
||||
list.splice(list.indexOf(""+id), 1);
|
||||
|
||||
this.saveNewCallsList(list);
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove all the calls from the list
|
||||
*/
|
||||
removeAllCalls: function(){
|
||||
this.saveNewCallsList([]);
|
||||
}
|
||||
}
|
103
assets/js/components/calls/interface.js
Normal file
103
assets/js/components/calls/interface.js
Normal file
@ -0,0 +1,103 @@
|
||||
/**
|
||||
* Calls interface
|
||||
*
|
||||
* @author Pierre HUBERT
|
||||
*/
|
||||
|
||||
ComunicWeb.components.calls.interface = {
|
||||
|
||||
/**
|
||||
* Get calls configuration
|
||||
*
|
||||
* @param {function} callback Function that will be called
|
||||
* once the operation has completed
|
||||
*/
|
||||
getConfig: function(callback){
|
||||
ComunicWeb.common.api.makeAPIrequest("calls/config", {}, true, callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Create a call for a conversation
|
||||
*
|
||||
* @param {Number} convID The ID of the target conversation
|
||||
* @param {function} callback
|
||||
*/
|
||||
createForConversation : function(convID, callback){
|
||||
ComunicWeb.common.api.makeAPIrequest(
|
||||
"calls/createForConversation",
|
||||
{
|
||||
conversationID: convID
|
||||
},
|
||||
true,
|
||||
callback
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Get information about a single call
|
||||
*
|
||||
* @param {Number} callID The ID of the target call
|
||||
* @param {function} callback Function called on request result
|
||||
*/
|
||||
getInfo: function (callID, callback){
|
||||
ComunicWeb.common.api.makeAPIrequest(
|
||||
"calls/getInfo",
|
||||
{
|
||||
call_id: callID
|
||||
},
|
||||
true,
|
||||
callback
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Get and return the next pending call for the
|
||||
* user
|
||||
*
|
||||
* @param {function} callback
|
||||
*/
|
||||
getNextPendingCall: function(callback){
|
||||
ComunicWeb.common.api.makeAPIrequest(
|
||||
"calls/nextPending",
|
||||
{},
|
||||
true,
|
||||
callback
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Respond to call
|
||||
*
|
||||
* @param {number} call_id The ID of the target call
|
||||
* @param {boolean} accept TRUE to accept call / FALSE els
|
||||
* @param {function} callback Function to call once response has been set
|
||||
*/
|
||||
respondToCall: function(call_id, accept, callback){
|
||||
ComunicWeb.common.api.makeAPIrequest(
|
||||
"calls/respond",
|
||||
{
|
||||
call_id: call_id,
|
||||
accept: accept
|
||||
},
|
||||
true,
|
||||
callback
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Hang up a call
|
||||
*
|
||||
* @param {Number} call_id The ID of the target call
|
||||
* @param {function} callback Function to call on call callback
|
||||
*/
|
||||
hangUp: function(call_id, callback){
|
||||
ComunicWeb.common.api.makeAPIrequest(
|
||||
"calls/hangUp",
|
||||
{
|
||||
call_id: call_id,
|
||||
},
|
||||
true,
|
||||
callback
|
||||
);
|
||||
}
|
||||
}
|
125
assets/js/components/calls/ringScreen.js
Normal file
125
assets/js/components/calls/ringScreen.js
Normal file
@ -0,0 +1,125 @@
|
||||
/**
|
||||
* Calls ring screen
|
||||
*
|
||||
* Display a popup to ask the user whether he wants
|
||||
* to respond to a call or not
|
||||
*
|
||||
* @author Pierre HUBERT
|
||||
*/
|
||||
|
||||
ComunicWeb.components.calls.ringScreen = {
|
||||
|
||||
/**
|
||||
* Song object
|
||||
*
|
||||
* @type {SongPlayer}
|
||||
*/
|
||||
_song: undefined,
|
||||
|
||||
/**
|
||||
* Notify user about an incoming call and offer him to respond it
|
||||
*
|
||||
* @param {String} title The title of the conversation
|
||||
* @param {number} timeout Timeout after which the call is automatically
|
||||
* considered as rejected
|
||||
* @param {(accept : boolean) => any} callback Callback function
|
||||
* @return {Object} Information about the window
|
||||
*/
|
||||
show: function(title, timeout, callback){
|
||||
|
||||
//Initialize song first
|
||||
if(this._song == undefined)
|
||||
this._song = new SongPlayer([
|
||||
ComunicWeb.__config.assetsURL + "audio/phone_ring.mp3",
|
||||
ComunicWeb.__config.assetsURL + "audio/phone_ring.ogg"
|
||||
]);
|
||||
this._song.playForever();
|
||||
|
||||
var callContainer = createElem2({
|
||||
appendTo: document.body,
|
||||
type: "div",
|
||||
class: "ring-call-container"
|
||||
});
|
||||
|
||||
var callMessageBox = createElem2({
|
||||
appendTo: callContainer,
|
||||
type: "div",
|
||||
class: "call-message-box"
|
||||
});
|
||||
|
||||
add_p(callMessageBox, "<i>" + title + "</i> is calling you");
|
||||
|
||||
|
||||
//Add buttons to respond to call
|
||||
var respondButtons = createElem2({
|
||||
appendTo: callMessageBox,
|
||||
type: "div",
|
||||
class: "response-buttons"
|
||||
});
|
||||
|
||||
var rejectButton = createElem2({
|
||||
appendTo: respondButtons,
|
||||
type: "button",
|
||||
class: "btn btn-danger",
|
||||
innerHTML: "Reject"
|
||||
});
|
||||
|
||||
var acceptButton = createElem2({
|
||||
appendTo: respondButtons,
|
||||
type: "button",
|
||||
class: "btn btn-success",
|
||||
innerHTML: "Accept"
|
||||
});
|
||||
|
||||
var close = function(){
|
||||
|
||||
ComunicWeb.components.calls.ringScreen._song.stop();
|
||||
|
||||
//Remove elem
|
||||
emptyElem(callContainer);
|
||||
callContainer.remove();
|
||||
|
||||
}
|
||||
|
||||
var hasResponded = false;
|
||||
var respond = function(accept){
|
||||
|
||||
close();
|
||||
|
||||
if(hasResponded)
|
||||
return;
|
||||
hasResponded = true;
|
||||
|
||||
callback(accept);
|
||||
}
|
||||
|
||||
rejectButton.addEventListener("click", function() {
|
||||
respond(false);
|
||||
});
|
||||
|
||||
acceptButton.addEventListener("click", function(){
|
||||
respond(true);
|
||||
});
|
||||
|
||||
//Automatically reject the call after a certain amount of time
|
||||
setTimeout(function(){
|
||||
respond(false);
|
||||
}, timeout*1000);
|
||||
|
||||
return {
|
||||
|
||||
/**
|
||||
* A function to close the current ringscreen, without
|
||||
* calling callback
|
||||
*/
|
||||
close: close,
|
||||
|
||||
/**
|
||||
* A function to programmatically respond to call
|
||||
*/
|
||||
respond: respond
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
32
assets/js/components/calls/userMedia.js
Normal file
32
assets/js/components/calls/userMedia.js
Normal file
@ -0,0 +1,32 @@
|
||||
/**
|
||||
* User media getter
|
||||
*
|
||||
* @author Pierre HUBERT
|
||||
*/
|
||||
|
||||
ComunicWeb.components.calls.userMedia = {
|
||||
|
||||
/**
|
||||
* Get user media
|
||||
*
|
||||
* @return {Promise} A promise to get user media
|
||||
*/
|
||||
get: function(){
|
||||
|
||||
//Use latest API
|
||||
return navigator.mediaDevices
|
||||
|
||||
//Request user media
|
||||
.getUserMedia({
|
||||
audio: true,
|
||||
video: true
|
||||
})
|
||||
|
||||
//Save stream
|
||||
.then(function(stream){
|
||||
ComunicWeb.components.calls.userMedia._currMedia = stream;
|
||||
return stream;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
44
assets/js/components/calls/utils.js
Normal file
44
assets/js/components/calls/utils.js
Normal file
@ -0,0 +1,44 @@
|
||||
/**
|
||||
* Calls utilities
|
||||
*
|
||||
* @author Pierre HUBERT
|
||||
*/
|
||||
|
||||
ComunicWeb.components.calls.utils = {
|
||||
|
||||
/**
|
||||
* Check out whether all the members of a conversation stop to follow it,
|
||||
* except the current user
|
||||
*
|
||||
* @param {Object} info Information about the conversation to analyze
|
||||
*/
|
||||
hasEveryoneLeft: function(info){
|
||||
|
||||
var allDisconnected = true;
|
||||
info.members.forEach(function(member){
|
||||
if(member.status != "rejected" && member.status != "hang_up" && member.userID != userID())
|
||||
allDisconnected = false;
|
||||
});
|
||||
|
||||
return allDisconnected;
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the current user response to a call
|
||||
*
|
||||
* @param {Call} call Current call information
|
||||
* @return The response of the current user to the call
|
||||
*/
|
||||
getCurrentUserState: function(call){
|
||||
|
||||
var userstate = undefined;
|
||||
call.members.forEach(function(member){
|
||||
if(member.userID == userID())
|
||||
userstate = member.status
|
||||
});
|
||||
|
||||
return userstate;
|
||||
}
|
||||
|
||||
};
|
@ -56,7 +56,7 @@ ComunicWeb.components.conversations.chatWindows = {
|
||||
infosBox.conversationID = infos.conversationID;
|
||||
|
||||
//Change box root class name
|
||||
infosBox.rootElem.className += " direct-chat direct-chat-primary";
|
||||
infosBox.rootElem.className += " chat-window direct-chat direct-chat-primary";
|
||||
|
||||
//Adapt close button behaviour
|
||||
infosBox.closeFunction = function(){
|
||||
@ -84,18 +84,21 @@ ComunicWeb.components.conversations.chatWindows = {
|
||||
});
|
||||
|
||||
//Add button to get conversation members
|
||||
infosBox.membersButton = createElem("button");
|
||||
infosBox.closeButton.parentNode.insertBefore(infosBox.membersButton, infosBox.closeButton);
|
||||
infosBox.membersButton.type = "button";
|
||||
infosBox.membersButton.className = "btn btn-box-tool";
|
||||
infosBox.membersButton = createElem2({
|
||||
type: "button",
|
||||
insertBefore: infosBox.closeButton,
|
||||
elemType: "button",
|
||||
class: "btn btn-box-tool",
|
||||
title: "Conversation members"
|
||||
});
|
||||
infosBox.membersButton.setAttribute("data-toggle", "tooltip");
|
||||
infosBox.membersButton.setAttribute("data-widget", "chat-pane-toggle");
|
||||
infosBox.membersButton.title = "Conversation members";
|
||||
|
||||
//Add button icon
|
||||
var buttonIcon = createElem("i", infosBox.membersButton);
|
||||
buttonIcon.className = "fa fa-users";
|
||||
|
||||
|
||||
//Add conversation members pane
|
||||
var membersPane = createElem("div", infosBox.boxBody);
|
||||
membersPane.className = "direct-chat-contacts";
|
||||
@ -319,6 +322,9 @@ ComunicWeb.components.conversations.chatWindows = {
|
||||
return false;
|
||||
};
|
||||
|
||||
//Add call button (if possible)
|
||||
ComunicWeb.components.conversations.chatWindows.showCallButton(conversationInfos);
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
@ -563,16 +569,46 @@ ComunicWeb.components.conversations.chatWindows = {
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Add a call button to the conversation, if possible
|
||||
*
|
||||
* @param {Object} conversation Information about the conversation
|
||||
*/
|
||||
showCallButton: function(conversation){
|
||||
|
||||
//Check if calls are disabled
|
||||
if(!ComunicWeb.components.calls.controller.isAvailable())
|
||||
return;
|
||||
|
||||
//Check if it is a conversation with an exceptable number of members
|
||||
if(conversation.infos.members.length < 2
|
||||
|| conversation.infos.members.length > ComunicWeb.components.calls.controller.getConfig().maximum_number_members)
|
||||
return;
|
||||
|
||||
//Add the call button
|
||||
var button = createElem2({
|
||||
insertBefore: conversation.box.boxTools.firstChild,
|
||||
type: "button",
|
||||
class: "btn btn-box-tool",
|
||||
innerHTML: "<i class='fa fa-phone'></i>"
|
||||
});
|
||||
conversation.box.callButton = button;
|
||||
|
||||
button.addEventListener("click", function(){
|
||||
ComunicWeb.components.calls.controller.call(conversation.infos.ID);
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Process submited update conversation form
|
||||
*
|
||||
* @param {Object} conversation Informations about the conversation
|
||||
* @param {Object} conversation Information about the conversation
|
||||
* @return {Boolean} True for a success
|
||||
*/
|
||||
submitUpdateForm: function(conversation){
|
||||
|
||||
//Then, get informations about the input
|
||||
//Then, get information about the input
|
||||
var newValues = {
|
||||
conversationID: conversation.infos.ID,
|
||||
following: conversation.settingsForm.followConversationInput.checked,
|
||||
@ -621,7 +657,7 @@ ComunicWeb.components.conversations.chatWindows = {
|
||||
/**
|
||||
* Submit a new message form
|
||||
*
|
||||
* @param {Object} convInfos Informations about the conversation
|
||||
* @param {Object} convInfos Information about the conversation
|
||||
* @return {Boolean} True for a success
|
||||
*/
|
||||
submitMessageForm: function(convInfos){
|
||||
@ -673,7 +709,7 @@ ComunicWeb.components.conversations.chatWindows = {
|
||||
/**
|
||||
* Reset a create a message form
|
||||
*
|
||||
* @param {Object} infos Informations about the conversation
|
||||
* @param {Object} infos Information about the conversation
|
||||
* @return {Boolean} True for a success
|
||||
*/
|
||||
resetCreateMessageForm: function(infos){
|
||||
|
@ -22,6 +22,9 @@ ComunicWeb.components.conversations.messageEditor = {
|
||||
|
||||
function(content){
|
||||
|
||||
if(!content)
|
||||
return;
|
||||
|
||||
//Intend to update message content
|
||||
ComunicWeb.components.conversations.interface.UpdateSingleMessage(
|
||||
message.ID,
|
||||
|
@ -54,6 +54,27 @@ ComunicWeb.components.conversations.utils = {
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Given a conversation ID, get its name
|
||||
*
|
||||
* @param {number} id The ID of the target conversation
|
||||
* @param {function} onGotName Function called once we have got the name of the conversation
|
||||
*/
|
||||
getNameForID: function(id, onGotName){
|
||||
|
||||
ComunicWeb.components.conversations.interface.getInfosOne(id, function(info){
|
||||
|
||||
//Check if an error occurred
|
||||
if(info.error)
|
||||
return onGotName(false);
|
||||
|
||||
//Get and return the name of the conversation
|
||||
ComunicWeb.components.conversations.utils.getName(info, onGotName);
|
||||
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Create and display a conversation creation / edition form
|
||||
*
|
||||
|
@ -25,14 +25,19 @@ ComunicWeb.components.notifications.interface = {
|
||||
/**
|
||||
* Get the number of unread news such as notifications or conversations
|
||||
*
|
||||
* @param {boolean} get_calls Get the number of pending calls
|
||||
* @param {function} callback
|
||||
*/
|
||||
getAllUnread: function(callback){
|
||||
getAllUnread: function(get_calls, callback){
|
||||
|
||||
//Perform API request
|
||||
var apiURI = "notifications/count_all_news";
|
||||
var params = {};
|
||||
|
||||
//Check if we have to get the number of pending calls
|
||||
if(get_calls)
|
||||
params.include_calls = true;
|
||||
|
||||
//Perform the request
|
||||
ComunicWeb.common.api.makeAPIrequest(apiURI, params, true, callback);
|
||||
|
||||
|
@ -35,7 +35,8 @@ ComunicWeb.components.notifications.service = {
|
||||
|
||||
|
||||
//Get the number of notifications from the API
|
||||
ComunicWeb.components.notifications.interface.getAllUnread(function(response){
|
||||
ComunicWeb.components.notifications.interface.getAllUnread(
|
||||
ComunicWeb.components.calls.controller.isAvailable(), function(response){
|
||||
|
||||
//Continue in case of success
|
||||
if(response.error)
|
||||
@ -76,6 +77,11 @@ ComunicWeb.components.notifications.service = {
|
||||
ComunicWeb.components.notifications.song.play();
|
||||
|
||||
ComunicWeb.components.notifications.service.last_notifs_number = total_number_notifs;
|
||||
|
||||
|
||||
//Process the number of calls if possible
|
||||
if(response.calls && response.calls > 0)
|
||||
ComunicWeb.components.calls.controller.newCallsAvailable(response.calls);
|
||||
});
|
||||
|
||||
}, 2000);
|
||||
|
@ -18,24 +18,13 @@ ComunicWeb.components.notifications.song = {
|
||||
|
||||
//Create song element if required
|
||||
if(this.songElem == null){
|
||||
this.songElem = createElem2({
|
||||
type: "audio"
|
||||
});
|
||||
|
||||
createElem2({
|
||||
type: "source",
|
||||
appendTo: this.songElem,
|
||||
src: ComunicWeb.__config.assetsURL + "audio/notif_song.mp3"
|
||||
});
|
||||
|
||||
createElem2({
|
||||
type: "source",
|
||||
appendTo: this.songElem,
|
||||
src: ComunicWeb.__config.assetsURL + "audio/notif_song.ogg"
|
||||
});
|
||||
this.songElem = new SongPlayer([
|
||||
ComunicWeb.__config.assetsURL + "audio/notif_song.mp3",
|
||||
ComunicWeb.__config.assetsURL + "audio/notif_song.ogg"
|
||||
]);
|
||||
}
|
||||
|
||||
//Play song
|
||||
this.songElem.play();
|
||||
this.songElem.playOnce();
|
||||
}
|
||||
}
|
@ -424,9 +424,41 @@ ComunicWeb.components.posts.ui = {
|
||||
//In case of YouTube video
|
||||
else if(info.kind == "youtube"){
|
||||
|
||||
//Create frame placeholder
|
||||
var youtube_placeholder = createElem2({
|
||||
appendTo: postRoot,
|
||||
type: "div",
|
||||
class: "post-youtube post-youtube-placeholder"
|
||||
});
|
||||
|
||||
//Title
|
||||
createElem2({
|
||||
appendTo: youtube_placeholder,
|
||||
type: "div",
|
||||
class: "title",
|
||||
innerHTML: "<i class='fa fa-youtube-play'></i> YouTube Movie"
|
||||
});
|
||||
|
||||
createElem2({
|
||||
appendTo: youtube_placeholder,
|
||||
type: "a",
|
||||
class: "btn btn-default",
|
||||
innerHTML: "Open on YouTube",
|
||||
href: "https://youtube.com/watch?v=" + info.file_path,
|
||||
}).target = "_blank";
|
||||
|
||||
var openHere = createElem2({
|
||||
appendTo: youtube_placeholder,
|
||||
type: "div",
|
||||
class: "cursor-pointer",
|
||||
innerHTML: "Open here"
|
||||
});
|
||||
|
||||
openHere.addEventListener("click", function(){
|
||||
|
||||
//Create iframe
|
||||
var youtube_iframe = createElem2({
|
||||
appendTo: postRoot,
|
||||
insertBefore: youtube_placeholder,
|
||||
type: "iframe",
|
||||
class: "post-youtube",
|
||||
src: "https://www.youtube-nocookie.com/embed/"+info.file_path+"?rel=0"
|
||||
@ -436,6 +468,10 @@ ComunicWeb.components.posts.ui = {
|
||||
youtube_iframe.setAttribute("allow", "encrypted-media");
|
||||
youtube_iframe.setAttribute("allowfullscreen", "");
|
||||
|
||||
youtube_placeholder.remove();
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
//In case of PDF
|
||||
|
@ -77,12 +77,18 @@ ComunicWeb.user.userLogin = {
|
||||
//Perform next action
|
||||
afterGetCurrentUserID(0);
|
||||
}
|
||||
else{
|
||||
else
|
||||
{
|
||||
//Update user ID
|
||||
ComunicWeb.user.userLogin.__userID = result.userID;
|
||||
|
||||
//Perform next action
|
||||
afterGetCurrentUserID(result.userID)
|
||||
afterGetCurrentUserID(result.userID);
|
||||
|
||||
//Notify about the event
|
||||
SendEvent("got_user_id", {
|
||||
userID: result.userID
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
|
1
assets/zip/.gitignore
vendored
Normal file
1
assets/zip/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
personnal-data-export-navigator.zip
|
9
assets/zip/personnal-data-export-navigator-builder.sh
Executable file
9
assets/zip/personnal-data-export-navigator-builder.sh
Executable file
@ -0,0 +1,9 @@
|
||||
#!/bin/bash
|
||||
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
|
||||
cd $SCRIPT_DIR;
|
||||
|
||||
rm -f personnal-data-export-navigator.zip
|
||||
cd personnal-data-export-navigator;
|
||||
zip -r personnal-data-export-navigator.zip assets Export.html
|
||||
mv personnal-data-export-navigator.zip ../
|
48
builder
48
builder
@ -63,11 +63,12 @@ function files_to_file(array $files, string $target) : bool {
|
||||
/**
|
||||
* Copy an array of files into a specific target file using uglifyJS
|
||||
*
|
||||
* @param string $begin_path The begining of each path
|
||||
* @param array $files The name of the source file
|
||||
* @param string $target The target file
|
||||
* @return bool TRUE in case of success / FALSE in case of failure
|
||||
*/
|
||||
function js_files_to_file(array $files, string $target){
|
||||
function js_files_to_file(string $begin_path, array $files, string $target){
|
||||
|
||||
$source = "";
|
||||
|
||||
@ -77,7 +78,28 @@ function js_files_to_file(array $files, string $target){
|
||||
|
||||
foreach($files as $file){
|
||||
|
||||
$uglifyjs = true;
|
||||
|
||||
//Check if file entry is an array or a string
|
||||
if(is_string($file))
|
||||
$file = $begin_path.$file;
|
||||
|
||||
//It is an array
|
||||
else if(is_array($file)) {
|
||||
|
||||
//Check if we have special information for uglifyjs
|
||||
if(isset($file["uglifyjs"]))
|
||||
$uglifyjs = $file["uglifyjs"];
|
||||
|
||||
$file = $begin_path.$file["path"];
|
||||
}
|
||||
|
||||
//Else the kind of entry is not supported
|
||||
else
|
||||
throw new Exception("Excepted string or array, got something else for javascript entry!");
|
||||
|
||||
//Compress file
|
||||
if($uglifyjs){
|
||||
notice("Parsing with UGLIFYJS: ".$file);
|
||||
exec("/usr/bin/uglifyjs '".$file."' -c -o ".TEMP_FILE, $output, $exit_code);
|
||||
|
||||
@ -88,6 +110,14 @@ function js_files_to_file(array $files, string $target){
|
||||
notice("An error (".$exit_code.") occured while parsing file ".$file, TRUE);
|
||||
exit(10);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//Else we take the file as is
|
||||
else
|
||||
$source .= "\n".file_get_contents($file);
|
||||
|
||||
|
||||
}
|
||||
|
||||
//Delete the temp file
|
||||
@ -177,6 +207,7 @@ if(file_exists(OUTPUT_DIRECTORY))
|
||||
mkdir(OUTPUT_DIRECTORY, 0777, true);
|
||||
mkdir($path_release_assets, 0777, true);
|
||||
mkdir($path_release_assets."/css", 0777, true);
|
||||
mkdir($path_release_assets."/zip", 0777, true);
|
||||
|
||||
|
||||
|
||||
@ -191,9 +222,8 @@ files_to_file($thirdPartyDebugFiles, $targetThirdPartyCSS);
|
||||
|
||||
//3rd party JS
|
||||
notice("Third Party JS");
|
||||
$thirdPartyDebugFiles = array_put_begining($path_debug_assets, $debug::THIRD_PARTY_JS);
|
||||
$targetThirdPartyJS = $path_release_assets.$release::THIRD_PARTY_JS;
|
||||
js_files_to_file($thirdPartyDebugFiles, $targetThirdPartyJS);
|
||||
js_files_to_file($path_debug_assets, $debug::THIRD_PARTY_JS, $targetThirdPartyJS);
|
||||
|
||||
//App CSS
|
||||
notice("App CSS");
|
||||
@ -203,9 +233,8 @@ files_to_file($appDebugFiles, $targetAppCSS);
|
||||
|
||||
//App JS
|
||||
notice("App JS");
|
||||
$appDebugFiles = array_put_begining($path_debug_assets, $debug::APP_JS);
|
||||
$targetAppJS = $path_release_assets.$release::APP_JS;
|
||||
js_files_to_file($appDebugFiles, $targetAppJS);
|
||||
js_files_to_file($path_debug_assets, $debug::APP_JS, $targetAppJS);
|
||||
|
||||
|
||||
//Make some adpations on third party files
|
||||
@ -230,6 +259,8 @@ rcopy($path_debug_assets."3rdparty/adminLTE/plugins/iCheck/flat/icheck-flat-imgs
|
||||
rcopy($path_debug_assets."img/", $path_release_assets."img/");
|
||||
rcopy($path_debug_assets."templates/", $path_release_assets."templates/");
|
||||
|
||||
//Copy songs
|
||||
rcopy($path_debug_assets."audio/", $path_release_assets."audio/");
|
||||
|
||||
//Copy dark theme
|
||||
rcopy($path_debug_assets."css/dark_theme.css", $path_release_assets."css/dark_theme.css");
|
||||
@ -237,6 +268,13 @@ rcopy($path_debug_assets."css/dark_theme.css", $path_release_assets."css/dark_th
|
||||
//Copy pacman
|
||||
rcopy($path_debug_assets."3rdparty/pacman", $path_release_assets."3rdparty/pacman");
|
||||
|
||||
|
||||
//Build and copy personnal data navigator
|
||||
notice("Build personnal data export navigator and add it to built files");
|
||||
exec($path_debug_assets."zip/personnal-data-export-navigator-builder.sh");
|
||||
rcopy($path_debug_assets."zip/personnal-data-export-navigator.zip", $path_release_assets."zip/personnal-data-export-navigator.zip");
|
||||
|
||||
|
||||
//Begin to write root PHP File
|
||||
notice("Generate PHP root file");
|
||||
$page_src = '<?php
|
||||
|
@ -159,6 +159,10 @@ class Dev {
|
||||
//JS BBCode Parser
|
||||
"3rdparty/js-bbcode-parser/bbcode-config.js",
|
||||
"3rdparty/js-bbcode-parser/bbcode-parser.js",
|
||||
|
||||
//Simple peer
|
||||
array("path" => "3rdparty/simplepeer/simplepeer.min.js", "uglifyjs" => false),
|
||||
array("path" => "3rdparty/SignalExchangerClient/SignalExchangerClient.js", "uglifyjs" => false)
|
||||
);
|
||||
|
||||
/**
|
||||
@ -218,6 +222,10 @@ class Dev {
|
||||
//Incognito mode component
|
||||
"css/components/incognito/ui.css",
|
||||
|
||||
//Calls component
|
||||
"css/components/calls/callWindow.css",
|
||||
"css/components/calls/ringScreen.css",
|
||||
|
||||
//Pacman (easter egg) stylesheet
|
||||
"css/components/pacman.css",
|
||||
|
||||
@ -312,6 +320,7 @@ class Dev {
|
||||
"js/common/formChecker.js",
|
||||
"js/common/date.js",
|
||||
"js/common/system.js",
|
||||
array("path" => "js/common/songPlayer.js", "uglifyjs" => false),
|
||||
|
||||
//Languages
|
||||
"js/langs/en.inc.js",
|
||||
@ -437,6 +446,15 @@ class Dev {
|
||||
"js/components/incognito/management.js",
|
||||
"js/components/incognito/keyboard.js",
|
||||
|
||||
//Calls compontent
|
||||
"js/components/calls/interface.js",
|
||||
"js/components/calls/controller.js",
|
||||
"js/components/calls/callWindow.js",
|
||||
"js/components/calls/currentList.js",
|
||||
"js/components/calls/userMedia.js",
|
||||
"js/components/calls/ringScreen.js",
|
||||
"js/components/calls/utils.js",
|
||||
|
||||
//Pacman component (easter egg)
|
||||
"js/components/pacman.js",
|
||||
|
||||
|
@ -134,6 +134,10 @@ function src_inc_list_js(string $assets_url, array $files) : string {
|
||||
|
||||
//Process the list of files
|
||||
foreach($files as $file){
|
||||
|
||||
if(is_array($file))
|
||||
$file = $file["path"];
|
||||
|
||||
$source .= src_inc_js($assets_url.$file)."\n\t\t";
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
Comunic web app client
|
||||
Main HTML file
|
||||
|
||||
(c) Pierre HUBERT 2017-2018
|
||||
(c) Pierre HUBERT 2017-2019
|
||||
-->
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
Reference in New Issue
Block a user