mirror of
https://github.com/pierre42100/ComunicWeb
synced 2025-07-13 06:28:08 +00:00
Compare commits
119 Commits
20-08-2018
...
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 | |||
c89edd3cd6 | |||
4d775494c0 | |||
f9c1f37ac6 | |||
0e3aee13bd | |||
4a24d5249c | |||
64ead89601 | |||
b48b6db028 | |||
59e6eb3c30 | |||
8072d1eb3e | |||
e1dea40167 | |||
f3efb3d390 | |||
2035b85a06 | |||
100032fa86 | |||
3da2455945 | |||
30a96d56ec | |||
a7a36d8665 | |||
a0ef614252 | |||
229b02534e | |||
f2ab71cf3f | |||
497b8f1274 | |||
98765057dd | |||
824de4bcdb | |||
72fe0843c4 | |||
584bb42c93 | |||
f8e8454b86 | |||
41354be949 | |||
457712cd35 | |||
a0d644469d | |||
f76d9ba9cd | |||
dc7fd44b67 | |||
fba7937d02 | |||
f418ee25b0 | |||
c05506a2a5 | |||
5bb4e2ae1f | |||
fc70840c41 | |||
dff9831228 | |||
6d61c0d621 | |||
b16ca0defd | |||
93df9d235d | |||
069922b7da | |||
a7337fa918 | |||
da047a976a | |||
379826f16b | |||
5dc64492cb | |||
5ac5101ced | |||
fdabb3e3fc | |||
2890b03283 | |||
f6e2c83dbd | |||
c2eba7b3be | |||
089739e141 | |||
5c19d9c04c | |||
46bb22b17b | |||
d49b04e6fb | |||
672cbc1409 | |||
34f652d3bf | |||
8f65890f29 | |||
413cc6ddf8 | |||
ccd0fd03a9 | |||
6588b3d13a | |||
4860ee6623 | |||
e8df43351f | |||
1c1dbe8454 | |||
7e09e0f2f9 | |||
c495337aa6 | |||
f91ffba2a4 | |||
affe9a96f5 |
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:
|
||||
|
||||
|
14
README.md
14
README.md
@ -21,8 +21,12 @@ ComunicWeb would not exists without the following technologies developped by the
|
||||
- jquery.hotkeys
|
||||
- bootstrap-wysiwyg (https://github.com/steveathon/bootstrap-wysiwyg/)
|
||||
- wdt-emoji-bundle (http://ned.im/wdt-emoji-bundle)
|
||||
- PNGLib (http://www.xarg.org/2010/03/generate-client-side-png-files-using-javascript/) (BSD Licence)
|
||||
- Identicon (http://github.com/stewartlord/identicon.js) (BSD Licence)
|
||||
- FileSaver.js (http://eligrey.com) (by Eli Grey) (MIT Licence)
|
||||
- JSZip (https://github.com/Stuk/jszip.git) (MIT Licence)
|
||||
- JSZip Utils (https://github.com/Stuk/jszip-utils.git) (MIT Licence)
|
||||
- PNGLib (http://www.xarg.org/2010/03/generate-client-side-png-files-using-javascript/) (BSD License)
|
||||
- Identicon (http://github.com/stewartlord/identicon.js) (BSD License)
|
||||
- FileSaver.js (http://eligrey.com) (by Eli Grey) (MIT License)
|
||||
- JSZip (https://github.com/Stuk/jszip.git) (MIT License)
|
||||
- JSZip Utils (https://github.com/Stuk/jszip-utils.git) (MIT License)
|
||||
- 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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
301
assets/3rdparty/js-bbcode-parser/bbcode-config.js
vendored
Normal file
301
assets/3rdparty/js-bbcode-parser/bbcode-config.js
vendored
Normal file
@ -0,0 +1,301 @@
|
||||
/*
|
||||
* Javascript BBCode Parser Config Options
|
||||
* @author Philip Nicolcev
|
||||
* @license MIT License
|
||||
*/
|
||||
|
||||
var parserColors = [ 'gray', 'silver', 'white', 'yellow', 'orange', 'red', 'fuchsia', 'blue', 'green', 'black', '#cd38d9' ];
|
||||
|
||||
var parserTags = {
|
||||
'b': {
|
||||
openTag: function(params,content) {
|
||||
return '<b>';
|
||||
},
|
||||
closeTag: function(params,content) {
|
||||
return '</b>';
|
||||
}
|
||||
},
|
||||
'code': {
|
||||
openTag: function(params,content) {
|
||||
return '<code>';
|
||||
},
|
||||
closeTag: function(params,content) {
|
||||
return '</code>';
|
||||
},
|
||||
noParse: true
|
||||
},
|
||||
'color': {
|
||||
openTag: function(params,content) {
|
||||
var colorCode = params.substr(1) || "inherit";
|
||||
BBCodeParser.regExpAllowedColors.lastIndex = 0;
|
||||
BBCodeParser.regExpValidHexColors.lastIndex = 0;
|
||||
if ( !BBCodeParser.regExpAllowedColors.test( colorCode ) ) {
|
||||
if ( !BBCodeParser.regExpValidHexColors.test( colorCode ) ) {
|
||||
colorCode = "inherit";
|
||||
} else {
|
||||
if (colorCode.substr(0,1) !== "#") {
|
||||
colorCode = "#" + colorCode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return '<span style="color:' + colorCode + '">';
|
||||
},
|
||||
closeTag: function(params,content) {
|
||||
return '</span>';
|
||||
}
|
||||
},
|
||||
'i': {
|
||||
openTag: function(params,content) {
|
||||
return '<i>';
|
||||
},
|
||||
closeTag: function(params,content) {
|
||||
return '</i>';
|
||||
}
|
||||
},
|
||||
'img': {
|
||||
openTag: function(params,content) {
|
||||
|
||||
var myUrl = content;
|
||||
|
||||
BBCodeParser.urlPattern.lastIndex = 0;
|
||||
if ( !BBCodeParser.urlPattern.test( myUrl ) ) {
|
||||
myUrl = "";
|
||||
}
|
||||
|
||||
return '<img class="bbCodeImage" src="' + myUrl + '">';
|
||||
},
|
||||
closeTag: function(params,content) {
|
||||
return '';
|
||||
},
|
||||
content: function(params,content) {
|
||||
return '';
|
||||
}
|
||||
},
|
||||
'list': {
|
||||
openTag: function(params,content) {
|
||||
return '<ul>';
|
||||
},
|
||||
closeTag: function(params,content) {
|
||||
return '</ul>';
|
||||
},
|
||||
restrictChildrenTo: ["*", "li"]
|
||||
},
|
||||
'noparse': {
|
||||
openTag: function(params,content) {
|
||||
return '';
|
||||
},
|
||||
closeTag: function(params,content) {
|
||||
return '';
|
||||
},
|
||||
noParse: true
|
||||
},
|
||||
'quote': {
|
||||
openTag: function(params,content) {
|
||||
return '<blockquote>';
|
||||
},
|
||||
closeTag: function(params,content) {
|
||||
return '</blockquote>';
|
||||
}
|
||||
},
|
||||
's': {
|
||||
openTag: function(params,content) {
|
||||
return '<s>';
|
||||
},
|
||||
closeTag: function(params,content) {
|
||||
return '</s>';
|
||||
}
|
||||
},
|
||||
'size': {
|
||||
openTag: function(params,content) {
|
||||
var mySize = parseInt(params.substr(1),10) || 0;
|
||||
if (mySize < 10 || mySize > 20) {
|
||||
mySize = 'inherit';
|
||||
} else {
|
||||
mySize = mySize + 'px';
|
||||
}
|
||||
return '<span style="font-size:' + mySize + '">';
|
||||
},
|
||||
closeTag: function(params,content) {
|
||||
return '</span>';
|
||||
}
|
||||
},
|
||||
'u': {
|
||||
openTag: function(params,content) {
|
||||
return '<span style="text-decoration:underline">';
|
||||
},
|
||||
closeTag: function(params,content) {
|
||||
return '</span>';
|
||||
}
|
||||
},
|
||||
'url': {
|
||||
openTag: function(params,content) {
|
||||
|
||||
var myUrl;
|
||||
|
||||
if (!params) {
|
||||
myUrl = content.replace(/<.*?>/g,"");
|
||||
} else {
|
||||
myUrl = params.substr(1);
|
||||
}
|
||||
|
||||
BBCodeParser.urlPattern.lastIndex = 0;
|
||||
if ( !BBCodeParser.urlPattern.test( myUrl ) ) {
|
||||
myUrl = "#";
|
||||
}
|
||||
|
||||
BBCodeParser.urlPattern.lastIndex = 0;
|
||||
if ( !BBCodeParser.urlPattern.test( myUrl ) ) {
|
||||
myUrl = "";
|
||||
}
|
||||
|
||||
return '<a href="' + myUrl + '">';
|
||||
},
|
||||
closeTag: function(params,content) {
|
||||
return '</a>';
|
||||
}
|
||||
},
|
||||
|
||||
//COMUNIC ADD BEGIN
|
||||
|
||||
'left': {
|
||||
openTag: function(params,content) {
|
||||
return '<p style="text-align: left;">';
|
||||
},
|
||||
closeTag: function(params,content) {
|
||||
return '</p>';
|
||||
}
|
||||
},
|
||||
|
||||
'center': {
|
||||
openTag: function(params,content) {
|
||||
return '<p style="text-align: center;">';
|
||||
},
|
||||
closeTag: function(params,content) {
|
||||
return '</p>';
|
||||
}
|
||||
},
|
||||
|
||||
'right': {
|
||||
openTag: function(params,content) {
|
||||
return '<p style="text-align: right;">';
|
||||
},
|
||||
closeTag: function(params,content) {
|
||||
return '</p>';
|
||||
}
|
||||
},
|
||||
|
||||
'justify': {
|
||||
openTag: function(params,content) {
|
||||
return '<p style="text-align: justify;">';
|
||||
},
|
||||
closeTag: function(params,content) {
|
||||
return '</p>';
|
||||
}
|
||||
},
|
||||
|
||||
'ul': {
|
||||
openTag: function(params,content) {
|
||||
return '<ul>';
|
||||
},
|
||||
closeTag: function(params,content) {
|
||||
return '</ul>';
|
||||
}
|
||||
},
|
||||
|
||||
'ol': {
|
||||
openTag: function(params,content) {
|
||||
return '<ol>';
|
||||
},
|
||||
closeTag: function(params,content) {
|
||||
return '</ol>';
|
||||
}
|
||||
},
|
||||
|
||||
'li': {
|
||||
openTag: function(params,content) {
|
||||
return '<li>';
|
||||
},
|
||||
closeTag: function(params,content) {
|
||||
return '</li>';
|
||||
}
|
||||
},
|
||||
|
||||
'sup': {
|
||||
openTag: function(params,content) {
|
||||
return '<sup>';
|
||||
},
|
||||
closeTag: function(params,content) {
|
||||
return '</sup>';
|
||||
}
|
||||
},
|
||||
|
||||
'sub': {
|
||||
openTag: function(params,content) {
|
||||
return '<sub>';
|
||||
},
|
||||
closeTag: function(params,content) {
|
||||
return '</sub>';
|
||||
}
|
||||
},
|
||||
|
||||
'ltr': {
|
||||
openTag: function(params,content) {
|
||||
return '<div style="text-align: left;">';
|
||||
},
|
||||
closeTag: function(params,content) {
|
||||
return '</div>';
|
||||
}
|
||||
},
|
||||
|
||||
'rtl': {
|
||||
openTag: function(params,content) {
|
||||
return '<div style="text-align: right;">';
|
||||
},
|
||||
closeTag: function(params,content) {
|
||||
return '</div>';
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
'table': {
|
||||
openTag: function(params,content) {
|
||||
return '<table border="1" style="margin: auto;">';
|
||||
},
|
||||
closeTag: function(params,content) {
|
||||
return '</table>';
|
||||
}
|
||||
},
|
||||
|
||||
'tr': {
|
||||
openTag: function(params,content) {
|
||||
return '<tr>';
|
||||
},
|
||||
closeTag: function(params,content) {
|
||||
return '</tr>';
|
||||
}
|
||||
},
|
||||
|
||||
'td': {
|
||||
openTag: function(params,content) {
|
||||
return '<td>';
|
||||
},
|
||||
closeTag: function(params,content) {
|
||||
return '</td>';
|
||||
}
|
||||
},
|
||||
|
||||
'hr': {
|
||||
openTag: function(params,content) {
|
||||
return '<hr />';
|
||||
},
|
||||
closeTag: function(params,content) {
|
||||
return '';
|
||||
},
|
||||
content: function(params, content){
|
||||
return '';
|
||||
}
|
||||
},
|
||||
|
||||
//COMUNIC ADD END
|
||||
};
|
152
assets/3rdparty/js-bbcode-parser/bbcode-parser.js
vendored
Normal file
152
assets/3rdparty/js-bbcode-parser/bbcode-parser.js
vendored
Normal file
@ -0,0 +1,152 @@
|
||||
/*
|
||||
* Javascript BBCode Parser
|
||||
* @author Philip Nicolcev
|
||||
* @license MIT License
|
||||
*/
|
||||
|
||||
var BBCodeParser = (function(parserTags, parserColors) {
|
||||
'use strict';
|
||||
|
||||
var me = {},
|
||||
urlPattern = /^(?:https?|file|c):(?:\/{1,3}|\\{1})[-a-zA-Z0-9:;@#%&()~_?\+=\/\\\.]*$/,
|
||||
emailPattern = /[^\s@]+@[^\s@]+\.[^\s@]+/,
|
||||
fontFacePattern = /^([a-z][a-z0-9_]+|"[a-z][a-z0-9_\s]+")$/i,
|
||||
tagNames = [],
|
||||
tagNamesNoParse = [],
|
||||
regExpAllowedColors,
|
||||
regExpValidHexColors = /^#?[a-fA-F0-9]{6}$/,
|
||||
ii, tagName, len;
|
||||
|
||||
// create tag list and lookup fields
|
||||
for (tagName in parserTags) {
|
||||
if (!parserTags.hasOwnProperty(tagName))
|
||||
continue;
|
||||
|
||||
if (tagName === '*') {
|
||||
tagNames.push('\\' + tagName);
|
||||
} else {
|
||||
tagNames.push(tagName);
|
||||
if ( parserTags[tagName].noParse ) {
|
||||
tagNamesNoParse.push(tagName);
|
||||
}
|
||||
}
|
||||
|
||||
parserTags[tagName].validChildLookup = {};
|
||||
parserTags[tagName].validParentLookup = {};
|
||||
parserTags[tagName].restrictParentsTo = parserTags[tagName].restrictParentsTo || [];
|
||||
parserTags[tagName].restrictChildrenTo = parserTags[tagName].restrictChildrenTo || [];
|
||||
|
||||
len = parserTags[tagName].restrictChildrenTo.length;
|
||||
for (ii = 0; ii < len; ii++) {
|
||||
parserTags[tagName].validChildLookup[ parserTags[tagName].restrictChildrenTo[ii] ] = true;
|
||||
}
|
||||
len = parserTags[tagName].restrictParentsTo.length;
|
||||
for (ii = 0; ii < len; ii++) {
|
||||
parserTags[tagName].validParentLookup[ parserTags[tagName].restrictParentsTo[ii] ] = true;
|
||||
}
|
||||
}
|
||||
|
||||
regExpAllowedColors = new RegExp('^(?:' + parserColors.join('|') + ')$');
|
||||
|
||||
/*
|
||||
* Create a regular expression that captures the innermost instance of a tag in an array of tags
|
||||
* The returned RegExp captures the following in order:
|
||||
* 1) the tag from the array that was matched
|
||||
* 2) all (optional) parameters included in the opening tag
|
||||
* 3) the contents surrounded by the tag
|
||||
*
|
||||
* @param {type} tagsArray - the array of tags to capture
|
||||
* @returns {RegExp}
|
||||
*/
|
||||
function createInnermostTagRegExp(tagsArray) {
|
||||
var openingTag = '\\[(' + tagsArray.join('|') + ')\\b(?:[ =]([\\w"#\\-\\:\\/= ]*?))?\\]',
|
||||
notContainingOpeningTag = '((?:(?=([^\\[]+))\\4|\\[(?!\\1\\b(?:[ =](?:[\\w"#\\-\\:\\/= ]*?))?\\]))*?)',
|
||||
closingTag = '\\[\\/\\1\\]';
|
||||
|
||||
return new RegExp( openingTag + notContainingOpeningTag + closingTag, 'i');
|
||||
}
|
||||
|
||||
/*
|
||||
* Escape the contents of a tag and mark the tag with a null unicode character.
|
||||
* To be used in a loop with a regular expression that captures tags.
|
||||
* Marking the tag prevents it from being matched again.
|
||||
*
|
||||
* @param {type} matchStr - the full match, including the opening and closing tags
|
||||
* @param {type} tagName - the tag that was matched
|
||||
* @param {type} tagParams - parameters passed to the tag
|
||||
* @param {type} tagContents - everything between the opening and closing tags
|
||||
* @returns {String} - the full match with the tag contents escaped and the tag marked with \u0000
|
||||
*/
|
||||
function escapeInnerTags(matchStr, tagName, tagParams, tagContents) {
|
||||
tagParams = tagParams || "";
|
||||
tagContents = tagContents || "";
|
||||
tagContents = tagContents.replace(/\[/g, "[").replace(/\]/g, "]");
|
||||
return "[\u0000" + tagName + tagParams + "]" + tagContents + "[/\u0000" + tagName + "]";
|
||||
}
|
||||
|
||||
/*
|
||||
* Escape all BBCodes that are inside the given tags.
|
||||
*
|
||||
* @param {string} text - the text to search through
|
||||
* @param {string[]} tags - the tags to search for
|
||||
* @returns {string} - the full text with the required code escaped
|
||||
*/
|
||||
function escapeBBCodesInsideTags(text, tags) {
|
||||
var innerMostRegExp;
|
||||
if (tags.length === 0 || text.length < 7)
|
||||
return text;
|
||||
innerMostRegExp = createInnermostTagRegExp(tags);
|
||||
while (
|
||||
text !== (text = text.replace(innerMostRegExp, escapeInnerTags))
|
||||
);
|
||||
return text.replace(/\u0000/g,'');
|
||||
}
|
||||
|
||||
/*
|
||||
* Process a tag and its contents according to the rules provided in parserTags.
|
||||
*
|
||||
* @param {type} matchStr - the full match, including the opening and closing tags
|
||||
* @param {type} tagName - the tag that was matched
|
||||
* @param {type} tagParams - parameters passed to the tag
|
||||
* @param {type} tagContents - everything between the opening and closing tags
|
||||
* @returns {string} - the fully processed tag and its contents
|
||||
*/
|
||||
function replaceTagsAndContent(matchStr, tagName, tagParams, tagContents) {
|
||||
tagName = tagName.toLowerCase();
|
||||
tagParams = tagParams || "";
|
||||
tagContents = tagContents || "";
|
||||
return parserTags[tagName].openTag(tagParams, tagContents) + (parserTags[tagName].content ? parserTags[tagName].content(tagParams, tagContents) : tagContents) + parserTags[tagName].closeTag(tagParams, tagContents);
|
||||
}
|
||||
|
||||
function processTags(text, tagNames) {
|
||||
var innerMostRegExp;
|
||||
|
||||
if (tagNames.length === 0 || text.length < 7)
|
||||
return text;
|
||||
|
||||
innerMostRegExp = createInnermostTagRegExp(tagNames);
|
||||
|
||||
while (
|
||||
text !== (text = text.replace(innerMostRegExp, replaceTagsAndContent))
|
||||
);
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
/*
|
||||
* Public Methods and Properties
|
||||
*/
|
||||
me.process = function(text, config) {
|
||||
text = escapeBBCodesInsideTags(text, tagNamesNoParse);
|
||||
|
||||
return processTags(text, tagNames);
|
||||
};
|
||||
|
||||
me.allowedTags = tagNames;
|
||||
me.urlPattern = urlPattern;
|
||||
me.emailPattern = emailPattern;
|
||||
me.regExpAllowedColors = regExpAllowedColors;
|
||||
me.regExpValidHexColors = regExpValidHexColors;
|
||||
|
||||
return me;
|
||||
})(parserTags, parserColors);
|
BIN
assets/3rdparty/pacman/BD_Cartoon_Shout-webfont.ttf
vendored
Normal file
BIN
assets/3rdparty/pacman/BD_Cartoon_Shout-webfont.ttf
vendored
Normal file
Binary file not shown.
13
assets/3rdparty/pacman/LICENSE
vendored
Normal file
13
assets/3rdparty/pacman/LICENSE
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
Version 2, December 2004
|
||||
|
||||
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim or modified
|
||||
copies of this license document, and changing it is allowed as long
|
||||
as the name is changed.
|
||||
|
||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. You just DO WHAT THE FUCK YOU WANT TO.
|
BIN
assets/3rdparty/pacman/audio/die.mp3
vendored
Normal file
BIN
assets/3rdparty/pacman/audio/die.mp3
vendored
Normal file
Binary file not shown.
BIN
assets/3rdparty/pacman/audio/die.ogg
vendored
Normal file
BIN
assets/3rdparty/pacman/audio/die.ogg
vendored
Normal file
Binary file not shown.
BIN
assets/3rdparty/pacman/audio/eatghost.mp3
vendored
Normal file
BIN
assets/3rdparty/pacman/audio/eatghost.mp3
vendored
Normal file
Binary file not shown.
BIN
assets/3rdparty/pacman/audio/eatghost.ogg
vendored
Normal file
BIN
assets/3rdparty/pacman/audio/eatghost.ogg
vendored
Normal file
Binary file not shown.
BIN
assets/3rdparty/pacman/audio/eating.mp3
vendored
Normal file
BIN
assets/3rdparty/pacman/audio/eating.mp3
vendored
Normal file
Binary file not shown.
BIN
assets/3rdparty/pacman/audio/eating.ogg
vendored
Normal file
BIN
assets/3rdparty/pacman/audio/eating.ogg
vendored
Normal file
Binary file not shown.
BIN
assets/3rdparty/pacman/audio/eating.short.mp3
vendored
Normal file
BIN
assets/3rdparty/pacman/audio/eating.short.mp3
vendored
Normal file
Binary file not shown.
BIN
assets/3rdparty/pacman/audio/eating.short.ogg
vendored
Normal file
BIN
assets/3rdparty/pacman/audio/eating.short.ogg
vendored
Normal file
Binary file not shown.
BIN
assets/3rdparty/pacman/audio/eatpill.mp3
vendored
Normal file
BIN
assets/3rdparty/pacman/audio/eatpill.mp3
vendored
Normal file
Binary file not shown.
BIN
assets/3rdparty/pacman/audio/eatpill.ogg
vendored
Normal file
BIN
assets/3rdparty/pacman/audio/eatpill.ogg
vendored
Normal file
Binary file not shown.
BIN
assets/3rdparty/pacman/audio/extra lives.mp3
vendored
Normal file
BIN
assets/3rdparty/pacman/audio/extra lives.mp3
vendored
Normal file
Binary file not shown.
BIN
assets/3rdparty/pacman/audio/extra lives.ogg
vendored
Normal file
BIN
assets/3rdparty/pacman/audio/extra lives.ogg
vendored
Normal file
Binary file not shown.
BIN
assets/3rdparty/pacman/audio/intermission.mp3
vendored
Normal file
BIN
assets/3rdparty/pacman/audio/intermission.mp3
vendored
Normal file
Binary file not shown.
BIN
assets/3rdparty/pacman/audio/intermission.ogg
vendored
Normal file
BIN
assets/3rdparty/pacman/audio/intermission.ogg
vendored
Normal file
Binary file not shown.
BIN
assets/3rdparty/pacman/audio/opening_song.mp3
vendored
Normal file
BIN
assets/3rdparty/pacman/audio/opening_song.mp3
vendored
Normal file
Binary file not shown.
BIN
assets/3rdparty/pacman/audio/opening_song.ogg
vendored
Normal file
BIN
assets/3rdparty/pacman/audio/opening_song.ogg
vendored
Normal file
Binary file not shown.
BIN
assets/3rdparty/pacman/audio/siren.mp3
vendored
Normal file
BIN
assets/3rdparty/pacman/audio/siren.mp3
vendored
Normal file
Binary file not shown.
BIN
assets/3rdparty/pacman/audio/siren.ogg
vendored
Normal file
BIN
assets/3rdparty/pacman/audio/siren.ogg
vendored
Normal file
Binary file not shown.
BIN
assets/3rdparty/pacman/audio/vcs_90.mp3
vendored
Normal file
BIN
assets/3rdparty/pacman/audio/vcs_90.mp3
vendored
Normal file
Binary file not shown.
BIN
assets/3rdparty/pacman/audio/vcs_90.ogg
vendored
Normal file
BIN
assets/3rdparty/pacman/audio/vcs_90.ogg
vendored
Normal file
Binary file not shown.
59
assets/3rdparty/pacman/index.html
vendored
Normal file
59
assets/3rdparty/pacman/index.html
vendored
Normal file
@ -0,0 +1,59 @@
|
||||
<!--
|
||||
WTFPL License
|
||||
Origin: https://github.com/daleharvey/pacman
|
||||
--><!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>HTML5 Pacman</title>
|
||||
|
||||
<style type="text/css">
|
||||
@font-face {
|
||||
font-family: 'BDCartoonShoutRegular';
|
||||
src: url('BD_Cartoon_Shout-webfont.ttf') format('truetype');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
#pacman {
|
||||
height:450px;
|
||||
width:342px;
|
||||
margin:0px auto;
|
||||
}
|
||||
#shim {
|
||||
font-family: BDCartoonShoutRegular;
|
||||
position:absolute;
|
||||
visibility:hidden
|
||||
}
|
||||
body {
|
||||
width:342px;
|
||||
margin:0px;
|
||||
font-family:sans-serif;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
|
||||
<div id="pacman"></div>
|
||||
<script src="pacman.js"></script>
|
||||
<script src="modernizr-1.5.min.js"></script>
|
||||
|
||||
<script>
|
||||
|
||||
var el = document.getElementById("pacman");
|
||||
|
||||
if (Modernizr.canvas && Modernizr.localstorage &&
|
||||
Modernizr.audio && (Modernizr.audio.ogg || Modernizr.audio.mp3)) {
|
||||
window.setTimeout(function () { PACMAN.init(el, "./"); }, 0);
|
||||
} else {
|
||||
el.innerHTML = "Sorry, needs a decent browser<br /><small>" +
|
||||
"(firefox 3.6+, Chrome 4+, Opera 10+ and Safari 4+)</small>";
|
||||
}
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
28
assets/3rdparty/pacman/modernizr-1.5.min.js
vendored
Normal file
28
assets/3rdparty/pacman/modernizr-1.5.min.js
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
/*!
|
||||
* Modernizr JavaScript library 1.5
|
||||
* http://www.modernizr.com/
|
||||
*
|
||||
* Copyright (c) 2009-2010 Faruk Ates - http://farukat.es/
|
||||
* Dual-licensed under the BSD and MIT licenses.
|
||||
* http://www.modernizr.com/license/
|
||||
*
|
||||
* Featuring major contributions by
|
||||
* Paul Irish - http://paulirish.com
|
||||
*/
|
||||
window.Modernizr=function(i,e,I){function C(a,b){for(var c in a)if(m[a[c]]!==I&&(!b||b(a[c],D)))return true}function r(a,b){var c=a.charAt(0).toUpperCase()+a.substr(1);return!!C([a,"Webkit"+c,"Moz"+c,"O"+c,"ms"+c,"Khtml"+c],b)}function P(){j[E]=function(a){for(var b=0,c=a.length;b<c;b++)J[a[b]]=!!(a[b]in n);return J}("autocomplete autofocus list placeholder max min multiple pattern required step".split(" "));j[Q]=function(a){for(var b=0,c,h=a.length;b<h;b++){n.setAttribute("type",a[b]);if(c=n.type!==
|
||||
"text"){n.value=K;/tel|search/.test(n.type)||(c=/url|email/.test(n.type)?n.checkValidity&&n.checkValidity()===false:n.value!=K)}L[a[b]]=!!c}return L}("search tel url email datetime date month week time datetime-local number range color".split(" "))}var j={},s=e.documentElement,D=e.createElement("modernizr"),m=D.style,n=e.createElement("input"),E="input",Q=E+"types",K=":)",M=Object.prototype.toString,y=" -o- -moz- -ms- -webkit- -khtml- ".split(" "),d={},L={},J={},N=[],u=function(){var a={select:"input",
|
||||
change:"input",submit:"form",reset:"form",error:"img",load:"img",abort:"img"},b={};return function(c,h){var t=arguments.length==1;if(t&&b[c])return b[c];h=h||document.createElement(a[c]||"div");c="on"+c;var g=c in h;if(!g&&h.setAttribute){h.setAttribute(c,"return;");g=typeof h[c]=="function"}h=null;return t?(b[c]=g):g}}(),F={}.hasOwnProperty,O;O=typeof F!=="undefined"&&typeof F.call!=="undefined"?function(a,b){return F.call(a,b)}:function(a,b){return b in a&&typeof a.constructor.prototype[b]==="undefined"};
|
||||
d.canvas=function(){return!!e.createElement("canvas").getContext};d.canvastext=function(){return!!(d.canvas()&&typeof e.createElement("canvas").getContext("2d").fillText=="function")};d.geolocation=function(){return!!navigator.geolocation};d.crosswindowmessaging=function(){return!!i.postMessage};d.websqldatabase=function(){var a=!!i.openDatabase;if(a)try{a=!!openDatabase("testdb","1.0","html5 test db",2E5)}catch(b){a=false}return a};d.indexedDB=function(){return!!i.indexedDB};d.hashchange=function(){return u("hashchange",
|
||||
i)&&(document.documentMode===I||document.documentMode>7)};d.historymanagement=function(){return!!(i.history&&history.pushState)};d.draganddrop=function(){return u("drag")&&u("dragstart")&&u("dragenter")&&u("dragover")&&u("dragleave")&&u("dragend")&&u("drop")};d.websockets=function(){return"WebSocket"in i};d.rgba=function(){m.cssText="background-color:rgba(150,255,150,.5)";return(""+m.backgroundColor).indexOf("rgba")!==-1};d.hsla=function(){m.cssText="background-color:hsla(120,40%,100%,.5)";return(""+
|
||||
m.backgroundColor).indexOf("rgba")!==-1};d.multiplebgs=function(){m.cssText="background:url(//:),url(//:),red url(//:)";return/(url\s*\(.*?){3}/.test(m.background)};d.backgroundsize=function(){return r("backgroundSize")};d.borderimage=function(){return r("borderImage")};d.borderradius=function(){return r("borderRadius","",function(a){return(""+a).indexOf("orderRadius")!==-1})};d.boxshadow=function(){return r("boxShadow")};d.opacity=function(){var a=y.join("opacity:.5;")+"";m.cssText=a;return(""+m.opacity).indexOf("0.5")!==
|
||||
-1};d.cssanimations=function(){return r("animationName")};d.csscolumns=function(){return r("columnCount")};d.cssgradients=function(){var a=("background-image:"+y.join("gradient(linear,left top,right bottom,from(#9f9),to(white));background-image:")+y.join("linear-gradient(left top,#9f9, white);background-image:")).slice(0,-17);m.cssText=a;return(""+m.backgroundImage).indexOf("gradient")!==-1};d.cssreflections=function(){return r("boxReflect")};d.csstransforms=function(){return!!C(["transformProperty",
|
||||
"WebkitTransform","MozTransform","OTransform","msTransform"])};d.csstransforms3d=function(){var a=!!C(["perspectiveProperty","WebkitPerspective","MozPerspective","OPerspective","msPerspective"]);if(a){var b=document.createElement("style"),c=e.createElement("div");b.textContent="@media ("+y.join("transform-3d),(")+"modernizr){#modernizr{height:3px}}";e.getElementsByTagName("head")[0].appendChild(b);c.id="modernizr";s.appendChild(c);a=c.offsetHeight===3;b.parentNode.removeChild(b);c.parentNode.removeChild(c)}return a};
|
||||
d.csstransitions=function(){return r("transitionProperty")};d.fontface=function(){var a;if(/*@cc_on@if(@_jscript_version>=5)!@end@*/0)a=true;else{var b=e.createElement("style"),c=e.createElement("span"),h,t=false,g=e.body,o,w;b.textContent="@font-face{font-family:testfont;src:url('data:font/ttf;base64,AAEAAAAMAIAAAwBAT1MvMliohmwAAADMAAAAVmNtYXCp5qrBAAABJAAAANhjdnQgACICiAAAAfwAAAAEZ2FzcP//AAMAAAIAAAAACGdseWYv5OZoAAACCAAAANxoZWFk69bnvwAAAuQAAAA2aGhlYQUJAt8AAAMcAAAAJGhtdHgGDgC4AAADQAAAABRsb2NhAIQAwgAAA1QAAAAMbWF4cABVANgAAANgAAAAIG5hbWUgXduAAAADgAAABPVwb3N03NkzmgAACHgAAAA4AAECBAEsAAUAAAKZAswAAACPApkCzAAAAesAMwEJAAACAAMDAAAAAAAAgAACbwAAAAoAAAAAAAAAAFBmRWQAAAAgqS8DM/8zAFwDMwDNAAAABQAAAAAAAAAAAAMAAAADAAAAHAABAAAAAABGAAMAAQAAAK4ABAAqAAAABgAEAAEAAgAuqQD//wAAAC6pAP///9ZXAwAAAAAAAAACAAAABgBoAAAAAAAvAAEAAAAAAAAAAAAAAAAAAAABAAIAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEACoAAAAGAAQAAQACAC6pAP//AAAALqkA////1lcDAAAAAAAAAAIAAAAiAogAAAAB//8AAgACACIAAAEyAqoAAwAHAC6xAQAvPLIHBADtMrEGBdw8sgMCAO0yALEDAC88sgUEAO0ysgcGAfw8sgECAO0yMxEhESczESMiARDuzMwCqv1WIgJmAAACAFUAAAIRAc0ADwAfAAATFRQWOwEyNj0BNCYrASIGARQGKwEiJj0BNDY7ATIWFX8aIvAiGhoi8CIaAZIoN/43KCg3/jcoAWD0JB4eJPQkHh7++EY2NkbVRjY2RgAAAAABAEH/+QCdAEEACQAANjQ2MzIWFAYjIkEeEA8fHw8QDxwWFhwWAAAAAQAAAAIAAIuYbWpfDzz1AAsEAAAAAADFn9IuAAAAAMWf0i797/8zA4gDMwAAAAgAAgAAAAAAAAABAAADM/8zAFwDx/3v/98DiAABAAAAAAAAAAAAAAAAAAAABQF2ACIAAAAAAVUAAAJmAFUA3QBBAAAAKgAqACoAWgBuAAEAAAAFAFAABwBUAAQAAgAAAAEAAQAAAEAALgADAAMAAAAQAMYAAQAAAAAAAACLAAAAAQAAAAAAAQAhAIsAAQAAAAAAAgAFAKwAAQAAAAAAAwBDALEAAQAAAAAABAAnAPQAAQAAAAAABQAKARsAAQAAAAAABgAmASUAAQAAAAAADgAaAUsAAwABBAkAAAEWAWUAAwABBAkAAQBCAnsAAwABBAkAAgAKAr0AAwABBAkAAwCGAscAAwABBAkABABOA00AAwABBAkABQAUA5sAAwABBAkABgBMA68AAwABBAkADgA0A/tDb3B5cmlnaHQgMjAwOSBieSBEYW5pZWwgSm9obnNvbi4gIFJlbGVhc2VkIHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgT3BlbiBGb250IExpY2Vuc2UuIEtheWFoIExpIGdseXBocyBhcmUgcmVsZWFzZWQgdW5kZXIgdGhlIEdQTCB2ZXJzaW9uIDMuYmFlYzJhOTJiZmZlNTAzMiAtIHN1YnNldCBvZiBKdXJhTGlnaHRiYWVjMmE5MmJmZmU1MDMyIC0gc3Vic2V0IG9mIEZvbnRGb3JnZSAyLjAgOiBKdXJhIExpZ2h0IDogMjMtMS0yMDA5YmFlYzJhOTJiZmZlNTAzMiAtIHN1YnNldCBvZiBKdXJhIExpZ2h0VmVyc2lvbiAyIGJhZWMyYTkyYmZmZTUwMzIgLSBzdWJzZXQgb2YgSnVyYUxpZ2h0aHR0cDovL3NjcmlwdHMuc2lsLm9yZy9PRkwAQwBvAHAAeQByAGkAZwBoAHQAIAAyADAAMAA5ACAAYgB5ACAARABhAG4AaQBlAGwAIABKAG8AaABuAHMAbwBuAC4AIAAgAFIAZQBsAGUAYQBzAGUAZAAgAHUAbgBkAGUAcgAgAHQAaABlACAAdABlAHIAbQBzACAAbwBmACAAdABoAGUAIABPAHAAZQBuACAARgBvAG4AdAAgAEwAaQBjAGUAbgBzAGUALgAgAEsAYQB5AGEAaAAgAEwAaQAgAGcAbAB5AHAAaABzACAAYQByAGUAIAByAGUAbABlAGEAcwBlAGQAIAB1AG4AZABlAHIAIAB0AGgAZQAgAEcAUABMACAAdgBlAHIAcwBpAG8AbgAgADMALgBiAGEAZQBjADIAYQA5ADIAYgBmAGYAZQA1ADAAMwAyACAALQAgAHMAdQBiAHMAZQB0ACAAbwBmACAASgB1AHIAYQBMAGkAZwBoAHQAYgBhAGUAYwAyAGEAOQAyAGIAZgBmAGUANQAwADMAMgAgAC0AIABzAHUAYgBzAGUAdAAgAG8AZgAgAEYAbwBuAHQARgBvAHIAZwBlACAAMgAuADAAIAA6ACAASgB1AHIAYQAgAEwAaQBnAGgAdAAgADoAIAAyADMALQAxAC0AMgAwADAAOQBiAGEAZQBjADIAYQA5ADIAYgBmAGYAZQA1ADAAMwAyACAALQAgAHMAdQBiAHMAZQB0ACAAbwBmACAASgB1AHIAYQAgAEwAaQBnAGgAdABWAGUAcgBzAGkAbwBuACAAMgAgAGIAYQBlAGMAMgBhADkAMgBiAGYAZgBlADUAMAAzADIAIAAtACAAcwB1AGIAcwBlAHQAIABvAGYAIABKAHUAcgBhAEwAaQBnAGgAdABoAHQAdABwADoALwAvAHMAYwByAGkAcAB0AHMALgBzAGkAbAAuAG8AcgBnAC8ATwBGAEwAAAAAAgAAAAAAAP+BADMAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAAAAAQACAQIAEQt6ZXJva2F5YWhsaQ==')}";
|
||||
e.getElementsByTagName("head")[0].appendChild(b);c.setAttribute("style","font:99px _,arial,helvetica;position:absolute;visibility:hidden");if(!g){g=s.appendChild(e.createElement("fontface"));t=true}c.innerHTML="........";c.id="fonttest";g.appendChild(c);h=c.offsetWidth*c.offsetHeight;c.style.font="99px testfont,_,arial,helvetica";a=h!==c.offsetWidth*c.offsetHeight;var v=function(){if(g.parentNode){a=j.fontface=h!==c.offsetWidth*c.offsetHeight;s.className=s.className.replace(/(no-)?fontface\b/,"")+
|
||||
(a?" ":" no-")+"fontface"}};setTimeout(v,75);setTimeout(v,150);addEventListener("load",function(){v();(w=true)&&o&&o(a);setTimeout(function(){t||(g=c);g.parentNode.removeChild(g);b.parentNode.removeChild(b)},50)},false)}j._fontfaceready=function(p){w||a?p(a):(o=p)};return a||h!==c.offsetWidth};d.video=function(){var a=e.createElement("video"),b=!!a.canPlayType;if(b){b=new Boolean(b);b.ogg=a.canPlayType('video/ogg; codecs="theora"');b.h264=a.canPlayType('video/mp4; codecs="avc1.42E01E"');b.webm=a.canPlayType('video/webm; codecs="vp8, vorbis"')}return b};
|
||||
d.audio=function(){var a=e.createElement("audio"),b=!!a.canPlayType;if(b){b=new Boolean(b);b.ogg=a.canPlayType('audio/ogg; codecs="vorbis"');b.mp3=a.canPlayType("audio/mpeg;");b.wav=a.canPlayType('audio/wav; codecs="1"');b.m4a=a.canPlayType("audio/x-m4a;")||a.canPlayType("audio/aac;")}return b};d.localStorage=function(){return"localStorage"in i&&i.localStorage!==null};d.sessionStorage=function(){try{return"sessionStorage"in i&&i.sessionStorage!==null}catch(a){return false}};d.webworkers=function(){return!!i.Worker};
|
||||
d.applicationCache=function(){var a=i.applicationCache;return!!(a&&typeof a.status!="undefined"&&typeof a.update=="function"&&typeof a.swapCache=="function")};d.svg=function(){return!!e.createElementNS&&!!e.createElementNS("http://www.w3.org/2000/svg","svg").createSVGRect};d.smil=function(){return!!e.createElementNS&&/SVG/.test(M.call(e.createElementNS("http://www.w3.org/2000/svg","animate")))};d.svgclippaths=function(){return!!e.createElementNS&&/SVG/.test(M.call(e.createElementNS("http://www.w3.org/2000/svg",
|
||||
"clipPath")))};for(var z in d)if(O(d,z))N.push(((j[z.toLowerCase()]=d[z]())?"":"no-")+z.toLowerCase());j[E]||P();j.addTest=function(a,b){a=a.toLowerCase();if(!j[a]){b=!!b();s.className+=" "+(b?"":"no-")+a;j[a]=b;return j}};m.cssText="";D=n=null;(function(){var a=e.createElement("div");a.innerHTML="<elem></elem>";return a.childNodes.length!==1})()&&function(a,b){function c(f,k){if(o[f])o[f].styleSheet.cssText+=k;else{var l=t[G],q=b[A]("style");q.media=f;l.insertBefore(q,l[G]);o[f]=q;c(f,k)}}function h(f,
|
||||
k){for(var l=new RegExp("\\b("+w+")\\b(?!.*[;}])","gi"),q=function(B){return".iepp_"+B},x=-1;++x<f.length;){k=f[x].media||k;h(f[x].imports,k);c(k,f[x].cssText.replace(l,q))}}for(var t=b.documentElement,g=b.createDocumentFragment(),o={},w="abbr|article|aside|audio|canvas|command|datalist|details|figure|figcaption|footer|header|hgroup|keygen|mark|meter|nav|output|progress|section|source|summary|time|video",v=w.split("|"),p=[],H=-1,G="firstChild",A="createElement";++H<v.length;){b[A](v[H]);g[A](v[H])}g=
|
||||
g.appendChild(b[A]("div"));a.attachEvent("onbeforeprint",function(){for(var f,k=b.getElementsByTagName("*"),l,q,x=new RegExp("^"+w+"$","i"),B=-1;++B<k.length;)if((f=k[B])&&(q=f.nodeName.match(x))){l=new RegExp("^\\s*<"+q+"(.*)\\/"+q+">\\s*$","i");g.innerHTML=f.outerHTML.replace(/\r|\n/g," ").replace(l,f.currentStyle.display=="block"?"<div$1/div>":"<span$1/span>");l=g.childNodes[0];l.className+=" iepp_"+q;l=p[p.length]=[f,l];f.parentNode.replaceChild(l[1],l[0])}h(b.styleSheets,"all")});a.attachEvent("onafterprint",
|
||||
function(){for(var f=-1,k;++f<p.length;)p[f][1].parentNode.replaceChild(p[f][0],p[f][1]);for(k in o)t[G].removeChild(o[k]);o={};p=[]})}(this,e);j._enableHTML5=true;j._version="1.5";s.className=s.className.replace(/\bno-js\b/,"")+" js";s.className+=" "+N.join(" ");return j}(this,this.document);
|
1270
assets/3rdparty/pacman/pacman.js
vendored
Normal file
1270
assets/3rdparty/pacman/pacman.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
3
assets/3rdparty/sceditor/formats/bbcode.js
vendored
Normal file
3
assets/3rdparty/sceditor/formats/bbcode.js
vendored
Normal file
File diff suppressed because one or more lines are too long
3
assets/3rdparty/sceditor/formats/xhtml.js
vendored
Normal file
3
assets/3rdparty/sceditor/formats/xhtml.js
vendored
Normal file
File diff suppressed because one or more lines are too long
3
assets/3rdparty/sceditor/icons/material.js
vendored
Normal file
3
assets/3rdparty/sceditor/icons/material.js
vendored
Normal file
File diff suppressed because one or more lines are too long
3
assets/3rdparty/sceditor/icons/monocons.js
vendored
Normal file
3
assets/3rdparty/sceditor/icons/monocons.js
vendored
Normal file
File diff suppressed because one or more lines are too long
3
assets/3rdparty/sceditor/jquery.sceditor.bbcode.min.js
vendored
Normal file
3
assets/3rdparty/sceditor/jquery.sceditor.bbcode.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
3
assets/3rdparty/sceditor/jquery.sceditor.min.js
vendored
Normal file
3
assets/3rdparty/sceditor/jquery.sceditor.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
3
assets/3rdparty/sceditor/jquery.sceditor.xhtml.min.js
vendored
Normal file
3
assets/3rdparty/sceditor/jquery.sceditor.xhtml.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
3
assets/3rdparty/sceditor/plugins/autosave.js
vendored
Normal file
3
assets/3rdparty/sceditor/plugins/autosave.js
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
/* SCEditor v2.1.3 | (C) 2017, Sam Clarke | sceditor.com/license */
|
||||
|
||||
!function(e){"use strict";var t="sce-autodraft-"+location.pathname+location.search;function i(e){localStorage.removeItem(e||t)}e.plugins.autosave=function(){var a,e=this,o=t,r=864e5,n=function(e){localStorage.setItem(o,JSON.stringify(e))},s=function(){return JSON.parse(localStorage.getItem(o))};e.init=function(){var e=(a=this).opts&&a.opts.autosave||{};n=e.save||n,s=e.load||s,o=e.storageKey||o,r=e.expires||r,function(){for(var e=0;e<localStorage.length;e++){var t=localStorage.key(e);if(/^sce\-autodraft\-/.test(t)){var a=JSON.parse(localStorage.getItem(o));a&&a.time<Date.now()-r&&i(t)}}}()},e.signalReady=function(){for(var e=a.getContentAreaContainer();e;){if(/form/i.test(e.nodeName)){e.addEventListener("submit",i.bind(null,o),!0);break}e=e.parentNode}var t=s();t&&(a.sourceMode(t.sourceMode),a.val(t.value,!1),a.focus(),t.sourceMode?a.sourceEditorCaret(t.caret):a.getRangeHelper().restoreRange()),n({caret:this.sourceEditorCaret(),sourceMode:this.sourceMode(),value:a.val(null,!1),time:Date.now()})},e.signalValuechangedEvent=function(e){n({caret:this.sourceEditorCaret(),sourceMode:this.sourceMode(),value:e.detail.rawValue,time:Date.now()})}},e.plugins.autosave.clear=i}(sceditor);
|
3
assets/3rdparty/sceditor/plugins/autoyoutube.js
vendored
Normal file
3
assets/3rdparty/sceditor/plugins/autoyoutube.js
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
/* SCEditor v2.1.3 | (C) 2017, Sam Clarke | sceditor.com/license */
|
||||
|
||||
!function(u,e){"use strict";var a=e.dom,c=/(^|\s)(?:https?:\/\/)?(?:www\.)?(?:youtu\.be\/|youtube\.com\/watch\?v=)([^"&?\/ ]{11})(?:\&[\&_\?0-9a-z\#]+)?(\s|$)/i;e.plugins.autoyoutube=function(){this.signalPasteRaw=function(e){if(!a.closest(this.currentNode(),"code")&&(e.html||e.text)){var t=u.createElement("div");e.html?t.innerHTML=e.html:t.textContent=e.text,function e(t){for(var o,n=t.firstChild;n;){if(3===n.nodeType){var i=n.nodeValue,r=n.parentNode,s=i.match(c);s&&(r.insertBefore(u.createTextNode(i.substr(0,s.index)+s[1]),n),r.insertBefore(a.parseHTML('<iframe width="560" height="315" frameborder="0" src="https://www.youtube-nocookie.com/embed/'+(o=s[2])+'" data-youtube-id="'+o+'" allowfullscreen></iframe>'),n),n.nodeValue=s[3]+i.substr(s.index+s[0].length))}else a.is(n,"code")||e(n);n=n.nextSibling}}(t),e.html=t.innerHTML}}}}(document,sceditor);
|
3
assets/3rdparty/sceditor/plugins/dragdrop.js
vendored
Normal file
3
assets/3rdparty/sceditor/plugins/dragdrop.js
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
/* SCEditor v2.1.3 | (C) 2017, Sam Clarke | sceditor.com/license */
|
||||
|
||||
!function(n){"use strict";var v="data:image/gif;base64,R0lGODlhlgBkAPABAH19ffb29iH5BAAKAAAAIf4aQ3JlYXRlZCB3aXRoIGFqYXhsb2FkLmluZm8AIf8LTkVUU0NBUEUyLjADAQAAACwAAAAAlgBkAAAC1YyPqcvtD6OctNqLs968+w+G4kiW5omm6sq27gvH8kzX9o3n+s73/g8MCofEovGITCqXzKbzCY1Kp9Sq9YrNarfcrvcLDovH5LL5jE6r1+y2+w2Py+f0uv2OvwD2fP6iD/gH6Pc2GIhg2JeQSNjGuLf4GMlYKIloefAIUEl52ZmJyaY5mUhqyFnqmQr6KRoaMKp66hbLumpQ69oK+5qrOyg4a6qYV2x8jJysvMzc7PwMHS09TV1tfY2drb3N3e39DR4uPk5ebn6Onq6+zt7u/g4fL99UAAAh+QQACgAAACwAAAAAlgBkAIEAAAB9fX329vYAAAAC3JSPqcvtD6OctNqLs968+w+G4kiW5omm6sq27gvH8kzX9o3n+s73/g8MCofEovGITCqXzKbzCY1Kp9Sq9YrNarfcrvcLDovH5LL5jE6r1+y2+w2Py+f0uv2OvwD2fP4iABgY+CcoCNeHuJdQyLjIaOiWiOj4CEhZ+SbZd/nI2RipqYhQOThKGpAZCuBZyArZprpqSupaCqtaazmLCRqai7rb2av5W5wqSShcm8fc7PwMHS09TV1tfY2drb3N3e39DR4uPk5ebn6Onq6+zt7u/g4fLz9PX29/j5/vVAAAIfkEAAoAAAAsAAAAAJYAZACBAAAAfX199vb2AAAAAuCUj6nL7Q+jnLTai7PevPsPhuJIluaJpurKtu4Lx/JM1/aN5/rO9/4PDAqHxKLxiEwql8ym8wmNSqfUqvWKzWq33K73Cw6Lx+Sy+YxOq9fstvsNj8vn9Lr9jr8E9nz+AgAYGLjQVwhXiJgguAiYgGjo9tinyCjoKLn3hpmJUGmJsBmguUnpCXCJOZraaXoKShoJe9DqehCqKlnqiZobuzrbyvuIO8xqKpxIPKlwrPCbBx0tPU1dbX2Nna29zd3t/Q0eLj5OXm5+jp6uvs7e7v4OHy8/T19vf4+fr7/P379UAAAh+QQACgAAACwAAAAAlgBkAIEAAAB9fX329vYAAAAC4JSPqcvtD6OctNqLs968+w+G4kiW5omm6sq27gvH8kzX9o3n+s73/g8MCofEovGITCqXzKbzCY1Kp9Sq9YrNarfcrvcLDovH5LL5jE6r1+y2+w2Py+f0uv2OvwT2fP6iD7gAMEhICAeImIAYiFDoOPi22KcouZfw6BhZGUBZeYlp6LbJiTD6CQqg6Vm6eQqqKtkZ24iaKtrKunpQa9tmmju7Wwu7KFtMi3oYDMzompkHHS09TV1tfY2drb3N3e39DR4uPk5ebn6Onq6+zt7u/g4fLz9PX29/j5+vv8/f31QAADs=",c=void 0!==window.FileReader,u=/data:[^;]+;base64,/i;function g(e){for(var t=e.substr(5,e.indexOf(";")-5),n=atob(e.substr(e.indexOf(",")+1)),r=new Uint8Array(n.length),i=0;i<n.length;i++)r[i]=n[i].charCodeAt(0);try{return new Blob([r],{type:t})}catch(e){return null}}n.plugins.dragdrop=function(){if(c){var A,r,o,i,a,s=0;this.signalReady=function(){A=(r=this).opts.dragdrop||{},o=A.handleFile,i=r.getContentAreaContainer().parentNode,a=i.appendChild(n.dom.parseHTML('<div class="sceditor-dnd-cover" style="display: none"><p>'+r._("Drop files here")+"</p></div>").firstChild),i.addEventListener("dragover",e),i.addEventListener("dragleave",d),i.addEventListener("dragend",d),i.addEventListener("drop",t),r.getBody().addEventListener("dragover",e),r.getBody().addEventListener("drop",d)},this.signalPasteHtml=function(e){if(!("handlePaste"in A)||A.handlePaste){var t=document.createElement("div");t.innerHTML=e.val;for(var n=t.querySelectorAll("img"),r=0;r<n.length;r++){var i=n[r];if(u.test(i.src)){var a=g(i.src);a&&l(a)?o(a,f(i)):i.parentNode.removeChild(i)}}e.val=t.innerHTML}}}function d(){a.style.display="none",i.className=i.className.replace(/(^| )dnd( |$)/g,"")}function l(e){return!("application/x-moz-file"!==e.type&&A.allowedTypes&&A.allowedTypes.indexOf(e.type)<0)&&(!A.isAllowed||A.isAllowed(e))}function f(e){var n=document.createElement("img");function t(e){var t=r.getBody().ownerDocument.getElementById(n.id);t&&("string"==typeof e&&t.insertAdjacentHTML("afterend",e),t.parentNode.removeChild(t))}return n.src=v,n.className="sceditor-ignore",n.id="sce-dragdrop-"+s++,function(){return e?e.parentNode.replaceChild(n,e):r.wysiwygEditorInsertHtml(n.outerHTML),{insert:function(e){t(e)},cancel:t}}}function e(e){for(var t=e.dataTransfer,n=t.files.length||!t.items?t.files:t.items,r=0;r<n.length;r++)if("string"===n[r].kind)return;"none"===a.style.display&&(a.style.display="block",i.className+=" dnd"),e.preventDefault()}function t(e){var t=e.dataTransfer,n=t.files.length||!t.items?t.files:t.items;d();for(var r=0;r<n.length;r++){if("string"===n[r].kind)return;l(n[r])&&o(n[r],f())}e.preventDefault()}}}(sceditor);
|
3
assets/3rdparty/sceditor/plugins/format.js
vendored
Normal file
3
assets/3rdparty/sceditor/plugins/format.js
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
/* SCEditor v2.1.3 | (C) 2017, Sam Clarke | sceditor.com/license */
|
||||
|
||||
!function(i){"use strict";i.plugins.format=function(){var n,a,c={p:"Paragraph",h1:"Heading 1",h2:"Heading 2",h3:"Heading 3",h4:"Heading 4",h5:"Heading 5",h6:"Heading 6",address:"Address",pre:"Preformatted Text"};this.init=function(){var e=this.opts,t=e.paragraphformat;e.format&&"bbcode"===e.format||(t&&(t.tags&&(c=t.tags),t.excludeTags&&t.excludeTags.forEach(function(e){delete c[e]})),this.commands.format||(this.commands.format={exec:a,txtExec:a,tooltip:"Format Paragraph"}),e.toolbar===i.defaultOptions.toolbar&&(e.toolbar=e.toolbar.replace(",color,",",color,format,")))},n=function(e,t){e.sourceMode()?e.insert("<"+t+">","</"+t+">"):e.execCommand("formatblock","<"+t+">")},a=function(e){var o=this,r=document.createElement("div");i.utils.each(c,function(t,a){var e=document.createElement("a");e.className="sceditor-option",e.textContent=a.name||a,e.addEventListener("click",function(e){o.closeDropDown(!0),a.exec?a.exec(o):n(o,t),e.preventDefault()}),r.appendChild(e)}),o.createDropDown(e,"format",r)}}}(sceditor);
|
3
assets/3rdparty/sceditor/plugins/plaintext.js
vendored
Normal file
3
assets/3rdparty/sceditor/plugins/plaintext.js
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
/* SCEditor v2.1.3 | (C) 2017, Sam Clarke | sceditor.com/license */
|
||||
|
||||
!function(t){"use strict";var i=t.utils.extend;t.plugins.plaintext=function(){var e=!0;this.init=function(){var t=this.commands,n=this.opts;n&&n.plaintext&&n.plaintext.addButton&&(e=n.plaintext.enabled,t.pastetext=i(t.pastetext||{},{state:function(){return e?1:0},exec:function(){e=!e}}))},this.signalPasteRaw=function(t){if(e){if(t.html&&!t.text){var n=document.createElement("div");n.innerHTML=t.html,t.text=n.innerText}t.html=null}}}}(sceditor);
|
2
assets/3rdparty/sceditor/plugins/strictbbcode.js
vendored
Normal file
2
assets/3rdparty/sceditor/plugins/strictbbcode.js
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
/* SCEditor v2.1.3 | (C) 2017, Sam Clarke | sceditor.com/license */
|
||||
|
3
assets/3rdparty/sceditor/plugins/undo.js
vendored
Normal file
3
assets/3rdparty/sceditor/plugins/undo.js
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
/* SCEditor v2.1.3 | (C) 2017, Sam Clarke | sceditor.com/license */
|
||||
|
||||
!function(e){"use strict";sceditor.plugins.undo=function(){var r,o,e=this,u=0,a=50,n=[],c=[],s=!1,l=function(e){s=!0,o=e.value,r.sourceMode(e.sourceMode),r.val(e.value,!1),r.focus(),e.sourceMode?r.sourceEditorCaret(e.caret):r.getRangeHelper().restoreRange(),s=!1};e.init=function(){a=(r=this).undoLimit||a,r.addShortcut("ctrl+z",e.undo),r.addShortcut("ctrl+shift+z",e.redo),r.addShortcut("ctrl+y",e.redo)},e.undo=function(){var e=c.pop(),t=r.val(null,!1);return e&&!n.length&&t===e.value&&(e=c.pop()),e&&(n.length||n.push({caret:r.sourceEditorCaret(),sourceMode:r.sourceMode(),value:t}),n.push(e),l(e)),!1},e.redo=function(){var e=n.pop();return c.length||(c.push(e),e=n.pop()),e&&(c.push(e),l(e)),!1},e.signalReady=function(){var e=r.val(null,!1);o=e,c.push({caret:this.sourceEditorCaret(),sourceMode:this.sourceMode(),value:e})},e.signalValuechangedEvent=function(e){var t=e.detail.rawValue;0<a&&c.length>a&&c.shift(),!s&&o&&o!==t&&(n.length=0,(u+=function(e,t){var r,o,u,a,n=e.length,c=t.length,s=Math.max(n,c);for(r=0;r<s&&e.charAt(r)===t.charAt(r);r++);for(u=n<c?c-n:0,a=c<n?n-c:0,o=s-1;0<=o&&e.charAt(o-u)===t.charAt(o-a);o--);return o-r+1}(o,t))<20||u<50&&!/\s$/g.test(e.rawValue)||(c.push({caret:r.sourceEditorCaret(),sourceMode:r.sourceMode(),value:t}),u=0,o=t))}}}();
|
3
assets/3rdparty/sceditor/plugins/v1compat.js
vendored
Normal file
3
assets/3rdparty/sceditor/plugins/v1compat.js
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
/* SCEditor v2.1.3 | (C) 2017, Sam Clarke | sceditor.com/license */
|
||||
|
||||
!function(t,r){"use strict";var e=t.plugins;function n(c){if(c._scePatched)return c;var t=function(){for(var t=[],e=0;e<arguments.length;e++){var n=arguments[e];n&&n.nodeType?t.push(r(n)):t.push(n)}return c.apply(this,t)};return t._scePatched=!0,t}function c(t){if(t._scePatched)return t;var e=function(){return r(t.apply(this,arguments))};return e._scePatched=!0,e}var o=t.command.set;if(t.command.set=function(t,e){return e&&"function"==typeof e.exec&&(e.exec=n(e.exec)),e&&"function"==typeof e.txtExec&&(e.txtExec=n(e.txtExec)),o.call(this,t,e)},e.bbcode){var a=e.bbcode.bbcode.set;e.bbcode.bbcode.set=function(t,e){return e&&"function"==typeof e.format&&(e.format=n(e.format)),a.call(this,t,e)}}var i=t.create;t.create=function(t,e){if(i.call(this,t,e),t&&t._sceditor){var n=t._sceditor;n.getBody=c(n.getBody),n.getContentAreaContainer=c(n.getContentAreaContainer)}}}(sceditor,jQuery);
|
3
assets/3rdparty/sceditor/sceditor.min.js
vendored
Normal file
3
assets/3rdparty/sceditor/sceditor.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
assets/3rdparty/sceditor/themes/content/default.min.css
vendored
Normal file
1
assets/3rdparty/sceditor/themes/content/default.min.css
vendored
Normal file
@ -0,0 +1 @@
|
||||
/*! SCEditor | (C) 2011-2013, Sam Clarke | sceditor.com/license */body,code:before,html,p,table{margin:0;padding:0;font-family:Verdana,Arial,Helvetica,sans-serif;font-size:14px;color:#111;line-height:1.25;overflow:visible}html{height:100%}.ios{overflow:auto;-webkit-overflow-scrolling:touch}.ios body{position:relative;overflow:auto}body{min-height:100%;word-wrap:break-word}body.placeholder::before{content:attr(placeholder);color:#555;font-style:italic}ol,ul{margin-top:0;margin-bottom:0;padding-top:0;padding-bottom:0}table,td{border:1px dotted #000;empty-cells:show}table td{min-width:5px}code{display:block;background:#f1f1f1;white-space:pre;padding:1em;text-align:left;margin:.25em 0;direction:ltr}blockquote{background:#fff7d9;margin:.25em 0;border-left:.3em solid #f4e59f;padding:.5em .5em .5em .75em}blockquote cite{font-weight:700;display:block;font-size:1em;margin:0 -.5em .25em -.75em;padding:0 .5em .15em .75em;border-bottom:1px solid #f4e59f}h1,h2,h3,h4,h5,h6{padding:0;margin:0}div,p{min-height:1.25em}
|
1
assets/3rdparty/sceditor/themes/default.min.css
vendored
Normal file
1
assets/3rdparty/sceditor/themes/default.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
assets/3rdparty/sceditor/themes/defaultdark.min.css
vendored
Normal file
1
assets/3rdparty/sceditor/themes/defaultdark.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
BIN
assets/3rdparty/sceditor/themes/famfamfam.png
vendored
Normal file
BIN
assets/3rdparty/sceditor/themes/famfamfam.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.5 KiB |
1
assets/3rdparty/sceditor/themes/modern.min.css
vendored
Normal file
1
assets/3rdparty/sceditor/themes/modern.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
assets/3rdparty/sceditor/themes/office-toolbar.min.css
vendored
Normal file
1
assets/3rdparty/sceditor/themes/office-toolbar.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
assets/3rdparty/sceditor/themes/office.min.css
vendored
Normal file
1
assets/3rdparty/sceditor/themes/office.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
assets/3rdparty/sceditor/themes/square.min.css
vendored
Normal file
1
assets/3rdparty/sceditor/themes/square.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
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/notif_song.mp3
Normal file
BIN
assets/audio/notif_song.mp3
Normal file
Binary file not shown.
BIN
assets/audio/notif_song.ogg
Normal file
BIN
assets/audio/notif_song.ogg
Normal file
Binary file not shown.
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.
551
assets/css/common/custom-sceditor.css
Normal file
551
assets/css/common/custom-sceditor.css
Normal file
@ -0,0 +1,551 @@
|
||||
/*! SCEditor | (C) 2011-2016, Sam Clarke | sceditor.com/license */
|
||||
/**
|
||||
* Default SCEditor
|
||||
* http://www.sceditor.com/
|
||||
*
|
||||
* Copyright (C) 2011-16, Sam Clarke
|
||||
*
|
||||
* SCEditor is licensed under the MIT license:
|
||||
* http://www.opensource.org/licenses/mit-license.php
|
||||
*
|
||||
* This version of the theme has been adapated by Pierre HUBERT for Comunic
|
||||
* Modifications: Copyright (c) 2018-2019 Pierre HUBERT (MIT License)
|
||||
*/
|
||||
div.sceditor-grip,
|
||||
.sceditor-button div {
|
||||
background-image: url("famfamfam.png");
|
||||
background-repeat: no-repeat;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.sceditor-button-youtube div {
|
||||
background-position: 0px 0px;
|
||||
}
|
||||
.sceditor-button-link div {
|
||||
background-position: 0px -16px;
|
||||
}
|
||||
.sceditor-button-unlink div {
|
||||
background-position: 0px -32px;
|
||||
}
|
||||
.sceditor-button-underline div {
|
||||
background-position: 0px -48px;
|
||||
}
|
||||
.sceditor-button-time div {
|
||||
background-position: 0px -64px;
|
||||
}
|
||||
.sceditor-button-table div {
|
||||
background-position: 0px -80px;
|
||||
}
|
||||
.sceditor-button-superscript div {
|
||||
background-position: 0px -96px;
|
||||
}
|
||||
.sceditor-button-subscript div {
|
||||
background-position: 0px -112px;
|
||||
}
|
||||
.sceditor-button-strike div {
|
||||
background-position: 0px -128px;
|
||||
}
|
||||
.sceditor-button-source div {
|
||||
background-position: 0px -144px;
|
||||
}
|
||||
.sceditor-button-size div {
|
||||
background-position: 0px -160px;
|
||||
}
|
||||
.sceditor-button-rtl div {
|
||||
background-position: 0px -176px;
|
||||
}
|
||||
.sceditor-button-right div {
|
||||
background-position: 0px -192px;
|
||||
}
|
||||
.sceditor-button-removeformat div {
|
||||
background-position: 0px -208px;
|
||||
}
|
||||
.sceditor-button-quote div {
|
||||
background-position: 0px -224px;
|
||||
}
|
||||
.sceditor-button-print div {
|
||||
background-position: 0px -240px;
|
||||
}
|
||||
.sceditor-button-pastetext div {
|
||||
background-position: 0px -256px;
|
||||
}
|
||||
.sceditor-button-paste div {
|
||||
background-position: 0px -272px;
|
||||
}
|
||||
.sceditor-button-outdent div {
|
||||
background-position: 0px -288px;
|
||||
}
|
||||
.sceditor-button-orderedlist div {
|
||||
background-position: 0px -304px;
|
||||
}
|
||||
.sceditor-button-maximize div {
|
||||
background-position: 0px -320px;
|
||||
}
|
||||
.sceditor-button-ltr div {
|
||||
background-position: 0px -336px;
|
||||
}
|
||||
.sceditor-button-left div {
|
||||
background-position: 0px -352px;
|
||||
}
|
||||
.sceditor-button-justify div {
|
||||
background-position: 0px -368px;
|
||||
}
|
||||
.sceditor-button-italic div {
|
||||
background-position: 0px -384px;
|
||||
}
|
||||
.sceditor-button-indent div {
|
||||
background-position: 0px -400px;
|
||||
}
|
||||
.sceditor-button-image div {
|
||||
background-position: 0px -416px;
|
||||
}
|
||||
.sceditor-button-horizontalrule div {
|
||||
background-position: 0px -432px;
|
||||
}
|
||||
.sceditor-button-format div {
|
||||
background-position: 0px -448px;
|
||||
}
|
||||
.sceditor-button-font div {
|
||||
background-position: 0px -464px;
|
||||
}
|
||||
.sceditor-button-emoticon div {
|
||||
background-position: 0px -480px;
|
||||
}
|
||||
.sceditor-button-email div {
|
||||
background-position: 0px -496px;
|
||||
}
|
||||
.sceditor-button-date div {
|
||||
background-position: 0px -512px;
|
||||
}
|
||||
.sceditor-button-cut div {
|
||||
background-position: 0px -528px;
|
||||
}
|
||||
.sceditor-button-copy div {
|
||||
background-position: 0px -544px;
|
||||
}
|
||||
.sceditor-button-color div {
|
||||
background-position: 0px -560px;
|
||||
}
|
||||
.sceditor-button-code div {
|
||||
background-position: 0px -576px;
|
||||
}
|
||||
.sceditor-button-center div {
|
||||
background-position: 0px -592px;
|
||||
}
|
||||
.sceditor-button-bulletlist div {
|
||||
background-position: 0px -608px;
|
||||
}
|
||||
.sceditor-button-bold div {
|
||||
background-position: 0px -624px;
|
||||
}
|
||||
div.sceditor-grip {
|
||||
background-position: 0px -640px;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
}
|
||||
.rtl div.sceditor-grip {
|
||||
background-position: 0px -650px;
|
||||
}
|
||||
/**
|
||||
* SCEditor
|
||||
* http://www.sceditor.com/
|
||||
*
|
||||
* Copyright (C) 2017, Sam Clarke (samclarke.com)
|
||||
*
|
||||
* SCEditor is licensed under the MIT license:
|
||||
* http://www.opensource.org/licenses/mit-license.php
|
||||
*/
|
||||
/*---------------------------------------------------
|
||||
LESS Elements 0.7
|
||||
---------------------------------------------------
|
||||
A set of useful LESS mixins
|
||||
More info at: http://lesselements.com
|
||||
---------------------------------------------------*/
|
||||
.sceditor-container {
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-ms-flex-direction: column;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
/*background: #fff;
|
||||
border: 1px solid #d9d9d9;
|
||||
font-size: 13px;
|
||||
font-family: Arial, "Helvetica Neue", Helvetica, sans-serif;
|
||||
color: #333;
|
||||
line-height: 1;
|
||||
font-weight: bold;
|
||||
height: 250px;
|
||||
border-radius: 4px;
|
||||
background-clip: padding-box;*/
|
||||
width: 100%;
|
||||
min-height: 100px;
|
||||
font-size: 14px;
|
||||
line-height: 18px;
|
||||
border: 1px solid #dddddd;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.sceditor-container *,
|
||||
.sceditor-container *:before,
|
||||
.sceditor-container *:after {
|
||||
-webkit-box-sizing: content-box;
|
||||
-moz-box-sizing: content-box;
|
||||
box-sizing: content-box;
|
||||
}
|
||||
.sceditor-container,
|
||||
.sceditor-container div,
|
||||
div.sceditor-dropdown,
|
||||
div.sceditor-dropdown div {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
z-index: 3;
|
||||
}
|
||||
.sceditor-container iframe,
|
||||
.sceditor-container textarea {
|
||||
display: block;
|
||||
-ms-flex: 1 1 0%;
|
||||
flex: 1 1 0%;
|
||||
line-height: 1.25;
|
||||
border: 0;
|
||||
outline: none;
|
||||
font-family: Verdana, Arial, Helvetica, sans-serif;
|
||||
font-size: 14px;
|
||||
color: #111;
|
||||
padding: 0;
|
||||
margin: 0px;
|
||||
resize: none;
|
||||
background: #fff;
|
||||
height: auto !important;
|
||||
width: auto !important;
|
||||
width: calc(100% - 10px) !important;
|
||||
min-height: 1px;
|
||||
}
|
||||
.sceditor-container textarea {
|
||||
margin: 7px 5px;
|
||||
}
|
||||
div.sceditor-dnd-cover {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border: 5px dashed #aaa;
|
||||
z-index: 200;
|
||||
font-size: 2em;
|
||||
text-align: center;
|
||||
color: #aaa;
|
||||
}
|
||||
div.sceditor-dnd-cover p {
|
||||
position: relative;
|
||||
top: 45%;
|
||||
pointer-events: none;
|
||||
}
|
||||
div.sceditor-resize-cover {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background: #000;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 10;
|
||||
opacity: 0.3;
|
||||
}
|
||||
div.sceditor-grip {
|
||||
overflow: hidden;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
z-index: 3;
|
||||
line-height: 0;
|
||||
}
|
||||
div.sceditor-grip.has-icon {
|
||||
background-image: none;
|
||||
}
|
||||
.sceditor-maximize {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100% !important;
|
||||
width: 100% !important;
|
||||
border-radius: 0;
|
||||
background-clip: padding-box;
|
||||
z-index: 2000;
|
||||
}
|
||||
html.sceditor-maximize,
|
||||
body.sceditor-maximize {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
.sceditor-maximize div.sceditor-grip {
|
||||
display: none;
|
||||
}
|
||||
.sceditor-maximize div.sceditor-toolbar {
|
||||
border-radius: 0;
|
||||
background-clip: padding-box;
|
||||
}
|
||||
/**
|
||||
* Dropdown styleing
|
||||
*/
|
||||
div.sceditor-dropdown {
|
||||
position: absolute;
|
||||
border: 1px solid #ccc;
|
||||
background: #fff;
|
||||
z-index: 4000;
|
||||
padding: 10px;
|
||||
font-weight: normal;
|
||||
font-size: 15px;
|
||||
border-radius: 2px;
|
||||
background-clip: padding-box;
|
||||
box-shadow: 1px 2px 4px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
div.sceditor-dropdown *,
|
||||
div.sceditor-dropdown *:before,
|
||||
div.sceditor-dropdown *:after {
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
div.sceditor-dropdown a,
|
||||
div.sceditor-dropdown a:link {
|
||||
color: #333;
|
||||
}
|
||||
div.sceditor-dropdown form {
|
||||
margin: 0;
|
||||
}
|
||||
div.sceditor-dropdown label {
|
||||
display: block;
|
||||
font-weight: bold;
|
||||
color: #3c3c3c;
|
||||
padding: 4px 0;
|
||||
}
|
||||
div.sceditor-dropdown input,
|
||||
div.sceditor-dropdown textarea {
|
||||
font-family: Arial, "Helvetica Neue", Helvetica, sans-serif;
|
||||
outline: 0;
|
||||
padding: 4px;
|
||||
border: 1px solid #ccc;
|
||||
border-top-color: #888;
|
||||
margin: 0 0 .75em;
|
||||
border-radius: 1px;
|
||||
background-clip: padding-box;
|
||||
}
|
||||
div.sceditor-dropdown textarea {
|
||||
padding: 6px;
|
||||
}
|
||||
div.sceditor-dropdown input:focus,
|
||||
div.sceditor-dropdown textarea:focus {
|
||||
border-color: #aaa;
|
||||
border-top-color: #666;
|
||||
box-shadow: inset 0 1px 5px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
div.sceditor-dropdown .button {
|
||||
font-weight: bold;
|
||||
color: #444;
|
||||
padding: 6px 12px;
|
||||
background: #ececec;
|
||||
border: solid 1px #ccc;
|
||||
border-radius: 2px;
|
||||
background-clip: padding-box;
|
||||
cursor: pointer;
|
||||
margin: .3em 0 0;
|
||||
}
|
||||
div.sceditor-dropdown .button:hover {
|
||||
background: #f3f3f3;
|
||||
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
div.sceditor-font-picker,
|
||||
div.sceditor-fontsize-picker,
|
||||
div.sceditor-format {
|
||||
padding: 6px 0;
|
||||
}
|
||||
div.sceditor-color-picker {
|
||||
padding: 4px;
|
||||
}
|
||||
div.sceditor-emoticons,
|
||||
div.sceditor-more-emoticons {
|
||||
padding: 0;
|
||||
}
|
||||
.sceditor-pastetext textarea {
|
||||
border: 1px solid #bbb;
|
||||
width: 20em;
|
||||
}
|
||||
.sceditor-emoticons img,
|
||||
.sceditor-more-emoticons img {
|
||||
padding: 0;
|
||||
cursor: pointer;
|
||||
margin: 2px;
|
||||
}
|
||||
.sceditor-more {
|
||||
border-top: 1px solid #bbb;
|
||||
display: block;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
padding: 6px 0;
|
||||
}
|
||||
.sceditor-dropdown a:hover {
|
||||
background: #eee;
|
||||
}
|
||||
.sceditor-fontsize-option,
|
||||
.sceditor-font-option,
|
||||
.sceditor-format a {
|
||||
display: block;
|
||||
padding: 7px 10px;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
color: #222;
|
||||
}
|
||||
.sceditor-fontsize-option {
|
||||
padding: 7px 13px;
|
||||
}
|
||||
.sceditor-color-column {
|
||||
float: left;
|
||||
}
|
||||
.sceditor-color-option {
|
||||
display: block;
|
||||
border: 2px solid #fff;
|
||||
height: 18px;
|
||||
width: 18px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.sceditor-color-option:hover {
|
||||
border: 1px solid #aaa;
|
||||
}
|
||||
/**
|
||||
* Toolbar styleing
|
||||
*/
|
||||
div.sceditor-toolbar {
|
||||
flex-shrink: 0;
|
||||
overflow: hidden;
|
||||
padding: 3px 5px 2px;
|
||||
/*background: #f7f7f7;
|
||||
border-bottom: 1px solid #c0c0c0;*/
|
||||
line-height: 0;
|
||||
text-align: left;
|
||||
user-select: none;
|
||||
border-radius: 3px 3px 0 0;
|
||||
background-clip: padding-box;
|
||||
}
|
||||
div.sceditor-group {
|
||||
display: inline-block;
|
||||
/*background: #ddd;*/
|
||||
margin: 1px 5px 1px 0;
|
||||
padding: 1px;
|
||||
/*border-bottom: 1px solid #aaa;*/
|
||||
border-radius: 3px;
|
||||
background-clip: padding-box;
|
||||
}
|
||||
.sceditor-button {
|
||||
float: left;
|
||||
cursor: pointer;
|
||||
padding: 3px 5px;
|
||||
width: 22px;
|
||||
height: 20px;
|
||||
background-clip: padding-box;
|
||||
background: #ddd;
|
||||
display: flex !important;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.sceditor-button:first-child {
|
||||
border-top-left-radius: 3px;
|
||||
border-bottom-left-radius: 3px;
|
||||
}
|
||||
.sceditor-button:last-child {
|
||||
border-top-right-radius: 3px;
|
||||
border-bottom-right-radius: 3px;
|
||||
}
|
||||
.sceditor-button:hover,
|
||||
.sceditor-button:active,
|
||||
.sceditor-button.active {
|
||||
background: #fff;
|
||||
box-shadow: inset 1px 1px 0 rgba(0,0,0,0.3), inset -1px 0 rgba(0,0,0,0.3), inset 0 -1px 0 rgba(0,0,0,0.2);
|
||||
}
|
||||
.sceditor-button:active {
|
||||
background: #fff;
|
||||
box-shadow: inset 1px 1px 0 rgba(0,0,0,0.3), inset -1px 0 rgba(0,0,0,0.3), inset 0 -1px 0 rgba(0,0,0,0.2), inset 0 0 8px rgba(0,0,0,0.3);
|
||||
}
|
||||
.sceditor-button.disabled:hover {
|
||||
background: inherit;
|
||||
cursor: default;
|
||||
box-shadow: none;
|
||||
}
|
||||
.sceditor-button,
|
||||
.sceditor-button div {
|
||||
display: block;
|
||||
}
|
||||
.sceditor-button svg {
|
||||
display: inline-block;
|
||||
height: 12px;
|
||||
width: 12px;
|
||||
margin: 2px 0;
|
||||
fill: #111;
|
||||
text-decoration: none;
|
||||
pointer-events: none;
|
||||
line-height: 1;
|
||||
}
|
||||
.sceditor-button.disabled svg {
|
||||
fill: #888;
|
||||
}
|
||||
.sceditor-button div {
|
||||
display: inline-block;
|
||||
margin: 2px 0;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
line-height: 0;
|
||||
font-size: 0;
|
||||
color: transparent;
|
||||
}
|
||||
.sceditor-button.has-icon div {
|
||||
display: none;
|
||||
}
|
||||
.sceditor-button.disabled div {
|
||||
opacity: 0.3;
|
||||
}
|
||||
.text .sceditor-button,
|
||||
.text .sceditor-button div,
|
||||
.sceditor-button.text,
|
||||
.sceditor-button.text div,
|
||||
.text-icon .sceditor-button,
|
||||
.text-icon .sceditor-button div,
|
||||
.sceditor-button.text-icon,
|
||||
.sceditor-button.text-icon div {
|
||||
display: inline-block;
|
||||
width: auto;
|
||||
line-height: 16px;
|
||||
font-size: 1em;
|
||||
color: inherit;
|
||||
text-indent: 0;
|
||||
}
|
||||
.text-icon .sceditor-button.has-icon div,
|
||||
.sceditor-button.has-icon div,
|
||||
.text .sceditor-button div,
|
||||
.sceditor-button.text div {
|
||||
padding: 0 2px;
|
||||
background: none;
|
||||
}
|
||||
.text .sceditor-button svg,
|
||||
.sceditor-button.text svg {
|
||||
display: none;
|
||||
}
|
||||
.text-icon .sceditor-button div,
|
||||
.sceditor-button.text-icon div {
|
||||
padding: 0 2px 0 20px;
|
||||
}
|
||||
.rtl div.sceditor-toolbar {
|
||||
text-align: right;
|
||||
}
|
||||
.rtl .sceditor-button {
|
||||
float: right;
|
||||
}
|
||||
.rtl div.sceditor-grip {
|
||||
right: auto;
|
||||
left: 0;
|
||||
}
|
||||
|
@ -23,4 +23,16 @@ a {
|
||||
|
||||
.a:focus, .a:hover {
|
||||
color: #72afd2;
|
||||
}
|
||||
|
||||
.cursor-pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sceditor iframe
|
||||
*/
|
||||
.sceditor-iframe-body {
|
||||
padding: 5px;
|
||||
padding-bottom: 0px;
|
||||
}
|
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
|
||||
*/
|
||||
@ -115,4 +123,8 @@
|
||||
|
||||
#conversationsElem .direct-chat-msg.not-last-message-from-user .direct-chat-img {
|
||||
margin-bottom: -5px;
|
||||
}
|
||||
|
||||
#conversationsElem .direct-chat-msg.open .dropdown-menu {
|
||||
margin-top: -20px;
|
||||
}
|
23
assets/css/components/incognito/ui.css
Normal file
23
assets/css/components/incognito/ui.css
Normal file
@ -0,0 +1,23 @@
|
||||
/**
|
||||
* Incognito mode stylesheet
|
||||
*
|
||||
* @author Pierre HUBERT
|
||||
*/
|
||||
|
||||
#incognito-block {
|
||||
position: fixed;
|
||||
left: 10px;
|
||||
bottom: 54px;
|
||||
text-align: center;
|
||||
background-color: #001F3F;
|
||||
padding: 10px;
|
||||
color: white;
|
||||
}
|
||||
|
||||
#incognito-block i {
|
||||
font-size: 200%;
|
||||
}
|
||||
|
||||
#incognito-block span {
|
||||
display: block;
|
||||
}
|
16
assets/css/components/pacman.css
Normal file
16
assets/css/components/pacman.css
Normal file
@ -0,0 +1,16 @@
|
||||
/**
|
||||
* Pacman stylesheet
|
||||
*
|
||||
* @author Pierre HUBERT
|
||||
*/
|
||||
|
||||
.pacman-iframe {
|
||||
width: 342px;
|
||||
height: 426px;
|
||||
margin: auto;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.pacman-iframe + p {
|
||||
text-align: center;
|
||||
}
|
@ -17,6 +17,9 @@
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.post-form .new-message-content-container {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
/**
|
||||
* Message type chooser
|
||||
|
@ -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;
|
||||
|
626
assets/css/dark_theme.css
Normal file
626
assets/css/dark_theme.css
Normal file
@ -0,0 +1,626 @@
|
||||
/**
|
||||
* ComunicWeb dark theme
|
||||
*
|
||||
* @author Pierre HUBERT
|
||||
*/
|
||||
|
||||
/**
|
||||
* General definitions
|
||||
*/
|
||||
:root {
|
||||
--black: #1119;
|
||||
--black2: #111111;
|
||||
--black3: #000;
|
||||
--black4: #686a6c;
|
||||
--black5: #232426;
|
||||
--black6: #0f0f0f;
|
||||
--white: silver;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: var(--black6);
|
||||
}
|
||||
|
||||
p, h1, h2, h3, h4, h5, h6 {
|
||||
color: var(--white);
|
||||
}
|
||||
|
||||
.content-wrapper {
|
||||
background-color: var(--black3);
|
||||
}
|
||||
|
||||
.link-black {
|
||||
color: var(--white);
|
||||
}
|
||||
|
||||
.table-hover > tbody > tr:hover {
|
||||
background-color: var(--black3);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Buttons
|
||||
*/
|
||||
.btn {
|
||||
color: var(--white);
|
||||
}
|
||||
|
||||
.btn-primary,
|
||||
.btn-success,
|
||||
.btn-default,
|
||||
.btn-danger,
|
||||
.btn-info {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.btn-default:active, .btn-default:focus {
|
||||
background-color: var(--white);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Callouts
|
||||
*/
|
||||
.callout.callout-info {
|
||||
background-color: var(--black-5) !important;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Forms
|
||||
*/
|
||||
.form-control {
|
||||
background-color: var(--black);
|
||||
color: var(--white);
|
||||
border-color: var(--black5);
|
||||
}
|
||||
|
||||
.form-control:focus {
|
||||
background-color: var(--black5);
|
||||
border-color: var(--black4);
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
color: var(--white);
|
||||
}
|
||||
|
||||
.form-control[disabled],
|
||||
.form-control[readonly],
|
||||
fieldset[disabled] .form-control {
|
||||
background-color: var(--black6);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Boxes
|
||||
*/
|
||||
.box {
|
||||
background-color: var(--black5);
|
||||
color: var(--white);
|
||||
}
|
||||
|
||||
.box-footer {
|
||||
background-color: var(--black5);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Modals
|
||||
*/
|
||||
.modal-header,
|
||||
.modal-footer {
|
||||
background-color: var(--black5);
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
background-color: var(--black6);
|
||||
}
|
||||
|
||||
.modal-primary .modal-header,
|
||||
.modal-primary .modal-footer {
|
||||
background-color: var(--black5) !important;;
|
||||
}
|
||||
|
||||
.modal-primary .modal-body {
|
||||
background-color: var(--black6) !important;
|
||||
}
|
||||
|
||||
.modal-danger .modal-header,
|
||||
.modal-danger .modal-footer {
|
||||
background-color: #a22819 !important;
|
||||
}
|
||||
|
||||
.modal-danger .modal-body {
|
||||
background-color: #872f24 !important;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dropdown menus
|
||||
*/
|
||||
.dropdown-menu {
|
||||
background-color: var(--black5) !important;
|
||||
}
|
||||
|
||||
.dropdown-menu > li > a {
|
||||
color: var(--white) !important;
|
||||
}
|
||||
|
||||
.dropdown-menu > li > a:hover {
|
||||
background-color: var(--black3) !important;
|
||||
}
|
||||
|
||||
.dropdown {
|
||||
color: var(--white);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Navbar
|
||||
*/
|
||||
.navbar {
|
||||
background-color: var(--black6) !important;;
|
||||
}
|
||||
|
||||
.navbar-nav > .notifications-menu > .dropdown-menu > li.header,
|
||||
.navbar-nav > .messages-menu > .dropdown-menu > li.header,
|
||||
.navbar-nav > .tasks-menu > .dropdown-menu > li.header {
|
||||
background-color: var(--black6);
|
||||
color: var(--white);
|
||||
}
|
||||
|
||||
.navbar-nav > .notifications-menu > .dropdown-menu > li.footer > a,
|
||||
.navbar-nav > .messages-menu > .dropdown-menu > li.footer > a,
|
||||
.navbar-nav > .tasks-menu > .dropdown-menu > li.footer > a {
|
||||
background-color: var(--black6) !important;
|
||||
color: var(--white) !important;
|
||||
}
|
||||
|
||||
.main-header #navbar-search-input.form-control:focus,
|
||||
.main-header #navbar-search-input.form-control:active {
|
||||
background-color: var(--black3);
|
||||
}
|
||||
|
||||
.navbar-nav > .messages-menu > .dropdown-menu > li .menu > li > a > h4 {
|
||||
color: var(--white);
|
||||
}
|
||||
|
||||
.navbar-nav > .notifications-menu > .dropdown-menu > li .menu > li > a:hover,
|
||||
.navbar-nav > .messages-menu > .dropdown-menu > li .menu > li > a:hover,
|
||||
.navbar-nav > .tasks-menu > .dropdown-menu > li .menu > li > a:hover {
|
||||
background-color: var(--black6);
|
||||
}
|
||||
|
||||
.skin-blue .main-header .navbar .dropdown-user-menu-action .dropdown-menu li a {
|
||||
color: var(--white) !important;;
|
||||
}
|
||||
|
||||
.skin-blue .main-header .navbar .nav > li > a:hover,
|
||||
.skin-blue .main-header .navbar .nav > li > a:active,
|
||||
.skin-blue .main-header .navbar .nav > li > a:focus,
|
||||
.skin-blue .main-header .navbar .nav .open > a,
|
||||
.skin-blue .main-header .navbar .nav .open > a:hover,
|
||||
.skin-blue .main-header .navbar .nav .open > a:focus,
|
||||
.skin-blue .main-header .navbar .nav > .active > a {
|
||||
background-color: var(--black4);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Footer
|
||||
*/
|
||||
.main-footer {
|
||||
background-color: var(--black3);
|
||||
}
|
||||
|
||||
.main-footer span {
|
||||
color: var(--white);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Friends list
|
||||
*/
|
||||
@media screen and (min-width: 1200px) {
|
||||
|
||||
#friendsList {
|
||||
background-color: var(--black6);
|
||||
}
|
||||
|
||||
#friendsList:hover {
|
||||
background-color: var(--black5);
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1200px) and (min-width: 513px) {
|
||||
#friendsList {
|
||||
background-color: var(--black5);
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 513px) {
|
||||
#friendsList {
|
||||
background-color: var(--black5);
|
||||
}
|
||||
}
|
||||
|
||||
#friendsList h4 {
|
||||
color: var(--white);
|
||||
}
|
||||
|
||||
#friendsList li:hover {
|
||||
background-color: var(--black4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emoji picker
|
||||
*/
|
||||
.wdt-emoji-popup {
|
||||
background-color: var(--black6);
|
||||
border: 1px var(--black6) solid;
|
||||
border-radius: 2;
|
||||
}
|
||||
|
||||
#wdt-emoji-menu-header {
|
||||
background-color: var(--black5);
|
||||
}
|
||||
|
||||
#wdt-emoji-footer {
|
||||
background-color: var(--black5);
|
||||
}
|
||||
|
||||
.wdt-emoji-popup h3 {
|
||||
background-color: var(--black4);
|
||||
color: var(--white);
|
||||
}
|
||||
|
||||
#wdt-emoji-search {
|
||||
background-color: var(--black5);
|
||||
color: var(--white);
|
||||
}
|
||||
|
||||
#wdt-emoji-search:focus {
|
||||
background-color: var(--black4);
|
||||
color: var(--white);
|
||||
}
|
||||
|
||||
.wdt-emoji:hover {
|
||||
background-color: var(--black4) !important;;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sceditor
|
||||
*/
|
||||
.sceditor-iframe-body {
|
||||
background-color: var(--black5);
|
||||
color: var(--white);
|
||||
}
|
||||
|
||||
.sceditor-container textarea {
|
||||
background-color: var(--black5);
|
||||
color: var(--white);
|
||||
}
|
||||
|
||||
.sceditor-button {
|
||||
background-color: var(--black6);
|
||||
}
|
||||
|
||||
.sceditor-button svg {
|
||||
fill: var(--black4);
|
||||
}
|
||||
|
||||
.sceditor-button:hover,
|
||||
.sceditor-button:active,
|
||||
.sceditor-button.active {
|
||||
background-color: var(--black4);
|
||||
color: white;
|
||||
}
|
||||
|
||||
|
||||
.sceditor-button:hover svg,
|
||||
.sceditor-button:active svg,
|
||||
.sceditor-button.active svg {
|
||||
fill: var(--black6);
|
||||
}
|
||||
|
||||
div.sceditor-dropdown {
|
||||
background-color: var(--black5);
|
||||
}
|
||||
|
||||
div.sceditor-dropdown input {
|
||||
background-color: var(--black4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Conversations
|
||||
*/
|
||||
.open-conversation-button {
|
||||
background-color: var(--black5);
|
||||
}
|
||||
|
||||
.nav-stacked > li > a {
|
||||
color: var(--white);
|
||||
}
|
||||
|
||||
.conversations-list-box .last-activity {
|
||||
color: var(--white);
|
||||
}
|
||||
|
||||
.nav > li > a:hover,
|
||||
.nav > li > a:active,
|
||||
.nav > li > a:focus {
|
||||
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);
|
||||
}
|
||||
|
||||
#conversationsElem .create-message-form .btn.btn-add-emoji:hover,
|
||||
#conversationsElem .create-message-form .btn.btn-add-image:hover {
|
||||
color: var(--white);
|
||||
}
|
||||
|
||||
.conversation-settings-pane {
|
||||
background-color: var(--black5) !important;
|
||||
}
|
||||
|
||||
/**
|
||||
* Home page
|
||||
*/
|
||||
#homeLandingScreen {
|
||||
background-image: none !important;
|
||||
background-color: var(--black5);
|
||||
}
|
||||
|
||||
#homeLandingScreen #homeMessage {
|
||||
background-color: var(--black);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Login page
|
||||
*/
|
||||
.login-page, .register-page {
|
||||
background-color: var(--black2);
|
||||
}
|
||||
|
||||
.login-box-body {
|
||||
background-color: var(--black3);
|
||||
}
|
||||
|
||||
.login-logo b, #loginForm label {
|
||||
color: var(--white) !important;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create account page
|
||||
*/
|
||||
.create-account-form label {
|
||||
color: var(--white);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* User information
|
||||
*/
|
||||
.list-group-item {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Posts
|
||||
*/
|
||||
.attachment-block {
|
||||
background-color: var(--black6);
|
||||
}
|
||||
|
||||
.post-comments {
|
||||
background-color: var(--black);
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.post {
|
||||
color: var(--white);
|
||||
}
|
||||
|
||||
.post-image {
|
||||
filter: brightness(80%);
|
||||
transition: filter 1s;
|
||||
}
|
||||
|
||||
.post-image:hover {
|
||||
filter: brightness(100%);
|
||||
}
|
||||
|
||||
.post-youtube-placeholder {
|
||||
background-color: var(--black6) !important;
|
||||
}
|
||||
|
||||
.box-comments .username,
|
||||
.box-comments .comment-content {
|
||||
color: var(--white);
|
||||
}
|
||||
|
||||
|
||||
.comment-creation-form .comment-image-select a,
|
||||
.comment-creation-form .comment-emoji-select a {
|
||||
color: var(--white);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Posts creation form
|
||||
*/
|
||||
|
||||
.post-form-choice span {
|
||||
color: var(--black4);
|
||||
}
|
||||
|
||||
.post-form-choice span span {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.post-form .post-form-choice input:checked ~ span {
|
||||
color: var(--white);
|
||||
}
|
||||
|
||||
.datepicker.dropdown-menu {
|
||||
background-color: var(--black6);
|
||||
color: var(--white);
|
||||
}
|
||||
|
||||
.select2-container--default .select2-selection--multiple {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.post-form .post-visiblity-container span {
|
||||
color: var(--black4);
|
||||
}
|
||||
|
||||
.post-form .post-visiblity-container input:checked ~ span {
|
||||
color: var(--white);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Posts edit modal
|
||||
*/
|
||||
.editor {
|
||||
background-color: var(--black5);
|
||||
color: var(--white);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Search page
|
||||
*/
|
||||
.nav > li > a:hover,
|
||||
.nav > li > a:active,
|
||||
.nav > li > a:focus {
|
||||
background-color: var(--black4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Conversation page
|
||||
*/
|
||||
.conversations-page-container a {
|
||||
background-color: var(--black6) !important;;
|
||||
}
|
||||
|
||||
.conversations-page-container a {
|
||||
color: var(--white) !important;
|
||||
}
|
||||
|
||||
.conversations-page-container a:hover {
|
||||
background-color: var(--black5) !important;;
|
||||
}
|
||||
|
||||
.conversations-page-container a:active {
|
||||
background-color: var(--black6) !important;;
|
||||
}
|
||||
|
||||
.conversations-page-container a.selected {
|
||||
background-color: var(--black5) !important;;
|
||||
}
|
||||
|
||||
.conversations-page-container .box-conversation .input-group-btn .btn {
|
||||
background-color: var(--black5);
|
||||
}
|
||||
|
||||
.conversations-page-container .box-conversation .input-group-btn .btn:hover {
|
||||
background-color: var(--black4);
|
||||
color: var(--black6);
|
||||
}
|
||||
|
||||
.conversations-page-container .box-conversation .input-group-btn .btn:focus,
|
||||
.conversations-page-container .box-conversation .input-group-btn .btn:active {
|
||||
color: var(--white);
|
||||
}
|
||||
|
||||
.big-box-conversation .direct-chat-text .a,
|
||||
.big-box-conversation .direct-chat-text a {
|
||||
background-color: transparent !important;
|
||||
color: inherit !important;
|
||||
}
|
||||
|
||||
/**
|
||||
* Groups logo
|
||||
*/
|
||||
img[src$="groups_logo/default.png"] {
|
||||
filter: invert(1) brightness(50%);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read only friends list
|
||||
*/
|
||||
.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);
|
||||
}
|
@ -11,6 +11,11 @@
|
||||
padding-top: 50px;
|
||||
}
|
||||
|
||||
.groups-main-page .no-group-notice {
|
||||
margin-top: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.groups-main-page .group-item {
|
||||
text-align: justify;
|
||||
margin-top: 10px;
|
||||
|
@ -18,6 +18,11 @@
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.group-members-page .invite-user-form {
|
||||
margin-top: 30px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.group-members-page .member {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
@ -35,4 +35,13 @@
|
||||
margin: auto;
|
||||
margin-bottom: 10px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.group-settings-container .delete-group-link-container {
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.group-settings-container .delete-group-link-container a {
|
||||
color: black;
|
||||
}
|
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;
|
@ -33,6 +33,10 @@ ComunicWeb.common.api = {
|
||||
|
||||
}
|
||||
|
||||
//Enable incognito mode if required
|
||||
if(ComunicWeb.components.incognito.management.isEnabled())
|
||||
params.incognito = true;
|
||||
|
||||
//Prepare data to send in request
|
||||
var count = 0;
|
||||
var datas = "";
|
||||
@ -90,7 +94,11 @@ ComunicWeb.common.api = {
|
||||
data.append('userToken2', tokens.token2);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//Enable incognito mode if required
|
||||
if(ComunicWeb.components.incognito.management.isEnabled())
|
||||
data.append("incognito", true);
|
||||
|
||||
//Create request
|
||||
var apiXHR = new XMLHttpRequest();
|
||||
|
@ -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;
|
||||
|
@ -109,6 +109,10 @@ ComunicWeb.common.error.pageNotFound = function(additionnalData, targetElement){
|
||||
*/
|
||||
ComunicWeb.common.error.syntaxtError = function(error, additional){
|
||||
|
||||
//Do not do anything in production mode
|
||||
if(ComunicWeb.__config.productionMode == true)
|
||||
return;
|
||||
|
||||
//Create a modal dialog to report error
|
||||
var dialog = ComunicWeb.common.messages.createDialogSkeleton({
|
||||
type: "danger",
|
||||
|
@ -118,6 +118,12 @@ var ComunicWeb = {
|
||||
* Prompt the user to input a string
|
||||
*/
|
||||
inputString: function(title, message, defaultValue, callback){},
|
||||
|
||||
/**
|
||||
* Prompt the user to enter his password
|
||||
*/
|
||||
promptPassword: function(info){},
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
@ -222,6 +228,22 @@ var ComunicWeb = {
|
||||
getAndShowJSONtemplate: function(targetElem, templateURI, additionalData, afterParsingJSONtemplate, cleanContainer){},
|
||||
},
|
||||
|
||||
/**
|
||||
* Page title management
|
||||
*/
|
||||
pageTitle: {
|
||||
|
||||
/**
|
||||
* Set a new title to the page
|
||||
*/
|
||||
setTitle: function(title){},
|
||||
|
||||
/**
|
||||
* Set new number of notifications
|
||||
*/
|
||||
setNotificationsNumber: function(number){}
|
||||
},
|
||||
|
||||
/**
|
||||
* Functions to check data input in forms
|
||||
*/
|
||||
@ -570,6 +592,13 @@ var ComunicWeb = {
|
||||
*/
|
||||
bottom: {
|
||||
|
||||
/**
|
||||
* Bottom links
|
||||
*/
|
||||
links: [
|
||||
//TODO : implement
|
||||
],
|
||||
|
||||
/**
|
||||
* Main bottom script file
|
||||
*/
|
||||
@ -781,7 +810,14 @@ var ComunicWeb = {
|
||||
*/
|
||||
unreadDropdown: {
|
||||
//TODO : implementd
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Conversation message editor
|
||||
*/
|
||||
messageEditor: {
|
||||
//TODO : implement
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
@ -1016,6 +1052,13 @@ var ComunicWeb = {
|
||||
//TODO : implement
|
||||
},
|
||||
|
||||
/**
|
||||
* Notification song system
|
||||
*/
|
||||
song: {
|
||||
//TODO : implement
|
||||
},
|
||||
|
||||
/**
|
||||
* Notifications utilities
|
||||
*/
|
||||
@ -1065,6 +1108,108 @@ var ComunicWeb = {
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Dark Theme component
|
||||
*/
|
||||
darkTheme: {
|
||||
//TODO : implement
|
||||
},
|
||||
|
||||
/**
|
||||
* Incognito mode component
|
||||
*/
|
||||
incognito: {
|
||||
|
||||
/**
|
||||
* Keyboard catcher
|
||||
*/
|
||||
keyboard: {
|
||||
//TODO : implement
|
||||
},
|
||||
|
||||
/**
|
||||
* Incognito management
|
||||
*/
|
||||
management: {
|
||||
//TODO : implement
|
||||
},
|
||||
|
||||
/**
|
||||
* UI management
|
||||
*/
|
||||
ui: {
|
||||
//TODO : implement
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
pacman: {
|
||||
//TODO : implement
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -382,4 +382,85 @@ ComunicWeb.common.messages.inputString = function(title, message, defaultValue,
|
||||
//Show the modal
|
||||
$(modal).modal('show');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Prompt the user to input his password
|
||||
*
|
||||
* @param {Object} info Additionnal information
|
||||
*/
|
||||
ComunicWeb.common.messages.promptPassword = function(info){
|
||||
|
||||
var dialog = ComunicWeb.common.messages.createDialogSkeleton({
|
||||
type: "danger",
|
||||
title: "Password required"
|
||||
});
|
||||
$(dialog.modal).modal("show");
|
||||
|
||||
//Create modal close function
|
||||
var closeModal = function(e, password){
|
||||
$(dialog.modal).modal("hide");
|
||||
emptyElem(dialog.modal);
|
||||
dialog.modal.remove();
|
||||
|
||||
//Callback
|
||||
if(info.callback)
|
||||
info.callback(password);
|
||||
};
|
||||
dialog.cancelButton.addEventListener("click", closeModal);
|
||||
dialog.closeModal.addEventListener("click", closeModal);
|
||||
|
||||
//Set dialog body
|
||||
var passwordForm = createElem2({
|
||||
appendTo: dialog.modalBody,
|
||||
type: "div"
|
||||
});
|
||||
|
||||
createElem2({
|
||||
appendTo: passwordForm,
|
||||
type: "p",
|
||||
innerHTML: "We need your password to continue."
|
||||
});
|
||||
|
||||
//Create pasword input group
|
||||
var inputGroup = createElem2({
|
||||
appendTo: passwordForm,
|
||||
type: "div",
|
||||
class: "input-group input-group-sm"
|
||||
});
|
||||
|
||||
//Create password input
|
||||
var passwordInput = createElem2({
|
||||
appendTo: inputGroup,
|
||||
type: "input",
|
||||
class: "form-control",
|
||||
elemType: "password"
|
||||
});
|
||||
|
||||
//Create input group
|
||||
var inputGroupContainer = createElem2({
|
||||
appendTo: inputGroup,
|
||||
type: "span",
|
||||
class: "input-group-btn"
|
||||
});
|
||||
|
||||
//Add submit button
|
||||
var submitButton = createElem2({
|
||||
appendTo: inputGroupContainer,
|
||||
type: "button",
|
||||
class: "btn btn-danger",
|
||||
innerHTML: "Confirm deletion"
|
||||
});
|
||||
|
||||
submitButton.addEventListener("click", function(e){
|
||||
|
||||
//Check given password
|
||||
var password = passwordInput.value;
|
||||
if(password.length < 4)
|
||||
return notify("Please check given password !", "danger");
|
||||
|
||||
//Close modal
|
||||
closeModal(null, password);
|
||||
|
||||
});
|
||||
}
|
@ -196,7 +196,7 @@ ComunicWeb.common.page = {
|
||||
}
|
||||
|
||||
//Change page title
|
||||
document.title = pageInfos.pageTitle;
|
||||
ComunicWeb.common.pageTitle.setTitle(pageInfos.pageTitle);
|
||||
|
||||
//Change page URL, if required
|
||||
if(additionnalData.no_url_update ? !additionnalData.no_url_update : true)
|
||||
@ -277,6 +277,11 @@ ComunicWeb.common.page = {
|
||||
|
||||
//Call the method related to the page
|
||||
eval(pageInfos.methodHandler + ("(additionnalData, pageTarget);"));
|
||||
|
||||
//Propagate information
|
||||
SendEvent("openPage", {
|
||||
page: pageURI
|
||||
});
|
||||
|
||||
//Success
|
||||
return true;
|
||||
|
53
assets/js/common/pageTitle.js
Normal file
53
assets/js/common/pageTitle.js
Normal file
@ -0,0 +1,53 @@
|
||||
/**
|
||||
* Page title management
|
||||
*
|
||||
* @author Pierre HUBERT
|
||||
*/
|
||||
|
||||
ComunicWeb.common.pageTitle = {
|
||||
|
||||
/**
|
||||
* Current page title
|
||||
*/
|
||||
_curr_title: "Comunic",
|
||||
|
||||
/**
|
||||
* Current number of notifications
|
||||
*/
|
||||
_curr_notifications_number: 0,
|
||||
|
||||
/**
|
||||
* Set a new title to the page
|
||||
*
|
||||
* @param {string} title The new title for the page
|
||||
*/
|
||||
setTitle: function(title){
|
||||
this._curr_title = title;
|
||||
this.__refresh();
|
||||
},
|
||||
|
||||
/**
|
||||
* Set new number of notifications
|
||||
*
|
||||
* @param {number} number The new number of notifications
|
||||
*/
|
||||
setNotificationsNumber: function(number){
|
||||
this._curr_notifications_number = number;
|
||||
this.__refresh();
|
||||
},
|
||||
|
||||
/**
|
||||
* Refresh document title
|
||||
*/
|
||||
__refresh: function(){
|
||||
var title = "";
|
||||
|
||||
if(this._curr_notifications_number > 0)
|
||||
title += "(" + this._curr_notifications_number + ") ";
|
||||
|
||||
title += this._curr_title;
|
||||
|
||||
document.title = title;
|
||||
}
|
||||
|
||||
}
|
@ -167,7 +167,8 @@ function getInfoGroup(id, callback){
|
||||
*
|
||||
* @param {Array} IDs The IDs of the groups to get information about
|
||||
* @param {Function} callback Callback to call once we have information about the group
|
||||
* @param {Boolean} force TRUE to force the request (ignore cache)
|
||||
*/
|
||||
function getInfoMultipleGroups(IDs, callback){
|
||||
ComunicWeb.components.groups.info.getInfoMultiple(IDs, callback);
|
||||
function getInfoMultipleGroups(IDs, callback, force){
|
||||
ComunicWeb.components.groups.info.getInfoMultiple(IDs, callback, force);
|
||||
}
|
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;
|
||||
}
|
||||
}
|
@ -48,6 +48,21 @@ ComunicWeb.common.system = {
|
||||
*/
|
||||
ComunicWeb.common.langs.initLanguages();
|
||||
|
||||
/**
|
||||
* Initialize incognito mode detection
|
||||
*/
|
||||
ComunicWeb.components.incognito.management.init();
|
||||
|
||||
/**
|
||||
* Refresh dark theme mode
|
||||
*/
|
||||
ComunicWeb.components.darkTheme.refresh();
|
||||
|
||||
/**
|
||||
* Initialize call system
|
||||
*/
|
||||
ComunicWeb.components.calls.controller.init();
|
||||
|
||||
/**
|
||||
* What to do after login refresh
|
||||
*/
|
||||
|
@ -40,6 +40,7 @@ function createElem(nodeType, appendTo){
|
||||
* @info {String} placeholder The placeholder of the new element
|
||||
* @info {String} innerHTML Specify the html content of the newly created element
|
||||
* @info {String} innerLang Specify the key of the lang to use to fill the element
|
||||
* @info {String} innerHTMLprefix Specify prefix to add at the begining of the content of the element
|
||||
* @info {boolean} disabled Set whether the field should be disabled or not (input only)
|
||||
* @return {HTMLElement} The newly created element
|
||||
*/
|
||||
@ -105,6 +106,9 @@ function createElem2(infos){
|
||||
|
||||
if(infos.innerLang)
|
||||
newElem.innerHTML = lang(infos.innerLang);
|
||||
|
||||
if(infos.innerHTMLprefix)
|
||||
newElem.innerHTML = infos.innerHTMLprefix + newElem.innerHTML;
|
||||
|
||||
//Set field state
|
||||
if(infos.disabled)
|
||||
@ -416,6 +420,10 @@ function checkString(value){
|
||||
*/
|
||||
function removeHtmlTags(input){
|
||||
|
||||
//Check if input string is empty
|
||||
if(input == null || typeof input !== "string")
|
||||
return "";
|
||||
|
||||
//Prepare update
|
||||
var output = input;
|
||||
|
||||
@ -435,6 +443,25 @@ function removeHtmlTags(input){
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace all line break with paragraph tags
|
||||
*
|
||||
* @param {string} input Input string to convert
|
||||
* @return {string} Generated string
|
||||
*/
|
||||
function lineBreakToPTags(input){
|
||||
|
||||
//Check if the string is empty
|
||||
if(input == null || input == "")
|
||||
return input;
|
||||
|
||||
//Change string
|
||||
while(input.includes("\n"))
|
||||
input = input.replace("\n", "</p><p>");
|
||||
|
||||
return "<p>"+input+"</p>";
|
||||
}
|
||||
|
||||
/**
|
||||
* Check a URL validity
|
||||
*
|
||||
@ -592,4 +619,138 @@ function dataURItoBlob(dataURI){
|
||||
|
||||
return new Blob([ia], {type: mimeString});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Satinize some HTML source code by removing all javascript event detectors
|
||||
* from it
|
||||
*
|
||||
* @param {string} html The source code to update
|
||||
* @return {string} Secured html
|
||||
*/
|
||||
function removeJavascriptEventsFromHTML(html){
|
||||
|
||||
//Check if the string to check is null (we will consider
|
||||
//at safe in this case)
|
||||
if(html == null)
|
||||
return html;
|
||||
|
||||
//Search for unexceptable references
|
||||
while(html.match(/on[a-zA-Z ]+=/i) != null){
|
||||
var match = html.match(/on[a-zA-Z ]+=/i)[0];
|
||||
html = html.replace(match, match.replace("on", "o<block></block>n"))
|
||||
}
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get and return the DOM of a specified iframe
|
||||
*
|
||||
* @param {HTMLIFrameElement} iframe The iframe to process
|
||||
* @return {HTMLDocument} DOM of the iframe
|
||||
*/
|
||||
function GetIframeDOM(iframe){
|
||||
return iframe.contentWindow
|
||||
? iframe.contentWindow.document
|
||||
: iframe.contentDocument;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize styles for a sceditor textarea
|
||||
*
|
||||
* @param {HTMLTextAreaElement} textarea Target textarea element that
|
||||
* have sceditor initialized
|
||||
*/
|
||||
function ApplySceditorStyle(textarea){
|
||||
|
||||
//Get iframe DOM
|
||||
var iframeDOM = GetIframeDOM(textarea.parentNode.getElementsByTagName("iframe")[0]);
|
||||
|
||||
//Apply stylesheets
|
||||
document.querySelectorAll("link[rel='stylesheet']").forEach(function(entry){
|
||||
|
||||
//Skip the entry if it is disabled
|
||||
if(entry.disabled)
|
||||
return;
|
||||
|
||||
var elem = iframeDOM.createElement("link");
|
||||
elem.rel = "stylesheet";
|
||||
elem.href = entry.href;
|
||||
iframeDOM.head.appendChild(elem);
|
||||
});
|
||||
|
||||
//Apply new styles to body
|
||||
iframeDOM.body.className += " sceditor-iframe-body";
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a new javascript event
|
||||
*
|
||||
* @param {String} name The name of the event to create
|
||||
* @param {Object} details Information about the event to create
|
||||
*/
|
||||
function SendEvent(name, details){
|
||||
|
||||
var event = new CustomEvent(name, {
|
||||
detail: details,
|
||||
bubbles: true,
|
||||
cancelable: false
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Parse account text data into ZIP file
|
||||
* Second step for export : Get and open personnal data explorer
|
||||
*
|
||||
* @param {Object} data Text data about the account
|
||||
* @param {Object} data Text data about the account (data not modified at this stage)
|
||||
*/
|
||||
parse: function(data){
|
||||
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, 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));
|
||||
|
||||
|
23
assets/js/components/bottom/links.js
Normal file
23
assets/js/components/bottom/links.js
Normal file
@ -0,0 +1,23 @@
|
||||
/**
|
||||
* Comunic bottom links list
|
||||
*
|
||||
* @author Pierre HUBERT
|
||||
*/
|
||||
|
||||
ComunicWeb.components.bottom.links = [
|
||||
|
||||
//Language selector
|
||||
{
|
||||
innerLang: "bottom_bar_action_language",
|
||||
icon: "fa-globe",
|
||||
onclick: function(){ComunicWeb.components.langPicker.show();}
|
||||
},
|
||||
|
||||
//About Comunic
|
||||
{
|
||||
innerLang: "bottom_bar_action_about",
|
||||
icon: "fa-question-circle",
|
||||
href: ComunicWeb.__config.aboutWebsiteURL,
|
||||
target: "_blank"
|
||||
}
|
||||
];
|
@ -43,27 +43,26 @@ ComunicWeb.components.bottom.main = {
|
||||
innerHTML: "Comunic "
|
||||
});
|
||||
|
||||
//Put the language selector link on the right
|
||||
var langLink = createElem2({
|
||||
appendTo: leftElements,
|
||||
type: "a",
|
||||
innerHTML: "<i class='fa fa-globe'></i> Language"
|
||||
});
|
||||
langLink.onclick = function(){
|
||||
ComunicWeb.components.langPicker.show();
|
||||
};
|
||||
ComunicWeb.components.bottom.links.forEach(function(link){
|
||||
|
||||
add_space(leftElements);
|
||||
add_space(leftElements);
|
||||
var linkEl = createElem2({
|
||||
appendTo: leftElements,
|
||||
type: "a",
|
||||
href: link.href,
|
||||
innerHTML: link.innerHTML,
|
||||
innerLang: link.innerLang,
|
||||
innerHTMLprefix: "<i class='fa "+link.icon+"'></i> "
|
||||
});
|
||||
|
||||
//Add about link
|
||||
var aboutLink = createElem2({
|
||||
appendTo: leftElements,
|
||||
type: "a",
|
||||
innerHTML: "<i class='fa fa-question-circle'></i> About",
|
||||
href: ComunicWeb.__config.aboutWebsiteURL
|
||||
if(link.target)
|
||||
linkEl.setAttribute("target", link.target);
|
||||
|
||||
if(link.onclick)
|
||||
linkEl.onclick = link.onclick;
|
||||
|
||||
add_space(leftElements);
|
||||
add_space(leftElements);
|
||||
});
|
||||
aboutLink.setAttribute("target", "_blank");
|
||||
}
|
||||
|
||||
}
|
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){
|
||||
@ -910,6 +946,95 @@ ComunicWeb.components.conversations.chatWindows = {
|
||||
element: textMessage,
|
||||
});
|
||||
|
||||
|
||||
//Add message dropdown menu
|
||||
messageContainer.className += " dropdown";
|
||||
|
||||
var dropdownToggle = createElem2({
|
||||
insertBefore: dateElem,
|
||||
type: "i",
|
||||
class: "hidden"
|
||||
});
|
||||
dropdownToggle.setAttribute("data-toggle", "dropdown");
|
||||
|
||||
var dropdownMenu = createElem2({
|
||||
insertBefore: dateElem,
|
||||
type: "ul",
|
||||
class: "dropdown-menu"
|
||||
});
|
||||
dropdownMenu.setAttribute("role", "menu");
|
||||
|
||||
messageTargetElem.addEventListener("dblclick", function(){
|
||||
$(dropdownToggle).dropdown("toggle");
|
||||
});
|
||||
|
||||
//Add message options
|
||||
if(userIsPoster){
|
||||
|
||||
//Update message content
|
||||
var updateLi = createElem2({
|
||||
type: "li",
|
||||
appendTo: dropdownMenu
|
||||
});
|
||||
|
||||
var updateLink = createElem2({
|
||||
type: "a",
|
||||
appendTo: updateLi,
|
||||
innerHTML: "Edit"
|
||||
});
|
||||
|
||||
updateLink.addEventListener("click", function(){
|
||||
ComunicWeb.components.conversations.messageEditor.open(message, function(newContent){
|
||||
|
||||
//Apply and parse new message
|
||||
textMessage.innerHTML = removeHtmlTags(newContent);
|
||||
ComunicWeb.components.textParser.parse({
|
||||
element: textMessage,
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
//Delete the message
|
||||
var deleteLi = createElem2({
|
||||
type: "li",
|
||||
appendTo: dropdownMenu
|
||||
});
|
||||
|
||||
var deleteLink = createElem2({
|
||||
type: "a",
|
||||
appendTo: deleteLi,
|
||||
innerHTML: "Delete"
|
||||
});
|
||||
|
||||
deleteLink.addEventListener("click", function(){
|
||||
ComunicWeb.common.messages.confirm(
|
||||
"Do you really want to delete this message? The operation can not be reverted!",
|
||||
function(confirm){
|
||||
if(!confirm) return;
|
||||
|
||||
//Hide the message
|
||||
messageTargetElem.style.display = "none";
|
||||
|
||||
//Execute the request
|
||||
ComunicWeb.components.conversations.interface.DeleteSingleMessage(
|
||||
message.ID,
|
||||
function(result){
|
||||
if(!result){
|
||||
messageTargetElem.style.display = "block";
|
||||
notify("Could delete conversation message!", "danger");
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
)
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
//Return information about the message
|
||||
return {
|
||||
userID: message.ID_user,
|
||||
|
@ -363,6 +363,50 @@ ComunicWeb.components.conversations.interface = {
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Intend to update the content of a single message
|
||||
*
|
||||
* @param {Number} messageID The ID of the message to update
|
||||
* @param {String} content New content for the message
|
||||
* @param {(success : Boolean) => any} callback Function called when
|
||||
* the request is terminated
|
||||
*/
|
||||
UpdateSingleMessage: function(messageID, content, callback){
|
||||
ComunicWeb.common.api.makeAPIrequest(
|
||||
"conversations/updateMessage",
|
||||
{
|
||||
"messageID": messageID,
|
||||
"content": content
|
||||
},
|
||||
true,
|
||||
|
||||
function(result){
|
||||
callback(result.error ? false : true);
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Intend to delete a single conversation message
|
||||
*
|
||||
* @param {Number} messageID The ID of the message to delete
|
||||
* @param {(success: Boolean) => any} callback Function to call once the
|
||||
* conversation message has been deleted
|
||||
*/
|
||||
DeleteSingleMessage: function(messageID, callback){
|
||||
|
||||
ComunicWeb.common.api.makeAPIrequest(
|
||||
"conversations/deleteMessage",
|
||||
{"messageID": messageID},
|
||||
true,
|
||||
|
||||
function(result){
|
||||
callback(result.error ? false : true);
|
||||
}
|
||||
);
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Empty conversations cache
|
||||
*
|
||||
|
49
assets/js/components/conversations/messageEditor.js
Normal file
49
assets/js/components/conversations/messageEditor.js
Normal file
@ -0,0 +1,49 @@
|
||||
/**
|
||||
* Conversation message editor
|
||||
*
|
||||
* @author Pierre HUBERT
|
||||
*/
|
||||
|
||||
ComunicWeb.components.conversations.messageEditor = {
|
||||
|
||||
/**
|
||||
* Open conversation message editor
|
||||
*
|
||||
* @param {Object} message Information about the message to open
|
||||
* @param {(newcontent : String) => any} callback Callback function called only
|
||||
* when the new message content has been applied
|
||||
*/
|
||||
open: function(message, callback){
|
||||
|
||||
ComunicWeb.common.messages.inputString(
|
||||
"Update message content",
|
||||
"Please specify the new content of the message:",
|
||||
message.message,
|
||||
|
||||
function(content){
|
||||
|
||||
if(!content)
|
||||
return;
|
||||
|
||||
//Intend to update message content
|
||||
ComunicWeb.components.conversations.interface.UpdateSingleMessage(
|
||||
message.ID,
|
||||
content,
|
||||
|
||||
function(result){
|
||||
|
||||
if(!result)
|
||||
return notify("Could not update conversation message content!", "danger");
|
||||
|
||||
message.message = content;
|
||||
callback(content);
|
||||
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -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
|
||||
*
|
||||
|
66
assets/js/components/darkTheme.js
Normal file
66
assets/js/components/darkTheme.js
Normal file
@ -0,0 +1,66 @@
|
||||
/**
|
||||
* Dark theme component
|
||||
*
|
||||
* @author Pierre HUBERT
|
||||
*/
|
||||
|
||||
ComunicWeb.components.darkTheme = {
|
||||
|
||||
/**
|
||||
* Specify whether dark theme has to be enabled or not
|
||||
*/
|
||||
_local_storage_name: "dark_theme_mode",
|
||||
|
||||
/**
|
||||
* CSS element that contains dark theme CSS rules
|
||||
*/
|
||||
_cssElem: null,
|
||||
|
||||
/**
|
||||
* Check out whether dark theme is enabled or not
|
||||
*
|
||||
* @return {boolean} TRUE if enabled / FALSE else
|
||||
*/
|
||||
isEnabled: function(){
|
||||
return localStorage.getItem(this._local_storage_name) == "true";
|
||||
},
|
||||
|
||||
/**
|
||||
* Specify whether dark theme should be enabled or not
|
||||
*
|
||||
* @param {boolean} enable TRUE to enable / FALSE else
|
||||
*/
|
||||
setEnabled: function(enable){
|
||||
localStorage.setItem(this._local_storage_name, enable ? "true" : "false");
|
||||
|
||||
this.refresh();
|
||||
},
|
||||
|
||||
/**
|
||||
* Refresh dark theme state
|
||||
*/
|
||||
refresh: function(){
|
||||
|
||||
//Check if the theme has to be disabled
|
||||
if(!this.isEnabled()){
|
||||
if(this._cssElem != null)
|
||||
this._cssElem.disabled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
//Check if CSS element is already loaded
|
||||
else if(this._cssElem != null)
|
||||
this._cssElem.disabled = false;
|
||||
|
||||
//We need to load dark theme
|
||||
else {
|
||||
|
||||
this._cssElem = createElem2({
|
||||
type: "link",
|
||||
href: ComunicWeb.__config.assetsURL + "css/dark_theme.css"
|
||||
});
|
||||
this._cssElem.setAttribute("rel", "stylesheet");
|
||||
document.head.appendChild(this._cssElem);
|
||||
}
|
||||
}
|
||||
}
|
@ -23,6 +23,7 @@ ComunicWeb.components.emoji.list = {
|
||||
|
||||
//Objects
|
||||
"(movie)": "📽",
|
||||
"(w)": " ❓"
|
||||
}
|
||||
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user