Implement tags and modernize CSS
This commit is contained in:
parent
ef70175403
commit
87352df5b2
758
Client.js
758
Client.js
|
@ -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;
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 2.5 KiB |
Binary file not shown.
After Width: | Height: | Size: 6.4 KiB |
65
index.html
65
index.html
|
@ -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>
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
Binary file not shown.
After Width: | Height: | Size: 2.4 KiB |
85
screen.css
85
screen.css
|
@ -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
490
script.js
|
@ -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();
|
||||
}
|
||||
})();*/
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 164 B |
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue