aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--build/client.js426
-rw-r--r--build/server.js139
-rw-r--r--res/css/cytube.css11
-rw-r--r--res/css/des.css3
-rw-r--r--res/index.html8
-rw-r--r--res/js/split.min.js3
-rw-r--r--src/Client.hx49
-rw-r--r--src/ClientTools.hx6
-rw-r--r--src/Types.hx2
-rw-r--r--src/client/Buttons.hx156
-rw-r--r--src/client/Main.hx197
-rw-r--r--src/client/MobileView.hx13
-rw-r--r--src/client/Player.hx2
-rw-r--r--src/client/Split.hx7
-rw-r--r--src/server/Main.hx37
15 files changed, 717 insertions, 342 deletions
diff --git a/build/client.js b/build/client.js
index 5997420..3c9515c 100644
--- a/build/client.js
+++ b/build/client.js
@@ -7,17 +7,32 @@ function $extend(from, fields) {
if( fields.toString !== Object.prototype.toString ) proto.toString = fields.toString;
return proto;
}
-var Client = function(ws,name,isLeader) {
- if(isLeader == null) {
- isLeader = false;
- }
- this.ws = ws;
+var ClientGroup = $hxEnums["ClientGroup"] = { __ename__ : true, __constructs__ : ["User","Leader","Admin"]
+ ,User: {_hx_index:0,__enum__:"ClientGroup",toString:$estr}
+ ,Leader: {_hx_index:1,__enum__:"ClientGroup",toString:$estr}
+ ,Admin: {_hx_index:2,__enum__:"ClientGroup",toString:$estr}
+};
+var Client = function(ws,id,name,group) {
this.name = name;
- this.isLeader = isLeader;
+ var i = group;
+ if(group == null) {
+ i = 0;
+ }
+ this.group = i;
};
Client.__name__ = true;
Client.fromData = function(data) {
- return new Client(null,data.name,data.isLeader);
+ return new Client(null,null,data.name,data.group);
+};
+Client.prototype = {
+ setGroupFlag: function(type,flag) {
+ if(flag) {
+ this.group |= 1 << type._hx_index;
+ } else {
+ this.group &= -1 - (1 << type._hx_index);
+ }
+ return flag;
+ }
};
var ClientTools = function() { };
ClientTools.__name__ = true;
@@ -27,13 +42,13 @@ ClientTools.setLeader = function(clients,name) {
var client = clients[_g];
++_g;
if(client.name == name) {
- client.isLeader = true;
- } else if(client.isLeader) {
- client.isLeader = false;
+ client.setGroupFlag(ClientGroup.Leader,true);
+ } else if((client.group & 2) != 0) {
+ client.setGroupFlag(ClientGroup.Leader,false);
}
}
};
-ClientTools.getByName = function(clients,name) {
+ClientTools.getByName = function(clients,name,def) {
var _g = 0;
while(_g < clients.length) {
var client = clients[_g];
@@ -42,7 +57,7 @@ ClientTools.getByName = function(clients,name) {
return client;
}
}
- return null;
+ return def;
};
var EReg = function(r,opt) {
this.r = new RegExp(r,opt.split("u").join(""));
@@ -57,6 +72,13 @@ EReg.prototype = {
this.r.s = s;
return this.r.m != null;
}
+ ,matched: function(n) {
+ if(this.r.m != null && n >= 0 && n < this.r.m.length) {
+ return this.r.m[n];
+ } else {
+ throw new js__$Boot_HaxeError("EReg::matched");
+ }
+ }
};
var HxOverrides = function() { };
HxOverrides.__name__ = true;
@@ -249,16 +271,150 @@ StringTools.startsWith = function(s,start) {
StringTools.replace = function(s,sub,by) {
return s.split(sub).join(by);
};
+var client_Buttons = function() { };
+client_Buttons.__name__ = true;
+client_Buttons.init = function(main) {
+ client_Buttons.initChatInput(main);
+ var smilesBtn = window.document.querySelector("#smilesbtn");
+ smilesBtn.onclick = function(e) {
+ smilesBtn.classList.toggle("active");
+ var smilesWrap = window.document.querySelector("#smileswrap");
+ if(smilesBtn.classList.contains("active")) {
+ return smilesWrap.style.display = "block";
+ } else {
+ return smilesWrap.style.display = "none";
+ }
+ };
+ window.document.querySelector("#clearchatbtn").style.display = "inline-block";
+ window.document.querySelector("#clearchatbtn").onclick = function(e1) {
+ if((main.personal.group & 4) != 0) {
+ main.send({ type : "ClearChat"});
+ }
+ return;
+ };
+ var userList = window.document.querySelector("#userlist");
+ userList.onclick = function(e2) {
+ if((main.personal.group & 4) == 0) {
+ return;
+ }
+ var el = e2.target;
+ if(userList == el) {
+ return;
+ }
+ if(!el.classList.contains("userlist_item")) {
+ el = el.parentElement;
+ }
+ var name = "";
+ if(el.children.length == 1) {
+ name = el.lastElementChild.innerText;
+ }
+ main.send({ type : "SetLeader", setLeader : { clientName : name}});
+ return;
+ };
+ client_Buttons.split = new Split(["#chatwrap","#videowrap"],{ sizes : [40,60], onDragEnd : function() {
+ return window.dispatchEvent(new Event("resize"));
+ }, minSize : 185, snapOffset : 0});
+ var userlistToggle = window.document.querySelector("#userlisttoggle");
+ userlistToggle.onclick = function(e3) {
+ var style = window.document.querySelector("#userlist").style;
+ if(style.display == "none") {
+ userlistToggle.classList.add("glyphicon-chevron-down");
+ userlistToggle.classList.remove("glyphicon-chevron-right");
+ return style.display = "block";
+ } else {
+ userlistToggle.classList.add("glyphicon-chevron-right");
+ userlistToggle.classList.remove("glyphicon-chevron-down");
+ return style.display = "none";
+ }
+ };
+ window.document.querySelector("#usercount").onclick = userlistToggle.onclick;
+ var extendPlayer = window.document.querySelector("#extendplayer");
+ extendPlayer.onclick = function(e4) {
+ if(extendPlayer.classList.contains("active")) {
+ client_Buttons.split.setSizes([40,60]);
+ window.document.querySelector("#userlist").style.width = "90px";
+ } else {
+ client_Buttons.split.setSizes([20,80]);
+ window.document.querySelector("#userlist").style.width = "80px";
+ }
+ extendPlayer.classList.toggle("active");
+ return window.dispatchEvent(new Event("resize"));
+ };
+ window.document.querySelector("#showmediaurl").onclick = function(e5) {
+ window.document.querySelector("#showmediaurl").classList.toggle("collapsed");
+ window.document.querySelector("#showmediaurl").classList.toggle("active");
+ return window.document.querySelector("#addfromurl").classList.toggle("collapse");
+ };
+ window.onresize = client_Buttons.onVideoResize;
+ window.dispatchEvent(new Event("resize"));
+};
+client_Buttons.onVideoResize = function() {
+ var height = window.document.querySelector("#ytapiplayer").offsetHeight - window.document.querySelector("#chatline").offsetHeight;
+ window.document.querySelector("#messagebuffer").style.height = "" + height + "px";
+ window.document.querySelector("#userlist").style.height = "" + height + "px";
+};
+client_Buttons.initChatInput = function(main) {
+ var guestName = window.document.querySelector("#guestname");
+ guestName.onkeydown = function(e) {
+ if(guestName.value.length == 0) {
+ return;
+ }
+ if(e.keyCode == 13) {
+ main.send({ type : "Login", login : { clientName : guestName.value}});
+ }
+ return;
+ };
+ var chatLine = window.document.querySelector("#chatline");
+ chatLine.onkeydown = function(e1) {
+ switch(e1.keyCode) {
+ case 13:
+ if(chatLine.value.length == 0) {
+ return;
+ }
+ main.send({ type : "Message", message : { clientName : "", text : chatLine.value}});
+ client_Buttons.personalHistory.push(chatLine.value);
+ if(client_Buttons.personalHistory.length > 50) {
+ client_Buttons.personalHistory.shift();
+ }
+ client_Buttons.personalHistoryId = -1;
+ chatLine.value = "";
+ break;
+ case 38:
+ client_Buttons.personalHistoryId--;
+ if(client_Buttons.personalHistoryId == -2) {
+ client_Buttons.personalHistoryId = client_Buttons.personalHistory.length - 1;
+ if(client_Buttons.personalHistoryId == -1) {
+ return;
+ }
+ } else if(client_Buttons.personalHistoryId == -1) {
+ client_Buttons.personalHistoryId++;
+ }
+ chatLine.value = client_Buttons.personalHistory[client_Buttons.personalHistoryId];
+ break;
+ case 40:
+ if(client_Buttons.personalHistoryId == -1) {
+ return;
+ }
+ client_Buttons.personalHistoryId++;
+ if(client_Buttons.personalHistoryId > client_Buttons.personalHistory.length - 1) {
+ client_Buttons.personalHistoryId = -1;
+ chatLine.value = "";
+ return;
+ }
+ chatLine.value = client_Buttons.personalHistory[client_Buttons.personalHistoryId];
+ break;
+ }
+ };
+};
var client_Main = function(host,port) {
if(port == null) {
port = 4201;
}
this.onTimeGet = new haxe_Timer(2000);
this.isConnected = false;
- this.personalHistoryId = -1;
+ this.personal = new Client(null,null,"Unknown",0);
this.filters = [];
this.pageTitle = window.document.title;
- this.personalHistory = [];
this.clients = [];
var _gthis = this;
this.player = new client_Player(this);
@@ -313,113 +469,41 @@ client_Main.prototype = {
}
,initListeners: function() {
var _gthis = this;
- window.document.querySelector("#smilesbtn").onclick = function(e) {
- var smilesWrap = window.document.querySelector("#smileswrap");
- if(smilesWrap.style.display == "") {
- return smilesWrap.style.display = "block";
- } else {
- return smilesWrap.style.display = "";
- }
- };
- var guestName = window.document.querySelector("#guestname");
- guestName.onkeydown = function(e1) {
- if(guestName.value.length == 0) {
- return;
- }
- if(e1.keyCode == 13) {
- _gthis.send({ type : "Login", login : { clientName : guestName.value}});
- }
- return;
- };
- var chatLine = window.document.querySelector("#chatline");
- chatLine.onkeydown = function(e2) {
- switch(e2.keyCode) {
- case 13:
- if(chatLine.value.length == 0) {
- return;
- }
- _gthis.send({ type : "Message", message : { clientName : "", text : chatLine.value}});
- _gthis.personalHistory.push(chatLine.value);
- if(_gthis.personalHistory.length > 50) {
- _gthis.personalHistory.shift();
- }
- _gthis.personalHistoryId = -1;
- chatLine.value = "";
- break;
- case 38:
- _gthis.personalHistoryId--;
- if(_gthis.personalHistoryId == -2) {
- _gthis.personalHistoryId = _gthis.personalHistory.length - 1;
- if(_gthis.personalHistoryId == -1) {
- return;
- }
- } else if(_gthis.personalHistoryId == -1) {
- _gthis.personalHistoryId++;
- }
- chatLine.value = _gthis.personalHistory[_gthis.personalHistoryId];
- break;
- case 40:
- if(_gthis.personalHistoryId == -1) {
- return;
- }
- _gthis.personalHistoryId++;
- if(_gthis.personalHistoryId > _gthis.personalHistory.length - 1) {
- _gthis.personalHistoryId = -1;
- chatLine.value = "";
- return;
- }
- chatLine.value = _gthis.personalHistory[_gthis.personalHistoryId];
- break;
- }
- };
+ client_Buttons.init(this);
client_MobileView.init();
- var leaderBtn = window.document.querySelector("#leader_btn");
- leaderBtn.onclick = function(e3) {
- if(_gthis.personal == null) {
- return;
- }
- if(!_gthis.personal.isLeader) {
- leaderBtn.classList.add("label-success");
- } else {
- leaderBtn.classList.remove("label-success");
- }
- _gthis.send({ type : "SetLeader", setLeader : { clientName : _gthis.personal.isLeader ? "" : _gthis.personal.name}});
+ window.document.querySelector("#leader_btn").onclick = function(e) {
+ _gthis.setLeaderButton((_gthis.personal.group & 2) == 0);
+ _gthis.send({ type : "SetLeader", setLeader : { clientName : (_gthis.personal.group & 2) != 0 ? "" : _gthis.personal.name}});
return;
};
- window.document.querySelector("#showmediaurl").onclick = function(e4) {
- window.document.querySelector("#showmediaurl").classList.toggle("collapsed");
- window.document.querySelector("#showmediaurl").classList.toggle("active");
- return window.document.querySelector("#addfromurl").classList.toggle("collapse");
- };
- window.document.querySelector("#queue_next").onclick = function(e5) {
+ window.document.querySelector("#queue_next").onclick = function(e1) {
_gthis.addVideoUrl();
return;
};
- window.document.querySelector("#queue_end").onclick = function(e6) {
+ window.document.querySelector("#queue_end").onclick = function(e2) {
_gthis.addVideoUrl();
return;
};
- window.document.querySelector("#mediaurl").onkeydown = function(e7) {
- if(e7.keyCode == 13) {
+ window.document.querySelector("#mediaurl").onkeydown = function(e3) {
+ if(e3.keyCode == 13) {
_gthis.addVideoUrl();
}
};
}
- ,isLeader: function() {
- if(this.personal != null) {
- return this.personal.isLeader;
- } else {
- return false;
- }
- }
,addVideoUrl: function() {
var _gthis = this;
var mediaUrl = window.document.querySelector("#mediaurl");
var url = mediaUrl.value;
- var name = this.personal == null ? "Unknown" : this.personal.name;
+ var pos = url.lastIndexOf("/") + 1;
+ var name = HxOverrides.substr(url,pos,null);
+ var matchName = new EReg("^(.+)\\.","");
+ if(matchName.match(name)) {
+ name = matchName.matched(1);
+ } else {
+ name = Lang.get("rawVideo");
+ }
this.getRemoteVideoDuration(mediaUrl.value,function(duration) {
- var tmp = Lang.get("rawVideo");
- _gthis.send({ type : "AddVideo", addVideo : { item : { url : url, title : tmp, author : name, duration : duration}}});
+ _gthis.send({ type : "AddVideo", addVideo : { item : { url : url, title : name, author : _gthis.personal.name, duration : duration}}});
return;
});
mediaUrl.value = "";
@@ -428,8 +512,11 @@ client_Main.prototype = {
var player = window.document.querySelector("#ytapiplayer");
var video = window.document.createElement("video");
video.src = src;
+ video.onerror = function(e) {
+ callback(0);
+ return;
+ };
video.onloadedmetadata = function() {
- haxe_Log.trace(video.duration,{ fileName : "src/client/Main.hx", lineNumber : 185, className : "client.Main", methodName : "getRemoteVideoDuration"});
player.removeChild(video);
callback(video.duration);
return;
@@ -447,7 +534,7 @@ client_Main.prototype = {
var data = JSON.parse(e.data);
var t = data.type;
var t1 = t.charAt(0).toLowerCase() + HxOverrides.substr(t,1,null);
- haxe_Log.trace("Event: " + data.type,{ fileName : "src/client/Main.hx", lineNumber : 201, className : "client.Main", methodName : "onMessage", customParams : [data[t1]]});
+ haxe_Log.trace("Event: " + data.type,{ fileName : "src/client/Main.hx", lineNumber : 149, className : "client.Main", methodName : "onMessage", customParams : [data[t1]]});
switch(data.type) {
case "AddVideo":
if(this.player.isListEmpty()) {
@@ -455,43 +542,16 @@ client_Main.prototype = {
}
this.player.addVideoItem(data.addVideo.item);
break;
+ case "ClearChat":
+ window.document.querySelector("#messagebuffer").innerHTML = "";
+ break;
case "Connected":
- this.setConfig(data.connected.config);
- if(data.connected.isUnknownClient) {
- this.updateClients(data.connected.clients);
- window.document.querySelector("#guestlogin").style.display = "block";
- window.document.querySelector("#chatline").style.display = "none";
- } else {
- this.onLogin(data.connected.clients,data.connected.clientName);
- }
- var guestName = window.document.querySelector("#guestname");
- if(guestName.value.length > 0) {
- this.send({ type : "Login", login : { clientName : guestName.value}});
- }
- var _g = 0;
- var _g1 = data.connected.history;
- while(_g < _g1.length) {
- var message = _g1[_g];
- ++_g;
- this.addMessage(message.name,message.text,message.time);
- }
- var list = data.connected.videoList;
- if(list.length == 0) {
- return;
- }
- this.player.setVideo(list[0]);
- var _g2 = 0;
- var _g3 = data.connected.videoList;
- while(_g2 < _g3.length) {
- var video = _g3[_g2];
- ++_g2;
- this.player.addVideoItem(video);
- }
+ this.onConnected(data);
break;
case "GetTime":
var newTime = data.getTime.time;
var time = this.player.getTime();
- if(this.isLeader()) {
+ if((this.personal.group & 1 << ClientGroup.Leader._hx_index) != 0) {
if(Math.abs(time - newTime) < 2) {
return;
}
@@ -517,22 +577,21 @@ client_Main.prototype = {
break;
case "Logout":
this.updateClients(data.logout.clients);
- this.personal = null;
- window.document.querySelector("#guestlogin").style.display = "block";
- window.document.querySelector("#chatline").style.display = "none";
+ this.personal = new Client(null,null,data.logout.clientName,0);
+ this.showGuestLoginPanel();
break;
case "Message":
this.addMessage(data.message.clientName,data.message.text);
break;
case "Pause":
- if(this.isLeader()) {
+ if((this.personal.group & 1 << ClientGroup.Leader._hx_index) != 0) {
return;
}
this.player.pause();
this.player.setTime(data.pause.time);
break;
case "Play":
- if(this.isLeader()) {
+ if((this.personal.group & 1 << ClientGroup.Leader._hx_index) != 0) {
return;
}
this.player.setTime(data.play.time);
@@ -547,13 +606,8 @@ client_Main.prototype = {
case "SetLeader":
ClientTools.setLeader(this.clients,data.setLeader.clientName);
this.updateUserList();
- var leaderBtn = window.document.querySelector("#leader_btn");
- if(this.isLeader()) {
- leaderBtn.classList.add("label-success");
- } else {
- leaderBtn.classList.remove("label-success");
- }
- if(this.isLeader()) {
+ this.setLeaderButton((this.personal.group & 1 << ClientGroup.Leader._hx_index) != 0);
+ if((this.personal.group & 1 << ClientGroup.Leader._hx_index) != 0) {
this.player.setTime(this.player.getTime(),false);
}
break;
@@ -567,9 +621,7 @@ client_Main.prototype = {
break;
case "UpdateClients":
this.updateClients(data.updateClients.clients);
- if(this.personal != null) {
- this.personal = ClientTools.getByName(this.clients,this.personal.name);
- }
+ this.personal = ClientTools.getByName(this.clients,this.personal.name,this.personal);
break;
case "VideoLoaded":
this.player.setTime(0);
@@ -577,6 +629,36 @@ client_Main.prototype = {
break;
}
}
+ ,onConnected: function(data) {
+ var connected = data.connected;
+ this.setConfig(connected.config);
+ if(connected.isUnknownClient) {
+ this.updateClients(connected.clients);
+ this.personal = ClientTools.getByName(this.clients,connected.clientName,this.personal);
+ this.showGuestLoginPanel();
+ } else {
+ this.onLogin(connected.clients,connected.clientName);
+ }
+ var guestName = window.document.querySelector("#guestname");
+ if(guestName.value.length > 0) {
+ this.send({ type : "Login", login : { clientName : guestName.value}});
+ }
+ var _g = 0;
+ var _g1 = connected.history;
+ while(_g < _g1.length) {
+ var message = _g1[_g];
+ ++_g;
+ this.addMessage(message.name,message.text,message.time);
+ }
+ var list = connected.videoList;
+ if(list.length == 0) {
+ return;
+ }
+ this.player.setVideo(list[0]);
+ var _g2 = 0;
+ var _g3 = connected.videoList;
+ while(_g2 < _g3.length) this.player.addVideoItem(_g3[_g2++]);
+ }
,setConfig: function(config) {
this.config = config;
this.pageTitle = config.channelName;
@@ -620,10 +702,18 @@ client_Main.prototype = {
}
,onLogin: function(data,clientName) {
this.updateClients(data);
- this.personal = ClientTools.getByName(this.clients,clientName);
- if(this.personal == null) {
+ var newPersonal = ClientTools.getByName(this.clients,clientName);
+ if(newPersonal == null) {
return;
}
+ this.personal = newPersonal;
+ this.hideGuestLoginPanel();
+ }
+ ,showGuestLoginPanel: function() {
+ window.document.querySelector("#guestlogin").style.display = "block";
+ window.document.querySelector("#chatline").style.display = "none";
+ }
+ ,hideGuestLoginPanel: function() {
window.document.querySelector("#guestlogin").style.display = "none";
window.document.querySelector("#chatline").style.display = "block";
}
@@ -674,10 +764,11 @@ client_Main.prototype = {
while(_g < _g1.length) {
var client1 = _g1[_g];
++_g;
- if(client1.isLeader) {
+ list_b += "<div class=\"userlist_item\">";
+ if((client1.group & 2) != 0) {
list_b += "<span class=\"glyphicon glyphicon-star-empty\"></span>";
}
- list_b += Std.string("<span class=\"" + "userlist_item" + "\">" + client1.name + "</span></br>");
+ list_b += Std.string("<span class=\"" + ((client1.group & 4) != 0 ? "userlist_owner" : "") + "\">" + client1.name + "</span></div>");
}
window.document.querySelector("#userlist").innerHTML = list_b;
}
@@ -716,7 +807,7 @@ client_Main.prototype = {
while(msgBuf.children.length > 200) msgBuf.removeChild(msgBuf.firstChild);
msgBuf.scrollTop = msgBuf.scrollHeight;
}
- if(this.personal != null && this.personal.name == name) {
+ if(this.personal.name == name) {
msgBuf.scrollTop = msgBuf.scrollHeight;
}
if(window.document.hidden && this.onBlinkTab == null) {
@@ -731,6 +822,14 @@ client_Main.prototype = {
this.onBlinkTab.run();
}
}
+ ,setLeaderButton: function(flag) {
+ var leaderBtn = window.document.querySelector("#leader_btn");
+ if((this.personal.group & 2) != 0) {
+ leaderBtn.classList.add("label-success");
+ } else {
+ leaderBtn.classList.remove("label-success");
+ }
+ }
,escapeRegExp: function(regex) {
var _this_r = new RegExp("([.*+?^${}()|[\\]\\\\])","g".split("u").join(""));
return regex.replace(_this_r,"\\$1");
@@ -743,14 +842,14 @@ client_MobileView.init = function() {
mvbtn.onclick = function(e) {
if(client_MobileView.toggleFullScreen()) {
window.document.body.classList.add("mobile-view");
- mvbtn.classList.add("label-success");
+ mvbtn.classList.add("active");
var vwrap = window.document.querySelector("#videowrap");
if(vwrap.children[0] == window.document.querySelector("currenttitle")) {
vwrap.appendChild(vwrap.children[0]);
}
} else {
window.document.body.classList.remove("mobile-view");
- mvbtn.classList.remove("label-success");
+ mvbtn.classList.remove("active");
var vwrap1 = window.document.querySelector("videowrap");
if(vwrap1.children[0] != window.document.querySelector("currenttitle")) {
vwrap1.insertBefore(vwrap1.children[1],vwrap1.children[0]);
@@ -812,21 +911,21 @@ client_Player.prototype = {
_gthis.skipSetTime = false;
return;
}
- if(!_gthis.main.isLeader()) {
+ if((_gthis.main.personal.group & 2) == 0) {
return;
}
_gthis.main.send({ type : "SetTime", setTime : { time : _gthis.video.currentTime}});
return;
};
this.video.onpause = function(e2) {
- if(!_gthis.main.isLeader()) {
+ if((_gthis.main.personal.group & 2) == 0) {
return;
}
_gthis.main.send({ type : "Pause", pause : { time : _gthis.video.currentTime}});
return;
};
this.video.onplay = function(e3) {
- if(!_gthis.main.isLeader()) {
+ if((_gthis.main.personal.group & 2) == 0) {
return;
}
_gthis.main.send({ type : "Play", play : { time : _gthis.video.currentTime}});
@@ -834,6 +933,7 @@ client_Player.prototype = {
};
this.player.innerHTML = "";
this.player.appendChild(this.video);
+ window.document.querySelector("#currenttitle").innerHTML = item.title;
}
,addVideoItem: function(item) {
var _gthis = this;
@@ -1377,5 +1477,7 @@ js_Boot.__toStr = ({ }).toString;
Lang.ids = ["en","ru"];
Lang.langs = new haxe_ds_StringMap();
Lang.lang = HxOverrides.substr(window.navigator.language,0,2).toLowerCase();
+client_Buttons.personalHistory = [];
+client_Buttons.personalHistoryId = -1;
client_Main.main();
})(typeof window != "undefined" ? window : typeof global != "undefined" ? global : typeof self != "undefined" ? self : this);
diff --git a/build/server.js b/build/server.js
index 14869f0..6c35793 100644
--- a/build/server.js
+++ b/build/server.js
@@ -1,23 +1,39 @@
// Generated by Haxe 4.0.5
(function ($global) { "use strict";
+var $estr = function() { return js_Boot.__string_rec(this,''); },$hxEnums = $hxEnums || {},$_;
function $extend(from, fields) {
var proto = Object.create(from);
for (var name in fields) proto[name] = fields[name];
if( fields.toString !== Object.prototype.toString ) proto.toString = fields.toString;
return proto;
}
-var Client = function(ws,name,isLeader) {
- if(isLeader == null) {
- isLeader = false;
- }
+var ClientGroup = $hxEnums["ClientGroup"] = { __ename__ : true, __constructs__ : ["User","Leader","Admin"]
+ ,User: {_hx_index:0,__enum__:"ClientGroup",toString:$estr}
+ ,Leader: {_hx_index:1,__enum__:"ClientGroup",toString:$estr}
+ ,Admin: {_hx_index:2,__enum__:"ClientGroup",toString:$estr}
+};
+var Client = function(ws,id,name,group) {
this.ws = ws;
+ this.id = id;
this.name = name;
- this.isLeader = isLeader;
+ var i = group;
+ if(group == null) {
+ i = 0;
+ }
+ this.group = i;
};
Client.__name__ = true;
Client.prototype = {
- getData: function() {
- return { name : this.name, isLeader : this.isLeader};
+ setGroupFlag: function(type,flag) {
+ if(flag) {
+ this.group |= 1 << type._hx_index;
+ } else {
+ this.group &= -1 - (1 << type._hx_index);
+ }
+ return flag;
+ }
+ ,getData: function() {
+ return { name : this.name, group : this.group};
}
};
var ClientTools = function() { };
@@ -28,20 +44,20 @@ ClientTools.setLeader = function(clients,name) {
var client = clients[_g];
++_g;
if(client.name == name) {
- client.isLeader = true;
- } else if(client.isLeader) {
- client.isLeader = false;
+ client.setGroupFlag(ClientGroup.Leader,true);
+ } else if((client.group & 2) != 0) {
+ client.setGroupFlag(ClientGroup.Leader,false);
}
}
};
ClientTools.hasLeader = function(clients) {
var _g = 0;
- while(_g < clients.length) if(clients[_g++].isLeader) {
+ while(_g < clients.length) if((clients[_g++].group & 2) != 0) {
return true;
}
return false;
};
-ClientTools.getByName = function(clients,name) {
+ClientTools.getByName = function(clients,name,def) {
var _g = 0;
while(_g < clients.length) {
var client = clients[_g];
@@ -50,7 +66,7 @@ ClientTools.getByName = function(clients,name) {
return client;
}
}
- return null;
+ return def;
};
var EReg = function(r,opt) {
this.r = new RegExp(r,opt.split("u").join(""));
@@ -406,6 +422,34 @@ js_Boot.__string_rec = function(o,s) {
case "function":
return "<function>";
case "object":
+ if(o.__enum__) {
+ var e = $hxEnums[o.__enum__];
+ var n = e.__constructs__[o._hx_index];
+ var con = e[n];
+ if(con.__params__) {
+ s = s + "\t";
+ return n + "(" + ((function($this) {
+ var $r;
+ var _g = [];
+ {
+ var _g1 = 0;
+ var _g2 = con.__params__;
+ while(true) {
+ if(!(_g1 < _g2.length)) {
+ break;
+ }
+ var p = _g2[_g1];
+ _g1 = _g1 + 1;
+ _g.push(js_Boot.__string_rec(o[p],s));
+ }
+ }
+ $r = _g;
+ return $r;
+ }(this))).join(",") + ")";
+ } else {
+ return n;
+ }
+ }
if(((o) instanceof Array)) {
var str = "[";
s += "\t";
@@ -490,7 +534,8 @@ server_HttpServer.serveFiles = function(req,res) {
res.end(tmp1);
} else {
res.statusCode = 500;
- res.end("Error getting the file: " + Std.string(err) + ".");
+ var tmp2 = "Error getting the file: " + Std.string(err) + ".";
+ res.end(tmp2);
}
return;
}
@@ -540,6 +585,7 @@ var server_Main = function(port,wsPort) {
this.messages = [];
this.videoTimer = new server_VideoTimer();
this.videoList = [];
+ this.freeIds = [];
this.clients = [];
this.rootDir = "" + __dirname + "/..";
this.config = this.getUserConfig();
@@ -551,16 +597,16 @@ var server_Main = function(port,wsPort) {
process.on("exit",exit);
process.on("SIGINT",exit);
process.on("uncaughtException",function(log) {
- haxe_Log.trace(log,{ fileName : "src/server/Main.hx", lineNumber : 40, className : "server.Main", methodName : "new"});
+ haxe_Log.trace(log,{ fileName : "src/server/Main.hx", lineNumber : 41, className : "server.Main", methodName : "new"});
return;
});
process.on("unhandledRejection",function(reason,promise) {
- haxe_Log.trace("Unhandled Rejection at:",{ fileName : "src/server/Main.hx", lineNumber : 43, className : "server.Main", methodName : "new", customParams : [reason]});
+ haxe_Log.trace("Unhandled Rejection at:",{ fileName : "src/server/Main.hx", lineNumber : 44, className : "server.Main", methodName : "new", customParams : [reason]});
return;
});
server_Utils.getGlobalIp(function(ip) {
- haxe_Log.trace("Local: http://" + server_Utils.getLocalIp() + ":" + port,{ fileName : "src/server/Main.hx", lineNumber : 48, className : "server.Main", methodName : "new"});
- haxe_Log.trace("Global: http://" + ip + ":" + port,{ fileName : "src/server/Main.hx", lineNumber : 49, className : "server.Main", methodName : "new"});
+ haxe_Log.trace("Local: http://" + server_Utils.getLocalIp() + ":" + port,{ fileName : "src/server/Main.hx", lineNumber : 49, className : "server.Main", methodName : "new"});
+ haxe_Log.trace("Global: http://" + ip + ":" + port,{ fileName : "src/server/Main.hx", lineNumber : 50, className : "server.Main", methodName : "new"});
return;
});
var dir = "" + this.rootDir + "/res";
@@ -589,7 +635,7 @@ server_Main.prototype = {
var field = _g1[_g];
++_g;
if(Reflect.field(config,field) == null) {
- haxe_Log.trace("Warning: config field \"" + field + "\" is unknown",{ fileName : "src/server/Main.hx", lineNumber : 67, className : "server.Main", methodName : "getUserConfig"});
+ haxe_Log.trace("Warning: config field \"" + field + "\" is unknown",{ fileName : "src/server/Main.hx", lineNumber : 68, className : "server.Main", methodName : "getUserConfig"});
}
config[field] = Reflect.field(customConfig,field);
}
@@ -597,9 +643,20 @@ server_Main.prototype = {
}
,onConnect: function(ws,req) {
var _gthis = this;
- haxe_Log.trace("Client connected (" + req.connection.remoteAddress + ")",{ fileName : "src/server/Main.hx", lineNumber : 75, className : "server.Main", methodName : "onConnect"});
- var client = new Client(ws,"Unknown",false);
+ var ip = req.connection.remoteAddress;
+ var id = this.freeIds.length > 0 ? this.freeIds.shift() : this.clients.length;
+ var name = "Guest " + (id + 1);
+ haxe_Log.trace("" + name + " connected (" + ip + ")",{ fileName : "src/server/Main.hx", lineNumber : 78, className : "server.Main", methodName : "onConnect"});
+ var client = new Client(ws,id,name,0);
+ if(req.connection.localAddress == ip) {
+ client.group |= 4;
+ }
this.clients.push(client);
+ if(this.clients.length == 1) {
+ if(this.videoTimer.isPaused()) {
+ this.videoTimer.play();
+ }
+ }
var tmp = this.config;
var tmp1 = this.messages;
var client1 = client.name;
@@ -615,17 +672,33 @@ server_Main.prototype = {
return;
});
ws.on("close",function(err) {
- haxe_Log.trace("Client " + client.name + " disconnected",{ fileName : "src/server/Main.hx", lineNumber : 98, className : "server.Main", methodName : "onConnect"});
+ haxe_Log.trace("Client " + client.name + " disconnected",{ fileName : "src/server/Main.hx", lineNumber : 105, className : "server.Main", methodName : "onConnect"});
+ _gthis.sortedPush(_gthis.freeIds,client.id);
HxOverrides.remove(_gthis.clients,client);
_gthis.sendClientList();
- if(client.isLeader) {
+ if((client.group & 2) != 0) {
if(_gthis.videoTimer.isPaused()) {
_gthis.videoTimer.play();
}
}
+ if(_gthis.clients.length == 0) {
+ _gthis.videoTimer.pause();
+ }
return;
});
}
+ ,sortedPush: function(ids,id) {
+ var _g = 0;
+ var _g1 = ids.length;
+ while(_g < _g1) {
+ var i = _g++;
+ if(id < ids[i]) {
+ ids.splice(i,0,id);
+ return;
+ }
+ }
+ ids.push(id);
+ }
,onMessage: function(client,data) {
switch(data.type) {
case "AddVideo":
@@ -635,6 +708,11 @@ server_Main.prototype = {
this.waitVideoStart = haxe_Timer.delay($bind(this,this.startVideoPlayback),3000);
}
break;
+ case "ClearChat":
+ if((client.group & 4) != 0) {
+ this.broadcast(data);
+ }
+ break;
case "Connected":
break;
case "GetTime":
@@ -662,8 +740,8 @@ server_Main.prototype = {
break;
case "Logout":
var oldName = client.name;
- client.name = "Unknown";
- this.send(client,{ type : data.type, logout : { clientName : oldName, clients : this.clientList()}});
+ client.name = "Guest " + (this.clients.indexOf(client) + 1);
+ this.send(client,{ type : data.type, logout : { oldClientName : oldName, clientName : client.name, clients : this.clientList()}});
this.sendClientList();
break;
case "Message":
@@ -679,7 +757,7 @@ server_Main.prototype = {
var time = "[" + new Date().toTimeString().split(" ")[0] + "] ";
this.messages.push({ text : text, name : client.name, time : time});
if(this.messages.length > this.config.serverChatHistory) {
- this.messages.pop();
+ this.messages.shift();
}
this.broadcast(data);
break;
@@ -687,7 +765,7 @@ server_Main.prototype = {
if(this.videoList.length == 0) {
return;
}
- if(!client.isLeader) {
+ if((client.group & 2) == 0) {
return;
}
this.videoTimer.pause();
@@ -697,7 +775,7 @@ server_Main.prototype = {
if(this.videoList.length == 0) {
return;
}
- if(!client.isLeader) {
+ if((client.group & 2) == 0) {
return;
}
this.videoTimer.play();
@@ -718,7 +796,7 @@ server_Main.prototype = {
break;
case "SetLeader":
ClientTools.setLeader(this.clients,data.setLeader.clientName);
- this.sendClientList();
+ this.broadcast({ type : "SetLeader", setLeader : { clientName : data.setLeader.clientName}});
if(this.videoList.length == 0) {
return;
}
@@ -733,7 +811,7 @@ server_Main.prototype = {
if(this.videoList.length == 0) {
return;
}
- if(!client.isLeader) {
+ if((client.group & 2) == 0) {
return;
}
this.videoTimer.setTime(data.setTime.time);
@@ -886,7 +964,6 @@ sys_FileSystem.exists = function(path) {
}
};
function $getIterator(o) { if( o instanceof Array ) return HxOverrides.iter(o); else return o.iterator(); }
-var $_;
function $bind(o,m) { if( m == null ) return null; if( m.__id__ == null ) m.__id__ = $global.$haxeUID++; var f; if( o.hx__closures__ == null ) o.hx__closures__ = {}; else f = o.hx__closures__[m.__id__]; if( f == null ) { f = m.bind(o); o.hx__closures__[m.__id__] = f; } return f; }
$global.$haxeUID |= 0;
var __map_reserved = {};
diff --git a/res/css/cytube.css b/res/css/cytube.css
index 310e72b..a879de3 100644
--- a/res/css/cytube.css
+++ b/res/css/cytube.css
@@ -287,20 +287,11 @@ li.ui-sortable-helper, li.ui-sortable-placeholder + li.queue_entry {
cursor: pointer;
}
-.userlist_siteadmin {
- color: #cc0000!important;
- font-weight: bold!important;
-}
-
.userlist_owner {
- color: #0000cc!important;
+ color: #cc0000!important;
font-weight: bold!important;
}
-.userlist_op {
- color: #00aa00!important;
-}
-
.userlist_guest {
color: #888888!important;
}
diff --git a/res/css/des.css b/res/css/des.css
index 014d3d6..b85d5f1 100644
--- a/res/css/des.css
+++ b/res/css/des.css
@@ -176,6 +176,9 @@ src:url('https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.2/fonts/gl
.glyphicon-chevron-down:before {
content:"\e114"
}
+.glyphicon-chevron-right::before {
+ content: "\e080";
+}
.glyphicon-retweet:before {
content:"\e115"
}
diff --git a/res/index.html b/res/index.html
index 9b5d6f4..9834175 100644
--- a/res/index.html
+++ b/res/index.html
@@ -63,7 +63,6 @@
<div class="split split-horizontal" id="chatwrap" style="width: calc(40% - 5px);">
<div id="chatheader"><i class="glyphicon glyphicon-chevron-down pull-left pointer" id="userlisttoggle" title="${toggleUserList}"></i>
<span class="pointer" id="usercount">${connection}...</span>
- <span class="label label-default pull-right pointer" id="mv_btn">${mobileViewBtn}</span>
<span class="label label-default pull-right pointer" id="leader_btn">${leader}</span>
</div>
<div id="userlist" style="height: 389px;"></div>
@@ -73,7 +72,7 @@
<input class="form-control" id="guestname" type="text" placeholder="${yourName}">
</div>
</div>
- <div class="gutter gutter-horizontal" style="width: 10px;"></div>
+ <!-- <div class="gutter gutter-horizontal" style="width: 10px;"></div> -->
<div class="split split-horizontal" id="videowrap" style="width: calc(60% - 5px);">
<p id="currenttitle">${nothingPlaying}</p>
<div id="ytapiplayer" class="embed-responsive embed-responsive-16by9"></div>
@@ -82,9 +81,9 @@
</div>
<div class="row" id="controlsrow">
<div class="col-lg-5 col-md-5" id="leftcontrols">
- <button class="btn btn-sm btn-default" id="clearchatbtn">${clearChat}</button>
+ <button id="mv_btn" class="btn btn-sm btn-default">${mobileViewBtn}</button>
<button class="btn btn-sm btn-default" id="smilesbtn">${emotes}</button>
- <button id="image-btn" class="btn btn-sm btn-default">${uploadImage}</button></div>
+ <button class="btn btn-sm btn-default" id="clearchatbtn" style="display: none;">${clearChat}</button></div>
<div class="col-lg-7 col-md-7" id="rightcontrols">
<div class="btn-group" id="plcontrol">
<button class="btn btn-sm btn-default collapsed" id="showsearch" title="${searchVideo}" data-toggle="collapse" data-target="#searchcontrol" aria-expanded="false"><span class="glyphicon glyphicon-search"></span></button>
@@ -198,6 +197,7 @@
</footer>
<!-- <script src="js/custom.js"></script> -->
+ <script src="js/split.min.js"></script>
<script src="client.js"></script>
<!-- <script defer="" src="https://www.youtube.com/iframe_api"></script>
<script defer="" src="js/sc.js"></script>
diff --git a/res/js/split.min.js b/res/js/split.min.js
new file mode 100644
index 0000000..6739874
--- /dev/null
+++ b/res/js/split.min.js
@@ -0,0 +1,3 @@
+/*! Split.js - v1.5.11 */
+!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.Split=t()}(this,function(){"use strict";var L=window,T=L.document,N="addEventListener",R="removeEventListener",q="getBoundingClientRect",H="horizontal",I=function(){return!1},W=L.attachEvent&&!L[N],i=["","-webkit-","-moz-","-o-"].filter(function(e){var t=T.createElement("div");return t.style.cssText="width:"+e+"calc(9px)",!!t.style.length}).shift()+"calc",s=function(e){return"string"==typeof e||e instanceof String},X=function(e){if(s(e)){var t=T.querySelector(e);if(!t)throw new Error("Selector "+e+" did not match a DOM element");return t}return e},Y=function(e,t,n){var r=e[t];return void 0!==r?r:n},G=function(e,t,n,r){if(t){if("end"===r)return 0;if("center"===r)return e/2}else if(n){if("start"===r)return 0;if("center"===r)return e/2}return e},J=function(e,t){var n=T.createElement("div");return n.className="gutter gutter-"+t,n},K=function(e,t,n){var r={};return s(t)?r[e]=t:r[e]=W?t+"%":i+"("+t+"% - "+n+"px)",r},P=function(e,t){var n;return(n={})[e]=t+"px",n};return function(e,i){void 0===i&&(i={});var u,t,s,o,r,a,l=e;Array.from&&(l=Array.from(l));var c=X(l[0]).parentNode,n=getComputedStyle?getComputedStyle(c):null,f=n?n.flexDirection:null,m=Y(i,"sizes")||l.map(function(){return 100/l.length}),h=Y(i,"minSize",100),d=Array.isArray(h)?h:l.map(function(){return h}),g=Y(i,"expandToMin",!1),v=Y(i,"gutterSize",10),p=Y(i,"gutterAlign","center"),y=Y(i,"snapOffset",30),z=Y(i,"dragInterval",1),S=Y(i,"direction",H),b=Y(i,"cursor",S===H?"col-resize":"row-resize"),_=Y(i,"gutter",J),E=Y(i,"elementStyle",K),w=Y(i,"gutterStyle",P);function k(t,e,n,r){var i=E(u,e,n,r);Object.keys(i).forEach(function(e){t.style[e]=i[e]})}function x(){return a.map(function(e){return e.size})}function M(e){return"touches"in e?e.touches[0][t]:e[t]}function U(e){var t=a[this.a],n=a[this.b],r=t.size+n.size;t.size=e/this.size*r,n.size=r-e/this.size*r,k(t.element,t.size,this._b,t.i),k(n.element,n.size,this._c,n.i)}function O(){var e=a[this.a].element,t=a[this.b].element,n=e[q](),r=t[q]();this.size=n[u]+r[u]+this._b+this._c,this.start=n[s],this.end=n[o]}function C(s){var o=function(e){if(!getComputedStyle)return null;var t=getComputedStyle(e);if(!t)return null;var n=e[r];return 0===n?null:n-=S===H?parseFloat(t.paddingLeft)+parseFloat(t.paddingRight):parseFloat(t.paddingTop)+parseFloat(t.paddingBottom)}(c);if(null===o)return s;if(d.reduce(function(e,t){return e+t},0)>o)return s;var a=0,u=[],e=s.map(function(e,t){var n=o*e/100,r=G(v,0===t,t===s.length-1,p),i=d[t]+r;return n<i?(a+=i-n,u.push(0),i):(u.push(n-i),n)});return 0===a?s:e.map(function(e,t){var n=e;if(0<a&&0<u[t]-a){var r=Math.min(a,u[t]-a);a-=r,n=e-r}return n/o*100})}function D(e){if(!("button"in e&&0!==e.button)){var t=this,n=a[t.a].element,r=a[t.b].element;t.dragging||Y(i,"onDragStart",I)(x()),e.preventDefault(),t.dragging=!0,t.move=function(e){var t,n=a[this.a],r=a[this.b];this.dragging&&(t=M(e)-this.start+(this._b-this.dragOffset),1<z&&(t=Math.round(t/z)*z),t<=n.minSize+y+this._b?t=n.minSize+this._b:t>=this.size-(r.minSize+y+this._c)&&(t=this.size-(r.minSize+this._c)),U.call(this,t),Y(i,"onDrag",I)())}.bind(t),t.stop=function(){var e=this,t=a[e.a].element,n=a[e.b].element;e.dragging&&Y(i,"onDragEnd",I)(x()),e.dragging=!1,L[R]("mouseup",e.stop),L[R]("touchend",e.stop),L[R]("touchcancel",e.stop),L[R]("mousemove",e.move),L[R]("touchmove",e.move),e.stop=null,e.move=null,t[R]("selectstart",I),t[R]("dragstart",I),n[R]("selectstart",I),n[R]("dragstart",I),t.style.userSelect="",t.style.webkitUserSelect="",t.style.MozUserSelect="",t.style.pointerEvents="",n.style.userSelect="",n.style.webkitUserSelect="",n.style.MozUserSelect="",n.style.pointerEvents="",e.gutter.style.cursor="",e.parent.style.cursor="",T.body.style.cursor=""}.bind(t),L[N]("mouseup",t.stop),L[N]("touchend",t.stop),L[N]("touchcancel",t.stop),L[N]("mousemove",t.move),L[N]("touchmove",t.move),n[N]("selectstart",I),n[N]("dragstart",I),r[N]("selectstart",I),r[N]("dragstart",I),n.style.userSelect="none",n.style.webkitUserSelect="none",n.style.MozUserSelect="none",n.style.pointerEvents="none",r.style.userSelect="none",r.style.webkitUserSelect="none",r.style.MozUserSelect="none",r.style.pointerEvents="none",t.gutter.style.cursor=b,t.parent.style.cursor=b,T.body.style.cursor=b,O.call(t),t.dragOffset=M(e)-t.end}}S===H?(u="width",t="clientX",s="left",o="right",r="clientWidth"):"vertical"===S&&(u="height",t="clientY",s="top",o="bottom",r="clientHeight"),m=C(m);var A=[];function j(e){var t=e.i===A.length,n=t?A[e.i-1]:A[e.i];O.call(n);var r=t?n.size-e.minSize-n._c:e.minSize+n._b;U.call(n,r)}function F(e){var s=C(e);s.forEach(function(e,t){if(0<t){var n=A[t-1],r=a[n.a],i=a[n.b];r.size=s[t-1],i.size=e,k(r.element,r.size,n._b,r.i),k(i.element,i.size,n._c,i.i)}})}function B(n,r){A.forEach(function(t){if(!0!==r?t.parent.removeChild(t.gutter):(t.gutter[R]("mousedown",t._a),t.gutter[R]("touchstart",t._a)),!0!==n){var e=E(u,t.a.size,t._b);Object.keys(e).forEach(function(e){a[t.a].element.style[e]="",a[t.b].element.style[e]=""})}})}return(a=l.map(function(e,t){var n,r,i,s={element:X(e),size:m[t],minSize:d[t],i:t};if(0<t&&((n={a:t-1,b:t,dragging:!1,direction:S,parent:c})._b=G(v,t-1==0,!1,p),n._c=G(v,!1,t===l.length-1,p),"row-reverse"===f||"column-reverse"===f)){var o=n.a;n.a=n.b,n.b=o}if(!W&&0<t){var a=_(t,S,s.element);r=a,i=w(u,v,t),Object.keys(i).forEach(function(e){r.style[e]=i[e]}),n._a=D.bind(n),a[N]("mousedown",n._a),a[N]("touchstart",n._a),c.insertBefore(a,s.element),n.gutter=a}return k(s.element,s.size,G(v,0===t,t===l.length-1,p),t),0<t&&A.push(n),s})).forEach(function(e){var t=e.element[q]()[u];t<e.minSize&&(g?j(e):e.minSize=t)}),W?{setSizes:F,destroy:B}:{setSizes:F,getSizes:x,collapse:function(e){j(a[e])},destroy:B,parent:c,pairs:A}}});
+//# sourceMappingURL=split.min.js.map
diff --git a/src/Client.hx b/src/Client.hx
index e44417a..7aa14c5 100644
--- a/src/Client.hx
+++ b/src/Client.hx
@@ -5,33 +5,70 @@ import js.npm.ws.WebSocket;
#elseif js
import js.html.WebSocket;
#end
+import haxe.EnumFlags;
+
+enum ClientGroup {
+ User;
+ Leader;
+ Admin;
+}
typedef ClientData = {
name:String,
- isLeader:Bool
+ group:Int
}
class Client {
+ #if nodejs
public final ws:WebSocket;
+ public final id:Int;
+ #end
public var name:String;
- public var isLeader:Bool;
+ public var group:EnumFlags<ClientGroup>;
+ public var isLeader(get, set):Bool;
+ public var isAdmin(get, set):Bool;
- public function new(?ws:WebSocket, name:String, isLeader = false) {
+ public function new(?ws:WebSocket, ?id:Int, name:String, group:Int) {
+ #if nodejs
this.ws = ws;
+ this.id = id;
+ #end
this.name = name;
- this.isLeader = isLeader;
+ this.group = new EnumFlags(group);
+ }
+
+ inline function get_isLeader():Bool {
+ return group.has(Leader);
+ }
+
+ inline function set_isLeader(flag:Bool):Bool {
+ return setGroupFlag(Leader, flag);
+ }
+
+ inline function get_isAdmin():Bool {
+ return group.has(Admin);
+ }
+
+ inline function set_isAdmin(flag:Bool):Bool {
+ return setGroupFlag(Admin, flag);
+ }
+
+ function setGroupFlag(type:ClientGroup, flag:Bool):Bool {
+ if (flag) group.set(type);
+ else group.unset(type);
+ return flag;
}
public function getData():ClientData {
return {
name: name,
- isLeader: isLeader
+ group: group.toInt()
}
}
public static function fromData(data:ClientData):Client {
- return new Client(data.name, data.isLeader);
+ return new Client(data.name, data.group);
}
}
diff --git a/src/ClientTools.hx b/src/ClientTools.hx
index 4503ada..26faab6 100644
--- a/src/ClientTools.hx
+++ b/src/ClientTools.hx
@@ -16,11 +16,13 @@ class ClientTools {
return false;
}
- public static function getByName(clients:Array<Client>, name:String):Null<Client> {
+ public static function getByName(
+ clients:Array<Client>, name:String, ?def:Client
+ ):Null<Client> {
for (client in clients) {
if (client.name == name) return client;
}
- return null;
+ return def;
}
}
diff --git a/src/Types.hx b/src/Types.hx
index 03f18bd..8a0bde2 100644
--- a/src/Types.hx
+++ b/src/Types.hx
@@ -54,6 +54,7 @@ typedef WsEvent = {
?isUnknownClient:Bool,
},
?logout:{
+ oldClientName:String,
clientName:String,
clients:Array<ClientData>,
},
@@ -105,4 +106,5 @@ enum abstract WsEventType(String) {
var GetTime;
var SetTime;
var SetLeader;
+ var ClearChat;
}
diff --git a/src/client/Buttons.hx b/src/client/Buttons.hx
new file mode 100644
index 0000000..a5fbfa4
--- /dev/null
+++ b/src/client/Buttons.hx
@@ -0,0 +1,156 @@
+package client;
+
+import js.html.KeyboardEvent;
+import js.html.InputElement;
+import js.html.ButtonElement;
+import js.html.Element;
+import client.Main.ge;
+import js.Browser.window;
+import js.html.Event;
+
+class Buttons {
+
+ static final personalHistory:Array<String> = [];
+ static var personalHistoryId = -1;
+ static var split:Split;
+
+ public static function init(main:Main):Void {
+ initChatInput(main);
+
+ final smilesBtn = ge("#smilesbtn");
+ smilesBtn.onclick = e -> {
+ smilesBtn.classList.toggle("active");
+ final smilesWrap = ge("#smileswrap");
+ if (smilesBtn.classList.contains("active"))
+ smilesWrap.style.display = "block";
+ else smilesWrap.style.display = "none";
+ }
+
+ ge("#clearchatbtn").style.display = "inline-block";
+ ge("#clearchatbtn").onclick = e -> {
+ if (main.isAdmin()) main.send({type: ClearChat});
+ }
+ final userList = ge("#userlist");
+ userList.onclick = e -> {
+ if (!main.isAdmin()) return;
+ var el:Element = cast e.target;
+ if (userList == el) return;
+ if (!el.classList.contains("userlist_item"))
+ el = el.parentElement;
+ var name = "";
+ if (el.children.length == 1) {
+ name = el.lastElementChild.innerText;
+ }
+ main.send({
+ type: SetLeader,
+ setLeader: {
+ clientName: name
+ }
+ });
+ }
+
+ split = new Split(["#chatwrap", "#videowrap"], {
+ sizes: [40, 60],
+ onDragEnd: () -> {
+ window.dispatchEvent(new Event("resize"));
+ },
+ minSize: 185,
+ snapOffset: 0
+ });
+
+ final userlistToggle = ge("#userlisttoggle");
+ userlistToggle.onclick = e -> {
+ final style = ge("#userlist").style;
+ if (style.display == "none") {
+ userlistToggle.classList.add("glyphicon-chevron-down");
+ userlistToggle.classList.remove("glyphicon-chevron-right");
+ style.display = "block";
+ } else {
+ userlistToggle.classList.add("glyphicon-chevron-right");
+ userlistToggle.classList.remove("glyphicon-chevron-down");
+ style.display = "none";
+ }
+ }
+ ge("#usercount").onclick = userlistToggle.onclick;
+
+ final extendPlayer = ge("#extendplayer");
+ extendPlayer.onclick = e -> {
+ if (extendPlayer.classList.contains("active")) {
+ split.setSizes([40, 60]);
+ ge('#userlist').style.width = "90px";
+ } else {
+ split.setSizes([20, 80]);
+ ge('#userlist').style.width = "80px";
+ }
+ extendPlayer.classList.toggle("active");
+ window.dispatchEvent(new Event('resize'));
+ }
+
+ final showMediaUrl:ButtonElement = cast ge("#showmediaurl");
+ showMediaUrl.onclick = e -> {
+ ge("#showmediaurl").classList.toggle("collapsed");
+ ge("#showmediaurl").classList.toggle("active");
+ ge("#addfromurl").classList.toggle("collapse");
+ }
+
+ window.onresize = onVideoResize;
+ window.dispatchEvent(new Event("resize"));
+ }
+
+ static function onVideoResize():Void {
+ final player = ge("#ytapiplayer");
+ final height = player.offsetHeight - ge("#chatline").offsetHeight;
+ ge("#messagebuffer").style.height = '${height}px';
+ ge("#userlist").style.height = '${height}px';
+ }
+
+
+ static function initChatInput(main:Main):Void {
+ final guestName:InputElement = cast ge("#guestname");
+ guestName.onkeydown = e -> {
+ if (guestName.value.length == 0) return;
+ if (e.keyCode == 13) main.send({
+ type: Login,
+ login: {
+ clientName: guestName.value
+ }
+ });
+ }
+
+ final chatLine:InputElement = cast ge("#chatline");
+ chatLine.onkeydown = function(e:KeyboardEvent) {
+ switch (e.keyCode) {
+ case 13: // Enter
+ if (chatLine.value.length == 0) return;
+ main.send({
+ type: Message,
+ message: {
+ clientName: "",
+ text: chatLine.value
+ }
+ });
+ personalHistory.push(chatLine.value);
+ if (personalHistory.length > 50) personalHistory.shift();
+ personalHistoryId = -1;
+ chatLine.value = "";
+ case 38: // Up
+ personalHistoryId--;
+ if (personalHistoryId == -2) {
+ personalHistoryId = personalHistory.length - 1;
+ if (personalHistoryId == -1) return;
+ } else if (personalHistoryId == -1) personalHistoryId++;
+ chatLine.value = personalHistory[personalHistoryId];
+ case 40: // Down
+ if (personalHistoryId == -1) return;
+ personalHistoryId++;
+ if (personalHistoryId > personalHistory.length - 1) {
+ personalHistoryId = -1;
+ chatLine.value = "";
+ return;
+ }
+ chatLine.value = personalHistory[personalHistoryId];
+ }
+ }
+ }
+
+}
diff --git a/src/client/Main.hx b/src/client/Main.hx
index 5b2dc4c..f9232eb 100644
--- a/src/client/Main.hx
+++ b/src/client/Main.hx
@@ -19,12 +19,10 @@ using ClientTools;
class Main {
final clients:Array<Client> = [];
- final personalHistory:Array<String> = [];
var pageTitle = document.title;
var config:Null<Config>;
final filters:Array<{regex:EReg, replace:String}> = [];
- var personal:Null<Client>;
- var personalHistoryId = -1;
+ var personal = new Client("Unknown", 0);
var isConnected = false;
var ws:WebSocket;
final player:Player;
@@ -70,67 +68,13 @@ class Main {
}
function initListeners():Void {
- final smilesBtn = ge("#smilesbtn");
- smilesBtn.onclick = e -> {
- final smilesWrap = ge("#smileswrap");
- if (smilesWrap.style.display == "")
- smilesWrap.style.display = "block";
- else smilesWrap.style.display = "";
- }
-
- final guestName:InputElement = cast ge("#guestname");
- guestName.onkeydown = (e:KeyboardEvent) -> {
- if (guestName.value.length == 0) return;
- if (e.keyCode == 13) send({
- type: Login,
- login: {
- clientName: guestName.value
- }
- });
- }
-
- final chatLine:InputElement = cast ge("#chatline");
- chatLine.onkeydown = function(e:KeyboardEvent) {
- switch (e.keyCode) {
- case 13: // Enter
- if (chatLine.value.length == 0) return;
- send({
- type: Message,
- message: {
- clientName: "",
- text: chatLine.value
- }
- });
- personalHistory.push(chatLine.value);
- if (personalHistory.length > 50) personalHistory.shift();
- personalHistoryId = -1;
- chatLine.value = "";
- case 38: // Up
- personalHistoryId--;
- if (personalHistoryId == -2) {
- personalHistoryId = personalHistory.length - 1;
- if (personalHistoryId == -1) return;
- } else if (personalHistoryId == -1) personalHistoryId++;
- chatLine.value = personalHistory[personalHistoryId];
- case 40: // Down
- if (personalHistoryId == -1) return;
- personalHistoryId++;
- if (personalHistoryId > personalHistory.length - 1) {
- personalHistoryId = -1;
- chatLine.value = "";
- return;
- }
- chatLine.value = personalHistory[personalHistoryId];
- }
- }
-
+ Buttons.init(this);
MobileView.init();
- final leaderBtn:InputElement = cast ge("#leader_btn");
+ final leaderBtn = ge("#leader_btn");
leaderBtn.onclick = (e) -> {
- if (personal == null) return;
- if (!personal.isLeader) leaderBtn.classList.add('label-success');
- else leaderBtn.classList.remove('label-success');
+ // change button style before answer
+ setLeaderButton(!personal.isLeader);
final name = personal.isLeader ? "" : personal.name;
send({
type: SetLeader,
@@ -140,12 +84,7 @@ class Main {
});
}
- final showMediaUrl:ButtonElement = cast ge("#showmediaurl");
- showMediaUrl.onclick = (e:MouseEvent) -> {
- ge("#showmediaurl").classList.toggle("collapsed");
- ge("#showmediaurl").classList.toggle("active");
- ge("#addfromurl").classList.toggle("collapse");
- }
+ // TODO next/end
ge("#queue_next").onclick = (e:MouseEvent) -> addVideoUrl();
ge("#queue_end").onclick = (e:MouseEvent) -> addVideoUrl();
ge("#mediaurl").onkeydown = function(e:KeyboardEvent) {
@@ -153,26 +92,32 @@ class Main {
}
}
- public function isLeader():Bool {
- return personal != null && personal.isLeader;
+ public inline function isLeader():Bool {
+ return personal.isLeader;
+ }
+
+ public inline function isAdmin():Bool {
+ return personal.isAdmin;
}
function addVideoUrl():Void {
final mediaUrl:InputElement = cast ge("#mediaurl");
final url = mediaUrl.value;
- final name = personal == null ? "Unknown" : personal.name;
+ var name = url.substr(url.lastIndexOf('/') + 1);
+ final matchName = ~/^(.+)\./;
+ if (matchName.match(name)) name = matchName.matched(1);
+ else name = Lang.get("rawVideo");
+
getRemoteVideoDuration(mediaUrl.value, (duration:Float) -> {
send({
- type: AddVideo,
- addVideo: {
+ type: AddVideo, addVideo: {
item: {
url: url,
- title: Lang.get("rawVideo"),
- author: name,
+ title: name,
+ author: personal.name,
duration: duration
}
- }
- });
+ }});
});
mediaUrl.value = "";
}
@@ -181,8 +126,11 @@ class Main {
final player:Element = ge("#ytapiplayer");
final video = document.createVideoElement();
video.src = src;
+ // TODO catch errors on AddVideo and getRemoteVideoDuration
+ video.onerror = e -> {
+ callback(0);
+ }
video.onloadedmetadata = () -> {
- trace(video.duration);
player.removeChild(video);
callback(video.duration);
}
@@ -201,30 +149,7 @@ class Main {
trace('Event: ${data.type}', untyped data[t]);
switch (data.type) {
case Connected:
- setConfig(data.connected.config);
- if (data.connected.isUnknownClient) {
- updateClients(data.connected.clients);
- ge("#guestlogin").style.display = "block";
- ge("#chatline").style.display = "none";
- } else {
- onLogin(data.connected.clients, data.connected.clientName);
- }
- final guestName:InputElement = cast ge("#guestname");
- if (guestName.value.length > 0) send({
- type: Login,
- login: {
- clientName: guestName.value
- }
- });
- for (message in data.connected.history) {
- addMessage(message.name, message.text, message.time);
- }
- final list = data.connected.videoList;
- if (list.length == 0) return;
- player.setVideo(list[0]);
- for (video in data.connected.videoList) {
- player.addVideoItem(video);
- }
+ onConnected(data);
case Login:
onLogin(data.login.clients, data.login.clientName);
case LoginError:
@@ -233,12 +158,11 @@ class Main {
serverMessage(4, text);
case Logout:
updateClients(data.logout.clients);
- personal = null;
- ge("#guestlogin").style.display = "block";
- ge("#chatline").style.display = "none";
+ personal = new Client(data.logout.clientName, 0);
+ showGuestLoginPanel();
case UpdateClients:
updateClients(data.updateClients.clients);
- if (personal != null) personal = clients.getByName(personal.name);
+ personal = clients.getByName(personal.name, personal);
case Message:
addMessage(data.message.clientName, data.message.text);
case AddVideo:
@@ -280,10 +204,38 @@ class Main {
case SetLeader:
clients.setLeader(data.setLeader.clientName);
updateUserList();
- final leaderBtn:InputElement = cast ge("#leader_btn");
- if (isLeader()) leaderBtn.classList.add('label-success');
- else leaderBtn.classList.remove('label-success');
+ setLeaderButton(isLeader());
if (isLeader()) player.setTime(player.getTime(), false);
+ case ClearChat:
+ ge("#messagebuffer").innerHTML = "";
+ }
+ }
+
+ function onConnected(data:WsEvent):Void {
+ final connected = data.connected;
+ setConfig(connected.config);
+ if (connected.isUnknownClient) {
+ updateClients(connected.clients);
+ personal = clients.getByName(connected.clientName, personal);
+ showGuestLoginPanel();
+ } else {
+ onLogin(connected.clients, connected.clientName);
+ }
+ final guestName:InputElement = cast ge("#guestname");
+ if (guestName.value.length > 0) send({
+ type: Login,
+ login: {
+ clientName: guestName.value
+ }
+ });
+ for (message in connected.history) {
+ addMessage(message.name, message.text, message.time);
+ }
+ final list = connected.videoList;
+ if (list.length == 0) return;
+ player.setVideo(list[0]);
+ for (video in connected.videoList) {
+ player.addVideoItem(video);
}
}
@@ -327,8 +279,18 @@ class Main {
function onLogin(data:Array<ClientData>, clientName:String):Void {
updateClients(data);
- personal = clients.getByName(clientName);
- if (personal == null) return;
+ final newPersonal = clients.getByName(clientName);
+ if (newPersonal == null) return;
+ personal = newPersonal;
+ hideGuestLoginPanel();
+ }
+
+ function showGuestLoginPanel():Void {
+ ge("#guestlogin").style.display = "block";
+ ge("#chatline").style.display = "none";
+ }
+
+ function hideGuestLoginPanel():Void {
ge("#guestlogin").style.display = "none";
ge("#chatline").style.display = "block";
}
@@ -376,10 +338,10 @@ class Main {
final list = new StringBuf();
for (client in clients) {
- // final klass = client.isLeader ? "userlist_owner" : "userlist_item";
- final klass = "userlist_item";
+ list.add('<div class="userlist_item">');
if (client.isLeader) list.add('<span class="glyphicon glyphicon-star-empty"></span>');
- list.add('<span class="$klass">${client.name}</span></br>');
+ final klass = client.isAdmin ? "userlist_owner" : "";
+ list.add('<span class="$klass">${client.name}</span></div>');
}
final userlist = ge("#userlist");
userlist.innerHTML = list.toString();
@@ -418,7 +380,7 @@ class Main {
while (msgBuf.children.length > 200) msgBuf.removeChild(msgBuf.firstChild);
msgBuf.scrollTop = msgBuf.scrollHeight;
}
- if (personal != null && personal.name == name) {
+ if (personal.name == name) {
msgBuf.scrollTop = msgBuf.scrollHeight;
}
if (document.hidden && onBlinkTab == null) {
@@ -432,6 +394,13 @@ class Main {
}
}
+ function setLeaderButton(flag:Bool):Void {
+ final leaderBtn = ge("#leader_btn");
+ if (isLeader()) {
+ leaderBtn.classList.add("label-success");
+ } else leaderBtn.classList.remove("label-success");
+ }
+
function escapeRegExp(regex:String):String {
return ~/([.*+?^${}()|[\]\\])/g.replace(regex, "\\$1");
}
diff --git a/src/client/MobileView.hx b/src/client/MobileView.hx
index 558df61..976958b 100644
--- a/src/client/MobileView.hx
+++ b/src/client/MobileView.hx
@@ -1,25 +1,24 @@
package client;
-import js.html.InputElement;
import js.Browser.document;
import client.Main.ge;
class MobileView {
public static function init():Void {
- final mvbtn:InputElement = cast ge("#mv_btn");
- mvbtn.onclick = (e) -> {
- final mobile_view = toggleFullScreen();
- if (mobile_view) {
+ final mvbtn = ge("#mv_btn");
+ mvbtn.onclick = e -> {
+ final mobileView = toggleFullScreen();
+ if (mobileView) {
document.body.classList.add('mobile-view');
- mvbtn.classList.add('label-success');
+ mvbtn.classList.add('active');
final vwrap = ge("#videowrap");
if (vwrap.children[0] == ge("currenttitle")) {
vwrap.appendChild(vwrap.children[0]);
}
} else {
document.body.classList.remove('mobile-view');
- mvbtn.classList.remove('label-success');
+ mvbtn.classList.remove('active');
final vwrap = ge("videowrap");
if (vwrap.children[0] != ge("currenttitle")) {
vwrap.insertBefore(vwrap.children[1],vwrap.children[0]);
diff --git a/src/client/Player.hx b/src/client/Player.hx
index efdb71a..19e0c84 100644
--- a/src/client/Player.hx
+++ b/src/client/Player.hx
@@ -66,6 +66,7 @@ class Player {
}
player.innerHTML = "";
player.appendChild(video);
+ ge("#currenttitle").innerHTML = item.title;
}
public function addVideoItem(item:VideoItem):Void {
@@ -108,6 +109,7 @@ class Player {
public function removeVideo():Void {
player.removeChild(video);
video = null;
+ ge("#currenttitle").innerHTML = Lang.get("nothingPlaying");
}
public function removeItem(url:String):Void {
diff --git a/src/client/Split.hx b/src/client/Split.hx
new file mode 100644
index 0000000..5d9d249
--- /dev/null
+++ b/src/client/Split.hx
@@ -0,0 +1,7 @@
+package client;
+
+@:native("Split")
+extern class Split {
+ function new(divs:Array<String>, opts:Dynamic):Void;
+ function setSizes(sizes:Array<Int>):Void;
+}
diff --git a/src/server/Main.hx b/src/server/Main.hx
index bc52718..45be2af 100644
--- a/src/server/Main.hx
+++ b/src/server/Main.hx
@@ -21,6 +21,7 @@ class Main {
final wss:WSServer;
final config:Config;
final clients:Array<Client> = [];
+ final freeIds:Array<Int> = [];
final videoList:Array<VideoItem> = [];
final videoTimer = new VideoTimer();
final messages:Array<Message> = [];
@@ -72,8 +73,12 @@ class Main {
function onConnect(ws:WebSocket, req):Void {
final ip = req.connection.remoteAddress;
- trace('Client connected ($ip)');
- final client = new Client(ws, "Unknown", false);
+ final id = freeIds.length > 0 ? freeIds.shift() : clients.length;
+ final name = 'Guest ${id + 1}';
+ trace('$name connected ($ip)');
+ final isAdmin = req.connection.localAddress == ip;
+ final client = new Client(ws, id, name, 0);
+ if (isAdmin) client.group.set(Admin);
clients.push(client);
if (clients.length == 1)
if (videoTimer.isPaused()) videoTimer.play();
@@ -98,6 +103,7 @@ class Main {
});
ws.on("close", err -> {
trace('Client ${client.name} disconnected');
+ sortedPush(freeIds, client.id);
clients.remove(client);
sendClientList();
if (client.isLeader) {
@@ -107,6 +113,17 @@ class Main {
});
}
+ function sortedPush(ids:Array<Int>, id:Int):Void {
+ for (i in 0...ids.length) {
+ final n = ids[i];
+ if (id < n) {
+ ids.insert(i, id);
+ return;
+ }
+ }
+ ids.push(id);
+ }
+
function onMessage(client:Client, data:WsEvent):Void {
switch (data.type) {
case Connected:
@@ -131,11 +148,13 @@ class Main {
case LoginError:
case Logout:
final oldName = client.name;
- client.name = "Unknown";
+ final id = clients.indexOf(client) + 1;
+ client.name = 'Guest $id';
send(client, {
type: data.type,
logout: {
- clientName: oldName,
+ oldClientName: oldName,
+ clientName: client.name,
clients: clientList()
}
});
@@ -150,7 +169,7 @@ class Main {
data.message.clientName = client.name;
final time = "[" + new Date().toTimeString().split(" ")[0] + "] ";
messages.push({text: text, name: client.name, time: time});
- if (messages.length > config.serverChatHistory) messages.pop();
+ if (messages.length > config.serverChatHistory) messages.shift();
broadcast(data);
case AddVideo:
videoList.push(data.addVideo.item);
@@ -202,7 +221,11 @@ class Main {
broadcastExcept(client, data);
case SetLeader:
clients.setLeader(data.setLeader.clientName);
- sendClientList();
+ broadcast({
+ type: SetLeader, setLeader: {
+ clientName: data.setLeader.clientName
+ }
+ });
if (videoList.length == 0) return;
if (!clients.hasLeader()) {
if (videoTimer.isPaused()) videoTimer.play();
@@ -212,6 +235,8 @@ class Main {
}
});
}
+ case ClearChat:
+ if (client.isAdmin) broadcast(data);
}
}
send patches to the email below
yukais@pinapelz.com
include the subject [PATCH repo_name]
pinapelz.com
homepage