6e416fea8a
Many older devices do not support the Let's Encrypt CA, for various reasons. This causes connection issues for sites using Let's Encrypt to support HTTPS connections. This commit adds a hack that can be enabled with a switch in callbacks.js to try to detect when the user's browser does not trust the certificate and permit the user to connect to an insecure endpoint instead. Unfortunately, the AJAX API does not allow to distinguish between *why* a request fails, so the best we can do is detect that the HTTPS request failed, try to make a request over plain HTTP, and if it works, assume the HTTPS request failed due to a certificate error. It's not 100% foolproof since the HTTPS endpoint could just be down for some reason, but it should work well enough in most cases. Closes #602
1214 lines
37 KiB
JavaScript
1214 lines
37 KiB
JavaScript
Callbacks = {
|
|
|
|
error: function (reason) {
|
|
window.SOCKET_ERROR_REASON = reason;
|
|
},
|
|
|
|
/* fired when socket connection completes */
|
|
connect: function() {
|
|
HAS_CONNECTED_BEFORE = true;
|
|
SOCKETIO_CONNECT_ERROR_COUNT = 0;
|
|
$("#socketio-connect-error").remove();
|
|
socket.emit("joinChannel", {
|
|
name: CHANNEL.name
|
|
});
|
|
|
|
if (CHANNEL.opts.password) {
|
|
socket.emit("channelPassword", CHANNEL.opts.password);
|
|
}
|
|
|
|
if (CLIENT.name && CLIENT.guest) {
|
|
socket.emit("login", {
|
|
name: CLIENT.name
|
|
});
|
|
}
|
|
|
|
$("<div/>").addClass("server-msg-reconnect")
|
|
.text("Connected")
|
|
.appendTo($("#messagebuffer"));
|
|
scrollChat();
|
|
stopQueueSpinner(null);
|
|
},
|
|
|
|
disconnect: function() {
|
|
if(KICKED)
|
|
return;
|
|
$("<div/>")
|
|
.addClass("server-msg-disconnect")
|
|
.text("Disconnected from server.")
|
|
.appendTo($("#messagebuffer"));
|
|
scrollChat();
|
|
},
|
|
|
|
errorMsg: function(data) {
|
|
if (data.alert) {
|
|
alert(data.msg);
|
|
} else {
|
|
errDialog(data.msg);
|
|
}
|
|
},
|
|
|
|
costanza: function (data) {
|
|
hidePlayer();
|
|
$("#costanza-modal").modal("hide");
|
|
var modal = makeModal();
|
|
modal.attr("id", "costanza-modal")
|
|
.appendTo($("body"));
|
|
|
|
var body = $("<div/>").addClass("modal-body")
|
|
.appendTo(modal.find(".modal-content"));
|
|
$("<img/>").attr("src", "http://i0.kym-cdn.com/entries/icons/original/000/005/498/1300044776986.jpg")
|
|
.appendTo(body);
|
|
|
|
$("<strong/>").text(data.msg).appendTo(body);
|
|
hidePlayer();
|
|
modal.modal();
|
|
},
|
|
|
|
announcement: function(data) {
|
|
$("#announcements").html("");
|
|
var signature = "<br>\u2014" + data.from;
|
|
var announcement = makeAlert(data.title, data.text + signature)
|
|
.appendTo($("#announcements"));
|
|
},
|
|
|
|
kick: function(data) {
|
|
KICKED = true;
|
|
$("<div/>").addClass("server-msg-disconnect")
|
|
.text("Kicked: " + data.reason)
|
|
.appendTo($("#messagebuffer"));
|
|
scrollChat();
|
|
},
|
|
|
|
noflood: function(data) {
|
|
$("<div/>")
|
|
.addClass("server-msg-disconnect")
|
|
.text(data.action + ": " + data.msg)
|
|
.appendTo($("#messagebuffer"));
|
|
scrollChat();
|
|
},
|
|
|
|
needPassword: function (wrongpw) {
|
|
var div = $("<div/>");
|
|
$("<strong/>").text("Channel Password")
|
|
.appendTo(div);
|
|
if (wrongpw) {
|
|
$("<br/>").appendTo(div);
|
|
$("<span/>").addClass("text-error")
|
|
.text("Wrong Password")
|
|
.appendTo(div);
|
|
}
|
|
|
|
var pwbox = $("<input/>").addClass("form-control")
|
|
.attr("type", "password")
|
|
.appendTo(div);
|
|
var submit = $("<button/>").addClass("btn btn-xs btn-default btn-block")
|
|
.css("margin-top", "5px")
|
|
.text("Submit")
|
|
.appendTo(div);
|
|
var parent = chatDialog(div);
|
|
parent.attr("id", "needpw");
|
|
var sendpw = function () {
|
|
socket.emit("channelPassword", pwbox.val());
|
|
parent.remove();
|
|
};
|
|
submit.click(sendpw);
|
|
pwbox.keydown(function (ev) {
|
|
if (ev.keyCode == 13) {
|
|
sendpw();
|
|
}
|
|
});
|
|
pwbox.focus();
|
|
},
|
|
|
|
cancelNeedPassword: function () {
|
|
$("#needpw").remove();
|
|
},
|
|
|
|
cooldown: function (time) {
|
|
time = time + 200;
|
|
$("#chatline").css("color", "#ff0000");
|
|
$(".pm-input").css("color", "#ff0000");
|
|
if (CHATTHROTTLE && $("#chatline").data("throttle_timer")) {
|
|
clearTimeout($("#chatline").data("throttle_timer"));
|
|
}
|
|
CHATTHROTTLE = true;
|
|
$("#chatline").data("throttle_timer", setTimeout(function () {
|
|
CHATTHROTTLE = false;
|
|
$("#chatline").css("color", "");
|
|
$(".pm-input").css("color", "");
|
|
}, time));
|
|
},
|
|
|
|
channelNotRegistered: function() {
|
|
var div = $("<div/>").addClass("alert alert-info")
|
|
.appendTo($("<div/>").addClass("col-md-12").appendTo($("#announcements")));
|
|
|
|
$("<button/>").addClass("close pull-right")
|
|
.appendTo(div)
|
|
.click(function () {
|
|
div.parent().remove();
|
|
})
|
|
.html("×");
|
|
$("<h4/>").appendTo(div).text("Unregistered channel");
|
|
$("<p/>").appendTo(div)
|
|
.html("This channel is not registered to a CyTube account. You can still " +
|
|
"use it, but some features will not be available. To register a " +
|
|
"channel to your account, visit your <a href='/account/channels'>" +
|
|
"channels</a> page.");
|
|
},
|
|
|
|
setMotd: function(motd) {
|
|
CHANNEL.motd = motd;
|
|
$("#motd").html(motd);
|
|
$("#cs-motdtext").val(motd);
|
|
if (motd != "") {
|
|
$("#motdwrap").show();
|
|
$("#motd").show();
|
|
$("#togglemotd").find(".glyphicon-plus")
|
|
.removeClass("glyphicon-plus")
|
|
.addClass("glyphicon-minus");
|
|
} else {
|
|
$("#motdwrap").hide();
|
|
}
|
|
},
|
|
|
|
chatFilters: function(entries) {
|
|
var tbl = $("#cs-chatfilters table");
|
|
tbl.data("entries", entries);
|
|
formatCSChatFilterList();
|
|
},
|
|
|
|
updateChatFilter: function (f) {
|
|
var entries = $("#cs-chatfilters table").data("entries") || [];
|
|
var found = false;
|
|
for (var i = 0; i < entries.length; i++) {
|
|
if (entries[i].name === f.name) {
|
|
entries[i] = f;
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found) {
|
|
entries.push(f);
|
|
}
|
|
|
|
$("#cs-chatfilters table").data("entries", entries);
|
|
formatCSChatFilterList();
|
|
},
|
|
|
|
deleteChatFilter: function (f) {
|
|
var entries = $("#cs-chatfilters table").data("entries") || [];
|
|
var found = false;
|
|
for (var i = 0; i < entries.length; i++) {
|
|
if (entries[i].name === f.name) {
|
|
entries[i] = f;
|
|
found = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (found !== false) {
|
|
entries.splice(found, 1);
|
|
}
|
|
|
|
$("#cs-chatfilters table").data("entries", entries);
|
|
formatCSChatFilterList();
|
|
},
|
|
|
|
channelOpts: function(opts) {
|
|
document.title = opts.pagetitle;
|
|
PAGETITLE = opts.pagetitle;
|
|
|
|
if (!USEROPTS.ignore_channelcss &&
|
|
opts.externalcss !== CHANNEL.opts.externalcss) {
|
|
$("#chanexternalcss").remove();
|
|
|
|
if (opts.externalcss.trim() !== "") {
|
|
$("#chanexternalcss").remove();
|
|
$("<link/>")
|
|
.attr("rel", "stylesheet")
|
|
.attr("href", opts.externalcss)
|
|
.attr("id", "chanexternalcss")
|
|
.appendTo($("head"));
|
|
}
|
|
}
|
|
|
|
if(opts.externaljs.trim() != "" && !USEROPTS.ignore_channeljs &&
|
|
opts.externaljs !== CHANNEL.opts.externaljs) {
|
|
checkScriptAccess(opts.externaljs, "external", function (pref) {
|
|
if (pref === "ALLOW") {
|
|
$.getScript(opts.externaljs);
|
|
}
|
|
});
|
|
}
|
|
|
|
CHANNEL.opts = opts;
|
|
|
|
if(opts.allow_voteskip)
|
|
$("#voteskip").attr("disabled", false);
|
|
else
|
|
$("#voteskip").attr("disabled", true);
|
|
handlePermissionChange();
|
|
},
|
|
|
|
setPermissions: function(perms) {
|
|
CHANNEL.perms = perms;
|
|
genPermissionsEditor();
|
|
handlePermissionChange();
|
|
},
|
|
|
|
channelCSSJS: function(data) {
|
|
$("#chancss").remove();
|
|
CHANNEL.css = data.css;
|
|
$("#cs-csstext").val(data.css);
|
|
if(data.css && !USEROPTS.ignore_channelcss) {
|
|
$("<style/>").attr("type", "text/css")
|
|
.attr("id", "chancss")
|
|
.text(data.css)
|
|
.appendTo($("head"));
|
|
}
|
|
|
|
$("#chanjs").remove();
|
|
CHANNEL.js = data.js;
|
|
$("#cs-jstext").val(data.js);
|
|
|
|
if(data.js && !USEROPTS.ignore_channeljs) {
|
|
var src = data.js
|
|
.replace(/&/g, "&")
|
|
.replace(/</g, "<")
|
|
.replace(/>/g, ">")
|
|
.replace(/\n/g, "<br>")
|
|
.replace(/\t/g, " ")
|
|
.replace(/ /g, " ");
|
|
src = encodeURIComponent(src);
|
|
|
|
var viewsource = "data:text/html, <body style='font: 9pt monospace;" +
|
|
"max-width:60rem;margin:0 auto;padding:4rem;'>" +
|
|
src + "</body>";
|
|
checkScriptAccess(viewsource, "embedded", function (pref) {
|
|
if (pref === "ALLOW") {
|
|
$("<script/>").attr("type", "text/javascript")
|
|
.attr("id", "chanjs")
|
|
.text(data.js)
|
|
.appendTo($("body"));
|
|
}
|
|
});
|
|
}
|
|
},
|
|
|
|
banlist: function(entries) {
|
|
var tbl = $("#cs-banlist table");
|
|
tbl.data("entries", entries);
|
|
formatCSBanlist();
|
|
},
|
|
|
|
banlistRemove: function (data) {
|
|
var entries = $("#cs-banlist table").data("entries") || [];
|
|
var found = false;
|
|
for (var i = 0; i < entries.length; i++) {
|
|
if (entries[i].id === data.id) {
|
|
found = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (found !== false) {
|
|
entries.splice(i, 1);
|
|
$("#cs-banlist table").data("entries", entries);
|
|
}
|
|
|
|
formatCSBanlist();
|
|
},
|
|
|
|
channelRanks: function(entries) {
|
|
var tbl = $("#cs-chanranks table");
|
|
tbl.data("entries", entries);
|
|
formatCSModList();
|
|
},
|
|
|
|
channelRankFail: function (data) {
|
|
if ($("#cs-chanranks").is(":visible")) {
|
|
makeAlert("Error", data.msg, "alert-danger")
|
|
.removeClass().addClass("vertical-spacer")
|
|
.insertAfter($("#cs-chanranks form"));
|
|
} else {
|
|
Callbacks.noflood({ action: "/rank", msg: data.msg });
|
|
}
|
|
},
|
|
|
|
readChanLog: function (data) {
|
|
var log = $("#cs-chanlog-text");
|
|
if (log.length == 0)
|
|
return;
|
|
|
|
if (data.success) {
|
|
setupChanlogFilter(data.data);
|
|
filterChannelLog();
|
|
} else {
|
|
$("#cs-chanlog-text").text("Error reading channel log");
|
|
}
|
|
},
|
|
|
|
voteskip: function(data) {
|
|
var icon = $("#voteskip").find(".glyphicon").remove();
|
|
if(data.count > 0) {
|
|
$("#voteskip").text(" ("+data.count+"/"+data.need+")");
|
|
} else {
|
|
$("#voteskip").text("");
|
|
}
|
|
|
|
icon.prependTo($("#voteskip"));
|
|
},
|
|
|
|
/* REGION Rank Stuff */
|
|
|
|
rank: function(r) {
|
|
if(r >= 255)
|
|
SUPERADMIN = true;
|
|
CLIENT.rank = r;
|
|
handlePermissionChange();
|
|
if(SUPERADMIN && $("#setrank").length == 0) {
|
|
var li = $("<li/>").addClass("dropdown")
|
|
.attr("id", "setrank")
|
|
.appendTo($(".nav")[0]);
|
|
$("<a/>").addClass("dropdown-toggle")
|
|
.attr("data-toggle", "dropdown")
|
|
.attr("href", "javascript:void(0)")
|
|
.html("Set Rank <b class='caret'></b>")
|
|
.appendTo(li);
|
|
var menu = $("<ul/>").addClass("dropdown-menu")
|
|
.appendTo(li);
|
|
|
|
function addRank(r, disp) {
|
|
var li = $("<li/>").appendTo(menu);
|
|
$("<a/>").attr("href", "javascript:void(0)")
|
|
.html(disp)
|
|
.click(function() {
|
|
socket.emit("borrow-rank", r);
|
|
})
|
|
.appendTo(li);
|
|
}
|
|
|
|
addRank(0, "<span class='userlist_guest'>Guest</span>");
|
|
addRank(1, "<span>Registered</span>");
|
|
addRank(2, "<span class='userlist_op'>Moderator</span>");
|
|
addRank(3, "<span class='userlist_owner'>Admin</span>");
|
|
addRank(255, "<span class='userlist_siteadmin'>Superadmin</span>");
|
|
}
|
|
},
|
|
|
|
login: function(data) {
|
|
if (!data.success) {
|
|
if (data.error != "Session expired") {
|
|
errDialog(data.error);
|
|
}
|
|
} else {
|
|
CLIENT.name = data.name;
|
|
CLIENT.guest = data.guest;
|
|
CLIENT.logged_in = true;
|
|
|
|
if (!CLIENT.guest) {
|
|
socket.emit("initUserPLCallbacks");
|
|
if ($("#loginform").length === 0) {
|
|
return;
|
|
}
|
|
var logoutform = $("<p/>").attr("id", "logoutform")
|
|
.addClass("navbar-text pull-right")
|
|
.insertAfter($("#loginform"));
|
|
|
|
$("<span/>").attr("id", "welcome").text("Welcome, " + CLIENT.name)
|
|
.appendTo(logoutform);
|
|
$("<span/>").html(" · ").appendTo(logoutform);
|
|
var domain = $("#loginform").attr("action").replace("/login", "");
|
|
$("<a/>").attr("id", "logout")
|
|
.attr("href", domain + "/logout?redirect=/r/" + CHANNEL.name)
|
|
.text("Logout")
|
|
.appendTo(logoutform);
|
|
|
|
$("#loginform").remove();
|
|
}
|
|
}
|
|
},
|
|
|
|
/* REGION Chat */
|
|
usercount: function(count) {
|
|
CHANNEL.usercount = count;
|
|
var text = count + " connected user";
|
|
if(count != 1) {
|
|
text += "s";
|
|
}
|
|
$("#usercount").text(text);
|
|
},
|
|
|
|
chatMsg: function(data) {
|
|
addChatMessage(data);
|
|
},
|
|
|
|
pm: function (data) {
|
|
var name = data.username;
|
|
if (IGNORED.indexOf(name) !== -1) {
|
|
return;
|
|
}
|
|
|
|
if (data.username === CLIENT.name) {
|
|
name = data.to;
|
|
} else {
|
|
pingMessage(true);
|
|
}
|
|
var pm = initPm(name);
|
|
var msg = formatChatMessage(data, pm.data("last"));
|
|
var buffer = pm.find(".pm-buffer");
|
|
msg.appendTo(buffer);
|
|
buffer.scrollTop(buffer.prop("scrollHeight"));
|
|
if (pm.find(".panel-body").is(":hidden")) {
|
|
pm.removeClass("panel-default").addClass("panel-primary");
|
|
}
|
|
},
|
|
|
|
clearchat: function() {
|
|
$("#messagebuffer").html("");
|
|
LASTCHAT.name = "";
|
|
},
|
|
|
|
userlist: function(data) {
|
|
$(".userlist_item").remove();
|
|
for(var i = 0; i < data.length; i++) {
|
|
Callbacks.addUser(data[i]);
|
|
}
|
|
},
|
|
|
|
addUser: function(data) {
|
|
var user = findUserlistItem(data.name);
|
|
// Remove previous instance of user, if there was one
|
|
if(user !== null)
|
|
user.remove();
|
|
var div = $("<div/>")
|
|
.addClass("userlist_item");
|
|
var icon = $("<span/>").appendTo(div);
|
|
var nametag = $("<span/>").text(data.name).appendTo(div);
|
|
div.data("name", data.name);
|
|
div.data("rank", data.rank);
|
|
div.data("leader", Boolean(data.leader));
|
|
div.data("profile", data.profile);
|
|
div.data("meta", data.meta);
|
|
div.data("afk", data.meta.afk);
|
|
if (data.meta.muted || data.meta.smuted) {
|
|
div.data("icon", "glyphicon-volume-off");
|
|
} else {
|
|
div.data("icon", false);
|
|
}
|
|
formatUserlistItem(div, data);
|
|
addUserDropdown(div, data);
|
|
div.appendTo($("#userlist"));
|
|
sortUserlist();
|
|
},
|
|
|
|
setUserMeta: function (data) {
|
|
var user = findUserlistItem(data.name);
|
|
if (user == null) {
|
|
return;
|
|
}
|
|
|
|
user.data("meta", data.meta);
|
|
if (data.meta.muted || data.meta.smuted) {
|
|
user.data("icon", "glyphicon-volume-off");
|
|
} else {
|
|
user.data("icon", false);
|
|
}
|
|
|
|
formatUserlistItem(user, data);
|
|
addUserDropdown(user, data);
|
|
sortUserlist();
|
|
},
|
|
|
|
setUserProfile: function (data) {
|
|
var user = findUserlistItem(data.name);
|
|
if (user === null)
|
|
return;
|
|
user.data("profile", data.profile);
|
|
formatUserlistItem(user);
|
|
},
|
|
|
|
setLeader: function (name) {
|
|
$(".userlist_item").each(function () {
|
|
$(this).find(".glyphicon-star-empty").remove();
|
|
if ($(this).data("leader")) {
|
|
$(this).data("leader", false);
|
|
addUserDropdown($(this));
|
|
}
|
|
});
|
|
if (name === "") {
|
|
CLIENT.leader = false;
|
|
if(LEADTMR)
|
|
clearInterval(LEADTMR);
|
|
LEADTMR = false;
|
|
return;
|
|
}
|
|
var user = findUserlistItem(name);
|
|
if (user) {
|
|
user.data("leader", true);
|
|
formatUserlistItem(user);
|
|
addUserDropdown(user);
|
|
}
|
|
if (name === CLIENT.name) {
|
|
CLIENT.leader = true;
|
|
// I'm a leader! Set up sync function
|
|
if(LEADTMR)
|
|
clearInterval(LEADTMR);
|
|
LEADTMR = setInterval(sendVideoUpdate, 5000);
|
|
handlePermissionChange();
|
|
} else if (CLIENT.leader) {
|
|
CLIENT.leader = false;
|
|
handlePermissionChange();
|
|
if(LEADTMR)
|
|
clearInterval(LEADTMR);
|
|
LEADTMR = false;
|
|
}
|
|
},
|
|
|
|
setUserRank: function (data) {
|
|
data.name = data.name.toLowerCase();
|
|
var entries = $("#cs-chanranks table").data("entries") || [];
|
|
var found = false;
|
|
for (var i = 0; i < entries.length; i++) {
|
|
if (entries[i].name.toLowerCase() === data.name) {
|
|
entries[i].rank = data.rank;
|
|
found = i;
|
|
break;
|
|
}
|
|
}
|
|
if (found === false) {
|
|
entries.push(data);
|
|
} else if (entries[found].rank < 2) {
|
|
entries.splice(found, 1);
|
|
}
|
|
formatCSModList();
|
|
|
|
var user = findUserlistItem(data.name);
|
|
if (user === null) {
|
|
return;
|
|
}
|
|
|
|
user.data("rank", data.rank);
|
|
if (data.name === CLIENT.name) {
|
|
CLIENT.rank = data.rank;
|
|
handlePermissionChange();
|
|
}
|
|
formatUserlistItem(user);
|
|
addUserDropdown(user);
|
|
if (USEROPTS.sort_rank) {
|
|
sortUserlist();
|
|
}
|
|
},
|
|
|
|
setUserIcon: function (data) {
|
|
var user = findUserlistItem(data.name);
|
|
if (user === null) {
|
|
return;
|
|
}
|
|
|
|
user.data("icon", data.icon);
|
|
formatUserlistItem(user);
|
|
},
|
|
|
|
setAFK: function (data) {
|
|
var user = findUserlistItem(data.name);
|
|
if(user === null)
|
|
return;
|
|
user.data("afk", data.afk);
|
|
formatUserlistItem(user);
|
|
if(USEROPTS.sort_afk)
|
|
sortUserlist();
|
|
},
|
|
|
|
userLeave: function(data) {
|
|
var user = findUserlistItem(data.name);
|
|
if(user !== null)
|
|
user.remove();
|
|
},
|
|
|
|
drinkCount: function(count) {
|
|
if(count != 0) {
|
|
var text = count + " drink";
|
|
if(count != 1) {
|
|
text += "s";
|
|
}
|
|
$("#drinkcount").text(text);
|
|
$("#drinkbar").show();
|
|
}
|
|
else {
|
|
$("#drinkbar").hide();
|
|
}
|
|
},
|
|
|
|
/* REGION Playlist Stuff */
|
|
playlist: function(data) {
|
|
PL_QUEUED_ACTIONS = [];
|
|
// Clear the playlist first
|
|
var q = $("#queue");
|
|
q.html("");
|
|
|
|
for(var i = 0; i < data.length; i++) {
|
|
var li = makeQueueEntry(data[i], false);
|
|
li.attr("title", data[i].queueby
|
|
? ("Added by: " + data[i].queueby)
|
|
: "Added by: Unknown");
|
|
li.appendTo(q);
|
|
}
|
|
|
|
rebuildPlaylist();
|
|
},
|
|
|
|
setPlaylistMeta: function(data) {
|
|
var c = data.count + " item";
|
|
if(data.count != 1)
|
|
c += "s";
|
|
$("#plcount").text(c);
|
|
$("#pllength").text(data.time);
|
|
},
|
|
|
|
queue: function(data) {
|
|
PL_ACTION_QUEUE.queue(function (plq) {
|
|
stopQueueSpinner(data.item.media);
|
|
var li = makeQueueEntry(data.item, true);
|
|
if (data.item.uid === PL_CURRENT)
|
|
li.addClass("queue_active");
|
|
li.hide();
|
|
var q = $("#queue");
|
|
li.attr("title", data.item.queueby
|
|
? ("Added by: " + data.item.queueby)
|
|
: "Added by: Unknown");
|
|
if (data.after === "prepend") {
|
|
li.prependTo(q);
|
|
li.show("fade", function () {
|
|
plq.release();
|
|
});
|
|
} else if (data.after === "append") {
|
|
li.appendTo(q);
|
|
li.show("fade", function () {
|
|
plq.release();
|
|
});
|
|
} else {
|
|
var liafter = playlistFind(data.after);
|
|
if (!liafter) {
|
|
plq.release();
|
|
return;
|
|
}
|
|
li.insertAfter(liafter);
|
|
li.show("fade", function () {
|
|
plq.release();
|
|
});
|
|
}
|
|
});
|
|
},
|
|
|
|
queueWarn: function (data) {
|
|
queueMessage(data, "alert-warning");
|
|
},
|
|
|
|
queueFail: function (data) {
|
|
if (data.id) {
|
|
stopQueueSpinner(data);
|
|
}
|
|
queueMessage(data, "alert-danger");
|
|
},
|
|
|
|
setTemp: function(data) {
|
|
var li = $(".pluid-" + data.uid);
|
|
if(li.length == 0)
|
|
return false;
|
|
|
|
if(data.temp)
|
|
li.addClass("queue_temp");
|
|
else
|
|
li.removeClass("queue_temp");
|
|
|
|
li.data("temp", data.temp);
|
|
var btn = li.find(".qbtn-tmp");
|
|
if(btn.length > 0) {
|
|
if(data.temp) {
|
|
btn.html(btn.html().replace("Make Temporary",
|
|
"Make Permanent"));
|
|
}
|
|
else {
|
|
btn.html(btn.html().replace("Make Permanent",
|
|
"Make Temporary"));
|
|
}
|
|
}
|
|
},
|
|
|
|
"delete": function(data) {
|
|
PL_ACTION_QUEUE.queue(function (plq) {
|
|
PL_WAIT_SCROLL = true;
|
|
var li = $(".pluid-" + data.uid);
|
|
li.hide("fade", function() {
|
|
li.remove();
|
|
plq.release();
|
|
PL_WAIT_SCROLL = false;
|
|
});
|
|
});
|
|
},
|
|
|
|
moveVideo: function(data) {
|
|
PL_ACTION_QUEUE.queue(function (plq) {
|
|
playlistMove(data.from, data.after, function () {
|
|
plq.release();
|
|
});
|
|
});
|
|
},
|
|
|
|
setCurrent: function(uid) {
|
|
PL_CURRENT = uid;
|
|
$("#queue li").removeClass("queue_active");
|
|
var li = $(".pluid-" + uid);
|
|
if (li.length !== 0) {
|
|
li.addClass("queue_active");
|
|
var tmr = setInterval(function () {
|
|
if (!PL_WAIT_SCROLL) {
|
|
scrollQueue();
|
|
clearInterval(tmr);
|
|
}
|
|
}, 100);
|
|
}
|
|
},
|
|
|
|
changeMedia: function(data) {
|
|
if ($("body").hasClass("chatOnly") || $("#videowrap").length === 0) {
|
|
return;
|
|
}
|
|
|
|
// Failsafe
|
|
if (isNaN(VOLUME) || VOLUME > 1 || VOLUME < 0) {
|
|
VOLUME = 1;
|
|
}
|
|
|
|
function loadNext() {
|
|
if (!PLAYER || data.type !== PLAYER.mediaType) {
|
|
loadMediaPlayer(data);
|
|
}
|
|
|
|
handleMediaUpdate(data);
|
|
}
|
|
|
|
// Persist the user's volume preference from the the player, if possible
|
|
if (PLAYER && typeof PLAYER.getVolume === "function") {
|
|
PLAYER.getVolume(function (v) {
|
|
if (typeof v === "number") {
|
|
if (v < 0 || v > 1) {
|
|
// Dailymotion's API was wrong once and caused a huge
|
|
// headache. This alert is here to make debugging easier.
|
|
alert("Something went wrong with retrieving the volume. " +
|
|
"Please tell calzoneman the following: " +
|
|
JSON.stringify({
|
|
v: v,
|
|
t: PLAYER.mediaType,
|
|
i: PLAYER.mediaId
|
|
}));
|
|
} else {
|
|
VOLUME = v;
|
|
setOpt("volume", VOLUME);
|
|
}
|
|
}
|
|
|
|
loadNext();
|
|
});
|
|
} else {
|
|
loadNext();
|
|
}
|
|
|
|
// Reset voteskip since the video changed
|
|
if (CHANNEL.opts.allow_voteskip) {
|
|
$("#voteskip").attr("disabled", false);
|
|
}
|
|
|
|
$("#currenttitle").text("Currently Playing: " + data.title);
|
|
},
|
|
|
|
mediaUpdate: function(data) {
|
|
if ($("body").hasClass("chatOnly") || $("#videowrap").length === 0) {
|
|
return;
|
|
}
|
|
|
|
if (PLAYER) {
|
|
handleMediaUpdate(data);
|
|
}
|
|
},
|
|
|
|
setPlaylistLocked: function (locked) {
|
|
CHANNEL.openqueue = !locked;
|
|
handlePermissionChange();
|
|
if(CHANNEL.openqueue) {
|
|
$("#qlockbtn").removeClass("btn-danger")
|
|
.addClass("btn-success")
|
|
.attr("title", "Playlist Unlocked");
|
|
$("#qlockbtn").find("span")
|
|
.removeClass("glyphicon-lock")
|
|
.addClass("glyphicon-ok");
|
|
}
|
|
else {
|
|
$("#qlockbtn").removeClass("btn-success")
|
|
.addClass("btn-danger")
|
|
.attr("title", "Playlist Locked");
|
|
$("#qlockbtn").find("span")
|
|
.removeClass("glyphicon-ok")
|
|
.addClass("glyphicon-lock");
|
|
}
|
|
},
|
|
|
|
searchResults: function(data) {
|
|
$("#search_clear").remove();
|
|
clearSearchResults();
|
|
$("#library").data("entries", data.results);
|
|
$("<button/>").addClass("btn btn-default btn-sm btn-block")
|
|
.css("margin-left", "0")
|
|
.attr("id", "search_clear")
|
|
.text("Clear Results")
|
|
.click(function() {
|
|
clearSearchResults();
|
|
})
|
|
.insertBefore($("#library"));
|
|
|
|
|
|
$("#search_pagination").remove();
|
|
var opts = {
|
|
preLoadPage: function () {
|
|
$("#library").html("");
|
|
},
|
|
|
|
generator: function (item, page, index) {
|
|
var li = makeSearchEntry(item, false);
|
|
if(hasPermission("playlistadd")) {
|
|
addLibraryButtons(li, item.id, data.source);
|
|
}
|
|
$(li).appendTo($("#library"));
|
|
},
|
|
|
|
itemsPerPage: 100
|
|
};
|
|
|
|
var p = Paginate(data.results, opts);
|
|
p.paginator.insertAfter($("#library"))
|
|
.addClass("pull-right")
|
|
.attr("id", "search_pagination");
|
|
$("#library").data("paginator", p);
|
|
},
|
|
|
|
/* REGION Polls */
|
|
newPoll: function(data) {
|
|
Callbacks.closePoll();
|
|
var pollMsg = $("<div/>").addClass("poll-notify")
|
|
.html(data.initiator + " opened a poll: \"" + data.title + "\"")
|
|
.appendTo($("#messagebuffer"));
|
|
scrollChat();
|
|
|
|
var poll = $("<div/>").addClass("well active").prependTo($("#pollwrap"));
|
|
$("<button/>").addClass("close pull-right").html("×")
|
|
.appendTo(poll)
|
|
.click(function() { poll.remove(); });
|
|
if(hasPermission("pollctl")) {
|
|
$("<button/>").addClass("btn btn-danger btn-sm pull-right").text("End Poll")
|
|
.appendTo(poll)
|
|
.click(function() {
|
|
socket.emit("closePoll")
|
|
});
|
|
}
|
|
|
|
$("<h3/>").html(data.title).appendTo(poll);
|
|
for(var i = 0; i < data.options.length; i++) {
|
|
(function(i) {
|
|
var callback = function () {
|
|
socket.emit("vote", {
|
|
option: i
|
|
});
|
|
poll.find(".option button").each(function() {
|
|
$(this).attr("disabled", "disabled");
|
|
});
|
|
$(this).parent().addClass("option-selected");
|
|
}
|
|
$("<button/>").addClass("btn btn-default btn-sm").text(data.counts[i])
|
|
.prependTo($("<div/>").addClass("option").html(data.options[i])
|
|
.appendTo(poll))
|
|
.click(callback);
|
|
})(i);
|
|
|
|
}
|
|
$("<span/>").addClass("label label-default pull-right").data('timestamp',data.timestamp).appendTo(poll)
|
|
.text(new Date(data.timestamp).toTimeString().split(" ")[0]);
|
|
|
|
poll.find(".btn").attr("disabled", !hasPermission("pollvote"));
|
|
},
|
|
|
|
updatePoll: function(data) {
|
|
var poll = $("#pollwrap .active");
|
|
var i = 0;
|
|
poll.find(".option button").each(function() {
|
|
$(this).html(data.counts[i]);
|
|
i++;
|
|
});
|
|
},
|
|
|
|
closePoll: function() {
|
|
if($("#pollwrap .active").length != 0) {
|
|
var poll = $("#pollwrap .active");
|
|
poll.removeClass("active").addClass("muted");
|
|
poll.find(".option button").each(function() {
|
|
$(this).attr("disabled", true);
|
|
});
|
|
poll.find(".btn-danger").each(function() {
|
|
$(this).remove()
|
|
});
|
|
}
|
|
},
|
|
|
|
listPlaylists: function(data) {
|
|
$("#userpl_list").data("entries", data);
|
|
formatUserPlaylistList();
|
|
},
|
|
|
|
emoteList: function (data) {
|
|
loadEmotes(data);
|
|
EMOTELIST.handleChange();
|
|
CSEMOTELIST.handleChange();
|
|
},
|
|
|
|
updateEmote: function (data) {
|
|
data.regex = new RegExp(data.source, "gi");
|
|
var found = false;
|
|
for (var i = 0; i < CHANNEL.emotes.length; i++) {
|
|
if (CHANNEL.emotes[i].name === data.name) {
|
|
found = true;
|
|
CHANNEL.emotes[i] = data;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found) {
|
|
CHANNEL.emotes.push(data);
|
|
}
|
|
|
|
EMOTELIST.handleChange();
|
|
CSEMOTELIST.handleChange();
|
|
},
|
|
|
|
removeEmote: function (data) {
|
|
var found = -1;
|
|
for (var i = 0; i < CHANNEL.emotes.length; i++) {
|
|
if (CHANNEL.emotes[i].name === data.name) {
|
|
found = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (found !== -1) {
|
|
var row = $("code:contains('" + data.name + "')").parent().parent();
|
|
row.hide("fade", row.remove.bind(row));
|
|
CHANNEL.emotes.splice(i, 1);
|
|
}
|
|
},
|
|
|
|
warnLargeChandump: function (data) {
|
|
function toHumanReadable(size) {
|
|
if (size > 1048576) {
|
|
return Math.floor((size / 1048576) * 100) / 100 + "MiB";
|
|
} else if (size > 1024) {
|
|
return Math.floor((size / 1024) * 100) / 100 + "KiB";
|
|
} else {
|
|
return size + "B";
|
|
}
|
|
}
|
|
|
|
if ($("#chandumptoobig").length > 0) {
|
|
$("#chandumptoobig").remove();
|
|
}
|
|
|
|
errDialog("This channel currently exceeds the maximum size of " +
|
|
toHumanReadable(data.limit) + " (channel size is " +
|
|
toHumanReadable(data.actual) + "). Please reduce the size by removing " +
|
|
"unneeded playlist items, filters, and/or emotes. Changes to the channel " +
|
|
"will not be saved until the size is reduced to under the limit.")
|
|
.attr("id", "chandumptoobig");
|
|
},
|
|
|
|
partitionChange: function (socketConfig) {
|
|
window.socket.disconnect();
|
|
HAS_CONNECTED_BEFORE = false;
|
|
ioServerConnect(socketConfig);
|
|
setupCallbacks();
|
|
}
|
|
}
|
|
|
|
var SOCKET_DEBUG = localStorage.getItem('cytube_socket_debug') === 'true';
|
|
setupCallbacks = function() {
|
|
for(var key in Callbacks) {
|
|
(function(key) {
|
|
socket.on(key, function(data) {
|
|
if (SOCKET_DEBUG) {
|
|
console.log(key, data);
|
|
}
|
|
try {
|
|
Callbacks[key](data);
|
|
} catch (e) {
|
|
if (SOCKET_DEBUG) {
|
|
console.log("EXCEPTION: " + e + "\n" + e.stack);
|
|
}
|
|
}
|
|
});
|
|
})(key);
|
|
}
|
|
|
|
socket.on("connect_error", function (error) {
|
|
// If the socket has connected at least once during this
|
|
// session and now gets a connect error, it is likely because
|
|
// the server is down temporarily and not because of any configuration
|
|
// issue. Therefore, omit the warning message about refreshing.
|
|
if (HAS_CONNECTED_BEFORE) {
|
|
return;
|
|
}
|
|
|
|
SOCKETIO_CONNECT_ERROR_COUNT++;
|
|
if (SOCKETIO_CONNECT_ERROR_COUNT >= 3 &&
|
|
$("#socketio-connect-error").length === 0) {
|
|
var message = "Failed to connect to the server. Try clearing your " +
|
|
"cache and refreshing the page.";
|
|
makeAlert("Error", message, "alert-danger")
|
|
.attr("id", "socketio-connect-error")
|
|
.appendTo($("#announcements"));
|
|
}
|
|
});
|
|
};
|
|
|
|
function ioServerConnect(socketConfig) {
|
|
if (socketConfig.error) {
|
|
makeAlert("Error", "Socket.io configuration returned error: " +
|
|
socketConfig.error, "alert-danger")
|
|
.appendTo($("#announcements"));
|
|
return;
|
|
}
|
|
|
|
var servers;
|
|
if (socketConfig.alt && socketConfig.alt.length > 0 &&
|
|
localStorage.useAltServer === "true") {
|
|
servers = socketConfig.alt;
|
|
console.log("Using alt servers: " + JSON.stringify(servers));
|
|
} else {
|
|
servers = socketConfig.servers;
|
|
}
|
|
|
|
var chosenServer = null;
|
|
servers.forEach(function (server) {
|
|
if (chosenServer === null) {
|
|
chosenServer = server;
|
|
} else if (server.secure && !chosenServer.secure) {
|
|
chosenServer = server;
|
|
} else if (!server.ipv6Only && chosenServer.ipv6Only) {
|
|
chosenServer = server;
|
|
}
|
|
});
|
|
|
|
console.log("Connecting to " + JSON.stringify(chosenServer));
|
|
|
|
if (chosenServer === null) {
|
|
makeAlert("Error",
|
|
"Socket.io configuration was unable to find a suitable server",
|
|
"alert-danger")
|
|
.appendTo($("#announcements"));
|
|
}
|
|
|
|
var opts = {
|
|
secure: chosenServer.secure
|
|
};
|
|
|
|
window.socket = io(chosenServer.url, opts);
|
|
}
|
|
|
|
var USING_LETS_ENCRYPT = false;
|
|
|
|
function initSocketIO(socketConfig) {
|
|
function genericConnectionError() {
|
|
var message = "The socket.io library could not be loaded from <code>" +
|
|
source + "</code>. Ensure that it is not being blocked " +
|
|
"by a script blocking extension or firewall and try again.";
|
|
makeAlert("Error", message, "alert-danger")
|
|
.appendTo($("#announcements"));
|
|
Callbacks.disconnect();
|
|
}
|
|
|
|
if (typeof io === "undefined") {
|
|
var script = document.getElementById("socketio-js");
|
|
var source = "unknown";
|
|
if (script) {
|
|
source = script.src;
|
|
}
|
|
|
|
if (/^https/.test(source) && location.protocol === "http:"
|
|
&& USING_LETS_ENCRYPT) {
|
|
checkLetsEncrypt(socketConfig, genericConnectionError);
|
|
return;
|
|
}
|
|
|
|
genericConnectionError();
|
|
return;
|
|
}
|
|
|
|
ioServerConnect(socketConfig);
|
|
setupCallbacks();
|
|
}
|
|
|
|
function checkLetsEncrypt(socketConfig, nonLetsEncryptError) {
|
|
var servers = socketConfig.servers.filter(function (server) {
|
|
return !server.secure && !server.ipv6Only
|
|
});
|
|
|
|
if (servers.length === 0) {
|
|
nonLetsEncryptError();
|
|
return;
|
|
}
|
|
|
|
$.ajax({
|
|
url: servers[0].url + "/socket.io/socket.io.js",
|
|
dataType: "script",
|
|
timeout: 10000
|
|
}).done(function () {
|
|
var message = "Your browser cannot connect securely because it does " +
|
|
"not support the newer Let's Encrypt certificate " +
|
|
"authority. Click below to acknowledge and continue " +
|
|
"connecting over an unencrypted connection. See " +
|
|
"<a href=\"https://community.letsencrypt.org/t/which-browsers-and-operating-systems-support-lets-encrypt/4394\" target=\"_blank\">here</a> " +
|
|
"for more details.";
|
|
var connectionAlert = makeAlert("Error", message, "alert-danger")
|
|
.appendTo($("#announcements"));
|
|
|
|
var button = document.createElement("button");
|
|
button.className = "btn btn-default";
|
|
button.textContent = "Connect Anyways";
|
|
|
|
var alertBox = connectionAlert.find(".alert")[0];
|
|
alertBox.appendChild(document.createElement("hr"));
|
|
alertBox.appendChild(button);
|
|
|
|
button.onclick = function connectAnyways() {
|
|
ioServerConnect({
|
|
servers: servers
|
|
});
|
|
setupCallbacks();
|
|
};
|
|
}).error(function () {
|
|
nonLetsEncryptError();
|
|
});
|
|
}
|
|
|
|
(function () {
|
|
$.getJSON("/socketconfig/" + CHANNEL.name + ".json")
|
|
.done(function (socketConfig) {
|
|
initSocketIO(socketConfig);
|
|
}).fail(function () {
|
|
makeAlert("Error", "Failed to retrieve socket.io configuration. " +
|
|
"Please try again in a few minutes.",
|
|
"alert-danger")
|
|
.appendTo($("#announcements"));
|
|
Callbacks.disconnect();
|
|
});
|
|
})();
|