Implement tags and modernize CSS

This commit is contained in:
Hri7566 2024-10-24 03:09:41 -04:00
parent ef70175403
commit 87352df5b2
12 changed files with 805 additions and 660 deletions

758
Client.js
View File

@ -1,335 +1,429 @@
WebSocket.prototype.send = new Proxy(WebSocket.prototype.send, {
apply: (target, thisArg, args) => {
if (localStorage.token && !args[0].startsWith(`[{"m":"hi"`))
args[0] = args[0].replace(localStorage.token, "[REDACTED]");
return target.apply(thisArg, args);
}
});
if(typeof module !== "undefined") {
module.exports = Client;
WebSocket = require("ws");
EventEmitter = require("events").EventEmitter;
} else {
this.Client = Client;
class Client extends EventEmitter {
constructor(uri) {
super();
this.uri = uri;
this.ws = undefined;
this.serverTimeOffset = 0;
this.user = undefined;
this.participantId = undefined;
this.channel = undefined;
this.ppl = {};
this.connectionTime = undefined;
this.connectionAttempts = 0;
this.desiredChannelId = undefined;
this.desiredChannelSettings = undefined;
this.pingInterval = undefined;
this.canConnect = false;
this.noteBuffer = [];
this.noteBufferTime = 0;
this.noteFlushInterval = undefined;
this.permissions = {};
this["🐈"] = 0;
this.loginInfo = undefined;
this.bindEventListeners();
this.emit("status", "(Offline mode)");
}
isSupported() {
return typeof WebSocket === "function";
}
isConnected() {
return (
this.isSupported() &&
this.ws &&
this.ws.readyState === WebSocket.OPEN
);
}
isConnecting() {
return (
this.isSupported() &&
this.ws &&
this.ws.readyState === WebSocket.CONNECTING
);
}
start(enableTokens = true, enableChallenge = true) {
this.canConnect = true;
if (!this.connectionTime) {
this.connect(enableTokens, enableChallenge);
}
}
stop() {
this.canConnect = false;
this.ws.close();
}
connect(enableTokens, enableChallenge) {
if (
!this.canConnect ||
!this.isSupported() ||
this.isConnected() ||
this.isConnecting()
)
return;
this.emit("status", "Connecting...");
if (typeof module !== "undefined") {
// nodejsicle
this.ws = new WebSocket(this.uri, {
origin: "https://www.multiplayerpiano.com"
});
} else {
// browseroni
this.ws = new WebSocket(this.uri);
}
var self = this;
this.ws.addEventListener("close", function (evt) {
self.user = undefined;
self.participantId = undefined;
self.channel = undefined;
self.setParticipants([]);
clearInterval(self.pingInterval);
clearInterval(self.noteFlushInterval);
self.emit("disconnect", evt);
self.emit("status", "Offline mode");
// reconnect!
if (self.connectionTime) {
self.connectionTime = undefined;
self.connectionAttempts = 0;
} else {
++self.connectionAttempts;
}
var ms_lut = [50, 2500, 10000];
var idx = self.connectionAttempts;
if (idx >= ms_lut.length) idx = ms_lut.length - 1;
var ms = ms_lut[idx];
setTimeout(self.connect.bind(self), ms);
});
this.ws.addEventListener("error", function (err) {
self.emit("wserror", err);
self.ws.close(); // self.ws.emit("close");
});
this.ws.addEventListener("open", function (evt) {
self.pingInterval = setInterval(function () {
self.sendPing();
}, 20000);
self.noteBuffer = [];
self.noteBufferTime = 0;
self.noteFlushInterval = setInterval(function () {
if (self.noteBufferTime && self.noteBuffer.length > 0) {
self.sendArray([
{
m: "n",
t: self.noteBufferTime + self.serverTimeOffset,
n: self.noteBuffer
}
]);
self.noteBufferTime = 0;
self.noteBuffer = [];
}
}, 200);
self.emit("connect");
self.emit("status", "Joining channel...");
if (!enableChallenge) {
const token = localStorage.token;
var hiMsg = { m: "hi", token };
self.sendArray([hiMsg]);
}
});
this.ws.addEventListener("message", async function (evt) {
var transmission = JSON.parse(evt.data);
for (var i = 0; i < transmission.length; i++) {
var msg = transmission[i];
self.emit(msg.m, msg);
}
});
}
bindEventListeners() {
var self = this;
this.on("hi", function (msg) {
self.connectionTime = Date.now();
self.user = msg.u;
self.receiveServerTime(msg.t, msg.e || undefined);
if (self.desiredChannelId) {
self.setChannel();
}
if (msg.token) localStorage.token = msg.token;
if (msg.permissions) {
self.permissions = msg.permissions;
} else {
self.permissions = {};
}
if (msg.accountInfo) {
self.accountInfo = msg.accountInfo;
} else {
self.accountInfo = undefined;
}
});
this.on("t", function (msg) {
self.receiveServerTime(msg.t, msg.e || undefined);
});
this.on("ch", function (msg) {
self.desiredChannelId = msg.ch._id;
self.desiredChannelSettings = msg.ch.settings;
self.channel = msg.ch;
if (msg.p) self.participantId = msg.p;
self.setParticipants(msg.ppl);
});
this.on("p", function (msg) {
self.participantUpdate(msg);
self.emit("participant update", self.findParticipantById(msg.id));
});
this.on("m", function (msg) {
if (self.ppl.hasOwnProperty(msg.id)) {
self.participantMoveMouse(msg);
}
});
this.on("bye", function (msg) {
self.removeParticipant(msg.p);
});
this.on("b", function (msg) {
var hiMsg = { m: "hi" };
hiMsg["🐈"] = self["🐈"]++ || undefined;
if (this.loginInfo) hiMsg.login = this.loginInfo;
this.loginInfo = undefined;
console.log(msg);
if (msg.code) {
try {
if (msg.code.startsWith("~")) {
hiMsg.code = Function(msg.code.substring(1))();
} else {
hiMsg.code = Function(msg.code)();
}
} catch (err) {
hiMsg.code = "broken";
}
}
if (localStorage.token) {
hiMsg.token = localStorage.token;
}
self.sendArray([hiMsg]);
});
}
send(raw) {
if (this.isConnected()) this.ws.send(raw);
}
sendArray(arr) {
this.send(JSON.stringify(arr));
}
setChannel(id, set) {
this.desiredChannelId = id || this.desiredChannelId || "lobby";
this.desiredChannelSettings =
set || this.desiredChannelSettings || undefined;
this.sendArray([
{
m: "ch",
_id: this.desiredChannelId,
set: this.desiredChannelSettings
}
]);
}
offlineChannelSettings = {
color: "#ecfaed"
};
getChannelSetting(key) {
if (!this.isConnected() || !this.channel || !this.channel.settings) {
return this.offlineChannelSettings[key];
}
return this.channel.settings[key];
}
setChannelSettings(settings) {
if (!this.isConnected() || !this.channel || !this.channel.settings) {
return;
}
if (this.desiredChannelSettings) {
for (var key in settings) {
this.desiredChannelSettings[key] = settings[key];
}
this.sendArray([{ m: "chset", set: this.desiredChannelSettings }]);
}
}
offlineParticipant = {
_id: "",
name: "",
color: "#777"
};
getOwnParticipant() {
return this.findParticipantById(this.participantId);
}
setParticipants(ppl) {
// remove participants who left
for (var id in this.ppl) {
if (!this.ppl.hasOwnProperty(id)) continue;
var found = false;
for (var j = 0; j < ppl.length; j++) {
if (ppl[j].id === id) {
found = true;
break;
}
}
if (!found) {
this.removeParticipant(id);
}
}
// update all
for (var i = 0; i < ppl.length; i++) {
this.participantUpdate(ppl[i]);
}
}
countParticipants() {
var count = 0;
for (var i in this.ppl) {
if (this.ppl.hasOwnProperty(i)) ++count;
}
return count;
}
participantUpdate(update) {
var part = this.ppl[update.id] || null;
if (part === null) {
part = update;
this.ppl[part.id] = part;
this.emit("participant added", part);
this.emit("count", this.countParticipants());
} else {
Object.keys(update).forEach(key => {
part[key] = update[key];
});
if (!update.tag) delete part.tag;
if (!update.vanished) delete part.vanished;
}
}
participantMoveMouse(update) {
var part = this.ppl[update.id] || null;
if (part !== null) {
part.x = update.x;
part.y = update.y;
}
}
removeParticipant(id) {
if (this.ppl.hasOwnProperty(id)) {
var part = this.ppl[id];
delete this.ppl[id];
this.emit("participant removed", part);
this.emit("count", this.countParticipants());
}
}
findParticipantById(id) {
return this.ppl[id] || this.offlineParticipant;
}
isOwner() {
return (
this.channel &&
this.channel.crown &&
this.channel.crown.participantId === this.participantId
);
}
preventsPlaying() {
return (
this.isConnected() &&
!this.isOwner() &&
this.getChannelSetting("crownsolo") === true &&
!this.permissions.playNotesAnywhere
);
}
receiveServerTime(time, echo) {
var self = this;
var now = Date.now();
var target = time - now;
// console.log("Target serverTimeOffset: " + target);
var duration = 1000;
var step = 0;
var steps = 50;
var step_ms = duration / steps;
var difference = target - this.serverTimeOffset;
var inc = difference / steps;
var iv;
iv = setInterval(function () {
self.serverTimeOffset += inc;
if (++step >= steps) {
clearInterval(iv);
// console.log("serverTimeOffset reached: " + self.serverTimeOffset);
self.serverTimeOffset = target;
}
}, step_ms);
// smoothen
// this.serverTimeOffset = time - now; // mostly time zone offset ... also the lags so todo smoothen this
// not smooth:
// if(echo) this.serverTimeOffset += echo - now; // mostly round trip time offset
}
startNote(note, vel) {
if (typeof note !== "string") return;
if (this.isConnected()) {
var vel = typeof vel === "undefined" ? undefined : +vel.toFixed(3);
if (!this.noteBufferTime) {
this.noteBufferTime = Date.now();
this.noteBuffer.push({ n: note, v: vel });
} else {
this.noteBuffer.push({
d: Date.now() - this.noteBufferTime,
n: note,
v: vel
});
}
}
}
stopNote(note) {
if (typeof note !== "string") return;
if (this.isConnected()) {
if (!this.noteBufferTime) {
this.noteBufferTime = Date.now();
this.noteBuffer.push({ n: note, s: 1 });
} else {
this.noteBuffer.push({
d: Date.now() - this.noteBufferTime,
n: note,
s: 1
});
}
}
}
sendPing() {
var msg = { m: "t", e: Date.now() };
this.sendArray([msg]);
}
setLoginInfo(loginInfo) {
this.loginInfo = loginInfo;
}
}
function mixin(obj1, obj2) {
for(var i in obj2) {
if(obj2.hasOwnProperty(i)) {
obj1[i] = obj2[i];
}
}
};
function Client(uri) {
EventEmitter.call(this);
this.uri = uri;
this.ws = undefined;
this.serverTimeOffset = 0;
this.user = undefined;
this.participantId = undefined;
this.channel = undefined;
this.ppl = {};
this.connectionTime = undefined;
this.connectionAttempts = 0;
this.desiredChannelId = undefined;
this.desiredChannelSettings = undefined;
this.pingInterval = undefined;
this.canConnect = false;
this.noteBuffer = [];
this.noteBufferTime = 0;
this.noteFlushInterval = undefined;
this['🐈'] = 0;
this.bindEventListeners();
this.emit("status", "(Offline mode)");
};
mixin(Client.prototype, EventEmitter.prototype);
Client.prototype.constructor = Client;
Client.prototype.isSupported = function() {
return typeof WebSocket === "function";
};
Client.prototype.isConnected = function() {
return this.isSupported() && this.ws && this.ws.readyState === WebSocket.OPEN;
};
Client.prototype.isConnecting = function() {
return this.isSupported() && this.ws && this.ws.readyState === WebSocket.CONNECTING;
};
Client.prototype.start = function() {
this.canConnect = true;
this.connect();
};
Client.prototype.stop = function() {
this.canConnect = false;
this.ws.close();
};
Client.prototype.connect = function() {
if(!this.canConnect || !this.isSupported() || this.isConnected() || this.isConnecting())
return;
this.emit("status", "Connecting...");
if(typeof module !== "undefined") {
// nodejsicle
this.ws = new WebSocket(this.uri, {
origin: "https://www.multiplayerpiano.com"
});
} else {
// browseroni
this.ws = new WebSocket(this.uri);
}
var self = this;
this.ws.addEventListener("close", function(evt) {
self.user = undefined;
self.participantId = undefined;
self.channel = undefined;
self.setParticipants([]);
clearInterval(self.pingInterval);
clearInterval(self.noteFlushInterval);
self.emit("disconnect", evt);
self.emit("status", "Offline mode");
// reconnect!
if(self.connectionTime) {
self.connectionTime = undefined;
self.connectionAttempts = 0;
} else {
++self.connectionAttempts;
}
var ms_lut = [50, 2950, 7000, 10000];
var idx = self.connectionAttempts;
if(idx >= ms_lut.length) idx = ms_lut.length - 1;
var ms = ms_lut[idx];
setTimeout(self.connect.bind(self), ms);
});
this.ws.addEventListener("error", function(err) {
self.emit("wserror", err);
self.ws.close(); // self.ws.emit("close");
});
this.ws.addEventListener("open", function(evt) {
self.connectionTime = Date.now();
self.sendArray([{"m": "hi", "🐈": self['🐈']++ || undefined }]);
self.pingInterval = setInterval(function() {
self.sendArray([{m: "t", e: Date.now()}]);
}, 20000);
//self.sendArray([{m: "t", e: Date.now()}]);
self.noteBuffer = [];
self.noteBufferTime = 0;
self.noteFlushInterval = setInterval(function() {
if(self.noteBufferTime && self.noteBuffer.length > 0) {
self.sendArray([{m: "n", t: self.noteBufferTime + self.serverTimeOffset, n: self.noteBuffer}]);
self.noteBufferTime = 0;
self.noteBuffer = [];
}
}, 200);
self.emit("connect");
self.emit("status", "Joining channel...");
});
this.ws.addEventListener("message", function(evt) {
var transmission = JSON.parse(evt.data);
for(var i = 0; i < transmission.length; i++) {
var msg = transmission[i];
self.emit(msg.m, msg);
}
});
};
Client.prototype.bindEventListeners = function() {
var self = this;
this.on("hi", function(msg) {
self.user = msg.u;
self.receiveServerTime(msg.t, msg.e || undefined);
if(self.desiredChannelId) {
self.setChannel();
}
});
this.on("t", function(msg) {
self.receiveServerTime(msg.t, msg.e || undefined);
});
this.on("ch", function(msg) {
self.desiredChannelId = msg.ch._id;
self.desiredChannelSettings = msg.ch.settings;
self.channel = msg.ch;
if(msg.p) self.participantId = msg.p;
self.setParticipants(msg.ppl);
});
this.on("p", function(msg) {
self.participantUpdate(msg);
self.emit("participant update", self.findParticipantById(msg.id));
});
this.on("m", function(msg) {
if(self.ppl.hasOwnProperty(msg.id)) {
self.participantUpdate(msg);
}
});
this.on("bye", function(msg) {
self.removeParticipant(msg.p);
});
};
Client.prototype.send = function(raw) {
if(this.isConnected()) this.ws.send(raw);
};
Client.prototype.sendArray = function(arr) {
this.send(JSON.stringify(arr));
};
Client.prototype.setChannel = function(id, set) {
this.desiredChannelId = id || this.desiredChannelId || "lobby";
this.desiredChannelSettings = set || this.desiredChannelSettings || undefined;
this.sendArray([{m: "ch", _id: this.desiredChannelId, set: this.desiredChannelSettings}]);
};
Client.prototype.offlineChannelSettings = {
color:"#ecfaed"
};
Client.prototype.getChannelSetting = function(key) {
if(!this.isConnected() || !this.channel || !this.channel.settings) {
return this.offlineChannelSettings[key];
}
return this.channel.settings[key];
};
Client.prototype.setChannelSettings = function(settings) {
if(!this.isConnected() || !this.channel || !this.channel.settings) {
return;
}
if(this.desiredChannelSettings){
for(var key in settings) {
this.desiredChannelSettings[key] = settings[key];
}
this.sendArray([{m: "chset", set: this.desiredChannelSettings}]);
}
};
Client.prototype.offlineParticipant = {
_id: "",
name: "",
color: "#777"
};
Client.prototype.getOwnParticipant = function() {
return this.findParticipantById(this.participantId);
};
Client.prototype.setParticipants = function(ppl) {
// remove participants who left
for(var id in this.ppl) {
if(!this.ppl.hasOwnProperty(id)) continue;
var found = false;
for(var j = 0; j < ppl.length; j++) {
if(ppl[j].id === id) {
found = true;
break;
}
}
if(!found) {
this.removeParticipant(id);
}
}
// update all
for(var i = 0; i < ppl.length; i++) {
this.participantUpdate(ppl[i]);
}
};
Client.prototype.countParticipants = function() {
var count = 0;
for(var i in this.ppl) {
if(this.ppl.hasOwnProperty(i)) ++count;
}
return count;
};
Client.prototype.participantUpdate = function(update) {
var part = this.ppl[update.id] || null;
if(part === null) {
part = update;
this.ppl[part.id] = part;
this.emit("participant added", part);
this.emit("count", this.countParticipants());
} else {
if(update.x) part.x = update.x;
if(update.y) part.y = update.y;
if(update.color) part.color = update.color;
if(update.name) part.name = update.name;
}
};
Client.prototype.removeParticipant = function(id) {
if(this.ppl.hasOwnProperty(id)) {
var part = this.ppl[id];
delete this.ppl[id];
this.emit("participant removed", part);
this.emit("count", this.countParticipants());
}
};
Client.prototype.findParticipantById = function(id) {
return this.ppl[id] || this.offlineParticipant;
};
Client.prototype.isOwner = function() {
return this.channel && this.channel.crown && this.channel.crown.participantId === this.participantId;
};
Client.prototype.preventsPlaying = function() {
return this.isConnected() && !this.isOwner() && this.getChannelSetting("crownsolo") === true;
};
Client.prototype.receiveServerTime = function(time, echo) {
var self = this;
var now = Date.now();
var target = time - now;
//console.log("Target serverTimeOffset: " + target);
var duration = 1000;
var step = 0;
var steps = 50;
var step_ms = duration / steps;
var difference = target - this.serverTimeOffset;
var inc = difference / steps;
var iv;
iv = setInterval(function() {
self.serverTimeOffset += inc;
if(++step >= steps) {
clearInterval(iv);
//console.log("serverTimeOffset reached: " + self.serverTimeOffset);
self.serverTimeOffset=target;
}
}, step_ms);
// smoothen
//this.serverTimeOffset = time - now; // mostly time zone offset ... also the lags so todo smoothen this
// not smooth:
//if(echo) this.serverTimeOffset += echo - now; // mostly round trip time offset
};
Client.prototype.startNote = function(note, vel) {
if(this.isConnected()) {
var vel = typeof vel === "undefined" ? undefined : +vel.toFixed(3);
if(!this.noteBufferTime) {
this.noteBufferTime = Date.now();
this.noteBuffer.push({n: note, v: vel});
} else {
this.noteBuffer.push({d: Date.now() - this.noteBufferTime, n: note, v: vel});
}
}
};
Client.prototype.stopNote = function(note) {
if(this.isConnected()) {
if(!this.noteBufferTime) {
this.noteBufferTime = Date.now();
this.noteBuffer.push({n: note, s: 1});
} else {
this.noteBuffer.push({d: Date.now() - this.noteBufferTime, n: note, s: 1});
}
}
};
this.Client = Client;

BIN
cat.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 654 B

BIN
discord.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

BIN
github.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

View File

@ -8,11 +8,76 @@
content="An online piano you can play alone or with others in real-time. MIDI support, 88 keys, velocity sensitive. You can show off your skill or chat while listening to others play."
/>
<link rel="stylesheet" href="/screen.css" />
{% if usersConfig.enableTags %}
<link rel="stylesheet" href="/tags.css" />
{% endif %} {% if config.topButtons == "mppnet" %}
<link rel="stylesheet" href="/top-button.css" />
{% endif %}
</head>
<body>
{% if config.topButtons == "original" %}
<div id="social">
<div id="more-button"></div>
</div>
{% elif config.topButtons == "mppnet" %}
<a
href="https://mpp.community/"
title="MPPNet Community Forum"
target="_blank"
>
<button
class="mppcommunity-button icon-button top-button"
aria-hidden="true"
>
<img src="/mppcommunity.ico" style="vertical-align: middle" />
</button>
</a>
<a
href="https://github.com/mppnet/frontend"
title="MPPNet Frontend Repo"
target="_blank"
>
<button
class="github-button icon-button top-button"
aria-hidden="true"
>
<img src="/github.ico" style="vertical-align: middle" />
</button>
</a>
<a
href="https://www.reddit.com/r/multiplayerpianonet/"
title="MPPNet Reddit"
target="_blank"
>
<button
class="reddit-button icon-button top-button"
aria-hidden="true"
>
<img src="/reddit.ico" style="vertical-align: middle" />
</button>
</a>
<a
href="https://discord.gg/338D2xMufC"
title="MPPNet Discord"
target="_blank"
>
<button
class="discord-button icon-button top-button"
aria-hidden="true"
>
<img src="/discord.ico" style="vertical-align: middle" />
</button>
</a>
<a
href="https://docs.google.com/document/d/1wQvGwQdaI8PuEjSWxKDDThVIoAlCYIxQOyfyi4o6HcM/edit?usp=sharing"
title="Multiplayer Piano Rules"
target="_blank"
>
<button class="mpp-rules-button top-button" aria-hidden="true">
Rules
</button>
</a>
{% endif %}
<div id="chat">
<ul></ul>

BIN
kitten0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
mppcommunity.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
reddit.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -8,9 +8,6 @@
* {
user-select: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
}
html,
@ -27,49 +24,27 @@ body {
position: absolute;
}
@property --color {
syntax: "<color>";
inherits: false;
/* initial-value: #ecfafd; */
initial-value: #000000;
}
@property --color2 {
syntax: "<color>";
inherits: false;
/* initial-value: #c5d5d8; */
initial-value: #000000;
}
body {
background: #3b5054; /* Old browsers */
background: -moz-radial-gradient(
center,
ellipse cover,
#ecfafd 0%,
#c5d5d8 100%
); /* FF3.6+ */
background: -webkit-gradient(
radial,
center center,
0px,
center center,
100%,
color-stop(0%, #ecfafd),
color-stop(100%, #c5d5d8)
); /* Chrome,Safari4+ */
background: -webkit-radial-gradient(
center,
ellipse cover,
#ecfafd 0%,
#c5d5d8 100%
); /* Chrome10+,Safari5.1+ */
background: -o-radial-gradient(
center,
ellipse cover,
#ecfafd 0%,
#c5d5d8 100%
); /* Opera 12+ */
background: -ms-radial-gradient(
center,
ellipse cover,
#ecfafd 0%,
#c5d5d8 100%
); /* IE10+ */
background: radial-gradient(
ellipse at center,
#ecfafd 0%,
#c5d5d8 100%
var(--color) 0%,
var(--color2) 100%
); /* W3C */
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ecfafd', endColorstr='#c5d5d8',GradientType=1 ); /* IE6-9 fallback on horizontal gradient */
transition: background 1000ms linear;
-webkit-transition: background 1000ms linear;
transition: --color 1000ms, --color2 1000ms;
}
a {
@ -221,11 +196,11 @@ table {
}
.ease-out {
transition: left 0.1s ease-out;
transition: left 0.2s ease-out;
}
.ease-in {
transition: left 0.1s ease-in;
transition: left 0.2s ease-in;
}
.slide-left {
@ -364,8 +339,10 @@ table {
left: 0px;
width: 100%;
height: 60px;
background: #9a9;
/* background: #9a9; */
background: var(--color2);
margin-bottom: 3px;
transition: --color 1000ms, --color2 1000ms;
}
#room,
@ -794,9 +771,6 @@ table {
#modal,
#modal * {
user-select: text;
-webkit-user-select: text;
-moz-user-select: text;
-ms-user-select: text;
}
.dialog {
@ -1087,6 +1061,21 @@ table {
z-index: 300;
}
#names .name.admin:before {
content: url("/silvercrown.png");
position: absolute;
top: -8px;
left: 4px;
}
#names .name.webmaster:before {
content: url("/cat.png");
position: absolute;
top: -8px;
left: 4px;
z-index: 302;
}
#piano {
z-index: 400;
}

490
script.js
View File

@ -1,15 +1,15 @@
// 钢琴
$(function () {
var test_mode =
const test_mode =
window.location.hash &&
window.location.hash.match(/^(?:#.+)*#test(?:#.+)*$/i);
var gSeeOwnCursor =
const gSeeOwnCursor =
window.location.hash &&
window.location.hash.match(/^(?:#.+)*#seeowncursor(?:#.+)*$/i);
var gMidiVolumeTest =
const gMidiVolumeTest =
window.location.hash &&
window.location.hash.match(/^(?:#.+)*#midivolumetest(?:#.+)*$/i);
@ -1166,8 +1166,13 @@ $(function () {
(isSecure ? "wss://" : "ws://") + window.location.hostname + ":" + port
);
let enableTokens = true;
let enableChallenge = true;
if (configs.usersConfig.tokenAuth == "none") enableTokens = false;
if (configs.usersConfig.browserChallenge == "none") enableChallenge = false;
gClient.setChannel(channel_id);
gClient.start();
gClient.start(enableTokens, enableChallenge);
gClient.on("disconnect", function (evt) {
console.log(evt);
@ -1226,6 +1231,25 @@ $(function () {
part.nameDiv = $("#names")[0].appendChild(div);
$(part.nameDiv).fadeIn(2000);
if (part.tag) {
if (configs.usersConfig.enableTags) {
console.log(part.tag);
const tag = document.createElement("div");
$(tag).addClass("nametag");
$(tag).text(part.tag.text);
$(tag).css("background", part.tag.color);
part.tagDiv = $(part.nameDiv).prepend(tag);
}
if (part.tag.text === "ADMIN") {
$(part.nameDiv).addClass("admin");
}
if (part.tag.text === "OWNER") {
$(part.nameDiv).addClass("webmaster");
}
}
// sort names
var arr = $("#names .name");
arr.sort(function (a, b) {
@ -1275,6 +1299,24 @@ $(function () {
.find(".name")
.text(name)
.css("background-color", color);
if (part.tag) {
if (configs.usersConfig.enableTags) {
console.log(part.tag);
const tag = document.createElement("div");
$(tag).addClass("nametag");
$(tag).text(part.tag.text);
$(tag).css("background", part.tag.color);
part.tagDiv = $(part.nameDiv).prepend(tag);
}
if (part.tag.text === "ADMIN") {
$(part.nameDiv).addClass("admin");
}
if (part.tag.text === "OWNER") {
$(part.nameDiv).addClass("webmaster");
}
}
});
gClient.on("ch", function (msg) {
for (var id in gClient.ppl) {
@ -1559,56 +1601,18 @@ $(function () {
var bottom = document.getElementById("bottom");
var duration = 50;
var step = 0;
var steps = 30;
var step_ms = duration / steps;
var difference = new Color(color1.r, color1.g, color1.b);
difference.r -= old_color1.r;
difference.g -= old_color1.g;
difference.b -= old_color1.b;
var inc1 = new Color(
difference.r / steps,
difference.g / steps,
difference.b / steps
);
difference = new Color(color2.r, color2.g, color2.b);
difference.r -= old_color2.r;
difference.g -= old_color2.g;
difference.b -= old_color2.b;
var inc2 = new Color(
difference.r / steps,
difference.g / steps,
difference.b / steps
);
var iv;
iv = setInterval(function () {
old_color1.add(inc1.r, inc1.g, inc1.b);
old_color2.add(inc2.r, inc2.g, inc2.b);
document.body.style.background =
"radial-gradient(ellipse at center, " +
old_color1.toHexa() +
" 0%," +
old_color2.toHexa() +
" 100%)";
bottom.style.background = old_color2.toHexa();
if (++step >= steps) {
clearInterval(iv);
old_color1 = color1;
old_color2 = color2;
document.body.style.background =
"radial-gradient(ellipse at center, " +
color1.toHexa() +
" 0%," +
color2.toHexa() +
" 100%)";
bottom.style.background = color2.toHexa();
}
}, step_ms);
document.body.style.setProperty("--color", color1.toHexa());
document.body.style.setProperty("--color2", color2.toHexa());
bottom.style.setProperty("--color", color1.toHexa());
bottom.style.setProperty("--color2", color2.toHexa());
}
function setColorToDefault() {
setColor("#000000", "#000000");
setColor(
configs.urlChannel.settings.color || "#000000",
configs.urlChannel.settings.color2 || "#000000"
);
}
setColorToDefault();
@ -1616,7 +1620,6 @@ $(function () {
gClient.on("ch", function (ch) {
if (ch.ch.settings) {
if (ch.ch.settings.color) {
console.log("debug!!!", ch.ch.settings);
setColor(ch.ch.settings.color, ch.ch.settings.color2);
} else {
setColorToDefault();
@ -2045,7 +2048,7 @@ $(function () {
eles.remove();
}
this.domElement = $(
'<div class="notification"><div class="notification-body"><div class="title"></div>' +
'<div class="notification" style="display: none;"><div class="notification-body"><div class="title"></div>' +
'<div class="text"></div></div><div class="x">Ⓧ</div></div>'
);
this.domElement[0].id = this.id;
@ -2070,6 +2073,8 @@ $(function () {
self.close();
});
$(this.domElement).fadeIn(100);
if (this.duration > 0) {
setTimeout(function () {
self.close();
@ -2098,7 +2103,7 @@ $(function () {
Notification.prototype.close = function () {
var self = this;
window.removeEventListener("resize", this.onresize);
this.domElement.fadeOut(500, function () {
this.domElement.fadeOut(250, function () {
self.domElement.remove();
self.emit("close");
});
@ -2239,16 +2244,24 @@ $(function () {
var room_name = "Room" + Math.floor(Math.random() * 1000000000000);
changeRoom(room_name, "right", { visible: false });
setTimeout(function () {
new Notification({
id: "share",
title: "Playing alone",
html:
let html =
"You are playing alone in a room by yourself, but you can always invite \
friends by sending them the link.";
if (configs.config.playingAloneSocialLinks) {
html =
"You are playing alone in a room by yourself, but you can always invite \
friends by sending them the link.<br/><br/>\
<a href=\"#\" onclick=\"window.open('https://www.facebook.com/sharer/sharer.php?u='+encodeURIComponent(location.href),'facebook-share-dialog','width=626,height=436');return false;\">Share on Facebook</a><br/><br/>\
<a href=\"http://twitter.com/home?status=" +
encodeURIComponent(location.href) +
'" target="_blank">Tweet</a>',
'" target="_blank">Tweet</a>';
}
new Notification({
id: "share",
title: "Playing alone",
html,
duration: 25000
});
}, 1000);
@ -2279,8 +2292,8 @@ $(function () {
function closeModal() {
$(document).off("keydown", modalHandleEsc);
$("#modal").fadeOut(100);
$("#modal #modals > *").hide();
$("#modal").fadeOut(300);
// $("#modal #modals > *").hide();
captureKeyboard();
gModal = null;
}
@ -2302,6 +2315,18 @@ $(function () {
closeModal();
changeRoom(name, "right", settings);
setTimeout(function () {
let html =
"You can invite friends to your room by sending them the link.<br/><br/>\
<a href=\"#\" onclick=\"window.open('https://www.facebook.com/sharer/sharer.php?u='+encodeURIComponent(location.href),'facebook-share-dialog','width=626,height=436');return false;\">Share on Facebook</a><br/><br/>\
<a href=\"http://twitter.com/home?status=" +
encodeURIComponent(location.href) +
'" target="_blank">Tweet</a>';
if (configs.config.createdRoomSocialLinks) {
html =
"You can invite friends to your room by sending them the link.";
}
new Notification({
id: "share",
title: "Created a Room",
@ -2359,27 +2384,33 @@ $(function () {
var t = 0,
d = 100;
$("#piano").addClass("slide");
if (configs.config.enableSlide) {
$("#piano").addClass("slide");
requestAnimationFrame(() => {
$("#piano")
.addClass("ease-out")
.addClass("slide-" + opposite);
setTimeout(function () {
requestAnimationFrame(() => {
$("#piano")
.removeClass("ease-out")
.removeClass("slide-" + opposite)
.addClass("slide-" + direction);
}, (t += d));
setTimeout(function () {
$("#piano")
.addClass("ease-in")
.removeClass("slide-" + direction);
}, (t += d));
setTimeout(function () {
$("#piano").removeClass("ease-in");
}, (t += d));
});
.addClass("ease-out")
.addClass("slide-" + opposite);
setTimeout(function () {
$("#piano")
.removeClass("ease-out")
.removeClass("slide-" + opposite)
.addClass("slide-" + direction);
}, (t += d));
setTimeout(function () {
$("#piano")
.addClass("ease-in")
.removeClass("slide-" + direction);
}, (t += d));
setTimeout(function () {
$("#piano").removeClass("ease-in");
setTimeout(function () {
$("#piano").removeClass("slide");
}, d);
}, (t += d));
});
}
}
var gHistoryDepth = 0;
@ -2939,105 +2970,105 @@ $(function () {
////////////////////////////////////////////////////////////////
window.onerror = function (message, url, line) {
var url = url || "(no url)";
var line = line || "(no line)";
// errors in socket.io
if (url.indexOf("socket.io.js") !== -1) {
if (message.indexOf("INVALID_STATE_ERR") !== -1) return;
if (message.indexOf("InvalidStateError") !== -1) return;
if (message.indexOf("DOM Exception 11") !== -1) return;
if (
message.indexOf(
"Property 'open' of object #<c> is not a function"
) !== -1
)
return;
if (
message.indexOf("Cannot call method 'close' of undefined") !==
-1
)
return;
if (message.indexOf("Cannot call method 'close' of null") !== -1)
return;
if (message.indexOf("Cannot call method 'onClose' of null") !== -1)
return;
if (message.indexOf("Cannot call method 'payload' of null") !== -1)
return;
if (
message.indexOf(
"Unable to get value of the property 'close'"
) !== -1
)
return;
if (message.indexOf("NS_ERROR_NOT_CONNECTED") !== -1) return;
if (
message.indexOf(
"Unable to get property 'close' of undefined or null reference"
) !== -1
)
return;
if (
message.indexOf(
"Unable to get value of the property 'close': object is null or undefined"
) !== -1
)
return;
if (message.indexOf("this.transport is null") !== -1) return;
}
// errors in soundmanager2
if (url.indexOf("soundmanager2.js") !== -1) {
// operation disabled in safe mode?
if (
message.indexOf(
"Could not complete the operation due to error c00d36ef"
) !== -1
)
return;
if (message.indexOf("_s.o._setVolume is not a function") !== -1)
return;
}
// errors in midibridge
if (url.indexOf("midibridge") !== -1) {
if (message.indexOf("Error calling method on NPObject") !== -1)
return;
}
// too many failing extensions injected in my html
if (url.indexOf(".js") !== url.length - 3) return;
// extensions inject cross-domain embeds too
if (url.toLowerCase().indexOf("multiplayerpiano.com") == -1) return;
// window.onerror = function (message, url, line) {
// var url = url || "(no url)";
// var line = line || "(no line)";
// // errors in socket.io
// if (url.indexOf("socket.io.js") !== -1) {
// if (message.indexOf("INVALID_STATE_ERR") !== -1) return;
// if (message.indexOf("InvalidStateError") !== -1) return;
// if (message.indexOf("DOM Exception 11") !== -1) return;
// if (
// message.indexOf(
// "Property 'open' of object #<c> is not a function"
// ) !== -1
// )
// return;
// if (
// message.indexOf("Cannot call method 'close' of undefined") !==
// -1
// )
// return;
// if (message.indexOf("Cannot call method 'close' of null") !== -1)
// return;
// if (message.indexOf("Cannot call method 'onClose' of null") !== -1)
// return;
// if (message.indexOf("Cannot call method 'payload' of null") !== -1)
// return;
// if (
// message.indexOf(
// "Unable to get value of the property 'close'"
// ) !== -1
// )
// return;
// if (message.indexOf("NS_ERROR_NOT_CONNECTED") !== -1) return;
// if (
// message.indexOf(
// "Unable to get property 'close' of undefined or null reference"
// ) !== -1
// )
// return;
// if (
// message.indexOf(
// "Unable to get value of the property 'close': object is null or undefined"
// ) !== -1
// )
// return;
// if (message.indexOf("this.transport is null") !== -1) return;
// }
// // errors in soundmanager2
// if (url.indexOf("soundmanager2.js") !== -1) {
// // operation disabled in safe mode?
// if (
// message.indexOf(
// "Could not complete the operation due to error c00d36ef"
// ) !== -1
// )
// return;
// if (message.indexOf("_s.o._setVolume is not a function") !== -1)
// return;
// }
// // errors in midibridge
// if (url.indexOf("midibridge") !== -1) {
// if (message.indexOf("Error calling method on NPObject") !== -1)
// return;
// }
// // too many failing extensions injected in my html
// if (url.indexOf(".js") !== url.length - 3) return;
// // extensions inject cross-domain embeds too
// if (url.toLowerCase().indexOf("multiplayerpiano.com") == -1) return;
// errors in my code
if (url.indexOf("script.js") !== -1) {
if (
message.indexOf("Object [object Object] has no method 'on'") !==
-1
)
return;
if (
message.indexOf(
"Object [object Object] has no method 'off'"
) !== -1
)
return;
if (
message.indexOf(
"Property '$' of object [object Object] is not a function"
) !== -1
)
return;
}
// // errors in my code
// if (url.indexOf("script.js") !== -1) {
// if (
// message.indexOf("Object [object Object] has no method 'on'") !==
// -1
// )
// return;
// if (
// message.indexOf(
// "Object [object Object] has no method 'off'"
// ) !== -1
// )
// return;
// if (
// message.indexOf(
// "Property '$' of object [object Object] is not a function"
// ) !== -1
// )
// return;
// }
var enc =
"/bugreport/" +
(message ? encodeURIComponent(message) : "") +
"/" +
(url ? encodeURIComponent(url) : "") +
"/" +
(line ? encodeURIComponent(line) : "");
var img = new Image();
img.src = enc;
};
// var enc =
// "/bugreport/" +
// (message ? encodeURIComponent(message) : "") +
// "/" +
// (url ? encodeURIComponent(url) : "") +
// "/" +
// (line ? encodeURIComponent(line) : "");
// var img = new Image();
// img.src = enc;
// };
// more button
(function () {
@ -3085,7 +3116,8 @@ $(function () {
chat: chat,
noteQuota: gNoteQuota,
soundSelector: gSoundSelector,
Notification: Notification
Notification: Notification,
configs
};
// record mp3
@ -3384,105 +3416,3 @@ $(function () {
}
})();
});
/*
function catSound() {
let sounds = [
"cat-sounds/meow1.mp3",
"cat-sounds/meow2.mp3",
"cat-sounds/meow3.mp3",
"cat-sounds/meow4.mp3",
"cat-sounds/meow5.mp3",
"cat-sounds/meow6.mp3",
"cat-sounds/meow7.mp3",
"cat-sounds/meow8.mp3",
"cat-sounds/meow9.mp3",
"cat-sounds/meow10.mp3"
];
let random = sounds[Math.floor(Math.random() * sounds.length)];
const meow = new Audio(random);
meow.play();
}
document.getElementById("more-button").onclick = catSound;
*/
// misc
////////////////////////////////////////////////////////////////
// analytics
window.google_analytics_uacct = "UA-882009-7";
var _gaq = _gaq || [];
_gaq.push(["_setAccount", "UA-882009-7"]);
_gaq.push(["_trackPageview"]);
_gaq.push(["_setAllowAnchor", true]);
(function () {
var ga = document.createElement("script");
ga.type = "text/javascript";
ga.async = true;
ga.src =
("https:" == document.location.protocol
? "https://ssl"
: "http://www") + ".google-analytics.com/ga.js";
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(ga, s);
})();
// twitter
!(function (d, s, id) {
var js,
fjs = d.getElementsByTagName(s)[0];
if (!d.getElementById(id)) {
js = d.createElement(s);
js.id = id;
js.src = "//platform.twitter.com/widgets.js";
fjs.parentNode.insertBefore(js, fjs);
}
})(document, "script", "twitter-wjs");
// fb
(function (d, s, id) {
var js,
fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) return;
js = d.createElement(s);
js.id = id;
js.src = "//connect.facebook.net/en_US/sdk.js#xfbml=1&version=v2.8";
fjs.parentNode.insertBefore(js, fjs);
})(document, "script", "facebook-jssdk");
// non-ad-free experience
/*(function() {
function adsOn() {
if(window.localStorage) {
var div = document.querySelector("#inclinations");
div.innerHTML = "Ads:<br>ON / <a id=\"adsoff\" href=\"#\">OFF</a>";
div.querySelector("#adsoff").addEventListener("click", adsOff);
localStorage.ads = true;
}
// adsterra
var script = document.createElement("script");
script.src = "//pl132070.puhtml.com/68/7a/97/687a978dd26d579c788cb41e352f5a41.js";
document.head.appendChild(script);
}
function adsOff() {
if(window.localStorage) localStorage.ads = false;
document.location.reload(true);
}
function noAds() {
var div = document.querySelector("#inclinations");
div.innerHTML = "Ads:<br><a id=\"adson\" href=\"#\">ON</a> / OFF";
div.querySelector("#adson").addEventListener("click", adsOn);
}
if(window.localStorage) {
if(localStorage.ads === undefined || localStorage.ads === "true")
adsOn();
else
noAds();
} else {
adsOn();
}
})();*/

BIN
silvercrown.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 B

67
top-button.css Normal file
View File

@ -0,0 +1,67 @@
.top-button {
height: 24px;
font-size: 12px;
background: #111;
border: 1px solid #444;
padding: 5px;
cursor: pointer;
line-height: 12px;
border-radius: 2px;
-webkit-border-radius: 2px;
-moz-border-radius: 2px;
overflow: hidden;
white-space: nowrap;
color: #fff;
}
.top-button:hover {
background: #222;
}
.top-button.stuck {
background: rgba(204, 187, 170, 0.35);
}
.icon-button img {
width: 16px;
height: 16px;
}
.icon-button {
position: fixed;
top: 6px;
z-index: 200;
width: 26px;
padding: 0;
}
.mppcommunity-button {
right: 6px;
}
.discord-button {
right: 36px;
}
.github-button {
right: 66px;
background-color: white;
filter: invert(100%);
border-color: #c6c6c6;
}
.github-button:hover {
background-color: #ddd;
}
.reddit-button {
right: 96px;
z-index: 500;
}
.mpp-rules-button {
position: fixed;
right: 6px;
top: 32px;
z-index: 200;
}